bugfixes and looping

This commit is contained in:
Shautvast 2024-12-07 22:51:55 +01:00
parent 45f31a92de
commit 2de4cac1af
6 changed files with 199 additions and 48 deletions

View file

@ -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) {
} }
} }

View file

@ -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,27 +74,43 @@ 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));
}, },
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;
} }
}, },
start: (x,y) => { start: (x, y) => {
tx = x * unit; tx = x * unit;
ty = y * unit; ty = y * unit;
tangle = 0; tangle = 0;
@ -129,7 +169,7 @@ export function interpret(init_env, code) {
} }
}, },
staircase: (number_of_steps, size, direction = DOWN) =>{ staircase: (number_of_steps, size, direction = DOWN) => {
const angle = direction === DOWN ? 90 : -90; const angle = direction === DOWN ? 90 : -90;
for (let i = 0; i < number_of_steps; i++) { for (let i = 0; i < number_of_steps; i++) {
THIS.go(size); THIS.go(size);
@ -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) {

View file

@ -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;
} }

View file

@ -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],
]) ])

View file

@ -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
View 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);