szpakowski-lang/js/parser.js
2024-12-07 23:01:09 +01:00

352 lines
7.9 KiB
JavaScript

import "./scanner";
import {
scan,
error,
BANG,
BANG_EQUAL,
EOF,
EQUAL,
EQUAL_EQUAL,
FALSE,
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";
export function parse(code) {
const tokens = scan(code);
// console.log(tokens);
let current = 0;
const parse = () => {
let statements = [];
while (!is_at_end()) {
try {
statements.push(declaration());
} catch (e) {
console.log(e);
current = tokens.length - 1; // stop compiling
}
}
return statements;
}
const declaration = () => {
if (match(VAR)) {
return varDeclaration();
}
return statement();
}
const varDeclaration = () => {
const name = consume(IDENTIFIER, "Expected a variable name");
let initializer;
if (match(EQUAL)) {
initializer = expression();
}
consume(SEMICOLON, "Expected semicolon");
return {
class: "var",
name: name,
initializer: initializer,
accept: (visitor) => visitor.visitVariableStatement(name, initializer)
};
}
const statement = () => {
if (match(PRINT)) {
return printStatement();
}
if (match(START)) {
return callStatement("start");
}
if (match(GO)) {
return callStatement("go");
}
if (match(LEFT)) {
return callStatement("left");
}
if (match(RIGHT)) {
return callStatement("right");
}
if (match(TURN)) {
return callStatement("turn");
}
if (match(STAIRCASE)) {
return callStatement("staircase");
}
if (match(PILLARS)) {
return callStatement("pillars");
}
if (match(MOVING_PILLARS)) {
return callStatement("moving_pillars");
}
if (match(REPEAT)) {
return call_block();
}
if (match(LEFT_BRACE)) {
const blockStatement = block();
return {class: "block", accept: (visitor) => visitor.visitBlockStatement(blockStatement)};
}
return expressionStatement();
}
const expression = () => {
return assignment();
}
const arg_expressions = () => {
consume(LEFT_PAREN, "Expect '('");
let exprs = [expression()];
while (match(COMMA)) {
exprs.push(expression());
}
consume(RIGHT_PAREN, "Expect ')'");
return exprs;
}
const assignment = () => {
let expr = equality();
if (match(EQUAL)) {
let equals = previous();
let value = assignment();
if (expr.class === 'Variable') {
let name = expr.name;
return {
class: "assign",
var_name: name,
init_expr: value,
accept: (visitor) => visitor.visitAssignExpr(name, value)
};
}
throw error(equals, "Invalid assignment target.");
}
return expr;
}
const printStatement = () => {
const value = expression();
consume(SEMICOLON, "Expected semicolon");
return {class: "print", value: value, accept: (visitor) => visitor.visitPrintStatement(value)};
}
const callStatement = (name) => {
const args = arg_expressions();
consume(SEMICOLON, "Expected semicolon");
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 value = expression();
consume(SEMICOLON, "Expected semicolon");
return {
class: "expressionStatement",
expression: value,
accept: (visitor) => visitor.visitExpressionStatement(value)
};
}
const block = () => {
let statements = [];
while (!check(RIGHT_BRACE) && !is_at_end()) {
statements.push(declaration());
}
consume(RIGHT_BRACE, "Expect '}' after block.");
return statements;
}
const equality = () => {
let expr = comparison();
while (match(BANG_EQUAL, EQUAL_EQUAL)) {
let operator = previous();
let right = comparison();
const left = expr;
expr = {
class: "binaryExpr",
operator: operator,
left: expr,
right: right,
accept: (visitor) => visitor.visitBinaryExpr(operator, left, right)
};
}
return expr;
}
const comparison = () => {
let expr = term();
while (match(GREATER, GREATER_EQUAL, LESS, LESS_EQUAL)) {
let operator = previous();
let right = term();
const left = expr;
expr = {
class: "binaryExpr",
operator: operator,
left: expr,
right: right,
accept: (visitor) => visitor.visitBinaryExpr(operator, left, right)
};
}
return expr;
}
const term = () => {
let expr = factor();
while (match(MINUS, PLUS)) {
let operator = previous();
let right = factor();
const left = expr;
expr = {
class: "binaryExpr",
operator: operator,
left: expr,
right: right,
accept: (visitor) => visitor.visitBinaryExpr(operator, left, right)
};
}
return expr;
}
const factor = () => {
let expr = unary();
while (match(SLASH, STAR)) {
let operator = previous();
let right = unary();
const left = expr;
expr = {
class: "binaryExpr",
operator: operator,
left: expr,
right: right,
accept: (visitor) => visitor.visitBinaryExpr(operator, left, right)
};
}
return expr;
}
const unary = () => {
if (match(BANG, MINUS)) {
let operator = previous();
let right = unary();
return {
class: "unaryExpr",
operator: operator,
right: right,
accept: (visitor) => visitor.visitUnaryExpr(operator, right)
};
}
return primary();
}
const primary = () => {
if (match(FALSE)) {
return {class: "boolean", value: false, accept: (visitor) => visitor.visitLiteralExpr(false)};
}
if (match(TRUE)) {
return {class: "boolean", value: true, accept: (visitor) => visitor.visitLiteralExpr(true)};
}
if (check(NUMBER)) {
advance();
let number = parseFloat(previous().literal);
return {class: "number", value: number, accept: (visitor) => visitor.visitLiteralExpr(number)};
}
if (check(STRING)) {
advance();
let string = previous().literal;
return {class: "string", value: string, accept: (visitor) => visitor.visitLiteralExpr(string)};
}
if (match(IDENTIFIER)) {
let identifier = previous();
return {class: "Variable", identifier: identifier, accept: (visitor) => visitor.visitVariableExpr(identifier)};
}
if (match(LEFT_PAREN)) {
let expr = expression();
consume(RIGHT_PAREN, "Expect ')' after expression.");
return {class: "groupExpr", expression: expr, accept: (visitor) => visitor.visitGroupingExpr(expr)};
}
throw error(peek(), "Expect expression.");
}
const consume = (type, message) => {
if (check(type)) {
return advance();
}
throw error(peek(), message);
}
const match = (...types) => {
for (let i = 0; i < types.length; i++) {
if (check(types[i])) {
advance();
return true;
}
}
return false;
}
const check = (type) => {
// console.log(peek().type+"==="+type);
if (is_at_end()) {
return false;
}
return peek().type === type;
}
const advance = () => {
if (!is_at_end()) {
current++;
}
return previous();
}
const is_at_end = () => {
return peek().type === EOF;
}
const peek = () => {
return tokens[current];
}
const previous = () => {
return tokens[current - 1];
}
return parse();
}