Merge branch 'dev' into tom

# Conflicts:
#	src/shapes.ts
This commit is contained in:
epioos 2021-11-30 00:28:44 +01:00
commit e53ad45cd4
10 changed files with 297 additions and 50 deletions

View file

@ -1,3 +1,2 @@
point(3 | 4)
point(6 | 7)
[point(3, 4) -> A]
line(A, point(7, 8))

View file

@ -1,16 +1,31 @@
# Syntax
```
point(3 | 4) -> A
point(6 | 7) -> B
[point(3, 4) -> A]
point(6, 7) -> B
line(A, B) -> AB
line(0 | 0, 100 | 100)
line[color=red, weight=4](A, B) -> AB
line(point(0, 0), point(100, 100))
circle(A, len(AB))
```
## Primitives
* `Point point(x, y)` is a 2D point. It returns an element of type `Point`
* `Line line(Point from, Point to)` is a straight line. It returns an element of type `Line`.
* `Circle circle(Point center, radius)` draws a circle at `center` and `radius`
## Behaviour
Every line is one instruction. It is possible to assign instructions names to re-use them later.
These variables are immutable. Objects do not exist in this script, in fact, variables are more similar to C-style macros than actual variables.
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)]
```

View file

@ -1,10 +1,11 @@
import { Vector2D } from "./vector.js"
import * as shape from "./shapes.js"
/// <reference path="vector.ts" />
/// <reference path="shapes.ts" />
/// <reference path="parser/parser.ts" />
function loadScript(filepath: string): string
{
var result = null;
var xmlhttp = new XMLHttpRequest();
let result = null;
let xmlhttp = new XMLHttpRequest();
xmlhttp.open("GET", filepath, false);
xmlhttp.send();
if (xmlhttp.status==200) {
@ -23,7 +24,7 @@ class Geometry extends HTMLElement
constructor()
{
super();
console.log("constructor")
if(!this.hasAttribute("src"))
{
return;
@ -32,32 +33,10 @@ class Geometry extends HTMLElement
let sourceFile = this.getAttribute("src");
let content = loadScript(sourceFile);
let lines = content.split("\n");
for(let line of lines)
let parser = new Parser(content);
for(let instr of parser.instructions)
{
if(line === "\r")
{
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;
}
}
console.log(instr.eval());
}
this.attachShadow({mode: "open"});
@ -76,8 +55,8 @@ class Geometry extends HTMLElement
private redraw()
{
shape.line(this.context, new Vector2D(), new Vector2D(300, 300));
shape.circle(this.context, new Vector2D(150, 150), 100);
line(this.context, new Vector2D(), new Vector2D(300, 300));
circle(this.context, new Vector2D(150, 150), 100);
}
}

51
src/parser/instruction.ts Normal file
View 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
View 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
View 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;
}
}

View file

@ -1,5 +1,4 @@
export class ShapeStyle
class ShapeStyle
{
public strokeWidth: number;
public strokeColor: string;

View file

@ -1,5 +1,5 @@
import { Vector2D } from "./vector.js"
import { ShapeStyle } from "./shapeStyle.js";
/// <reference path="vector.ts" />
/// <reference path="shapeStyle.ts" />
abstract class Shape
{

View file

@ -1,4 +1,4 @@
export class Vector2D
class Vector2D
{
public x: number;
public y: number;

View file

@ -1,8 +1,7 @@
{
"compilerOptions": {
"target": "es6",
// "outFile": "./out/lauchpioos.js",
"outDir": "./out",
"outFile": "./out/geometry.js",
"sourceRoot": "./src",
"rootDir": "./src",
"sourceMap": true