Socket
Socket
Sign inDemoInstall

math-codegen

Package Overview
Dependencies
2
Maintainers
1
Versions
15
Alerts
File Explorer

Advanced tools

Install Socket

Detect and block malicious and high-risk dependencies

Install

Comparing version 0.2.5 to 0.3.0

lib/misc/Operators.js

4

index.js

@@ -7,3 +7,3 @@ /*

*/
'use strict';
module.exports = require('./lib/CodeGenerator');
'use strict'
module.exports = require('./lib/CodeGenerator')

@@ -1,69 +0,87 @@

/**
* Created by mauricio on 5/12/15.
*/
'use strict';
'use strict'
var esprima = require('esprima');
var Interpreter = require('./Interpreter');
var extend = require('extend');
var Parser = require('mr-parser').Parser
var Interpreter = require('./Interpreter')
var extend = require('extend')
function CodeGenerator(options, defs) {
this.statements = [];
this.defs = defs || {};
this.interpreter = new Interpreter(this, options);
function CodeGenerator (options, defs) {
this.statements = []
this.defs = defs || {}
this.interpreter = new Interpreter(this, options)
}
CodeGenerator.prototype.setDefs = function (defs) {
this.defs = extend(this.defs, defs);
return this;
};
this.defs = extend(this.defs, defs)
return this
}
CodeGenerator.prototype.compile = function (namespace) {
if (!namespace) {
throw Error('namespace required');
if (!namespace || typeof namespace !== 'object') {
throw TypeError('namespace must be an object')
}
if (typeof namespace.factory !== 'function') {
throw TypeError('namespace.factory must be a function')
}
// default process scope hook
this.defs.ns = namespace;
this.defs.$$processScope = this.defs.$$processScope || function () {};
// definitions available in the function
// each property under this.defs is mapped to local variables
// e.g
//
// function (defs) {
// var ns = defs['ns']
// // code generated for the expression
// }
this.defs.ns = namespace
this.defs.$$mathCodegen = {
throwUndefined: function (symbol) {
throw SyntaxError('symbol "' + symbol + '" is undefined')
},
functionProxy: function (fn, name) {
if (typeof fn !== 'function') {
throw SyntaxError('symbol "' + name + '" must be a function')
}
return fn
}
}
this.defs.$$processScope = this.defs.$$processScope || function () {}
var defsCode = Object.keys(this.defs).map(function (name) {
return 'var ' + name + ' = defs["' + name + '"];';
});
return 'var ' + name + ' = defs["' + name + '"]'
})
// statement join
if (!this.statements.length) {
throw Error('there are no statements saved in this generator');
throw Error('there are no statements saved in this generator')
}
// last statement is always a return statement
this.statements[this.statements.length - 1] = 'return ' + this.statements[this.statements.length - 1];
this.statements[this.statements.length - 1] = 'return ' + this.statements[this.statements.length - 1]
var code = this.statements.join(';');
var factoryCode =
defsCode.join(' ') +
'return {' +
' eval: function (scope) {' +
' scope = scope || {};' +
' $$processScope(scope);' +
' ' + code +
' },' +
' code: \'' + code + '\'' +
'};';
var code = this.statements.join(';')
var factoryCode = defsCode.join('\n') + '\n' + [
'return {',
' eval: function (scope) {',
' scope = scope || {}',
' $$processScope(scope)',
' ' + code,
' },',
" code: '" + code + "'",
'}'
].join('\n')
/* eslint-disable */
var factory = new Function('defs', factoryCode);
var factory = new Function('defs', factoryCode)
return factory(this.defs)
/* eslint-enable */
return factory(this.defs);
};
}
CodeGenerator.prototype.parse = function (code) {
var self = this;
var program = esprima.parse(code);
this.statements = program.body.map(function (statement) {
return self.interpreter.next(statement);
});
return this;
};
var self = this
var program = new Parser().parse(code)
this.statements = program.blocks.map(function (statement) {
return self.interpreter.next(statement)
})
return this
}
module.exports = CodeGenerator;
module.exports = CodeGenerator

@@ -1,20 +0,17 @@

'use strict';
var extend = require('extend');
'use strict'
var extend = require('extend')
var types = {
// node
ArrayExpression: require('./node/ArrayExpression'),
AssignmentExpression: require('./node/AssignmentExpression'),
BinaryExpression: require('./node/BinaryExpression'),
CallExpression: require('./node/CallExpression'),
ConditionalExpression: require('./node/ConditionalExpression'),
ExpressionStatement: require('./node/ExpressionStatement'),
UnaryExpression: require('./node/UnaryExpression'),
// misc
Identifier: require('./misc/Identifier'),
Literal: require('./misc/Literal')
};
ArrayNode: require('./node/ArrayNode'),
AssignmentNode: require('./node/AssignmentNode'),
ConditionalNode: require('./node/ConditionalNode'),
ConstantNode: require('./node/ConstantNode'),
FunctionNode: require('./node/FunctionNode'),
OperatorNode: require('./node/OperatorNode'),
SymbolNode: require('./node/SymbolNode'),
UnaryNode: require('./node/UnaryNode')
}
var Interpreter = function (owner, options) {
this.owner = owner;
this.owner = owner
this.options = extend({

@@ -25,26 +22,26 @@ factory: 'ns.factory',

rawCallExpressionElements: false
}, options);
};
}, options)
}
extend(Interpreter.prototype, types);
extend(Interpreter.prototype, types)
// run is the main method which decides which expression to call
// main method which decides which expression to call
Interpreter.prototype.next = function (node) {
if (!(node.type in this)) {
throw new TypeError('the node type ' + node.type + ' is not implemented');
throw new TypeError('the node type ' + node.type + ' is not implemented')
}
return this[node.type](node);
};
return this[node.type](node)
}
Interpreter.prototype.rawify = function (test, fn) {
var oldRaw = this.options.raw;
var oldRaw = this.options.raw
if (test) {
this.options.raw = true;
this.options.raw = true
}
fn();
fn()
if (test) {
this.options.raw = oldRaw;
this.options.raw = oldRaw
}
};
}
module.exports = Interpreter;
module.exports = Interpreter
{
"name": "math-codegen",
"version": "0.2.5",
"version": "0.3.0",
"description": "Generates code from mathematical expressions",

@@ -27,4 +27,4 @@ "bugs": "https://github.com/maurizzzio/math-codegen/issues",

"dependencies": {
"esprima": "^2.2.0",
"extend": "^3.0.0"
"extend": "^3.0.0",
"mr-parser": "^0.1.0"
},

@@ -34,16 +34,16 @@ "devDependencies": {

"doctoc": "^0.14.2",
"eslint": "^0.21.0",
"istanbul": "^0.3.8",
"mocha": "^2.2.1",
"mocha-lcov-reporter": "^0.0.2",
"nodemon": "^1.3.7"
"nodemon": "^1.3.7",
"standard": "^4.5.4"
},
"scripts": {
"istanbul": "istanbul cover _mocha --report lcovonly -- -R spec test/",
"lint": "eslint index.js lib test",
"lint": "standard",
"test": "mocha -R spec test/",
"test:watch": "nodemon --watch lib --watch test --watch index.js --exec 'npm test'",
"start": "npm run test:watch",
"toc": "./node_modules/.bin/doctoc ."
"toc": "doctoc ."
}
}
# math-codegen
[![Build Status][travis-image]][travis-url]
[![NPM][npm-image]][npm-url]
[![Build Status][travis-image]][travis-url]
[![Coverage Status][coveralls-image]][coveralls-url]
[![Dependency Status][david-image]][david-url]
[![unstable](http://badges.github.io/stability-badges/dist/unstable.svg)](http://github.com/badges/stability-badges)
[![Stability](https://img.shields.io/badge/stability-stable-green.svg)]()
[![js-standard-style](https://cdn.rawgit.com/feross/standard/master/badge.svg)](https://github.com/feross/standard)
> Generates JavaScript code from mathematical expressions

@@ -14,3 +14,3 @@

<!-- DON'T EDIT THIS SECTION, INSTEAD RE-RUN doctoc TO UPDATE -->
**Table of Contents** *generated with [DocToc](https://github.com/thlorenz/doctoc)*
**Table of Contents** * generated with [DocToc](https://github.com/thlorenz/doctoc) *

@@ -23,2 +23,3 @@ - [Description](#description)

- [Differences with math.js expression parser](#differences-with-mathjs-expression-parser)
- [Operators](#operators)
- [Install](#install)

@@ -42,3 +43,3 @@ - [Usage](#usage)

A flexible interpreter for mathematical expressions which allows the programmer to change the usual semantic of an
An 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),

@@ -51,9 +52,12 @@ in addition an expression can be evaluated under any adapted namespace providing expression portability between numeric libraries

- `parse` a mathematical expression is parsed with [Esprima](http://esprima.org/)
- `compile` the parsed string is compiled against a namespace producing executable JavaScript code
- `eval` the executable JavaScript code is evaluated against a context
- `parse`: a mathematical expression is parsed with [`mr-parse`](https://github.com/maurizzzio/mr-parser), in the ideal scenario
it would use [math.js expression parser](http://mathjs.org/docs/expressions/index.html) however it's not modularized yet
and including all math.js is just an overkill, probably `mr-parse` will be replaced with math.js expression parser when
it reaches npm as a module :)
- `compile`: the parsed string is compiled against a namespace producing executable JavaScript code
- `eval`: the 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:
For example let's consider the following expression with the variable `x` which is defined by the user:

@@ -64,7 +68,7 @@ ```javascript

the expression can be emulated with function calls instead of operators (the parser identifies the addition and multiplication
expression as [Binary Expressions](https://github.com/estree/estree/blob/master/spec.md#binaryexpression))
the expression can be emulated with function calls instead of operators, math-codegen will map many mathematical
operators to callable methods
```javascript
'add(1, multiply(2, x))'
'add(1, mul(2, x))'
```

@@ -75,10 +79,10 @@

```javascript
'ns.add(1, ns.multiply(2, x))'
'ns.add(1, ns.mul(2, x))'
```
the variables (which for the parser are [Identifiers](https://github.com/estree/estree/blob/master/spec.md#identifier))
the variables (which for the parser are `symbols`
come from a context called `scope` but they might also be constant values defined in the namespace:
```javascript
'ns.add(1, ns.multiply(2, (scope["x"] || ns["x"]) ))'
'ns.add(1, ns.mul(2, (scope["x"] || ns["x"]) ))'
```

@@ -90,3 +94,3 @@

```javascript
'ns.add(ns.factory(1), ns.multiply(ns.factory(2), (scope["x"] || ns["x"]) ))'
'ns.add(ns.factory(1), ns.mul(ns.factory(2), (scope["x"] || ns["x"]) ))'
```

@@ -103,3 +107,4 @@

// returns something like this
(function (ns) {
(function (definitions) {
var ns = definitions.namespace
return {

@@ -110,5 +115,6 @@ eval: function (scope) {

// the string parsed above goes here
return ns.add(ns.factory(1), ns.mul(ns.factory(2), (scope["x"] || ns["x"]) ))
}
}
})(namespace)
})(definitions) // definitions created by math-codegen
```

@@ -129,5 +135,2 @@

- `math.js` has a custom expression parser (which means it has additional types of nodes),
`math-codegen` uses Esprima which support the ES5 grammar only
[(ESTree AST nodes)](https://github.com/estree/estree/blob/master/spec.md)
- `math.js` v1.x arrays can represent matrices with `ns.matrix` or as a raw arrays, `math-codegen` doesn't

@@ -137,2 +140,45 @@ make any assumptions of the arrays and treats them just like any other literal allowing the namespace to

### Operators
The following operators recognized by `mr-parser` are named as follows when compiled
```javascript
'+': 'add'
'-': 'sub'
'*': 'mul'
'/': 'div'
'^': 'pow'
'%': 'mod'
'!': 'factorial'
// misc operators
'|': 'bitwiseOR'
'^|': 'bitwiseXOR'
'&': 'bitwiseAND'
'||': 'logicalOR'
'xor': 'logicalXOR'
'&&': 'logicalAND'
// comparison
'<': 'lessThan'
'>': 'greaterThan'
'<=': 'lessEqualThan'
'>=': 'greaterEqualThan'
'===': 'strictlyEqual'
'==': 'equal'
'!==': 'strictlyNotEqual'
'!=': 'notEqual'
// shift
'>>': 'shiftRight'
'<<': 'shiftLeft'
'>>>': 'unsignedRightShift'
// unary
'+': 'positive'
'-': 'negative'
'~': 'oneComplement'
```
## Install

@@ -163,4 +209,4 @@

* `[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. `-1` will be compiled as
* `[options.raw=false]` {boolean} True to interpret OperatorNode, UnaryNode and ArrayNode
in a raw way without wrapping the operators with identifiers e.g. `-1` will be compiled as
`-1` instead of `ns.negative(ns.factory(1))`

@@ -177,28 +223,6 @@ * `[options.rawArrayExpressionElements=true]` {boolean} true to interpret the array elements in a raw way

Parses a program using [Esprima](http://esprima.org/), each Expression Statement is saved in
Parses a program using [`mr-parse`](https://github.com/maurizzzio/mr-parser), each Expression Statement is saved in
`instance.statements`
Node types implemented:
- Nodes:
- [ExpressionStatement](https://github.com/estree/estree/blob/master/spec.md#expressionstatement)
- Expressions:
- [ArrayExpression](https://github.com/estree/estree/blob/master/spec.md#arrayexpression)
- [UnaryExpression](https://github.com/estree/estree/blob/master/spec.md#unaryexpression)
available operators emulated with function calls can be found
[here](https://github.com/maurizzzio/math-codegen/blob/master/lib/misc/UnaryOperator.js)
- [BinaryExpression](https://github.com/estree/estree/blob/master/spec.md#binaryexpression)
available operators emulated with function calls can be found
[here](https://github.com/maurizzzio/math-codegen/blob/master/lib/misc/BinaryOperator.js)
- [AssignmentExpression](https://github.com/estree/estree/blob/master/spec.md#assignmentexpression)
- [ConditionalExpression](https://github.com/estree/estree/blob/master/spec.md#conditionalexpression)
- [CallExpression](https://github.com/estree/estree/blob/master/spec.md#callexpression)
- Misc:
- [Identifiers](https://github.com/estree/estree/blob/master/spec.md#identifier), identifier
resolution follows this order:
- namespace
- scope
- definitions stored in `instance.defs`
- [Literals](https://github.com/estree/estree/blob/master/spec.md#literal)
The documentation for the available nodes is described in [`mr-parse`](https://github.com/maurizzzio/mr-parser)

@@ -211,5 +235,7 @@ ### `instance.compile(namespace)`

Compiles the code making `namespace`'s properties available during evaluation
Compiles the code making `namespace`'s properties available during evaluation, **it's required
to have the `factory` property defined**
**returns** {Object}
* `return.code` {string} the body of the function to be evaluated with `eval`
* `return.eval` {Function} Function to be evaluated under a context

@@ -232,20 +258,17 @@ **params**

```javascript
'use strict'
var CodeGenerator = require('math-codegen')
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;
}
};
factory: function (a) { return a },
add: function (a, b) { return a + b },
mul: function (a, b) { return a * b }
}
var instance = new CodeGenerator()
// 1 + 2 * 3 = 7
new CodeGenerator()
.parse('1 + 2 * x')
.compile(numeric);
instance.eval({x : 3}); // 1 + 2 * 3 = 7
.compile(numeric)
.eval({x: 3})
)
```

@@ -256,3 +279,4 @@

```javascript
var instance = new CodeGenerator();
'use strict'
var CodeGenerator = require('math-codegen')

@@ -263,18 +287,20 @@ var imaginary = {

if (typeof a === 'number') {
return [a, 0];
return [a, 0]
}
return [a[0] || 0, a[1] || 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];
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];
var re = a[0] * b[0] - a[1] * b[1]
var im = a[0] * b[1] + a[1] * b[0]
return [re, im]
}
};
}
var instance = new CodeGenerator()
// [1, 0] + [2, 0] * [1, 1]

@@ -301,2 +327,5 @@ // [1, 0] + [2, 2]

```javascript
'use strict'
var CodeGenerator = require('math-codegen')
var interval = {

@@ -306,25 +335,27 @@ factory: function (a) {

if (typeof a === 'number') {
return [a, a];
return [a, a]
}
return a;
return [a[0], a[1]]
},
add: function (x, y) {
return [x[0] + y[0], x[1] + y[1]];
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)];
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)]
}
};
}
var instance = new CodeGenerator()
// [1, 1] + [2, 2] * [-1, 2]
// [1, 1] + [-2, 4]
// [-1, 5]
var instance = new CodeGenerator()
instance
.parse('1 + 2 * x')
.compile(interval)
.eval({x : [-1, 2]});
.eval({x: [-1, 2]})
```

@@ -335,2 +366,3 @@

- [math.js expression parser](http://mathjs.org/docs/expressions/index.html)
- [angular v1.x parser](https://github.com/angular/angular.js/blob/master/src/ng/parse.js)

@@ -341,3 +373,3 @@ ## License

[npm-image]: https://nodei.co/npm/math-codegen.png?downloads=true
[npm-image]: https://img.shields.io/npm/v/math-codegen.svg?style=flat
[npm-url]: https://npmjs.org/package/math-codegen

@@ -348,3 +380,1 @@ [travis-image]: https://travis-ci.org/maurizzzio/math-codegen.svg?branch=master

[coveralls-url]: https://coveralls.io/r/maurizzzio/math-codegen?branch=master
[david-image]: https://david-dm.org/maurizzzio/math-codegen.svg
[david-url]: https://david-dm.org/maurizzzio/math-codegen

Sorry, the diff of this file is not supported yet

SocketSocket SOC 2 Logo

Product

  • Package Alerts
  • Integrations
  • Docs
  • Pricing
  • FAQ
  • Roadmap

Stay in touch

Get open source security insights delivered straight into your inbox.


  • Terms
  • Privacy
  • Security

Made with ⚡️ by Socket Inc