First step is done.

index.html - playground
This commit is contained in:
Onoshko Dan 2014-04-15 17:52:01 +07:00
parent b5dd0a9774
commit b7d220ae12
4 changed files with 220 additions and 55 deletions

View File

@ -853,6 +853,13 @@ var AST_String = DEFNODE("String", "value", {
} }
}, AST_Constant); }, 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", { var AST_Number = DEFNODE("Number", "value", {
$documentation: "A number literal", $documentation: "A number literal",
$propdoc: { $propdoc: {

View File

@ -13,23 +13,91 @@
<script src="./mozilla-ast.js"></script> <script src="./mozilla-ast.js"></script>
<script src="./translate.js"></script> <script src="./translate.js"></script>
<script src="./std.js"></script> <script src="./std.js"></script>
<style>
body {
padding: 0;
margin: 0;
line-height: 100%;
position: fixed;
top: 0;
left: 0;
width: 100%;
height: 100%;
}
textarea {
box-sizing: border-box;
width: 33.333%;
height: 100%;
font-size: 12px;
float: left;
font-family: "Andale Mono";
}
#controls {
position: fixed;
bottom: 0;
left: 0;
}
</style>
</head> </head>
<body> <body>
<textarea id="source" style="width: 100%; min-height: 300px"></textarea> <textarea id="source" onkeyup="compile()"></textarea>
<textarea id="translation"></textarea>
<textarea id="result"></textarea>
<div id="controls"><button id="exec" onclick="exec()">Execute</button></div>
</body> </body>
<script> <script>
var sourceArea = document.getElementById("source"), source; var sourceArea = document.getElementById("source"),
translationArea = document.getElementById("translation"),
resultArea = document.getElementById("result"),
source;
sourceArea.value = source = localStorage.source; sourceArea.value = source = localStorage.source;
sourceArea.onkeyup = function(){ function compile(){
source = sourceArea.value; source = sourceArea.value;
localStorage.source = source; localStorage.source = source;
};
stream = OutputStream({ beautify : true });
compressor = Compressor();
try {
// 1. compile
ast = parse(source);
ast = translate(ast);
ast.print(stream);
translationArea.value = stream.toString();
// 2. compress
ast.figure_out_scope();
ast = ast.transform(compressor);
// 3. mangle
ast.figure_out_scope();
ast.compute_char_frequency();
ast.mangle_names({ sort : true, toplevel : true });
stream = OutputStream();
ast.print(stream);
resultArea.value = stream.toString();
} catch(e){
translationArea.value = '';
resultArea.value = '';
}
}
function exec(){
eval(resultArea.value)
}
function Translate(){ function Translate(){
stream = OutputStream(); stream = OutputStream({ beautify : true });
translate(parse(source)).print(stream); translate(parse(source)).print(stream);
return stream.toString(); return stream.toString();
} }
compile();
</script> </script>
</html> </html>

View File

@ -227,7 +227,8 @@ function tokenizer($TEXT, filename, html5_comments) {
this.inside = false; this.inside = false;
this.after_at = false; this.after_at = false;
this.inside_at = false; this.inside_at = false;
this.at_balance = 0; this.inside_braces = false;
this.balance = 0;
} }
var S = { var S = {
@ -250,7 +251,7 @@ function tokenizer($TEXT, filename, html5_comments) {
comments_before : [] 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) { function next(signal_eof, in_string) {
var ch = S.text.charAt(S.pos++); var ch = S.text.charAt(S.pos++);
@ -302,14 +303,6 @@ function tokenizer($TEXT, filename, html5_comments) {
nlb : S.newline_before, nlb : S.newline_before,
file : filename 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) { if (!is_comment) {
ret.comments_before = S.comments_before; ret.comments_before = S.comments_before;
S.comments_before = []; S.comments_before = [];
@ -319,7 +312,6 @@ function tokenizer($TEXT, filename, html5_comments) {
} }
} }
S.newline_before = false; S.newline_before = false;
console.log(ret);
return new AST_Token(ret); return new AST_Token(ret);
}; };
@ -394,25 +386,39 @@ function tokenizer($TEXT, filename, html5_comments) {
return num; 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 = ""; var quote = next(), ret = "";
if(S.string.at[S.string.level].inside && S.string.at[S.string.level].inside_at){ 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.level++;
S.string.at[S.string.level] = new StringInfo(); S.string.at[S.string.level] = new StringInfo();
} }
S.string.at[S.string.level].inside = true; S.string.at[S.string.level].inside = true;
if(quote != '"' && quote != "'" && quote != '`'){ if (quote != '"' && quote != "'" && quote != '`') {
ret = quote; ret = quote;
quote = S.string.at[S.string.level].quote; quote = S.string.at[S.string.level].quote;
} else S.string.at[S.string.level].quote = 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 (;;) { for (;;) {
var ch = next(true); var ch = next(true);
if (ch == "\\") { if (!raw && ch == "\\") {
// read OctalEscapeSequence (XXX: deprecated if "strict mode") // read OctalEscapeSequence (XXX: deprecated if "strict mode")
// https://github.com/mishoo/UglifyJS/issues/178 // https://github.com/mishoo/UglifyJS/issues/178
var octal_len = 0, first = null; var octal_len = 0, first = null;
@ -431,16 +437,18 @@ function tokenizer($TEXT, filename, html5_comments) {
else ch = read_escaped_char(true); else ch = read_escaped_char(true);
} }
else if (ch == quote) { else if (ch == quote) {
if(!raw){
S.string.at[S.string.level].inside = false; S.string.at[S.string.level].inside = false;
S.string.at[S.string.level].quote = ''; S.string.at[S.string.level].quote = '';
if(S.string.level != 0){ if(S.string.level != 0){
delete S.string.at[S.string.level]; delete S.string.at[S.string.level];
S.string.level--; S.string.level--;
} }
}
break; break;
} }
ret += ch; ret += ch;
if (peek() == '@') break; if (!raw && (peek() == '@' || peek() == '{' && peek(1) == '{')) break;
} }
return token("string", ret); return token("string", ret);
@ -456,13 +464,20 @@ function tokenizer($TEXT, filename, html5_comments) {
if (S.string.at[S.string.level].inside && peek() == '{') { if (S.string.at[S.string.level].inside && peek() == '{') {
S.string.at[S.string.level].inside_at = true; 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()); return token("punc", "@" + next());
} }
parse_error('Unexpected character "@"'); 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) { function skip_line_comment(type) {
var regex_allowed = S.regex_allowed; var regex_allowed = S.regex_allowed;
var i = find("\n"), ret; var i = find("\n"), ret;
@ -601,11 +616,10 @@ function tokenizer($TEXT, filename, html5_comments) {
return read_regexp(force_regexp); return read_regexp(force_regexp);
var ch; 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 && !S.string.at[S.string.level].inside_braces) {
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();
ch = peek(); ch = peek();
if (ch == '@') return read_at(); if (ch == '@') return read_at();
if (ch == '{' && peek(1) == '{') return read_braces();
return read_string(); return read_string();
} }
@ -631,17 +645,23 @@ function tokenizer($TEXT, filename, html5_comments) {
} }
if (is_digit(code)) return read_num(); if (is_digit(code)) return read_num();
if (PUNC_CHARS(ch)){ if (PUNC_CHARS(ch)){
if (S.string.at[S.string.level].inside && S.string.at[S.string.level].inside_at) { 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].at_balance++; if (ch == '{') S.string.at[S.string.level].balance++;
else if (ch == '}') S.string.at[S.string.level].at_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()); return token("punc", next());
} }
if (ch == '@') return read_at(); if (ch == '@') return read_at();
if (OPERATOR_CHARS(ch)) return read_operator(); 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(); if (code == 92 || is_identifier_start(code)) return read_word();
parse_error("Unexpected character '" + ch + "'"); parse_error("Unexpected character '" + ch + "'");
}; };
@ -1200,6 +1220,57 @@ function parse($TEXT, options) {
}), true); }), 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() { function as_atom_node() {
var tok = S.token, ret; var tok = S.token, ret;
switch (tok.type) { switch (tok.type) {
@ -1211,8 +1282,8 @@ function parse($TEXT, options) {
ret = new AST_Number({ start: tok, end: tok, value: tok.value }); ret = new AST_Number({ start: tok, end: tok, value: tok.value });
break; break;
case "string": case "string":
ret = new AST_String({ start: tok, end: tok, value: tok.value }); return string_template_(tok);//new AST_String({ start: tok, end: tok, value: tok.value });
break; //break;
case "regexp": case "regexp":
ret = new AST_RegExp({ start: tok, end: tok, value: tok.value }); ret = new AST_RegExp({ start: tok, end: tok, value: tok.value });
break; break;

View File

@ -97,17 +97,14 @@ function translate(tree){
end : props.start end : props.start
}); });
parent = CopyObject(parent);
parent.body = CopyObject(node);
newNode = new AST_If({ newNode = new AST_If({
body : parent, body : CopyObject(parent),
start : new AST_Token({ nlb : false, type : 'keyword', value : 'if' }), start : new AST_Token({ nlb : false, type : 'keyword', value : 'if' }),
end : new AST_Token({ nlb : false, type : 'punc', value : ';' }), end : new AST_Token({ nlb : false, type : 'punc', value : ';' }),
condition : new AST_Call(props) condition : new AST_Call(props)
}); });
queue.push(function(){ queue.push(function(){
ReplaceObject(node, newNode); ReplaceObject(parent, newNode);
}); });
} else { } else {
node.operator = '='; node.operator = '=';
@ -173,6 +170,28 @@ function translate(tree){
}); });
} else } 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))){ 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'); node.value = node.value.replace(/[\r\n\s]/g,'').replace(/(\/[\w]*)x([\w]*$)/, '$1$2');
} }