CLIBJS
JS-like script engine implemented by C++.
Reference: quickjs
Features
- 64-bit support
- Parsing
jquery.js
andVue.js
in 1s. - Translate javascript file into Python Bytecode temporarily.
- Build VM.
- Closure.
- GC, cache unused object
- Binop(tested in
test_all.js
, TEST PASSED), BinComp, Unary - Y-combinator recursion.(tested in
test_6.js
) - Prototype, Attribute, Method
- New, Delete
- Rest parameters(
...
), Rest array elements([...[]]
), Rest object elements({...{}}
) - Base functions(Such as
Number
, etc) -
sys.exec(...)
supports relative path - Try, catch, finally(Nesting), Stacktrace
Environment
- CLion, MinGW on Win7x64
- C++14
TODO
- Lexer(scanned js files in
test
dir) - Parser(Use LALR1 Parser with backtrace, see clibparser, Grammar: antlr/grammars-v4/javascript)
- Syntax Tree(Auto-generation by LALR1 Parser)
- AST(On progress)
- IL Design(Use Python Bytecode temporarily)
- Gen(On progress)
- GC
- Runtime(On progress)
- Prototype(On progress)
- Interface
Prototype
- root(
debug_dump(builtin)
) - object(
hasOwnProperty
) - number
- boolean
- string
- function(
call
(builtin),apply
(builtin),bind
) - array(
slice
,concat
,push
,map
,filter
,reduce
,fill
,toString
)
Modules and API
- console(
log
(builtin),trace
(builtin)) - sys(
exec_file
(builtin))
Global Function
- setTimeout, setInterval, clearTimeout, clearInterval
Control Flow
- if, else
- while, do-while
- for, for-in
- switch
- try, catch, finally
Error
- Error
- ReferenceError
- SyntaxError
TEST
Scanned js files successfully, but spent more time BECAUSE IT IS BACKTRACE LALR1 PARSER, now HARDCORE FIXED.
- test/jquery.js
- test/jquery.min.js(
parsingnow it's ok)for-in
spent much time - test/vue.js
- test/vue.min.js
Can adjust grammar priority when facing shift-conflicts by setting cost value for sorting.
See clib::cjsparser::gen
function.
unit.adjust(&functionExpression, &anonymousFunction, e_shift, -1);
unit.adjust(&iterationStatement, &forInStatement, e_shift, -1);
unit.adjust(&iterationStatement, &forStatement, e_shift, 0, (void *) &pred_for);
unit.adjust(&inExpression, &inExpression, e_left_recursion, 0, (void *) &pred_in);
FIX: Add manual LL(n)
You can read ALL of the lexer words to decide whether you need to ALLOW/DELAY/REMOVE the PDA transition edge.
pda_coll_pred cjsparser::pred_for(const cjslexer *lexer, int idx) {
auto end = lexer->get_unit_size();
auto find_in = false;
for (auto i = idx + 1; i < end; i++) {
const auto &U = lexer->get_unit(i);
if (U.t == T_SEMI) {
break;
}
if (U.t == K_IN) {
find_in = true;
break;
}
}
return find_in ? p_DELAY : p_ALLOW;
}
pda_coll_pred cjsparser::pred_in(const cjslexer *lexer, int idx) {
auto find_for = false;
for (auto i = idx - 1; i >= 0; i--) {
const auto &U = lexer->get_unit(i);
if (U.t == T_LPARAN) {
if (i > 0 && lexer->get_unit(i - 1).t == K_FOR) {
find_for = true;
}
break;
}
if (U.t != ID) {
break;
}
}
return find_for ? p_REMOVE : p_ALLOW;
}
FIX: Removing backtrace saved before
If runs to var a = 1;
,it meets ;
, so removed backtrace array(Indicating that AST checking before was correct).
FIX: Support no line terminator
Such as:
return
a
Grammar: returnStatement = _K_RETURN + *(~_RULE_NO_LINE + expressionSequence) + *eos;
RULE_NO_LINE
eats no lexer words but looks ahead for NO LINE WHITESPACE.
AST and IL
Features:
- Unary, Sinop, Binop, Triop
- Member Index, Member Dot Function
- Array, Object
- Function, Lambda
Input: (test/test_2.js)
var a = b = c = 1;
var d = a + (b * c);
var e = 1 && 2 > 3;
a + b, a - b;
1 + ++--a--++;
a = b = c, a += b -= c--;
a.b.c, a.b.c++;
a[1]["1"], a[1]["1"]++;
a = [1, 2, [3 + 4], []];
b = {1: "a", 'b': {c: c}, [2]: 1};
a.b(1, 2).c(2, 1), a(3)(4), a[5](6), a.b.c(1);
(a > b ? a : b).c(), !++a ? b : c + 1;
function fun_a(b) {var a = 1; function c(){var b = 1;}}
var f = function (b) {var b = 1;}
function (c) {return function (){return c++ +g;}}
Output:
See Output2.txt, for parsing test/test_2.js
file.
Input: (test/test_3.js)
var a = 1;
b = 2;
function add(a,b) {return a+b;}
return add(a, b);
Output:
See Output3.txt, for parsing and running test/test_3.js
file.
Input: (test/test_4.js) -- Closure
var a = 1, b = 2;
return (a => b => a+b)(a)(b);
Output:
See Output4.txt, for parsing and running test/test_3.js
file.
Input: (test/test_7.js) -- Method and prototype
console.log("a".debug_dump());
console.log(0 .debug_dump());
console.log({}.debug_dump());
console.log(null.debug_dump());
console.log(undefined.debug_dump());
console.log(this.debug_dump());
console.log(true.debug_dump());
console.log(false.debug_dump());
console.log(console.debug_dump());
Output:
Str: a, Type: string, Ptr: 00fa3928
Str: 0, Type: number, Ptr: 015074e0
Str: [object Object], Type: object, Ptr: 0158f79c
Str: null, Type: object, Ptr: 01507374
Str: undefined, Type: undefined, Ptr: 015073a4
Str: [object Object], Type: object, Ptr: 0158f004
Str: true, Type: boolean, Ptr: 015073d4
Str: false, Type: boolean, Ptr: 01507404
Str: [object Object], Type: object, Ptr: 015077f4
undefined
Input: (test/test_8.js) -- New
function A(a, b) {
this.a = a;
this.b = b;
this.c = function () {
console.log(console.trace());
return this.a + ' ' + this.b;
};
}
var d = new A('123', 12.3);
console.log(d.c());
var obj = {0: 'a', 1: 'b', length: 2};
console.log([].slice.call(obj, 0).slice(1));
console.log([1].concat(1, [2], 3));
console.log([1, 2, 3, 4].map(x => x + 1).filter(x => x % 2 === 0));
console.log([1, 2, 3, 4].reduce((a, b) => a + b));
console.log([1, 2, 3, 4].reduce((a, b) => a + b, 1));
console.log([...[1, 2], ...[3, 4]].fill(5));
console.log.bind(null, 1, 2, 3)();
Array.prototype.concat.bind(0, 1, 2, 3)();
for (var i in {a: 1, ...{b: 2}}) console.log(i);
for (var i in [1,2]) console.log(i);
Output:
library loaded
3: (..\test\test_8.js:4:5) A.prototype.c
2: (..\test\test_8.js:1:1) <entry>
1: (<starter>) <entry>
123 12.3
[b]
[1, 1, 2, 3]
[2, 4]
10
11
[5, 5, 5, 5]
1 2 3
[0, 1, 2, 3]
a
b
0
1
Input: (test/test_9.js) -- setTimeout
(function () {
var i = 0;
console.log(i);
setTimeout(function a() {
if (++i > 4) return;
console.log(i);
setTimeout(a);
});
})();
Output:
0
undefined
1
2
3
4
Input: (test/test_10.js) -- Try/catch
Input: (test/test_all.js) -- All type binop
Output: (test/test_all_output.txt)
Grammar
LALR1 PDA Table
See PDA.txt.
Output
See Output.txt, for parsing test/test_1.js
file.