Compare commits
22 commits
Author | SHA1 | Date | |
---|---|---|---|
![]() |
a502abf051 | ||
![]() |
ab353dc594 | ||
![]() |
d06c2c6078 | ||
![]() |
e53ad45cd4 | ||
![]() |
097931d292 | ||
![]() |
ee6fc7b02d | ||
![]() |
b43f547ae5 | ||
![]() |
e82de580af | ||
![]() |
f105e32af5 | ||
![]() |
74527f68d6 | ||
![]() |
01165d43fb | ||
![]() |
253513f62b | ||
![]() |
0b637d6870 | ||
![]() |
79c54f3c0d | ||
![]() |
d01cf5790b | ||
![]() |
2558a548b3 | ||
![]() |
04b3c0c2ca | ||
![]() |
191cc6af59 | ||
![]() |
ff9e140f1e | ||
![]() |
6526241f5e | ||
![]() |
8140a02305 | ||
![]() |
ecdac9bfa8 |
|
@ -1,3 +1,10 @@
|
||||||
point(3 | 4)
|
point(200, 300) -> A
|
||||||
|
point(400, 300) -> B
|
||||||
|
[point(300, 128) -> C]
|
||||||
|
|
||||||
point(6 | 7)
|
line(A, B) -> AB
|
||||||
|
line(B, C)
|
||||||
|
line(C, A)
|
||||||
|
|
||||||
|
circle(A, len(AB))
|
||||||
|
circle(B, len(AB))
|
9
examples/test.gs.disable
Normal file
9
examples/test.gs.disable
Normal file
|
@ -0,0 +1,9 @@
|
||||||
|
point(-1, 0) -> A
|
||||||
|
point(1, 0) -> B
|
||||||
|
line(A, B) -> AB
|
||||||
|
len(AB) -> length
|
||||||
|
circle(A, len(AB)) -> circleA
|
||||||
|
circle(B, len(AB)) -> circleB
|
||||||
|
intersection(circleA, circleB, 0) -> C
|
||||||
|
line(A, C)
|
||||||
|
line(B, C)
|
|
@ -1,16 +1,31 @@
|
||||||
# Syntax
|
# Syntax
|
||||||
|
|
||||||
```
|
```
|
||||||
point(3 | 4) -> A
|
[point(3, 4) -> A]
|
||||||
point(6 | 7) -> B
|
point(6, 7) -> B
|
||||||
|
|
||||||
line(A, B) -> AB
|
line[color=red, weight=4](A, B) -> AB
|
||||||
line(0 | 0, 100 | 100)
|
line(point(0, 0), point(100, 100))
|
||||||
|
|
||||||
circle(A, len(AB))
|
circle(A, len(AB))
|
||||||
```
|
```
|
||||||
|
|
||||||
## Primitives
|
## Behaviour
|
||||||
* `Point point(x, y)` is a 2D point. It returns an element of type `Point`
|
Every line is one instruction. It is possible to assign instructions names to re-use them later.
|
||||||
* `Line line(Point from, Point to)` is a straight line. It returns an element of type `Line`.
|
These variables are immutable. Objects do not exist in this script, in fact, variables are more similar to C-style macros than actual variables.
|
||||||
* `Circle circle(Point center, radius)` draws a circle at `center` and `radius`
|
|
||||||
|
Lines in brackets `[]` are "hidden". They are parsed, but will not be rendered.
|
||||||
|
|
||||||
|
It is possible to add an optional set of parameters in front of each parameter list, in order to specify the appearance of the element.
|
||||||
|
|
||||||
|
## Primitives vs Functions
|
||||||
|
Primitives (e.g. `point`, `line`) and Functions (e.g. `len`, `intersection`) are syntactically indistinguishable.
|
||||||
|
Both can be used as parameters or instructions and can be assigned to variables. The only difference is that Primitives generate a visual output (unless they are surrounded by square brackets)
|
||||||
|
|
||||||
|
## Grammar
|
||||||
|
```
|
||||||
|
instruction ::= identifier({parameter, }) [-> identifer]
|
||||||
|
parameter ::= instruction | identifier | number
|
||||||
|
identifier ::= (A-Za-z)
|
||||||
|
number ::= (0-9)[.(0-9)]
|
||||||
|
```
|
||||||
|
|
9
src/doc/todo.md
Normal file
9
src/doc/todo.md
Normal file
|
@ -0,0 +1,9 @@
|
||||||
|
# TODO (Parser)
|
||||||
|
* Type checking
|
||||||
|
* Ignore case in instruction names
|
||||||
|
* Implement remaining functions
|
||||||
|
* Abort parsing on error
|
||||||
|
|
||||||
|
# TODO (Renderer)
|
||||||
|
* Implement shape classes
|
||||||
|
* Render shape classes
|
|
@ -1,10 +1,11 @@
|
||||||
import { Vector2D } from "./vector.js"
|
/// <reference path="vector.ts" />
|
||||||
import * as shape from "./shapes.js"
|
/// <reference path="gfx/polygon.ts" />
|
||||||
|
/// <reference path="parser/parser.ts" />
|
||||||
|
|
||||||
function loadScript(filepath: string): string
|
function loadScript(filepath: string): string
|
||||||
{
|
{
|
||||||
var result = null;
|
let result = null;
|
||||||
var xmlhttp = new XMLHttpRequest();
|
let xmlhttp = new XMLHttpRequest();
|
||||||
xmlhttp.open("GET", filepath, false);
|
xmlhttp.open("GET", filepath, false);
|
||||||
xmlhttp.send();
|
xmlhttp.send();
|
||||||
if (xmlhttp.status==200) {
|
if (xmlhttp.status==200) {
|
||||||
|
@ -16,6 +17,7 @@ function loadScript(filepath: string): string
|
||||||
|
|
||||||
class Geometry extends HTMLElement
|
class Geometry extends HTMLElement
|
||||||
{
|
{
|
||||||
|
private shapes: Shape[];
|
||||||
private canvas: HTMLCanvasElement;
|
private canvas: HTMLCanvasElement;
|
||||||
private context: CanvasRenderingContext2D;
|
private context: CanvasRenderingContext2D;
|
||||||
private sourceFile: string;
|
private sourceFile: string;
|
||||||
|
@ -23,7 +25,7 @@ class Geometry extends HTMLElement
|
||||||
constructor()
|
constructor()
|
||||||
{
|
{
|
||||||
super();
|
super();
|
||||||
|
console.log("constructor")
|
||||||
if(!this.hasAttribute("src"))
|
if(!this.hasAttribute("src"))
|
||||||
{
|
{
|
||||||
return;
|
return;
|
||||||
|
@ -32,37 +34,16 @@ class Geometry extends HTMLElement
|
||||||
let sourceFile = this.getAttribute("src");
|
let sourceFile = this.getAttribute("src");
|
||||||
let content = loadScript(sourceFile);
|
let content = loadScript(sourceFile);
|
||||||
|
|
||||||
let lines = content.split("\n");
|
let parser = new Parser(content);
|
||||||
for(let line of lines)
|
if(!parser.good())
|
||||||
{
|
{
|
||||||
if(line === "\r")
|
console.error("Failed to create parser for script " + sourceFile);
|
||||||
{
|
return;
|
||||||
console.log("empty");
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
|
|
||||||
let instruction = line.split("(")[0];
|
|
||||||
|
|
||||||
switch(instruction)
|
|
||||||
{
|
|
||||||
case instruction:
|
|
||||||
{
|
|
||||||
let coords = line.split("(")[1].split("|");
|
|
||||||
console.log(coords);
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
|
|
||||||
default:
|
|
||||||
{
|
|
||||||
console.log("something else");
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
this.attachShadow({mode: "open"});
|
this.attachShadow({mode: "open"});
|
||||||
let canvas = document.createElement("canvas");
|
let canvas = document.createElement("canvas");
|
||||||
canvas.width = 500;
|
canvas.width = 700;
|
||||||
canvas.height = 500;
|
canvas.height = 500;
|
||||||
let context = canvas.getContext("2d");
|
let context = canvas.getContext("2d");
|
||||||
|
|
||||||
|
@ -71,13 +52,38 @@ class Geometry extends HTMLElement
|
||||||
|
|
||||||
this.shadowRoot.append(this.canvas);
|
this.shadowRoot.append(this.canvas);
|
||||||
|
|
||||||
|
|
||||||
|
this.shapes = []
|
||||||
|
for(let instruction of parser.instructions)
|
||||||
|
{
|
||||||
|
let value = instruction.eval();
|
||||||
|
switch(instruction.getType())
|
||||||
|
{
|
||||||
|
case InstructionType.Line:
|
||||||
|
{
|
||||||
|
console.log("New line " + value)
|
||||||
|
this.shapes.push(new Line(this.context, new Vector2D(value[0].x, value[0].y), new Vector2D(value[1].x, value[1].y)));
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
case InstructionType.Circle:
|
||||||
|
{
|
||||||
|
console.log("New circle " + value)
|
||||||
|
this.shapes.push(new Circle(this.context, new Vector2D(value[0].x, value[0].y), value[1]));
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
this.redraw();
|
this.redraw();
|
||||||
}
|
}
|
||||||
|
|
||||||
private redraw()
|
private redraw()
|
||||||
{
|
{
|
||||||
shape.line(this.context, new Vector2D(), new Vector2D(300, 300));
|
for (let shape of this.shapes)
|
||||||
shape.circle(this.context, new Vector2D(150, 150), 100);
|
{
|
||||||
|
shape.draw()
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
30
src/gfx/polygon.ts
Normal file
30
src/gfx/polygon.ts
Normal file
|
@ -0,0 +1,30 @@
|
||||||
|
/// <reference path="./shapes.ts" />
|
||||||
|
|
||||||
|
class Polygon extends Shape
|
||||||
|
{
|
||||||
|
private points: Vector2D[]
|
||||||
|
|
||||||
|
|
||||||
|
constructor(ctx, points) {
|
||||||
|
super(ctx);
|
||||||
|
if (points.length <3)
|
||||||
|
{
|
||||||
|
console.error("cant draw polygon, need min 3 points")
|
||||||
|
}
|
||||||
|
this.points = points
|
||||||
|
}
|
||||||
|
|
||||||
|
public draw()
|
||||||
|
{
|
||||||
|
let last_element = this.points[this.points.length-1]
|
||||||
|
this.ctx.beginPath();
|
||||||
|
this.ctx.moveTo(last_element.x, last_element.y);
|
||||||
|
for (let point of this.points)
|
||||||
|
{
|
||||||
|
this.ctx.lineTo(point.x, point.y);
|
||||||
|
}
|
||||||
|
this.ctx.lineWidth = this.style.strokeWidth;
|
||||||
|
this.ctx.strokeStyle = this.style.strokeColor;
|
||||||
|
this.ctx.stroke();
|
||||||
|
}
|
||||||
|
}
|
64
src/gfx/shapes.ts
Normal file
64
src/gfx/shapes.ts
Normal file
|
@ -0,0 +1,64 @@
|
||||||
|
/// <reference path="../vector.ts" />
|
||||||
|
/// <reference path="../shapeStyle.ts" />
|
||||||
|
|
||||||
|
abstract class Shape
|
||||||
|
{
|
||||||
|
protected ctx: CanvasRenderingContext2D
|
||||||
|
protected style: ShapeStyle
|
||||||
|
|
||||||
|
constructor(ctx) {
|
||||||
|
this.ctx = ctx
|
||||||
|
this.style = new ShapeStyle()
|
||||||
|
}
|
||||||
|
|
||||||
|
abstract draw()
|
||||||
|
}
|
||||||
|
|
||||||
|
class Line extends Shape
|
||||||
|
{
|
||||||
|
private from: Vector2D
|
||||||
|
private to: Vector2D
|
||||||
|
|
||||||
|
constructor(ctx,from, to) {
|
||||||
|
super(ctx)
|
||||||
|
this.from = from
|
||||||
|
this.to = to
|
||||||
|
}
|
||||||
|
|
||||||
|
public draw()
|
||||||
|
{
|
||||||
|
this.ctx.beginPath();
|
||||||
|
this.ctx.moveTo(this.from.x, this.from.y);
|
||||||
|
this.ctx.lineTo(this.to.x, this.to.y);
|
||||||
|
|
||||||
|
this.ctx.lineWidth = this.style.strokeWidth;
|
||||||
|
this.ctx.strokeStyle = this.style.strokeColor;
|
||||||
|
this.ctx.stroke();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
class Circle extends Shape
|
||||||
|
{
|
||||||
|
|
||||||
|
private center: Vector2D
|
||||||
|
private radius: number
|
||||||
|
|
||||||
|
constructor(ctx,center, radius) {
|
||||||
|
super(ctx)
|
||||||
|
this.center = center
|
||||||
|
this.radius = radius
|
||||||
|
}
|
||||||
|
|
||||||
|
public draw()
|
||||||
|
{
|
||||||
|
this.ctx.beginPath();
|
||||||
|
this.ctx.arc(this.center.x, this.center.y, this.radius, 0, 2 * Math.PI, false);
|
||||||
|
|
||||||
|
this.ctx.fillStyle = this.style.fillColor;
|
||||||
|
this.ctx.fill();
|
||||||
|
|
||||||
|
this.ctx.lineWidth = this.style.strokeWidth;
|
||||||
|
this.ctx.strokeStyle = this.style.strokeColor;
|
||||||
|
this.ctx.stroke();
|
||||||
|
}
|
||||||
|
}
|
88
src/parser/instruction.ts
Normal file
88
src/parser/instruction.ts
Normal file
|
@ -0,0 +1,88 @@
|
||||||
|
/// <reference path="../vector.ts" />
|
||||||
|
|
||||||
|
enum InstructionType
|
||||||
|
{
|
||||||
|
Point,
|
||||||
|
Line,
|
||||||
|
Circle,
|
||||||
|
Length
|
||||||
|
}
|
||||||
|
|
||||||
|
abstract class Instruction
|
||||||
|
{
|
||||||
|
private type: InstructionType;
|
||||||
|
public params :Parameter[];
|
||||||
|
private argc: number;
|
||||||
|
|
||||||
|
constructor(type: InstructionType, argc: number)
|
||||||
|
{
|
||||||
|
this.type = type;
|
||||||
|
this.argc = argc;
|
||||||
|
this.params = [];
|
||||||
|
}
|
||||||
|
|
||||||
|
abstract eval();
|
||||||
|
public getParameterCount(): number { return this.argc; }
|
||||||
|
public getType(): InstructionType { return this.type; }
|
||||||
|
}
|
||||||
|
|
||||||
|
class PointInstruction extends Instruction
|
||||||
|
{
|
||||||
|
constructor()
|
||||||
|
{
|
||||||
|
super(InstructionType.Point, 2);
|
||||||
|
}
|
||||||
|
|
||||||
|
eval()
|
||||||
|
{
|
||||||
|
return new Vector2D(this.params[0].eval(), this.params[1].eval());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
class LineInstruction extends Instruction
|
||||||
|
{
|
||||||
|
constructor()
|
||||||
|
{
|
||||||
|
super(InstructionType.Line, 2);
|
||||||
|
}
|
||||||
|
|
||||||
|
eval()
|
||||||
|
{
|
||||||
|
return [
|
||||||
|
this.params[0].eval(),
|
||||||
|
this.params[1].eval()
|
||||||
|
];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
class CircleInstruction extends Instruction
|
||||||
|
{
|
||||||
|
constructor()
|
||||||
|
{
|
||||||
|
super(InstructionType.Circle, 2);
|
||||||
|
}
|
||||||
|
|
||||||
|
eval()
|
||||||
|
{
|
||||||
|
return [
|
||||||
|
this.params[0].eval(),
|
||||||
|
this.params[1].eval()
|
||||||
|
];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
class LengthInstruction extends Instruction
|
||||||
|
{
|
||||||
|
constructor()
|
||||||
|
{
|
||||||
|
super(InstructionType.Line, 1);
|
||||||
|
}
|
||||||
|
|
||||||
|
eval()
|
||||||
|
{
|
||||||
|
let line = this.params[0].eval();
|
||||||
|
let dx = line[1].x - line[0].x;
|
||||||
|
let dy = line[1].y - line[0].y;
|
||||||
|
return Math.sqrt(Math.pow(dx, 2) + Math.pow(dy, 2));
|
||||||
|
}
|
||||||
|
}
|
19
src/parser/instructionFactory.ts
Normal file
19
src/parser/instructionFactory.ts
Normal file
|
@ -0,0 +1,19 @@
|
||||||
|
/// <reference path="instruction.ts" />
|
||||||
|
|
||||||
|
abstract class InstructionFactory
|
||||||
|
{
|
||||||
|
private static symbolDict: { [name: string] : Function } = {
|
||||||
|
"point": (): PointInstruction => { return new PointInstruction(); },
|
||||||
|
"line": (): LineInstruction => { return new LineInstruction(); },
|
||||||
|
"circle": (): CircleInstruction => { return new CircleInstruction(); },
|
||||||
|
"len": (): LengthInstruction => { return new LengthInstruction(); }
|
||||||
|
}
|
||||||
|
|
||||||
|
public static createInstruction(name: string): Instruction
|
||||||
|
{
|
||||||
|
if(!(name in InstructionFactory.symbolDict))
|
||||||
|
return null;
|
||||||
|
|
||||||
|
return InstructionFactory.symbolDict[name]();
|
||||||
|
}
|
||||||
|
}
|
52
src/parser/parameter.ts
Normal file
52
src/parser/parameter.ts
Normal file
|
@ -0,0 +1,52 @@
|
||||||
|
enum ParameterType
|
||||||
|
{
|
||||||
|
Instruction,
|
||||||
|
Identifier,
|
||||||
|
Number
|
||||||
|
}
|
||||||
|
|
||||||
|
abstract class Parameter
|
||||||
|
{
|
||||||
|
public type: ParameterType;
|
||||||
|
|
||||||
|
constructor(type: ParameterType)
|
||||||
|
{
|
||||||
|
this.type = type;
|
||||||
|
}
|
||||||
|
|
||||||
|
abstract eval();
|
||||||
|
}
|
||||||
|
|
||||||
|
class NumberParameter extends Parameter
|
||||||
|
{
|
||||||
|
public val: number;
|
||||||
|
|
||||||
|
constructor(val: number)
|
||||||
|
{
|
||||||
|
super(ParameterType.Number);
|
||||||
|
|
||||||
|
this.val = val;
|
||||||
|
}
|
||||||
|
|
||||||
|
eval()
|
||||||
|
{
|
||||||
|
return this.val;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
class InstructionParameter extends Parameter
|
||||||
|
{
|
||||||
|
public instr: Instruction;
|
||||||
|
|
||||||
|
constructor(instr: Instruction)
|
||||||
|
{
|
||||||
|
super(ParameterType.Identifier);
|
||||||
|
|
||||||
|
this.instr = instr;
|
||||||
|
}
|
||||||
|
|
||||||
|
eval()
|
||||||
|
{
|
||||||
|
return this.instr.eval();
|
||||||
|
}
|
||||||
|
}
|
166
src/parser/parser.ts
Normal file
166
src/parser/parser.ts
Normal file
|
@ -0,0 +1,166 @@
|
||||||
|
/// <reference path="parameter.ts" />
|
||||||
|
/// <reference path="instructionFactory.ts" />
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Turns a .gs script into a list of instructions to be passed to the renderer
|
||||||
|
*/
|
||||||
|
class Parser
|
||||||
|
{
|
||||||
|
public instructions: Instruction[];
|
||||||
|
private macros: { [id: string] : InstructionParameter};
|
||||||
|
private success: boolean;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Parses each line of the script and turns them into instructions or adds them to the macro list
|
||||||
|
*
|
||||||
|
* @param source The source code of the script
|
||||||
|
*/
|
||||||
|
constructor(source: string)
|
||||||
|
{
|
||||||
|
this.instructions = [];
|
||||||
|
this.macros = {};
|
||||||
|
this.success = false;
|
||||||
|
|
||||||
|
let lines = source.split(/\r?\n/);
|
||||||
|
let currentLine = 1;
|
||||||
|
for(let line of lines)
|
||||||
|
{
|
||||||
|
let instr;
|
||||||
|
try
|
||||||
|
{
|
||||||
|
instr = this.parseInstruction(line);
|
||||||
|
}
|
||||||
|
catch(e)
|
||||||
|
{
|
||||||
|
console.error("Error in line " + currentLine);
|
||||||
|
console.error(e);
|
||||||
|
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// If the instruction is null it means that the instruction was a function and not a primitive
|
||||||
|
if(instr !== null)
|
||||||
|
if(!instr[0])
|
||||||
|
this.instructions.push(instr[1]);
|
||||||
|
|
||||||
|
currentLine++;
|
||||||
|
}
|
||||||
|
|
||||||
|
this.success = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
public good() { return this.success; }
|
||||||
|
|
||||||
|
private parseInstruction(instruction: string): [boolean, Instruction]
|
||||||
|
{
|
||||||
|
// If the instruction is an empty line, do nothing for now
|
||||||
|
if(instruction === "")
|
||||||
|
return null;
|
||||||
|
|
||||||
|
// Handle [] syntax. Lines in [] will be processed but not rendered
|
||||||
|
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
|
||||||
|
throw new Error("Line does not contain a valid instruction.");
|
||||||
|
|
||||||
|
if(matches.length > 1) // more than one match
|
||||||
|
throw new Error("Line may only contain one instruction");
|
||||||
|
|
||||||
|
let instr = matches[0]; // get the instruction
|
||||||
|
let paranthesisPos = instr.search(/\(/); // Find the position of the first opening paranthesis
|
||||||
|
|
||||||
|
|
||||||
|
let symbol = instr.substr(0, paranthesisPos); // get function name
|
||||||
|
let paramlist = instr.substring(paranthesisPos + 1, instr.length - 1); // get parameter list
|
||||||
|
|
||||||
|
// Construct the parameter list
|
||||||
|
let match;
|
||||||
|
let params = [];
|
||||||
|
while((match = paramlist.search(/,(?![^\(]*\))/)) !== -1)
|
||||||
|
{
|
||||||
|
params.push(paramlist.substring(0, match));
|
||||||
|
paramlist = paramlist.substring(match + 1, paramlist.length);
|
||||||
|
}
|
||||||
|
params.push(paramlist);
|
||||||
|
|
||||||
|
// Create appropriate instruction
|
||||||
|
let newInstruction = InstructionFactory.createInstruction(symbol);
|
||||||
|
if(newInstruction === null)
|
||||||
|
throw new Error("Unknown instruction: \"" + symbol + "\"");
|
||||||
|
|
||||||
|
// Check that the number of arguments passed to the function is correct
|
||||||
|
let expectedArgs = newInstruction.getParameterCount();
|
||||||
|
if(expectedArgs !== params.length)
|
||||||
|
throw new Error("Wrong number of arguments for instruction \"" + symbol + "\". Expected " + expectedArgs + " arguments but received " + params.length + " instead.");
|
||||||
|
|
||||||
|
// Parse the individual parameters
|
||||||
|
for(let param of params)
|
||||||
|
{
|
||||||
|
if(!this.parseParameter(newInstruction, param))
|
||||||
|
throw new Error("Error during parameter parsing: \"" + param + "\" failed to be parsed.");
|
||||||
|
}
|
||||||
|
|
||||||
|
// In case there is an assignment, add the instruction to the macro list
|
||||||
|
let assignment = instruction.search(/->/);
|
||||||
|
if(assignment !== -1)
|
||||||
|
{
|
||||||
|
let variableName = instruction.substring(assignment + 2, instruction.length);
|
||||||
|
if(variableName in this.macros)
|
||||||
|
throw new Error("Redefinition of variable \"" + variableName + "\" is not allowed.");
|
||||||
|
|
||||||
|
this.macros[variableName] = new InstructionParameter(newInstruction);
|
||||||
|
}
|
||||||
|
|
||||||
|
return [hidden, newInstruction];
|
||||||
|
}
|
||||||
|
|
||||||
|
private parseParameter(instr: Instruction, parameter: string): boolean
|
||||||
|
{
|
||||||
|
// Parameter is a number
|
||||||
|
let match = parameter.match(/-?\d*\.?\d*$/);
|
||||||
|
if(match !== null && match[0] === parameter && match.index === 0)
|
||||||
|
{
|
||||||
|
let val = parseFloat(parameter);
|
||||||
|
let paramObj = new NumberParameter(val);
|
||||||
|
|
||||||
|
instr.params.push(paramObj);
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Parameter is an identifier (macro)
|
||||||
|
match = parameter.match(/[A-Za-z]*/)
|
||||||
|
if(match !== null && match[0] === parameter && match.index === 0)
|
||||||
|
{
|
||||||
|
let paramObj = this.macros[parameter];
|
||||||
|
if(paramObj === undefined)
|
||||||
|
{
|
||||||
|
console.error("Variable \"" + parameter + "\" is not defined");
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
instr.params.push(paramObj);
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Parameter is another instruction
|
||||||
|
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;
|
||||||
|
}
|
||||||
|
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
|
@ -1,5 +1,4 @@
|
||||||
|
class ShapeStyle
|
||||||
export class ShapeStyle
|
|
||||||
{
|
{
|
||||||
public strokeWidth: number;
|
public strokeWidth: number;
|
||||||
public strokeColor: string;
|
public strokeColor: string;
|
||||||
|
|
|
@ -1,26 +0,0 @@
|
||||||
import { Vector2D } from "./vector.js"
|
|
||||||
import { ShapeStyle } from "./shapeStyle.js";
|
|
||||||
|
|
||||||
export function line(ctx: CanvasRenderingContext2D, from: Vector2D , to: Vector2D, style: ShapeStyle = new ShapeStyle())
|
|
||||||
{
|
|
||||||
ctx.beginPath();
|
|
||||||
ctx.moveTo(from.x, from.y);
|
|
||||||
ctx.lineTo(to.x, to.y);
|
|
||||||
|
|
||||||
ctx.lineWidth = style.strokeWidth;
|
|
||||||
ctx.strokeStyle = style.strokeColor;
|
|
||||||
ctx.stroke();
|
|
||||||
}
|
|
||||||
|
|
||||||
export function circle(ctx: CanvasRenderingContext2D, center: Vector2D, radius: number, style: ShapeStyle = new ShapeStyle())
|
|
||||||
{
|
|
||||||
ctx.beginPath();
|
|
||||||
ctx.arc(center.x, center.y, radius, 0, 2 * Math.PI, false);
|
|
||||||
|
|
||||||
ctx.fillStyle = style.fillColor;
|
|
||||||
ctx.fill();
|
|
||||||
|
|
||||||
ctx.lineWidth = style.strokeWidth;
|
|
||||||
ctx.strokeStyle = style.strokeColor;
|
|
||||||
ctx.stroke();
|
|
||||||
}
|
|
|
@ -1,4 +1,4 @@
|
||||||
export class Vector2D
|
class Vector2D
|
||||||
{
|
{
|
||||||
public x: number;
|
public x: number;
|
||||||
public y: number;
|
public y: number;
|
||||||
|
|
|
@ -1,8 +1,7 @@
|
||||||
{
|
{
|
||||||
"compilerOptions": {
|
"compilerOptions": {
|
||||||
"target": "es6",
|
"target": "es6",
|
||||||
// "outFile": "./out/lauchpioos.js",
|
"outFile": "./out/geometry.js",
|
||||||
"outDir": "./out",
|
|
||||||
"sourceRoot": "./src",
|
"sourceRoot": "./src",
|
||||||
"rootDir": "./src",
|
"rootDir": "./src",
|
||||||
"sourceMap": true
|
"sourceMap": true
|
||||||
|
|
Loading…
Reference in a new issue