From b7d220ae12560b37503738abf0224d548c9c6f83 Mon Sep 17 00:00:00 2001 From: Onoshko Dan Date: Tue, 15 Apr 2014 17:52:01 +0700 Subject: [PATCH] First step is done. index.html - playground --- lib/ast.js | 7 +++ lib/index.html | 78 ++++++++++++++++++++++-- lib/parse.js | 153 ++++++++++++++++++++++++++++++++++------------- lib/translate.js | 37 +++++++++--- 4 files changed, 220 insertions(+), 55 deletions(-) diff --git a/lib/ast.js b/lib/ast.js index 513f239e..beff1390 100644 --- a/lib/ast.js +++ b/lib/ast.js @@ -853,6 +853,13 @@ var AST_String = DEFNODE("String", "value", { } }, AST_Constant); +var AST_StringTemplate = DEFNODE("StringTemplate", null, { + $documentation: "A string template", + $propdoc: { + body: "[AST_Statement*] the contents of this string template" + } +}, AST_Block); + var AST_Number = DEFNODE("Number", "value", { $documentation: "A number literal", $propdoc: { diff --git a/lib/index.html b/lib/index.html index e627c725..80cda37f 100644 --- a/lib/index.html +++ b/lib/index.html @@ -13,23 +13,91 @@ + - + + + +
diff --git a/lib/parse.js b/lib/parse.js index 6b413371..3f6a9665 100644 --- a/lib/parse.js +++ b/lib/parse.js @@ -227,7 +227,8 @@ function tokenizer($TEXT, filename, html5_comments) { this.inside = false; this.after_at = false; this.inside_at = false; - this.at_balance = 0; + this.inside_braces = false; + this.balance = 0; } var S = { @@ -250,7 +251,7 @@ function tokenizer($TEXT, filename, html5_comments) { comments_before : [] }; - function peek() { return S.text.charAt(S.pos); }; + function peek(offset) { return S.text.charAt(S.pos + (offset ? offset : 0)); }; function next(signal_eof, in_string) { var ch = S.text.charAt(S.pos++); @@ -302,14 +303,6 @@ function tokenizer($TEXT, filename, html5_comments) { nlb : S.newline_before, file : filename }; - //if (type == 'string') { - //ret.leftoffset = ret.value.match(/[\n\r][ \t]*$/); - //ret.value = ret.value.replace(/^[ \t]*[\n\r][ \t]*/,''); - //if(offstr[0]){ - // offstr = offstr[0]; - // while(ret.value.indexOf(offstr) != -1) ret.value = ret.value.replace(offstr, '\\n'); - //} - //} if (!is_comment) { ret.comments_before = S.comments_before; S.comments_before = []; @@ -319,7 +312,6 @@ function tokenizer($TEXT, filename, html5_comments) { } } S.newline_before = false; - console.log(ret); return new AST_Token(ret); }; @@ -394,25 +386,39 @@ function tokenizer($TEXT, filename, html5_comments) { return num; }; - var read_string = with_eof_error("Unterminated string constant", function(){ + var read_string = with_eof_error("Unterminated string constant", function(raw){ var quote = next(), ret = ""; - - if(S.string.at[S.string.level].inside && S.string.at[S.string.level].inside_at){ - S.string.level++; - S.string.at[S.string.level] = new StringInfo(); - } - S.string.at[S.string.level].inside = true; - if(quote != '"' && quote != "'" && quote != '`'){ - ret = quote; - quote = S.string.at[S.string.level].quote; - } else S.string.at[S.string.level].quote = quote; + if (!raw) { + if (S.string.at[S.string.level].inside && (S.string.at[S.string.level].inside_at || S.string.at[S.string.level].inside_braces)) { + S.string.level++; + S.string.at[S.string.level] = new StringInfo(); + } + S.string.at[S.string.level].inside = true; + + if (quote != '"' && quote != "'" && quote != '`') { + ret = quote; + quote = S.string.at[S.string.level].quote; + } else - if (peek() == '@') return token("string", ""); + if (quote == S.string.at[S.string.level].quote){ + S.string.at[S.string.level].inside = false; + S.string.at[S.string.level].quote = ''; + if(S.string.level != 0){ + delete S.string.at[S.string.level]; + S.string.level--; + } + return next_token(); + } else + + S.string.at[S.string.level].quote = quote; + + if (peek() == '@' || peek() == '{' && peek(1) == '{') return token("string", ""); + } for (;;) { var ch = next(true); - if (ch == "\\") { + if (!raw && ch == "\\") { // read OctalEscapeSequence (XXX: deprecated if "strict mode") // https://github.com/mishoo/UglifyJS/issues/178 var octal_len = 0, first = null; @@ -431,16 +437,18 @@ function tokenizer($TEXT, filename, html5_comments) { else ch = read_escaped_char(true); } else if (ch == quote) { - S.string.at[S.string.level].inside = false; - S.string.at[S.string.level].quote = ''; - if(S.string.level != 0){ - delete S.string.at[S.string.level]; - S.string.level--; + if(!raw){ + S.string.at[S.string.level].inside = false; + S.string.at[S.string.level].quote = ''; + if(S.string.level != 0){ + delete S.string.at[S.string.level]; + S.string.level--; + } } break; } ret += ch; - if (peek() == '@') break; + if (!raw && (peek() == '@' || peek() == '{' && peek(1) == '{')) break; } return token("string", ret); @@ -456,12 +464,19 @@ function tokenizer($TEXT, filename, html5_comments) { if (S.string.at[S.string.level].inside && peek() == '{') { S.string.at[S.string.level].inside_at = true; - S.string.at[S.string.level].at_balance = 1; + S.string.at[S.string.level].balance = 1; return token("punc", "@" + next()); } parse_error('Unexpected character "@"'); } + + function read_braces(){ + next(), next(); + S.string.at[S.string.level].inside_braces = true; + S.string.at[S.string.level].balance = 1; + return token("punc", "{{"); + } function skip_line_comment(type) { var regex_allowed = S.regex_allowed; @@ -601,14 +616,13 @@ function tokenizer($TEXT, filename, html5_comments) { return read_regexp(force_regexp); var ch; - - if (S.string.at[S.string.level].inside && !S.string.at[S.string.level].after_at && !S.string.at[S.string.level].inside_at) { - start_token(); + if (S.string.at[S.string.level].inside && !S.string.at[S.string.level].after_at && !S.string.at[S.string.level].inside_at && !S.string.at[S.string.level].inside_braces) { ch = peek(); if (ch == '@') return read_at(); + if (ch == '{' && peek(1) == '{') return read_braces(); return read_string(); } - + skip_whitespace(); start_token(); if (html5_comments) { @@ -631,17 +645,23 @@ function tokenizer($TEXT, filename, html5_comments) { } if (is_digit(code)) return read_num(); if (PUNC_CHARS(ch)){ - if (S.string.at[S.string.level].inside && S.string.at[S.string.level].inside_at) { - if (ch == '{') S.string.at[S.string.level].at_balance++; - else if (ch == '}') S.string.at[S.string.level].at_balance--; + if (S.string.at[S.string.level].inside && (S.string.at[S.string.level].inside_at || S.string.at[S.string.level].inside_braces)) { + if (ch == '{') S.string.at[S.string.level].balance++; + else if (ch == '}') S.string.at[S.string.level].balance--; - if (S.string.at[S.string.level].at_balance == 0) S.string.at[S.string.level].inside_at = false; + if (S.string.at[S.string.level].balance == 0) + if (S.string.at[S.string.level].inside_at) S.string.at[S.string.level].inside_at = false; + else { + S.string.at[S.string.level].inside_braces = false; + return next(), next(), token("punc", "}}"); + } } return token("punc", next()); } if (ch == '@') return read_at(); if (OPERATOR_CHARS(ch)) return read_operator(); + if (ch == 'r' && (peek(1) == '"' || peek(1) == "'" || peek(1) == '`')) return next(), read_string(true); if (code == 92 || is_identifier_start(code)) return read_word(); parse_error("Unexpected character '" + ch + "'"); }; @@ -1200,6 +1220,57 @@ function parse($TEXT, options) { }), true); }; + function string_template_(start_token) { + var body = [new AST_String({ start: start_token, end: start_token, value: start_token.value })]; + next(); + + //if (!is('punc', '@') && !is('punc', '@{')) return body[0]; + while (is('punc', '@') || is('punc', '@{') || is('punc', '{{') || is('string', null)) + if (is('string', null)) { + body.push(new AST_String({ start: S.token, end: S.token, value: S.token.value })); + next(); + } else + + if (is('punc', '@')) { + next(); + body.push(_make_symbol(AST_SymbolRef)); + next(); + } else + + if (is('punc', '@{')) { + next(); + body.push(expression(true)); + expect('}'); + } else + + if (is('punc', '{{')) { + next(); + body.push(expression(true)); + expect('}}'); + } + + var last = body[body.length - 1]; + body[0].value = body[0].value.replace(/^[ \t]*[\n\r]/,''); + if (last instanceof AST_String) { + var offstr = last.value.match(/[\n\r][ \t]*$/); + if(offstr){ + offstr = offstr[0]; + for(var i in body) if(body[i] instanceof AST_String){ + body[i].value = body[i].value.replace(new RegExp(offstr, 'g'), '\n'); + body[i].value = body[i].value.replace(/\n/g, '\n'); + } + last.value = last.value.replace(/[\n\r]$/,''); + } + } + + if (body.length == 1) return body[0]; + return new AST_StringTemplate({ + start : start_token, + end : prev(), + body : body + }); + } + function as_atom_node() { var tok = S.token, ret; switch (tok.type) { @@ -1211,8 +1282,8 @@ function parse($TEXT, options) { ret = new AST_Number({ start: tok, end: tok, value: tok.value }); break; case "string": - ret = new AST_String({ start: tok, end: tok, value: tok.value }); - break; + return string_template_(tok);//new AST_String({ start: tok, end: tok, value: tok.value }); + //break; case "regexp": ret = new AST_RegExp({ start: tok, end: tok, value: tok.value }); break; diff --git a/lib/translate.js b/lib/translate.js index 10a7b35f..c417daae 100644 --- a/lib/translate.js +++ b/lib/translate.js @@ -92,22 +92,19 @@ function translate(tree){ end : new AST_Token({ nlb : false, type : 'punc', value : ')' }) }; props.expression = new AST_SymbolRef({ - name : '$_cola_isntset', + name : '$_cola_isntset', start : props.start, - end : props.start + end : props.start }); - parent = CopyObject(parent); - parent.body = CopyObject(node); - newNode = new AST_If({ - body : parent, + body : CopyObject(parent), start : new AST_Token({ nlb : false, type : 'keyword', value : 'if' }), end : new AST_Token({ nlb : false, type : 'punc', value : ';' }), condition : new AST_Call(props) }); queue.push(function(){ - ReplaceObject(node, newNode); + ReplaceObject(parent, newNode); }); } else { node.operator = '='; @@ -118,9 +115,9 @@ function translate(tree){ end : new AST_Token({ nlb : false, type : 'punc', value : ')' }) }; props.expression = new AST_SymbolRef({ - name : '$_cola_isntset', + name : '$_cola_isntset', start : props.start, - end : props.start + end : props.start }); newNode = new AST_Conditional({ @@ -173,6 +170,28 @@ function translate(tree){ }); } else + if(node instanceof AST_StringTemplate){ + newNode = new AST_Binary({ + operator : '+', + left : node.body[0], + right : node.body[1], + start : node.body[0].start, + end : node.body[1].end + }); + for(var i = 2; i < node.body.length; i++) + newNode = new AST_Binary({ + operator : '+', + left : newNode, + right : node.body[i], + start : newNode.start, + end : node.body[i].end + }); + + queue.push(function(){ + ReplaceObject(node, newNode); + }); + } else + if(node instanceof AST_RegExp && (node.value.indexOf('\n') != -1 || /\/[\w]*x[\w]*$/.test(node.value))){ node.value = node.value.replace(/[\r\n\s]/g,'').replace(/(\/[\w]*)x([\w]*$)/, '$1$2'); }