bugfixes and looping
This commit is contained in:
parent
45f31a92de
commit
2de4cac1af
6 changed files with 199 additions and 48 deletions
|
|
@ -75,7 +75,7 @@ import {interpret} from "./interpreter.js";
|
||||||
|
|
||||||
try {
|
try {
|
||||||
canvas.clearRect(0, 0, width, height);
|
canvas.clearRect(0, 0, width, height);
|
||||||
interpret({canvas: canvas, x: tx, y: ty, angle: tangle, unit: 10, width, height}, script);
|
interpret({canvas: canvas, x: tx, y: ty, angle: tangle, unit: 10, width:width, height:height}, script);
|
||||||
} catch (Exception) {
|
} catch (Exception) {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -37,6 +37,30 @@ export function interpret(init_env, code) {
|
||||||
THIS.executeBlock(statements, {enclosing: THIS.current_environment});
|
THIS.executeBlock(statements, {enclosing: THIS.current_environment});
|
||||||
},
|
},
|
||||||
|
|
||||||
|
visitParametrizedBlock: (argList, statements) => {
|
||||||
|
let args = argList.map(THIS.evaluate);
|
||||||
|
let start,end;
|
||||||
|
if (args.length===2){
|
||||||
|
start = args[0];
|
||||||
|
end = args[1];
|
||||||
|
} else {
|
||||||
|
start =0;
|
||||||
|
end = args[0];
|
||||||
|
}
|
||||||
|
let previous = THIS.current_environment;
|
||||||
|
THIS.current_environment = {enclosing: previous};
|
||||||
|
try {
|
||||||
|
for (let i = start; i < end; i++) {
|
||||||
|
THIS.current_environment.it = i;
|
||||||
|
for (let i = 0; i < statements.length; i++) {
|
||||||
|
THIS.execute(statements[i]);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} finally {
|
||||||
|
THIS.current_environment = previous;
|
||||||
|
}
|
||||||
|
},
|
||||||
|
|
||||||
visitExpressionStatement: (expression) => {
|
visitExpressionStatement: (expression) => {
|
||||||
THIS.evaluate(expression);
|
THIS.evaluate(expression);
|
||||||
},
|
},
|
||||||
|
|
@ -50,7 +74,6 @@ export function interpret(init_env, code) {
|
||||||
},
|
},
|
||||||
|
|
||||||
visitPrintStatement: (expression) => {
|
visitPrintStatement: (expression) => {
|
||||||
console.log(THIS.current_environment);
|
|
||||||
let value = THIS.evaluate(expression);
|
let value = THIS.evaluate(expression);
|
||||||
console.log(THIS.stringify(value));
|
console.log(THIS.stringify(value));
|
||||||
},
|
},
|
||||||
|
|
@ -58,15 +81,32 @@ export function interpret(init_env, code) {
|
||||||
visitCallStatement: (fun, argList) => {
|
visitCallStatement: (fun, argList) => {
|
||||||
let args = argList.map(THIS.evaluate);
|
let args = argList.map(THIS.evaluate);
|
||||||
switch (fun) {
|
switch (fun) {
|
||||||
case "start": THIS.start(...args); break;
|
case "start":
|
||||||
case "go": THIS.go(...args); break;
|
THIS.start(...args);
|
||||||
case "turn": THIS.turn(...args); break;
|
break;
|
||||||
case "left": THIS.left(...args); break;
|
case "go":
|
||||||
case "right": THIS.right(...args); break;
|
THIS.go(...args);
|
||||||
case "pillars": THIS.pillars(...args); break;
|
break;
|
||||||
case "moving_pillars": THIS.moving_pillars(...args); break;
|
case "turn":
|
||||||
case "staircase": THIS.staircase(...args); break;
|
THIS.turn(...args);
|
||||||
default: throw "Unknown function: " + fun;
|
break;
|
||||||
|
case "left":
|
||||||
|
THIS.left(...args);
|
||||||
|
break;
|
||||||
|
case "right":
|
||||||
|
THIS.right(...args);
|
||||||
|
break;
|
||||||
|
case "pillars":
|
||||||
|
THIS.pillars(...args);
|
||||||
|
break;
|
||||||
|
case "moving_pillars":
|
||||||
|
THIS.moving_pillars(...args);
|
||||||
|
break;
|
||||||
|
case "staircase":
|
||||||
|
THIS.staircase(...args);
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
throw "Unknown function: " + fun;
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
|
||||||
|
|
@ -151,9 +191,9 @@ export function interpret(init_env, code) {
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
|
||||||
visitBinaryExpr: (operator, _left, _right) => {
|
visitBinaryExpr: (operator, l, r) => {
|
||||||
let left = THIS.evaluate(_left);
|
let left = THIS.evaluate(l);
|
||||||
let right = THIS.evaluate(_right);
|
let right = THIS.evaluate(r);
|
||||||
|
|
||||||
switch (operator.type) {
|
switch (operator.type) {
|
||||||
case MINUS:
|
case MINUS:
|
||||||
|
|
@ -230,7 +270,6 @@ export function interpret(init_env, code) {
|
||||||
try {
|
try {
|
||||||
const statements = parse(code);
|
const statements = parse(code);
|
||||||
for (let i = 0; i < statements.length; i++) {
|
for (let i = 0; i < statements.length; i++) {
|
||||||
|
|
||||||
THIS.execute(statements[i]);
|
THIS.execute(statements[i]);
|
||||||
}
|
}
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
|
|
|
||||||
101
js/parser.js
101
js/parser.js
|
|
@ -1,14 +1,42 @@
|
||||||
import "./scanner";
|
import "./scanner";
|
||||||
import {
|
import {
|
||||||
scan, error,
|
scan,
|
||||||
BANG, BANG_EQUAL, EOF,
|
error,
|
||||||
EQUAL, EQUAL_EQUAL,
|
BANG,
|
||||||
FALSE, GREATER,
|
BANG_EQUAL,
|
||||||
GREATER_EQUAL, IDENTIFIER,
|
EOF,
|
||||||
LEFT_BRACE, LEFT_PAREN, LESS,
|
EQUAL,
|
||||||
LESS_EQUAL, MINUS, NUMBER, PLUS,
|
EQUAL_EQUAL,
|
||||||
PRINT, RIGHT_BRACE, RIGHT_PAREN,
|
FALSE,
|
||||||
SEMICOLON, SLASH, STAR, STRING, TRUE, VAR, START, COMMA, GO, STAIRCASE, PILLARS, MOVING_PILLARS, LEFT, RIGHT, TURN
|
GREATER,
|
||||||
|
GREATER_EQUAL,
|
||||||
|
IDENTIFIER,
|
||||||
|
LEFT_BRACE,
|
||||||
|
LEFT_PAREN,
|
||||||
|
LESS,
|
||||||
|
LESS_EQUAL,
|
||||||
|
MINUS,
|
||||||
|
NUMBER,
|
||||||
|
PLUS,
|
||||||
|
PRINT,
|
||||||
|
RIGHT_BRACE,
|
||||||
|
RIGHT_PAREN,
|
||||||
|
SEMICOLON,
|
||||||
|
SLASH,
|
||||||
|
STAR,
|
||||||
|
STRING,
|
||||||
|
TRUE,
|
||||||
|
VAR,
|
||||||
|
START,
|
||||||
|
COMMA,
|
||||||
|
GO,
|
||||||
|
STAIRCASE,
|
||||||
|
PILLARS,
|
||||||
|
MOVING_PILLARS,
|
||||||
|
LEFT,
|
||||||
|
RIGHT,
|
||||||
|
TURN,
|
||||||
|
REPEAT
|
||||||
} from "./scanner";
|
} from "./scanner";
|
||||||
|
|
||||||
export function parse(code) {
|
export function parse(code) {
|
||||||
|
|
@ -38,7 +66,7 @@ export function parse(code) {
|
||||||
initializer = expression();
|
initializer = expression();
|
||||||
}
|
}
|
||||||
consume(SEMICOLON, "Expected semicolon");
|
consume(SEMICOLON, "Expected semicolon");
|
||||||
return {accept: (visitor) => visitor.visitVariableStatement(name, initializer)};
|
return {class: "var", name:name, initializer: initializer, accept: (visitor) => visitor.visitVariableStatement(name, initializer)};
|
||||||
}
|
}
|
||||||
|
|
||||||
const statement = () => {
|
const statement = () => {
|
||||||
|
|
@ -69,8 +97,12 @@ export function parse(code) {
|
||||||
if (match(MOVING_PILLARS)) {
|
if (match(MOVING_PILLARS)) {
|
||||||
return callStatement("moving_pillars");
|
return callStatement("moving_pillars");
|
||||||
}
|
}
|
||||||
|
if (match(REPEAT)){
|
||||||
|
return call_block();
|
||||||
|
}
|
||||||
if (match(LEFT_BRACE)) {
|
if (match(LEFT_BRACE)) {
|
||||||
return {accept: (visitor) => visitor.visitBlockStatement(block())};
|
const blockStatement = block();
|
||||||
|
return {class: "block", accept: (visitor) => visitor.visitBlockStatement(blockStatement)};
|
||||||
}
|
}
|
||||||
|
|
||||||
return expressionStatement();
|
return expressionStatement();
|
||||||
|
|
@ -80,7 +112,7 @@ export function parse(code) {
|
||||||
return assignment();
|
return assignment();
|
||||||
}
|
}
|
||||||
|
|
||||||
const expressions = () => {
|
const arg_expressions = () => {
|
||||||
consume(LEFT_PAREN, "Expect '('");
|
consume(LEFT_PAREN, "Expect '('");
|
||||||
let exprs = [expression()];
|
let exprs = [expression()];
|
||||||
while (match(COMMA)) {
|
while (match(COMMA)) {
|
||||||
|
|
@ -98,7 +130,7 @@ export function parse(code) {
|
||||||
|
|
||||||
if (expr.class === 'Variable') {
|
if (expr.class === 'Variable') {
|
||||||
let name = expr.name;
|
let name = expr.name;
|
||||||
return {accept: (visitor) => visitor.visitAssignExpr(name, value)};
|
return {class: "assign", var_name: name, init_expr: value, accept: (visitor) => visitor.visitAssignExpr(name, value)};
|
||||||
}
|
}
|
||||||
|
|
||||||
throw error(equals, "Invalid assignment target.");
|
throw error(equals, "Invalid assignment target.");
|
||||||
|
|
@ -109,19 +141,26 @@ export function parse(code) {
|
||||||
const printStatement = () => {
|
const printStatement = () => {
|
||||||
const value = expression();
|
const value = expression();
|
||||||
consume(SEMICOLON, "Expected semicolon");
|
consume(SEMICOLON, "Expected semicolon");
|
||||||
return {accept: (visitor) => visitor.visitPrintStatement(value)};
|
return {class: "print", value: value, accept: (visitor) => visitor.visitPrintStatement(value)};
|
||||||
}
|
}
|
||||||
|
|
||||||
const callStatement = (name) => {
|
const callStatement = (name) => {
|
||||||
const values = expressions();
|
const args = arg_expressions();
|
||||||
consume(SEMICOLON, "Expected semicolon");
|
consume(SEMICOLON, "Expected semicolon");
|
||||||
return {accept: (visitor) => visitor.visitCallStatement(name, values)};
|
return {class: "call", accept: (visitor) => visitor.visitCallStatement(name, args)};
|
||||||
|
}
|
||||||
|
|
||||||
|
const call_block = () =>{
|
||||||
|
const args = arg_expressions();
|
||||||
|
consume(LEFT_BRACE, "Expect block");
|
||||||
|
const action = block();
|
||||||
|
return {class: "block", accept: (visitor)=> visitor.visitParametrizedBlock(args, action)};
|
||||||
}
|
}
|
||||||
|
|
||||||
const expressionStatement = () => {
|
const expressionStatement = () => {
|
||||||
const value = expression();
|
const value = expression();
|
||||||
consume(SEMICOLON, "Expected semicolon");
|
consume(SEMICOLON, "Expected semicolon");
|
||||||
return {accept: (visitor) => visitor.visitExpressionStatement(value)};
|
return {class: "expressionStatement", expression: value, accept: (visitor) => visitor.visitExpressionStatement(value)};
|
||||||
}
|
}
|
||||||
|
|
||||||
const block = () => {
|
const block = () => {
|
||||||
|
|
@ -138,7 +177,8 @@ export function parse(code) {
|
||||||
while (match(BANG_EQUAL, EQUAL_EQUAL)) {
|
while (match(BANG_EQUAL, EQUAL_EQUAL)) {
|
||||||
let operator = previous();
|
let operator = previous();
|
||||||
let right = comparison();
|
let right = comparison();
|
||||||
expr = {accept: (visitor) => visitor.visitBinaryExpr(operator, expr, right)};
|
const left = expr;
|
||||||
|
expr = {class: "binaryExpr", operator: operator, left: expr, right: right, accept: (visitor) => visitor.visitBinaryExpr(operator, left, right)};
|
||||||
}
|
}
|
||||||
return expr;
|
return expr;
|
||||||
}
|
}
|
||||||
|
|
@ -148,7 +188,8 @@ export function parse(code) {
|
||||||
while (match(GREATER, GREATER_EQUAL, LESS, LESS_EQUAL)) {
|
while (match(GREATER, GREATER_EQUAL, LESS, LESS_EQUAL)) {
|
||||||
let operator = previous();
|
let operator = previous();
|
||||||
let right = term();
|
let right = term();
|
||||||
expr = {accept: (visitor) => visitor.visitBinaryExpr(operator, expr, right)};
|
const left = expr;
|
||||||
|
expr = {class: "binaryExpr", operator: operator, left: expr, right: right, accept: (visitor) => visitor.visitBinaryExpr(operator, left, right)};
|
||||||
}
|
}
|
||||||
return expr;
|
return expr;
|
||||||
}
|
}
|
||||||
|
|
@ -158,7 +199,8 @@ export function parse(code) {
|
||||||
while (match(MINUS, PLUS)) {
|
while (match(MINUS, PLUS)) {
|
||||||
let operator = previous();
|
let operator = previous();
|
||||||
let right = factor();
|
let right = factor();
|
||||||
expr = {accept: (visitor) => visitor.visitBinaryExpr(operator, expr, right)};
|
const left = expr;
|
||||||
|
expr = {class: "binaryExpr", operator: operator, left: expr, right: right, accept: (visitor) => visitor.visitBinaryExpr(operator, left, right)};
|
||||||
}
|
}
|
||||||
return expr;
|
return expr;
|
||||||
}
|
}
|
||||||
|
|
@ -168,7 +210,8 @@ export function parse(code) {
|
||||||
while (match(SLASH, STAR)) {
|
while (match(SLASH, STAR)) {
|
||||||
let operator = previous();
|
let operator = previous();
|
||||||
let right = unary();
|
let right = unary();
|
||||||
expr = {accept: (visitor) => visitor.visitBinaryExpr(operator, expr, right)};
|
const left = expr;
|
||||||
|
expr = {class: "binaryExpr", operator: operator, left: expr, right: right, accept: (visitor) => visitor.visitBinaryExpr(operator, left, right)};
|
||||||
}
|
}
|
||||||
return expr;
|
return expr;
|
||||||
}
|
}
|
||||||
|
|
@ -177,36 +220,36 @@ export function parse(code) {
|
||||||
if (match(BANG, MINUS)) {
|
if (match(BANG, MINUS)) {
|
||||||
let operator = previous();
|
let operator = previous();
|
||||||
let right = unary();
|
let right = unary();
|
||||||
return {accept: (visitor) => visitor.visitUnaryExpr(operator, right)};
|
return {class: "unaryExpr", operator: operator, right: right, accept: (visitor) => visitor.visitUnaryExpr(operator, right)};
|
||||||
}
|
}
|
||||||
return primary();
|
return primary();
|
||||||
}
|
}
|
||||||
|
|
||||||
const primary = () => {
|
const primary = () => {
|
||||||
if (match(FALSE)) {
|
if (match(FALSE)) {
|
||||||
return {accept: (visitor) => visitor.visitLiteralExpr(false)};
|
return {class: "boolean", value: false, accept: (visitor) => visitor.visitLiteralExpr(false)};
|
||||||
}
|
}
|
||||||
if (match(TRUE)) {
|
if (match(TRUE)) {
|
||||||
return {accept: (visitor) => visitor.visitLiteralExpr(true)};
|
return {class: "boolean", value: true, accept: (visitor) => visitor.visitLiteralExpr(true)};
|
||||||
}
|
}
|
||||||
if (check(NUMBER)) {
|
if (check(NUMBER)) {
|
||||||
advance();
|
advance();
|
||||||
let number = parseFloat(previous().literal);
|
let number = parseFloat(previous().literal);
|
||||||
return {accept: (visitor) => visitor.visitLiteralExpr(number)};
|
return {class: "number", value:number, accept: (visitor) => visitor.visitLiteralExpr(number)};
|
||||||
}
|
}
|
||||||
if (check(STRING)) {
|
if (check(STRING)) {
|
||||||
advance();
|
advance();
|
||||||
let string = previous().literal;
|
let string = previous().literal;
|
||||||
return {accept: (visitor) => visitor.visitLiteralExpr(string)};
|
return {class: "string", value: string, accept: (visitor) => visitor.visitLiteralExpr(string)};
|
||||||
}
|
}
|
||||||
if (match(IDENTIFIER)) {
|
if (match(IDENTIFIER)) {
|
||||||
let identifier = previous();
|
let identifier = previous();
|
||||||
return {class: "Variable", accept: (visitor) => visitor.visitVariableExpr(identifier)};
|
return {class: "Variable", identifier: identifier, accept: (visitor) => visitor.visitVariableExpr(identifier)};
|
||||||
}
|
}
|
||||||
if (match(LEFT_PAREN)) {
|
if (match(LEFT_PAREN)) {
|
||||||
let expr = expression();
|
let expr = expression();
|
||||||
consume(RIGHT_PAREN, "Expect ')' after expression.");
|
consume(RIGHT_PAREN, "Expect ')' after expression.");
|
||||||
return {accept: (visitor) => visitor.visitGroupingExpr(expr)};
|
return {class: "groupExpr", expression: expr, accept: (visitor) => visitor.visitGroupingExpr(expr)};
|
||||||
}
|
}
|
||||||
throw error(peek(), "Expect expression.");
|
throw error(peek(), "Expect expression.");
|
||||||
}
|
}
|
||||||
|
|
@ -225,10 +268,12 @@ export function parse(code) {
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
const check = (type) => {
|
const check = (type) => {
|
||||||
|
// console.log(peek().type+"==="+type);
|
||||||
if (is_at_end()) {
|
if (is_at_end()) {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -241,6 +241,7 @@ export const RIGHT = 41;
|
||||||
export const PILLARS = 42;
|
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 keywords = new Map([
|
export const keywords = new Map([
|
||||||
["and", AND],
|
["and", AND],
|
||||||
|
|
@ -262,5 +263,6 @@ export const keywords = new Map([
|
||||||
["right", RIGHT],
|
["right", RIGHT],
|
||||||
["pillars", PILLARS],
|
["pillars", PILLARS],
|
||||||
["moving_pillars", MOVING_PILLARS],
|
["moving_pillars", MOVING_PILLARS],
|
||||||
["staircase", STAIRCASE]
|
["staircase", STAIRCASE],
|
||||||
|
["repeat", REPEAT],
|
||||||
])
|
])
|
||||||
|
|
|
||||||
|
|
@ -1,5 +1,5 @@
|
||||||
{
|
{
|
||||||
"name": " ",
|
"name": "szpakowski-lang",
|
||||||
"version": "0.0.1",
|
"version": "0.0.1",
|
||||||
"description": "",
|
"description": "",
|
||||||
"private": true,
|
"private": true,
|
||||||
|
|
|
||||||
65
szpaks/wobble_block.szpak
Normal file
65
szpaks/wobble_block.szpak
Normal file
|
|
@ -0,0 +1,65 @@
|
||||||
|
start(0, 20);
|
||||||
|
go(1);
|
||||||
|
staircase(2, 1, DOWN);
|
||||||
|
go(2);
|
||||||
|
left(90);
|
||||||
|
go(1);
|
||||||
|
left(90);
|
||||||
|
staircase(2, 1, DOWN);
|
||||||
|
go(1);
|
||||||
|
right(90);
|
||||||
|
go(6);
|
||||||
|
right(90);
|
||||||
|
go(1);
|
||||||
|
right(90);
|
||||||
|
go(5);
|
||||||
|
left(90);
|
||||||
|
staircase(2, 1, DOWN);
|
||||||
|
go(1);
|
||||||
|
right(90);
|
||||||
|
go(2);
|
||||||
|
left(90);
|
||||||
|
go(1);
|
||||||
|
left(90);
|
||||||
|
go(3);
|
||||||
|
left(90);
|
||||||
|
staircase(2, 1, DOWN);
|
||||||
|
go(1);
|
||||||
|
right(90);
|
||||||
|
go(4);
|
||||||
|
right(90);
|
||||||
|
go(1);
|
||||||
|
right(90);
|
||||||
|
go(3);
|
||||||
|
left(90);
|
||||||
|
staircase(2, 1, DOWN);
|
||||||
|
go(1);
|
||||||
|
right(90);
|
||||||
|
go(4);
|
||||||
|
left(90);
|
||||||
|
go(1);
|
||||||
|
left(90);
|
||||||
|
go(5);
|
||||||
|
left(90);
|
||||||
|
staircase(2, 1, DOWN);
|
||||||
|
go(1);
|
||||||
|
right(90);
|
||||||
|
go(2);
|
||||||
|
right(90);
|
||||||
|
go(1);
|
||||||
|
right(90);
|
||||||
|
go(1);
|
||||||
|
left(90);
|
||||||
|
staircase(2, 1, DOWN);
|
||||||
|
go(1);
|
||||||
|
right(90);
|
||||||
|
go(6);
|
||||||
|
left(90);
|
||||||
|
go(1);
|
||||||
|
left(90);
|
||||||
|
go(7);
|
||||||
|
left(90);
|
||||||
|
staircase(2, 1, DOWN);
|
||||||
|
go(1);
|
||||||
|
right(90);
|
||||||
|
go(3);
|
||||||
Loading…
Add table
Reference in a new issue