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 {<initializeCprecandCassoc>} /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 <makesrepresent 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.
<makesrepresent 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 makesrepresent 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_pcdeclaration tosand 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_pcdeclaration tosand 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).
<initializeCprecandCassoc>= (<-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