code: purgatorio

ref: ec35f468e0eba87c9f09cbbe5fa8af2591e6f914
dir: /appl/lib/ecmascript/pprint.b/

View raw version
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;
}