nv-js-parse
v1.4.25
Published
nv-js-parse ============ - nv-js-parse - wrap of @babel/parser - add some display/show/path/traverse methods , make it easy for manual-edit ast - nv-js-parse is slow, coz it insert some functions to original-babel-parser - and rebuild the path/traverse o
Downloads
48
Readme
nv-js-parse
- nv-js-parse
- wrap of @babel/parser
- add some display/show/path/traverse methods , make it easy for manual-edit ast
- nv-js-parse is slow, coz it insert some functions to original-babel-parser
- and rebuild the path/traverse of babel
- its for build cli-code-tools,NOT for creat-runtime
install
- npm install nv-js-parse
usage
basic
const {tree,parse,unparse,pprint_ast} = require('nv-js-parse')
var code = `class T {
#a = 1999
get() {
return this.#a
}
}`
> var t = tree(code)
> t
[program <Program> {"sourceType":"module","interpreter":null}] {}
astml is a simple syntax for manually writing ast
shape: %key <%type:String> %attr:Object %NL %listKey[%index:Int] <%type:String> %attr:Object %NL
%type must be ast-node-type of estree/ts/flow/jsx %key AND %listKey must be visitor/list-visitor of %type
> t.show_astml()
program <Program> {"sourceType":"module","interpreter":null}
body[0] <ClassDeclaration> {}
id <Identifier> {"name":"T"}
body <ClassBody> {}
body[0] <ClassPrivateProperty> {"static":false}
key <PrivateName> {}
id <Identifier> {"name":"a"}
value <NumericLiteral> {"value":1999}
body[1] <ClassMethod> {"generator":false,"async":false,"static":false,"computed":false,"kind":"method"}
key <Identifier> {"name":"get"}
body <BlockStatement> {}
body[0] <ReturnStatement> {}
argument <MemberExpression> {"computed":false}
object <ThisExpression> {}
property <PrivateName> {}
id <Identifier> {"name":"a"}
> t.show_code()
class T {
#a = 1999;
get() {
return this.#a;
}
}
> t.get_all_ast_bracket_paths()
[
'',
'["body"]["0"]',
'["body"]["0"]["id"]',
'["body"]["0"]["body"]',
'["body"]["0"]["body"]["body"]["0"]',
'["body"]["0"]["body"]["body"]["0"]["key"]',
'["body"]["0"]["body"]["body"]["0"]["key"]["id"]',
'["body"]["0"]["body"]["body"]["0"]["value"]',
'["body"]["0"]["body"]["body"]["1"]',
'["body"]["0"]["body"]["body"]["1"]["key"]',
'["body"]["0"]["body"]["body"]["1"]["body"]',
'["body"]["0"]["body"]["body"]["1"]["body"]["body"]["0"]',
'["body"]["0"]["body"]["body"]["1"]["body"]["body"]["0"]["argument"]',
'["body"]["0"]["body"]["body"]["1"]["body"]["body"]["0"]["argument"]["object"]',
'["body"]["0"]["body"]["body"]["1"]["body"]["body"]["0"]["argument"]["property"]',
'["body"]["0"]["body"]["body"]["1"]["body"]["body"]["0"]["argument"]["property"]["id"]'
]
> t.ast["body"]["0"]["body"]["body"]["1"]["body"]["body"]["0"]["argument"]["property"]["id"]
Node {
type: 'Identifier',
start: 67,
end: 68,
loc: SourceLocation {
start: Position { line: 4, column: 23 },
end: Position { line: 4, column: 24 },
filename: undefined,
identifierName: 'a'
},
name: 'a'
}
const2let
for debug using
actually ,const is a runtime-check feature
/* > function tst() { ... const a; const a; ^ Uncaught SyntaxError: Missing initializer in const declaration > a=100 100 > } } ^ */ cd =`function tst() { const a; a=100 }` > cd =`function tst() { const a; a=100 }` 'function tst() {\n const a;\n a=100\n}' > > const2let(cd) 'function tst() {\n let a;\n a = 100;\n}' >
hoist_func_decl
stage0, func declaration hoist
not the final ,only hoist to first ance-block, not to function block
var cd =`function A () { let x = 0 console.log(tst()) { console.log(tst()) function tst() {return(x+1)} } function tst() {return(x)} }` s0 = split_var_decl(cd) s1 = split_var_declor(s0) s2 = hoist_var(s1) s3 = hoist_func_decl(s2) > console.log(s3) function A() { function tst() { return x; } let x; x = 0; console.log(tst()); { function tst() { return x + 1; } console.log(tst()); } }
fdecl2fexpr
must be used after hoist_func_decl
var cd = ` function tst(u,v) { let a=100; function inner() { var x; function tst() {} } } ` > console.log(fdecl2fexpr(cd)) var tst = function tst(u, v) { let a = 100; var inner = function inner() { var x; var tst = function tst() {}; }; };
cdecl2cexpr
class declaration to class expression
var cd = ` class A { method() { class B {} class C {} function tst() { class D {} } } } ` > console.log(cdecl2cexpr(cd)) let A = class A { method() { let B = class B {}; let C = class C {}; function tst() { let D = class D {}; } } };
split_var_decl
- for debug AND hoist using
const {split_var_decl} = require("nv-js-parse")
var code = `
function tst() {
let a,b=3,c;
return(a+b+c)
}`
> console.log(split_var_decl(code))
function tst() {
let a;
let b = 3;
let c;
return a + b + c;
}
var code = `
function tst() {
let x,[{a,b},c]=[{a:666,b:777},3],y;
return(a+b+c)
}`
> console.log(split_var_decl(code))
function tst() {
let x;
let [{
a,
b
}, c] = [{
a: 666,
b: 777
}, 3];
let y;
return a + b + c;
}
> tst()
1446
>
> var cd = `function tst() {
... var a,b=8;
... for(let i=0,j;i<3;i++,j++) {let {x,y}={}}
... var c = a+b
... }`
> console.log(split_var_decl(cd))
function tst() {
var a;
var b = 8;
{ //----------------
let i = 0;
let j;
for (; i < 3; i++, j++) {
let {
x,
y
} = {};
}
} //------------------special handle of for AND forin AND forof
var c = a + b;
}
split_var_declor
- for debug AND hoist using
- must be used after split_var_decl
var cd = `
function tst(u,v) {
let x,e,ee;
let [{a,b=e='EEEEE',...f},c=333,d,...g]=[{a:666,b:777},3];
let [{aa,bb=ee='1111111',...ff},cc=333,dd,...gg]=[{aa:666,bb:777},3];
let [] = [],[,] =[],[...args] = [1,2],{...D} = {1:2,3:4},[A] = [1,2],{B} = {B:2,3:4},[C=10] = [],{E=999} = {};
return([u,v,x,a,b,e,f,c,d,g,aa,bb,ee,ff,cc,dd,gg,args,D,A,B,C,E])
}
`
/*
> eval(`(${cd})(100,500)`)
[
100, 500,
undefined, 666,
777, undefined,
{}, 3,
undefined, [],
666, 777,
undefined, {},
3, undefined,
[], [ 1, 2 ],
{ '1': 2, '3': 4 }, 1,
2, 10,
999
]
>
*/
s0 = split_var_decl(cd)
console.log(s0)
/*
function tst(u, v) {
let x;
let e;
let ee;
let [{
a,
b = e = 'EEEEE',
...f
}, c = 333, d, ...g] = [{
a: 666,
b: 777
}, 3];
let [{
aa,
bb = ee = '1111111',
...ff
}, cc = 333, dd, ...gg] = [{
aa: 666,
bb: 777
}, 3];
let [] = [];
let [,] = [];
let [...args] = [1, 2];
let { ...D
} = {
1: 2,
3: 4
};
let [A] = [1, 2];
let {
B
} = {
B: 2,
3: 4
};
let [C = 10] = [];
let {
E = 999
} = {};
return [u, v, x, a, b, e, f, c, d, g, aa, bb, ee, ff, cc, dd, gg, args, D, A, B, C, E];
}
*/
/*
> eval(`(${s0})(100,500)`)
[
100, 500,
undefined, 666,
777, undefined,
{}, 3,
undefined, [],
666, 777,
undefined, {},
3, undefined,
[], [ 1, 2 ],
{ '1': 2, '3': 4 }, 1,
2, 10,
999
]
>
*/
s1 = split_var_declor(s0)
console.log(s1)
/*
function tst(u, v) {
let x;
let e;
let ee;
let a;
let b;
let f;
let c;
let d;
let g;
[{
a,
b = e = 'EEEEE',
...f
}, c = 333, d, ...g] = [{
a: 666,
b: 777
}, 3];
let aa;
let bb;
let ff;
let cc;
let dd;
let gg;
[{
aa,
bb = ee = '1111111',
...ff
}, cc = 333, dd, ...gg] = [{
aa: 666,
bb: 777
}, 3];
[] = [];
[,] = [];
let args;
[...args] = [1, 2];
let D;
({ ...D
} = {
1: 2,
3: 4
});
let A;
[A] = [1, 2];
let B;
({
B
} = {
B: 2,
3: 4
});
let C;
[C = 10] = [];
let E;
({
E = 999
} = {});
return [u, v, x, a, b, e, f, c, d, g, aa, bb, ee, ff, cc, dd, gg, args, D, A, B, C, E];
}
*/
/*
> eval(`(${s1})(100,500)`)
[
100, 500,
undefined, 666,
777, undefined,
{}, 3,
undefined, [],
666, 777,
undefined, {},
3, undefined,
[], [ 1, 2 ],
{ '1': 2, '3': 4 }, 1,
2, 10,
999
]
>
*/
var cd = `function tst() {for(let i=1,[x,y]=[888];i<3;i++) {}}`
s0 = split_var_decl(cd)
s1 = split_var_declor(s0)
console.log(s1)
/*
function tst() {
{
let i;
i = 1;
let x;
let y;
[x, y] = [888];
for (; i < 3; i++) {}
}
}
*/
var cd = `function tst() {for(let i;i<3;i++) {}}`
s0 = split_var_decl(cd)
s1 = split_var_declor(s0)
console.log(s1)
/*
function tst() {
{
let i;
for (; i < 3; i++) {}
}
}
*/
var cd = `
async function tst() {
let arr =[];
let d = {};
let g = (async function *(){})();
for(let i=1,y;i<3;i++) {}
for(let ele of arr) {}
for(let k in d) {}
for await (let each of g) {}
}
`
s0 = split_var_decl(cd)
s1 = split_var_declor(s0)
console.log(s1)
/*
async function tst() {
let arr;
arr = [];
let d;
d = {};
let g;
g = async function* () {}();
{
let i;
i = 1;
let y;
for (; i < 3; i++) {}
}
{
let ele;
for (ele of arr) {}
}
{
let k;
for (k in d) {}
}
{
let each;
for await (each of g) {}
}
}
*/
hoist_var
must be used after split_var_decl -> split_var_declor
const { split_var_decl, split_var_declor, tree, hoist_var, } = require("nv-js-parse") var cd = ` function tst(u,v) { let x; { var y=888; { var [z,{w,r}] = [1,{w:2,r:3}]; let A; } } } ` s0 = split_var_decl(cd) s1 = split_var_declor(s0) s2 = hoist_var(s1) > console.log(s2) function tst(u, v) { let x; var y; var z; var w; var r; { y = 888; { [z, { w, r }] = [1, { w: 2, r: 3 }]; let A; } } }
rare special cases
> var cd =`
... function tst() {
... var a;
... {
..... var a;
..... a = 13;
..... }
... }`
>
> hoist_var(cd)
'function tst() {\n var a;\n var a;\n {\n a = 13;\n }\n}'
> console.log(hoist_var(cd))
function tst() {
var a;
var a;
{
a = 13;
}
}
var cd =`
var tst;
tst = function tst() {
var a;
{
var a;
}
};
`
> var cd =`
... var tst;
...
... tst = function tst() {
... var a;
... {
..... var a;
..... }
... };
... `
> console.log(hoist_var(cd))
var tst;
tst = function tst() {
var a;
var a;
{}
};
>
merge_dupe_var_decl
- must be used after hoist_var
var cd = `
function tst() {
var a;
var a;
{
a = 13;
}
}`
merge_dupe_var_decl(cd)
> console.log(merge_dupe_var_decl(cd))
[
[ id <Identifier> {"name":"tst"}] {},
[ id <Identifier> {"name":"a"}] {},
[ id <Identifier> {"name":"a"}] {},
[ left <Identifier> {"name":"a"}] {}
]
function tst() {
var a;
{
a = 13;
}
}
###when conflict with params-
var cd= `function tst(a) {
var a;
return(a)
}`
> console.log(merge_dupe_var_decl(cd))
function tst(a) {
return a;
}
split_nonidlval_asgn_expr
- must be used after split_var_declor
- if the AssignmentExpression left(LVal) is NOT direct Identifier
- (such as MemberExpression,ArrayPattern,ObjectPattern....)
- it will split it to three part, see below
var cd =`[z,{w,r}] = [1,{w:2,r:3}]`
console.log(split_nonidlval_asgn_expr(cd))
/*
let ___swapper_b1cf8a9c_$AssignmentExpression___ = [1, {
w: 2,
r: 3
}];
[z, {
w,
r
}] = ___swapper_b1cf8a9c_$AssignmentExpression___;
*/
var cd = `
function tst(u,v) {
let x;
{
var y=888;
{
var [z,{w,r}] = [1,{w:2,r:3}];
var [u,{v},...X] = [10,{v:20}];
let A;
var B=D=E=1111
}
}
var [z,{w,r}] = [10,{w:20,r:30}];
}
`
s0 = split_var_decl(cd)
s1 = split_var_declor(s0)
s2 = hoist_var(s1)
s3 = split_nonidlval_asgn_expr(s2)
s4 = merge_dupe_var_decl(s3)
console.log(s4)
/*
function tst(u, v) {
var y;
var z;
var w;
var r;
var X;
var B;
let x;
{
y = 888;
{
let ___swapper_ec071153_$AssignmentExpression___ = [1, {
w: 2,
r: 3
}];
[z, {
w,
r
}] = ___swapper_ec071153_$AssignmentExpression___;
let ___swapper_fcc8fd5d_$AssignmentExpression___ = [10, {
v: 20
}];
[u, {
v
}, ...X] = ___swapper_fcc8fd5d_$AssignmentExpression___;
let A;
B = D = E = 1111;
}
}
let ___swapper_b72b9d99_$AssignmentExpression___ = [10, {
w: 20,
r: 30
}];
[z, {
w,
r
}] = ___swapper_b72b9d99_$AssignmentExpression___;
}
*/
var_to_let
must be used after merge_dupe_var_decl
var cd = ` function tst() { var a; var a; { a = 13; } }` var s0 = merge_dupe_var_decl(cd) var s1 = var2let(s0) > console.log(s1) function tst() { let a; { a = 13; } }
split_rtrn_stmt
- move argument of return-statement out
const {split_rtrn_stmt} = require("nv-js-parse");
var cd =`
function tst(a,b,c) {
function inner() {
return(c*3)
}
return((a+b+inner()))
}
`
console.log(split_rtrn_stmt(cd))
function tst(a, b, c) {
function inner() {
let ___rslt_0296616b_$ReturnStatement___;
___rslt_0296616b_$ReturnStatement___ = c * 3;
return ___rslt_0296616b_$ReturnStatement___;
}
let ___rslt_32b00b98_$ReturnStatement___;
___rslt_32b00b98_$ReturnStatement___ = a + b + inner();
return ___rslt_32b00b98_$ReturnStatement___;
}
split_thrw_stmt
var src = `
function tst() {
throw(new Error("!!"));
{
throw a;
}
}
`
> console.log(x.split_thrw_stmt(src))
function tst() {
let ___rslt_2a23863e_$ThrowStatement___;
___rslt_2a23863e_$ThrowStatement___ = new Error("!!");
throw ___rslt_2a23863e_$ThrowStatement___;
{
let ___rslt_91675550_$ThrowStatement___;
___rslt_91675550_$ThrowStatement___ = a;
throw ___rslt_91675550_$ThrowStatement___;
}
}
ternery to if-else
const {conexpr_to_ifelse} = require("nv-js-parse");
var cd0 = `function tst(a,b,c,d,e) {return(a?b:c?d:e)}`;
/*
> function tst(a,b,c,d,e) {return(a?b:c?d:e)}
> tst(true,false,true,'d','e')
false
>
*/
console.log(conexpr_to_ifelse(cd0));
/*
function tst(a, b, c, d, e) {
return (() => {
if (a) {
return b;
} else {
return (() => {
if (c) {
return d;
} else {
return e;
}
})();
}
})();
}
> tst(true,false,true,'d','e')
false
>
*/
var cd1 = `function tst(a,b,c,d,e) {return((a?b:c)?d:e)}`;
/*
function tst(a,b,c,d,e) {return((a?b:c)?d:e)}
> tst(true,false,true,'d','e')
'e'
>
*/
console.log(conexpr_to_ifelse(cd1));
/*
function tst(a, b, c, d, e) {
return (() => {
if ((() => {
if (a) {
return b;
} else {
return c;
}
})()) {
return d;
} else {
return e;
}
})();
}
> tst(true,false,true,'d','e')
'e'
>
*/
var cd2 = `
function tst(a,b,c,d,e) {
let x = a?b:null
function inner() {
let y = a?(
c?d:e
):(
d?100:(e?200:300)
)
}
return(inner())
}
`
console.log(conexpr_to_ifelse(cd2));
/*
function tst(a, b, c, d, e) {
let x = (() => {
if (a) {
return b;
} else {
return null;
}
})();
function inner() {
let y = (() => {
if (a) {
return (() => {
if (c) {
return d;
} else {
return e;
}
})();
} else {
return (() => {
if (d) {
return 100;
} else {
return (() => {
if (e) {
return 200;
} else {
return 300;
}
})();
}
})();
}
})();
}
return inner();
}
*/
tree
- read-only
const {tree} = require("nv-js-parse");
var cd2 =`
function outter() {
let a ;
function inner() {
b
a;
{
a;
}
}
}
`
var t = tree(cd2);
var arr = t.$sdfs()
var ids = arr.filter(r=>r.path.node.type==='Identifier')
> ids.map(r=>r.path.node.name)
[ 'outter', 'a', 'inner', 'b', 'a', 'a' ]
>
> ids[0].get_closure_scope_nd()
[Program] {}
>
> ids[1].get_closure_scope_nd().show_code()
> ids[1].get_ast().name
'a'
> ids[1].get_closure_scope_nd().show_code()
function outter() {
let a;
function inner() {
b;
a;
{
a;
}
}
}
> ids[2].get_ast().name
'inner'
> ids[2].get_closure_scope_nd().show_code()
function outter() {
let a;
function inner() {
b;
a;
{
a;
}
}
}
> ids[3].get_ast().name
'b'
> var top = ids[3].get_closure_scope_nd()
[Program] {}
>
> top.path.scope.globals
[Object: null prototype] {
b: Node {
type: 'Identifier',
start: 80,
end: 81,
loc: SourceLocation {
start: [Position],
end: [Position],
filename: undefined,
identifierName: 'b'
},
name: 'b'
}
}
>
> ids[5].get_ast().name
'a'
>
> ids[5].get_closure_scope_nd().show_code()
function outter() {
let a;
function inner() {
b;
a;
{
a;
}
}
}
var cd3 = `
function tst(A,B) {
function inner() {
return(A)
}
}
`
var t = tree(cd3);
var arr = t.$sdfs()
var ids = arr.filter(r=>r.path.node.type==='Identifier')
ids.map(r=>r.path.node.name)
[ 'tst', 'A', 'B', 'inner', 'A' ]
> ids[1].get_closure_scope_nd().show_code()
function tst(A, B) {
function inner() {
return A;
}
}
undefined
> ids[1].get_ast().name
'A'
> ids[1].get_closure_scope_nd().show_code()
function tst(A, B) {
function inner() {
return A;
}
}
> ids[4].get_ast().name
'A'
> ids[4].get_closure_scope_nd().show_code()
function tst(A, B) {
function inner() {
return A;
}
}
binding
var t = tree(`
let P;
function X([tst=666,{a,b}]){
let uuu=y;
class C {}
}
try{}catch({e}) { let vvv;}
var D = { s(u,{v}){} }
class U {}
{let m;}
for(let j=0;j<5;j++) {}
for(let k in X) {}
for(let ele of X) {}
`)
t.get_decl_ids().map(r=>r.get_sig_plstr())
/*
[
'body-declarations-id',
'body-id',
'params-elements-left',
'params-elements-properties-key',
'params-elements-properties-key',
'body-body-declarations-id',
'body-body-id',
'param-properties-key',
'body-body-declarations-id',
'body-declarations-id',
'params',
'params-properties-key',
'body-id',
'body-declarations-id',
'init-declarations-id',
'left-declarations-id',
'left-declarations-id'
]
*/
var fdecl = t.FunctionDeclaration()[0];
/*
> fdecl.show_code()
function X([tst = 666, {
a,
b
}]) {
let uuu = y;
class C {}
}
*/
> fdecl.get_binding_def_ids()
{
var: [],
let: [
[ id <Identifier> {"name":"uuu"}] {}
],
const: [],
fdecl: [],
cdecl: [
[ id <Identifier> {"name":"C"}] {}
],
params: [
[ left <Identifier> {"name":"tst"}] {},
[ value <Identifier> {"name":"a"}] {},
[ value <Identifier> {"name":"b"}] {}
],
global: [],
catch_param: []
}
>
var cd = `
function tst() {
let [{
a,
b = e = 'EEEEE',
...f
},
c = 333, d,
[A,B,C,{E,F}],
...g
] =[];
}
`
var t = tree(cd);
var fdecl = t.$sdfs()[1]
> fdecl.get_decl_ids().map(r=>r.ast.name)
[
'a', 'b', 'f', 'c',
'd', 'A', 'B', 'C',
'E', 'F', 'g'
]
>
function tst() {
let [{
a,
b = e = 'EEEEE', //identifier-e ast-path-sign not match
...f
},
c = 333, d,
[A,B,C,{E,F}],
...g
] =[{},undefined,undefined,[1,2,3,{}]];
}
/*
> tst()
> e
'EEEEE' //e is global
*/
decl id type
for static trace using
var cd = ` function tst(a,{b}) { let c; { var d; } try{}catch(e){} } let _f1 = function (f) {} const _lambda = (g)=> {} class C { method(h,[j]) {} #method([k,{l}]){} } let O = {m(n){}} try{}catch({p}){} for(let i=0;i<5;i++) {} for(let {I}={I:0};i<5;i++) {} for(let ele of []) {} for(let [ELE] of [[1],[2]]) {} for(let k in {}) {} for(let {length} in [0,1,2,3,4,5,6,7,8,9,10]) {} //this is a valid syntax ` var t = tree(cd) > ids = t.get_decl_ids() [ [ id <Identifier> {"name":"tst"}] {}, [ params[0] <Identifier> {"name":"a"}] {}, [ value <Identifier> {"name":"b"}] {}, [ id <Identifier> {"name":"c"}] {}, [ id <Identifier> {"name":"d"}] {}, [ param <Identifier> {"name":"e"}] {}, [ id <Identifier> {"name":"_f1"}] {}, [ params[0] <Identifier> {"name":"f"}] {}, [ id <Identifier> {"name":"_lambda"}] {}, [ params[0] <Identifier> {"name":"g"}] {}, [ id <Identifier> {"name":"C"}] {}, [ params[0] <Identifier> {"name":"h"}] {}, [ elements[0] <Identifier> {"name":"j"}] {}, [ elements[0] <Identifier> {"name":"k"}] {}, [ value <Identifier> {"name":"l"}] {}, [ id <Identifier> {"name":"O"}] {}, [ params[0] <Identifier> {"name":"n"}] {}, [ value <Identifier> {"name":"p"}] {}, [ id <Identifier> {"name":"i"}] {}, [ value <Identifier> {"name":"I"}] {}, [ id <Identifier> {"name":"ele"}] {}, [ elements[0] <Identifier> {"name":"ELE"}] {}, [ id <Identifier> {"name":"k"}] {}, [ value <Identifier> {"name":"length"}] {} ] > > ids[0].is_fdecl_id() true > ids[1].is_fdecl_param_id() true > ids[2].is_fdecl_param_id() true > > ids[3].is_let_decl_id() true > > ids[4].is_var_decl_id() true > > ids[5].is_catch_param_id() true > > ids[6].is_let_decl_id() true > > ids[7].is_fexpr_param_id() true > > ids[8].is_const_decl_id() true > > ids[9].is_arrow_param_id() true > > ids[10].is_cdecl_id() true > > ids[11].is_cls_method_param_id() true > > ids[12].is_cls_method_param_id() true > > ids[13].is_cls_priv_method_param_id() true > > ids[14].is_cls_priv_method_param_id() true > > ids[15].is_let_decl_id() true > > ids[16].is_obj_method_param_id() true > > ids[17].is_catch_param_id() true > > ids[18].is_for_init_id() true > > ids[19].is_for_init_id() true > > ids[20].is_forof_id() true > ids[21].is_forof_id() true > ids[22].is_forin_id() true > ids[23].is_forin_id() true >
find escaped id
var cd = `
function tst(a) {
let b=10,c={'key':X};
a = b+c ;
{
let u = 5;
a = a + u;
}
return(a)
}
`
var t = tree(cd)
var f = t.$fstch()
> f.get_decl_ids()
[
[ params[0] <Identifier> {"name":"a"}] {},
[ id <Identifier> {"name":"b"}] {},
[ id <Identifier> {"name":"c"}] {},
[ id <Identifier> {"name":"u"}] {} //后代block内的
]
>
> f.get_own_local_decl_ids()
{
var: [],
let: [
[ id <Identifier> {"name":"b"}] {},
[ id <Identifier> {"name":"c"}] {}
],
const: [],
fdecl: [],
cdecl: [],
params: [
[ params[0] <Identifier> {"name":"a"}] {}
],
global: [],
catch_param: []
}
>
> f.get_own_local_escaped_decl_ids()
[
[ params[0] <Identifier> {"name":"a"}] {} // 参数 a 被里层 block 使用
]
>
> f.get_own_local_noescaped_decl_ids()
[
[ id <Identifier> {"name":"b"}] {},
[ id <Identifier> {"name":"c"}] {}
]
>
> f.get_nolocal_refed_ids()
[
[ value <Identifier> {"name":"X"}] {}
]
>
> f.get_nodecl_notation_ids()
[
[ value <Identifier> {"name":"X"}] {},
[ left <Identifier> {"name":"a"}] {},
[ left <Identifier> {"name":"b"}] {},
[ right <Identifier> {"name":"c"}] {},
[ left <Identifier> {"name":"a"}] {}, //<a> = a + u
[ left <Identifier> {"name":"a"}] {}, // a = <a> + u
[ right <Identifier> {"name":"u"}] {}, // a = a + <u>
[ argument <Identifier> {"name":"a"}] {}
]
>
> f.get_own_local_nodecl_ids()
[
[ left <Identifier> {"name":"a"}] {}, //<a>=b+c
[ left <Identifier> {"name":"b"}] {}, //a=<b>+c
[ right <Identifier> {"name":"c"}] {}, //a=b+<c>
[ argument <Identifier> {"name":"a"}] {} //return(<a>)
]
>
TAC format
used in a special js-runtime
normally useless
> const {tree} = require("nv-js-parse") > var src =`ReactDOM.render(<App />, document.getElementById('root'));` > var t = tree(src,{plugins:["typescript","jsx"]}) > t.$sdfs() [ [program <Program> {"sourceType":"module","interpreter":null}] {}, [ body[0] <ExpressionStatement> {}] {}, [ expression <CallExpression> {}] {}, [ callee <MemberExpression> {"computed":false}] {}, [ object <Identifier> {"name":"ReactDOM"}] {}, [ property <Identifier> {"name":"render"}] {}, [ arguments[0] <JSXElement> {}] {}, [ openingElement <JSXOpeningElement> {"selfClosing":true}] {}, [ name <JSXIdentifier> {"name":"App"}] {}, [ arguments[1] <CallExpression> {}] {}, [ callee <MemberExpression> {"computed":false}] {}, [ object <Identifier> {"name":"document"}] {}, [ property <Identifier> {"name":"getElementById"}] {}, [ arguments[0] <StringLiteral> {"value":"root"}] {} ] > // K = listKey:AstString | Key:AstString // T = K@TYPE // Attr = Object // Ele = [T,Attr?,Array<Ele>?] // > var tac = t.to_tac() > console.dir(tac,{depth:null}) [ 'program@Program',{ sourceType: 'module', interpreter: null }, [ 'body@ExpressionStatement', [ 'expression@CallExpression', [ 'callee@MemberExpression',{ computed: false },[ 'object@Identifier',{ name: 'ReactDOM' }, 'property@Identifier',{ name: 'render' } ], 'arguments@JSXElement',[ 'openingElement@JSXOpeningElement',{ selfClosing: true },[ 'name@JSXIdentifier', { name: 'App' } ] ], 'arguments@CallExpression',[ 'callee@MemberExpression',{ computed: false },[ 'object@Identifier',{ name: 'document' }, 'property@Identifier',{ name: 'getElementById' } ], 'arguments@StringLiteral',{ value: 'root' } ] ] ] ] ]
rplc_nodecl_undef_with_void0
var code =`
function tst() {
let a = undefined; //============================>this undefined is right-hand and on global,SHOULD-BE-REPLACED
function inner() {
let undefined = 999 //this undefined is left-hand and local #0
return(undefined) //this undefined is #0
}
console.log(1,inner()) //999
function inner2() {
function undefined () { //this undefined is left-hand and local #1
return(undefined) //this undefined is #1
}
return(undefined) //this undefined is #1
}
console.log(2,inner2()) //[Function: undefined]
function inner3() {
let undefined =888; //this undefined is left-hand and local #2
let r = (()=> {
return(undefined) //this undefined is #2
})();
return(r)
}
console.log(3,inner3()) //888
/////
function inner4() {
try {
x = undefined ; // ============================>this undefined is right-hand and on global ,SHOULD-BE-REPLACED
throw(x)
} catch(e){
return(e)
}
}
console.log(4,inner4()) // undefined
/////
function inner5() {
class undefined {
m() {return(undefined)} //class undefined
}
t= new undefined()
return(t)
}
console.log(5,inner5()) //undefined {}
////
function inner6() {
class C {
undefined() {return(undefined)} //global undefined coz classMethod
}
t= new C()
return(t.undefined())
}
console.log(6,inner6()) //undefined
/////
function inner7() {
let D= {
undefined() {return(undefined)} //global undefined coz ObjectMethod
}
return(D.undefined())
}
console.log(7,inner7()) //undefined
/////
function inner8() {
function undefined() {
console.log('in fdecl',undefined)
return(undefined) //function, coz FunctionDeclaration
}
console.log('in outter',undefined)
return(undefined())
}
console.log(8,inner8()) //[Function: undefined]
////
function inner9() {
let _f = function undefined() {
console.log('A',undefined===_f) //true
return(undefined) //function coz FunctionExpression
}
console.log('B:',undefined===_f) //false
return(_f())
}
console.log(9,inner9())
}
`
/*
> tst()
1 999
2 [Function: undefined]
3 888
4 undefined
5 undefined {}
6 undefined
7 undefined
in outter [Function: undefined]
in fdecl [Function: undefined]
8 [Function: undefined]
B: false
A true
9 [Function: undefined]
undefined
*/
console.log(rplc_nodecl_undef_with_void0(code))
/*
function tst() {
let a = void 0; //============================>this undefined is right-hand and on global,SHOULD-BE-REPLACED
function inner() {
let undefined = 999; //this undefined is left-hand and local #0
return undefined; //this undefined is #0
}
console.log(1, inner()); //999
function inner2() {
function undefined() {
//this undefined is left-hand and local #1
return undefined; //this undefined is #1
}
return undefined; //this undefined is #1
}
console.log(2, inner2()); //[Function: undefined]
function inner3() {
let undefined = 888; //this undefined is left-hand and local #2
let r = (() => {
return undefined; //this undefined is #2
})();
return r;
}
console.log(3, inner3()); //888
/////
function inner4() {
try {
x = void 0; // ============================>this undefined is right-hand and on global ,SHOULD-BE-REPLACED
throw x;
} catch (e) {
return e;
}
}
console.log(4, inner4()); // undefined
/////
function inner5() {
class undefined {
m() {
return undefined;
} //class undefined
}
t = new undefined();
return t;
}
console.log(5, inner5()); //undefined {}
////
function inner6() {
class C {
undefined() {
return void 0;
} //global undefined coz classMethod
}
t = new C();
return t.undefined();
}
console.log(6, inner6()); //undefined
/////
function inner7() {
let D = {
undefined() {
return void 0;
} //global undefined coz ObjectMethod
};
return D.undefined();
}
console.log(7, inner7()); //undefined
/////
function inner8() {
function undefined() {
console.log('in fdecl', undefined);
return undefined; //function, coz FunctionDeclaration
}
console.log('in outter', undefined);
return undefined();
}
console.log(8, inner8()); //[Function: undefined]
////
function inner9() {
let _f = function undefined() {
console.log('A', undefined === _f); //true
return undefined; //function coz FunctionExpression
};
console.log('B:', void 0 === _f); //false
return _f();
}
console.log(9, inner9());
}
*/
/*
> tst()
1 999
2 [Function: undefined]
3 888
4 undefined
5 undefined {}
6 undefined
7 undefined
in outter [Function: undefined]
in fdecl [Function: undefined]
8 [Function: undefined]
B: false
A true
9 [Function: undefined]
*/
get funclike description
funclike: it is for convert a func/lambda/method to a single-param-function-without-any-lexical-binding
used in nvlang for fctx grammar
similiar to with , but NOT permit any lexical VarDeclaration
for tracing purpose
{ FunctionParent: [ 'FunctionDeclaration', 'FunctionExpression', 'ObjectMethod', 'ArrowFunctionExpression', 'ClassMethod', 'ClassPrivateMethod' ] }
var t = tree(`
function tst(a,{b,c},...args) {
let A;
let B;
let {x,y} = {};
}
`)
/*
> f.get_desc_of_funclike()
[
'tst', //name
'FunctionDeclaration', //ast-type
{ generator: false, async: false }, //attribs
{
A: null,
B: null,
x: null,
y: null,
a: null,
b: null,
c: null,
args: []
} //---ctx after extract all params AND lexical-binding
]
>
*/
signed-pl
- find all declaration/def ids in a file, and generate a unique-path
- it is used in nvlang for gproc grammar, which NOT permit any local variable
> t.$sdfs().filter(nd=>nd.is_decl_id()).map(id=>id.get_id_plsign())
[
'body-0-declarations-0-id:traverse',
'body-1-declarations-0-id-properties-0-value:SimpleStack',
'body-1-declarations-0-id-properties-1-value:DPGT',
'body-1-declarations-0-id-properties-2-value:is_int',
'body-1-declarations-0-id-properties-3-value:is_str',
'body-2-declarations-0-id-properties-0-value:Root',
'body-3-declarations-0-id:ary_clu',
.....
for stmt to while
var cd = `
function tst() {
for(let i=0;i<999;i++) {
console.log(i);
let b = i +666
}
}`
console.log(for_stmt_to_while(cd))
/*
function tst() {
{
let i = 0;
while (i < 999) {
console.log(i);
let b = i + 666;
i++
}
}
}
*/
var cd = `
async function tst() {
let b;
for(let i=0;i<999;i++) {
await i;
console.log(i);
b = i +666;
if(b>1500) {
break
}
}
return(b)
}`
console.log(for_stmt_to_while(cd))
/*
async function tst() {
let b;
{
let i = 0;
while (i < 999) {
await i;
console.log(i);
b = i + 666;
if (b > 1500) {
break;
}
i++
}
}
return b;
}
*/
fmt-una-expr
- wrap UnaryExpression in a SequenceExpression
- normally useless
> x.fmt_una_expr('!a')
'!(0, a);'
>
> x.fmt_una_expr('!(typeof u.v)')
'!(0, typeof (0, u.v));'
>
replace update-expression WITH a closure
- update-expr is boring to do static tracing
var cd =`
function tst(arr) {
let v = 100;
let d = {prop:100}
arr = arr.forEach(
nd => {
v = v++;
v = v--;
v = ++v;
--v;
d.prop++;
d.prop=++d.prop;
d.prop--;
d.prop=--d.prop;
}
)
return([v,d])
}
`
/*
> tst([1,1,1])
[ 100, { prop: 100 } ]
>
*/
console.log(rplc_updt_expr_with_closure(cd))
/*
function tst(arr) {
let v = 100;
let d = {
prop: 100
};
arr = arr.forEach(nd => {
v = (() => {
let old_val = v;
let new_val = old_val + 1;
v = new_val;
return old_val; // v++ postfix-rtrn-old
})();
v = (() => {
let old_val = v;
let new_val = old_val - 1;
v = new_val;
return old_val; //v-- postfix-rtrn-old
})();
v = (() => {
let old_val = v;
let new_val = old_val + 1;
v = new_val;
return new_val; //++v prefix-rtrn-new
})();
(() => {
let old_val = v;
let new_val = old_val - 1;
v = new_val;
return new_val; //--v prefix-rtrn-new
})();
(() => {
let old_val = d.prop;
let new_val = old_val + 1;
d.prop = new_val;
return old_val;
})();
d.prop = (() => {
let old_val = d.prop;
let new_val = old_val + 1;
d.prop = new_val;
return new_val;
})();
(() => {
let old_val = d.prop;
let new_val = old_val - 1;
d.prop = new_val;
return old_val;
})();
d.prop = (() => {
let old_val = d.prop;
let new_val = old_val - 1;
d.prop = new_val;
return new_val;
})();
});
return [v, d];
}
*/
> tst([1,1,1])
[ 100, { prop: 100 } ]
>
get_completion_parent_nd
- get the completion parent closure/block of
- return/throw/break/continue
var src = `
function tst() {
var m=0
xx:while(m<3){
console.log("here")
i = 0;
n = 0;
while (i < 5) {
i++;
if (i === 3) {
continue xx;
}
n += i;
}
m=m+1
}
function inner() {
for(let i=0;i<5;i++){
break
return(i)
}
}
}
`
//return
var t = tree(src);
var rtrn_stmt = t.ReturnStatement()[0];
> rtrn_stmt
[69: body[1] <ReturnStatement> {}] {}
>
> var completion = rtrn_stmt.get_completion_parent_nd()
> completion
[54: body[2] <FunctionDeclaration> {"generator":false,"async":false}] {}
>
> completion.show_code()
function inner() {
for (let i = 0; i < 5; i++) {
break;
return i;
}
}
//break
> var brk_stmt = t.BreakStatement()[0]
> brk_stmt
[68: body[0] <BreakStatement> {}] {}
>
> var completion = brk_stmt.get_completion_parent_nd()
> completion
[57: body[0] <ForStatement> {}] {}
> completion.show_code()
for (let i = 0; i < 5; i++) {
break;
return i;
}
//continue label
> var conti_stmt = t.ContinueStatement()[0]
undefined
> conti_stmt
[42: body[0] <ContinueStatement> {}] {}
>
> var completion = conti_stmt.get_completion_parent_nd()
undefined
> completion
[10: body <WhileStatement> {}] {}
>
> completion.show_code()
while (m < 3) {
console.log("here");
i = 0;
n = 0;
while (i < 5) {
i++;
if (i === 3) {
continue xx;
}
n += i;
}
m = m + 1;
}
> completion.$parent()
[8: body[1] <LabeledStatement> {}] {}
> completion.$parent().show_code()
xx: while (m < 3) {
console.log("here");
i = 0;
n = 0;
while (i < 5) {
i++;
if (i === 3) {
continue xx;
}
n += i;
}
m = m + 1;
}
get_completion_check_points
- find break/continue 's corresponding block/forin/forof/for/while/dowhile/switch
- find return/throw 's corresponding func-decl/func-expr/cls-method/obj-method/arrow/cls-priv-method
var src = `
function tst() {
while(true) {
return(100)
}
try {
return(200)
} catch(e) {
()=> {
return('!!')
}
} finally {
}
{
throw(400)
}
}`
> var t = tree(src)
> t.FunctionDeclaration()[0]
[1: body[0] <FunctionDeclaration> {"generator":false,"async":false}] {}
> var fdecl = t.FunctionDeclaration()[0]
> fdecl.get_rtrn_check_points()
[
[7: body[0] <ReturnStatement> {}] {},
[11: body[0] <ReturnStatement> {}] {}
]
> var rtrns = fdecl.get_rtrn_check_points()
> rtrns
[
[7: body[0] <ReturnStatement> {}] {},
[11: body[0] <ReturnStatement> {}] {}
]
> rtrns.map(nd=>nd.show_code())
return 100;
return 200;
rm_sibs_after_completion
- remove following-sibs of completion-node ,see below
var src = `
function tst() {
try {
let a = 1;
return(a);
let b = a*2;
console.log(b) //-----------------------------remove
} catch(e) {
LBL:for(let a in M) {
while(true) {
if(x) {
break;
console.log(x) //-----------------------------remove
} else if(y) {
continue LBL;
console.log(666) //-----------------------------remove
} else {
switch(XX) {
case(0): {
}
case(1): {
break;
console.log(UUU) //-----------------------------remove
}
}
}
}
}
}
}
`
console.log(x.rm_sibs_after_completion(src))
/*
function tst() {
try {
let a = 1;
return a;
} catch (e) {
LBL: for (let a in M) {
while (true) {
if (x) {
break;
} else if (y) {
continue LBL;
} else {
switch (XX) {
case 0:
{}
case 1:
{
break;
}
}
}
}
}
}
}
*/
tailize_try_catch_final
why
- finalizer of try-statement is confusing
- its write-order is at last, but its executing order is just before return/throw in try/catch
- but the write-order of try/catch is BEFORE finalizer
- I can NOT understand this
- so transform the finalizer AND make it "reasonable"
example
> function tst() {
... try {
..... console.log(`before-return-in-try`)
//<==== #0
..... return(`i-am-returned`)
..... } finally {
..... console.log(`finalizer write after try-block`)
..... }
... }
>
> tst()
before-return-in-try
finalizer write after try-block //
'i-am-returned'
>
>
step
- this will take the return-statement|throw-statement out from try-stmt-block|try-catch-handler
- convert 【return-statement|throw-statement in 1.】 to a 【if/esle block】
- convert try-stmt-finalizer to 【a block】
- delete the original try-stmt-finalizer
- put the 【if/esle block in 2.】 after 【converted-block-from-try-stmt-finalizer in 3.】
- generate a 【block-contain-return|throw】
- put the 【block-contain-return|throw in 6.】 at end
purpose
- its purpose is to tailized rtrn/thrw of try-stmt AND transform the finalizer to its proper position
- to make the write/read order same-as the execution-order
- coz, the finally block is boring for static-tracing
- hard to explain , just see the below examples,example is easy to understand
input
var src =`
function tst() {
let a = 100;
try {
let a = (()=>{
try {
throw(1);
console.log("unreachable")
} catch(e) {
return(e);
console.log("unreachable")
} finally {
console.log('inner')
}
})();
if(a>0) {
return(a*2);
console.log("unreachable")
} else {
return(a*3)
console.log("unreachable")
}
} catch ({e}) {
console.log("unreachable")
} finally {
let arr = [a,a*2,a*3]
console.log(arr)
}
let c = a*3;
return(c)
}
`
/*
> tst()
inner
[ 100, 200, 300 ]
2
>
*/
output
> console.log(x.tailize_try_catch_final(src))
function tst() {
let a = 100;
//outer-try-catch-final
{
let ___τfinalización = [undefined, undefined];
//outer-try-block
{
let a = (() => {
//inner- try-catch-final
{
let ___τfinalización = [undefined, undefined];
//inner-try-block
{
___τfinalización = [false, 1];
}
//inner-catch-handle
if (___τfinalización[0] === false) {
let ___τcatch_clause_value = ___τfinalización[1];
{
let e = ___τcatch_clause_value;
___τfinalización = [true, e];
}
} else {}
//inner-final-block
{
console.log('inner');
}
//inner-tailized-completion-of-try-catch-final-stmt
if (___τfinalización[0] === undefined) {} else if (___τfinalización[0] === true) {
return ___τfinalización[1];
} else {
throw ___τfinalización[1];
}
}
})();
if (a > 0) {
___τfinalización = [true, a * 2];
} else {
___τfinalización = [true, a * 3];
}
}
//outer-catch-handle
if (___τfinalización[0] === false) {
let ___τcatch_clause_value = ___τfinalización[1];
{
let {
e
} = ___τcatch_clause_value;
console.log("unreachable");
}
} else {}
//outer-final-block
{
let arr = [a, a * 2, a * 3];
console.log(arr);
}
//out-tailized-completion-of-try-catch-final-stmt
if (___τfinalización[0] === undefined) {} else if (___τfinalización[0] === true) {
return ___τfinalización[1];
} else {
throw ___τfinalización[1];
}
}
let c = a * 3;
return c;
}
/*
> tst()
inner
[ 100, 200, 300 ]
2
>
*/
fmt-asgn-expr
- must used after split_var_decl AND split_var_declor
replace the below with a = a<op> b
OP_TO_BINOP: {
'+=': '+',
'-=': '-',
'/=': '/',
'%=': '%',
'*=': '*',
'**=': '**',
'&=': '&',
'|=': '|',
'>>=': '>>',
'>>>=': '>>>',
'<<=': '<<',
'^=': '^',
'||=': '||',
'&&=': '&&',
'??=': '??'
},
for example:
a+=999
will be replaced to
a = a + 999;
this is for functionize-bin-operator, which will remove all bin-operator
normally useless
var cd =`
function tst() {