Skip to content

Commit a09ed0b

Browse files
committed
Initial Runtime
1 parent 744429c commit a09ed0b

4 files changed

Lines changed: 109 additions & 24 deletions

File tree

package.json

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -23,5 +23,8 @@
2323
"eslint-plugin-strict-newline": "^1.0.0",
2424
"istanbul": "^0.4.4",
2525
"mocha": "^2.5.3"
26+
},
27+
"dependencies": {
28+
"async": "^2.0.1"
2629
}
2730
}

src/Parser.js

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -137,9 +137,10 @@ class Parser {
137137
}
138138

139139
generateAST() {
140+
this.sources = [];
140141
const ast = this.generateRootNode();
141-
142142
this.getExpectedToken(TOKEN_STRUCTURE_EOF);
143+
ast.sources = this.sources;
143144
return ast;
144145
}
145146

@@ -275,6 +276,7 @@ class Parser {
275276

276277
this.getExpectedToken(TOKEN_BOUNDARY_TAG_END);
277278

279+
this.sources.push(value);
278280
return {
279281
type: PARSER_TYPE_INCLUDE,
280282
value

src/Runner.js

Lines changed: 95 additions & 23 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,30 @@
11
'use strict';
22

3+
const async = require('async');
4+
const fs = require('fs');
35
const RuntimeError = require('./error/Runtime');
46

7+
const Parser = require('./Parser');
8+
const Lexer = require('./Lexer');
9+
10+
// credit: http://stackoverflow.com/a/3886106/124861
11+
function isFloat(n){
12+
return Number(n) === n && n % 1 !== 0;
13+
}
14+
15+
function floatEqual(a, b, epsilon = Number.EPSILON) {
16+
if (a === b) {
17+
return true;
18+
}
19+
20+
if (isNaN(a) || isNaN(b) || !isFinite(a) || !isFinite(b)) {
21+
return false;
22+
}
23+
24+
const diff = Math.abs(a - b);
25+
return diff < epsilon ? true : diff <= Math.max(Math.abs(a), Math.abs(b)) * epsilon;
26+
}
27+
528
const {
629
OPERATOR_EQUALS,
730
OPERATOR_NOT_EQUALS,
@@ -15,7 +38,6 @@ const {
1538
OPERATOR_GREATER_EQUAL_THAN,
1639
OPERATOR_LESS_EQUAL_THAN,
1740

18-
PARSER_TYPE_ROOT,
1941
PARSER_TYPE_TEXT_LITERAL,
2042
PARSER_TYPE_INCLUDE,
2143
PARSER_TYPE_VALUE,
@@ -26,43 +48,64 @@ const {
2648
} = require('./constants');
2749

2850
class Runner {
29-
constructor(ast, input) {
30-
this.ast = ast;
31-
this.input = input;
51+
constructor() {
52+
this.astCache = {};
3253
this.result = [];
3354
}
3455

35-
invoke() {
36-
this.invokeRoot(this.ast);
56+
invoke(ast, input, cb) {
57+
this.result = [];
58+
this.input = input;
59+
this.loadSources(ast.sources, (err) => {
60+
return err ? cb(err) : cb(null, this.invokeStatements(ast.statements));
61+
});
62+
}
63+
64+
loadSources(sources, cb) {
65+
const tasks = {};
66+
67+
for (const value of sources) {
68+
const path = value.type === PARSER_TYPE_VARIABLE ? this.getValueFromVariable(value.name) : value.value;
69+
tasks[path] = fs.readFile.bind(fs, path);
70+
}
71+
72+
async.parallel(tasks, (err, files) => {
73+
if (err) {
74+
return cb(err);
75+
}
76+
for (const f in files) {
77+
this.astCache[f] = new Parser(new Lexer(files[f])).generateAST();
78+
}
79+
return cb();
80+
});
3781
}
3882

39-
invokeRoot(ast) {
40-
for (const statement in ast.statements) {
83+
invokeStatements(statements) {
84+
for (const statement of statements) {
4185
if (statement.type === PARSER_TYPE_TEXT_LITERAL) {
4286
this.result.push(statement.value);
4387
}
4488
else if (statement.type === PARSER_TYPE_INCLUDE) {
4589
this.invokeInclude(statement);
4690
}
47-
else (statement.type === PARSER_TYPE_BRANCH) {
91+
else if (statement.type === PARSER_TYPE_BRANCH) {
4892
this.invokeBranch(statement);
4993
}
94+
throw new RuntimeError(`Unexpected statement: ${statement}`);
5095
}
5196
}
5297

5398
invokeInclude(statement) {
54-
const path = statement.value;
5599

56-
if (typeof path !== string) {
57-
throw new RuntimeError('Non string type passed to include.')
58-
}
59-
// TODO: include other file
100+
if ()
101+
102+
this.invoke(ast);
60103
}
61104

62105
invokeBranch(statement) {
63106
for (const branch of statement.branches) {
64-
if (this.evaluateExpression(branch.condition)) {
65-
107+
if (branch.condition === undefined || this.evaluateExpression(branch.condition)) {
108+
return this.invokeStatements(branch.consequent.statements);
66109
}
67110
}
68111
}
@@ -78,38 +121,65 @@ class Runner {
78121
return expression.value;
79122
}
80123
else if (expression.type === PARSER_TYPE_VARIABLE) {
81-
return this.getValueFromVariable(expression.value);
124+
return this.getValueFromVariable(expression.name);
82125
}
83126
}
84127

85128
evaluateBinaryExpression(expression) {
129+
const leftValue = this.evaluateExpression(expression.left);
130+
const rightValue = this.evaluateExpression(expression.right);
131+
86132
if (expression.operator === OPERATOR_EQUALS) {
87-
return this.evaluateExpression(expression.left) == this.evaluateExpression(expression.right);
133+
return this.evaluateEquals(leftValue, rightValue);
88134
}
89135

90136
if (expression.operator === OPERATOR_NOT_EQUALS) {
91-
return this.evaluateExpression(expression.left) != this.evaluateExpression(expression.right);
137+
return !this.evaluateEquals(leftValue, rightValue);
92138
}
93139

94140
if (expression.operator === OPERATOR_STRICT_EQUALS) {
95-
return this.evaluateExpression(expression.left) === this.evaluateExpression(expression.right);
141+
return this.evaluateEquals(leftValue, rightValue, true);
96142
}
97143

98144
if (expression.operator === OPERATOR_STRICT_NOT_EQUALS) {
99-
return this.evaluateExpression(expression.left) !== this.evaluateExpression(expression.right);
145+
return !this.evaluateEquals(leftValue, rightValue, true);
146+
}
147+
148+
if (expression.operator === OPERATOR_GREATER_EQUAL_THAN) {
149+
return leftValue > rightValue || this.evaluateEquals(leftValue, rightValue);
150+
}
151+
152+
if (expression.operator === OPERATOR_LESS_EQUAL_THAN) {
153+
return leftValue < rightValue || this.evaluateEquals(leftValue, rightValue);
154+
}
155+
156+
if (expression.operator === OPERATOR_GREATER_THAN) {
157+
return leftValue > rightValue;
158+
}
159+
160+
if (expression.operator === OPERATOR_LESS_THAN) {
161+
return leftValue > rightValue;
100162
}
101163

102164
if (expression.operator === OPERATOR_AND) {
103-
return this.evaluateExpression(expression.left) && this.evaluateExpression(expression.right);
165+
return leftValue && rightValue;
104166
}
105167

106168
if (expression.operator === OPERATOR_OR) {
107-
return this.evaluateExpression(expression.left) || this.evaluateExpression(expression.right);
169+
return leftValue || rightValue;
108170
}
109171

110172
throw new RuntimeError(`Unknown operator: ${expression.operator}`);
111173
}
112174

175+
evaluateEquals(leftValue, rightValue, strict = false) {
176+
if (isFloat(leftValue) && isFloat(rightValue)) {
177+
return floatEqual(leftValue, rightValue);
178+
}
179+
180+
return strict ? leftValue === rightValue : leftValue == rightValue;
181+
}
182+
113183
evaluateUnaryExpression(expression) {
114184
if (expression.operator === OPERATOR_NOT) {
115185
return !this.evaluateExpression(expression.expression);
@@ -126,3 +196,5 @@ class Runner {
126196
}
127197

128198
}
199+
200+
module.exports = Runner;

test/Parser/Parser.generate.spec.js

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -36,6 +36,7 @@ describe('Parser.generate', function() {
3636
nb.include(nb.value('foo'))
3737
];
3838

39+
expect(result.sources).to.deep.equal([nb.value('foo')]);
3940
expect(result.statements).to.deep.equal(expected);
4041
});
4142

@@ -45,9 +46,16 @@ describe('Parser.generate', function() {
4546
nb.include(nb.variable('foo'))
4647
];
4748

49+
expect(result.sources).to.deep.equal([nb.variable('foo')]);
4850
expect(result.statements).to.deep.equal(expected);
4951
});
5052

53+
it('should set sources from inside if blocks', function() {
54+
const result = getParserResult('{{if bar}}{{include baz}}{{fi}}');
55+
56+
expect(result.sources).to.deep.equal([nb.variable('baz')]);
57+
});
58+
5159
it('should parse with basic if statement', function() {
5260
const result = getParserResult('{{if foo}}bar{{fi}}');
5361
const expected = [

0 commit comments

Comments
 (0)