Merge branch 'dev' into tom
# Conflicts: # src/shapes.ts
This commit is contained in:
commit
e53ad45cd4
|
@ -1,3 +1,2 @@
|
||||||
point(3 | 4)
|
[point(3, 4) -> A]
|
||||||
|
line(A, point(7, 8))
|
||||||
point(6 | 7)
|
|
|
@ -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)]
|
||||||
|
```
|
||||||
|
|
|
@ -1,10 +1,11 @@
|
||||||
import { Vector2D } from "./vector.js"
|
/// <reference path="vector.ts" />
|
||||||
import * as shape from "./shapes.js"
|
/// <reference path="shapes.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) {
|
||||||
|
@ -23,7 +24,7 @@ class Geometry extends HTMLElement
|
||||||
constructor()
|
constructor()
|
||||||
{
|
{
|
||||||
super();
|
super();
|
||||||
|
console.log("constructor")
|
||||||
if(!this.hasAttribute("src"))
|
if(!this.hasAttribute("src"))
|
||||||
{
|
{
|
||||||
return;
|
return;
|
||||||
|
@ -32,32 +33,10 @@ 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)
|
for(let instr of parser.instructions)
|
||||||
{
|
{
|
||||||
if(line === "\r")
|
console.log(instr.eval());
|
||||||
{
|
|
||||||
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"});
|
||||||
|
@ -76,8 +55,8 @@ class Geometry extends HTMLElement
|
||||||
|
|
||||||
private redraw()
|
private redraw()
|
||||||
{
|
{
|
||||||
shape.line(this.context, new Vector2D(), new Vector2D(300, 300));
|
line(this.context, new Vector2D(), new Vector2D(300, 300));
|
||||||
shape.circle(this.context, new Vector2D(150, 150), 100);
|
circle(this.context, new Vector2D(150, 150), 100);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
51
src/parser/instruction.ts
Normal file
51
src/parser/instruction.ts
Normal file
|
@ -0,0 +1,51 @@
|
||||||
|
/// <reference path="../vector.ts" />
|
||||||
|
|
||||||
|
enum InstructionType
|
||||||
|
{
|
||||||
|
Point,
|
||||||
|
Line,
|
||||||
|
Circle
|
||||||
|
}
|
||||||
|
|
||||||
|
abstract class Instruction
|
||||||
|
{
|
||||||
|
public fn: InstructionType;
|
||||||
|
public params :Parameter[];
|
||||||
|
|
||||||
|
constructor(type: InstructionType)
|
||||||
|
{
|
||||||
|
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()
|
||||||
|
];
|
||||||
|
}
|
||||||
|
}
|
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();
|
||||||
|
}
|
||||||
|
}
|
153
src/parser/parser.ts
Normal file
153
src/parser/parser.ts
Normal file
|
@ -0,0 +1,153 @@
|
||||||
|
/// <reference path="parameter.ts" />
|
||||||
|
/// <reference path="instruction.ts" />
|
||||||
|
|
||||||
|
class Parser
|
||||||
|
{
|
||||||
|
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)
|
||||||
|
{
|
||||||
|
let instr = this.parseInstruction(line);
|
||||||
|
if(!instr[0])
|
||||||
|
this.instructions.push(instr[1]);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private parseInstruction(instruction: string): [boolean, Instruction]
|
||||||
|
{
|
||||||
|
// If the instruction is an empty line, do nothing for now
|
||||||
|
if(instruction === "")
|
||||||
|
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 null;
|
||||||
|
}
|
||||||
|
|
||||||
|
if(matches.length > 1) // more than one match
|
||||||
|
{
|
||||||
|
console.error("Script may only contain one instruction per line");
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
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
|
||||||
|
|
||||||
|
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);
|
||||||
|
|
||||||
|
let newInstruction;
|
||||||
|
switch(symbol)
|
||||||
|
{
|
||||||
|
case "point":
|
||||||
|
{
|
||||||
|
newInstruction = new PointInstruction();
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
case "line":
|
||||||
|
{
|
||||||
|
newInstruction = new LineInstruction();
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
default:
|
||||||
|
{
|
||||||
|
console.error("Unknown instruction \"" + symbol + "\"");
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
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(instr: Instruction, parameter: string): boolean
|
||||||
|
{
|
||||||
|
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;
|
||||||
|
}
|
||||||
|
|
||||||
|
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;
|
||||||
|
}
|
||||||
|
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
|
@ -1,5 +1,4 @@
|
||||||
|
class ShapeStyle
|
||||||
export class ShapeStyle
|
|
||||||
{
|
{
|
||||||
public strokeWidth: number;
|
public strokeWidth: number;
|
||||||
public strokeColor: string;
|
public strokeColor: string;
|
||||||
|
|
|
@ -1,5 +1,5 @@
|
||||||
import { Vector2D } from "./vector.js"
|
/// <reference path="vector.ts" />
|
||||||
import { ShapeStyle } from "./shapeStyle.js";
|
/// <reference path="shapeStyle.ts" />
|
||||||
|
|
||||||
abstract class Shape
|
abstract class Shape
|
||||||
{
|
{
|
||||||
|
|
|
@ -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