diff --git a/src/js/functions.js b/src/js/functions.js new file mode 100644 index 0000000..68ba227 --- /dev/null +++ b/src/js/functions.js @@ -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(, , , ): draws a vector from x0,y0 to x,y + - remove(|): 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; +} \ No newline at end of file diff --git a/src/js/index.js b/src/js/index.js index b1d0d12..af67510 100644 --- a/src/js/index.js +++ b/src/js/index.js @@ -1,13 +1,18 @@ import '../css/app.css'; import {scan, token_types} from './scanner'; import {parse} from './parser'; +import {add_vector_arrow, add_vector_arrow_to_svg, update_vector_arrow} from "./svg_functions"; import { - add_vector_arrow, - add_vector_arrow_to_svg, - remove_vector_arrow, - update_label_text, - update_vector_arrow -} from "./svg_functions"; + addition, + division, + functions, + label, + logical_and, + logical_or, + multiplication, + subtraction, + test_equal +} from './functions.js'; const state = {}; // binding -> 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 = ''; let command_history = ['']; let command_history_index = 0; -let index_sequence = 0; -const hide = function (vector) { - vector.visible = false; - remove_vector_arrow(vector); - 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`}; +const keywords = { + 'true': true, + 'false': false } 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) { // update view after reevaluation of lazy vectors update_vector_arrow(existing_value.id, new_value); @@ -218,7 +209,7 @@ const visit = function (expr) { case 'binary': { switch (expr.operator) { case token_types.MINUS: - return subtract(visit(expr.left), visit(expr.right)); + return subtraction(visit(expr.left), visit(expr.right)); case token_types.PLUS: return addition(visit(expr.left), visit(expr.right)); case token_types.STAR: @@ -227,19 +218,28 @@ const visit = function (expr) { return division(visit(expr.left), visit(expr.right)); case token_types.DOT: 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'}; } case 'identifier': { - if (state[expr.name]) { - let value = state[expr.name]; - while (value.lazy_expression) { - value = value.get(); - } - return value; + if (expr.name in keywords) { + return keywords[expr.name]; } else { - break; + if (state[expr.name]) { + let value = state[expr.name]; + while (value.lazy_expression) { + value = value.get(); + } + return value; + } else { + break; + } } } 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(, , , ): draws a vector from x0,y0 to x,y - - remove(|): 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) { return argument_exprs.map(expr => { let value = visit(expr); diff --git a/src/js/parser.js b/src/js/parser.js index dc9b40d..7d26670 100644 --- a/src/js/parser.js +++ b/src/js/parser.js @@ -47,7 +47,7 @@ export const parse = function (tokens) { function addition() { 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 right = multiplication(); expr = {type: 'binary', left: expr, operator: operator, right: right}; @@ -59,7 +59,7 @@ export const parse = function (tokens) { function multiplication() { 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 right = unary(); expr = {type: 'binary', left: expr, operator: operator, right: right}; diff --git a/src/js/scanner.js b/src/js/scanner.js index 419af5d..51055e7 100644 --- a/src/js/scanner.js +++ b/src/js/scanner.js @@ -65,6 +65,10 @@ export const scan = function (command) { } else { return token_types.EQUALS; } + case '&': + return token_types.AND; + case '|': + return token_types.OR; case '\'': return string(); case '"': @@ -205,5 +209,7 @@ export const token_types = { IDENTIFIER: {type: 'identifier', value: undefined}, STRING: {type: 'string', value: 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'} };