added trigonometry functions, logical operators and keywords. extracted functions in separate file
This commit is contained in:
parent
06f7c169a7
commit
c21ba29ee1
4 changed files with 221 additions and 150 deletions
179
src/js/functions.js
Normal file
179
src/js/functions.js
Normal file
|
|
@ -0,0 +1,179 @@
|
||||||
|
import {add_vector_arrow_to_svg, remove_vector_arrow, update_label_text} from "./svg_functions";
|
||||||
|
|
||||||
|
let index_sequence = 0;
|
||||||
|
|
||||||
|
export const functions = {
|
||||||
|
help: () => help(),
|
||||||
|
vector: (args) => {
|
||||||
|
if (args.length === 2) {
|
||||||
|
return create_vector(0, 0, args[0], args[1]);
|
||||||
|
} else {
|
||||||
|
return create_vector(args[0], args[1], args[2], args[3]);
|
||||||
|
}
|
||||||
|
},
|
||||||
|
hide: (args) => {
|
||||||
|
return hide(args[0]);
|
||||||
|
},
|
||||||
|
label: (args) => {
|
||||||
|
return label(args[0], args[1]);
|
||||||
|
},
|
||||||
|
show: (args) => {
|
||||||
|
return show(args[0]);
|
||||||
|
},
|
||||||
|
pi: function () {
|
||||||
|
return Math.PI;
|
||||||
|
},
|
||||||
|
sin: function (a) {
|
||||||
|
return Math.sin(a);
|
||||||
|
},
|
||||||
|
cos: function (a) {
|
||||||
|
return Math.cos(a);
|
||||||
|
},
|
||||||
|
tan: function (a) {
|
||||||
|
return Math.tan(a);
|
||||||
|
},
|
||||||
|
atan: function (a) {
|
||||||
|
return Math.atan(a);
|
||||||
|
},
|
||||||
|
atan2: function (x, y) {
|
||||||
|
return Math.atan2(x, y);
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
|
export const hide = function (vector) {
|
||||||
|
vector.visible = false;
|
||||||
|
remove_vector_arrow(vector);
|
||||||
|
return {description: `vector@${vector.id} is hidden`};
|
||||||
|
}
|
||||||
|
|
||||||
|
export const label = function (vector, text) {
|
||||||
|
vector.label_text = text;
|
||||||
|
update_label_text(vector.id, text);
|
||||||
|
return `label set to ${text} on vector@${vector.id}`;
|
||||||
|
}
|
||||||
|
|
||||||
|
export const show = function (vector) {
|
||||||
|
vector.visible = true;
|
||||||
|
add_vector_arrow_to_svg(vector);
|
||||||
|
return {description: `vector@${vector.id} is visible`};
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
const help = function () {
|
||||||
|
return {
|
||||||
|
description:
|
||||||
|
`- vector(<x0>, <y0>, <x>, <y>): draws a vector from x0,y0 to x,y
|
||||||
|
- remove(<identifier>|<ref>): removes an object,
|
||||||
|
a ref is @n where n is the reference number asigned to the object`
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
const create_vector = function (x0, y0, x, y) { //rename to create_vector
|
||||||
|
const vector = {
|
||||||
|
id: index_sequence++,
|
||||||
|
x0: x0,
|
||||||
|
y0: y0,
|
||||||
|
x: x,
|
||||||
|
y: y,
|
||||||
|
is_visual: true,
|
||||||
|
is_vector: true, // for type comparison
|
||||||
|
type: 'vector', // for showing type to user
|
||||||
|
is_new: true, // to determine view action
|
||||||
|
visible: true,
|
||||||
|
toString: function () {
|
||||||
|
return `vector@${this.id}{x0:${vector.x0},y0:${vector.y0} x:${vector.x},y:${vector.y}}`;
|
||||||
|
},
|
||||||
|
hide: function () {
|
||||||
|
return hide(this);
|
||||||
|
},
|
||||||
|
label: function (text) {
|
||||||
|
return label(this, text);
|
||||||
|
},
|
||||||
|
show: function () {
|
||||||
|
return show(this);
|
||||||
|
},
|
||||||
|
equals: function (other) {
|
||||||
|
return (this.id === other.id || (this.x0 === other.x0 && this.y0 === other.y0 && this.x === other.x && this.y === other.y));
|
||||||
|
}
|
||||||
|
};
|
||||||
|
return vector;
|
||||||
|
}
|
||||||
|
|
||||||
|
export const multiplication = function (left, right) {
|
||||||
|
|
||||||
|
const multiply = function (vector, scalar) {
|
||||||
|
return create_vector({
|
||||||
|
x0: vector.x0 * scalar,
|
||||||
|
y0: vector.y0 * scalar,
|
||||||
|
x: vector.x * scalar,
|
||||||
|
y: vector.y * scalar
|
||||||
|
});
|
||||||
|
};
|
||||||
|
|
||||||
|
if (left.is_vector && !right.is_vector) {
|
||||||
|
return multiply(left, right);
|
||||||
|
}
|
||||||
|
if (right.is_vector && !left.is_vector) {
|
||||||
|
return multiply(right, left);
|
||||||
|
}
|
||||||
|
return left * right;
|
||||||
|
}
|
||||||
|
|
||||||
|
export const division = function (left, right) {
|
||||||
|
const divide = function (vector, scalar) {
|
||||||
|
return create_vector({
|
||||||
|
x0: vector.x0 / scalar,
|
||||||
|
y0: vector.y0 / scalar,
|
||||||
|
x: vector.x / scalar,
|
||||||
|
y: vector.y / scalar
|
||||||
|
});
|
||||||
|
};
|
||||||
|
|
||||||
|
if (left.is_vector && !right.is_vector) {
|
||||||
|
return divide(left, right);
|
||||||
|
}
|
||||||
|
if (!left.is_vector && !right.is_vector) {
|
||||||
|
return left / right;
|
||||||
|
}
|
||||||
|
throw {message: 'meaningless division'};
|
||||||
|
}
|
||||||
|
|
||||||
|
export const addition = function (left, right) {
|
||||||
|
if (left && left.is_vector && right && right.is_vector) {
|
||||||
|
return create_vector({
|
||||||
|
x0: left.x0 + right.x0,
|
||||||
|
y0: left.x0 + right.x0,
|
||||||
|
x: left.x + right.x,
|
||||||
|
y: left.y + right.y
|
||||||
|
});
|
||||||
|
}
|
||||||
|
return left + right;
|
||||||
|
}
|
||||||
|
|
||||||
|
export const subtraction = function (left, right) {
|
||||||
|
if (left && left.is_vector && right && right.is_vector) {
|
||||||
|
return create_vector({
|
||||||
|
x0: left.x0 - right.x0,
|
||||||
|
y0: left.x0 - right.x0,
|
||||||
|
x: left.x - right.x,
|
||||||
|
y: left.y - right.y
|
||||||
|
});
|
||||||
|
}
|
||||||
|
return left - right;
|
||||||
|
}
|
||||||
|
|
||||||
|
export const test_equal = function (left, right) {
|
||||||
|
if (left.is_vector && right.is_vector) {
|
||||||
|
return left.equals(right);
|
||||||
|
} else {
|
||||||
|
return left === right;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
export const logical_and = function (left, right) {
|
||||||
|
return left && right;
|
||||||
|
}
|
||||||
|
|
||||||
|
export const logical_or = function (left, right) {
|
||||||
|
return left || right;
|
||||||
|
}
|
||||||
180
src/js/index.js
180
src/js/index.js
|
|
@ -1,13 +1,18 @@
|
||||||
import '../css/app.css';
|
import '../css/app.css';
|
||||||
import {scan, token_types} from './scanner';
|
import {scan, token_types} from './scanner';
|
||||||
import {parse} from './parser';
|
import {parse} from './parser';
|
||||||
|
import {add_vector_arrow, add_vector_arrow_to_svg, update_vector_arrow} from "./svg_functions";
|
||||||
import {
|
import {
|
||||||
add_vector_arrow,
|
addition,
|
||||||
add_vector_arrow_to_svg,
|
division,
|
||||||
remove_vector_arrow,
|
functions,
|
||||||
update_label_text,
|
label,
|
||||||
update_vector_arrow
|
logical_and,
|
||||||
} from "./svg_functions";
|
logical_or,
|
||||||
|
multiplication,
|
||||||
|
subtraction,
|
||||||
|
test_equal
|
||||||
|
} from './functions.js';
|
||||||
|
|
||||||
const state = {}; // binding -> value
|
const state = {}; // binding -> value
|
||||||
const bindings = {}; // binding -> {name:binding_name, evaluated: evaluated_lazy_value, previous: previous_value}
|
const bindings = {}; // binding -> {name:binding_name, evaluated: evaluated_lazy_value, previous: previous_value}
|
||||||
|
|
@ -17,24 +22,10 @@ const command_history_element = document.getElementById('command_history');
|
||||||
command_input_element.value = '';
|
command_input_element.value = '';
|
||||||
let command_history = [''];
|
let command_history = [''];
|
||||||
let command_history_index = 0;
|
let command_history_index = 0;
|
||||||
let index_sequence = 0;
|
|
||||||
|
|
||||||
const hide = function (vector) {
|
const keywords = {
|
||||||
vector.visible = false;
|
'true': true,
|
||||||
remove_vector_arrow(vector);
|
'false': false
|
||||||
return {description: `vector@${vector.id} is hidden`};
|
|
||||||
}
|
|
||||||
|
|
||||||
const label = function (vector, text) {
|
|
||||||
vector.label_text = text;
|
|
||||||
update_label_text(vector.id, text);
|
|
||||||
return `label set to ${text} on vector@${vector.id}`;
|
|
||||||
}
|
|
||||||
|
|
||||||
const show = function (vector) {
|
|
||||||
vector.visible = true;
|
|
||||||
add_vector_arrow_to_svg(vector);
|
|
||||||
return {description: `vector@${vector.id} is visible`};
|
|
||||||
}
|
}
|
||||||
|
|
||||||
export const update_visible_objects = function () {
|
export const update_visible_objects = function () {
|
||||||
|
|
@ -73,7 +64,7 @@ export const update_visible_objects = function () {
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
function update_vector(existing_value, new_value, binding_name) {
|
const update_vector = function (existing_value, new_value, binding_name) {
|
||||||
if (existing_value && existing_value.id) {
|
if (existing_value && existing_value.id) {
|
||||||
// update view after reevaluation of lazy vectors
|
// update view after reevaluation of lazy vectors
|
||||||
update_vector_arrow(existing_value.id, new_value);
|
update_vector_arrow(existing_value.id, new_value);
|
||||||
|
|
@ -218,7 +209,7 @@ const visit = function (expr) {
|
||||||
case 'binary': {
|
case 'binary': {
|
||||||
switch (expr.operator) {
|
switch (expr.operator) {
|
||||||
case token_types.MINUS:
|
case token_types.MINUS:
|
||||||
return subtract(visit(expr.left), visit(expr.right));
|
return subtraction(visit(expr.left), visit(expr.right));
|
||||||
case token_types.PLUS:
|
case token_types.PLUS:
|
||||||
return addition(visit(expr.left), visit(expr.right));
|
return addition(visit(expr.left), visit(expr.right));
|
||||||
case token_types.STAR:
|
case token_types.STAR:
|
||||||
|
|
@ -227,19 +218,28 @@ const visit = function (expr) {
|
||||||
return division(visit(expr.left), visit(expr.right));
|
return division(visit(expr.left), visit(expr.right));
|
||||||
case token_types.DOT:
|
case token_types.DOT:
|
||||||
return method_call(visit(expr.left), expr.right); // right is not evaluated. It's the method name
|
return method_call(visit(expr.left), expr.right); // right is not evaluated. It's the method name
|
||||||
// could also be evaluated to itself, BUT it's of type call which would invoke a function (see below)
|
case token_types.EQUALS_EQUALS:
|
||||||
|
return test_equal(visit(expr.left), visit(expr.right));
|
||||||
|
case token_types.AND:
|
||||||
|
return logical_and(visit(expr.left), visit(expr.right));
|
||||||
|
case token_types.OR:
|
||||||
|
return logical_or(visit(expr.left), visit(expr.right));
|
||||||
}
|
}
|
||||||
throw {message: 'illegal binary operator'};
|
throw {message: 'illegal binary operator'};
|
||||||
}
|
}
|
||||||
case 'identifier': {
|
case 'identifier': {
|
||||||
if (state[expr.name]) {
|
if (expr.name in keywords) {
|
||||||
let value = state[expr.name];
|
return keywords[expr.name];
|
||||||
while (value.lazy_expression) {
|
|
||||||
value = value.get();
|
|
||||||
}
|
|
||||||
return value;
|
|
||||||
} else {
|
} else {
|
||||||
break;
|
if (state[expr.name]) {
|
||||||
|
let value = state[expr.name];
|
||||||
|
while (value.lazy_expression) {
|
||||||
|
value = value.get();
|
||||||
|
}
|
||||||
|
return value;
|
||||||
|
} else {
|
||||||
|
break;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
case 'literal': {
|
case 'literal': {
|
||||||
|
|
@ -298,120 +298,6 @@ const method_call = function (object, method_or_property) {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
const functions = {
|
|
||||||
help: () => help(),
|
|
||||||
vector: (args) => {
|
|
||||||
if (args.length === 2) {
|
|
||||||
return create_vector({x0: 0, y0: 0, x: args[0], y: args[1]});
|
|
||||||
} else {
|
|
||||||
return create_vector({x0: args[0], y0: args[1], x: args[2], y: args[3]});
|
|
||||||
}
|
|
||||||
},
|
|
||||||
hide: (args) => {
|
|
||||||
return hide(args[0]);
|
|
||||||
},
|
|
||||||
label: (args) => {
|
|
||||||
return label(args[0], args[1]);
|
|
||||||
},
|
|
||||||
show: (args) => {
|
|
||||||
return show(args[0]);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
const help = function () {
|
|
||||||
return {
|
|
||||||
description:
|
|
||||||
`- vector(<x0>, <y0>, <x>, <y>): draws a vector from x0,y0 to x,y
|
|
||||||
- remove(<identifier>|<ref>): removes an object,
|
|
||||||
a ref is @n where n is the reference number asigned to the object`
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
const multiplication = function (left, right) {
|
|
||||||
|
|
||||||
const multiply = function (vector, scalar) {
|
|
||||||
return create_vector({
|
|
||||||
x0: vector.x0 * scalar,
|
|
||||||
y0: vector.y0 * scalar,
|
|
||||||
x: vector.x * scalar,
|
|
||||||
y: vector.y * scalar
|
|
||||||
});
|
|
||||||
};
|
|
||||||
|
|
||||||
if (left.is_vector && !right.is_vector) {
|
|
||||||
return multiply(left, right);
|
|
||||||
}
|
|
||||||
if (right.is_vector && !left.is_vector) {
|
|
||||||
return multiply(right, left);
|
|
||||||
}
|
|
||||||
return left * right;
|
|
||||||
}
|
|
||||||
|
|
||||||
const division = function (left, right) {
|
|
||||||
const divide = function (vector, scalar) {
|
|
||||||
return create_vector({
|
|
||||||
x0: vector.x0 / scalar,
|
|
||||||
y0: vector.y0 / scalar,
|
|
||||||
x: vector.x / scalar,
|
|
||||||
y: vector.y / scalar
|
|
||||||
});
|
|
||||||
};
|
|
||||||
|
|
||||||
if (left.is_vector && !right.is_vector) {
|
|
||||||
return divide(left, right);
|
|
||||||
}
|
|
||||||
if (!left.is_vector && !right.is_vector) {
|
|
||||||
return left / right;
|
|
||||||
}
|
|
||||||
throw {message: 'meaningless division'};
|
|
||||||
}
|
|
||||||
|
|
||||||
const addition = function (left, right) {
|
|
||||||
if (left && left.is_vector && right && right.is_vector) {
|
|
||||||
return create_vector({
|
|
||||||
x0: left.x0 + right.x0,
|
|
||||||
y0: left.x0 + right.x0,
|
|
||||||
x: left.x + right.x,
|
|
||||||
y: left.y + right.y
|
|
||||||
});
|
|
||||||
}
|
|
||||||
return left + right;
|
|
||||||
}
|
|
||||||
|
|
||||||
const subtract = function (left, right) {
|
|
||||||
if (left && left.is_vector && right && right.is_vector) {
|
|
||||||
return create_vector({
|
|
||||||
x0: left.x0 - right.x0,
|
|
||||||
y0: left.x0 - right.x0,
|
|
||||||
x: left.x - right.x,
|
|
||||||
y: left.y - right.y
|
|
||||||
});
|
|
||||||
}
|
|
||||||
return left - right;
|
|
||||||
}
|
|
||||||
|
|
||||||
export const create_vector = function (vector) { //rename to create_vector
|
|
||||||
vector.id = index_sequence++;
|
|
||||||
vector.is_visual = true;
|
|
||||||
vector.is_vector = true; // for comparison
|
|
||||||
vector.type = 'vector'; // for showing type to user
|
|
||||||
vector.is_new = true;
|
|
||||||
vector.visible = true;
|
|
||||||
vector.toString = function () {
|
|
||||||
return `vector@${this.id}{x0:${vector.x0},y0:${vector.y0} x:${vector.x},y:${vector.y}}`;
|
|
||||||
};
|
|
||||||
vector.hide = function () {
|
|
||||||
return hide(this);
|
|
||||||
};
|
|
||||||
vector.label = function (text) {
|
|
||||||
return label(this, text);
|
|
||||||
};
|
|
||||||
vector.show = function () {
|
|
||||||
return show(this);
|
|
||||||
};
|
|
||||||
return vector;
|
|
||||||
}
|
|
||||||
|
|
||||||
const resolve_arguments = function (argument_exprs) {
|
const resolve_arguments = function (argument_exprs) {
|
||||||
return argument_exprs.map(expr => {
|
return argument_exprs.map(expr => {
|
||||||
let value = visit(expr);
|
let value = visit(expr);
|
||||||
|
|
|
||||||
|
|
@ -47,7 +47,7 @@ export const parse = function (tokens) {
|
||||||
function addition() {
|
function addition() {
|
||||||
let expr = multiplication();
|
let expr = multiplication();
|
||||||
|
|
||||||
while (match([token_types.MINUS, token_types.PLUS])) {
|
while (match([token_types.OR, token_types.MINUS, token_types.PLUS])) {
|
||||||
let operator = previous_token();
|
let operator = previous_token();
|
||||||
let right = multiplication();
|
let right = multiplication();
|
||||||
expr = {type: 'binary', left: expr, operator: operator, right: right};
|
expr = {type: 'binary', left: expr, operator: operator, right: right};
|
||||||
|
|
@ -59,7 +59,7 @@ export const parse = function (tokens) {
|
||||||
function multiplication() {
|
function multiplication() {
|
||||||
let expr = unary();
|
let expr = unary();
|
||||||
|
|
||||||
while (match([token_types.SLASH, token_types.STAR, token_types.DOT])) {
|
while (match([token_types.AND, token_types.SLASH, token_types.STAR, token_types.DOT])) {
|
||||||
let operator = previous_token();
|
let operator = previous_token();
|
||||||
let right = unary();
|
let right = unary();
|
||||||
expr = {type: 'binary', left: expr, operator: operator, right: right};
|
expr = {type: 'binary', left: expr, operator: operator, right: right};
|
||||||
|
|
|
||||||
|
|
@ -65,6 +65,10 @@ export const scan = function (command) {
|
||||||
} else {
|
} else {
|
||||||
return token_types.EQUALS;
|
return token_types.EQUALS;
|
||||||
}
|
}
|
||||||
|
case '&':
|
||||||
|
return token_types.AND;
|
||||||
|
case '|':
|
||||||
|
return token_types.OR;
|
||||||
case '\'':
|
case '\'':
|
||||||
return string();
|
return string();
|
||||||
case '"':
|
case '"':
|
||||||
|
|
@ -205,5 +209,7 @@ export const token_types = {
|
||||||
IDENTIFIER: {type: 'identifier', value: undefined},
|
IDENTIFIER: {type: 'identifier', value: undefined},
|
||||||
STRING: {type: 'string', value: undefined},
|
STRING: {type: 'string', value: undefined},
|
||||||
LAZY: {type: 'lazy', expression: undefined, parsed_expression: undefined},
|
LAZY: {type: 'lazy', expression: undefined, parsed_expression: undefined},
|
||||||
AT: {type: 'reference', value: undefined}
|
AT: {type: 'reference', value: undefined},
|
||||||
|
AND: {type: 'logical_and'},
|
||||||
|
OR: {type: 'logical_or'}
|
||||||
};
|
};
|
||||||
|
|
|
||||||
Loading…
Add table
Reference in a new issue