Going to ColaScript!!!

Start work on ColaScript translator.
This commit is contained in:
Onoshko Dan 2014-04-14 10:35:26 +07:00
parent f5c09d0bbf
commit b5dd0a9774
6 changed files with 407 additions and 20 deletions

View File

@ -84,7 +84,7 @@ function DEFNODE(type, props, methods, base) {
return ctor;
};
var AST_Token = DEFNODE("Token", "type value line col pos endpos nlb comments_before file", {
var AST_Token = DEFNODE("Token", "type value line col pos endpos nlb comments_before file" /* ColaScropt + " endcol endline"*/, {
}, null);
var AST_Node = DEFNODE("Node", "start end", {

35
lib/index.html Normal file
View File

@ -0,0 +1,35 @@
<!DOCTYPE html>
<html>
<head>
<title>ColaScript Playground</title>
<script src="./utils.js"></script>
<script src="./ast.js"></script>
<script src="./parse.js"></script>
<script src="./transform.js"></script>
<script src="./scope.js"></script>
<script src="./output.js"></script>
<script src="./compress.js"></script>
<script src="./sourcemap.js"></script>
<script src="./mozilla-ast.js"></script>
<script src="./translate.js"></script>
<script src="./std.js"></script>
</head>
<body>
<textarea id="source" style="width: 100%; min-height: 300px"></textarea>
</body>
<script>
var sourceArea = document.getElementById("source"), source;
sourceArea.value = source = localStorage.source;
sourceArea.onkeyup = function(){
source = sourceArea.value;
localStorage.source = source;
};
function Translate(){
stream = OutputStream();
translate(parse(source)).print(stream);
return stream.toString();
}
</script>
</html>

View File

@ -45,9 +45,12 @@
"use strict";
var KEYWORDS = 'break case catch const continue debugger default delete do else finally for function if in instanceof new return switch throw try typeof var void while with';
KEYWORDS += ' is isnt class singleton injector'; // ColaScript
var KEYWORDS_ATOM = 'false null true';
var RESERVED_WORDS = 'abstract boolean byte char class double enum export extends final float goto implements import int interface long native package private protected public short static super synchronized this throws transient volatile yield'
KEYWORDS_ATOM += ' on yes off no'; // ColaScript
var RESERVED_WORDS = 'abstract boolean byte char double enum export extends final float goto implements import int interface long native package private protected public short static super synchronized this throws transient volatile yield'
+ " " + KEYWORDS_ATOM + " " + KEYWORDS;
// ColaScript: RESERVED_WORD -= class
var KEYWORDS_BEFORE_EXPRESSION = 'return new delete throw else case';
KEYWORDS = makePredicate(KEYWORDS);
@ -66,7 +69,7 @@ var OPERATORS = makePredicate([
"instanceof",
"typeof",
"new",
"void",
//"void", ColaScript
"delete",
"++",
"--",
@ -105,7 +108,14 @@ var OPERATORS = makePredicate([
"^=",
"&=",
"&&",
"||"
"||",
// ColaScript
"is",
"isnt",
"**",
"%%",
"?="
]);
var WHITESPACE_CHARS = makePredicate(characters(" \u00a0\n\r\t\f\u000b\u200b\u180e\u2000\u2001\u2002\u2003\u2004\u2005\u2006\u2007\u2008\u2009\u200a\u202f\u205f\u3000"));
@ -212,6 +222,14 @@ var EX_EOF = {};
function tokenizer($TEXT, filename, html5_comments) {
function StringInfo(){
this.quote = '';
this.inside = false;
this.after_at = false;
this.inside_at = false;
this.at_balance = 0;
}
var S = {
text : $TEXT.replace(/\r\n?|[\n\u2028\u2029]/g, "\n").replace(/\uFEFF/g, ''),
filename : filename,
@ -223,6 +241,12 @@ function tokenizer($TEXT, filename, html5_comments) {
tokcol : 0,
newline_before : false,
regex_allowed : false,
string : {
at : [ new StringInfo() ],
level : 0
},
in_string : false,
string_quote : '',
comments_before : []
};
@ -278,6 +302,14 @@ 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 = [];
@ -287,6 +319,7 @@ function tokenizer($TEXT, filename, html5_comments) {
}
}
S.newline_before = false;
console.log(ret);
return new AST_Token(ret);
};
@ -363,6 +396,20 @@ function tokenizer($TEXT, filename, html5_comments) {
var read_string = with_eof_error("Unterminated string constant", function(){
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 (peek() == '@') return token("string", "");
for (;;) {
var ch = next(true);
if (ch == "\\") {
@ -383,12 +430,39 @@ function tokenizer($TEXT, filename, html5_comments) {
if (octal_len > 0) ch = String.fromCharCode(parseInt(ch, 8));
else ch = read_escaped_char(true);
}
else if (ch == quote) break;
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--;
}
break;
}
ret += ch;
}
if (peek() == '@') break;
}
return token("string", ret);
});
function read_at(){
var at = next();
if (S.string.at[S.string.level].inside && is_identifier_char(peek())) {
S.string.at[S.string.level].after_at = true;
return token("punc", "@");
} else
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;
return token("punc", "@" + next());
}
parse_error('Unexpected character "@"');
}
function skip_line_comment(type) {
var regex_allowed = S.regex_allowed;
var i = find("\n"), ret;
@ -442,6 +516,7 @@ function tokenizer($TEXT, filename, html5_comments) {
hex = name.charCodeAt(0).toString(16).toUpperCase();
name = "\\u" + "0000".substr(hex.length) + hex + name.slice(1);
}
if (S.string.at[S.string.level].inside && S.string.at[S.string.level].after_at) S.string.at[S.string.level].after_at = false;
return name;
};
@ -464,7 +539,7 @@ function tokenizer($TEXT, filename, html5_comments) {
regexp += ch;
}
var mods = read_name();
return token("regexp", new RegExp(regexp, mods));
return token("regexp", /* new RegExp(regexp, mods) ColaScript */ "/" + regexp + "/" + mods);
});
function read_operator(prefix) {
@ -524,6 +599,16 @@ function tokenizer($TEXT, filename, html5_comments) {
function next_token(force_regexp) {
if (force_regexp != null)
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();
ch = peek();
if (ch == '@') return read_at();
return read_string();
}
skip_whitespace();
start_token();
if (html5_comments) {
@ -536,16 +621,26 @@ function tokenizer($TEXT, filename, html5_comments) {
return skip_line_comment("comment4");
}
}
var ch = peek();
ch = peek();
if (!ch) return token("eof");
var code = ch.charCodeAt(0);
switch (code) {
case 34: case 39: return read_string();
case 34: case 39: /* ColaScript */ case 96: return read_string();
case 46: return handle_dot();
case 47: return handle_slash();
}
if (is_digit(code)) return read_num();
if (PUNC_CHARS(ch)) return token("punc", next());
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].at_balance == 0) S.string.at[S.string.level].inside_at = false;
}
return token("punc", next());
}
if (ch == '@') return read_at();
if (OPERATOR_CHARS(ch)) return read_operator();
if (code == 92 || is_identifier_start(code)) return read_word();
parse_error("Unexpected character '" + ch + "'");
@ -564,7 +659,7 @@ function tokenizer($TEXT, filename, html5_comments) {
var UNARY_PREFIX = makePredicate([
"typeof",
"void",
//"void", ColaScript
"delete",
"--",
"++",
@ -576,7 +671,7 @@ var UNARY_PREFIX = makePredicate([
var UNARY_POSTFIX = makePredicate([ "--", "++" ]);
var ASSIGNMENT = makePredicate([ "=", "+=", "-=", "/=", "*=", "%=", ">>=", "<<=", ">>>=", "|=", "^=", "&=" ]);
var ASSIGNMENT = makePredicate([ "=", "+=", "-=", "/=", "*=", "%=", ">>=", "<<=", ">>>=", "|=", "^=", "&="/* ColaScript */, "?=" ]);
var PRECEDENCE = (function(a, ret){
for (var i = 0; i < a.length; ++i) {
@ -597,7 +692,9 @@ var PRECEDENCE = (function(a, ret){
["<", ">", "<=", ">=", "in", "instanceof"],
[">>", "<<", ">>>"],
["+", "-"],
["*", "/", "%"]
["*", "/", "%"],
// ColaScript
["is", "isnt", "**", "%%"]
],
{}
);
@ -615,14 +712,11 @@ function parse($TEXT, options) {
filename : null,
toplevel : null,
expression : false,
html5_comments : true,
html5_comments : true
});
var S = {
input : (typeof $TEXT == "string"
? tokenizer($TEXT, options.filename,
options.html5_comments)
: $TEXT),
input : (typeof $TEXT == "string" ? tokenizer($TEXT, options.filename, options.html5_comments) : $TEXT),
token : null,
prev : null,
peeked : null,
@ -687,6 +781,7 @@ function parse($TEXT, options) {
function expect(punc) { return expect_token("punc", punc); };
function can_insert_semicolon() {
return false; // ColaScript
return !options.strict && (
S.token.nlb || is("eof") || is("punc", "}")
);
@ -1123,10 +1218,10 @@ function parse($TEXT, options) {
break;
case "atom":
switch (tok.value) {
case "false":
case "false": /* ColaScript */ case "off": case "no":
ret = new AST_False({ start: tok, end: tok });
break;
case "true":
case "true": /* ColaScript */ case "on": case "yes":
ret = new AST_True({ start: tok, end: tok });
break;
case "null":

56
lib/std.js Normal file
View File

@ -0,0 +1,56 @@
/***********************************************************************
ColaScript standart library. Need for translation.
TrigenSoftware. dangreen.
Distributed under the BSD license:
Copyright 2012 (c) TrigenSoftware <danon0404@gmail.com>
Redistribution and use in source and binary forms, with or without
modification, are permitted provided that the following conditions
are met:
* Redistributions of source code must retain the above
copyright notice, this list of conditions and the following
disclaimer.
* Redistributions in binary form must reproduce the above
copyright notice, this list of conditions and the following
disclaimer in the documentation and/or other materials
provided with the distribution.
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDER AS IS AND ANY
EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER BE
LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY,
OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR
TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF
THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
SUCH DAMAGE.
***********************************************************************/
function $_cola_is(_object, _type){
return _object instanceof _type || _object.__proto__ == _type.prototype;
}
function $_cola_isnt(_object, _type){
return !(_object instanceof _type || _object.__proto__ == _type.prototype);
}
function $_cola_modulo(_a, _b){
return (_a % _b + +_b) % _b;
}
function $_cola_isset(_object){
return !(typeof _object === "undefined" || _object === null);
}
function $_cola_isntset(_object){
return (typeof _object === "undefined" || _object === null);
}

187
lib/translate.js Normal file
View File

@ -0,0 +1,187 @@
/***********************************************************************
AST-Tree translator, ColaScript -> JavaScript.
TrigenSoftware. dangreen.
Distributed under the BSD license:
Copyright 2012 (c) TrigenSoftware <danon0404@gmail.com>
Redistribution and use in source and binary forms, with or without
modification, are permitted provided that the following conditions
are met:
* Redistributions of source code must retain the above
copyright notice, this list of conditions and the following
disclaimer.
* Redistributions in binary form must reproduce the above
copyright notice, this list of conditions and the following
disclaimer in the documentation and/or other materials
provided with the distribution.
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDER AS IS AND ANY
EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER BE
LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY,
OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR
TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF
THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
SUCH DAMAGE.
***********************************************************************/
"use strict";
function translate(tree){
var queue = [];
tree.walk(new TreeWalker(function(node){
var newNode, props;
if(node instanceof AST_Binary && node.operator == '**'){
props = {
args : [node.left, node.right],
start : new AST_Token({ nlb : false, type : 'name', value : 'Math' }),
end : new AST_Token({ nlb : false, type : 'punc', value : ')' })
};
props.expression = new AST_Dot({
property : 'pow',
start : props.start,
end : new AST_Token({ nlb : false, type : 'name', value : 'pow' }),
expression : new AST_SymbolRef({ name : 'Math', start : props.start, end : props.start })
});
newNode = new AST_Call(props);
queue.push(function(){
ReplaceObject(node, newNode);
});
} else
if(node instanceof AST_Binary && node.operator == '%%'){
props = {
args : [node.left, node.right],
start : new AST_Token({ nlb : false, type : 'name', value : '$_cola_modulo' }),
end : new AST_Token({ nlb : false, type : 'punc', value : ')' })
};
props.expression = new AST_SymbolRef({
name : '$_cola_modulo',
start : props.start,
end : props.start
});
newNode = new AST_Call(props);
queue.push(function(){
ReplaceObject(node, newNode);
});
} else
if(node instanceof AST_Assign && node.operator == '?='){
var parent = this.parent();
if(parent instanceof AST_SimpleStatement){
node.operator = '=';
props = {
args : [node.left],
start : new AST_Token({ nlb : false, type : 'name', value : '$_cola_isntset' }),
end : new AST_Token({ nlb : false, type : 'punc', value : ')' })
};
props.expression = new AST_SymbolRef({
name : '$_cola_isntset',
start : props.start,
end : props.start
});
parent = CopyObject(parent);
parent.body = CopyObject(node);
newNode = new AST_If({
body : 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);
});
} else {
node.operator = '=';
props = {
args : [node.left],
start : new AST_Token({ nlb : false, type : 'name', value : '$_cola_isntset' }),
end : new AST_Token({ nlb : false, type : 'punc', value : ')' })
};
props.expression = new AST_SymbolRef({
name : '$_cola_isntset',
start : props.start,
end : props.start
});
newNode = new AST_Conditional({
start : new AST_Token({ nlb : false, type : 'punc', value : '(' }),
end : new AST_Token({ nlb : false, type : 'punc', value : ')' }),
condition : new AST_Call(props),
consequent : CopyObject(node),
alternative : node.left
});
queue.push(function(){
ReplaceObject(node, newNode);
});
}
} else
if(node instanceof AST_Binary && node.operator == 'is'){
props = {
args : [node.left, node.right],
start : new AST_Token({ nlb : false, type : 'name', value : '$_cola_is' }),
end : new AST_Token({ nlb : false, type : 'punc', value : ')' })
};
props.expression = new AST_SymbolRef({
name : '$_cola_is',
start : props.start,
end : props.start
});
newNode = new AST_Call(props);
queue.push(function(){
ReplaceObject(node, newNode);
});
} else
if(node instanceof AST_Binary && node.operator == 'isnt'){
props = {
args : [node.left, node.right],
start : new AST_Token({ nlb : false, type : 'name', value : '$_cola_isnt' }),
end : new AST_Token({ nlb : false, type : 'punc', value : ')' })
};
props.expression = new AST_SymbolRef({
name : '$_cola_isnt',
start : props.start,
end : props.start
});
newNode = new AST_Call(props);
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');
}
}));
queue.forEach(function(f){
f();
});
return tree;
}

View File

@ -300,3 +300,17 @@ Dictionary.prototype = {
return ret;
}
};
function CopyObject(obj){
var newObj = {};
for(var i in obj) if(obj.hasOwnProperty(i)) newObj[i] = obj[i];
newObj.__proto__ = obj.__proto__;
return newObj
}
function ReplaceObject(obj, newObj){
for(var i in obj) if(obj.hasOwnProperty(i)) delete obj[i];
for(var i in newObj) if(newObj.hasOwnProperty(i)) obj[i] = newObj[i];
obj.__proto__ = newObj.__proto__;
return obj;
}