Skip to content

HTTPS clone URL

Subversion checkout URL

You can clone with HTTPS or Subversion.

Download ZIP
Browse files

compiler: fix apply+local, remove context

  • Loading branch information...
commit 88a85850e6a08948dd1adf1ac09a9505bec88adf 1 parent 28edc94
@indutny indutny authored
View
89 README.md
@@ -39,17 +39,17 @@ is powered by [ometajs][2].
Input:
```javascript
-template(this.url === '/') {
+template(this.url === '/')(function() {
return render('home page')
-}
+});
-template(this.url === '/login') {
+template(this.url === '/login')(function() {
return render('login form')
-}
+});
-template(this.url === '/login' && this.cookie.is_logined) {
+template(this.url === '/login', this.cookie.is_logined)(function() {
return redirect('user page')
-}
+});
```
Output (simplified):
@@ -94,9 +94,9 @@ XJST extends JavaScript syntax with a following keywords: `template`, `local`,
### Template
```javascript
-template(expression1 === value1 && ... && expressionN === valueN) {
+template(expression1 === value1, ... , expressionN === valueN)(function() {
// will be run if condition above equals to true
-}
+})
```
Multiple `template` statements will be grouped to construct optimal conditions
@@ -115,10 +115,10 @@ There're few restrictions for templates:
### Local
```javascript
-var x = 1;
+var obj = { x: 1 };
-console.log(local(x = 2) x); // 2
-console.log(x); // 1
+console.log(local(obj)({ x: 2 })(obj.x)); // 2
+console.log(obj.x); // 1
```
`local` allows you to make temporary changes to a visible variables scope. Every
@@ -128,46 +128,35 @@ execution.
You can make multiple assignments in the one statement:
```javascript
-local(this.x = 2, this.y = 3) ...
+local({ x: 2, y: 3 })(/* your code */)
```
Or use `local` with a block:
```javascript
-local(...) { var a = 1; return a * 2; }
+local({ ... })(function() { var a = 1; return a * 2; });
```
Or as an expression:
```javascript
-var newX = local(x = 2) x;
-```
-
-### Extends
-
-```javascript
-extends 'relative/or/absolute/path/to/xjst/file'
-
-// ... your template statements ..
+var newX = local(this)({ x: 2 })(this.x);
```
-Extend current transformation with one passed into `extends`, not that
-comparisons in current file will have a higher priority.
-
### Apply
```javascript
-template(true) {
- return apply(this.type = 'first');
-}
+template(true)(function() {
+ return apply({ type: 'first' });
+});
-template(this.type === 'first') {
+template(this.type === 'first')(function() {
return apply({ type: 'second' });
-}
+});
-template(this.type === 'second') {
+template(this.type === 'second')(function() {
return 'here am I';
-}
+});
```
XJST is intended to be applied recursively to the same data, while making small
@@ -177,36 +166,16 @@ reverting them after the execution), but with small distinction - `apply`
doesn't have a body, so it's just doing some changes to the data and applying
template recursively (the context will be preserved).
-### Super apply
-
-```javascript
-template(this.page === 'home') {
- // do something
-}
-```
-
-```javascript
-// second.xjst
-extends 'first'
-
-template(this.page === 'home') {
- super apply(); // Will call "do something" in upper template
-}
-```
-
-Super call gives you an ability to pass execution into transformations that
-you're extending.
-
### Apply next
```javascript
-template(this.page === 'home' && this.action === 'login') {
+template(this.page === 'home' && this.action === 'login')(function() {
// match here
-}
+});
-template(this.page === 'home') {
+template(this.page === 'home')(function() {
applyNext();
-}
+});
```
`applyNext()` call will reapply all templates, except one where it was called,
@@ -243,12 +212,12 @@ known context's state).
Input:
```javascript
-template(this.type === 'a') {
+template(this.type === 'a')(function() {
// body 1
-}
-template(this.type === 'b') {
+});
+template(this.type === 'b')(function() {
// body 2
-}
+});
```
Output (simplified):
View
44 lib/xjst/compiler/body.js
@@ -38,26 +38,19 @@ Body.prototype.rollOut = function rollOut() {
Body.prototype.rollOutSpecific = function rollOutSpecific(node) {
if (node.type !== 'CallExpression') return;
- var callee = node.callee;
- if (callee.type !== 'CallExpression') return;
-
- // apply(ctx)(locals)
- if (callee.callee.type === 'Identifier') {
- var name = callee.callee.name;
+ // apply(locals)
+ if (node.callee.type === 'Identifier') {
+ var name = node.callee.name;
if (name !== 'apply' && name !== 'applyNext') return;
- return this.rollOutApply(name,
- node.callee.arguments[0],
- node.arguments);
- // local(ctx)(locals)(body)
- } else if (callee.callee.type === 'CallExpression' &&
- callee.callee.callee.type === 'Identifier') {
- var name = callee.callee.callee.name;
+ return this.rollOutApply(name, node.arguments);
+ // local(locals)(body)
+ } else if (node.callee.type === 'CallExpression' &&
+ node.callee.callee.type === 'Identifier') {
+ var name = node.callee.callee.name;
if (name !== 'local') return;
- return this.rollOutLocal(node.callee.callee.arguments[0],
- node.callee.arguments,
- node.arguments[0]);
+ return this.rollOutLocal(node.callee.arguments, node.arguments[0]);
}
};
@@ -69,10 +62,12 @@ Body.prototype.leaveSpecific = function leaveSpecific(node) {
};
// Roll-out apply expression
-Body.prototype.rollOutApply = function rollOutApply(type, ctx, changes) {
+Body.prototype.rollOutApply = function rollOutApply(type, changes) {
+ var ctx = this.compiler.ctx;
+
// Process changes in local
if (changes.length > 0) {
- return this.rollOutLocal(ctx, changes, function() {
+ return this.rollOutLocal(changes, function() {
return this.rollOutApply(type, ctx, [])
});
}
@@ -88,7 +83,7 @@ Body.prototype.rollOutApply = function rollOutApply(type, ctx, changes) {
argument: {
type: 'MemberExpression',
computed: true,
- object: this.compiler.ctx,
+ object: ctx,
property: this.applyFlag
}
}, {
@@ -139,7 +134,7 @@ Body.prototype.rollOutApply = function rollOutApply(type, ctx, changes) {
type: 'Identifier',
name: 'applyc'
},
- arguments: [ ctx || this.compiler.ctx ]
+ arguments: [ ctx ]
};
}
@@ -180,8 +175,9 @@ Body.prototype.rollOutApply = function rollOutApply(type, ctx, changes) {
};
// Roll-out local expression
-Body.prototype.rollOutLocal = function rollOutLocal(ctx, changes, body) {
+Body.prototype.rollOutLocal = function rollOutLocal(changes, body) {
var self = this,
+ ctx = this.compiler.ctx,
pairs = [],
predicates = [],
pairIndex = 0;
@@ -214,7 +210,7 @@ Body.prototype.rollOutLocal = function rollOutLocal(ctx, changes, body) {
};
return sub;
- }, ctx || this.compiler.ctx);
+ }, ctx);
addPair(this.compiler.replaceThis(prop),
this.compiler.replaceThis(property.value));
@@ -232,7 +228,7 @@ Body.prototype.rollOutLocal = function rollOutLocal(ctx, changes, body) {
arguments: []
};
- this.compiler.addChange(ctx, predicates);
+ this.compiler.addChange(predicates);
if (typeof body === 'function') {
body = body.call(this);
}
@@ -259,7 +255,7 @@ Body.prototype.rollOutLocal = function rollOutLocal(ctx, changes, body) {
var left = pair.prop;
// Store object
- if (left.object !== this.compiler.ctx) {
+ if (left.object !== ctx) {
var tmp = getVar();
before.push({
type: 'VariableDeclaration',
View
2  lib/xjst/compiler/group.js
@@ -28,7 +28,7 @@ Group.prototype.render = function render() {
result.push(this.pairs.map(function(pair) {
var out = [];
pair.bodies.forEach(function(body) {
- this.compiler.addChange(this.compiler.ctx, pair.predicate);
+ this.compiler.addChange(pair.predicate);
var local = body.render();
assert(!body.applyNext);
View
10 lib/xjst/compiler/index.js
@@ -67,7 +67,7 @@ Compiler.prototype.compile = function compile(code) {
var out = this.generate(code),
exports = {};
- vm.runInNewContext(out, { exports: exports });
+ vm.runInNewContext(out, { exports: exports, console: console });
return exports;
};
@@ -182,15 +182,9 @@ Compiler.prototype.replaceThis = function replaceThis(stmt) {
return stmt;
};
-Compiler.prototype.addChange = function addChange(ctx, predicate) {
+Compiler.prototype.addChange = function addChange(predicate) {
var predicates = Array.isArray(predicate) ? predicate : [ predicate ];
- // Changes apply only to this.ctx context
- if (!(ctx.type === 'Identifier' && ctx.name === this.ctx.name)) {
- this.renderHistory.push(0);
- return;
- }
-
this.renderHistory.push(predicates.length);
for (var i = 0; i < predicates.length; i++) {
this.renderStack.push(predicates[i]);
View
2  lib/xjst/compiler/template.js
@@ -30,7 +30,7 @@ Template.prototype.clone = function clone() {
Template.prototype.rollOut = function rollOut() {
var body = this.body;
- this.compiler.addChange(this.compiler.ctx, this.predicates);
+ this.compiler.addChange(this.predicates);
body.rollOut();
this.compiler.revertChange();
};
View
98 lib/xjst/utils.js
@@ -33,70 +33,64 @@ utils.run = function run(templates, context, ignore) {
};
};
- function local(context) {
- return function changesHandler() {
- var backup = [];
-
- Array.prototype.forEach.call(arguments, function(change) {
- Object.keys(change).forEach(function(key) {
- var parts = key.split('.'),
- newValue = change[key],
- oldValue,
- subContext = context;
-
- // Dive inside
- for (var i = 0; i < parts.length - 1; i++) {
- subContext = subContext[parts[i]];
- }
-
- // Set property and remember old value
- oldValue = subContext[parts[i]];
- subContext[parts[i]] = newValue;
-
- // Push old value to backup list
- backup.push({
- key: parts,
- value: oldValue
- });
+ function local() {
+ var backup = [];
+
+ Array.prototype.forEach.call(arguments, function(change) {
+ Object.keys(change).forEach(function(key) {
+ var parts = key.split('.'),
+ newValue = change[key],
+ oldValue,
+ subContext = context;
+
+ // Dive inside
+ for (var i = 0; i < parts.length - 1; i++) {
+ subContext = subContext[parts[i]];
+ }
+
+ // Set property and remember old value
+ oldValue = subContext[parts[i]];
+ subContext[parts[i]] = newValue;
+
+ // Push old value to backup list
+ backup.push({
+ key: parts,
+ value: oldValue
});
});
+ });
- return function bodyHandler(body) {
- var result = typeof body === 'function' ? body.call(context) : body;
-
- // Rollback old values
- for (var i = backup.length - 1; i >= 0; i--) {
- var subContext = context,
- change = backup[i];
+ return function bodyHandler(body) {
+ var result = typeof body === 'function' ? body.call(context) : body;
- // Dive inside
- for (var j = 0; j < change.key.length - 1; j++) {
- subContext = subContext[change.key[j]];
- }
+ // Rollback old values
+ for (var i = backup.length - 1; i >= 0; i--) {
+ var subContext = context,
+ change = backup[i];
- // Restore value
- subContext[change.key[j]] = change.value;
+ // Dive inside
+ for (var j = 0; j < change.key.length - 1; j++) {
+ subContext = subContext[change.key[j]];
}
- return result;
- };
+ // Restore value
+ subContext[change.key[j]] = change.value;
+ }
+
+ return result;
};
};
- function apply(context) {
- return function changeHandler() {
- return local(context).apply(this, arguments)(function() {
- return run(templates, context, ignore);
- });
- };
+ function apply() {
+ return local.apply(this, arguments)(function() {
+ return run(templates, context, ignore);
+ });
};
- function applyNext(context) {
- return function changeHandler() {
- return local(context).apply(this, arguments)(function() {
- return run(templates, context, ignore.concat(currentId));
- });
- };
+ function applyNext() {
+ return local.apply(this, arguments)(function() {
+ return run(templates, context, ignore.concat(currentId));
+ });
};
templates.call(context, template, local, apply, applyNext);
View
33 test/unit/compiler-test.js
@@ -4,12 +4,13 @@ var assert = require('assert');
describe('XJST Compiler', function () {
function run(fn, data, expected) {
var code = fn.toString().replace(/^function[^{]*{|}$/g, '');
- var c = xjst.compiler.create();
- var runtime = c.compile(code, { optimize: false }).apply.call(data || {});
+ var runtime = xjst.compile(code, {
+ optimize: false
+ }).apply.call(data || {});
assert.deepEqual(runtime, expected);
- var optimized = c.compile(code).apply.call(data || {});
+ var optimized = xjst.compile(code).apply.call(data || {});
assert.deepEqual(runtime, optimized);
}
@@ -17,12 +18,12 @@ describe('XJST Compiler', function () {
it('should support all syntax', function() {
run(function() {
template()(function() {
- return apply(this)({ x: 1 });
+ return apply({ x: 1 });
});
template(this.x === 1)(function() {
- return local(this)({ y: 2 }, { a: {}, 'a.b': 3 })(function() {
- return applyNext(this)();
+ return local({ y: 2 }, { a: {}, 'a.b': 3 })(function() {
+ return applyNext();
});
});
@@ -83,33 +84,17 @@ describe('XJST Compiler', function () {
it('should propagate local\'s context', function() {
run(function() {
template()(function() {
- return local(this)({ prop: this.nop })(function() {
+ return local({ prop: this.nop })(function() {
return this.prop;
});
});
}, { nop: 'yay' }, 'yay');
});
- it('should compile local with dynamic base', function() {
- run(function() {
- var once = 0;
- function base() {
- return {
- x: ++once
- };
- }
- template()(function() {
- return local(base())({ prop: this.nop })(function() {
- return once;
- });
- });
- }, { nop: 'yay' }, 1);
- });
-
it('should apply optimizations correctly', function() {
run(function() {
template(this.x === 1, this.y === 4)(function() {
- return local(this)({ y: 5 })(apply(this)());
+ return local({ y: 5 })(apply());
});
template(this.x === 1, this.y === 3)(function() {
return 'bad';
Please sign in to comment.
Something went wrong with that request. Please try again.