From 79c54f3c0dad6fd8490e8bb70a5b1320c3606a71 Mon Sep 17 00:00:00 2001 From: Lauchmelder Date: Sat, 27 Nov 2021 22:30:20 +0100 Subject: [PATCH] first stable parser --- examples/test.gs | 2 +- src/geometry.ts | 4 ++ src/parser/instruction.ts | 35 +++++++++++- src/parser/parameter.ts | 17 ++++++ src/parser/parser.ts | 110 ++++++++++++++++++++++++++++++++------ 5 files changed, 149 insertions(+), 19 deletions(-) diff --git a/examples/test.gs b/examples/test.gs index 1b5f447..fa68652 100644 --- a/examples/test.gs +++ b/examples/test.gs @@ -1,2 +1,2 @@ -point(3, 4) -> A +[point(3, 4) -> A] line(A, point(7, 8)) \ No newline at end of file diff --git a/src/geometry.ts b/src/geometry.ts index 7443bb0..64da89e 100644 --- a/src/geometry.ts +++ b/src/geometry.ts @@ -34,6 +34,10 @@ class Geometry extends HTMLElement let content = loadScript(sourceFile); let parser = new Parser(content); + for(let instr of parser.instructions) + { + console.log(instr.eval()); + } this.attachShadow({mode: "open"}); let canvas = document.createElement("canvas"); diff --git a/src/parser/instruction.ts b/src/parser/instruction.ts index 1a381f6..67f49c6 100644 --- a/src/parser/instruction.ts +++ b/src/parser/instruction.ts @@ -1,3 +1,5 @@ +/// + enum InstructionType { Point, @@ -5,7 +7,7 @@ enum InstructionType Circle } -class Instruction +abstract class Instruction { public fn: InstructionType; public params :Parameter[]; @@ -15,4 +17,35 @@ class Instruction this.fn = type; this.params = []; } + + abstract eval(); +} + +class PointInstruction extends Instruction +{ + constructor() + { + super(InstructionType.Point); + } + + eval() + { + return new Vector2D(this.params[0].eval(), this.params[1].eval()); + } +} + +class LineInstruction extends Instruction +{ + constructor() + { + super(InstructionType.Line); + } + + eval() + { + return [ + this.params[0].eval(), + this.params[1].eval() + ]; + } } \ No newline at end of file diff --git a/src/parser/parameter.ts b/src/parser/parameter.ts index ec2fd56..efce571 100644 --- a/src/parser/parameter.ts +++ b/src/parser/parameter.ts @@ -33,3 +33,20 @@ class NumberParameter extends Parameter return this.val; } } + +class InstructionParameter extends Parameter +{ + public instr: Instruction; + + constructor(instr: Instruction) + { + super(ParameterType.Identifier); + + this.instr = instr; + } + + eval() + { + return this.instr.eval(); + } +} \ No newline at end of file diff --git a/src/parser/parser.ts b/src/parser/parser.ts index 9f10096..2a74445 100644 --- a/src/parser/parser.ts +++ b/src/parser/parser.ts @@ -3,38 +3,53 @@ class Parser { - private instructions: Instruction[]; + public instructions: Instruction[]; + private variables: { [id: string] : InstructionParameter}; constructor(source: string) { this.instructions = []; + this.variables = {}; + let lines = source.split(/\r?\n/); for(let line of lines) - this.parseInstruction(line); + { + let instr = this.parseInstruction(line); + if(!instr[0]) + this.instructions.push(instr[1]); + } } - private parseInstruction(instruction: string): boolean + private parseInstruction(instruction: string): [boolean, Instruction] { // If the instruction is an empty line, do nothing for now if(instruction === "") - return false; + return null; + let hidden = false; + if(instruction[0] === "[" && instruction[instruction.length - 1] === "]") + { + hidden = true; + instruction = instruction.substring(1, instruction.length - 1); + } + + instruction = instruction.split(" ").join(""); // Remove spaces + // match the pattern "text(text)" let matches = instruction.match(/[A-Za-z]*\(.*\)/); if(matches === null) // no match found { console.error("Invalid syntax"); - return false; + return null; } if(matches.length > 1) // more than one match { console.error("Script may only contain one instruction per line"); - return false; + return null; } let instr = matches[0]; // get the instruction - instr = instr.split(" ").join(""); let paranthesisPos = instr.search(/\(/); // Find the position of the first opening paranthesis @@ -50,25 +65,86 @@ class Parser } params.push(paramlist); - this.instructions.push(new Instruction(InstructionType.Point)); - for(let param of params) + let newInstruction; + switch(symbol) { - if(!this.parseParameter(param)) + case "point": { - console.error("Error during parameter parsing"); - return false; + newInstruction = new PointInstruction(); + break; + } + + case "line": + { + newInstruction = new LineInstruction(); + break; + } + + default: + { + console.error("Unknown instruction \"" + symbol + "\""); + return null; } } - return true; + for(let param of params) + { + if(!this.parseParameter(newInstruction, param)) + { + console.error("Error during parameter parsing: \"" + param + "\" failed to be parsed."); + return null; + } + } + + let assignment = instruction.search(/->/); + if(assignment !== -1) + { + let variableName = instruction.substring(assignment + 2, instruction.length); + + if(variableName in this.variables) + { + console.error("Redefinition of variable \"" + variableName + "\" is not allowed."); + return null; + } + + this.variables[variableName] = new InstructionParameter(newInstruction); + } + + return [hidden, newInstruction]; } - private parseParameter(parameter: string): boolean + private parseParameter(instr: Instruction, parameter: string): boolean { - let match = parameter.search(/\d*\.?\d*$/); - if(match === 0) + let match = parameter.match(/\d*\.?\d*$/); + if(match !== null && match[0] === parameter && match.index === 0) { - this.instructions[this.instructions.length - 1].params.push(new NumberParameter(4)); + let val = parseFloat(parameter); + let paramObj = new NumberParameter(val); + + instr.params.push(paramObj); + return true; + } + + match = parameter.match(/[A-Za-z]/) + if(match !== null && match[0] === parameter && match.index === 0) + { + let paramObj = this.variables[parameter]; + if(paramObj === undefined) + { + console.error("Variable \"" + parameter + "\" is not defined"); + return false; + } + + instr.params.push(paramObj); + return true; + } + + match = parameter.match(/[A-Za-z]*\(.*\)/) + if(match !== null && match[0] === parameter && match.index === 0) + { + let paramObj = new InstructionParameter(this.parseInstruction(parameter)[1]); + + instr.params.push(paramObj); return true; }