Skip to content

Commit

Permalink
Merge 6225194 into ff2a6bc
Browse files Browse the repository at this point in the history
  • Loading branch information
PK1A committed Aug 20, 2014
2 parents ff2a6bc + 6225194 commit bbe57b7
Show file tree
Hide file tree
Showing 90 changed files with 1,542 additions and 270 deletions.
2 changes: 1 addition & 1 deletion hsp/compiler/jsgenerator/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -159,7 +159,7 @@ function _generateLineMap (res, file) {
}
}

var nbrOfLinesInCompiledTemplate = 5;
var nbrOfLinesInCompiledTemplate = 6;
var lineMap = [], pos = HEADER_SZ, template;
var pos1 = -1; // position of the next template start
var pos2 = -1; // position of the next template end
Expand Down
27 changes: 22 additions & 5 deletions hsp/compiler/jsgenerator/processors.js
Original file line number Diff line number Diff line change
@@ -1,3 +1,6 @@
var exParser = require('../../expressions/parser');
var exIdentifiers = require('../../expressions/identifiers');

/**
* Escapes new lines characters in a string.
* @param {String} text the input string.
Expand Down Expand Up @@ -39,21 +42,24 @@ exports["template"] = function (node, walker) {
}

//Generates the code of the template's content
var templateCode = ["[", walker.walk(node.content, module.exports).join(","), "]"].join("");
var templateCode = ['[', ['__s'].concat(walker.walk(node.content, module.exports)).join(","), ']'].join("");
var globals = walker._globals;

//Generates globals validation statement - e.g. var _c;try {_c=c} catch(e) {};
var globalsStatement = [], globalsLength = globals.length;
var scopeStatements = [], scopeStr;
if (globalsLength) {
var gnm;
globalsStatement = [" var _" + globals.join(',_') + ";"];
for (var i=0; i < globalsLength; i++) {
gnm=globals[i];
globalsStatement.push( "try {_" + gnm + "=", gnm ,"} catch(e) {_" + gnm + "=n.g('", gnm ,"')};");
scopeStatements.push(gnm + " : typeof " + gnm + " === 'undefined' ? undefined : " + gnm);
}
globalsStatement.push(CRLF);
}
var globalsStatementString = globalsStatement.join("");
scopeStr = " var __s = {" + scopeStatements.join(", ") + "};" + CRLF;

//Resets template scope and global list
walker.resetScope();
Expand All @@ -68,17 +74,17 @@ exports["template"] = function (node, walker) {

var hspRef='require("hsp/rt")';
if (walker.mode.isGlobal) {
hspRef=walker.globalRef; // default: "hsp"
exportString=''; // export should be ignored if commonJS is not used
hspRef = walker.globalRef; // default: "hsp"
exportString = ''; // export should be ignored if commonJS is not used
}

if (node.controller) {
var path = node.controller.path;
return ['var ', templateName, exportString, ' = ',hspRef,'.template({ctl:[', path[0], ',', walker.each(path, argAsString),
'],ref:"', node.controller.ref, '"}, function(n){', CRLF, globalsStatementString, ' return ', templateCode, ';', CRLF, '});', CRLF].join("");
'],ref:"', node.controller.ref, '"}, function(n){', CRLF, globalsStatementString, scopeStr, ' return ', templateCode, ';', CRLF, '});', CRLF].join("");
} else {
return ['var ', templateName, exportString, ' = ',hspRef,'.template([', walker.each(node.args, argAsString),
'], function(n){', CRLF, globalsStatementString, ' return ', templateCode, ';', CRLF, '});', CRLF].join("");
'], function(n){', CRLF, globalsStatementString, scopeStr, ' return ', templateCode, ';', CRLF, '});', CRLF].join("");
}
};

Expand Down Expand Up @@ -480,6 +486,17 @@ function formatExpression (expression, firstIndex, walker) {
codefragments.splice(0, 0, code0);
code = codefragments.join(',');
nextIndex = index;
} else if (category === 'jsexptext') {
//compile the expression to detect errors and parse-out identifiers
try {
exIdentifiers(exParser(expression.value)).forEach(function(ident){
walker.addGlobalRef(ident);
});
code = ['e', exprIndex, ':[9,"', ('' + expression.value).replace(/"/g, "\\\""), '"]'].join('');
} catch (err) {
walker.logError("Invalid expression: '" + expression.value + "'", expression);
}
nextIndex++;
} else {
walker.logError("Unsupported expression: " + category, expression);
}
Expand Down
17 changes: 15 additions & 2 deletions hsp/compiler/parser/hspblocks.pegjs
Original file line number Diff line number Diff line change
Expand Up @@ -111,11 +111,11 @@ InvalidBlock
{return {type:"invalidblock", code:chars.join(''), line:line, column:column}}

IfBlock "if statement"
= "{" _ "if " _ expr:HPipeExpression _ "}" EOS?
= "{" _ "if " _ expr:CoreExpText _ "}" EOS?
{return {type:"if", condition:expr, line:line, column:column}}

ElseIfBlock "elseif statement"
= "{" _ "else " _ "if" _ expr:HPipeExpression _ "}" EOS?
= "{" _ "else " _ "if" _ expr:CoreExpText _ "}" EOS?
{return {type:"elseif", condition:expr, line:line, column:column}}

ElseBlock
Expand Down Expand Up @@ -296,6 +296,19 @@ ExpressionBlock
return r;
}

CoreExpText // valid or invalid expression text
= c:( c1:[^{}]+ {return c1.join('')}
/ c2:("{" CoreExpText? "}") {return c2.join('')}
/ c3:("{" CoreExpText? ) {return c3.join('')}
)+ {
return {
"category": "jsexptext",
"value": c.join(''),
"line": line,
"column": column
};
}

HExpression
= HExpressionCssClassElt
/ HPipeExpression
Expand Down
10 changes: 7 additions & 3 deletions hsp/compiler/treebuilder/syntaxTree.js
Original file line number Diff line number Diff line change
Expand Up @@ -408,9 +408,13 @@ var SyntaxTree = klass({
__if : function (index, blocks, out) {
//creates the if node
var node = new Node("if"), block = blocks[index], lastValidIndex = index;
var condition = new HExpression(block.condition, this);
node.condition = condition.getSyntaxTree();
node.condition.bound = true;
node.condition = {
"category": block.condition.category,
"value": block.condition.value,
"line": block.condition.line,
"column": block.condition.column
};
node.condition.bound = true; //TODO: what does it mean?
node.content1 = [];
out.push(node);

Expand Down
2 changes: 1 addition & 1 deletion hsp/es5.js
Original file line number Diff line number Diff line change
Expand Up @@ -69,7 +69,7 @@ if (!Function.prototype.bind) {
};
}

//Object.create
// Object.create
if (typeof Object.create != 'function') {
(function () {
var F = function () {};
Expand Down
97 changes: 97 additions & 0 deletions hsp/expressions/evaluator.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,97 @@
function forgivingPropertyAccessor(left, right) {
return typeof left === 'undefined' || left === null ? undefined : left[right];
}

var UNARY_OPERATORS = {
'!': function (right) { return !right; },
'-': function (right) { return -right; },
'[': function (right) { return right; }, //array literal
'{': function (right) { //object literal

var result = {}, keyVal;
for (var i = 0; i < right.length; i++) {
keyVal = right[i];
result[keyVal.k] = keyVal.v;
}

return result;
}
};

var BINARY_OPERATORS = {
'+': function (left, right) { return left + right; },
'-': function (left, right) { return left - right; },
'*': function (left, right) { return left * right; },
'/': function (left, right) { return left / right; },
'%': function (left, right) { return left % right; },
'<': function (left, right) { return left < right; },
'>': function (left, right) { return left > right; },
'>=': function (left, right) { return left >= right; },
'<=': function (left, right) { return left <= right; },
'==': function (left, right) { return left == right; },
'!=': function (left, right) { return left != right; },
'===': function (left, right) { return left === right; },
'!==': function (left, right) { return left !== right; },
'||': function (left, right) { return left || right; },
'&&': function (left, right) { return left && right; },
'(': function (left, right) { //function call on a scope
return left.apply(left, right);
},
'.': forgivingPropertyAccessor,
'[': forgivingPropertyAccessor
};

var TERNARY_OPERATORS = {
'(': function (target, name, args) { //function call on an object
return typeof target === 'undefined' || target === null ?
undefined : target[name].apply(target, args);
},
'?': function (test, trueVal, falseVal) { return test ? trueVal : falseVal; },
'|': function (input, pipeFn, args) { return pipeFn.apply(pipeFn, [input].concat(args)); } //pipe (filter)
};

module.exports = function getTreeValue(tree, scope) {

var operatorFn, result;
var parsedVal, argExp, arrayResult;

if (tree instanceof Array) {

if (tree.length > 0) {
result = new Array(tree.length);
for (var i = 0; i < tree.length; i++) {
argExp = tree[i];
arrayResult = parsedVal = getTreeValue(argExp, scope);
if (argExp.key) {
arrayResult = {
k: argExp.key,
v: parsedVal
};
}
result[i] = arrayResult;
}
} else {
result = [];
}
return result;
}

if (tree.a === 'literal') {
result = tree.v;
} else if (tree.a === 'idn') {
result = scope[tree.v];
} else if (tree.a === 'unr' && UNARY_OPERATORS[tree.v]) {
operatorFn = UNARY_OPERATORS[tree.v];
result = operatorFn(getTreeValue(tree.l, scope));
} else if (tree.a === 'bnr' && BINARY_OPERATORS[tree.v]) {
operatorFn = BINARY_OPERATORS[tree.v];
result = operatorFn(getTreeValue(tree.l, scope), getTreeValue(tree.r, scope));
} else if (tree.a === 'tnr' && TERNARY_OPERATORS[tree.v]) {
operatorFn = TERNARY_OPERATORS[tree.v];
result = operatorFn(getTreeValue(tree.l, scope), getTreeValue(tree.r, scope), getTreeValue(tree.othr, scope));
} else {
throw new Error('Unknown tree entry of type "'+ tree.a +' and value ' + tree.v + ' in:' + JSON.stringify(tree));
}

return result;
};
29 changes: 29 additions & 0 deletions hsp/expressions/identifiers.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,29 @@
module.exports = function getIdentifiers(tree) {

var partialResult;

if (tree instanceof Array) {
partialResult = [];
if (tree.length > 0) {
for (var i = 0; i < tree.length; i++) {
partialResult = partialResult.concat(getIdentifiers(tree[i]));
}
}
return partialResult;
}

if (tree.a === 'literal') {
return [];
} else if (tree.a === 'idn') {
return [tree.v];
} else if (tree.a === 'unr') {
return getIdentifiers(tree.l);
} else if (tree.a === 'bnr') {
return getIdentifiers(tree.l).concat(getIdentifiers(tree.r));
} else if (tree.a === 'tnr') {
return getIdentifiers(tree.l).concat(getIdentifiers(tree.r))
.concat(getIdentifiers(tree.othr));
} else {
throw new Error('unknown entry' + JSON.stringify(tree));
}
};
124 changes: 124 additions & 0 deletions hsp/expressions/lexer.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,124 @@
function isWhitespace(ch) {
return ch === '\t' || ch === '\r' || ch === '\n' || ch === ' ';
}

function isQuote(ch) {
return ch === '"' || ch === "'";
}

function isDigit(ch) {
return ch >= '0' && ch <= '9';
}

function isIdentifierStart(ch) {
return ch >= 'a' && ch <= 'z' || ch >= 'A' && ch <= 'Z' || ch === '$' || ch === '_';
}

function isIdentifierPart(ch) {
return isIdentifierStart(ch) || isDigit(ch);
}

function isOperator(ch) {
return '+-*/%!|&.,=<>()[]{}?:'.indexOf(ch) > -1;
}

function isSuffixOperator(ch) {
return '=|&'.indexOf(ch) > -1;
}

/**
* A lexing function
* @param input - a string of characters to be tokenised
* @returns {Array} - an array of token objects with the following properties:
* - t: type of token, one of: num (number), idn (identifier), str (string), opr (operator)
* - v: value of a token
* - f: from where (index) a given token starts in the input
* @throws {Error} when an unknown character is detected in the input (ex.: ^)
*/
module.exports = function (initialInput) {

var input, EOF = String.fromCharCode(0);
var result = [];
var i = 0, current, quote; //current is a character that the lexer is currently looking at
var from, value;

if (typeof initialInput === 'string') {

//append special EOF token to avoid constant checks for the input end
input = initialInput + EOF;

current = input.charAt(0);
while (current !== EOF) {

//reset variables responsible for accumulating results
from = i;
value = '';

if (isWhitespace(current)) {

current = input.charAt(++i); //skip

} else if (isOperator(current)) {

do {
value += current;
current = input.charAt(++i);

} while (isSuffixOperator(current));

result.push({t: 'opr', v: value, f: from});

} else if (isIdentifierStart(current)) {

do {
value += current;
current = input.charAt(++i);

} while (isIdentifierPart(current));

result.push({t: 'idn', v: value, f: from});

} else if (isQuote(current)) {

quote = current;
current = input.charAt(++i); //skip the initial quote

while (current !== quote && current !== EOF) {

if (current === '\\' && input.charAt(i + 1) === quote) {
value += quote;
current = input.charAt(++i);
} else {
value += current;
}
current = input.charAt(++i);
}

if (isQuote(current)) {
result.push({t: 'str', v: value, f: from});
current = input.charAt(++i); //consume the closing quote
} else {
throw new Error('Error parsing "' + initialInput + '": unfinished string at ' + from);
}

} else if (isDigit(current)) {

do {
value += current;
current = input.charAt(++i);

} while (isDigit(current) || current === '.');

result.push({
t: 'num',
v: value.indexOf('.') > -1 ? parseFloat(value) : parseInt(value),
f: from});

} else {
throw new Error('Error parsing "' + initialInput + '": unknown token ' + current + ' at ' + from);
}
}
}

return result;
};

0 comments on commit bbe57b7

Please sign in to comment.