lots of things
This commit is contained in:
parent
4f574e4198
commit
21daf0d4be
7 changed files with 246 additions and 137 deletions
|
|
@ -1,48 +1,64 @@
|
||||||
html {
|
html {
|
||||||
color: #222;
|
|
||||||
background: rgb(249,235,213);
|
background: rgb(249,235,213);
|
||||||
font-size: 1em;
|
font-size: 1em;
|
||||||
line-height: 1.4;
|
line-height: 1.4;
|
||||||
|
overflow: hidden;
|
||||||
}
|
}
|
||||||
|
|
||||||
#console {
|
#console {
|
||||||
font: 13px monospace, sans-serif;
|
font: 13px monospace, sans-serif;
|
||||||
padding: 5px;
|
padding: 5px;
|
||||||
color: greenyellow;
|
color: black;
|
||||||
background: black;
|
background: rgb(255,241,219);
|
||||||
position: fixed;
|
position: fixed;
|
||||||
right: 10px;
|
right: 1px;
|
||||||
bottom: 0;
|
bottom: 4em;
|
||||||
width: 30%;
|
width: 30em;
|
||||||
height: 20em;
|
height: 90%;
|
||||||
border: 2px solid darkgray;
|
border: 2px solid darkgray;
|
||||||
border-radius: 10px;
|
border-radius: 10px;
|
||||||
z-index: 0;
|
z-index: 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
#help{
|
#help{
|
||||||
background: black;
|
|
||||||
font: 13px monospace, sans-serif;
|
|
||||||
padding: 5px;
|
|
||||||
width: 90%;
|
|
||||||
color: greenyellow;
|
|
||||||
position: absolute;
|
position: absolute;
|
||||||
|
background: rgb(249,235,213);
|
||||||
|
font: 13px monospace, sans-serif;
|
||||||
|
padding-left: 5px;
|
||||||
|
width: 90%;
|
||||||
|
color: black;
|
||||||
z-index: 2;
|
z-index: 2;
|
||||||
|
scroll-behavior: auto;
|
||||||
|
overflow: auto;
|
||||||
|
border: none transparent;
|
||||||
}
|
}
|
||||||
|
|
||||||
#command_input {
|
#command_input {
|
||||||
background: black;
|
background: rgb(255,241,219);
|
||||||
color: greenyellow;
|
color: black;
|
||||||
outline: none;
|
outline: none;
|
||||||
width: 90%;
|
width: 90%;
|
||||||
height: 20em;
|
height: 99%;
|
||||||
resize: none;
|
resize: none;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#log{
|
||||||
|
font: 13px monospace, sans-serif;
|
||||||
|
padding: 5px;
|
||||||
|
color: black;
|
||||||
|
background: rgb(255,241,219);
|
||||||
|
position: fixed;
|
||||||
|
right: 1px;
|
||||||
|
bottom: 0;
|
||||||
|
width: 40em;
|
||||||
|
height: 2em;
|
||||||
|
border: 2px solid darkgray;
|
||||||
|
border-radius: 10px;
|
||||||
|
z-index: 0;
|
||||||
|
}
|
||||||
|
|
||||||
.multiline {
|
.multiline {
|
||||||
border: none transparent;
|
border: none transparent;
|
||||||
border-top-left-radius: 3px;
|
|
||||||
border-top-right-radius: 3px;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
.single_line {
|
.single_line {
|
||||||
|
|
|
||||||
|
|
@ -29,10 +29,10 @@ moving_pillars(5, 10,1, false);
|
||||||
</label>
|
</label>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<textarea id="help" style="visibility: hidden"></textarea>
|
<span id="help" style="visibility: hidden"></span>
|
||||||
|
|
||||||
|
<div id="log"></div>
|
||||||
<script src="js/app.js"></script>
|
<script src="js/app.js"></script>
|
||||||
|
|
||||||
</body>
|
</body>
|
||||||
|
|
||||||
</html>
|
</html>
|
||||||
|
|
|
||||||
81
js/app.js
81
js/app.js
|
|
@ -5,14 +5,21 @@ import {interpret} from "./interpreter.js";
|
||||||
const command_input_element = document.getElementById('command_input');
|
const command_input_element = document.getElementById('command_input');
|
||||||
const canvas_element = document.getElementById('canvas');
|
const canvas_element = document.getElementById('canvas');
|
||||||
const canvas = canvas_element.getContext('2d');
|
const canvas = canvas_element.getContext('2d');
|
||||||
const width = canvas_element.width = window.innerWidth;
|
const width = window.innerWidth;
|
||||||
const height = canvas_element.height = window.innerHeight;
|
const height = window.innerHeight;
|
||||||
canvas.fillStyle = 'white';
|
canvas.clearRect(0, 0, width, height);
|
||||||
canvas.strokeStyle = 'black';
|
|
||||||
canvas.lineWidth = 2;
|
canvas.lineWidth = 2;
|
||||||
|
canvas.globalAlpha = 1;
|
||||||
|
canvas.strokeStyle = 'black';
|
||||||
|
canvas.fillStyle = 'black';
|
||||||
|
canvas_element.width = width;
|
||||||
|
canvas_element.height = height;
|
||||||
|
|
||||||
const help_element = document.getElementById('help');
|
const help_element = document.getElementById('help');
|
||||||
let script;
|
let script;
|
||||||
|
let tx = 0;
|
||||||
|
let ty = 0;
|
||||||
|
let tangle = 0;
|
||||||
|
|
||||||
document.body.onkeydown = function (event) {
|
document.body.onkeydown = function (event) {
|
||||||
if (event.key === 'Tab') {
|
if (event.key === 'Tab') {
|
||||||
|
|
@ -20,6 +27,21 @@ import {interpret} from "./interpreter.js";
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const count = function (string, substring) {
|
||||||
|
if (!substring) {
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
let count = 0;
|
||||||
|
let pos = string.indexOf(substring);
|
||||||
|
|
||||||
|
while (pos !== -1) {
|
||||||
|
count++;
|
||||||
|
pos = string.indexOf(substring, pos + substring.length);
|
||||||
|
}
|
||||||
|
|
||||||
|
return count;
|
||||||
|
};
|
||||||
|
|
||||||
const help = [
|
const help = [
|
||||||
{v1: "start(x,y)", v2: "start(0,0);"},
|
{v1: "start(x,y)", v2: "start(0,0);"},
|
||||||
{v1: "go(distance)", v2: "go(1);"},
|
{v1: "go(distance)", v2: "go(1);"},
|
||||||
|
|
@ -27,9 +49,14 @@ import {interpret} from "./interpreter.js";
|
||||||
{v1: "left(angle)", v2: "left(90);"},
|
{v1: "left(angle)", v2: "left(90);"},
|
||||||
{v1: "right(angle)", v2: "right(90);"},
|
{v1: "right(angle)", v2: "right(90);"},
|
||||||
{v1: "pillars(number, length, shift = 0, direction = UP)", v2: "pillars(3, 10, 0, DOWN);"},
|
{v1: "pillars(number, length, shift = 0, direction = UP)", v2: "pillars(3, 10, 0, DOWN);"},
|
||||||
{v1: "moving_pillars(number, length, shift = 0, direction = DOWN)", v2: "moving_pillars(3, 10, 0, DOWN);"},
|
{v1: "moving_pillars(number, length, shift = 1, direction = DOWN)", v2: "moving_pillars(3, 10, 1, DOWN);"},
|
||||||
{v1: "staircase(number, size, direction = DOWN)", v2: "staircase(3, 1, DOWN);"},
|
{v1: "staircase(number, size, direction = DOWN)", v2: "staircase(3, 1, DOWN);"},
|
||||||
{v1: "repeat([start], end){...}", v2: "repeat(5){\n}"},
|
{v1: "repeat([start], end){...}", v2: "repeat(5){\n \n}"},
|
||||||
|
{v1: "random(range-start, range-end)", v2: "random(0,1);"},
|
||||||
|
{v1: "sin(alpha)", v2: "sin();"},
|
||||||
|
{v1: "cos(alpha)", v2: "cos();"},
|
||||||
|
{v1: "tan(alpha)", v2: "tan();"},
|
||||||
|
{v1: "atan(tangent)", v2: "atan();"},
|
||||||
];
|
];
|
||||||
const slices = {};
|
const slices = {};
|
||||||
for (let i = 0; i < help.length; i++) {
|
for (let i = 0; i < help.length; i++) {
|
||||||
|
|
@ -43,11 +70,17 @@ import {interpret} from "./interpreter.js";
|
||||||
slices[help[i].v1] = [help[i]];
|
slices[help[i].v1] = [help[i]];
|
||||||
}
|
}
|
||||||
|
|
||||||
let tx = 0;
|
function refresh() {
|
||||||
let ty = 0;
|
script = command_input_element.value;
|
||||||
let tangle = 0;
|
try {
|
||||||
|
canvas.clearRect(0, 0, width, height);
|
||||||
|
interpret({canvas: canvas, x: tx, y: ty, angle: tangle, unit: 10, width: width, height: height}, script);
|
||||||
|
} catch (e) {
|
||||||
|
document.getElementById('log').innerHTML = e;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
document.onkeyup = function (event) {
|
command_input_element.onkeyup = function handle_key_input(event) {
|
||||||
if (event.key === 'Tab') {
|
if (event.key === 'Tab') {
|
||||||
const script = command_input_element.value;
|
const script = command_input_element.value;
|
||||||
event.preventDefault();
|
event.preventDefault();
|
||||||
|
|
@ -69,19 +102,6 @@ import {interpret} from "./interpreter.js";
|
||||||
refresh();
|
refresh();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
};
|
|
||||||
|
|
||||||
function refresh() {
|
|
||||||
script = command_input_element.value;
|
|
||||||
|
|
||||||
try {
|
|
||||||
canvas.clearRect(0, 0, width, height);
|
|
||||||
interpret({canvas: canvas, x: tx, y: ty, angle: tangle, unit: 10, width:width, height:height}, script);
|
|
||||||
} catch (Exception) {
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
command_input_element.onkeyup = function handle_key_input(event) {
|
|
||||||
if (!['ArrowUp', 'ArrowDown', 'ArrowLeft', 'ArrowRight'].includes(event.key)) {
|
if (!['ArrowUp', 'ArrowDown', 'ArrowLeft', 'ArrowRight'].includes(event.key)) {
|
||||||
script = command_input_element.value;
|
script = command_input_element.value;
|
||||||
|
|
||||||
|
|
@ -98,14 +118,17 @@ import {interpret} from "./interpreter.js";
|
||||||
const command = currentLineText.trim();
|
const command = currentLineText.trim();
|
||||||
if (slices[command]) {
|
if (slices[command]) {
|
||||||
const help = slices[command].map(x => x.v1);
|
const help = slices[command].map(x => x.v1);
|
||||||
const help_text = help.join('\n');
|
let help_text = "";
|
||||||
|
for (let x = 0; x < help.length; x++) {
|
||||||
|
help_text += `<div id="l-${x}">${help[x]}</div>`;
|
||||||
|
}
|
||||||
|
|
||||||
const computedStyle = window.getComputedStyle(command_input_element);
|
const computedStyle = window.getComputedStyle(command_input_element);
|
||||||
const fontSize = parseFloat(computedStyle.fontSize);
|
const fontSize = parseFloat(computedStyle.fontSize);
|
||||||
const lineHeight = computedStyle.lineHeight === 'normal' ? fontSize * 1.2 : parseFloat(computedStyle.lineHeight);
|
const lineHeight = computedStyle.lineHeight === 'normal' ? fontSize * 1.2 : parseFloat(computedStyle.lineHeight);
|
||||||
|
|
||||||
const textBounding = command_input_element.getBoundingClientRect();
|
const textBounding = command_input_element.getBoundingClientRect();
|
||||||
const topPosition = textBounding.top + window.scrollY;// + (cursor_position - startOfLine) * lineHeight/ script.split('\n').length;
|
const topPosition = textBounding.top + window.scrollY + (count(script, '\n') + 4) * lineHeight;
|
||||||
|
|
||||||
help_element.style.visibility = 'visible';
|
help_element.style.visibility = 'visible';
|
||||||
help_element.style.left = `${textBounding.left + window.scrollX}px`;
|
help_element.style.left = `${textBounding.left + window.scrollX}px`;
|
||||||
|
|
@ -116,7 +139,9 @@ import {interpret} from "./interpreter.js";
|
||||||
}
|
}
|
||||||
refresh();
|
refresh();
|
||||||
}
|
}
|
||||||
};
|
}
|
||||||
|
//main
|
||||||
refresh();
|
refresh();
|
||||||
})();
|
})
|
||||||
|
();
|
||||||
|
|
||||||
|
|
|
||||||
19
js/functions.js
Normal file
19
js/functions.js
Normal file
|
|
@ -0,0 +1,19 @@
|
||||||
|
export function random(s, e = 0) {
|
||||||
|
return Math.random() * (e - s) + s;
|
||||||
|
}
|
||||||
|
|
||||||
|
export function cos(a) {
|
||||||
|
return Math.cos(a);
|
||||||
|
}
|
||||||
|
|
||||||
|
export function sin(a) {
|
||||||
|
return Math.sin(a);
|
||||||
|
}
|
||||||
|
|
||||||
|
export function tan(a) {
|
||||||
|
return Math.tan(a);
|
||||||
|
}
|
||||||
|
|
||||||
|
export function atan(a) {
|
||||||
|
return Math.atan(a);
|
||||||
|
}
|
||||||
|
|
@ -12,6 +12,7 @@ import {
|
||||||
SLASH,
|
SLASH,
|
||||||
STAR
|
STAR
|
||||||
} from "./scanner";
|
} from "./scanner";
|
||||||
|
import {atan, cos, random, sin, tan} from "./functions";
|
||||||
|
|
||||||
export function interpret(init_env, code) {
|
export function interpret(init_env, code) {
|
||||||
|
|
||||||
|
|
@ -110,6 +111,22 @@ export function interpret(init_env, code) {
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
|
||||||
|
visitCallFunction: (name, argList) => {
|
||||||
|
let args = argList.map(THIS.evaluate);
|
||||||
|
switch (name) {
|
||||||
|
case "random":
|
||||||
|
return random(...args);
|
||||||
|
case "cos":
|
||||||
|
return cos(...args);
|
||||||
|
case "sin":
|
||||||
|
return sin(...args);
|
||||||
|
case "tan":
|
||||||
|
return tan(...args);
|
||||||
|
case "atan":
|
||||||
|
return atan(...args);
|
||||||
|
}
|
||||||
|
},
|
||||||
|
|
||||||
start: (x, y) => {
|
start: (x, y) => {
|
||||||
tx = x * unit;
|
tx = x * unit;
|
||||||
ty = y * unit;
|
ty = y * unit;
|
||||||
|
|
@ -159,11 +176,11 @@ export function interpret(init_env, code) {
|
||||||
length += shift;
|
length += shift;
|
||||||
for (let i = 0; i < n; i++) {
|
for (let i = 0; i < n; i++) {
|
||||||
THIS.turn(-90);
|
THIS.turn(-90);
|
||||||
THIS.go(direction === DOWN ? -length2 : length2);
|
THIS.go(direction === UP ? -length2 : length2);
|
||||||
THIS.turn(90);
|
THIS.turn(90);
|
||||||
THIS.go(1);
|
THIS.go(1);
|
||||||
THIS.turn(90);
|
THIS.turn(90);
|
||||||
THIS.go(direction === DOWN ? -length : length);
|
THIS.go(direction === UP ? -length : length);
|
||||||
THIS.turn(-90);
|
THIS.turn(-90);
|
||||||
THIS.go(1);
|
THIS.go(1);
|
||||||
}
|
}
|
||||||
|
|
@ -269,6 +286,8 @@ export function interpret(init_env, code) {
|
||||||
|
|
||||||
const statements = parse(code);
|
const statements = parse(code);
|
||||||
for (let i = 0; i < statements.length; i++) {
|
for (let i = 0; i < statements.length; i++) {
|
||||||
|
// console.log(statements[i]);
|
||||||
THIS.execute(statements[i]);
|
THIS.execute(statements[i]);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
23
js/parser.js
23
js/parser.js
|
|
@ -36,10 +36,12 @@ import {
|
||||||
LEFT,
|
LEFT,
|
||||||
RIGHT,
|
RIGHT,
|
||||||
TURN,
|
TURN,
|
||||||
REPEAT
|
REPEAT, functions,
|
||||||
} from "./scanner";
|
} from "./scanner";
|
||||||
|
|
||||||
export function parse(code) {
|
export function parse(code) {
|
||||||
|
const log_console = document.getElementById("log");
|
||||||
|
|
||||||
const tokens = scan(code);
|
const tokens = scan(code);
|
||||||
// console.log(tokens);
|
// console.log(tokens);
|
||||||
let current = 0;
|
let current = 0;
|
||||||
|
|
@ -48,10 +50,12 @@ export function parse(code) {
|
||||||
let statements = [];
|
let statements = [];
|
||||||
|
|
||||||
while (!is_at_end()) {
|
while (!is_at_end()) {
|
||||||
|
log_console.innerHTML = "OK";
|
||||||
try {
|
try {
|
||||||
statements.push(declaration());
|
statements.push(declaration());
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
console.log(e);
|
// console.log(e);
|
||||||
|
log_console.innerHTML = e;
|
||||||
current = tokens.length - 1; // stop compiling
|
current = tokens.length - 1; // stop compiling
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -163,7 +167,7 @@ export function parse(code) {
|
||||||
const callStatement = (name) => {
|
const callStatement = (name) => {
|
||||||
const args = arg_expressions();
|
const args = arg_expressions();
|
||||||
consume(SEMICOLON, "Expected semicolon");
|
consume(SEMICOLON, "Expected semicolon");
|
||||||
return {class: "call", accept: (visitor) => visitor.visitCallStatement(name, args)};
|
return {class: "call_stmt", accept: (visitor) => visitor.visitCallStatement(name, args)};
|
||||||
}
|
}
|
||||||
|
|
||||||
const call_block = () => {
|
const call_block = () => {
|
||||||
|
|
@ -271,6 +275,16 @@ export function parse(code) {
|
||||||
accept: (visitor) => visitor.visitUnaryExpr(operator, right)
|
accept: (visitor) => visitor.visitUnaryExpr(operator, right)
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
return fun();
|
||||||
|
}
|
||||||
|
|
||||||
|
const fun = () => {
|
||||||
|
let function_name = peek().lexeme;
|
||||||
|
if (functions.has(function_name)) {
|
||||||
|
advance();
|
||||||
|
let args = arg_expressions();
|
||||||
|
return {class: "call_fun", accept: (visitor) => visitor.visitCallFunction(function_name, args)};
|
||||||
|
}
|
||||||
return primary();
|
return primary();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -307,7 +321,7 @@ export function parse(code) {
|
||||||
if (check(type)) {
|
if (check(type)) {
|
||||||
return advance();
|
return advance();
|
||||||
}
|
}
|
||||||
throw error(peek(), message);
|
throw error(peek(), message + " but was " + peek().lexeme);
|
||||||
}
|
}
|
||||||
|
|
||||||
const match = (...types) => {
|
const match = (...types) => {
|
||||||
|
|
@ -322,7 +336,6 @@ export function parse(code) {
|
||||||
}
|
}
|
||||||
|
|
||||||
const check = (type) => {
|
const check = (type) => {
|
||||||
// console.log(peek().type+"==="+type);
|
|
||||||
if (is_at_end()) {
|
if (is_at_end()) {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -10,7 +10,7 @@ export const scan = (source) => {
|
||||||
scan_token();
|
scan_token();
|
||||||
}
|
}
|
||||||
|
|
||||||
tokens.push({type: EOF, lexeme: "", line: line});
|
tokens.push({type: EOF, lexeme: "EOF", line: line});
|
||||||
return tokens;
|
return tokens;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
@ -107,8 +107,10 @@ export const scan = (source) => {
|
||||||
}
|
}
|
||||||
let text = source.substring(start, current);
|
let text = source.substring(start, current);
|
||||||
let type = keywords.get(text);
|
let type = keywords.get(text);
|
||||||
|
if (type === undefined) {
|
||||||
if (type == null) {
|
type = functions.get(text);
|
||||||
|
}
|
||||||
|
if (type === undefined) {
|
||||||
type = IDENTIFIER;
|
type = IDENTIFIER;
|
||||||
}
|
}
|
||||||
add_token(type);
|
add_token(type);
|
||||||
|
|
@ -153,7 +155,7 @@ export const scan = (source) => {
|
||||||
advance();
|
advance();
|
||||||
|
|
||||||
let value = source.substring(start + 1, current - 1);
|
let value = source.substring(start + 1, current - 1);
|
||||||
console.log("string "+value);
|
// console.log("string " + value);
|
||||||
add_token(STRING, value);
|
add_token(STRING, value);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -242,6 +244,11 @@ export const PILLARS = 42;
|
||||||
export const MOVING_PILLARS = 43;
|
export const MOVING_PILLARS = 43;
|
||||||
export const STAIRCASE = 44;
|
export const STAIRCASE = 44;
|
||||||
export const REPEAT = 45;
|
export const REPEAT = 45;
|
||||||
|
export const RANDOM = 46;
|
||||||
|
export const COS = 47;
|
||||||
|
export const SIN = 48;
|
||||||
|
export const TAN = 49;
|
||||||
|
export const ATAN = 50;
|
||||||
|
|
||||||
export const keywords = new Map([
|
export const keywords = new Map([
|
||||||
["and", AND],
|
["and", AND],
|
||||||
|
|
@ -265,4 +272,14 @@ export const keywords = new Map([
|
||||||
["moving_pillars", MOVING_PILLARS],
|
["moving_pillars", MOVING_PILLARS],
|
||||||
["staircase", STAIRCASE],
|
["staircase", STAIRCASE],
|
||||||
["repeat", REPEAT],
|
["repeat", REPEAT],
|
||||||
])
|
["random", RANDOM],
|
||||||
|
]);
|
||||||
|
|
||||||
|
export const functions = new Map([
|
||||||
|
["random", RANDOM],
|
||||||
|
["cos", COS],
|
||||||
|
["sin", SIN],
|
||||||
|
["tan", TAN],
|
||||||
|
["atan", ATAN],
|
||||||
|
]);
|
||||||
|
|
||||||
|
|
|
||||||
Loading…
Add table
Reference in a new issue