prettyC
returns C code for an expression (or statement)
with embedded prettyprinting escapes.
nohex
is a purely internal flag.
If it is non-null, constants are in hex; otherwise they're in decimal.
<*>= [D->] procedure prettyC(e, precedence, associativity, nohex) <local declarations forprettyC
> initial {<initializeCprec
andCassoc
>} /precedence := 0 /associativity := "L" return case type(e) of { <cases forprettyC
> default : impossible("Bad code to prettyC") } end
DefinesprettyC
(links are to index).
<cases forprettyC
>= (<-U) [D->] "list" : commaseparate(maplist2(prettyC, e, Cprec[","]), ", $o") "set" : commaseparate(maplist2(prettyC, sort(e), Cprec["&&"]), " && $o") "eqn" : { o := Cop(e.op) Cbracket(prettyC(e.left, Cprec[o]) || " " || o || " $o" || prettyC(e.right, Cprec[o]), "==", precedence) } "table" : { # standard normal form <makes
represent tablee
, but elide additive and multiplicative units> "$t${" || s || "$}$b" } "string" : e "literal" : list_to_string(e.s) "integer" : if e > max_decimal then "0x" || hexstring(e) else string(e)
The old version printed in any old order, but the new version prints positive terms first, then negative terms, with the constant term last in its group.
<makes
represent tablee
, but elide additive and multiplicative units>= (<-U) { s := ""; leadingsign := "" <add positive terms tos
> leadingsign := " - $o" <add negative terms tos
> s := if s == "" then "0" else Cbracket(s, "+", precedence) }
<add positive terms to s
>= (<-U)
every e[k := 1 ~=== key(e)] > 0 do {
s ||:= leadingsign
s ||:= (1 ~= abs(e[k])) || "*" # print coefficient if not 1
s ||:= prettyC(k, Cprec["*"])
leadingsign := " + $o"
}
if e[1] > 0 then s ||:= leadingsign || string(e[1])
<add negative terms to s
>= (<-U)
every e[k := 1 ~=== key(e)] < 0 do {
s ||:= leadingsign
s ||:= (1 ~= abs(e[k])) || "*" # print coefficient if not 1
s ||:= prettyC(k, Cprec["*"])
}
if e[1] < 0 then s ||:= leadingsign || string(-e[1])
<old makes
represent tablee
, but elide additive and multiplicative units>= { # print constant term if nonzero if e[1] ~= 0 then { s := string(e[1]) ; leadingsign := " + $o" } else s := leadingsign := "" # print every nonconstant term k every k := 1 ~=== key(e) do { s ||:= if e[k] < 0 then " - " else leadingsign leadingsign := " + $o" s ||:= (1 ~= abs(e[k])) || "*" # print coefficient if not 1 s ||:= prettyC(k, Cprec["*"]) } s := if s == "" then "0" else Cbracket(s, "+", precedence) }
<local declarations for prettyC
>= (<-U) [D->]
local leadingsign
<cases for prettyC
>+= (<-U) [<-D->]
"Eorb" : { s := prettyC(e.x, Cprec["|"], "L")
t := prettyC(e.y, Cprec["|"], "R")
Cbracket(s || " | $o" || t, "|", precedence)
}
"Eand" : { s := prettyC(e.x, Cprec["&&"], "L")
t := prettyC(e.y, Cprec["&&"], "R")
Cbracket(s || " && $o" || t, "&&", precedence)
}
<cases for prettyC
>+= (<-U) [<-D->]
"Eslice" : { s := prettyC(super_simplify(Eshift(e.x, -e.lo)), Cprec["&"], "L")
Cbracket(s || " & " || mask(e.n), "&", precedence)
}
"Eshift" : { s := prettyC(e.x, Cprec["<<"])
s ||:= if e.n < 0 then (" >> " || -e.n)
else (" << " || e.n)
Cbracket(s, "<<", precedence)
}
<cases for prettyC
>+= (<-U) [<-D->]
"Enarrowu" : prettyC(e.x, precedence, associativity) # checked, so must be identity
"Enarrows" : prettyC(super_simplify(Eslice(e.x, 0, e.n)), precedence, associativity)
"Ewiden" : "sign_extend($t${$o" || prettyC(e.x) || ", $o" || e.n || "$}$b)"
<cases for prettyC
>+= (<-U) [<-D->]
"Ediv" : { s := prettyC(e.x, Cprec["/"])
s := "((int)" || s || ")" # temporary hack to fix a bug
s ||:= " / " || e.n
Cbracket(s, "/", precedence)
}
"Emod" : { s := prettyC(e.x, Cprec["%"])
s ||:= " % " || e.n
Cbracket(s, "%", precedence)
}
Have to be careful emitting no tokens.
<cases forprettyC
>+= (<-U) [<-D->] "Semit" : if *e.x = 0 then "{/*emit epsilon*/}" else { s := if *e.x > 1 then "$t${{ $c" else "" <if direct style and late pc, addENCODE_pc
declaration tos
and setpc_override
> s ||:= commaseparate(maplist(prettyC, e.x), " $c") <restorepc_override
> s || if *e.x > 1 then " $b$c}$}" else "" } "Stoken" : case emitterstyle of { "direct" : {<direct call throughemittername
>} "closure": {<closure call toemitter
>} } | impossible("emitter style")
<direct call through emittername
>= (<-U)
s := emittername || "$t(" || prettyC(e.x, Cprec[","]) || ", $o" || e.n || ")"
Cbracket("$c" || s, "app", precedence) || ";$b"
For the closure call, we assume that the closure is stored in local variable _c
.
<closure call to emitter
>= (<-U)
o := if e.offset > 0 then " + " || e.offset else ""
s := "emitter$t(_c->loc.dest_block, $o_c->loc.dest_lc" || o || ", $o" ||
prettyC(e.x, Cprec[","]) || ", $o" || e.n || ")"
Cbracket("$c" || s, "app", precedence) || ";$b"
This code fixes bug 5 by using ENCODE_pc
instead of cur_pc
when
an emitted token other than the first requires the program counter.
It's needed because cur_pc()
changes as a side effect of emit
,
but ``the program counter'' always refers to the location of
the first token emitted.
<if direct style and late pc, addENCODE_pc
declaration tos
and setpc_override
>= (<-U) if *e.x > 1 & emitterstyle == "direct" & expwalk(e.x[2:0], equals_pc) then { s ||:= "unsigned ENCODE_pc = cur_pc(); $c" save_pc_override := pc_override pc_override := "ENCODE_pc" }
<restore pc_override
>= (<-U)
pc_override := save_pc_override
<local declarations for prettyC
>+= (<-U) [<-D->]
local save_pc_override
<cases for prettyC
>+= (<-U) [<-D->]
"Epc" : \pc_override | "cur_pc()"
"Epc_known" : "cur_pc_known()"
<cases for prettyC
>+= (<-U) [<-D->]
"Eclosure_loc" : "_c->loc"
"Eclosure_addr" : "_c->v.a" || e.n
"Eclosure_val" : "_c->v.u" || e.n
<*>+= [<-D->] global pc_override
Definespc_override
(links are to index).
<cases for prettyC
>+= (<-U) [<-D->]
"Efitsu" : { s := "(unsigned)(" || prettyC(e.x) || ") < " || tworaised(e.n)
Cbracket(s, "<", precedence)
}
"Efitss" : { s := "(unsigned)(" || prettyC(addconst(e.x, 2^(e.n-1))) ||
") < " || tworaised(e.n)
Cbracket(s, "<", precedence)
}
<cases for prettyC
>+= (<-U) [<-D->]
"Eforce" | "Eforceable" : {
(if type(e.x) == "Eclosure_loc" then "pc_" else "") ||
"location" ||
(if type(e) == "Eforceable" then "_known" else "") ||
"$t${(" || prettyC(e.x) || ")$}$b"
}
<cases for prettyC
>+= (<-U) [<-D->]
"Enot" : { s := "!" || prettyC(e.x, Cprec["!"])
Cbracket(s, "!", precedence)
}
<cases for prettyC
>+= (<-U) [<-D->]
"Enosimp" : prettyC(e.x, precedence, associativity)
<cases for prettyC
>+= (<-U) [<-D->]
"Sstmts" : "$t${{ $c" || commaseparate(maplist2(prettyC, e.x), " $c") || " $b$c}$}"
<cases for prettyC
>+= (<-U) [<-D->]
"Gcall" : { l := []; every put(l, prettyC(!e.args))
Cnoreserve(e.name) || "(" || commaseparate(l) || ")"
}
<cases for prettyC
>+= (<-U) [<-D->]
"Einstance_input" : {
s := prettyC(e.x, Cprec["."]) || ".u." || e.cons.name || "." || e.name
Cbracket(s, ".", precedence)
}
"Einstance_tagged" : {
s := prettyC(e.x, Cprec["="]) || ".tag == " || e.cons.name || "_TAG"
Cbracket(s, "==", precedence)
}
I assume the if statement has been simplified, so there are no arms beyond
any else
arm
<cases for prettyC
>+= (<-U) [<-D->]
"Sif" : { preif := ""
s := ""
every a := !e.arms do {
s ||:= preif || Ctest(a.guard) || Carm(a.x)
preif := "$celse "
}
"${" || s || "$}"
}
<cases for prettyC
>+= (<-U) [<-D->]
"Efail" : error("Generate-time operation failed: ", e.msg)
<*>+= [<-D->] procedure Ctest(guard) return if guard_always_satisfied(guard) then "" else "if $t${(" || prettyC(guard) || ")$}$b " end
DefinesCtest
(links are to index).
<*>+= [<-D->] procedure Carm(e) return if type(e) == ("Gblock"|"Sstmts") then prettyC(e) || " " else "$t$c${" || prettyC(e) || "$}$b " end
DefinesCarm
(links are to index).
<cases for prettyC
>+= (<-U) [<-D->]
"Stagcase" : { s := "switch (" || prettyC(e.x, Cprec["."]) || ".tag) {$t${"
every c := kept_constructors(e.type) do
s ||:= "$ccase " || c.name || "_TAG: $t$c${" ||
prettyC(e.arms[c]) || " $cbreak;$}$b"
s || "$cdefault: assert(0);$}$b$c} /* " ||
Cuncomment(prettyC(e.x)) || " */"
}
C comments don't nest, but some implemenations might.
<*>+= [<-D->] procedure Cuncomment(s) r := "" s ? { while r ||:= tab(upto('/*')) do if ="/*" then r ||:= "--" else if ="*/" then r ||:= "--" else r ||:= move(1) return r || tab(0) } end
DefinesCuncomment
(links are to index).
<cases for prettyC
>+= (<-U) [<-D->]
"Sfail" : { s := "fail($t${" || image(escape_dollars(e.fmt))
every a := e.a1 | e.a2 | e.a3 do
s ||:= ", $o" || prettyC(\a, Cprec[","])
s ||:= "$}$b);"
Cbracket(s, "app", precedence)
}
"Sepsilon" : "{/* skip */}"
<cases for prettyC
>+= (<-U) [<-D->]
"Sclosure" : prettyC(\e.creation) | impossible("creating closure")
<cases for prettyC
>+= (<-U) [<-D->]
"Glines" : { s := ""; every s ||:= prettyC(!e.x) || "\n"; s }
"Gresynch" : "$n#line " || e.line || " " || image(\e.file | "generated-code")
<cases for prettyC
>+= (<-U) [<-D->]
"Gblock" : { s := "{ ${$t$c"
every s ||:= prettyC(!e.decls) || ";$n"
s ||:= commaseparate(maplist(prettyC, e.stmts), "$c")
s || "$b $c}$}"
}
"Gdecl" : { s := "${" || prettyC(\e.type | infer_C_type(e.init))
s ||:= " " || e.name
s ||:= " = $t$c" || prettyC(\e.init) || "$b"
s || "$}"
}
<*>+= [<-D->] procedure infer_C_type(value) if /value then impossible("variable with no type and no value") return case type(value) of { "Ewiden" : Tsigned(value.n) default : Tunsigned(wordsize) } end
Definesinfer_C_type
(links are to index).
<cases for prettyC
>+= (<-U) [<-D->]
"Gdeclnamearray" : {
/na_count := 0
/e.na.codename := "MATCH_name_" || e.na.field.field.name || "_" || (na_count +:= 1)
/e.na.storageclass := "static"
s := e.na.storageclass || " char *" || e.na.codename || "[] = {"
s ||:= "${$t"
every i := 0 to e.na.hi - 1 do
s ||:= " $c" || escape_dollars(image(\e.na.tbl[i]|0)) || ","
s || "$b $c$}}"
}
<local declarations for prettyC
>+= (<-U) [<-D]
static na_count
<cases for prettyC
>+= (<-U) [<-D->]
"Gsetname" : "char *" || e.lhs || " = ${$t$o" || case type(e.name) of {
"string" : image(e.name)
"namearray" : e.name.codename || "[" || prettyC(e.name.field) || "]"
default : impossible("type of node name field")
} || "$b$}"
<cases for prettyC
>+= (<-U) [<-D->]
"Gcase" : { s := "switch (" || prettyC(e.x) || ") {$t${ "
every s ||:= prettyC(!e.arms)
s || "$cdefault: assert(0);$}$b$c} /* " ||
Cuncomment(prettyC(e.x)) || " */"
}
"Gcasearm" : { s := "$n"
every i := 1 to *e.tags by 2 do {
if e.tags[i] + 1 = e.tags[i+1] then
s ||:= "$ocase " || e.tags[i] || ": "
else
every s ||:= "$ocase " || (e.tags[i] to (e.tags[i+1]-1)) || ": "
}
s ||:= "$t$c${" || prettyC(e.x) || "$cbreak;$}$b"
}
<cases for prettyC
>+= (<-U) [<-D->]
"Ginrange" : { x := prettyC(e.x, Cprec["<"])
if e.lo + 1 = e.hi then {
Cbracket(x || " == " || e.lo, "==", Cprec["&&"])
} else {
c1 := Cbracket(e.lo || " <= " || x, "<=", Cprec["&&"])
c2 := Cbracket(x || " < " || e.hi, "<", Cprec["&&"])
Cbracket(c1 || " && " || c2, "&&", precedence)
}
}
"Gsetname" : "${" || e.lhs || " = $t$o" || case type(e.name) of {
"string" : image(e.name)
"namearray" : e.name.codename || "[" || e.name.field.name || "]"
default : impossible("type of node name field")
} || ";$b$}"
"Gasgn" : "${" || prettyC(e.lhs) || " = $t$o" || prettyC(e.x, Cprec["="]) || ";$b$}"
<cases for prettyC
>+= (<-U) [<-D->]
"Gnomatch" : "assert(0); /* no match */$n"
"Tunsigned" : unsigned_type(e.width)
"Tsigned" : signed_type(e.width)
"Gcomment" : "/* " || Cuncomment(e.s) || " */"
"Gcommented" :
prettyC(e.e, precedence, associativity, nohex) ||
" /* " || Cuncomment(e.comment) || " */"
<*>+= [<-D->] procedure unsigned_type(width) return "unsigned" || unsigned_width_comment(width) end procedure signed_type(width) return "int" || signed_width_comment(width) end procedure unsigned_width_comment(width) return if \width < wordsize then " /* [0.." || (2^width-1) || "] */" else "" end procedure signed_width_comment(width) return if \width < wordsize then " /* [" || -(2^(width-1)) || ".." || (2^(width-1)-1) || "] */" else "" end
Definessigned_type
,signed_width_comment
,unsigned_type
,unsigned_width_comment
(links are to index).
<cases for prettyC
>+= (<-U) [<-D]
"absolute_field" :
prettyC(super_simplify(afieldexp(e)), precedence, associativity, nohex)
<*>+= [<-D->] global Cprec, Cassoc procedure Cbracket(s, op, p, a) /a := "L" return "${" || (if Cprec[op] > p | (Cprec[op] = p & Cassoc[p] == a) then s else "(" || s || ")") || "$}" end
DefinesCassoc
,Cbracket
,Cprec
(links are to index).
<initializeCprec
andCassoc
>= (<-U) ops := ["N", ["low"], "L", [";"], "L", [","], "R", ["=", "+=", "-=", "*=", "/=", "%=", "&=", "^=", "|=", ">>=", "<<="], "R", [":?"], "L", ["||"], "L", ["&&"], "L", ["|"], "L", ["^"], "L", ["&"], "L", ["==", "!="], "L", ["<=", "<", ">=", ">"], "L", [">>", "<<"], "L", ["+", "-"], "L", ["%", "/", "*"], "R", ["!", "~", "++", "--", "cast", "sizeof"], "L", [".", "->", "app"], "N", ["high"] ] Cprec := table([]) # missed lookups break arithmetic comparisons Cassoc := table() every i := 1 to *ops by 2 do { every Cprec[!ops[i+1]] := i Cassoc[i] := ops[i] }
<*>+= [<-D->] procedure Cop(op) return if op == "=" then "==" else op end
DefinesCop
(links are to index).
<*>+= [<-D->] procedure Cnoreserve(word) static reserved initial reserved := set([ "asm", "auto", "break", "case", "char", "const", "continue", "default", "double", "do", "else", "enum", "extern", "float", "for", "goto", "if", "int", "long", "register", "return", "short", "signed", "sizeof", "static", "struct", "switch", "typedef", "union", "unsigned", "void", "volatile", "while"]) return if member(reserved, word) then Cnoreserve(word||"_") else word end
DefinesCnoreserve
(links are to index).
<*>+= [<-D->] procedure hexstring(i,n) local s if i = 0 then s := "0" else { s := "" while i ~= 0 do { s := "0123456789abcdef"[iand(i,15) + 1] || s i := ishift(i,-4) } } if \n > *s then s := right(s,n,"0") return s end
Defineshexstring
(links are to index).
<*>+= [<-D->] procedure Generate_C() pretty := prettyC hex_prefix := "0x" gen_outer_decls := [] gen_file_header := ["#include <assert.h>\n", "#define sign_extend(N,SIZE) ", # no newline after #define "(((int)((N) << (sizeof(unsigned)*8-(SIZE)))) >> (sizeof(unsigned)*8-(SIZE)))" ] fetchtab["type"] := "unsigned char *"; fetchtab["add"] := "((%a) + %o)" fetchtab["integer"] := "(unsigned)(%a)" # fetchtab[8] := "(unsigned)*(unsigned char *) (%a+%o/8)" # fetchtab[16] := "(unsigned)*(unsigned short *)(%a+%o/16)" # fetchtab[32] := "(unsigned)*(unsigned *) (%a+%o/32)" end
DefinesGenerate_C
(links are to index).
Used to declare functions according to the proper style of indirection.
<*>+= [<-D] procedure c_function_declaration(pp, returntype, name, args, indirect) emit_template(pp, if \indirectname then "indirect-proto.t" else "direct-proto.t", "return", returntype, "name", name, "args", args) return end
Definesc_function_declaration
(links are to index).
<direct-proto.t>= extern %return %name(%args);
<indirect-proto.t>= %return (*%name)(%args);
s
>: U1, D2
s
>: U1, D2
prettyC
>: U1, D2, D3, D4, D5, D6, D7, D8, D9, D10, D11, D12, D13, D14, D15, D16, D17, D18, D19, D20, D21, D22, D23, D24, D25, D26, D27, D28, D29
emitter
>: U1, D2
emittername
>: U1, D2
ENCODE_pc
declaration to s
and set pc_override
>: U1, D2
Cprec
and Cassoc
>: U1, D2
prettyC
>: U1, D2, D3, D4
s
represent table e
, but elide additive and multiplicative units>: U1, D2
s
represent table e
, but elide additive and multiplicative units>: D1
pc_override
>: U1, D2