Skip to content

Commit

Permalink
Fixing api.js to get expression grammar working properly
Browse files Browse the repository at this point in the history
* fix MultiplicativeExpression
* ability to fetch solver and variables from API
* quad reimplemented using parser (in /demos/parser)
* fix reference to mutation-summary
  • Loading branch information
EnotionZ committed Sep 23, 2016
1 parent e0417d6 commit 737c1a0
Show file tree
Hide file tree
Showing 6 changed files with 262 additions and 16 deletions.
2 changes: 1 addition & 1 deletion .gitmodules
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@
url = git://github.com/slightlyoff/ometa-js.git
[submodule "third_party/mutation-summary"]
path = third_party/mutation-summary
url = https://code.google.com/p/mutation-summary/
url = https://github.com/rafaelw/mutation-summary.git
[submodule "third_party/benchmarkjs"]
path = third_party/benchmarkjs
url = https://github.com/bestiejs/benchmark.js.git
4 changes: 2 additions & 2 deletions bin/c.js

Large diffs are not rendered by default.

36 changes: 36 additions & 0 deletions demos/parser/index.html
Original file line number Diff line number Diff line change
@@ -0,0 +1,36 @@
<!DOCTYPE html>
<html>
<head>
<meta http-equiv="X-UA-Compatible" content="chrome=1">

<title>Quadrilateral demo - Cassowary Javascript</title>
<style type="text/css">
canvas { background-color: lightgray }
* { font-family: sans-serif}
</style>
</head>
<body>
<h1>Quadrilateral demo - Cassowary Javascript</h1>
<p>Below is the bounded quadrilateral demo reimplemented using expressions. <a href="../quad/quaddemo.html">See original implementation</a></p>
<canvas id="c" width="800" height="600"></canvas>
</body>

<script src='../../src/c.js'></script>
<script src='../../src/HashTable.js'></script>
<script src='../../src/HashSet.js'></script>
<script src='../../src/Error.js'></script>
<script src='../../src/SymbolicWeight.js'></script>
<script src='../../src/Strength.js'></script>
<script src='../../src/Variable.js'></script>
<script src='../../src/Point.js'></script>
<script src='../../src/Expression.js'></script>
<script src='../../src/Constraint.js'></script>
<script src='../../src/EditInfo.js'></script>
<script src='../../src/Tableau.js'></script>
<script src='../../src/SimplexSolver.js'></script>
<script src='../../src/Timer.js'></script>
<script src='../../src/parser/parser.js'></script>
<script src='../../src/parser/api.js'></script>

<script src='quadparser.js'></script>
</html>
172 changes: 172 additions & 0 deletions demos/parser/quadparser.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,172 @@
/*global c*/
/**
* [c1, c2, c3, c4] for corners starting from top left clockwise
* [m1, m2, m3, m4] for midpoints starting from top clockwise
*/
var expressions = [
// midpoints constrained in corners
'(c1x + c2x) / 2 == m1x', '(c1y + c2y) / 2 == m1y',
'(c2x + c3x) / 2 == m2x', '(c2y + c3y) / 2 == m2y',
'(c4x + c3x) / 2 == m3x', '(c4y + c3y) / 2 == m3y',
'(c1x + c4x) / 2 == m4x', '(c1y + c4y) / 2 == m4y',

// spaces between points
'c1x + 20 <= c2x', 'c1x + 20 <= c3x',
'c4x + 20 <= c2x', 'c4x + 20 <= c3x',
'c1y + 20 <= c3y', 'c1y + 20 <= c4y',
'c2y + 20 <= c3y', 'c2y + 20 <= c4y',

// contained inside canvas
'c1x >= 0', 'c2x >= 0', 'c3x >= 0', 'c4x >= 0',
'c1y >= 0', 'c2y >= 0', 'c3y >= 0', 'c4y >= 0',
'c1x <= 800', 'c2x <= 800', 'c3x <= 800', 'c4x <= 800',
'c1y <= 600', 'c2y <= 600', 'c3y <= 600', 'c4y <= 600',
].join(';');

var cOut = c(expressions);
var solver = c('solver');


/**
* Point - holds c.Variable coordinate representing a point
*/
class Point {
constructor(x, y) {
this.x = x;
this.y = y;
this.size = 15;
}

// checks if a given coordinate is inside the box representing the point
contains(x, y) {
return (Math.abs(x - this.x.value) <= this.size/2 &&
Math.abs(y - this.y.value) <= this.size/2);
}

// draws a box representing the point
draw(ctx) {
var z = this.size;
ctx.strokeRect(this.x.value - z/2, this.y.value - z/2, z, z);
}

// sets stay value on the point
stay(x, y) {
this.x.value = x;
this.y.value = y;
solver.addStay(this.x).addStay(this.y);
}

// makes point coordinate variables editable
edit() {
return solver.addEditVar(this.x).addEditVar(this.y);
}

// suggests coordinate values for the point
suggest(x, y) {
return solver.suggestValue(this.x, x).suggestValue(this.y, y);
}
}



/**
* Application
*/
var App = {
init: function() {
this.canvas = document.getElementById('c');
this.cwidth = this.canvas.width;
this.cheight = this.canvas.height;
this._ctx = this.canvas.getContext('2d');
this._dragPoint = null;

// populating corners, midpoints
var ps = this.points = [];
var cs = this.corners = [];
var ms = this.midpoints = [];
var point;
for(var i=1; i<=4; i++) {
point = new Point(c(`c${i}x`), c(`c${i}y`));
cs.push(point);
ps.push(point);

point = new Point(c(`m${i}x`), c(`m${i}y`));
ms.push(point);
ps.push(point);
}

// set initial position
cs[0].stay(100, 100);
cs[1].stay(400, 100);
cs[2].stay(400, 400);
cs[3].stay(100, 400);
ms[0].stay(250, 100);
ms[1].stay(400, 250);
ms[2].stay(250, 400);
ms[3].stay(100, 250);

this.draw();

this._bindEvents();
},

draw: function() {
var g = this._ctx;
g.clearRect(0, 0, this.cwidth, this.cheight);
g.strokeStyle = 'black';

this.points.forEach(function(point) { point.draw(g); });

this._drawLine(this.midpoints);
this._drawLine(this.corners);
},

_drawLine: function(points) {
var g = this._ctx;

g.beginPath();
g.moveTo(points[0].x.value, points[0].y.value);
g.lineTo(points[1].x.value, points[1].y.value);
g.lineTo(points[2].x.value, points[2].y.value);
g.lineTo(points[3].x.value, points[3].y.value);
g.closePath();
g.stroke();
},

_bindEvents: function() {
this.canvas.addEventListener('mousedown', ev => this._mousedown(ev));
document.body.addEventListener('mousemove', ev => this._mousemove(ev));
document.body.addEventListener('mouseup', ev => this._mouseup(ev));
},

_mousedown: function(ev) {
var x = ev.pageX - this.canvas.offsetLeft;
var y = ev.pageY - this.canvas.offsetTop;

for(var i=0; i<this.points.length; i++) {
if(this.points[i].contains(x, y)) {
this._dragPoint = this.points[i];
this._dragPoint.edit().beginEdit();
document.body.style.cursor = 'move';
}
}
},

_mousemove: function(ev) {
if(!this._dragPoint) return;

var x = ev.pageX - this.canvas.offsetLeft;
var y = ev.pageY - this.canvas.offsetTop;
this._dragPoint.suggest(x, y).resolve();
this.draw();
},

_mouseup: function(ev) {
if(this._dragPoint) {
solver.endEdit();
document.body.style.cursor = '';
}
this._dragPoint = null;
}
};
App.init();
51 changes: 38 additions & 13 deletions src/parser/api.js
Original file line number Diff line number Diff line change
Expand Up @@ -18,30 +18,39 @@ var _c = function(expr) {
if (exprs[expr]) {
return exprs[expr];
}
var i;
switch(expr.type) {
case "Inequality":
var op = (expr.operator == "<=") ? c.LEQ : c.GEQ;
var i = new c.Inequality(_c(expr.left), op, _c(expr.right), weak);
var op = (expr.operator === "<=") ? c.LEQ : c.GEQ;
i = new c.Inequality(_c(expr.left), op, _c(expr.right), strong);
solver.addConstraint(i);
return i;
case "Equality":
var i = new c.Equation(_c(expr.left), _c(expr.right), weak);
i = new c.Equation(_c(expr.left), _c(expr.right), strong);
solver.addConstraint(i);
return i;
case "MultiplicativeExpression":
var i = c.times(_c(expr.left), _c(expr.right));
solver.addConstraint(i);
if (expr.operator === "/") {
i = c.divide(_c(expr.left), _c(expr.right));
} else {
i = c.times(_c(expr.left), _c(expr.right));
}
return i;
case "AdditiveExpression":
if (expr.operator == "+") {
return c.plus(_c(expr.left), _c(expr.right));
if (expr.operator === "+") {
i = c.plus(_c(expr.left), _c(expr.right));
} else {
return c.minus(_c(expr.left), _c(expr.right));
i = c.minus(_c(expr.left), _c(expr.right));
}
return i;
case "NumericLiteral":
return new c.Expression(expr.value);
case "Variable":
// console.log(expr);
// special variable to get the solver instance
if(expr.name === 'solver') {
return solver;
}

if(!vars[expr.name]) {
vars[expr.name] = new c.Variable({ name: expr.name });
}
Expand All @@ -59,12 +68,28 @@ var compile = function(expressions) {
// Global API entrypoint
c._api = function() {
var args = Array.prototype.slice.call(arguments);
if (args.length == 1) {
if(typeof args[0] == "string") {
var out = {};

if (args.length === 1) {
if(typeof args[0] === "string") {
// Parse and execute it
var r = c.parser.parse(args[0]);
return compile(r);
} else if(typeof args[0] == "function") {
out = compile(r);

// easy getters for solver instance and variables
// allows you to perform c('solver') and c('variableName')
if(out.length === 1 && (
out[0] instanceof c.SimplexSolver ||
out[0] instanceof c.Variable
)) { return out[0]; }

// attach solver and variable list
out.solver = solver;
out.vars = vars;

return out;

} else if(typeof args[0] === "function") {
solver._addCallback(args[0]);
}
}
Expand Down
13 changes: 13 additions & 0 deletions tests/parser/smoketest.js
Original file line number Diff line number Diff line change
Expand Up @@ -70,5 +70,18 @@ define([
});
});
});

it("can parse multiplication", function() {
expect(c("10*a==30")[0]).to.be.instanceOf(c.Constraint);
});

it("can parse multiple operators along with division", function() {
var constraint = c("x/2 - 1 == 29")[0];
expect(constraint).to.be.instanceOf(c.Constraint);
expect(constraint.expression.terms.size).to.equal(1);
constraint.expression.terms.each(function(cVar) {
expect(cVar.value).to.equal(60);
});
});
});
});

0 comments on commit 737c1a0

Please sign in to comment.