Skip to content

Commit

Permalink
White space management
Browse files Browse the repository at this point in the history
  • Loading branch information
marclaval committed Sep 10, 2014
1 parent 5facfb4 commit a5ca809
Show file tree
Hide file tree
Showing 3 changed files with 73 additions and 29 deletions.
2 changes: 1 addition & 1 deletion hsp/compiler/jsgenerator/processors.js
Original file line number Diff line number Diff line change
Expand Up @@ -526,7 +526,7 @@ function formatTextBlock (node, nextExprIndex, walker) {
// (=args)
for (var i = 0; i < content.length; i++) {
item = content[i];
if (item.type === "text") {
if (item.type === "text" || item.type === "eol") {
if (index % 2 === 0) {
// even index: arg must be a string
args[index] = '"' + escapeNewLines(item.value.replace(/"/g, "\\\"")) + '"';
Expand Down
42 changes: 20 additions & 22 deletions hsp/compiler/parser/hspblocks.pegjs
Original file line number Diff line number Diff line change
Expand Up @@ -75,7 +75,7 @@ TemplateEnd2
{return {type:"/template",line:line,column:column}}

TemplateContent "template content" // TODO: CSSClassExpression
= _ blocks:( TplTextBlock
= blocks:( EOLBlock / TplTextBlock
/ CommentBlock / HTMLCommentBlock
/ IfBlock / ElseIfBlock / ElseBlock / EndIfBlock
/ ForeachBlock / EndForeachBlock
Expand All @@ -96,41 +96,38 @@ TplTextBlock "text"
TplTextChar "text character"
= "\\{" {return "\u007B"} // { = \u007B
/ "\\}" {return "\u007D"} // } = \u007D
/ "\\n" {return "\n"}
/ EOL &TemplateEnd {return ""} // ignore last EOL
/ EOL _ {return " "}
/ "#" !(_ "\/template") {return "#"}
/ "\/" !"/" {return "/"}
/ "\\/" {return "/"}
/ "\\//" {return "//"}
/ "\\<" {return "<"}
/ [^{#/<]
/ [^{#/<\n\r\u2028\u2029]

InvalidBlock
= "{" !(_ "/template" _ "}") chars:[^{}#]* "}"
{return {type:"invalidblock", code:chars.join(''), line:line, column:column}}

IfBlock "if statement"
= "{" _ "if " _ expr:(IfCondWithBrackets / CoreExpText) _ "}" EOS?
= "{" _ "if " _ expr:(IfCondWithBrackets / CoreExpText) _ "}"
{return {type:"if", condition:expr, line:line, column:column}}

IfCondWithBrackets
= "(" expr:CoreExpText ")" {return expr}

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

ElseBlock
= "{" _ "else" _ "}" EOS?
= "{" _ "else" _ "}"
{return {type:"else", line:line, column:column}}

EndIfBlock
= "{" _ "/if" _ "}" EOS?
= "{" _ "/if" _ "}"
{return {type:"endif", line:line, column:column}}

CommentBlock
= _ "\/\/" chars:[^\r\n]* &EOL
= _ "\/\/" chars:[^\r\n]*
{return {type:"comment", value:chars.join('')}}

HTMLCommentBlock
Expand All @@ -144,7 +141,7 @@ HTMLCommentChar
/ [^>\-]

ForeachBlock
= "{" _ "foreach " _ args:( ForeachArgs / ("(" _ a:ForeachArgs _ ")") {return a}) _ "}" EOS?
= "{" _ "foreach " _ args:( ForeachArgs / ("(" _ a:ForeachArgs _ ")") {return a}) _ "}"
{return {type:"foreach", item:args.item, key:args.key, colref:args.colref, line:line, column:column}}

ForeachArgs
Expand All @@ -163,30 +160,30 @@ EndForeachBlock
{return {type:"endforeach", line:line, column:column}}

HTMLElement
= "<" name:HTMLName atts:HTMLElementAttributes? S? end:"/"? ">" EOS?
= "<" name:HTMLName atts:HTMLElementAttributes? S? end:"/"? ">"
{return {type:"element", name:name, closed:(end!==""), attributes:atts, line:line, column:column}}

HTMLElementAttributes
= atts:((S att:(HTMLAttribute)) {return att})*

EndHTMLElement // TODO support comments inside Element
= "</" name:HTMLName S? ">" EOS?
= "</" name:HTMLName S? ">"
{return {type:"endelement", name:name, line:line, column:column}}

HspComponent
= "<#" ref:JSObjectRef atts:HTMLElementAttributes? S? end:"/"? ">" EOS?
= "<#" ref:JSObjectRef atts:HTMLElementAttributes? S? end:"/"? ">"
{return {type:"component", ref:ref, closed:(end!==""), attributes:atts, line:line, column:column}}

EndHspComponent
= "</#" ref:JSObjectRef S? ">" EOS?
= "</#" ref:JSObjectRef S? ">"
{return {type:"endcomponent", ref:ref, line:line, column:column}}

HspCptAttribute
= "<@" ref:VarIdentifier atts:HTMLElementAttributes? S? end:"/"? ">" EOS?
= "<@" ref:VarIdentifier atts:HTMLElementAttributes? S? end:"/"? ">"
{return {type:"cptattribute", name:ref, closed:(end!==""), attributes:atts, line:line, column:column}}

EndHspCptAttribute
= "</@" ref:VarIdentifier S? ">" EOS?
= "</@" ref:VarIdentifier S? ">"
{return {type:"endcptattribute", name:ref, line:line, column:column}}

InvalidHTMLElement
Expand Down Expand Up @@ -221,7 +218,7 @@ HTMLAttributeChar // TODO look at W3C specs
/ [^{\"\n\r]

LogBlock
= "{" _ "log " _ first:CoreExpText _ next:("," _ CoreExpText)* _"}" EOS?
= "{" _ "log " _ first:CoreExpText _ next:("," _ CoreExpText)* _"}"
{
var exprs=[first];
if (next) {
Expand All @@ -233,7 +230,7 @@ LogBlock
}

LetBlock
= "{" _ "let " _ first:CoreExpText __ next:("," __ CoreExpText)* "}" EOS?
= "{" _ "let " _ first:CoreExpText __ next:("," __ CoreExpText)* "}"
{
var asn=[first];
if (next) {
Expand Down Expand Up @@ -365,6 +362,10 @@ InvalidExpressionValue
= !("/template" _) chars:[^}]+
{return {type:"invalidexpression", code:chars.join(''), line:line, column:column}}

EOLBlock "End of line block"
= eol:EOL
{return {type:"eol", value:eol, line:line, column:column};}

// White spaces
// mandatory padding including line breaks
S "white space"
Expand All @@ -383,9 +384,6 @@ EOL "end of line"
/ "\u2028" // line separator
/ "\u2029" // paragraph separator

EOS "end of statement" //
= empty:(_ EOL _)

EOF "end of file"
= !.

Expand Down
58 changes: 52 additions & 6 deletions hsp/compiler/treebuilder/syntaxTree.js
Original file line number Diff line number Diff line change
Expand Up @@ -277,6 +277,43 @@ var SyntaxTree = klass({
return index;
},

/**
* Manages a End Of Line block
* @param {Array} blocks the full list of blocks.
* @param {Integer} index the index of the block to manage.
* @param {Array} out the output as an array of Node.
* @return {Integer} the index of the block where the function stopped or -1 if all blocks have been handled.
*/
__eol : function(index, blocks, out) {
return this.__text(index, blocks, out);
},

_skipWhitespaces: function(index, blocks, out) {
//Skips opening whitespace block and ending EOL block if the line is only made of blocks which don't generate DOM element or text
var toBeSkipped = true;
var nextIndex = index + 1;
while (nextIndex < blocks.length) {
var nextBlock = blocks[nextIndex];
if (!(nextBlock.type === "text" && nextBlock.value.match(/^(\s)+$/) ||
["if", "elseif", "else", "endif", "comment", "foreach", "endforeach", "eol", "log", "let"].indexOf(nextBlock.type) > -1)) {
toBeSkipped = false;
break;
}
if (nextBlock.type === "eol") {
break;
}
nextIndex++;
}
if (index + 1 < blocks.length && toBeSkipped) {
if (blocks[index + 1].type === "text") {
blocks[index + 1].toBeSkipped = true;
}
if (blocks[nextIndex].type === "eol") {
blocks[nextIndex].toBeSkipped = true;
}
}
},

/**
* Manages a text block: regroups adjacent text and expression blocks
* @param {Array} blocks the full list of blocks.
Expand All @@ -287,19 +324,28 @@ var SyntaxTree = klass({
__text : function (index, blocks, out) {
var length = blocks.length, buffer = [];

if (index === 0) {
this._skipWhitespaces(-1, blocks, out);
}

//Regroups adjacent text and expression blocks by looking at the next ones
var nextIndex = index, goAhead = (length > nextIndex), block;
while (goAhead) {
block = blocks[nextIndex];
if (block.type === "text") {
if (block.type === "text" || block.type === "eol") {
if (block.value !== "") {
try {
block.value = htmlEntitiesToUtf8(block.value);
buffer.push(block);
block.value = htmlEntitiesToUtf8(block.value);
if (typeof block.toBeSkipped === "undefined") {
buffer.push(block);
}
} catch (e) {
this._logError(e.message, block);
this._logError(e.message, block);
}
}
if (block.type === "eol") {
this._skipWhitespaces(nextIndex, blocks, out);
}
} else if (block.type === "expression") {
if (block.category === "jsexpression") {
// pre-process expression
Expand Down Expand Up @@ -327,15 +373,15 @@ var SyntaxTree = klass({

//Manages the adjacent text and expression blocks found
var node = null;
if (buffer.length === 1 && buffer[0].type === "text") {
if (buffer.length === 1 && (buffer[0].type === "text" || buffer[0].type === "eol")) {
// only one text block
node = new Node("text");
node.value = buffer[0].value;
} else if (buffer.length > 0) {
// if buffer is composed of only text expressions we concatenate them
var onlyText=true;
for (var i = 0; i < buffer.length; i++) {
if (buffer[i].type !== "text") {
if (buffer[i].type !== "text" && buffer[i].type !== "eol") {
onlyText = false;
break;
}
Expand Down

0 comments on commit a5ca809

Please sign in to comment.