More compiler tests, docs and improvements

This commit is contained in:
Patrick Steele-Idem 2015-12-11 15:48:35 -07:00
parent eed53e84ec
commit 28a851723a
26 changed files with 819 additions and 195 deletions

View File

@ -24,22 +24,43 @@ var HtmlComment = require('./ast/HtmlComment');
var SelfInvokingFunction = require('./ast/SelfInvokingFunction'); var SelfInvokingFunction = require('./ast/SelfInvokingFunction');
var ForStatement = require('./ast/ForStatement'); var ForStatement = require('./ast/ForStatement');
var BinaryExpression = require('./ast/BinaryExpression'); var BinaryExpression = require('./ast/BinaryExpression');
var UpdateExpression = require('./ast/UpdateExpression');
class Builder { class Builder {
program(body) { assignment(left, right) {
return new Program({body}); return new Assignment({left, right});
} }
node(type) { binaryExpression(left, operator, right) {
return new Node(type); return new BinaryExpression({left, operator, right});
} }
templateRoot(body) { elseStatement(body) {
return new TemplateRoot({body}); return new Else({body});
} }
functionDeclaration(name, params, body) { elseIfStatement(test, body, elseStatement) {
return new FunctionDeclaration({name, params, body}); return new ElseIf({
if: new If({test, body, else: elseStatement})
});
}
forEach(varName, target, body) {
if (typeof varName === 'object') {
var options = varName;
return new ForEach(options);
} else {
return new ForEach({varName, target, body});
}
}
forStatement(init, test, update, body) {
if (typeof init === 'object' && !init.type) {
var def = arguments[0];
return new ForStatement(def);
} else {
return new ForStatement({init, test, update, body});
}
} }
functionCall(callee, args) { functionCall(callee, args) {
@ -54,53 +75,16 @@ class Builder {
return new FunctionCall({callee, args}); return new FunctionCall({callee, args});
} }
selfInvokingFunction(params, args, body) { functionDeclaration(name, params, body) {
if (arguments.length === 1) { return new FunctionDeclaration({name, params, body});
body = arguments[0];
params = null;
args = null;
}
return new SelfInvokingFunction({params, args, body});
} }
literal(value) { html(argument) {
return new Literal({value}); return new Html({argument});
} }
identifier(name) { htmlComment(comment) {
return new Identifier({name}); return new HtmlComment({comment});
}
ifStatement(test, body, elseStatement) {
return new If({test, body, else: elseStatement});
}
elseIfStatement(test, body, elseStatement) {
return new ElseIf({
if: new If({test, body, else: elseStatement})
});
}
elseStatement(body) {
return new Else({body});
}
assignment(left, right) {
return new Assignment({left, right});
}
strictEquality(left, right) {
var operator = '===';
return new BinaryExpression({left, right, operator});
}
vars(declarations, kind) {
return new Vars({declarations, kind});
}
returnStatement(argument) {
return new Return({argument});
} }
htmlElement(tagName, attributes, body, argument) { htmlElement(tagName, attributes, body, argument) {
@ -115,29 +99,24 @@ class Builder {
return new HtmlElement({tagName, attributes, body, argument}); return new HtmlElement({tagName, attributes, body, argument});
} }
html(argument) { identifier(name) {
return new Html({argument}); return new Identifier({name});
} }
text(argument, escape) { ifStatement(test, body, elseStatement) {
return new Text({argument, escape}); return new If({test, body, else: elseStatement});
} }
htmlComment(comment) { literal(value) {
return new HtmlComment({comment}); return new Literal({value});
} }
forEach(varName, target, body) { node(type) {
if (typeof varName === 'object') { return new Node(type);
var options = varName;
return new ForEach(options);
} else {
return new ForEach({varName, target, body});
}
} }
slot() { program(body) {
return new Slot(); return new Program({body});
} }
require(path) { require(path) {
@ -146,17 +125,43 @@ class Builder {
return new FunctionCall({callee, args}); return new FunctionCall({callee, args});
} }
forStatement(init, test, update, body) { returnStatement(argument) {
if (typeof init === 'object' && !init.type) { return new Return({argument});
var def = arguments[0];
return new ForStatement(def);
} else {
return new ForStatement({init, test, update, body});
}
} }
binaryExpression(left, operator, right) { selfInvokingFunction(params, args, body) {
return new BinaryExpression({left, operator, right}); if (arguments.length === 1) {
body = arguments[0];
params = null;
args = null;
}
return new SelfInvokingFunction({params, args, body});
}
slot() {
return new Slot();
}
strictEquality(left, right) {
var operator = '===';
return new BinaryExpression({left, right, operator});
}
templateRoot(body) {
return new TemplateRoot({body});
}
text(argument, escape) {
return new Text({argument, escape});
}
updateExpression(argument, operator, prefix) {
return new UpdateExpression({argument, operator, prefix});
}
vars(declarations, kind) {
return new Vars({declarations, kind});
} }
} }

View File

@ -76,9 +76,8 @@ class Compiler {
var context = new CompileContext(src, filename, this.builder); var context = new CompileContext(src, filename, this.builder);
var ast = this.parser.parse(src, context); var ast = this.parser.parse(src, context);
// console.log('ROOT', JSON.stringify(ast, null, 2));
console.log('ROOT', JSON.stringify(ast, null, 2));
var transformedAST = transformTree(ast, context); var transformedAST = transformTree(ast, context);
// console.log('transformedAST', JSON.stringify(ast, null, 2)); // console.log('transformedAST', JSON.stringify(ast, null, 2));

View File

@ -162,7 +162,8 @@ class HtmlElement extends Node {
type: this.type, type: this.type,
tagName: this.tagName, tagName: this.tagName,
attributes: this._attributes, attributes: this._attributes,
argument: this.argument argument: this.argument,
body: this.body
}; };
} }
} }

View File

@ -24,6 +24,11 @@ class Node {
wrapperNode.appendChild(this); wrapperNode.appendChild(this);
} }
/**
* Converts the provided `array` into a `ArrayContainer`. If the provided `array` is already an instance of a `Container` then it is simply returned.
* @param {[type]} array [description]
* @return {[type]} [description]
*/
makeContainer(array) { makeContainer(array) {
if (array instanceof Container) { if (array instanceof Container) {
return array; return array;

View File

@ -0,0 +1,39 @@
'use strict';
var Node = require('./Node');
class UpdateExpression extends Node {
constructor(def) {
super('UpdateExpression');
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.generateCode(operator);
}
generator.generateCode(argument);
if (!prefix) {
generator.generateCode(operator);
}
}
toJSON() {
return {
type: 'UpdateExpression',
argument: this.argument,
operator: this.operator,
prefix: this.prefix
};
}
}
module.exports = UpdateExpression;

View File

@ -15,52 +15,104 @@ Let's take a look at how to create a compile-time tag by providing our own code
} }
``` ```
A code generator module should export a function with the following signature: A code generator module should export a function as shown below:
```javascript ```javascript
function generateCode(elNode, generator) module.exports = function generateCode(elNode, generator) {
// ...
}
``` ```
Continuing with the `<greeting>`, let's assume we have the following template: The `elNode` argument will be an instance of [`HtmlElement`](../compiler/ast/HtmlElement.js). The `generator` argument will be an instance of [`CodeGenerator`](../compiler/CodeGenerator.js).
Continuing with the `<greeting>` example, let's assume we have the following template:
```xml ```xml
<greeting name="Frank"/> <greeting name="Frank"/>
``` ```
Let's implement the greeting tag that generates code by return an array of output AST nodes. Let's implement the greeting tag by generating code that will output `Hello Frank` using `console.log`:
```javascript ```javascript
module.exports = function generateCode(elNode, generator) { module.exports = function generateCode(elNode, generator) {
var builder = generator.builder; var builder = generator.builder;
var nameValue = elNode.getAttributeValue('name'); return builder.functionCall('console.log', [
return builder.functionCall('console.log', nameValue); builder.literal('Hello'),
elNode.getAttributeValue('name')
]);
}; };
``` ```
The above code results in the following compiled code: The above code results in the following compiled code:
```javascript ```javascript
out.w("Hello FrankHello " + console.log("Hello", data.name);
escapeXml(data.name));
``` ```
In the example code above, the `generateCode(elNode, generator)` method returns a new [`FunctionCall`](../compiler/ast/FunctionCall.js) node using the provided [`Builder`](../compiler/Builder.js) instance. The builder provides methods for generating nodes associated with JavaScript primitives. This is the recommended way to produce JavaScript code since it ensures that code is generated correctly and with proper formatting. However, for illustration purposes, the following also _works_ (just don't do it):
```javascript
module.exports = function generateCode(elNode, generator) {
var nameValue = elNode.getAttributeValue('name');
generator.write('console.log(');
generator.write('"Hello"');
generator.write(', ');
generator.generateCode(nameValue);
generator.write(')');
};
```
Writing to standard out using `console.log()` is probably _not_ what you want to do as a custom tag developer. You typically want to produce HTML output. Continuing with the same example, let's update our custom tag implementation to generate code that produces the following HTML output:
```html
<div class="greeting">Hello Frank</div>
```
To do that we will utilize the builder API to generate the appropriate tree of nodes:
```javascript ```javascript
module.exports = function generateCode(elNode, generator) { module.exports = function generateCode(elNode, generator) {
var builder = generator.builder; var builder = generator.builder;
return [
builder.text(builder.literal('Hello Frank')), return builder.htmlElement(
builder.text(elNode.getAttributeValue('name')) 'div',
]; {
'class': builder.literal('greeting')
},
[
builder.text(builder.literal('Hello ')),
builder.text(elNode.getAttributeValue('name'))
]);
}; };
``` ```
For the following template:
So when should you a custom compile-time tag and when should use use a compile-time transformer? ```xml
<greeting name="Frank"/>
```
Utilize custom compile-time tags when you need to create new custom tags that are capable of generating JavaScript code at compile-time. Examples of custom compile-time tags: The code generated by the custom tag will be the following:
- ```javascript
out.w("<div class=\"greeting\">Hello Frank</div>");
```
Compile-time transformers are useful for mod For the following template, where the name is a non-literal JavaScript expression:
```xml
<greeting name=data.name/>
```
The code generated by the custom tag will be the following:
```javascript
out.w("<div class=\"greeting\">Hello " +
escapeXml(data.name) +
"</div>");
```
# The Builder API
The Builder API plays a crucial role in generating code for a tag. The builder provides the building blocks for generating potentially complex JavaScript code.

View File

@ -1,28 +1,27 @@
Compiler Advanced Compiler Advanced
==================== ====================
Marko allows developers to control how templates generate JavaScript code at compile-time. Developers can create custom compile-time tags and it is also possible to transform the intermediate parse tree by adding, removing, modifying or rearranging nodes at compilation time. The Marko compiler is responsible for taking an input Marko template and producing an output JavaScript program. The Marko compiler was designed to be flexible and to allow developers to control how JavaScript code is produced.
# Overview # Compiler stages
The three primary stages of the Marko compiler are parse, transform, generate. Each of these stages is described in more detail below: The three primary stages of the Marko compiler are parse, transform, generate:
- __parse__ - Parse the template source to produce an [Abstract Syntax Tree (AST)](https//en.wikipedia.org/wiki/Abstract_syntax_tree).
- __transform__ - Transform the AST (add/remove/modify/rearrange nodes)
- __generate__ - Generate compiled JavaScript code based on the final AST
Each of these stages is described in more detail in the sections below.
## Parse stage ## Parse stage
The first stage of the Marko compiler takes the source template string and produces an Abstract Syntax Tree (AST). The first stage of the Marko compiler takes the source template string and produces an initial AST.
For example, given the following input template: For example, given the following input template:
```xml ```xml
Hello ${data.name}! <div if(data.name)>
Hello ${data.name}!
<ul if(notEmpty(data.colors))>
<li for(color in data.colors)>
${color}
</li>
</ul>
<div else>
No colors!
</div> </div>
``` ```
@ -32,47 +31,33 @@ The following AST will be produced:
{ {
"type": "TemplateRoot", "type": "TemplateRoot",
"body": [ "body": [
{
"type": "Text",
"argument": {
"type": "Literal",
"value": "Hello "
}
},
{
"type": "Text",
"argument": "data.name"
},
{
"type": "Text",
"argument": {
"type": "Literal",
"value": "!\n\n"
}
},
{
"type": "HtmlElement",
"tagName": "ul",
"attributes": [
{
"name": "if",
"argument": "notEmpty(data.colors)"
}
]
},
{
"type": "Text",
"argument": {
"type": "Literal",
"value": "\n"
}
},
{ {
"type": "HtmlElement", "type": "HtmlElement",
"tagName": "div", "tagName": "div",
"attributes": [ "attributes": [
{ {
"name": "else" "name": "if",
"argument": "data.name"
}
],
"body": [
{
"type": "Text",
"argument": {
"type": "Literal",
"value": "\n Hello "
}
},
{
"type": "Text",
"argument": "data.name"
},
{
"type": "Text",
"argument": {
"type": "Literal",
"value": "!\n"
}
} }
] ]
} }
@ -80,61 +65,113 @@ The following AST will be produced:
} }
``` ```
----------
# Creating a compile-time tag _TIP:_ If you want a pretty dump of an AST tree for debugging purposes you can use the following code:
Let's take a look at how to create a compile-time tag by providing our own code generator. The first step is to register the custom tag in a `marko-taglib.json` file. We will use the `code-generator` property to associate the custom tag with a function that will be used to generate the code for the element node at compile-time: ```javascript
console.log(JSON.stringify(astNode, null ,2));
```
```json ----------
{
"<greeting>": { ## Transform stage
"code-generator": "./greeting-tag"
} During the transform stage, the AST is manipulated in order to produce the correct compiled code during the generate stage. For example, AST transformers are used to process special attributes such as the following:
- `if()`
- `else-if()`
- `else`
- `for()`
- etc.
In the case of the `if()` attribute, the node is transformed by a builtin Marko transformer (specifically, [taglibs/core/core-transformer.js](../taglibs/core/core-transformer.js)) in order to be wrapped with an actual `If` node using code similar to the following:
```javascript
var ifAttr = elNode.getAttribute('if');
if (ifAttr && ifAttr.argument) {
// Remove the if() attribute from the HTML element node
elNode.removeAttribute('if');
// Create a new "If" node using the provided "builder"
// (described later)
var ifNode = builder.ifStatement(ifArgument);
//Surround the existing node with an "If" node
node.wrap(ifNode);
} }
``` ```
The code generator module should export a function with the following signature: Continuing with the previous example, after the transformation stage, the AST will be the following:
```json
{
"type": "TemplateRoot",
"body": [
{
"type": "If",
"test": "data.name",
"body": [
{
"type": "HtmlElement",
"tagName": "div",
"attributes": [],
"body": [
{
"type": "Text",
"argument": {
"type": "Literal",
"value": "\n Hello "
}
},
{
"type": "Text",
"argument": "data.name"
},
{
"type": "Text",
"argument": {
"type": "Literal",
"value": "!\n"
}
}
]
}
]
}
]
}
```
You'll notice in the transformed AST that the `HtmlElement` associated with the `<div>` tag was wrapped with a new `If` node. After the AST has been transformed it is now time to generate the compiled JavaScript code.
During the transform stage, the entire AST might be walked multiple times. Not until there are no more nodes transformed does the transform stage complete.
## Generate stage
The generate stage is the final stage of the Marko compiler. During the generate stage the Marko compiler will walk the tree to produce the final JavaScript code. Each node in the tree will have an opportunity to generate JavaScript code. The Marko compiler provides a [`CodeGenerator`](../compiler/CodeGenerator.js) class and an API for generating fragments of JavaScript code that makes it easy to produce well-formed and readable JavaScript code as output.
Every node in the tree must implement one of the following methods:
- `generateCode(generator)`
- `generate<OUTPUT_TYPE>Code(generator)` (e.g. `generateHtmlCode(generator)`)
The `generator` argument will be an instance of [`CodeGenerator`](../compiler/CodeGenerator.js).
The Marko compiler supports compiling templates differently based on an "output type". Currently, the only supported output type is "Html". With the "Html" output type, the compiled template will be a program that, when executed, will produce an HTML string as output. In the future we may support other output types such as DOM, Virtual DOM, incremental DOM, etc. For example, with the "DOM" output type, the compiled program could use the web browser's DOM API to produce a DOM tree as output (instead of an HTML string).
Below is the fragment of code used by the `If` node to generate the output JavaScript code:
```javascript ```javascript
function generateCode(elNode, generator) : Node generator.write('if (');
generator.generateCode(test);
generator.write(') ');
generator.generateBlock(body);
if (elseStatement) {
generator.write(' ');
generator.generateCode(elseStatement);
} else {
generator.write('\n');
}
``` ```
Continuing with the `<greeting>`, let's assume we have the following template:
```xml
<greeting name="Frank"/>
```
Let's implement the greeting tag that generates code that uses `console.log` to output `Hello <NAME>` as shown below:
```javascript
module.exports = function generateCode(elNode, generator) {
var builder = generator.builder;
return [
builder.text(builder.literal('Hello Frank')),
builder.text(elNode.getAttributeValue('name'))
];
};
```
```javascript
module.exports = function generateCode(elNode, generator) {
var builder = generator.builder;
return [
builder.text(builder.literal('Hello Frank')),
builder.text(elNode.getAttributeValue('name'))
];
};
```
So when should you a custom compile-time tag and when should use use a compile-time transformer?
Utilize custom compile-time tags when you need to create new custom tags that are capable of generating JavaScript code at compile-time. Examples of custom compile-time tags:
-
Compile-time transformers are useful for mod

312
docs/compiler-api.md Normal file
View File

@ -0,0 +1,312 @@
The Compiler API
================
# AST
## Node
The `Node` type is the base class for all AST nodes. All AST nodes added to the AST must extend `Node`.
### Methods
#### wrap(wrapperNode)
Makes the current node a child of the provided `wrapperNode`. Similar to the following:
```javascript
this.container.replaceChild(wrapperNode, this);
wrapperNode.appendChild(this);
```
#### makeContainer(array)
Converts the provided `array` into a `ArrayContainer`. If the provided `array` is already an instance of a `Container` then it is simply returned.
#### appendChild(node)
Appends a child node to the associated container for the node. The `this.body` property is used as the default container, but this method can be overridden in derived nodes.
### Properties
#### type
The node type as a String. Example node types: `"TemplateRoot"`, `"HtmlElement"`, `"Text"`, `"If"`, `"Else"`, `"ForEach"`, etc.
#### container
If a `Node` is the child of another `Node` then it will be associated with a `Container`. For example:
```javascript
if (this.container) {
var parentNode = this.container.node;
// NOTE: The following lines produce the same result:
this.container.removeChild(this)
this.detach()
} else {
// Either the node is the root node or it is detached from the AST
}
```
## Container
## TemplateRoot
## HtmlElement
## Text
## JavaScript node types
# Builder
## methods
### assignment(left, right)
Returns a node that generates the following code:
```javascript
<left> = <right>;
```
For example:
```javascript
builder.assignment(
builder.identifier('foo'),
builder.literal('123'));
// Output code:
foo = '123';
```
### binaryExpression(left, operator, right)
Returns a node that generates the following code:
```javascript
<left> <operator> <right>;
```
For example:
```javascript
builder.binaryExpression(
builder.identifier('foo'),
'<'
builder.literal(99));
// Output code:
foo < 99;
```
### elseStatement(body)
Returns a node that generates the following code:
```javascript
else {
<body>
}
```
For example:
```javascript
builder.elseStatement([
builder.functionCall('console.log', ['hello']),
builder.functionCall('console.log', ['world'])
]);
// Output code:
else {
console.log('hello');
console.log('world');
}
```
### elseIfStatement(test, body, elseStatement)
Returns a node that generates the following code:
```javascript
else if (<test>) {
<body>
}[ <elseStatement>]
```
For example:
```javascript
builder.elseIfStatement(
builder.literal(true),
[
builder.functionCall('console.log', ['hello']),
builder.functionCall('console.log', ['world'])
]);
// Output code:
else if (true) {
console.log('hello');
console.log('world');
}
```
### forEach(def)
Returns a node that generates code to loop over an array, object properties or a range.
___array:___
```javascript
builder.forEach({
varName: 'color',
target: 'colors'
body: [
builder.functionCall('console.log', [
builder.identifier('color')
])
]
})
// Output code:
var forEach = __helpers.f; // Static variable
// ...
forEach(data.colors, function(color) {
out.w(escapeXml(color));
});
```
___object properties:___
TBD
___range:___
```javascript
builder.forEach({
varName: 'i',
from: 0,
to: 'myArray.length',
step: 2,
body: [
builder.functionCall('console.log', ['hello'])
]
})
// Output code:
(function() {
for (var i = 0; i<=myArray.length; i+=2) {
console.log(i);
}
}());
```
### forEach(varName, target, body)
Returns a node that generates a simple `forEach`. See `forEach(def)`.
### forStatement(init, test, update, body)
Returns a node that generates the following code:
```javascript
for (<init>; <test>; <update>) {
<body>
}
```
For example:
```javascript
builder.forStatement(
builder.vars([
{
id: 'i',
init: builder.literal(0)
}
]),
builder.binaryExpression('i', '<', builder.literal(0)),
builder.updateExpression('i', '++'),
[
builder.functionCall('console.log', [
builder.identifier('i')
])
]
)
// Output:
for (var i = 0; i < 0; i++) {
console.log(i);
}
```
### functionCall(callee, args)
### functionDeclaration(name, params, body)
### html(argument)
### htmlComment(comment)
### htmlElement(tagName, attributes, body, argument)
### identifier(name)
### ifStatement(test, body, elseStatement)
Returns a node that generates the following code:
```javascript
if (<test>) {
<body>
}[ <elseStatement>]
```
For example:
```javascript
builder.ifStatement(
builder.literal(true),
[
builder.functionCall('console.log', ['hello']),
builder.functionCall('console.log', ['world'])
]);
// Output code:
if (true) {
console.log('hello');
console.log('world');
}
```
### literal(value)
### node(type)
### program(body)
### require(path)
### returnStatement(argument)
### selfInvokingFunction(params, args, body)
### slot()
### strictEquality(left, right)
### templateRoot(body)
### text(argument, escape)
### updateExpression(argument, operator, prefix)
### vars(declarations, kind)
# CodeGenerator

View File

@ -1,9 +1,9 @@
var parseForEach = require('./util/parseForEach'); var parseForEach = require('./util/parseForEach');
module.exports = function nodeFactory(elNode, context) { module.exports = function codeGenerator(elNode, generator) {
var argument = elNode.argument; var argument = elNode.argument;
if (!argument) { if (!argument) {
context.addError(elNode, 'Invalid <for> tag. Argument is missing. Example; <for(color in colors)>'); generator.addError('Invalid <for> tag. Argument is missing. Example; <for(color in colors)>');
return elNode; return elNode;
} }
@ -18,5 +18,5 @@ module.exports = function nodeFactory(elNode, context) {
forEachProps.statusVarName = elNode.getAttributeValue('status-var'); forEachProps.statusVarName = elNode.getAttributeValue('status-var');
} }
return context.builder.forEach(forEachProps); return generator.builder.forEach(forEachProps);
}; };

View File

@ -1,6 +1,6 @@
{ {
"<for>": { "<for>": {
"node-factory": "./for-tag" "code-generator": "./for-tag"
}, },
"<if>": { "<if>": {
"node-factory": "./if-tag" "node-factory": "./if-tag"

1
test/.gitignore vendored Normal file
View File

@ -0,0 +1 @@
/node_modules

View File

@ -0,0 +1,15 @@
function create(__helpers) {
var str = __helpers.s,
empty = __helpers.e,
notEmpty = __helpers.ne,
escapeXml = __helpers.x,
forEach = __helpers.f;
return function render(data, out) {
forEach(data.colors, function(color) {
out.w(escapeXml(color));
});
};
}
(module.exports = require("marko").c(__filename)).c(create);

View File

@ -0,0 +1,22 @@
'use strict';
module.exports = function(builder) {
var templateRoot = builder.templateRoot;
var forEach = builder.forEach;
return templateRoot([
forEach({
nameVarName: 'k',
valueVarName: 'v',
target: 'myArray',
body: [
builder.functionCall('console.log', [
builder.literal('k:'),
builder.identifier('k'),
builder.literal('v:'),
builder.identifier('v')
])
]
})
]);
};

View File

@ -0,0 +1,16 @@
function create(__helpers) {
var str = __helpers.s,
empty = __helpers.e,
notEmpty = __helpers.ne,
escapeXml = __helpers.x;
return function render(data, out) {
(function() {
for (var i = 0; i<=myArray.length; i+=2) {
console.log(i);
}
}());
};
}
(module.exports = require("marko").c(__filename)).c(create);

View File

@ -0,0 +1,20 @@
'use strict';
module.exports = function(builder) {
var templateRoot = builder.templateRoot;
var forEach = builder.forEach;
return templateRoot([
forEach({
varName: 'i',
from: 0,
to: 'myArray.length',
step: 2,
body: [
builder.functionCall('console.log', [
builder.identifier('i')
])
]
})
]);
};

View File

@ -0,0 +1,14 @@
function create(__helpers) {
var str = __helpers.s,
empty = __helpers.e,
notEmpty = __helpers.ne,
escapeXml = __helpers.x;
return function render(data, out) {
for (var i = 0; i < 0; i++) {
console.log(i);
}
};
}
(module.exports = require("marko").c(__filename)).c(create);

View File

@ -0,0 +1,22 @@
'use strict';
module.exports = function(builder) {
return builder.templateRoot([
builder.forStatement(
builder.vars([
{
id: 'i',
init: builder.literal(0)
}
]),
builder.binaryExpression('i', '<', builder.literal(0)),
builder.updateExpression('i', '++'),
[
builder.functionCall('console.log', [
builder.identifier('i')
])
]
)
]);
};

View File

@ -75,6 +75,43 @@
"value": "colors" "value": "colors"
} }
} }
],
"body": [
{
"type": "FunctionCall",
"callee": "forEach",
"args": [
"data.colors",
{
"type": "FunctionDeclaration",
"name": null,
"params": [
"color"
],
"body": [
{
"type": "HtmlElement",
"tagName": "li",
"attributes": [
{
"name": "class",
"value": {
"type": "Literal",
"value": "color"
}
}
],
"body": [
{
"type": "Text",
"argument": "color"
}
]
}
]
}
]
}
] ]
} }
] ]

View File

@ -0,0 +1 @@
<div class="greeting">Hello Frank</div><div class="greeting">Hello John</div>

View File

@ -0,0 +1,2 @@
<test-tag-code-generator-return-tree name="Frank"/>
<test-tag-code-generator-return-tree name=data.name/>

View File

@ -0,0 +1,3 @@
exports.templateData = {
name: 'John'
};

View File

@ -0,0 +1 @@
FRANK 3650

View File

@ -0,0 +1,3 @@
<var name='Frank' age=10/>
${name.toUpperCase()} ${age*365}

View File

@ -0,0 +1 @@
exports.templateData = {};

View File

@ -0,0 +1,13 @@
module.exports = function generateCode(elNode, generator) {
var builder = generator.builder;
return builder.htmlElement(
'div',
{
'class': builder.literal('greeting')
},
[
builder.text(builder.literal('Hello ')),
builder.text(elNode.getAttributeValue('name'))
]);
};

View File

@ -0,0 +1,3 @@
{
"@name": "string"
}