mirror of
https://github.com/marko-js/marko.git
synced 2025-12-08 19:26:05 +00:00
Lot's of improvements
All string expressions are now parsed using esprima when using the builder API
This commit is contained in:
parent
2655a67b22
commit
1bf6838c2c
@ -19,20 +19,40 @@ var HtmlElement = require('./ast/HtmlElement');
|
||||
var Html = require('./ast/Html');
|
||||
var Text = require('./ast/Text');
|
||||
var ForEach = require('./ast/ForEach');
|
||||
var ForRange = require('./ast/ForRange');
|
||||
var Slot = require('./ast/Slot');
|
||||
var HtmlComment = require('./ast/HtmlComment');
|
||||
var SelfInvokingFunction = require('./ast/SelfInvokingFunction');
|
||||
var ForStatement = require('./ast/ForStatement');
|
||||
var BinaryExpression = require('./ast/BinaryExpression');
|
||||
var UpdateExpression = require('./ast/UpdateExpression');
|
||||
var Expression = require('./ast/Expression');
|
||||
var UnaryExpression = require('./ast/UnaryExpression');
|
||||
var MemberExpression = require('./ast/MemberExpression');
|
||||
var parseExpression = require('./util/parseExpression');
|
||||
|
||||
function makeNode(arg) {
|
||||
if (typeof arg === 'string') {
|
||||
return parseExpression(arg);
|
||||
} else if (arg instanceof Node) {
|
||||
return arg;
|
||||
} else {
|
||||
return undefined;
|
||||
}
|
||||
}
|
||||
|
||||
class Builder {
|
||||
assignment(left, right) {
|
||||
return new Assignment({left, right});
|
||||
assignment(left, right, operator) {
|
||||
if (operator == null) {
|
||||
operator = '=';
|
||||
}
|
||||
left = makeNode(left);
|
||||
right = makeNode(right);
|
||||
return new Assignment({left, right, operator});
|
||||
}
|
||||
|
||||
binaryExpression(left, operator, right) {
|
||||
left = makeNode(left);
|
||||
right = makeNode(right);
|
||||
return new BinaryExpression({left, operator, right});
|
||||
}
|
||||
|
||||
@ -41,38 +61,66 @@ class Builder {
|
||||
}
|
||||
|
||||
elseIfStatement(test, body, elseStatement) {
|
||||
test = makeNode(test);
|
||||
|
||||
return new ElseIf({
|
||||
if: new If({test, body, else: elseStatement})
|
||||
});
|
||||
}
|
||||
|
||||
expression(value) {
|
||||
return new Expression({value});
|
||||
return makeNode(value);
|
||||
}
|
||||
|
||||
forEach(varName, target, body) {
|
||||
if (typeof varName === 'object') {
|
||||
var options = varName;
|
||||
return new ForEach(options);
|
||||
forEach(varName, inExpression, body) {
|
||||
if (arguments.length === 1) {
|
||||
var def = arguments[0];
|
||||
return new ForEach(def);
|
||||
} else {
|
||||
return new ForEach({varName, target, body});
|
||||
varName = makeNode(varName);
|
||||
inExpression = makeNode(inExpression);
|
||||
return new ForEach({varName, in: inExpression, body});
|
||||
}
|
||||
}
|
||||
|
||||
forRange(varName, from, to, step, body) {
|
||||
if (arguments.length === 1) {
|
||||
var def = arguments[0];
|
||||
return new ForRange(def);
|
||||
} else {
|
||||
varName = makeNode(varName);
|
||||
from = makeNode(from);
|
||||
to = makeNode(to);
|
||||
step = makeNode(step);
|
||||
body = makeNode(body);
|
||||
|
||||
return new ForRange({varName, from, to, step, body});
|
||||
}
|
||||
}
|
||||
|
||||
forStatement(init, test, update, body) {
|
||||
if (typeof init === 'object' && !init.type) {
|
||||
if (arguments.length === 1) {
|
||||
var def = arguments[0];
|
||||
return new ForStatement(def);
|
||||
} else {
|
||||
init = makeNode(init);
|
||||
test = makeNode(test);
|
||||
update = makeNode(update);
|
||||
return new ForStatement({init, test, update, body});
|
||||
}
|
||||
}
|
||||
|
||||
functionCall(callee, args) {
|
||||
callee = makeNode(callee);
|
||||
|
||||
if (args) {
|
||||
if (!isArray(args)) {
|
||||
args = [args];
|
||||
}
|
||||
|
||||
for (var i=0; i<args.length; i++) {
|
||||
args[i] = makeNode(args[i]);
|
||||
}
|
||||
} else {
|
||||
args = [];
|
||||
}
|
||||
@ -85,6 +133,8 @@ class Builder {
|
||||
}
|
||||
|
||||
html(argument) {
|
||||
argument = makeNode(argument);
|
||||
|
||||
return new Html({argument});
|
||||
}
|
||||
|
||||
@ -94,14 +144,11 @@ class Builder {
|
||||
|
||||
htmlElement(tagName, attributes, body, argument) {
|
||||
if (typeof tagName === 'object' && !(tagName instanceof Node)) {
|
||||
let elInfo = tagName;
|
||||
tagName = elInfo.tagName;
|
||||
attributes = elInfo.attributes;
|
||||
body = elInfo.body;
|
||||
argument = elInfo.argument;
|
||||
let def = arguments[0];
|
||||
return new HtmlElement(def);
|
||||
} else {
|
||||
return new HtmlElement({tagName, attributes, body, argument});
|
||||
}
|
||||
|
||||
return new HtmlElement({tagName, attributes, body, argument});
|
||||
}
|
||||
|
||||
identifier(name) {
|
||||
@ -116,6 +163,21 @@ class Builder {
|
||||
return new Literal({value});
|
||||
}
|
||||
|
||||
memberExpression(object, property, computed) {
|
||||
object = makeNode(object);
|
||||
property = makeNode(property);
|
||||
|
||||
return new MemberExpression({object, property, computed});
|
||||
}
|
||||
|
||||
negate(argument) {
|
||||
argument = makeNode(argument);
|
||||
|
||||
var operator = '!';
|
||||
var prefix = true;
|
||||
return new UnaryExpression({argument, operator, prefix});
|
||||
}
|
||||
|
||||
node(type, generateCode) {
|
||||
if (typeof type === 'function') {
|
||||
generateCode = arguments[0];
|
||||
@ -134,12 +196,16 @@ class Builder {
|
||||
}
|
||||
|
||||
require(path) {
|
||||
path = makeNode(path);
|
||||
|
||||
let callee = 'require';
|
||||
let args = [ path ];
|
||||
return new FunctionCall({callee, args});
|
||||
}
|
||||
|
||||
returnStatement(argument) {
|
||||
argument = makeNode(argument);
|
||||
|
||||
return new Return({argument});
|
||||
}
|
||||
|
||||
@ -158,6 +224,9 @@ class Builder {
|
||||
}
|
||||
|
||||
strictEquality(left, right) {
|
||||
left = makeNode(left);
|
||||
right = makeNode(right);
|
||||
|
||||
var operator = '===';
|
||||
return new BinaryExpression({left, right, operator});
|
||||
}
|
||||
@ -167,14 +236,48 @@ class Builder {
|
||||
}
|
||||
|
||||
text(argument, escape) {
|
||||
argument = makeNode(argument);
|
||||
|
||||
return new Text({argument, escape});
|
||||
}
|
||||
|
||||
unaryExpression(argument, operator, prefix) {
|
||||
argument = makeNode(argument);
|
||||
|
||||
return new UnaryExpression({argument, operator, prefix});
|
||||
}
|
||||
|
||||
updateExpression(argument, operator, prefix) {
|
||||
argument = makeNode(argument);
|
||||
return new UpdateExpression({argument, operator, prefix});
|
||||
}
|
||||
|
||||
vars(declarations, kind) {
|
||||
if (declarations) {
|
||||
if (Array.isArray(declarations)) {
|
||||
for (let i=0; i<declarations.length; i++) {
|
||||
var declaration = declarations[i];
|
||||
if (typeof declaration === 'string') {
|
||||
declarations[i] = {
|
||||
id: makeNode(declaration)
|
||||
};
|
||||
} else if (declaration instanceof Identifier) {
|
||||
declarations[i] = {
|
||||
id: declaration
|
||||
};
|
||||
}
|
||||
}
|
||||
} else if (typeof declarations === 'object') {
|
||||
// Convert the object into an array of variables
|
||||
declarations = Object.keys(declarations).map((key) => {
|
||||
let id = new Identifier({name: key});
|
||||
let init = makeNode(declarations[key]);
|
||||
return { id, init };
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
return new Vars({declarations, kind});
|
||||
}
|
||||
}
|
||||
|
||||
@ -7,15 +7,32 @@ class Assignment extends Node {
|
||||
super('Assignment');
|
||||
this.left = def.left;
|
||||
this.right = def.right;
|
||||
this.operator = def.operator;
|
||||
}
|
||||
|
||||
generateCode(generator) {
|
||||
var left = this.left;
|
||||
var right = this.right;
|
||||
var operator = this.operator;
|
||||
|
||||
generator.generateCode(left);
|
||||
generator.write(' = ');
|
||||
generator.write(' ' + (operator || '=') + ' ');
|
||||
|
||||
var wrap = right instanceof Assignment;
|
||||
|
||||
if (wrap) {
|
||||
generator.write('(');
|
||||
}
|
||||
|
||||
generator.generateCode(right);
|
||||
|
||||
if (wrap) {
|
||||
generator.write(')');
|
||||
}
|
||||
}
|
||||
|
||||
isCompoundExpression() {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@ -1,6 +1,21 @@
|
||||
'use strict';
|
||||
|
||||
var Node = require('./Node');
|
||||
var isCompoundExpression = require('../util/isCompoundExpression');
|
||||
|
||||
function generateCodeForOperand(node, generator) {
|
||||
var wrap = isCompoundExpression(node);
|
||||
|
||||
if (wrap) {
|
||||
generator.write('(');
|
||||
}
|
||||
|
||||
generator.generateCode(node);
|
||||
|
||||
if (wrap) {
|
||||
generator.write(')');
|
||||
}
|
||||
}
|
||||
|
||||
class BinaryExpression extends Node {
|
||||
constructor(def) {
|
||||
@ -8,35 +23,26 @@ class BinaryExpression extends Node {
|
||||
this.left = def.left;
|
||||
this.operator = def.operator;
|
||||
this.right = def.right;
|
||||
this.parens = def.parens === true;
|
||||
}
|
||||
|
||||
generateCode(generator) {
|
||||
var left = this.left;
|
||||
var operator = this.operator;
|
||||
var right = this.right;
|
||||
var parens = this.parens || this.data.isSubExpression;
|
||||
|
||||
if (left instanceof Node) {
|
||||
left.data.isSubExpression = true;
|
||||
if (!left || !right) {
|
||||
throw new Error('Invalid BinaryExpression: ' + this);
|
||||
}
|
||||
|
||||
if (right instanceof Node) {
|
||||
right.data.isSubExpression = true;
|
||||
}
|
||||
|
||||
if (parens) {
|
||||
generator.write('(');
|
||||
}
|
||||
|
||||
generator.generateCode(this.left);
|
||||
generateCodeForOperand(left, generator);
|
||||
generator.write(' ');
|
||||
generator.generateCode(this.operator);
|
||||
generator.generateCode(operator);
|
||||
generator.write(' ');
|
||||
generator.generateCode(this.right);
|
||||
generateCodeForOperand(right, generator);
|
||||
}
|
||||
|
||||
if (parens) {
|
||||
generator.write(')');
|
||||
}
|
||||
isCompoundExpression() {
|
||||
return true;
|
||||
}
|
||||
|
||||
toJSON() {
|
||||
|
||||
@ -1,16 +1,22 @@
|
||||
'use strict';
|
||||
|
||||
var Node = require('./Node');
|
||||
var ok = require('assert').ok;
|
||||
|
||||
class Expression extends Node {
|
||||
constructor(def) {
|
||||
super('Html');
|
||||
super('Expression');
|
||||
this.value = def.value;
|
||||
ok(this.value != null, 'Invalid expression');
|
||||
}
|
||||
|
||||
generateCode(generator) {
|
||||
generator.generateCode(this.value);
|
||||
}
|
||||
|
||||
isCompoundExpression() {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
module.exports = Expression;
|
||||
@ -6,66 +6,23 @@ class ForEach extends Node {
|
||||
constructor(def) {
|
||||
super('ForEach');
|
||||
this.varName = def.varName;
|
||||
this.target = def.target;
|
||||
this.in = def.in;
|
||||
this.body = this.makeContainer(def.body);
|
||||
this.separator = def.separator;
|
||||
this.statusVarName = def.statusVarName;
|
||||
this.from = def.from;
|
||||
this.to = def.to;
|
||||
this.step = def.step;
|
||||
|
||||
ok(this.varName, '"varName" is required');
|
||||
ok(this.target != null || this.from != null, '"target" or "from" is required');
|
||||
ok(this.in != null, '"in" is required');
|
||||
}
|
||||
|
||||
generateCode(generator) {
|
||||
var varName = this.varName;
|
||||
var target = this.target;
|
||||
var inExpression = this.in;
|
||||
var separator = this.separator;
|
||||
var statusVarName = this.statusVarName;
|
||||
|
||||
var builder = generator.builder;
|
||||
|
||||
if (this.from != null) {
|
||||
// This is a range loop
|
||||
var from = this.from;
|
||||
var to = this.to;
|
||||
var step = this.step;
|
||||
var comparison = '<=';
|
||||
|
||||
if (typeof step === 'number') {
|
||||
if (step < 0) {
|
||||
comparison = '>=';
|
||||
}
|
||||
|
||||
if (step === 1) {
|
||||
step = varName + '++';
|
||||
} else if (step === -1) {
|
||||
step = varName + '--';
|
||||
} else if (step > 0) {
|
||||
step = varName + '+=' + step;
|
||||
} else if (step === 0) {
|
||||
throw new Error('Invalid step of 0');
|
||||
} else if (step < 0) {
|
||||
step = 0-step; // Make the step positive and switch to -=
|
||||
step = varName + '-=' + step;
|
||||
}
|
||||
} else {
|
||||
step = varName + '+=' + step;
|
||||
}
|
||||
|
||||
return builder.selfInvokingFunction([
|
||||
builder.forStatement({
|
||||
init: [
|
||||
builder.vars([ { id: varName, init: from }])
|
||||
],
|
||||
test: varName + comparison + to,
|
||||
update: step,
|
||||
body: this.body
|
||||
})
|
||||
]);
|
||||
}
|
||||
|
||||
if (separator && !statusVarName) {
|
||||
statusVarName = '__loop';
|
||||
}
|
||||
@ -75,22 +32,28 @@ class ForEach extends Node {
|
||||
let body = this.body;
|
||||
|
||||
if (separator) {
|
||||
let isNotLastTest = builder.functionCall(
|
||||
builder.memberExpression(statusVarName, builder.identifier('isLast')),
|
||||
[]);
|
||||
|
||||
isNotLastTest = builder.negate(isNotLastTest);
|
||||
|
||||
body = body.items.concat([
|
||||
builder.ifStatement('!' + statusVarName + '.isLast()', [
|
||||
builder.ifStatement(isNotLastTest, [
|
||||
builder.text(separator)
|
||||
])
|
||||
]);
|
||||
}
|
||||
|
||||
return builder.functionCall(forEachVarName, [
|
||||
target,
|
||||
inExpression,
|
||||
builder.functionDeclaration(null, [varName, statusVarName], body)
|
||||
]);
|
||||
} else {
|
||||
let forEachVarName = generator.addStaticVar('forEach', '__helpers.f');
|
||||
|
||||
return builder.functionCall(forEachVarName, [
|
||||
target,
|
||||
inExpression,
|
||||
builder.functionDeclaration(null, [varName], this.body)
|
||||
]);
|
||||
}
|
||||
|
||||
97
compiler/ast/ForRange.js
Normal file
97
compiler/ast/ForRange.js
Normal file
@ -0,0 +1,97 @@
|
||||
'use strict';
|
||||
var ok = require('assert').ok;
|
||||
var Node = require('./Node');
|
||||
var Literal = require('./Literal');
|
||||
var Identifier = require('./Identifier');
|
||||
|
||||
class ForRange extends Node {
|
||||
constructor(def) {
|
||||
super('ForRange');
|
||||
this.varName = def.varName;
|
||||
this.body = this.makeContainer(def.body);
|
||||
this.from = def.from;
|
||||
this.to = def.to;
|
||||
this.step = def.step;
|
||||
|
||||
ok(this.varName, '"varName" is required');
|
||||
ok(this.from != null, '"from" is required');
|
||||
}
|
||||
|
||||
generateCode(generator) {
|
||||
var varName = this.varName;
|
||||
var from = this.from;
|
||||
var to = this.to;
|
||||
var step = this.step;
|
||||
|
||||
var builder = generator.builder;
|
||||
|
||||
var comparison = '<=';
|
||||
|
||||
if (varName instanceof Identifier) {
|
||||
varName = varName.name;
|
||||
}
|
||||
|
||||
var updateExpression;
|
||||
|
||||
if (step == null) {
|
||||
let fromLiteral = (from instanceof Literal) && from.value;
|
||||
let toLiteral = (to instanceof Literal) && to.value;
|
||||
|
||||
if (typeof fromLiteral === 'number' && typeof toLiteral === 'number') {
|
||||
if (fromLiteral > toLiteral) {
|
||||
updateExpression = varName + '--';
|
||||
comparison = '>=';
|
||||
} else {
|
||||
updateExpression = varName + '++';
|
||||
}
|
||||
}
|
||||
} else {
|
||||
let stepLiteral;
|
||||
|
||||
if (step instanceof Literal) {
|
||||
stepLiteral = step.value;
|
||||
} else if (typeof step === 'number') {
|
||||
stepLiteral = step;
|
||||
}
|
||||
|
||||
if (typeof stepLiteral === 'number') {
|
||||
if (stepLiteral < 0) {
|
||||
comparison = '>=';
|
||||
}
|
||||
|
||||
if (stepLiteral === 1) {
|
||||
updateExpression = varName + '++';
|
||||
} else if (stepLiteral === -1) {
|
||||
updateExpression = varName + '--';
|
||||
} else if (stepLiteral > 0) {
|
||||
updateExpression = varName + ' += ' + stepLiteral;
|
||||
} else if (stepLiteral === 0) {
|
||||
throw new Error('Invalid step of 0');
|
||||
} else if (stepLiteral < 0) {
|
||||
stepLiteral = 0-stepLiteral; // Make the step positive and switch to -=
|
||||
updateExpression = varName + ' -= ' + stepLiteral;
|
||||
}
|
||||
} else {
|
||||
updateExpression = builder.assignment(varName, step, '+=');
|
||||
}
|
||||
}
|
||||
|
||||
if (updateExpression == null) {
|
||||
updateExpression = varName + '++';
|
||||
}
|
||||
|
||||
return builder.selfInvokingFunction([
|
||||
builder.forStatement({
|
||||
init: [
|
||||
builder.vars([ { id: varName, init: from }])
|
||||
],
|
||||
test: builder.binaryExpression(varName, comparison, to),
|
||||
update: updateExpression,
|
||||
body: this.body
|
||||
})
|
||||
]);
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
module.exports = ForRange;
|
||||
@ -51,6 +51,10 @@ class FunctionDeclaration extends Node {
|
||||
generator.write('\n');
|
||||
}
|
||||
}
|
||||
|
||||
isCompoundExpression() {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
module.exports = FunctionDeclaration;
|
||||
@ -14,8 +14,6 @@ class Html extends Node {
|
||||
|
||||
generateHtmlCode(generator) {
|
||||
let argument = this.argument;
|
||||
|
||||
console.log(module.id, 'Html', argument);
|
||||
generator.addWrite(argument);
|
||||
}
|
||||
}
|
||||
|
||||
40
compiler/ast/MemberExpression.js
Normal file
40
compiler/ast/MemberExpression.js
Normal file
@ -0,0 +1,40 @@
|
||||
'use strict';
|
||||
|
||||
var Node = require('./Node');
|
||||
|
||||
class MemberExpression extends Node {
|
||||
constructor(def) {
|
||||
super('MemberExpression');
|
||||
this.object = def.object;
|
||||
this.property = def.property;
|
||||
this.computed = def.computed;
|
||||
}
|
||||
|
||||
generateCode(generator) {
|
||||
var object = this.object;
|
||||
var property = this.property;
|
||||
var computed = this.computed;
|
||||
|
||||
generator.generateCode(object);
|
||||
|
||||
if (computed) {
|
||||
generator.write('[');
|
||||
generator.generateCode(property);
|
||||
generator.write(']');
|
||||
} else {
|
||||
generator.write('.');
|
||||
generator.generateCode(property);
|
||||
}
|
||||
}
|
||||
|
||||
toJSON() {
|
||||
return {
|
||||
type: 'MemberExpression',
|
||||
object: this.object,
|
||||
property: this.property,
|
||||
computed: this.computed
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
module.exports = MemberExpression;
|
||||
@ -84,6 +84,14 @@ class Node {
|
||||
this.container.removeChild(this);
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns true if the current node represents a compound expression (e.g. )
|
||||
* @return {Boolean} [description]
|
||||
*/
|
||||
isCompoundExpression() {
|
||||
return false;
|
||||
}
|
||||
|
||||
isDetached() {
|
||||
return this.container == null;
|
||||
}
|
||||
|
||||
58
compiler/ast/UnaryExpression.js
Normal file
58
compiler/ast/UnaryExpression.js
Normal file
@ -0,0 +1,58 @@
|
||||
'use strict';
|
||||
|
||||
var Node = require('./Node');
|
||||
var isCompoundExpression = require('../util/isCompoundExpression');
|
||||
|
||||
class UnaryExpression extends Node {
|
||||
constructor(def) {
|
||||
super('UnaryExpression');
|
||||
this.argument = def.argument;
|
||||
this.operator = def.operator;
|
||||
this.prefix = def.prefix === true;
|
||||
}
|
||||
|
||||
generateCode(generator) {
|
||||
var argument = this.argument;
|
||||
var operator = this.operator;
|
||||
var prefix = this.prefix;
|
||||
|
||||
if (prefix) {
|
||||
generator.write(operator);
|
||||
|
||||
if (operator === 'typeof') {
|
||||
generator.write(' ');
|
||||
}
|
||||
}
|
||||
|
||||
var wrap = isCompoundExpression(argument);
|
||||
|
||||
if (wrap) {
|
||||
generator.write('(');
|
||||
}
|
||||
|
||||
generator.generateCode(argument);
|
||||
|
||||
if (wrap) {
|
||||
generator.write(')');
|
||||
}
|
||||
|
||||
if (!prefix) {
|
||||
generator.write(operator);
|
||||
}
|
||||
}
|
||||
|
||||
isCompoundExpression() {
|
||||
return true;
|
||||
}
|
||||
|
||||
toJSON() {
|
||||
return {
|
||||
type: 'UnaryExpression',
|
||||
argument: this.argument,
|
||||
operator: this.operator,
|
||||
prefix: this.prefix
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
module.exports = UnaryExpression;
|
||||
@ -1,6 +1,7 @@
|
||||
'use strict';
|
||||
|
||||
var Node = require('./Node');
|
||||
var isCompoundExpression = require('../util/isCompoundExpression');
|
||||
|
||||
class UpdateExpression extends Node {
|
||||
constructor(def) {
|
||||
@ -19,13 +20,27 @@ class UpdateExpression extends Node {
|
||||
generator.generateCode(operator);
|
||||
}
|
||||
|
||||
var wrap = isCompoundExpression(argument);
|
||||
|
||||
if (wrap) {
|
||||
generator.write('(');
|
||||
}
|
||||
|
||||
generator.generateCode(argument);
|
||||
|
||||
if (wrap) {
|
||||
generator.write(')');
|
||||
}
|
||||
|
||||
if (!prefix) {
|
||||
generator.generateCode(operator);
|
||||
}
|
||||
}
|
||||
|
||||
isCompoundExpression() {
|
||||
return true;
|
||||
}
|
||||
|
||||
toJSON() {
|
||||
return {
|
||||
type: 'UpdateExpression',
|
||||
|
||||
@ -24,14 +24,6 @@ class Vars extends Node {
|
||||
return generator.builder.selfInvokingFunction([ this ]);
|
||||
}
|
||||
|
||||
if (declarations && !Array.isArray(declarations) && typeof declarations === 'object') {
|
||||
// Convert the object into an array of variables
|
||||
declarations = Object.keys(declarations).map((id) => {
|
||||
let init = declarations[id];
|
||||
return { id, init };
|
||||
});
|
||||
}
|
||||
|
||||
if (!declarations || !declarations.length) {
|
||||
return;
|
||||
}
|
||||
@ -39,12 +31,6 @@ class Vars extends Node {
|
||||
for (let i=0; i<declarations.length; i++) {
|
||||
var declaration = declarations[i];
|
||||
|
||||
if (typeof declaration === 'string' || declaration instanceof Identifier) {
|
||||
declaration = {
|
||||
id: declaration
|
||||
};
|
||||
}
|
||||
|
||||
if (i === 0) {
|
||||
generator.write(kind + ' ');
|
||||
} else {
|
||||
@ -52,14 +38,14 @@ class Vars extends Node {
|
||||
generator.writeLineIndent();
|
||||
}
|
||||
|
||||
var varName = declaration.id || declaration.name;
|
||||
var varId = declaration.id || declaration.name;
|
||||
|
||||
if (typeof varName !== 'string') {
|
||||
throw new Error('Invalid variable name: ' + varName);
|
||||
if (!(varId instanceof Identifier) && typeof varId !== 'string') {
|
||||
throw new Error('Invalid variable name: ' + varId);
|
||||
}
|
||||
|
||||
// TODO Validate the variable name
|
||||
generator.generateCode(varName);
|
||||
generator.generateCode(varId);
|
||||
|
||||
var initValue;
|
||||
if (declaration.hasOwnProperty('init')) {
|
||||
|
||||
@ -124,6 +124,7 @@ exports.compile = compile;
|
||||
exports.defaultOptions = defaultOptions;
|
||||
exports.checkUpToDate = checkUpToDate;
|
||||
exports.createWalker = createWalker;
|
||||
exports.defaultBuilder = defaultBuilder;
|
||||
|
||||
var taglibLookup = require('./taglib-lookup');
|
||||
taglibLookup.registerTaglib(require.resolve('../taglibs/core/marko-taglib.json'));
|
||||
|
||||
10
compiler/util/isCompoundExpression.js
Normal file
10
compiler/util/isCompoundExpression.js
Normal file
@ -0,0 +1,10 @@
|
||||
function isCompoundExpression(expression) {
|
||||
if (typeof expression === 'string') {
|
||||
// TBD: Should we use Esprima to parse the expression string to see if it is a compount expression?
|
||||
return true;
|
||||
}
|
||||
|
||||
return expression.isCompoundExpression();
|
||||
}
|
||||
|
||||
module.exports = isCompoundExpression;
|
||||
88
compiler/util/parseExpression.js
Normal file
88
compiler/util/parseExpression.js
Normal file
@ -0,0 +1,88 @@
|
||||
'use strict';
|
||||
|
||||
const esprima = require('esprima');
|
||||
const Expression = require('../ast/Expression');
|
||||
var compiler = require('../');
|
||||
|
||||
function convert(node) {
|
||||
var builder = compiler.defaultBuilder;
|
||||
|
||||
switch(node.type) {
|
||||
case 'Program': {
|
||||
if (node.body && node.body.length === 1) {
|
||||
return convert(node.body[0]);
|
||||
}
|
||||
return null;
|
||||
}
|
||||
case 'ExpressionStatement': {
|
||||
return convert(node.expression);
|
||||
}
|
||||
case 'Identifier': {
|
||||
return builder.identifier(node.name);
|
||||
}
|
||||
case 'Literal': {
|
||||
return builder.literal(node.value);
|
||||
}
|
||||
case 'BinaryExpression': {
|
||||
let left = convert(node.left);
|
||||
if (!left) {
|
||||
return null;
|
||||
}
|
||||
|
||||
let right = convert(node.right);
|
||||
if (!right) {
|
||||
return null;
|
||||
}
|
||||
|
||||
return builder.binaryExpression(left, node.operator, right);
|
||||
}
|
||||
case 'UnaryExpression': {
|
||||
let argument = convert(node.argument);
|
||||
if (!argument) {
|
||||
return null;
|
||||
}
|
||||
|
||||
return builder.unaryExpression(argument, node.operator, node.prefix);
|
||||
}
|
||||
case 'AssignmentExpression': {
|
||||
let left = convert(node.left);
|
||||
if (!left) {
|
||||
return null;
|
||||
}
|
||||
|
||||
let right = convert(node.right);
|
||||
if (!right) {
|
||||
return null;
|
||||
}
|
||||
|
||||
return builder.assignment(left, right, node.operator);
|
||||
}
|
||||
case 'MemberExpression': {
|
||||
let object = convert(node.object);
|
||||
if (!object) {
|
||||
return null;
|
||||
}
|
||||
|
||||
let property = convert(node.property);
|
||||
if (!property) {
|
||||
return null;
|
||||
}
|
||||
|
||||
return builder.memberExpression(object, property, node.computed);
|
||||
}
|
||||
default:
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
function parseExpression(src) {
|
||||
let jsAST = esprima.parse(src);
|
||||
var converted = convert(jsAST);
|
||||
if (converted == null) {
|
||||
converted = new Expression({value: src});
|
||||
}
|
||||
|
||||
return converted;
|
||||
}
|
||||
|
||||
module.exports = parseExpression;
|
||||
@ -182,14 +182,18 @@ forEach(data.colors, function(color) {
|
||||
});
|
||||
```
|
||||
|
||||
___object properties:___
|
||||
### forEach(varName, target, body)
|
||||
|
||||
TBD
|
||||
Returns a node that generates a simple `forEach`. See `forEach(def)`.
|
||||
|
||||
___range:___
|
||||
### forRange(def)
|
||||
|
||||
Returns a node that generates code to loop over a number range
|
||||
|
||||
___array:___
|
||||
|
||||
```javascript
|
||||
builder.forEach({
|
||||
builder.forRange({
|
||||
varName: 'i',
|
||||
from: 0,
|
||||
to: 'myArray.length',
|
||||
@ -199,6 +203,7 @@ builder.forEach({
|
||||
]
|
||||
})
|
||||
|
||||
|
||||
// Output code:
|
||||
(function() {
|
||||
for (var i = 0; i<=myArray.length; i+=2) {
|
||||
@ -207,9 +212,9 @@ builder.forEach({
|
||||
}());
|
||||
```
|
||||
|
||||
### forEach(varName, target, body)
|
||||
### forRange(varName, from, to, step, body)
|
||||
|
||||
Returns a node that generates a simple `forEach`. See `forEach(def)`.
|
||||
Returns a node that generates a simple `forRange`. See `forRange(def)`.
|
||||
|
||||
### forStatement(init, test, update, body)
|
||||
|
||||
@ -443,6 +448,23 @@ var aString = "abc",
|
||||
]
|
||||
```
|
||||
|
||||
### negate(argument)
|
||||
|
||||
Returns a node that generates the following code:
|
||||
|
||||
```javascript
|
||||
!<argument>
|
||||
```
|
||||
|
||||
For example:
|
||||
|
||||
```javascript
|
||||
builder.negate(builder.identifier('foo'))
|
||||
|
||||
// Output:
|
||||
!foo
|
||||
```
|
||||
|
||||
### node([type, ]generatCode)
|
||||
|
||||
Returns a generic `Node` instance with the given node type (optional) and a `generateCode(node, generator)` function that should be used to generate the code for the node. If a `generateCode(node, generator)` function is not provided the node bust be monkey-patched to add a `generateCode(generator)` method.
|
||||
@ -624,6 +646,8 @@ a === b
|
||||
|
||||
### text(argument, escape)
|
||||
|
||||
### unaryExpression(argument, operator, prefix)
|
||||
|
||||
### updateExpression(argument, operator, prefix)
|
||||
|
||||
### vars(declarations, kind)
|
||||
|
||||
@ -1,9 +1,6 @@
|
||||
'use strict';
|
||||
|
||||
var extend = require('raptor-util/extend');
|
||||
var parseComplexAttribute = require('./util/parseComplexAttribute');
|
||||
var parseForEach = require('./util/parseForEach');
|
||||
var parseJavaScriptIdentifier = require('./util/parseJavaScriptIdentifier');
|
||||
var createLoopNode = require('./util/createLoopNode');
|
||||
|
||||
var coreAttrHandlers = [
|
||||
[
|
||||
@ -13,49 +10,12 @@ var coreAttrHandlers = [
|
||||
return;
|
||||
}
|
||||
|
||||
var forEachProps = parseComplexAttribute(forArgument, {
|
||||
'each': true,
|
||||
'separator': true,
|
||||
'iterator': true,
|
||||
'status-var': true,
|
||||
'for-loop': true
|
||||
},
|
||||
{
|
||||
removeDashes: true,
|
||||
defaultName: 'each',
|
||||
errorHandler: function (message) {
|
||||
this.addError('Invalid for attribute of "' + attr + '". Error: ' + message);
|
||||
}
|
||||
});
|
||||
var loopNode = createLoopNode(forArgument, null, this.builder);
|
||||
|
||||
if (!forEachProps.each) {
|
||||
this.addError('Invalid "for" attribute.');
|
||||
}
|
||||
|
||||
var statusVarName = forEachProps.statusVar;
|
||||
|
||||
if (statusVarName) {
|
||||
// statusVar is expected to be a String literal expression
|
||||
// For example: statusVar: "'foo'"
|
||||
// We need to parse it into an actual string such as "foo"
|
||||
statusVarName = parseJavaScriptIdentifier(statusVarName);
|
||||
if (!statusVarName) {
|
||||
this.addError('Invalid "status-var": ' + forEachProps.statusVar);
|
||||
}
|
||||
delete forEachProps.statusVar;
|
||||
forEachProps.statusVarName = statusVarName;
|
||||
}
|
||||
|
||||
var parsedForEach = parseForEach(forEachProps.each);
|
||||
delete forEachProps.each;
|
||||
extend(forEachProps, parsedForEach);
|
||||
|
||||
forEachProps.pos = node.pos;
|
||||
|
||||
//Copy the position property
|
||||
var forEachNode = this.builder.forEach(forEachProps);
|
||||
//Surround the existing node with a "forEach" node
|
||||
node.wrap(forEachNode);
|
||||
//Surround the existing node with the newly created loop node
|
||||
// NOTE: The loop node will be one of the following:
|
||||
// ForEach, ForRange, ForEachProp or ForStatement
|
||||
node.wrap(loopNode);
|
||||
}
|
||||
],
|
||||
[
|
||||
|
||||
@ -1,4 +1,4 @@
|
||||
var parseForEach = require('./util/parseForEach');
|
||||
var createLoopNode = require('./util/createLoopNode');
|
||||
|
||||
module.exports = function codeGenerator(elNode, generator) {
|
||||
var argument = elNode.argument;
|
||||
@ -7,16 +7,14 @@ module.exports = function codeGenerator(elNode, generator) {
|
||||
return elNode;
|
||||
}
|
||||
|
||||
var forEachProps = parseForEach(argument);
|
||||
forEachProps.body = elNode.body;
|
||||
var builder = generator.builder;
|
||||
|
||||
if (elNode.hasAttribute('separator')) {
|
||||
forEachProps.separator = elNode.getAttributeValue('separator');
|
||||
var loopNode = createLoopNode(argument, elNode.body, builder);
|
||||
|
||||
if (loopNode.error) {
|
||||
generator.addError(loopNode.error);
|
||||
return elNode;
|
||||
}
|
||||
|
||||
if (elNode.hasAttribute('status-var')) {
|
||||
forEachProps.statusVarName = elNode.getAttributeValue('status-var');
|
||||
}
|
||||
|
||||
return generator.builder.forEach(forEachProps);
|
||||
return loopNode;
|
||||
};
|
||||
23
taglibs/core/util/createLoopNode.js
Normal file
23
taglibs/core/util/createLoopNode.js
Normal file
@ -0,0 +1,23 @@
|
||||
var parseFor = require('./parseFor');
|
||||
|
||||
function createLoopNode(str, body, builder) {
|
||||
var forDef = parseFor(str);
|
||||
|
||||
if (forDef.error) {
|
||||
return forDef;
|
||||
}
|
||||
|
||||
forDef.body = body;
|
||||
|
||||
if (forDef.loopType === 'ForEach') {
|
||||
return builder.forEach(forDef);
|
||||
} else if (forDef.loopType === 'ForRange') {
|
||||
return builder.forRange(forDef);
|
||||
} else if (forDef.loopType === 'For') {
|
||||
return builder.forStatement(forDef);
|
||||
} else {
|
||||
throw new Error('Unsupported loop type: ' + forDef.loopType);
|
||||
}
|
||||
}
|
||||
|
||||
module.exports = createLoopNode;
|
||||
312
taglibs/core/util/parseFor.js
Normal file
312
taglibs/core/util/parseFor.js
Normal file
@ -0,0 +1,312 @@
|
||||
'use strict';
|
||||
var Expression = require('../../../compiler/ast/Expression');
|
||||
var Literal = require('../../../compiler/ast/Literal');
|
||||
var Identifier = require('../../../compiler/ast/Identifier');
|
||||
var removeComments = require('./removeComments');
|
||||
|
||||
var integerRegExp = /^-?\d+$/;
|
||||
var numberRegExp = /^-?(?:\d+|\d+\.\d*|\d*\.\d+|\d+\.\d+)$/;
|
||||
|
||||
var tokenizer = require('./tokenizer').create([
|
||||
{
|
||||
name: 'stringDouble',
|
||||
pattern: /"(?:[^"]|\\")*"/,
|
||||
},
|
||||
{
|
||||
name: 'stringSingle',
|
||||
pattern: /'(?:[^']|\\')*'/
|
||||
},
|
||||
{
|
||||
name: 'in',
|
||||
pattern: /\s+in\s+/,
|
||||
},
|
||||
{
|
||||
name: 'from',
|
||||
pattern: /\s+from\s+/
|
||||
},
|
||||
{
|
||||
name: 'to',
|
||||
pattern: /\s+to\s+/,
|
||||
},
|
||||
{
|
||||
name: 'step',
|
||||
pattern: /\s+step\s+/,
|
||||
},
|
||||
{
|
||||
name: 'semicolon',
|
||||
pattern: /[;]/,
|
||||
},
|
||||
{
|
||||
name: 'separator',
|
||||
pattern: /separator=/
|
||||
},
|
||||
{
|
||||
name: 'status-var',
|
||||
pattern: /status\-var=/
|
||||
},
|
||||
{
|
||||
name: 'iterator',
|
||||
pattern: /iterator=/
|
||||
},
|
||||
{
|
||||
name: 'pipe',
|
||||
pattern: /\s+\|\s+/
|
||||
},
|
||||
{
|
||||
name: 'groupOpen',
|
||||
pattern: /[\{\(\[]/
|
||||
},
|
||||
{
|
||||
name: 'groupClose',
|
||||
pattern: /[\}\)\]]/
|
||||
}
|
||||
]);
|
||||
|
||||
|
||||
function createNumberExpression(str) {
|
||||
if (str == null) {
|
||||
return null;
|
||||
}
|
||||
|
||||
if (integerRegExp.test(str)) {
|
||||
return new Literal({value: parseInt(str, 10)});
|
||||
} else if (numberRegExp.test(str)) {
|
||||
return new Literal({value: parseFloat(str)});
|
||||
} else {
|
||||
return new Expression({value: str});
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Parses a for loop string in the following forms:
|
||||
*
|
||||
* <varName> in <expression>
|
||||
* <varName> in <expression> | status-var=<varName> separator=<expression>
|
||||
* <varName> from <expression> to <expression>
|
||||
* <varName> from <expression> to <expression> step <expression>
|
||||
* <init>; <test>; <update>
|
||||
*/
|
||||
module.exports = function(str) {
|
||||
str = removeComments(str);
|
||||
|
||||
let depth = 0;
|
||||
var prevToken;
|
||||
var loopType;
|
||||
var pipeFound = false;
|
||||
|
||||
var varName;
|
||||
var nameVarName;
|
||||
var valueVarName;
|
||||
var inExpression;
|
||||
var statusVarName;
|
||||
var separatorExpression;
|
||||
var fromExpression;
|
||||
var toExpression;
|
||||
var stepExpression;
|
||||
var iteratorExpression;
|
||||
|
||||
var forInit;
|
||||
var forTest;
|
||||
var forUpdate;
|
||||
|
||||
function finishVarName(end) {
|
||||
varName = str.substring(0, end).trim();
|
||||
}
|
||||
|
||||
function finishPrevPart(end) {
|
||||
if (!prevToken) {
|
||||
return;
|
||||
}
|
||||
|
||||
var start = prevToken.end;
|
||||
var part = str.substring(start, end).trim();
|
||||
|
||||
switch(prevToken.name) {
|
||||
case 'from':
|
||||
fromExpression = part;
|
||||
break;
|
||||
case 'to':
|
||||
toExpression = part;
|
||||
break;
|
||||
case 'in':
|
||||
inExpression = part;
|
||||
break;
|
||||
case 'step':
|
||||
stepExpression = part;
|
||||
break;
|
||||
case 'status-var':
|
||||
statusVarName = part;
|
||||
break;
|
||||
case 'separator':
|
||||
separatorExpression = part;
|
||||
break;
|
||||
case 'iterator':
|
||||
iteratorExpression = part;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
tokenizer.forEachToken(str, (token) => {
|
||||
switch(token.name) {
|
||||
case 'groupOpen':
|
||||
depth++;
|
||||
break;
|
||||
case 'groupClose':
|
||||
depth--;
|
||||
break;
|
||||
case 'in':
|
||||
if (depth === 0 && !loopType) {
|
||||
loopType = 'ForEach';
|
||||
finishVarName(token.start);
|
||||
prevToken = token;
|
||||
}
|
||||
break;
|
||||
case 'from':
|
||||
if (depth === 0 && !loopType) {
|
||||
loopType = 'ForRange';
|
||||
finishVarName(token.start);
|
||||
prevToken = token;
|
||||
}
|
||||
break;
|
||||
case 'to':
|
||||
if (depth === 0 && prevToken && prevToken.name === 'from') {
|
||||
finishPrevPart(token.start);
|
||||
prevToken = token;
|
||||
}
|
||||
break;
|
||||
case 'step':
|
||||
if (depth === 0 && prevToken && prevToken.name === 'to') {
|
||||
finishPrevPart(token.start);
|
||||
prevToken = token;
|
||||
}
|
||||
break;
|
||||
case 'semicolon':
|
||||
if (depth === 0) {
|
||||
loopType = 'For';
|
||||
|
||||
if (forInit == null) {
|
||||
forInit = str.substring(0, token.start);
|
||||
} else if (forTest == null) {
|
||||
forTest = str.substring(prevToken.end, token.start);
|
||||
forUpdate = str.substring(token.end);
|
||||
} else {
|
||||
return {
|
||||
error: 'Invalid native for loop. Expected format: <init>; <test>; <update>'
|
||||
};
|
||||
}
|
||||
|
||||
prevToken = token;
|
||||
}
|
||||
break;
|
||||
case 'pipe':
|
||||
if (depth === 0) {
|
||||
pipeFound = true;
|
||||
finishPrevPart(token.start);
|
||||
prevToken = token;
|
||||
}
|
||||
break;
|
||||
case 'status-var':
|
||||
if (depth === 0 && pipeFound && str.charAt(token.start-1) === ' ') {
|
||||
finishPrevPart(token.start);
|
||||
prevToken = token;
|
||||
}
|
||||
break;
|
||||
case 'separator':
|
||||
if (depth === 0 && pipeFound && str.charAt(token.start-1) === ' ') {
|
||||
finishPrevPart(token.start);
|
||||
prevToken = token;
|
||||
}
|
||||
break;
|
||||
case 'iterator':
|
||||
if (depth === 0 && pipeFound && str.charAt(token.start-1) === ' ') {
|
||||
finishPrevPart(token.start);
|
||||
prevToken = token;
|
||||
}
|
||||
break;
|
||||
}
|
||||
});
|
||||
|
||||
finishPrevPart(str.length);
|
||||
|
||||
if (loopType === 'ForEach') {
|
||||
var nameValue = varName.split(',');
|
||||
if (nameValue.length === 2) {
|
||||
nameVarName = new Identifier({name: nameValue[0]});
|
||||
valueVarName = new Identifier({name: nameValue[1]});
|
||||
loopType = 'ForEachProp';
|
||||
}
|
||||
}
|
||||
|
||||
if (inExpression) {
|
||||
inExpression = new Expression({value: inExpression});
|
||||
}
|
||||
|
||||
if (separatorExpression) {
|
||||
separatorExpression = new Expression({value: separatorExpression});
|
||||
}
|
||||
|
||||
if (iteratorExpression) {
|
||||
iteratorExpression = new Expression({value: iteratorExpression});
|
||||
}
|
||||
|
||||
if (fromExpression) {
|
||||
fromExpression = createNumberExpression(fromExpression);
|
||||
}
|
||||
|
||||
if (toExpression) {
|
||||
toExpression = createNumberExpression(toExpression);
|
||||
}
|
||||
|
||||
if (stepExpression) {
|
||||
stepExpression = createNumberExpression(stepExpression);
|
||||
}
|
||||
|
||||
varName = new Identifier({name: varName});
|
||||
|
||||
if (statusVarName) {
|
||||
statusVarName = new Identifier({name: statusVarName});
|
||||
}
|
||||
|
||||
// No more tokens... now we need to sort out what happened
|
||||
if (loopType === 'ForEach') {
|
||||
return {
|
||||
'loopType': loopType,
|
||||
'varName': varName,
|
||||
'in': inExpression,
|
||||
'separator': separatorExpression,
|
||||
'statusVarName': statusVarName,
|
||||
'iterator': iteratorExpression
|
||||
};
|
||||
} else if (loopType === 'ForEachProp') {
|
||||
return {
|
||||
'loopType': loopType,
|
||||
'nameVarName': nameVarName,
|
||||
'valueVarName': valueVarName,
|
||||
'in': inExpression
|
||||
};
|
||||
} else if (loopType === 'ForRange') {
|
||||
return {
|
||||
'loopType': loopType,
|
||||
'varName': varName,
|
||||
'from': fromExpression,
|
||||
'to': toExpression,
|
||||
'step': stepExpression
|
||||
};
|
||||
} else if (loopType === 'For') {
|
||||
if (forTest == null) {
|
||||
return {
|
||||
error: 'Invalid native for loop. Expected format: <init>; <test>; <update>'
|
||||
};
|
||||
}
|
||||
return {
|
||||
'loopType': loopType,
|
||||
'init': forInit,
|
||||
'test': forTest,
|
||||
'update': forUpdate
|
||||
};
|
||||
} else {
|
||||
return {
|
||||
'error': 'Invalid for loop'
|
||||
};
|
||||
}
|
||||
};
|
||||
@ -1,104 +0,0 @@
|
||||
var forEachRegEx = /^\s*([A-Za-z_][A-Za-z0-9_]*)\s+in\s+(.+)$/;
|
||||
var forEachPropRegEx = /^\(\s*([A-Za-z_][A-Za-z0-9_]*)\s*,\s*([A-Za-z_][A-Za-z0-9_]*)\s*\)\s+in\s+(.+)$/;
|
||||
var forRangeRegEx = /^\s*([A-Za-z_][A-Za-z0-9_]*)\s+from\s+(.+)$/; // i from 0 to 10 or i from 0 to 10 step 5
|
||||
var forRangeKeywordsRegExp = /"(?:[^"]|\\")*"|'(?:[^']|\\')*'|\s+(to|step)\s+/g;
|
||||
var integerRegExp = /^-?\d+$/;
|
||||
var numberRegExp = /^-?(?:\d+|\d+\.\d*|\d*\.\d+|\d+\.\d+)$/;
|
||||
|
||||
function convertNumber(str) {
|
||||
if (!str) {
|
||||
return str;
|
||||
}
|
||||
|
||||
if (integerRegExp.test(str)) {
|
||||
return parseInt(str, 10);
|
||||
} else if (numberRegExp.test(str)) {
|
||||
return parseFloat(str);
|
||||
} else {
|
||||
return str;
|
||||
}
|
||||
}
|
||||
|
||||
module.exports = function(value) {
|
||||
var match = value.match(forEachRegEx);
|
||||
if (match) {
|
||||
return {
|
||||
'varName': match[1],
|
||||
'target': match[2]
|
||||
};
|
||||
} else if ((match = value.match(forEachPropRegEx))) {
|
||||
|
||||
|
||||
return {
|
||||
'nameVar': match[1],
|
||||
'valueVar': match[2],
|
||||
'target': match[3]
|
||||
};
|
||||
} else if ((match = value.match(forRangeRegEx))) {
|
||||
var nameVar = match[1];
|
||||
|
||||
|
||||
var remainder = match[2];
|
||||
var rangeMatches;
|
||||
|
||||
var fromStart = 0;
|
||||
var fromEnd = -1;
|
||||
|
||||
var toStart = -1;
|
||||
var toEnd = remainder.length;
|
||||
|
||||
var stepStart = -1;
|
||||
var stepEnd = -1;
|
||||
|
||||
while ((rangeMatches = forRangeKeywordsRegExp.exec(remainder))) {
|
||||
if (rangeMatches[1] === 'to') {
|
||||
fromEnd = rangeMatches.index;
|
||||
toStart = forRangeKeywordsRegExp.lastIndex;
|
||||
} else if (rangeMatches[1] === 'step') {
|
||||
if (toStart === -1) {
|
||||
continue;
|
||||
}
|
||||
toEnd = rangeMatches.index;
|
||||
stepStart = forRangeKeywordsRegExp.lastIndex;
|
||||
stepEnd = remainder.length;
|
||||
}
|
||||
}
|
||||
|
||||
if (toStart === -1 || fromEnd === -1) {
|
||||
throw new Error('Invalid each attribute of "' + value + '"');
|
||||
}
|
||||
|
||||
var from = remainder.substring(fromStart, fromEnd).trim();
|
||||
var to = remainder.substring(toStart, toEnd).trim();
|
||||
var step;
|
||||
|
||||
from = convertNumber(from);
|
||||
to = convertNumber(to);
|
||||
|
||||
if (stepStart !== -1) {
|
||||
step = remainder.substring(stepStart, stepEnd).trim();
|
||||
step = convertNumber(step);
|
||||
} else {
|
||||
if (typeof from === 'number' && typeof to === 'number') {
|
||||
if (from < to) {
|
||||
step = 1;
|
||||
} else {
|
||||
step = -1;
|
||||
}
|
||||
} else {
|
||||
step = 1;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
return {
|
||||
'varName': nameVar,
|
||||
'from': from,
|
||||
'to': to,
|
||||
'step': step
|
||||
};
|
||||
} else {
|
||||
throw new Error('Invalid each attribute of "' + value + '"');
|
||||
}
|
||||
|
||||
};
|
||||
53
taglibs/core/util/removeComments.js
Normal file
53
taglibs/core/util/removeComments.js
Normal file
@ -0,0 +1,53 @@
|
||||
'use strict';
|
||||
var tokenizer = require('./tokenizer').create([
|
||||
{
|
||||
name: 'stringDouble',
|
||||
pattern: /"(?:[^"]|\\")*"/,
|
||||
},
|
||||
{
|
||||
name: 'stringSingle',
|
||||
pattern: /'(?:[^']|\\')*'/
|
||||
},
|
||||
{
|
||||
name: 'singleLineComment',
|
||||
pattern: /\/\/.*/
|
||||
},
|
||||
{
|
||||
name: 'multiLineComment',
|
||||
pattern: /\/\*(?:[\s\S]*?)\*\//
|
||||
}
|
||||
]);
|
||||
|
||||
/**
|
||||
* Parses a for loop string in the following forms:
|
||||
*
|
||||
* <varName> in <expression>
|
||||
* <varName> in <expression> | status-var=<varName> separator=<expression>
|
||||
* <varName> from <expression> to <expression>
|
||||
* <varName> from <expression> to <expression> step <expression>
|
||||
* <init>; <test>; <update>
|
||||
*/
|
||||
module.exports = function removeComments(str) {
|
||||
|
||||
var comments = [];
|
||||
|
||||
tokenizer.forEachToken(str, (token) => {
|
||||
switch(token.name) {
|
||||
case 'singleLineComment':
|
||||
case 'multiLineComment':
|
||||
comments.push(token);
|
||||
break;
|
||||
}
|
||||
});
|
||||
|
||||
var len = comments.length;
|
||||
|
||||
if (len) {
|
||||
for (var i=len-1; i>=0; i--) {
|
||||
var comment = comments[i];
|
||||
str = str.substring(0, comment.start) + str.substring(comment.end);
|
||||
}
|
||||
}
|
||||
|
||||
return str;
|
||||
};
|
||||
37
taglibs/core/util/tokenizer.js
Normal file
37
taglibs/core/util/tokenizer.js
Normal file
@ -0,0 +1,37 @@
|
||||
'use strict';
|
||||
|
||||
function create(tokens) {
|
||||
function getToken(matches) {
|
||||
for (var i=0; i<tokens.length; i++) {
|
||||
let tokenValue = matches[i + 1];
|
||||
if (tokenValue != null) {
|
||||
var tokenDef = tokens[i];
|
||||
return {
|
||||
start: matches.index,
|
||||
end: matches.index + matches[0].length,
|
||||
name: tokenDef.name,
|
||||
value: tokenValue
|
||||
};
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
var tokensRegExp = new RegExp(tokens
|
||||
.map((token) => {
|
||||
return '(' + token.pattern.source + ')';
|
||||
})
|
||||
.join('|'), 'g');
|
||||
|
||||
return {
|
||||
forEachToken: function(value, callback, thisObj) {
|
||||
tokensRegExp.lastIndex = 0; // Start searching from the beginning again
|
||||
var matches;
|
||||
while ((matches = tokensRegExp.exec(value))) {
|
||||
let token = getToken(matches);
|
||||
callback.call(this, token);
|
||||
}
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
exports.create = create;
|
||||
@ -1,4 +1,12 @@
|
||||
module.exports = function nodeFactory(el, context) {
|
||||
var attributes = el.attributes;
|
||||
return context.builder.vars(attributes);
|
||||
var builder = context.builder;
|
||||
|
||||
var declarations = el.attributes.map((attr) => {
|
||||
return {
|
||||
id: builder.identifier(attr.name),
|
||||
init: builder.expression(attr.value)
|
||||
};
|
||||
});
|
||||
|
||||
return context.builder.vars(declarations);
|
||||
};
|
||||
@ -10,6 +10,11 @@ function autoTest(name, dir, run, options) {
|
||||
var actualPath = path.join(dir, 'actual' + compareExtension);
|
||||
var expectedPath = path.join(dir, 'expected' + compareExtension);
|
||||
|
||||
try {
|
||||
fs.unlinkSync(actualPath);
|
||||
} catch(e) {}
|
||||
|
||||
|
||||
var actual = run(dir);
|
||||
if (actual === '$PASS$') {
|
||||
return;
|
||||
|
||||
@ -2,13 +2,12 @@
|
||||
|
||||
module.exports = function(builder) {
|
||||
var templateRoot = builder.templateRoot;
|
||||
var forEach = builder.forEach;
|
||||
|
||||
return templateRoot([
|
||||
forEach({
|
||||
builder.forEachProp({
|
||||
nameVarName: 'k',
|
||||
valueVarName: 'v',
|
||||
target: 'myArray',
|
||||
in: 'myArray',
|
||||
body: [
|
||||
builder.functionCall('console.log', [
|
||||
builder.literal('k:'),
|
||||
|
||||
@ -6,7 +6,7 @@ function create(__helpers) {
|
||||
|
||||
return function render(data, out) {
|
||||
(function() {
|
||||
for (var i = 0; i<=myArray.length; i+=2) {
|
||||
for (var i = 0; i <= myArray.length; i += 2) {
|
||||
console.log(i);
|
||||
}
|
||||
}());
|
||||
@ -2,10 +2,9 @@
|
||||
|
||||
module.exports = function(builder) {
|
||||
var templateRoot = builder.templateRoot;
|
||||
var forEach = builder.forEach;
|
||||
|
||||
return templateRoot([
|
||||
forEach({
|
||||
builder.forRange({
|
||||
varName: 'i',
|
||||
from: 0,
|
||||
to: 'myArray.length',
|
||||
1
test/fixtures/codegen/autotest/negate/expected.js
vendored
Normal file
1
test/fixtures/codegen/autotest/negate/expected.js
vendored
Normal file
@ -0,0 +1 @@
|
||||
!foo
|
||||
5
test/fixtures/codegen/autotest/negate/index.js
vendored
Normal file
5
test/fixtures/codegen/autotest/negate/index.js
vendored
Normal file
@ -0,0 +1,5 @@
|
||||
'use strict';
|
||||
|
||||
module.exports = function(builder) {
|
||||
return builder.negate(builder.identifier('foo'));
|
||||
};
|
||||
1
test/fixtures/codegen/autotest/unaryExpression/expected.js
vendored
Normal file
1
test/fixtures/codegen/autotest/unaryExpression/expected.js
vendored
Normal file
@ -0,0 +1 @@
|
||||
!foo
|
||||
5
test/fixtures/codegen/autotest/unaryExpression/index.js
vendored
Normal file
5
test/fixtures/codegen/autotest/unaryExpression/index.js
vendored
Normal file
@ -0,0 +1,5 @@
|
||||
'use strict';
|
||||
|
||||
module.exports = function(builder) {
|
||||
return builder.negate(builder.identifier('foo'));
|
||||
};
|
||||
12
test/fixtures/parseExpression/autotest/binaryExpression/expected.json
vendored
Normal file
12
test/fixtures/parseExpression/autotest/binaryExpression/expected.json
vendored
Normal file
@ -0,0 +1,12 @@
|
||||
{
|
||||
"type": "BinaryExpression",
|
||||
"left": {
|
||||
"type": "Identifier",
|
||||
"name": "foo"
|
||||
},
|
||||
"operator": "+",
|
||||
"right": {
|
||||
"type": "Literal",
|
||||
"value": "12"
|
||||
}
|
||||
}
|
||||
1
test/fixtures/parseExpression/autotest/binaryExpression/input.txt
vendored
Normal file
1
test/fixtures/parseExpression/autotest/binaryExpression/input.txt
vendored
Normal file
@ -0,0 +1 @@
|
||||
foo+'12'
|
||||
44
test/fixtures/parseExpression/autotest/complex/expected.json
vendored
Normal file
44
test/fixtures/parseExpression/autotest/complex/expected.json
vendored
Normal file
@ -0,0 +1,44 @@
|
||||
{
|
||||
"type": "Assignment",
|
||||
"left": {
|
||||
"type": "Identifier",
|
||||
"name": "a"
|
||||
},
|
||||
"right": {
|
||||
"type": "Assignment",
|
||||
"left": {
|
||||
"type": "Identifier",
|
||||
"name": "b"
|
||||
},
|
||||
"right": {
|
||||
"type": "BinaryExpression",
|
||||
"left": {
|
||||
"type": "BinaryExpression",
|
||||
"left": {
|
||||
"type": "Identifier",
|
||||
"name": "a"
|
||||
},
|
||||
"operator": "+",
|
||||
"right": {
|
||||
"type": "Identifier",
|
||||
"name": "b"
|
||||
}
|
||||
},
|
||||
"operator": ">",
|
||||
"right": {
|
||||
"type": "Assignment",
|
||||
"left": {
|
||||
"type": "Identifier",
|
||||
"name": "c"
|
||||
},
|
||||
"right": {
|
||||
"type": "Literal",
|
||||
"value": 2
|
||||
},
|
||||
"operator": "+="
|
||||
}
|
||||
},
|
||||
"operator": "="
|
||||
},
|
||||
"operator": "="
|
||||
}
|
||||
1
test/fixtures/parseExpression/autotest/complex/input.txt
vendored
Normal file
1
test/fixtures/parseExpression/autotest/complex/input.txt
vendored
Normal file
@ -0,0 +1 @@
|
||||
a = b = (a+b)>(c+=2)
|
||||
4
test/fixtures/parseExpression/autotest/id/expected.json
vendored
Normal file
4
test/fixtures/parseExpression/autotest/id/expected.json
vendored
Normal file
@ -0,0 +1,4 @@
|
||||
{
|
||||
"type": "Identifier",
|
||||
"name": "foo"
|
||||
}
|
||||
1
test/fixtures/parseExpression/autotest/id/input.txt
vendored
Normal file
1
test/fixtures/parseExpression/autotest/id/input.txt
vendored
Normal file
@ -0,0 +1 @@
|
||||
foo
|
||||
9
test/fixtures/parseExpression/autotest/negate/expected.json
vendored
Normal file
9
test/fixtures/parseExpression/autotest/negate/expected.json
vendored
Normal file
@ -0,0 +1,9 @@
|
||||
{
|
||||
"type": "UnaryExpression",
|
||||
"argument": {
|
||||
"type": "Identifier",
|
||||
"name": "foo"
|
||||
},
|
||||
"operator": "!",
|
||||
"prefix": true
|
||||
}
|
||||
1
test/fixtures/parseExpression/autotest/negate/input.txt
vendored
Normal file
1
test/fixtures/parseExpression/autotest/negate/input.txt
vendored
Normal file
@ -0,0 +1 @@
|
||||
!foo
|
||||
11
test/fixtures/parseFor/autotest/forEach-comment/expected.json
vendored
Normal file
11
test/fixtures/parseFor/autotest/forEach-comment/expected.json
vendored
Normal file
@ -0,0 +1,11 @@
|
||||
{
|
||||
"loopType": "ForEach",
|
||||
"varName": {
|
||||
"type": "Identifier",
|
||||
"name": "color"
|
||||
},
|
||||
"in": {
|
||||
"type": "Expression",
|
||||
"value": "colors"
|
||||
}
|
||||
}
|
||||
1
test/fixtures/parseFor/autotest/forEach-comment/input.txt
vendored
Normal file
1
test/fixtures/parseFor/autotest/forEach-comment/input.txt
vendored
Normal file
@ -0,0 +1 @@
|
||||
color in colors /* | separator="," */
|
||||
23
test/fixtures/parseFor/autotest/forEach-crazy/expected.json
vendored
Normal file
23
test/fixtures/parseFor/autotest/forEach-crazy/expected.json
vendored
Normal file
@ -0,0 +1,23 @@
|
||||
{
|
||||
"loopType": "ForEach",
|
||||
"varName": {
|
||||
"type": "Identifier",
|
||||
"name": "color"
|
||||
},
|
||||
"in": {
|
||||
"type": "Expression",
|
||||
"value": "(a in test | 0)"
|
||||
},
|
||||
"separator": {
|
||||
"type": "Expression",
|
||||
"value": "(iterator==1 ? 'foo' : 'bar')"
|
||||
},
|
||||
"statusVarName": {
|
||||
"type": "Identifier",
|
||||
"name": "loop"
|
||||
},
|
||||
"iterator": {
|
||||
"type": "Expression",
|
||||
"value": "my Iterator"
|
||||
}
|
||||
}
|
||||
1
test/fixtures/parseFor/autotest/forEach-crazy/input.txt
vendored
Normal file
1
test/fixtures/parseFor/autotest/forEach-crazy/input.txt
vendored
Normal file
@ -0,0 +1 @@
|
||||
color in (a in test | 0) | status-var=loop separator=(iterator==1 ? 'foo' : 'bar') iterator=my Iterator
|
||||
15
test/fixtures/parseFor/autotest/forEach-iterator/expected.json
vendored
Normal file
15
test/fixtures/parseFor/autotest/forEach-iterator/expected.json
vendored
Normal file
@ -0,0 +1,15 @@
|
||||
{
|
||||
"loopType": "ForEach",
|
||||
"varName": {
|
||||
"type": "Identifier",
|
||||
"name": "color"
|
||||
},
|
||||
"in": {
|
||||
"type": "Expression",
|
||||
"value": "colors"
|
||||
},
|
||||
"iterator": {
|
||||
"type": "Expression",
|
||||
"value": "myIterator"
|
||||
}
|
||||
}
|
||||
1
test/fixtures/parseFor/autotest/forEach-iterator/input.txt
vendored
Normal file
1
test/fixtures/parseFor/autotest/forEach-iterator/input.txt
vendored
Normal file
@ -0,0 +1 @@
|
||||
color in colors | iterator=myIterator
|
||||
11
test/fixtures/parseFor/autotest/forEach-nesting/expected.json
vendored
Normal file
11
test/fixtures/parseFor/autotest/forEach-nesting/expected.json
vendored
Normal file
@ -0,0 +1,11 @@
|
||||
{
|
||||
"loopType": "ForEach",
|
||||
"varName": {
|
||||
"type": "Identifier",
|
||||
"name": "color"
|
||||
},
|
||||
"in": {
|
||||
"type": "Expression",
|
||||
"value": "[(({a} in test | 0))]"
|
||||
}
|
||||
}
|
||||
1
test/fixtures/parseFor/autotest/forEach-nesting/input.txt
vendored
Normal file
1
test/fixtures/parseFor/autotest/forEach-nesting/input.txt
vendored
Normal file
@ -0,0 +1 @@
|
||||
color in [(({a} in test | 0))]
|
||||
11
test/fixtures/parseFor/autotest/forEach-simple/expected.json
vendored
Normal file
11
test/fixtures/parseFor/autotest/forEach-simple/expected.json
vendored
Normal file
@ -0,0 +1,11 @@
|
||||
{
|
||||
"loopType": "ForEach",
|
||||
"varName": {
|
||||
"type": "Identifier",
|
||||
"name": "color"
|
||||
},
|
||||
"in": {
|
||||
"type": "Expression",
|
||||
"value": "colors"
|
||||
}
|
||||
}
|
||||
1
test/fixtures/parseFor/autotest/forEach-simple/input.txt
vendored
Normal file
1
test/fixtures/parseFor/autotest/forEach-simple/input.txt
vendored
Normal file
@ -0,0 +1 @@
|
||||
color in colors
|
||||
23
test/fixtures/parseFor/autotest/forEach-status-var-separator-iterator/expected.json
vendored
Normal file
23
test/fixtures/parseFor/autotest/forEach-status-var-separator-iterator/expected.json
vendored
Normal file
@ -0,0 +1,23 @@
|
||||
{
|
||||
"loopType": "ForEach",
|
||||
"varName": {
|
||||
"type": "Identifier",
|
||||
"name": "color"
|
||||
},
|
||||
"in": {
|
||||
"type": "Expression",
|
||||
"value": "colors"
|
||||
},
|
||||
"separator": {
|
||||
"type": "Expression",
|
||||
"value": "','"
|
||||
},
|
||||
"statusVarName": {
|
||||
"type": "Identifier",
|
||||
"name": "loop"
|
||||
},
|
||||
"iterator": {
|
||||
"type": "Expression",
|
||||
"value": "myIterator"
|
||||
}
|
||||
}
|
||||
1
test/fixtures/parseFor/autotest/forEach-status-var-separator-iterator/input.txt
vendored
Normal file
1
test/fixtures/parseFor/autotest/forEach-status-var-separator-iterator/input.txt
vendored
Normal file
@ -0,0 +1 @@
|
||||
color in colors | status-var=loop separator=',' iterator=myIterator
|
||||
19
test/fixtures/parseFor/autotest/forEach-status-var-separator/expected.json
vendored
Normal file
19
test/fixtures/parseFor/autotest/forEach-status-var-separator/expected.json
vendored
Normal file
@ -0,0 +1,19 @@
|
||||
{
|
||||
"loopType": "ForEach",
|
||||
"varName": {
|
||||
"type": "Identifier",
|
||||
"name": "color"
|
||||
},
|
||||
"in": {
|
||||
"type": "Expression",
|
||||
"value": "colors"
|
||||
},
|
||||
"separator": {
|
||||
"type": "Expression",
|
||||
"value": "','"
|
||||
},
|
||||
"statusVarName": {
|
||||
"type": "Identifier",
|
||||
"name": "loop"
|
||||
}
|
||||
}
|
||||
1
test/fixtures/parseFor/autotest/forEach-status-var-separator/input.txt
vendored
Normal file
1
test/fixtures/parseFor/autotest/forEach-status-var-separator/input.txt
vendored
Normal file
@ -0,0 +1 @@
|
||||
color in colors | status-var=loop separator=','
|
||||
15
test/fixtures/parseFor/autotest/forEach-status-var/expected.json
vendored
Normal file
15
test/fixtures/parseFor/autotest/forEach-status-var/expected.json
vendored
Normal file
@ -0,0 +1,15 @@
|
||||
{
|
||||
"loopType": "ForEach",
|
||||
"varName": {
|
||||
"type": "Identifier",
|
||||
"name": "color"
|
||||
},
|
||||
"in": {
|
||||
"type": "Expression",
|
||||
"value": "colors"
|
||||
},
|
||||
"statusVarName": {
|
||||
"type": "Identifier",
|
||||
"name": "loop"
|
||||
}
|
||||
}
|
||||
1
test/fixtures/parseFor/autotest/forEach-status-var/input.txt
vendored
Normal file
1
test/fixtures/parseFor/autotest/forEach-status-var/input.txt
vendored
Normal file
@ -0,0 +1 @@
|
||||
color in colors | status-var=loop
|
||||
11
test/fixtures/parseFor/autotest/forEach-strings/expected.json
vendored
Normal file
11
test/fixtures/parseFor/autotest/forEach-strings/expected.json
vendored
Normal file
@ -0,0 +1,11 @@
|
||||
{
|
||||
"loopType": "ForEach",
|
||||
"varName": {
|
||||
"type": "Identifier",
|
||||
"name": "color"
|
||||
},
|
||||
"in": {
|
||||
"type": "Expression",
|
||||
"value": "' | separator=\",\";'"
|
||||
}
|
||||
}
|
||||
1
test/fixtures/parseFor/autotest/forEach-strings/input.txt
vendored
Normal file
1
test/fixtures/parseFor/autotest/forEach-strings/input.txt
vendored
Normal file
@ -0,0 +1 @@
|
||||
color in ' | separator=",";'
|
||||
15
test/fixtures/parseFor/autotest/forRange-to-expr/expected.json
vendored
Normal file
15
test/fixtures/parseFor/autotest/forRange-to-expr/expected.json
vendored
Normal file
@ -0,0 +1,15 @@
|
||||
{
|
||||
"loopType": "ForRange",
|
||||
"varName": {
|
||||
"type": "Identifier",
|
||||
"name": "i"
|
||||
},
|
||||
"from": {
|
||||
"type": "Literal",
|
||||
"value": 0
|
||||
},
|
||||
"to": {
|
||||
"type": "Expression",
|
||||
"value": "'abc'.length"
|
||||
}
|
||||
}
|
||||
1
test/fixtures/parseFor/autotest/forRange-to-expr/input.txt
vendored
Normal file
1
test/fixtures/parseFor/autotest/forRange-to-expr/input.txt
vendored
Normal file
@ -0,0 +1 @@
|
||||
i from 0 to 'abc'.length
|
||||
6
test/fixtures/parseFor/autotest/nativeFor-empty-init/expected.json
vendored
Normal file
6
test/fixtures/parseFor/autotest/nativeFor-empty-init/expected.json
vendored
Normal file
@ -0,0 +1,6 @@
|
||||
{
|
||||
"loopType": "For",
|
||||
"init": "",
|
||||
"test": " i<foo.length",
|
||||
"update": " i++"
|
||||
}
|
||||
1
test/fixtures/parseFor/autotest/nativeFor-empty-init/input.txt
vendored
Normal file
1
test/fixtures/parseFor/autotest/nativeFor-empty-init/input.txt
vendored
Normal file
@ -0,0 +1 @@
|
||||
; i<foo.length; i++
|
||||
3
test/fixtures/parseFor/autotest/nativeFor-invalid/expected.json
vendored
Normal file
3
test/fixtures/parseFor/autotest/nativeFor-invalid/expected.json
vendored
Normal file
@ -0,0 +1,3 @@
|
||||
{
|
||||
"error": "Invalid native for loop. Expected format: <init>; <test>; <update>"
|
||||
}
|
||||
1
test/fixtures/parseFor/autotest/nativeFor-invalid/input.txt
vendored
Normal file
1
test/fixtures/parseFor/autotest/nativeFor-invalid/input.txt
vendored
Normal file
@ -0,0 +1 @@
|
||||
var i=0; i<foo.length
|
||||
6
test/fixtures/parseFor/autotest/nativeFor/expected.json
vendored
Normal file
6
test/fixtures/parseFor/autotest/nativeFor/expected.json
vendored
Normal file
@ -0,0 +1,6 @@
|
||||
{
|
||||
"loopType": "For",
|
||||
"init": "var i=0",
|
||||
"test": " i<foo.length",
|
||||
"update": " i++"
|
||||
}
|
||||
1
test/fixtures/parseFor/autotest/nativeFor/input.txt
vendored
Normal file
1
test/fixtures/parseFor/autotest/nativeFor/input.txt
vendored
Normal file
@ -0,0 +1 @@
|
||||
var i=0; i<foo.length; i++
|
||||
@ -6,25 +6,48 @@
|
||||
"kind": "var",
|
||||
"declarations": [
|
||||
{
|
||||
"name": "x",
|
||||
"value": {
|
||||
"id": {
|
||||
"type": "Identifier",
|
||||
"name": "x"
|
||||
},
|
||||
"init": {
|
||||
"type": "Literal",
|
||||
"value": 1
|
||||
}
|
||||
},
|
||||
{
|
||||
"name": "y",
|
||||
"value": {
|
||||
"id": {
|
||||
"type": "Identifier",
|
||||
"name": "y"
|
||||
},
|
||||
"init": {
|
||||
"type": "Literal",
|
||||
"value": 7
|
||||
}
|
||||
},
|
||||
{
|
||||
"name": "z",
|
||||
"value": "x+10"
|
||||
"id": {
|
||||
"type": "Identifier",
|
||||
"name": "z"
|
||||
},
|
||||
"init": {
|
||||
"type": "BinaryExpression",
|
||||
"left": {
|
||||
"type": "Identifier",
|
||||
"name": "x"
|
||||
},
|
||||
"operator": "+",
|
||||
"right": {
|
||||
"type": "Literal",
|
||||
"value": 10
|
||||
}
|
||||
}
|
||||
},
|
||||
{
|
||||
"name": "unusedvar"
|
||||
"id": {
|
||||
"type": "Identifier",
|
||||
"name": "unusedvar"
|
||||
}
|
||||
}
|
||||
],
|
||||
"body": [
|
||||
@ -37,7 +60,10 @@
|
||||
},
|
||||
{
|
||||
"type": "Text",
|
||||
"argument": "x"
|
||||
"argument": {
|
||||
"type": "Identifier",
|
||||
"name": "x"
|
||||
}
|
||||
},
|
||||
{
|
||||
"type": "Text",
|
||||
@ -48,7 +74,10 @@
|
||||
},
|
||||
{
|
||||
"type": "Text",
|
||||
"argument": "y"
|
||||
"argument": {
|
||||
"type": "Identifier",
|
||||
"name": "y"
|
||||
}
|
||||
},
|
||||
{
|
||||
"type": "Text",
|
||||
@ -59,7 +88,10 @@
|
||||
},
|
||||
{
|
||||
"type": "Text",
|
||||
"argument": "z"
|
||||
"argument": {
|
||||
"type": "Identifier",
|
||||
"name": "z"
|
||||
}
|
||||
},
|
||||
{
|
||||
"type": "Text",
|
||||
|
||||
@ -3,8 +3,15 @@
|
||||
"body": [
|
||||
{
|
||||
"type": "Assignment",
|
||||
"left": "a",
|
||||
"right": "1"
|
||||
"left": {
|
||||
"type": "Identifier",
|
||||
"name": "a"
|
||||
},
|
||||
"right": {
|
||||
"type": "Literal",
|
||||
"value": 1
|
||||
},
|
||||
"operator": "="
|
||||
}
|
||||
]
|
||||
}
|
||||
@ -46,7 +46,18 @@
|
||||
},
|
||||
{
|
||||
"type": "Html",
|
||||
"argument": "data.name"
|
||||
"argument": {
|
||||
"type": "MemberExpression",
|
||||
"object": {
|
||||
"type": "Identifier",
|
||||
"name": "data"
|
||||
},
|
||||
"property": {
|
||||
"type": "Identifier",
|
||||
"name": "name"
|
||||
},
|
||||
"computed": false
|
||||
}
|
||||
},
|
||||
{
|
||||
"type": "Text",
|
||||
@ -59,9 +70,23 @@
|
||||
"type": "If",
|
||||
"test": {
|
||||
"type": "FunctionCall",
|
||||
"callee": "notEmpty",
|
||||
"callee": {
|
||||
"type": "Identifier",
|
||||
"name": "notEmpty"
|
||||
},
|
||||
"args": [
|
||||
"data.colors"
|
||||
{
|
||||
"type": "MemberExpression",
|
||||
"object": {
|
||||
"type": "Identifier",
|
||||
"name": "data"
|
||||
},
|
||||
"property": {
|
||||
"type": "Identifier",
|
||||
"name": "colors"
|
||||
},
|
||||
"computed": false
|
||||
}
|
||||
]
|
||||
},
|
||||
"body": [
|
||||
@ -80,9 +105,23 @@
|
||||
"body": [
|
||||
{
|
||||
"type": "FunctionCall",
|
||||
"callee": "forEach",
|
||||
"callee": {
|
||||
"type": "Identifier",
|
||||
"name": "forEach"
|
||||
},
|
||||
"args": [
|
||||
"data.colors",
|
||||
{
|
||||
"type": "MemberExpression",
|
||||
"object": {
|
||||
"type": "Identifier",
|
||||
"name": "data"
|
||||
},
|
||||
"property": {
|
||||
"type": "Identifier",
|
||||
"name": "colors"
|
||||
},
|
||||
"computed": false
|
||||
},
|
||||
{
|
||||
"type": "FunctionDeclaration",
|
||||
"name": null,
|
||||
@ -105,7 +144,10 @@
|
||||
"body": [
|
||||
{
|
||||
"type": "Text",
|
||||
"argument": "color"
|
||||
"argument": {
|
||||
"type": "Identifier",
|
||||
"name": "color"
|
||||
}
|
||||
}
|
||||
]
|
||||
}
|
||||
|
||||
@ -1,3 +1,3 @@
|
||||
<div for(item in ['red', 'green', 'blue']; separator = ', '; status-var=loop)>
|
||||
<div for(item in ['red', 'green', 'blue'] | separator=', ' status-var=loop)>
|
||||
${item} - ${loop.isFirst()} - ${loop.isLast()} - ${loop.getIndex()} - ${loop.getLength()}
|
||||
</div>
|
||||
@ -1,3 +1,3 @@
|
||||
<b for(item in ['red', 'green', 'blue']; separator=', ')>
|
||||
<b for(item in ['red', 'green', 'blue'] | separator=', ')>
|
||||
${item}
|
||||
</b>
|
||||
1
test/fixtures/render/autotest/for-range-descending-step/expected.html
vendored
Normal file
1
test/fixtures/render/autotest/for-range-descending-step/expected.html
vendored
Normal file
@ -0,0 +1 @@
|
||||
9876543210
|
||||
3
test/fixtures/render/autotest/for-range-descending-step/template.marko
vendored
Normal file
3
test/fixtures/render/autotest/for-range-descending-step/template.marko
vendored
Normal file
@ -0,0 +1,3 @@
|
||||
<for(i from 9 to 0 step -1)>
|
||||
${i}
|
||||
</for>
|
||||
1
test/fixtures/render/autotest/for-range-descending-step/test.js
vendored
Normal file
1
test/fixtures/render/autotest/for-range-descending-step/test.js
vendored
Normal file
@ -0,0 +1 @@
|
||||
exports.templateData = {};
|
||||
1
test/fixtures/render/autotest/for-range-descending/expected.html
vendored
Normal file
1
test/fixtures/render/autotest/for-range-descending/expected.html
vendored
Normal file
@ -0,0 +1 @@
|
||||
9876543210
|
||||
3
test/fixtures/render/autotest/for-range-descending/template.marko
vendored
Normal file
3
test/fixtures/render/autotest/for-range-descending/template.marko
vendored
Normal file
@ -0,0 +1,3 @@
|
||||
<for(i from 9 to 0)>
|
||||
${i}
|
||||
</for>
|
||||
1
test/fixtures/render/autotest/for-range-descending/test.js
vendored
Normal file
1
test/fixtures/render/autotest/for-range-descending/test.js
vendored
Normal file
@ -0,0 +1 @@
|
||||
exports.templateData = {};
|
||||
1
test/fixtures/render/autotest/for-range-from-to-expr/expected.html
vendored
Normal file
1
test/fixtures/render/autotest/for-range-from-to-expr/expected.html
vendored
Normal file
@ -0,0 +1 @@
|
||||
0123
|
||||
3
test/fixtures/render/autotest/for-range-from-to-expr/template.marko
vendored
Normal file
3
test/fixtures/render/autotest/for-range-from-to-expr/template.marko
vendored
Normal file
@ -0,0 +1,3 @@
|
||||
<for(i from ''.length to 'abc'.length)>
|
||||
${i}
|
||||
</for>
|
||||
1
test/fixtures/render/autotest/for-range-from-to-expr/test.js
vendored
Normal file
1
test/fixtures/render/autotest/for-range-from-to-expr/test.js
vendored
Normal file
@ -0,0 +1 @@
|
||||
exports.templateData = {};
|
||||
1
test/fixtures/render/autotest/for-range-step-2/expected.html
vendored
Normal file
1
test/fixtures/render/autotest/for-range-step-2/expected.html
vendored
Normal file
@ -0,0 +1 @@
|
||||
02468
|
||||
3
test/fixtures/render/autotest/for-range-step-2/template.marko
vendored
Normal file
3
test/fixtures/render/autotest/for-range-step-2/template.marko
vendored
Normal file
@ -0,0 +1,3 @@
|
||||
<for(i from 0 to 9 step 2)>
|
||||
${i}
|
||||
</for>
|
||||
1
test/fixtures/render/autotest/for-range-step-2/test.js
vendored
Normal file
1
test/fixtures/render/autotest/for-range-step-2/test.js
vendored
Normal file
@ -0,0 +1 @@
|
||||
exports.templateData = {};
|
||||
1
test/fixtures/render/autotest/for-range-step-neg2/expected.html
vendored
Normal file
1
test/fixtures/render/autotest/for-range-step-neg2/expected.html
vendored
Normal file
@ -0,0 +1 @@
|
||||
97531
|
||||
3
test/fixtures/render/autotest/for-range-step-neg2/template.marko
vendored
Normal file
3
test/fixtures/render/autotest/for-range-step-neg2/template.marko
vendored
Normal file
@ -0,0 +1,3 @@
|
||||
<for(i from 9 to 0 step -2)>
|
||||
${i}
|
||||
</for>
|
||||
1
test/fixtures/render/autotest/for-range-step-neg2/test.js
vendored
Normal file
1
test/fixtures/render/autotest/for-range-step-neg2/test.js
vendored
Normal file
@ -0,0 +1 @@
|
||||
exports.templateData = {};
|
||||
1
test/fixtures/render/autotest/for-range-to-expr/expected.html
vendored
Normal file
1
test/fixtures/render/autotest/for-range-to-expr/expected.html
vendored
Normal file
@ -0,0 +1 @@
|
||||
0123
|
||||
3
test/fixtures/render/autotest/for-range-to-expr/template.marko
vendored
Normal file
3
test/fixtures/render/autotest/for-range-to-expr/template.marko
vendored
Normal file
@ -0,0 +1,3 @@
|
||||
<for(i from 0 to 'abc'.length)>
|
||||
${i}
|
||||
</for>
|
||||
1
test/fixtures/render/autotest/for-range-to-expr/test.js
vendored
Normal file
1
test/fixtures/render/autotest/for-range-to-expr/test.js
vendored
Normal file
@ -0,0 +1 @@
|
||||
exports.templateData = {};
|
||||
@ -1 +1 @@
|
||||
0123456789 - 9876543210 - 9876543210 - 02468 - 97531 - 0123 - 0123
|
||||
0123456789
|
||||
@ -1,27 +1,3 @@
|
||||
<for(i from 0 to 9)>
|
||||
${i}
|
||||
</for>
|
||||
-
|
||||
<for(i from 9 to 0)>
|
||||
${i}
|
||||
</for>
|
||||
-
|
||||
<for(i from 9 to 0 step -1)>
|
||||
${i}
|
||||
</for>
|
||||
-
|
||||
<for(i from 0 to 9 step 2)>
|
||||
${i}
|
||||
</for>
|
||||
-
|
||||
<for(i from 9 to 0 step -2)>
|
||||
${i}
|
||||
</for>
|
||||
-
|
||||
<for(i from 0 to 'abc'.length)>
|
||||
${i}
|
||||
</for>
|
||||
-
|
||||
<for(i from ''.length to 'abc'.length)>
|
||||
${i}
|
||||
</for>
|
||||
1
test/fixtures/render/autotest/for-tag-native/expected.html
vendored
Normal file
1
test/fixtures/render/autotest/for-tag-native/expected.html
vendored
Normal file
@ -0,0 +1 @@
|
||||
<div>0</div><div>1</div><div>2</div>
|
||||
5
test/fixtures/render/autotest/for-tag-native/template.marko
vendored
Normal file
5
test/fixtures/render/autotest/for-tag-native/template.marko
vendored
Normal file
@ -0,0 +1,5 @@
|
||||
<for(var i=0; i<3; i++)>
|
||||
<div>
|
||||
${i}
|
||||
</div>
|
||||
</for>
|
||||
1
test/fixtures/render/autotest/for-tag-native/test.js
vendored
Normal file
1
test/fixtures/render/autotest/for-tag-native/test.js
vendored
Normal file
@ -0,0 +1 @@
|
||||
exports.templateData = {};
|
||||
@ -1,4 +1,4 @@
|
||||
<for(item in ['red', 'green', 'blue']) separator=', ' status-var=loop>
|
||||
<for(item in ['red', 'green', 'blue'] | separator=', ' status-var=loop)>
|
||||
<div>
|
||||
${item} - ${loop.isFirst()} - ${loop.isLast()} - ${loop.getIndex()} - ${loop.getLength()}
|
||||
</div>
|
||||
|
||||
@ -1,4 +1,4 @@
|
||||
<for(item in ['red', 'green', 'blue']) separator=', '>
|
||||
<for(item in ['red', 'green', 'blue'] | separator=', ') >
|
||||
<b>
|
||||
${item}
|
||||
</b>
|
||||
|
||||
@ -1 +1 @@
|
||||
FRANK 3650
|
||||
FRANK 3650 []
|
||||
@ -1,3 +1,3 @@
|
||||
<var name='Frank' age=10/>
|
||||
<var name='Frank' age=10 foo/>
|
||||
|
||||
${name.toUpperCase()} ${age*365}
|
||||
${name.toUpperCase()} ${age*365} [${foo}]
|
||||
25
test/parseExpression-test.js
Normal file
25
test/parseExpression-test.js
Normal file
@ -0,0 +1,25 @@
|
||||
'use strict';
|
||||
var chai = require('chai');
|
||||
chai.config.includeStack = true;
|
||||
var parseExpression = require('../compiler/util/parseExpression');
|
||||
var autotest = require('./autotest');
|
||||
var fs = require('fs');
|
||||
var path = require('path');
|
||||
|
||||
describe('parseExpression' , function() {
|
||||
|
||||
var autoTestDir = path.join(__dirname, 'fixtures/parseExpression/autotest');
|
||||
|
||||
autotest.scanDir(
|
||||
autoTestDir,
|
||||
function run(dir) {
|
||||
var inputPath = path.join(dir, 'input.txt');
|
||||
var input = fs.readFileSync(inputPath, {encoding: 'utf8'});
|
||||
var parsed = parseExpression(input);
|
||||
return parsed;
|
||||
},
|
||||
{
|
||||
deepEqual: true,
|
||||
compareExtension: '.json'
|
||||
});
|
||||
});
|
||||
Some files were not shown because too many files have changed in this diff Show More
Loading…
x
Reference in New Issue
Block a user