Package Exports
- math-codegen
This package does not declare an exports field, so the exports above have been automatically detected and optimized by JSPM instead. If any package subpath is missing, it is recommended to post an issue to the original package (math-codegen) to support the "exports" field. If that is not possible, create a JSPM override to customize the exports field for this package.
Readme
math-codegen
Generates JavaScript code from mathematical expressions
Table of Contents generated with DocToc
Description
A flexible interpreter for mathematical expressions which allows the programmer to change the usual semantic of an operator bringing the operator overloading polymorphism to JavaScript (emulated with function calls), in addition an expression can be evaluated under any adapted namespace providing expression portability between numeric libraries
Lifecycle
parsea mathematical expression is parsed with Esprimacompilethe parsed string is compiled against a namespace producing executable JavaScript codeevalthe executable JavaScript code is evaluated against a context
Parse
For example let's consider the following expression with the variable x which is user defined:
'1 + 2 * x'the expression can be emulated with function calls instead of operators (the parser identifies the addition and multiplication expression as Binary Expressions)
'add(1, multiply(2, x))'now we can introduce the namespace ns where add and multiply come from
'ns.add(1, ns.multiply(2, x))'the variables (which for the parser are Identifiers)
come from a context called scope but they might also be constant values defined in the namespace:
'ns.add(1, ns.multiply(2, (scope["x"] || ns["x"]) ))'the constant values might have different meanings for different namespaces therefore a factory is needed
on the namespace to transform these values into values the namespace can operate with
'ns.add(ns.factory(1), ns.multiply(ns.factory(2), (scope["x"] || ns["x"]) ))'Compile
Now that we have a parsed expression we have to compile it against a namespace to produce executable JavaScript code
parse('1 + 2 * x').compile(namespace)
// returns something like this
(function (ns) {
return {
eval: function (scope) {
// scope processing
// ...
// the string parsed above goes here
}
}
})(namespace)Eval
The object returned above can be evaluated within a context
parse('1 + 2 * x').compile(namespace).eval(scope)Differences with math.js expression parser
Math.js expression parser API is quite similar having the same lifecycle however there are some important facts I've found:
math.jshas a custom expression parser (which means it has additional types of nodes),math-codegenuses Esprima which support the ES5 grammar only (ESTree AST nodes)math.jsv1.x arrays can represent matrices withns.matrixor as a raw arrays,math-codegendoesn't make any assumptions of the arrays and treats them just like any other literal allowing the namespace to decide what to do with an array in itsfactorymethod
Install
$ npm install --save math-codegenUsage
var CodeGenerator = require('math-codegen');
new CodeGenerator([options]).parse(code).compile(namespace).eval(scope)API
var instance = new CodeGenerator([options])
properties
statements{Array} An array of statements parsed from an expressioninterpreter{Interpreter} Instance of the Interpreter classdefs{Object} An object with additional definitions available during the compilation that exist during the instance lifespan
params
options{Object} Options available for the interpreter[options.factory="ns.factory"]{string} factory method under the namespace[options.raw=false]{boolean} True to interpret BinaryExpression, UnaryExpression and ArrayExpression in a raw way without wrapping the operators with identifiers, e.g.-1will be compiled as-1instead ofns.negative(ns.factory(1))[options.rawArrayExpressionElements=true]{boolean} true to interpret the array elements in a raw way[options.rawCallExpressionElements=false]{boolean} true to interpret call expression elements in a raw way
instance.parse(code)
chainable params
code{string} string to be parsed
Parses a program using Esprima, each Expression Statement is saved in
instance.statements
Node types implemented:
- Nodes:
- Expressions:
- ArrayExpression
- UnaryExpression available operators emulated with function calls can be found here
- BinaryExpression available operators emulated with function calls can be found here
- AssignmentExpression
- ConditionalExpression
- CallExpression
- Misc:
Identifiers, identifier resolution follows this order:
- namespace
- scope
- definitions stored in
instance.defs
instance.compile(namespace)
chainable params
namespace{Object}
Compiles the code making namespace's properties available during evaluation
returns {Object}
return.eval{Function} Function to be evaluated under a context paramsscope{Object}
instance.setDefs(defs)
params
defs{Object}
An object whose properties will be available during evaluation, properties can be accessed by the property name in the program
Examples
built-in math
var numeric = {
factory: function (a) {
// anything is a number :)
return Number(a);
},
add: function (a, b) {
return a + b;
},
mul: function (a, b) {
return a * b;
}
};
var instance = new CodeGenerator()
.parse('1 + 2 * x')
.compile(numeric);
instance.eval({x : 3}); // 1 + 2 * 3 = 7imaginary
var instance = new CodeGenerator();
var imaginary = {
factory: function (a) {
// a = [re, im]
if (typeof a === 'number') {
return [a, 0];
}
return [a[0] || 0, a[1] || 0];
},
add: function (a, b) {
var re = a[0] + b[0];
var im = a[1] + b[1];
return [re, im];
},
mul: function (a, b) {
var re = a[0] * b[0] - a[1] * b[1];
var im = a[0] * b[1] + a[1] * b[0];
return [re, im];
}
};
// [1, 0] + [2, 0] * [1, 1]
// [1, 0] + [2, 2]
// [3, 2]
instance
.parse('1 + 2 * x')
.compile(imaginary)
.eval({x : [1, 1]})
// because of the way the factory works it can also receive an array as a parameter
// [1, 0] + [2, 0] * [1, 1]
// [1, 0] + [2, 2]
// [3, 2]
instance
.parse('[1, 0] + [2, 0] * x')
.compile(imaginary)
.eval({x : [1, 1]});interval arithmetic
var interval = {
factory: function (a) {
// a = [lo, hi]
if (typeof a === 'number') {
return [a, a];
}
return a;
},
add: function (x, y) {
return [x[0] + y[0], x[1] + y[1]];
},
mul: function (x, y) {
var ac = x[0] * y[0];
var ad = x[0] * y[1];
var bc = x[1] * y[0];
var bd = x[1] * y[1];
return [Math.min(ac, ad, bc, bd), Math.max(ac, ad, bc, bd)];
}
};
// [1, 1] + [2, 2] * [-1, 2]
// [1, 1] + [-2, 4]
// [-1, 5]
var instance = new CodeGenerator()
.parse('1 + 2 * x')
.compile(interval)
.eval({x : [-1, 2]});Inspiration projects
License
2015 MIT © Mauricio Poppe
