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 {
|
||||
color: #222;
|
||||
background: rgb(249,235,213);
|
||||
font-size: 1em;
|
||||
line-height: 1.4;
|
||||
overflow: hidden;
|
||||
}
|
||||
|
||||
#console {
|
||||
font: 13px monospace, sans-serif;
|
||||
padding: 5px;
|
||||
color: greenyellow;
|
||||
background: black;
|
||||
color: black;
|
||||
background: rgb(255,241,219);
|
||||
position: fixed;
|
||||
right: 10px;
|
||||
bottom: 0;
|
||||
width: 30%;
|
||||
height: 20em;
|
||||
right: 1px;
|
||||
bottom: 4em;
|
||||
width: 30em;
|
||||
height: 90%;
|
||||
border: 2px solid darkgray;
|
||||
border-radius: 10px;
|
||||
z-index: 0;
|
||||
}
|
||||
|
||||
#help{
|
||||
background: black;
|
||||
font: 13px monospace, sans-serif;
|
||||
padding: 5px;
|
||||
width: 90%;
|
||||
color: greenyellow;
|
||||
position: absolute;
|
||||
background: rgb(249,235,213);
|
||||
font: 13px monospace, sans-serif;
|
||||
padding-left: 5px;
|
||||
width: 90%;
|
||||
color: black;
|
||||
z-index: 2;
|
||||
scroll-behavior: auto;
|
||||
overflow: auto;
|
||||
border: none transparent;
|
||||
}
|
||||
|
||||
#command_input {
|
||||
background: black;
|
||||
color: greenyellow;
|
||||
background: rgb(255,241,219);
|
||||
color: black;
|
||||
outline: none;
|
||||
width: 90%;
|
||||
height: 20em;
|
||||
height: 99%;
|
||||
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 {
|
||||
border: none transparent;
|
||||
border-top-left-radius: 3px;
|
||||
border-top-right-radius: 3px;
|
||||
}
|
||||
|
||||
.single_line {
|
||||
|
|
|
|||
|
|
@ -29,10 +29,10 @@ moving_pillars(5, 10,1, false);
|
|||
</label>
|
||||
</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>
|
||||
|
||||
</body>
|
||||
|
||||
</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 canvas_element = document.getElementById('canvas');
|
||||
const canvas = canvas_element.getContext('2d');
|
||||
const width = canvas_element.width = window.innerWidth;
|
||||
const height = canvas_element.height = window.innerHeight;
|
||||
canvas.fillStyle = 'white';
|
||||
canvas.strokeStyle = 'black';
|
||||
const width = window.innerWidth;
|
||||
const height = window.innerHeight;
|
||||
canvas.clearRect(0, 0, width, height);
|
||||
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');
|
||||
let script;
|
||||
let tx = 0;
|
||||
let ty = 0;
|
||||
let tangle = 0;
|
||||
|
||||
document.body.onkeydown = function (event) {
|
||||
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 = [
|
||||
{v1: "start(x,y)", v2: "start(0,0);"},
|
||||
{v1: "go(distance)", v2: "go(1);"},
|
||||
|
|
@ -27,9 +49,14 @@ import {interpret} from "./interpreter.js";
|
|||
{v1: "left(angle)", v2: "left(90);"},
|
||||
{v1: "right(angle)", v2: "right(90);"},
|
||||
{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: "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 = {};
|
||||
for (let i = 0; i < help.length; i++) {
|
||||
|
|
@ -43,11 +70,17 @@ import {interpret} from "./interpreter.js";
|
|||
slices[help[i].v1] = [help[i]];
|
||||
}
|
||||
|
||||
let tx = 0;
|
||||
let ty = 0;
|
||||
let tangle = 0;
|
||||
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 (e) {
|
||||
document.getElementById('log').innerHTML = e;
|
||||
}
|
||||
}
|
||||
|
||||
document.onkeyup = function (event) {
|
||||
command_input_element.onkeyup = function handle_key_input(event) {
|
||||
if (event.key === 'Tab') {
|
||||
const script = command_input_element.value;
|
||||
event.preventDefault();
|
||||
|
|
@ -69,19 +102,6 @@ import {interpret} from "./interpreter.js";
|
|||
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)) {
|
||||
script = command_input_element.value;
|
||||
|
||||
|
|
@ -98,14 +118,17 @@ import {interpret} from "./interpreter.js";
|
|||
const command = currentLineText.trim();
|
||||
if (slices[command]) {
|
||||
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 fontSize = parseFloat(computedStyle.fontSize);
|
||||
const lineHeight = computedStyle.lineHeight === 'normal' ? fontSize * 1.2 : parseFloat(computedStyle.lineHeight);
|
||||
|
||||
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.left = `${textBounding.left + window.scrollX}px`;
|
||||
|
|
@ -116,7 +139,9 @@ import {interpret} from "./interpreter.js";
|
|||
}
|
||||
refresh();
|
||||
}
|
||||
};
|
||||
|
||||
}
|
||||
//main
|
||||
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,
|
||||
STAR
|
||||
} from "./scanner";
|
||||
import {atan, cos, random, sin, tan} from "./functions";
|
||||
|
||||
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) => {
|
||||
tx = x * unit;
|
||||
ty = y * unit;
|
||||
|
|
@ -159,11 +176,11 @@ export function interpret(init_env, code) {
|
|||
length += shift;
|
||||
for (let i = 0; i < n; i++) {
|
||||
THIS.turn(-90);
|
||||
THIS.go(direction === DOWN ? -length2 : length2);
|
||||
THIS.go(direction === UP ? -length2 : length2);
|
||||
THIS.turn(90);
|
||||
THIS.go(1);
|
||||
THIS.turn(90);
|
||||
THIS.go(direction === DOWN ? -length : length);
|
||||
THIS.go(direction === UP ? -length : length);
|
||||
THIS.turn(-90);
|
||||
THIS.go(1);
|
||||
}
|
||||
|
|
@ -269,6 +286,8 @@ export function interpret(init_env, code) {
|
|||
|
||||
const statements = parse(code);
|
||||
for (let i = 0; i < statements.length; i++) {
|
||||
// console.log(statements[i]);
|
||||
THIS.execute(statements[i]);
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
|||
23
js/parser.js
23
js/parser.js
|
|
@ -36,10 +36,12 @@ import {
|
|||
LEFT,
|
||||
RIGHT,
|
||||
TURN,
|
||||
REPEAT
|
||||
REPEAT, functions,
|
||||
} from "./scanner";
|
||||
|
||||
export function parse(code) {
|
||||
const log_console = document.getElementById("log");
|
||||
|
||||
const tokens = scan(code);
|
||||
// console.log(tokens);
|
||||
let current = 0;
|
||||
|
|
@ -48,10 +50,12 @@ export function parse(code) {
|
|||
let statements = [];
|
||||
|
||||
while (!is_at_end()) {
|
||||
log_console.innerHTML = "OK";
|
||||
try {
|
||||
statements.push(declaration());
|
||||
} catch (e) {
|
||||
console.log(e);
|
||||
// console.log(e);
|
||||
log_console.innerHTML = e;
|
||||
current = tokens.length - 1; // stop compiling
|
||||
}
|
||||
}
|
||||
|
|
@ -163,7 +167,7 @@ export function parse(code) {
|
|||
const callStatement = (name) => {
|
||||
const args = arg_expressions();
|
||||
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 = () => {
|
||||
|
|
@ -271,6 +275,16 @@ export function parse(code) {
|
|||
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();
|
||||
}
|
||||
|
||||
|
|
@ -307,7 +321,7 @@ export function parse(code) {
|
|||
if (check(type)) {
|
||||
return advance();
|
||||
}
|
||||
throw error(peek(), message);
|
||||
throw error(peek(), message + " but was " + peek().lexeme);
|
||||
}
|
||||
|
||||
const match = (...types) => {
|
||||
|
|
@ -322,7 +336,6 @@ export function parse(code) {
|
|||
}
|
||||
|
||||
const check = (type) => {
|
||||
// console.log(peek().type+"==="+type);
|
||||
if (is_at_end()) {
|
||||
return false;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -10,7 +10,7 @@ export const scan = (source) => {
|
|||
scan_token();
|
||||
}
|
||||
|
||||
tokens.push({type: EOF, lexeme: "", line: line});
|
||||
tokens.push({type: EOF, lexeme: "EOF", line: line});
|
||||
return tokens;
|
||||
};
|
||||
|
||||
|
|
@ -107,8 +107,10 @@ export const scan = (source) => {
|
|||
}
|
||||
let text = source.substring(start, current);
|
||||
let type = keywords.get(text);
|
||||
|
||||
if (type == null) {
|
||||
if (type === undefined) {
|
||||
type = functions.get(text);
|
||||
}
|
||||
if (type === undefined) {
|
||||
type = IDENTIFIER;
|
||||
}
|
||||
add_token(type);
|
||||
|
|
@ -153,7 +155,7 @@ export const scan = (source) => {
|
|||
advance();
|
||||
|
||||
let value = source.substring(start + 1, current - 1);
|
||||
console.log("string "+value);
|
||||
// console.log("string " + value);
|
||||
add_token(STRING, value);
|
||||
}
|
||||
|
||||
|
|
@ -242,6 +244,11 @@ export const PILLARS = 42;
|
|||
export const MOVING_PILLARS = 43;
|
||||
export const STAIRCASE = 44;
|
||||
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([
|
||||
["and", AND],
|
||||
|
|
@ -265,4 +272,14 @@ export const keywords = new Map([
|
|||
["moving_pillars", MOVING_PILLARS],
|
||||
["staircase", STAIRCASE],
|
||||
["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