ref: babf901b4a508c3ec5d1f89655f10377bbdf9637
dir: /appl/lib/ecmascript/pprint.b/
PPrint: adt
{
ex: ref Exec;
code: ref Code;
stack: array of string;
sp: int;
};
mkpprint(ex: ref Exec, code: ref Code): ref PPrint
{
return ref PPrint(ex, code, array[4] of string, 0);
}
funcprint(ex: ref Exec, func: ref Ecmascript->Obj): string
{
params := func.call.params;
(nil, name) := str->splitr(func.val.str, ".");
s := "function " + name + "(";
sep := "";
for(i := 0; i < len params; i++){
s += sep + params[i];
sep = ", ";
}
s += "){";
if(func.host != nil)
s += "[host code]";
else
s += "\n" + pprint(ex, func.call.code, " ");
s += "}";
return s;
}
pprint(ex: ref Exec, code: ref Code, indent: string): string
{
pp := ref PPrint(ex, code, array[4] of string, 0);
#for(i:=0; i < code.npc; i++) sys->print("%d: %d\n", i, int code.ops[i]);
s := pstmt(pp, 0, code.npc, indent);
if(pp.sp != 0)
fatal(ex, "pprint stack not balanced");
return s;
}
pstmt(pp: ref PPrint, pc, epc: int, indent: string): string
{
e, e1, e2: string;
c, apc: int;
code := pp.code;
s := "";
while(pc < epc){
op := int code.ops[pc++];
while(op == Llabel){
(pc, c) = getconst(code.ops, pc);
s += code.strs[c] + ":\n";
op = int code.ops[pc++];
}
s += indent;
case op{
Lbreak or
Lcontinue or
Lreturn =>
s += tokname(op);
if(op == Lreturn){
(pc, e) = pexp(pp, pc, code.npc);
s += " " + e;
}
s += ";\n";
Lbreaklab or
Lcontinuelab =>
s += tokname(op);
(pc, c) = getconst(code.ops, pc);
s += " " + code.strs[c] + ";\n";
'{' =>
(pc, apc) = getjmp(code.ops, pc);
s += "{\n" + pstmt(pp, pc, apc, indent+" ") + indent + "}\n";
pc = apc;
Lif or
Lwith or
Lwhile =>
(pc, apc) = getjmp(code.ops, pc);
(pc, e) = pexp(pp, pc, apc);
(pc, apc) = getjmp(code.ops, pc);
s += tokname(op) + "(" + e + "){\n";
s += pstmt(pp, pc, apc, indent+" ");
if(op == Lif){
(pc, apc) = getjmp(code.ops, apc);
if(pc != apc)
s += indent + "}else{\n";
s += pstmt(pp, pc, apc, indent+" ");
}
s += indent + "}\n";
pc = apc;
Ldo =>
(pc, apc) = getjmp(code.ops, pc);
e = pstmt(pp, pc, apc, indent+" ");
(pc, apc) = getjmp(code.ops, apc);
(pc, e1) = pexp(pp, pc, apc);
s += "do{\n" + e + indent + "}(while(" + e1 + ");\n";
pc = apc;
Lfor or
Lforvar or
Lforin or
Lforvarin =>
(pc, apc) = getjmp(code.ops, pc);
(pc, e) = pexp(pp, pc, apc);
(pc, apc) = getjmp(code.ops, pc);
(pc, e1) = pexp(pp, pc, apc);
s += "for(";
if(op == Lforvar || op == Lforvarin)
s += "var ";
s += e;
if(op == Lfor || op == Lforvar){
(pc, apc) = getjmp(code.ops, pc);
(pc, e2) = pexp(pp, pc, apc);
s += "; " + e1 + "; " + e2;
}else
s += " in " + e1;
s += "){\n";
(pc, apc) = getjmp(code.ops, pc);
s += pstmt(pp, pc, apc, indent+" ");
s += indent + "}\n";
pc = apc;
';' =>
s += ";\n";
Lvar =>
(pc, apc) = getjmp(code.ops, pc);
(pc, e) = pexp(pp, pc, apc);
s += "var " + e + ";\n";
Lswitch =>
(pc, apc) = getjmp(code.ops, pc);
(pc, e) = pexp(pp, pc, apc);
s += "switch (" + e + ") {\n";
(pc, apc) = getjmp(code.ops, pc);
(pc, e) = pcaseblk(pp, pc, apc, indent);
s += e + indent + "}\n";
pc = apc;
Lthrow =>
(pc, e) = pexp(pp, pc, code.npc);
s += "throw " + e + "\n";
Ltry =>
s += "try\n";
(pc, apc) = getjmp(code.ops, pc);
s += pstmt(pp, pc, apc, indent+" ");
(pc, apc) = getjmp(code.ops, apc);
if(pc != apc){
(pc, c) = getconst(code.ops, ++pc);
s += "catch(" + code.strs[c] + ")\n";
s += pstmt(pp, pc, apc, indent+" ");
}
(pc, apc) = getjmp(code.ops, apc);
if(pc != apc){
s += "finally\n";
s += pstmt(pp, pc, apc, indent+" ");
}
pc = apc;
* =>
(pc, e) = pexp(pp, pc-1, code.npc);
s += e + ";\n";
}
}
return s;
}
pexp(pp: ref PPrint, pc, epc: int): (int, string)
{
c, apc: int;
s, f, a, a1, a2: string;
code := pp.code;
savesp := pp.sp;
out: while(pc < epc){
case op := int code.ops[pc++]{
Lthis =>
s = "this";
Lid or
Lnum or
Lstr or
Lregexp =>
(pc, c) = getconst(code.ops, pc);
if(op == Lnum)
s = string code.nums[c];
else{
s = code.strs[c];
if(op == Lstr)
s = "\""+escstr(code.strs[c])+"\"";
}
'*' or
'/' or
'%' or
'+' or
'-' or
Llsh or
Lrsh or
Lrshu or
'<' or
'>' or
Lleq or
Lgeq or
Lin or
Linstanceof or
Leq or
Lneq or
Lseq or
Lsne or
'&' or
'^' or
'|' or
'=' or
'.' or
',' or
'[' =>
a2 = ppop(pp);
a1 = ppop(pp);
s = tokname(op);
if(a1[0] == '='){
s += "=";
a1 = a1[1:];
}
if(op == '[')
s = a1 + "[" + a2 + "]";
else{
if(op != '.'){
if(op != ',')
s = " " + s;
s = s + " ";
}
s = a1 + s + a2;
}
Ltypeof or
Ldelete or
Lvoid or
Lnew or
Linc or
Ldec or
Lpreadd or
Lpresub or
'~' or
'!' or
Lpostinc or
Lpostdec =>
a = ppop(pp);
s = tokname(op);
if(op == Lpostinc || op == Lpostdec)
s = a + s;
else{
if(op == Ltypeof || op == Ldelete || op == Lvoid || op == Lnew)
s += " ";
s += a;
}
'(' =>
s = "(";
')' =>
s = ppop(pp);
if(ppop(pp) != "(")
fatal(pp.ex, "unbalanced () in pexp");
s = "(" + s + ")";
Lgetval or
Las =>
continue;
Lasop =>
s = "=" + ppop(pp);
Lcall or
Lnewcall =>
(pc, c) = getconst(code.ops, pc);
a = "";
sep := "";
for(sp := pp.sp-c; sp < pp.sp; sp++){
a += sep + pp.stack[sp];
sep = ", ";
}
pp.sp -= c;
f = ppop(pp);
if(op == Lnewcall)
f = "new " + f;
s = f + "(" + a + ")";
';' =>
break out;
Landand or
Loror or
'?' =>
s = ppop(pp);
(pc, apc) = getjmp(code.ops, pc);
(pc, a1) = pexp(pp, pc, apc);
s += " " + tokname(op) + " " + a1;
if(op == '?'){
(pc, apc) = getjmp(code.ops, pc);
(pc, a2) = pexp(pp, pc, apc);
s += " : "+ a2;
}
* =>
fatal(pp.ex, "pexp: unknown op " + tokname(op));
}
ppush(pp, s);
}
if(savesp == pp.sp)
return (pc, "");
if(savesp != pp.sp-1)
fatal(pp.ex, "unbalanced stack in pexp");
return (pc, ppop(pp));
}
pcaseblk(pp: ref PPrint, pc, epc: int, indent: string): (int, string)
{
code := pp.code;
defpc, clausepc, nextpc, apc: int;
s, a: string;
(pc, defpc) = getjmp(code.ops, pc);
clausepc = pc;
(pc, nextpc) = getjmp(code.ops, pc);
for (; pc < epc; (clausepc, (pc, nextpc)) = (nextpc, getjmp(code.ops, nextpc))) {
if (clausepc == defpc) {
s += indent + "default:\n";
} else {
(pc, apc) = getjmp(code.ops, pc);
(pc, a) = pexp(pp, pc, apc);
s += indent + "case " + a + ":\n";
}
s += pstmt(pp, pc, nextpc, indent+"\t");
}
return (epc, s);
}
ppush(pp: ref PPrint, s: string)
{
if(pp.sp >= len pp.stack){
st := array[2 * len pp.stack] of string;
st[:] = pp.stack;
pp.stack = st;
}
pp.stack[pp.sp++] = s;
}
ppop(pp: ref PPrint): string
{
if(pp.sp == 0)
fatal(pp.ex, "popping too far off the pstack");
return pp.stack[--pp.sp];
}
unescmap := array[128] of
{
'\'' => byte '\'',
'"' => byte '"',
'\\' => byte '\\',
'\b' => byte 'b',
'\u000c' => byte 'f',
'\n' => byte 'n',
'\r' => byte 'r',
'\t' => byte 't',
* => byte 0
};
escstr(s: string): string
{
n := len s;
sb := "";
for(i := 0; i < n; i++){
c := s[i];
if(c < 128 && (e := int unescmap[c])){
sb[len sb] = '\\';
sb[len sb] = e;
}else if(c > 128 || c < 32){
sb += "\\u0000";
for(j := 1; j <= 4; j++){
sb[len sb - j] = "0123456789abcdef"[c & 16rf];
c >>= 4;
}
}else
sb[len sb] = c;
}
return sb;
}