Ivy to cpp
include ivylang
include cpplang
include hash
include reader
include pass_flat
include pass_typeinfer
include analysis
module generic_to_cpp(ivytype,cpptype,cppclass) = {
action to_cpp(s:ivytype,st:tocppst) returns (res:cppclass,st:tocppst) = {
var t : cpptype;
(t,st) := s.to_cpp_int(st);
t.ann := s.ann;
res := t
}
}
module binary_op_to_cpp(ivytype,cpptype,cppclass) = {
instantiate generic_to_cpp(ivytype,cpptype,cppclass)
action to_cpp_int(s:ivytype,st:tocppst) returns (res:cpptype,st:tocppst) = {
(res.lhs,st) := s.lhs.to_cpp(st);
(res.rhs,st) := s.rhs.to_cpp(st);
}
}
object ivy = { ...
function cpp_reserved_word(X:str) : bool
after init {
cpp_reserved_word("bool") := true;
cpp_reserved_word("char") := true;
cpp_reserved_word("int") := true;
cpp_reserved_word("long") := true;
cpp_reserved_word("new") := true;
cpp_reserved_word("and") := true;
cpp_reserved_word("or") := true;
cpp_reserved_word("not") := true;
}
f.a(x).b(y) is [f,a,b]. If a.b is a
function, then the path of a.b(x,y).c is [a.b,c]. On the
other hand, (x+y).a is not an lvalue and does not have a path.
Paths are used in alias analysis.
object access_path = {
type this = struct {
elems : vector[ident]
}
}
action path_may_alias(v:access_path,w:access_path) returns (res : bool) = {
res := true;
var idx := v.elems.begin;
while res & idx < v.elems.end & idx < w.elems.end {
res := (v.elems.value(idx) = w.elems.value(idx));
idx := idx.next
}
}
type lvalue_count = struct {
lvalue : cpp.expr,
path : access_path,
cnt : pos
}
instance ident_to_declvec : hash_map(ident,vector[decl])
instance ident_to_cppclass : hash_map(ident,expr)
instance ident_to_prototype : hash_map(ident,prototype)
object tocppst = {
type this = struct {
members : ident_to_declvec,
cppclasses : ident_to_cppclass,
objects : ident_set,
globals : global_types,
is_member : bool,
this_ident : ident,
in_class : bool,
proto_only : bool,
subtype_rel : subtypes,
native : bool,
forward : bool,
outputs : vector[expr],
code : vector[cpp.stmt],
counter : pos,
protos : ident_to_prototype,
dead : vector[lvalue_count],
locals : local_tracker,
constructors : ident_set,
dot_rhs : bool
}
action add_member(s:this,namesp:ident,member:decl) returns (s:this) = {
var emp : vector[decl];
s.members := s.members.set(namesp,s.members.get(namesp,emp).append(member));
}
action add_stmt(s:this,code:cpp.stmt) returns (s:this) = {
s.code := s.code.append(code);
}
action get_code(s:this,ann:annot) returns (s:this,res:cpp.stmt) = {
res := cpp.sequence.fold_right(s.code,ann);
s.code := vector[cpp.stmt].empty;
}
action wrap_stmt(s:this,code:cpp.stmt,ann:annot) returns (s:this,res:cpp.stmt) = {
s := s.add_stmt(code);
(s,res) := s.get_code(ann);
}
}
action make_temp(s:tocppst,ty:expr,ann:annot) returns (s:tocppst,res:cpp.expr) = {
var name : str;
name := "__tmp";
name := name.extend(s.counter.to_str);
s.counter := s.counter.next;
res := cpp.symbol.makestr(name,ann);
var vst : cpp.varst;
(vst.vtype._type,s) := fix_variant_type(ty,s);
vst.vtype.name := res;
vst.ann := ann;
var vstt : cpp.stmt := vst;
s := s.add_stmt(vstt);
}
f.a(x).b(y) is f.(a.b). The path of
a.b(x,y) is a.b. On the other hand, (x+y).a is not an lvalue and does
not have a path. For such cases, we return ok = false.
action lvalue_path(s:expr,path:access_path) returns (path:access_path, ok:bool) = {
if s isa symbol {
if s.get_verb = verb.none {
path.elems := path.elems.append(s.get_name);
ok := true
}
} else if s isa app {
if s.is(verb.colon) {
(path,ok) := lvalue_path(s.get_arg(0),path)
} else if s.is(verb.dot) {
(path,ok) := lvalue_path(s.get_arg(0),path);
if ok {
path.elems := path.elems.append(s.get_arg(1).get_arg(0).get_name);
}
} else {
if s.is(verb.none) {
(path,ok) := lvalue_path(s.get_func,path);
}
}
}
}
var dummy_vector_access_path : vector[access_path]
action lvalue_paths(s:expr,paths:vector[access_path],ao:bool)
returns (paths:vector[access_path]) =
{
if s.is(verb.colon) {
paths := lvalue_paths(s.get_arg(0),paths,ao)
} else {
if ~ao {
var path : access_path;
var ok : bool;
(path,ok) := lvalue_path(s,path);
if ok {
paths := paths.append(path);
}
};
if s isa app {
if s.is(verb.dot) {
paths := lvalue_paths(s.get_arg(0),paths,true);
} else {
var args := s.get_args;
var idx := args.begin;
while idx < args.end {
paths := lvalue_paths(args.value(idx),paths,false);
idx := idx.next;
}
}
}
}
}
action is_dead(e:cpp.expr,st:tocppst,cnt:pos) returns (res:bool) = {
var idx := st.dead.begin;
while ~res & idx < st.dead.end {
var d := st.dead.value(idx);
res := cpp.expr.eq(e,d.lvalue) & d.cnt <= cnt;
idx := idx.next
}
}
action name_in_context(name:expr,st:tocppst) returns(name:expr) = {
if st.in_class {
name := symbol.make(name.get_name.get_member,name.get_ann);
};
}
object ident = { ...
action to_cpp(s:this,native:bool) returns (res:cpp.ident)
}
x::y into x__y, which is
needed for name subscripts to make legal C++ identifiers.
action mangle(s:cpp.ident) returns (res:str) = {
if s isa cpp.dotident {
res := mangle(s.get_namesp);
res := res.extend("__");
res := res.extend(s.get_member.to_str);
} else {
res := s.to_str
}
}
native argument is true, we treate the identifier as a native C++
name and do not perform this translation. Also non-native names with
subscripts need to be mangled. For example, x[t] becomes x__t.
action strident_to_cpp(s:strident,native:bool) returns (t:cpp.strident) = {
if ~native & cpp_reserved_word(s.val) {
t.val := "__";
t.val := t.val.extend(s.val);
} else {
t.val := s.val;
};
var idx := s.subscrs.begin;
while idx < s.subscrs.end {
var subs := s.subscrs.value(idx).to_cpp(native);
if native {
t.subscrs := t.subscrs.append(subs);
} else {
t.val := t.val.extend("__");
t.val := t.val.extend(mangle(subs))
};
idx := idx.next
}
}
object strident = { ...
action to_cpp(s:this,native:bool) returns (res:cpp.ident) = {
var x := strident_to_cpp(s,native); # workaround
var y : cpp.ident := x;
res := y;
}
}
object dotident = { ...
action to_cpp(s:this,native:bool) returns (res:cpp.ident) = {
var t : cpp.dotident;
t.namesp := s.namesp.to_cpp(native);
t.member := strident_to_cpp(s.member,native);
res := t
}
}
object expr = { ...
action to_cpp(s:expr,st:tocppst) returns (res:cpp.expr,st:tocppst)
}
.__t to the identifier of the type.
action fix_object_clash(id:ident,st:tocppst) returns (id:ident) = {
if st.objects.mem(id) {
var tok : strident;
tok.val := "__t";
id := dotident.make(id,tok);
}
}
(*this). A few verbs are spelled differently in C++.
We fix this here.
object symbol = { ...
instantiate generic_to_cpp(ivy.symbol,cpp.symbol,cpp.expr)
action to_cpp_int(s:symbol,st:tocppst) returns (res:cpp.symbol,st:tocppst) = {
var vrb := s.get_verb;
if vrb = verb.not {
res.name := cpp.strident.make("!")
} else if vrb = verb.equals {
res.name := cpp.strident.make("==")
} else if vrb = verb.notequals {
res.name := cpp.strident.make("!=")
} else if vrb = verb.ite {
res.name := cpp.strident.make("?")
} else if st.is_member & s.name = st.this_ident & ~st.dot_rhs {
res.name := cpp.strident.make("(*this)");
} else {
var id := s.name;
if ~st.locals.mem(id) {
id := fix_object_clash(s.name,st);
};
res.name := id.to_cpp(st.native);
};
res.vrb := cpp.str_to_verb(res.name.to_str);
}
}
action make_vector_type(ty:expr) returns (ty:expr) = {
var vid : strident;
vid.val := "vector";
vid.subscrs := vid.subscrs.append(ty.get_name);
var name := vid.prefix(strident.make("ivy"));
ty := symbol.make(name,ty.get_ann)
}
action fix_tpl_param(s:expr,st:tocppst) returns (res:cpp.expr,st:tocppst) = {
(res,st) := fix_variant_type(s,st)
}
action make_md_vector_type(dom:vector[expr],rng:expr,st:tocppst)
returns (res:cpp.expr,st:tocppst) =
{
var vid : cpp.strident;
vid.val := "vector";
var crng : cpp.expr;
(crng,st) := fix_tpl_param(rng,st);
vid.subscrs := vid.subscrs.append(crng.get_name);
var idx := dom.begin;
while idx < dom.end {
var dty : cpp.expr;
(dty,st) := fix_tpl_param(dom.value(idx),st);
vid.subscrs := vid.subscrs.append(dty.get_name);
idx := idx.next
};
var name := vid.prefix(cpp.strident.make("ivy"));
res := cpp.symbol.make(name,rng.get_ann)
}
std::vector. This works only when the domain types are
convertible to size_t. We also need at least a hash table type
here for sparse data.
action function_type(ty:expr,st:tocppst) returns (res:cpp.expr,st:tocppst) = {
if ty isa app {
(res,st) := make_md_vector_type(times.unfold_left(ty.get_arg(0)),ty.get_arg(1),st)
} else {
(res,st) := ty.to_cpp(st)
}
}
ivy::from_str(arg)
action make_from_string(ty:cpp.expr,arg:cpp.expr,ann:annot) returns(res:cpp.expr) = {
var id : cpp.strident;
id.val := "from_str";
id.subscrs := id.subscrs.append(ty.get_name);
var name := cpp.dotident.make(cpp.strident.make("ivy"),id);
res := cpp.app.make1(cpp.symbol.make(name,ann),arg,ann);
}
*this. This case requires special
handling for indirect calls.
action is_cpp_this(s : cpp.expr) returns (res:bool) = {
if s isa cpp.symbol {
res := (s.get_name.to_str = "(*this)");
}
}
Ordinary function call: prefix.f(a0..an)
Method call: a0.f(a1..an)
Indirect method call: a0->f(a1..an)
The last case is used if f : (t * ... -> u) is a member function where
type t is a variant type. However, if the first argument is (this) we
do not* use this form, as this is a normal C++ pointer, not a special
Ivy variant pointer.
action make_cpp_call(func:expr, args:vector[cpp.expr], ann:annot, st:tocppst)
returns (res:cpp.expr, st:tocppst) =
{
if func_is_member(func) {
var fid := func.get_arg(0).get_name.to_cpp(false);
var cfunc := cpp.symbol.make(fid.get_member,func.get_ann);
if is_variant_type(get_dom0(func.get_arg(1)),st) & ~is_cpp_this(args.value(0)) {
cfunc := cpp.arrow.make(args.value(0),cfunc,ann)
} else {
cfunc := cpp.dot.make(args.value(0),cfunc,ann)
};
res := cpp.app.make(cfunc,args.segment(1,args.end),ann);
} else {
var cfunc : cpp.expr;
(cfunc,st) := func.to_cpp(st);
res := cpp.app.make(cfunc,args,ann);
}
}
a := a.resize(size(a) - 1);
Here, the call to size has ownership over a which would
prevent resize from side-effecting a. However, since the
call to size executes first, it gives up its ownership before
resize executes, so resize can side-effect a.
action unown_func_args(args:vector[expr],st:tocppst) returns (st:tocppst) = {
var idx := args.begin;
while idx < args.end {
var arg := args.value(idx);
var path : access_path;
var ok : bool;
(path,ok) := lvalue_path(arg,path);
if ok {
st := unown_path(path,st);
};
idx := idx.next
}
}
In-place optimization. There are two cases when a call can modify an argument in-place:
(1) Borrowing. This is an in/out parameter given an lvalue that is returned by the expression and has exactly one alias in the expression.
(2) Return by reference. This is an out parameter given an lvalue that is returned by the expression and has no aliases in the expression.
In both of these cases, we simply pass the lvalue by non-const reference. Any other non-const reference parameter is copied to/from a temporary to preserve value semantics.
If the call is return-by-value, then we return the translated function call. If return-by-reference, we emit the function call and return the lvalue passed to the output parameter, which may be a temporary. If the call is the expression root and it is return-by-value, then we we assign the outputs as a side effect, by copying any temporaries back to the original lvalues, and we return ().
action call_to_cpp (func:expr, inputs:vector[expr], ann:annot, st:tocppst)
returns (res:cpp.expr,st:tocppst) =
{
var proto := st.protos.value(func.get_arg(0).get_name);
var inpvals : vector[cpp.expr];
var idx := proto.args.begin;
while idx < proto.args.end {
var inp : cpp.expr;
var parg := proto.args.value(idx);
if parg.is_input {
var save_outputs := st.outputs;
st.outputs := vector[expr].empty;
(inp,st) := inputs.value(parg.inpos).to_cpp(st);
st.outputs := save_outputs;
};
inpvals := inpvals.append(inp);
idx := idx.next
};
var args : vector[cpp.expr];
idx := proto.args.begin;
var rets : vector[cpp.stmt];
var ret_vals : vector[cpp.expr];
var kdx := inpvals .begin;
while idx < proto.args.end {
var parg := proto.args.value(idx);
var inp := inpvals.value(kdx);
var out : cpp.expr;
if parg.is_output & st.outputs.end > 0 {
(out,st) := st.outputs.value(parg.outpos).to_cpp(st);
if ~is_dead(out,st, 1 if parg.is_input else 0) {
var orig := out;
(st,out) := make_temp(st,parg.name.get_arg(1),ann);
rets := rets.append(cpp.asgn.make(orig,out,ann));
}
} else if parg.is_ref & ~parg.is_const {
if is_dead(inp,st,1) {
out := inp; # if input is a dead lvalue, side-effect it
} else {
(st,out) := make_temp(st,parg.name.get_arg(1),ann);
};
ret_vals := ret_vals.append(out);
};
if parg.is_input & parg.is_ref & ~parg.is_const & ~cpp.expr.eq(inp,out) {
st := st.add_stmt(cpp.asgn.make(out,inp,ann));
inp := out;
} else if parg.is_output {
inp := out
};
args := args.append(inp);
idx := idx.next;
kdx := kdx.next
};
(res,st) := make_cpp_call(func,args,ann,st);
st := unown_func_args(inputs,st);
if ret_vals.end > 0 | rets.end > 0 | ~proto.has_ret {
st := st.add_stmt(cpp.asgn.make(cpp.empty.make(ann),res,ann));
var jdx := rets.begin;
while jdx < rets.end {
st := st.add_stmt(rets.value(jdx));
jdx := jdx.next;
};
res := cpp.comma.fold_left(ret_vals,ann);
}
}
action make_cast_to_bool(s:cpp.expr) returns (s:cpp.expr) = {
s := cpp.app.make1(cpp.symbol.makestr("ivy::native_bool",s.get_ann),s,s.get_ann);
}
action make_isa(s:cpp.expr,ty:cpp.expr) returns (s:cpp.expr) = {
s := cpp.app.make1(cpp.symbol.makestr1("ivy::isa",ty.get_name,s.get_ann),s,s.get_ann);
}
object app = { ...
action to_cpp(s:this,st:tocppst) returns (res:cpp.expr,st:tocppst) = {
if s.is(verb.colon) {
ivy::from_str to convert to type t.
If a symbol is an action and its type is not a function type, we have to add () for C++.
var arg := s.args.value(0);
(res,st) := arg.to_cpp(st);
if (arg isa symbol) {
if arg.get_verb = verb.truev | arg.get_verb = verb.falsev {
res := cpp.app.make1(cpp.symbol.makestr("ivy::native_bool",s.get_ann),res,s.get_ann);
} else if arg.get_verb = verb.numeral | st.constructors.mem(arg.get_name) {
var ty : cpp.expr;
(ty,st) := s.args.value(1).to_cpp(st);
res := cpp.app.make1(ty,res,s.get_ann);
} else if arg.get_verb = verb.string {
var ty : cpp.expr;
(ty,st) := s.args.value(1).to_cpp(st);
res := make_from_string(ty,res,s.get_ann);
} else if st.globals.is_action.mem(arg.get_name) {
if ~(s.args.value(1).is(verb.arrow)) {
var args : vector[cpp.expr];
res := cpp.app.make(res,args,res.get_ann);
}
}
}
} else if s.is(verb.arrow) {
var tmp : expr := s; # workaround
(res,st) := function_type(tmp,st)
} else if s.is(verb.isav) {
x isa t is translated to x.isa()
var arg0 : cpp.expr;
var arg1 : cpp.expr;
(arg0,st) := s.args.value(0).to_cpp(st);
(arg1,st) := s.args.value(1).to_cpp(st);
if is_cpp_this(arg0) {
res := make_isa(arg0,arg1);
} else {
var isam : cpp.strident;
isam.val := "isa";
isam.subscrs := isam.subscrs.append(arg1.get_name);
var id : cpp.ident := isam;
res := cpp.symbol.make(id,s.ann);
res := cpp.dot.make(arg0,res,s.ann);
res := cpp.app.make0(res,s.ann);
}
} else if s.func.is(verb.colon) & s.func.get_arg(0).get_verb = verb.castv {
var ty := s.func.get_arg(1).get_arg(1); # the range type of the cast operator
(res,st) := upcast(ty,s.args.value(0),st)
} else {
var func : expr;
var args : vector[expr];
(func,args) := get_app(s);
if st.globals.is_action.mem(func.get_arg(0).get_name) {
(res,st) := call_to_cpp(func,args,s.ann,st);
} else {
var capp : cpp.app;
(capp.func,st) := s.func.to_cpp(st);
{
var save_outputs := st.outputs;
st.outputs := vector[expr].empty;
var idx := s.args.begin;
while idx < s.args.end {
var arg : cpp.expr;
if s.func.get_verb = verb.dot & idx = 1 { st.dot_rhs := true; };
(arg,st) := s.args.value(idx).to_cpp(st);
st.dot_rhs := false;
capp.args := capp.args.append(arg);
idx := idx.next
};
st.outputs := save_outputs;
if capp.is(cpp.verb.ite) {
var tmp := capp.args.value(0);
capp.args := capp.args.set(0,capp.args.value(1));
capp.args := capp.args.set(1,tmp);
};
if s.func.get_verb = verb.dot {
var lhs := s.args.value(0);
if lhs.is(verb.colon) {
var ty := lhs.get_arg(1);
if is_variant_type(ty,st) & ~is_cpp_this(capp.args.value(0)){
capp.func := cpp.symbol.makestr("->",capp.func.get_ann)
}
}
}
};
capp.ann := s.ann;
res := capp;
st := unown_func_args(s.args,st);
if s.is(verb.dot) { var lhs := s.args.value(0); var ty := lhs.get_type; var fid := s.args.value(1).get_arg(0).get_name.prefix(ty.get_name); if st.globals.is_action.mem(fid) { if ~(s.args.value(1).get_arg(1).get_arg(1).is(verb.arrow)) { var args : vector[cpp.expr]; res := cpp.app.make(res,args,res.get_ann); } } }
}
}
}
}
object stmt = { ...
action to_cpp(s:stmt,st:tocppst) returns (res:cpp.stmt,st:tocppst)
}
object skipst = { ...
instantiate generic_to_cpp(ivy.skipst,cpp.skipst,cpp.stmt)
action to_cpp_int(s:ivy.skipst,st:tocppst) returns (res:cpp.skipst,st:tocppst) = {
}
}
action upcast(lhsty:expr,rhs:expr,st:tocppst) returns (res:cpp.expr,st:tocppst) = {
(res,st) := rhs.to_cpp(st);
if false & rhs.is(verb.colon) {
var rhsty := rhs.get_arg(1);
if st.subtype_rel.is_subtype(rhsty,lhsty) {
var crhsty : cpp.expr;
(crhsty,st) := rhsty.to_cpp(st);
res := cpp.new.make(cpp.app.make1(crhsty,res,rhs.get_ann),rhs.get_ann);
var clhsty : cpp.expr;
(clhsty,st) := fix_variant_type(lhsty,st);
res := cpp.app.make1(clhsty,res,rhs.get_ann)
}
}
}
action unown_path(path:access_path,st:tocppst) returns (st:tocppst) = {
var idx := st.dead.begin;
while idx < st.dead.end {
var lc := st.dead.value(idx);
if path_may_alias(path,lc.path) {
lc.cnt := lc.cnt.prev;
st.dead := st.dead.set(idx,lc);
};
idx := idx.next
}
}
action kill_lvalue(e:expr,st:tocppst,paths:vector[access_path]) returns (st:tocppst) = {
var path : access_path;
var ok : bool;
(path,ok) := lvalue_path(e,path);
if ok {
var alias_count : pos := 0;
var idx := paths.begin;
while idx < paths.end {
if path_may_alias(path,paths.value(idx)) {
alias_count := alias_count.next;
};
idx := idx.next
};
var lc : lvalue_count;
(lc.lvalue,st) := e.to_cpp(st);
lc.path := path;
lc.cnt := alias_count;
st.dead := st.dead.append(lc);
}
}
action kill_lvalues(es:vector[expr],st:tocppst,paths:vector[access_path])
returns (st:tocppst) =
{
var idx := es.begin;
while idx < es.end {
st := kill_lvalue(es.value(idx),st,paths);
idx := idx.next
}
}
__upcast method of
the object to get a C-o-W pointer to the object. Otherwise C++
will slice the object.
action fix_variant_arg(s:expr,rhs:cpp.expr,st:tocppst) returns (rhs:cpp.expr) = {
if ~s.is_typed(verb.castv) & is_cpp_this(rhs) {
var ty := s.get_type;
if is_variant_type(ty,st) {
var ann := rhs.get_ann;
rhs := cpp.dot.make(rhs,cpp.app.make0(cpp.symbol.makestr("__upcast",ann),ann),ann);
}
}
}
st.outputs. We then translate the
rhs. If the rhs is a call to an action with return by reference,
then this will have the side effect of assigning the outputs.
If so, the return value will be (). Otherwise, we generate an
assigment of the return values to the outputs. Finally, we
assemble the generated code and restore the context.
object asgn = { ...
action to_cpp(s:this,st:tocppst) returns (resd:cpp.stmt,st:tocppst) = {
var paths : vector[access_path];
paths := lvalue_paths(s.rhs,paths,false);
var res : cpp.asgn;
res.ann := s.ann;
(res.lhs,st) := s.lhs.to_cpp(st);
st.outputs := comma.unfold_left(s.lhs);
st := kill_lvalues(st.outputs,st,paths);
(res.rhs,st) := s.rhs.to_cpp(st);
if res.rhs.get_verb ~= cpp.verb.empty {
res.rhs := fix_variant_arg(s.rhs,res.rhs,st);
st := st.add_stmt(res); # Don't generate () = ();
};
(st,resd) := st.get_code(s.ann);
st.outputs := vector[expr].empty;
st.dead := vector[lvalue_count].empty;
}
}
object sequence = { ...
action to_cpp(s:this,st:tocppst) returns (resd:cpp.stmt,st:tocppst) = {
var res : cpp.sequence;
res.ann := s.ann;
st.locals := st.locals.push_stmt(s.lhs);
(res.lhs,st) := s.lhs.to_cpp(st);
(res.rhs,st) := s.rhs.to_cpp(st);
st.locals := st.locals.pop;
resd := res;
}
}
if f(x) (s1) else {s2}
where f(x) is return-by-reference. This will translate to:
bool __tmpXXX = x;
f(__tmpXXX);
if (__tmpXXX) {s1} else {s2}
object ifst = { ...
action to_cpp(s:this,st:tocppst) returns (resd:cpp.stmt,st:tocppst) = {
var res : cpp.ifst;
res.ann := s.ann;
(res.cond,st) := s.cond.to_cpp(st);
var code := st.code; # side effects of cond evaluation
st.code := vector[cpp.stmt].empty;
(res.thenst,st) := s.thenst.to_cpp(st);
(res.elsest,st) := s.elsest.to_cpp(st);
resd := res;
st.code := code;
(st,resd) := st.wrap_stmt(resd,s.ann);
}
}
while f(x) {s1}
where f(x) is return-by-reference. This will translate to:
while (true) {
bool __tmpXXX = x;
f(__tmpXXX);
if (!__tmpXXX) break;
s1
}
object whilest = { ...
action to_cpp(s:this,st:tocppst) returns (resd:cpp.stmt,st:tocppst) = {
var res : cpp.whilest;
res.ann := s.ann;
(res.cond,st) := s.cond.to_cpp(st);
var code := st.code; # side effects of cond evaluation
st.code := vector[cpp.stmt].empty;
(res.body,st) := s.body.to_cpp(st);
if code.end > 0 {
st.code := code;
var brkcond := cpp.not.make(res.cond,s.ann);
var brkif := cpp.breakst.make(s.ann);
var brkelse := cpp.skipst.make(s.ann);
var brkst := cpp.ifst.make(brkcond,brkif,brkelse,s.ann);
st := st.add_stmt(brkst);
res.cond := cpp.symbol.makestr("true",s.ann);
(st,res.body) := st.wrap_stmt(res.body,s.ann);
};
resd := res;
(st,resd) := st.wrap_stmt(resd,s.ann);
}
}
object varst = { ...
instantiate generic_to_cpp(ivy.varst,cpp.varst,cpp.stmt)
action to_cpp_int(s:varst,st:tocppst) returns (res:cpp.varst,st:tocppst) = {
(res.vtype._type,st) := fix_variant_type(s.name.get_arg(1),st);
(res.vtype.name,st) := s.name.get_arg(0).to_cpp(st);
}
}
object decl = { ...
action to_cpp(s:this,st:tocppst) returns (res:cpp.decl,st:tocppst)
action reg_member(s:this,st:tocppst) returns (st:tocppst)
action emitted(s:this,st:tocppst) returns (res:bool) = {
res := true;
}
action record_prototypes(s:this,st:tocppst) returns (st:tocppst)
}
action full_action_name(name:expr,is_member : bool,st:tocppst)
returns (res:cpp.expr,st:tocppst) =
{
if is_member {
var pref : ident := name.get_name.get_namesp;
var clsid := fix_object_clash(pref,st);
var funid := name.get_name.get_member.prefix(clsid);
res := cpp.symbol.make(funid.to_cpp(false),name.get_ann);
} else {
(res,st) := name.to_cpp(st)
}
}
action is_input_param(s:actdc,p:expr) returns (res:bool) = {
var idx := s.inputs.begin;
while ~res & idx < s.inputs.end {
res := expr.eq(s.inputs.value(idx),p);
idx := idx.next
}
}
object actdc = { ...
action record_prototypes(s:this,st:tocppst) returns (st:tocppst) = {
var proto := s.get_proto;
st.protos := st.protos.set(s.name.get_name,proto);
}
*this.
action to_cpp(s:this,st:tocppst) returns (resd:cpp.decl,st:tocppst) = {
var res : cpp.funcdecl;
res.ann := s.ann;
var proto := st.protos.value(s.name.get_name);
var has_output := proto.has_ret;
if has_output {
(res.ftype.base._type,st) := fix_variant_type(s.outputs.value(proto.ret.outpos).get_arg(1),st);
} else {
res.ftype.base._type := cpp.voidtype(s.ann);
};
var cprot := st.proto_only;
var is_member := s.is_member;
var full_name : cpp.expr;
(full_name,st) := full_action_name(s.name,is_member,st);
res.ftype.base.name := member_name(full_name) if (cprot | st.in_class) else full_name;
var idx := proto.args.begin;
st.is_member := is_member;
if st.is_member {
idx := idx.next; # skip the first input "this" if a member
st.this_ident := proto.args.value(0).name.get_arg(0).get_name;
res.ftype.is_const := proto.args.value(0).is_const;
if cprot & is_variant_type(proto.args.value(0).name.get_arg(1),st) {
res.is_virtual := true
}
};
st.locals := st.locals.push;
while idx < proto.args.end {
var parg := proto.args.value(idx);
var arg := parg.name;
var argt : cpp.simpletype;
argt.is_const := parg.is_const;
argt.is_ref := parg.is_ref;
(argt._type,st) := fix_variant_type(arg.get_arg(1),st);
st.locals := st.locals.add_var(arg);
(argt.name,st) := arg.get_arg(0).to_cpp(st);
res.ftype.args := res.ftype.args.append(argt);
idx := idx.next
};
res.has_body := ~cprot;
if res.has_body {
if s.has_body {
var body :cpp.stmt;
(body,st) := s.body.to_cpp(st);
res.body := body;
} else {
res.body := cpp.skipst.make(s.ann);
};
if has_output {
var rvar : cpp.expr;
(rvar,st) := s.outputs.value(0).get_arg(0).to_cpp(st);
var ret := cpp.retst.make(rvar,s.ann);
res.body := cpp.sequence.make(res.body,ret,s.ann);
if ~is_input_param(s,s.outputs.value(0)) {
var vs := cpp.varst.make(res.ftype.base._type,rvar,s.ann);
res.body := cpp.sequence.make(vs,res.body,s.ann);
}
}
};
st.locals := st.locals.pop;
st.is_member := false;
resd := res;
if ~st.in_class & cprot {
resd := add_namespaces(resd,s.name.get_name);
}
}
action reg_member(s:this,st:tocppst) returns (st:tocppst) = {
if s.is_member {
var actd := s;
actd.has_body := false;
st := st.add_member(s.member_type,actd);
}
}
action emitted(s:this,st:tocppst) returns (res:bool) = {
res := (~s.is_member | ~st.proto_only) & s.kind ~= action_kind.external;
}
}
object initdc = { ...
action emitted(s:this,st:tocppst) returns (res:bool) = {
res := false;
}
}
action add_namespaces_rec(d:cpp.decl,id:ident) returns(d:cpp.decl) = {
var nd : cpp.namespacedecl;
nd.ann := d.get_ann;
var name := id.get_member if (id isa dotident) else id;
nd.name := cpp.symbol.make(name.to_cpp(false),d.get_ann);
nd.members := nd.members.append(d);
d := nd;
if id isa dotident {
d := add_namespaces_rec(d,id.get_namesp);
}
}
action add_namespaces(d:cpp.decl,id:ident) returns(d:cpp.decl) = {
if id isa dotident {
d := add_namespaces_rec(d,id.get_namesp);
}
}
action member_name(s:cpp.expr) returns(s:cpp.expr) = {
if (s.get_name) isa cpp.dotident {
s := cpp.symbol.make(s.get_name.get_member,s.get_ann)
}
}
ivy.tpl[ty].
action make_std_tpl(tpl:str, ty:str, ann:annot) returns (res:cpp.expr) = {
var vid : cpp.strident;
vid.val := tpl;
vid.subscrs := vid.subscrs.append(cpp.strident.make(ty));
var tname := cpp.dotident.make(cpp.strident.make("ivy"),vid);
res := cpp.symbol.make(tname,ann);
}
__enum_name where `name is the Ivy name.
action enum_name(name : cpp.expr) returns (res:str) = {
res := "__enum_";
res := res.extend(name.get_name.to_str)
}
t, we declared a C++ enum
named __enum_t. We then instantiate a template
ivy::native_enum<__enum_t> defined in the standard header.
This wraps the enum in a class with the necessary standard
traits. The form of an enum type name in the C++ code is:
enum __enum_name {elem1,...elemN};
struct name : ivy::native_enum< __enum_name > {
name() : ivy::native_enum< __enum_name >() {}
name(long long value) :
name(__enum_name value) : ivy::native_enum< __enum_name >(value) {}
};
Note we have to promote the constructors of the base class to
the derived class. The parameter sd is the struct declaration.
This action prepends the C++ enum declaration.
action enum_to_cpp(name:cpp.expr,spec:typespec,sd:cpp.decl,st:tocppst)
returns(res:cpp.decl,st:tocppst) =
{
__enum_t.
var ed : cpp.enumdecl;
ed.ann := name.get_ann;
ed.name := cpp.symbol.makestr(enum_name(name),ed.ann);
var cnstrs := spec.get_elems;
var idx := cnstrs.begin;
while idx < cnstrs.end {
var e : cpp.expr;
(e,st) := cnstrs.value(idx).to_cpp(st);
ed.elems := ed.elems.append(member_name(e));
idx := idx.next
};
var gd : cpp.groupdc;
gd.ann := name.get_ann;
gd.decls := gd.decls.append(ed);
gd.decls := gd.decls.append(sd);
res := gd;
}
(1) A default constructor (2) A constructor from (signed or unsigned) long long. (3) A conversion to size_t (4) A static predicate __is_seq (5) A == overload (6) A != overload (7) A predicate __is_zero (8) A class __hash of hashers
Here, we add the standard traits to a C++ structure.
Make a C++ constructor for a struct Default constructor does nothing: name() {};
action make_cpp_cons(t:cpp.structdecl) returns (s:cpp.funcdecl) = {
s.ftype.base.name := t.name;
s.has_body := true;
s.body := cpp.skipst.make(t.ann);
}
action make_virt_destr(t:cpp.structdecl) returns (s:cpp.funcdecl) = {
var name : str;
name := name.extend("~");
name := name.extend(t.name.get_name.to_str);
s.ftype.base.name := cpp.symbol.makestr(name,t.ann);
s.has_body := true;
s.body := cpp.skipst.make(t.ann);
s.is_virtual := true;
}
action make_upcast_method(t:cpp.structdecl) returns (s:cpp.funcdecl) = {
s.ftype.base.name := cpp.symbol.makestr("__upcast",t.ann);
var ty := t.super if t.has_super else t.name;
s.ftype.base._type := cpp.symbol.makestr1("ivy::ptr",ty.get_name,t.ann);
s.has_body := true;
s.body := cpp.retst.make(cpp.symbol.makestr("(*this)",t.ann),t.ann);
s.is_virtual := true;
s.ftype.is_const := true;
}
action add_numeric_cons(s:cpp.structdecl) returns (s:cpp.structdecl) = {
var ncons := make_cpp_cons(s);
var nconsarg0 : cpp.simpletype;
nconsarg0._type := cpp.symbol.makestr("long long",s.ann);
nconsarg0.name := cpp.symbol.makestr("value",ncons.ann);
ncons.ftype.args := ncons.ftype.args.append(nconsarg0);
s.members := s.members.append(ncons);
}
action add_sizet_conv(s:cpp.structdecl) returns (s:cpp.structdecl) = {
var tosizet : cpp.funcdecl;
tosizet.ftype.base.name := cpp.symbol.makestr("operator std::size_t",s.ann);
tosizet.ftype.is_const := true;
tosizet.has_body := true;
tosizet.body := cpp.retst.make(cpp.symbol.makestr("0",s.ann),s.ann);
s.members := s.members.append(tosizet);
}
action add_is_seq_pred(s:cpp.structdecl) returns (s:cpp.structdecl) = {
var isseq : cpp.funcdecl;
isseq.ftype.base._type := cpp.symbol.makestr("bool",s.ann);
isseq.ftype.base.name := cpp.symbol.makestr("__is_seq",s.ann);
isseq.is_static := true;
isseq.has_body := true;
isseq.body := cpp.retst.make(cpp.symbol.makestr("false",s.ann),s.ann);
s.members := s.members.append(isseq);
}
action add_eq_pred(s:cpp.structdecl) returns (s:cpp.structdecl) = {
var eq : cpp.funcdecl;
eq.ftype.base._type := cpp.symbol.makestr("ivy::native_bool",s.ann);
eq.ftype.base.name := cpp.symbol.makestr("operator ==",s.ann);
eq.ftype.is_const := true;
var eqarg0 : cpp.simpletype;
eqarg0._type := s.name;
eqarg0.is_const := true;
eqarg0.is_ref := true;
eqarg0.name := cpp.symbol.makestr("other",eq.ann);
eq.ftype.args := eq.ftype.args.append(eqarg0);
eq.has_body := true;
var eqs : vector[cpp.expr];
var idx := s.members.begin;
while idx < s.members.end {
var d := s.members.value(idx);
if d isa cpp.vardecl {
var f0 := d.get_name;
var f1 := cpp.dot.make(eqarg0.name,d.get_name,s.ann);
var e := cpp.equals.make(f0,f1,s.ann);
eqs := eqs.append(e);
};
idx := idx.next
};
var retexp := cpp.symbol.makestr("true",s.ann);
if eqs.end > 0 {
retexp := cpp.and.fold_left(eqs,s.ann);
};
eq.body := cpp.retst.make(retexp,s.ann);
s.members := s.members.append(eq);
}
action add_diseq_pred(s:cpp.structdecl) returns (s:cpp.structdecl) = {
var diseq : cpp.funcdecl;
diseq.ftype.base._type := cpp.symbol.makestr("ivy::native_bool",s.ann);
diseq.ftype.base.name := cpp.symbol.makestr("operator !=",s.ann);
diseq.ftype.is_const := true;
var diseqarg0 : cpp.simpletype;
diseqarg0._type := s.name;
diseqarg0.is_const := true;
diseqarg0.is_ref := true;
diseqarg0.name := cpp.symbol.makestr("other",diseq.ann);
diseq.ftype.args := diseq.ftype.args.append(diseqarg0);
diseq.has_body := true;
var f0 := cpp.symbol.makestr("(*this)",s.ann);
var diseqret := cpp.not.make(cpp.equals.make(f0,diseqarg0.name,s.ann),s.ann);
diseq.body := cpp.retst.make(diseqret,s.ann);
s.members := s.members.append(diseq);
}
action add_is_zero_pred(s:cpp.structdecl) returns (s:cpp.structdecl) = {
var iszero : cpp.funcdecl;
iszero.ftype.base._type := cpp.symbol.makestr("bool",s.ann);
iszero.ftype.base.name := cpp.symbol.makestr("__is_zero",s.ann);
iszero.ftype.is_const := true;
iszero.has_body := true;
var iszeros : vector[cpp.expr];
var idx := s.members.begin;
while idx < s.members.end {
var d := s.members.value(idx);
if d isa cpp.vardecl {
var f0 := d.get_name;
var f1 := cpp.dot.make(d.get_name,iszero.ftype.base.name,s.ann);
var f2 : vector[cpp.expr];
var e := cpp.app.make(f1,f2,s.ann);
iszeros := iszeros.append(e);
};
idx := idx.next
};
var iszeroret := cpp.symbol.makestr("true",s.ann);
if iszeros.end > 0 {
iszeroret := cpp.and.fold_left(iszeros,s.ann);
};
iszero.body := cpp.retst.make(iszeroret,s.ann);
s.members := s.members.append(iszero);
}
action add_hasher(s:cpp.structdecl) returns (s:cpp.structdecl) = {
var hashstr : cpp.structdecl;
hashstr.ann := s.ann;
hashstr.name := cpp.symbol.makestr("__hash",s.ann);
hashstr.has_members := true;
var hash : cpp.funcdecl;
hash.ftype.base._type := cpp.symbol.makestr("std::size_t",s.ann);
hash.ftype.base.name := cpp.symbol.makestr("operator ()",s.ann);
hash.ftype.is_const := true;
var hasharg0 : cpp.simpletype;
hasharg0._type := s.name;
hasharg0.is_const := true;
hasharg0.is_ref := true;
hasharg0.name := cpp.symbol.makestr("x",hash.ann);
hash.ftype.args := hash.ftype.args.append(hasharg0);
hash.has_body := true;
var hashs : vector[cpp.expr];
var idx := s.members.begin;
while idx < s.members.end {
var d := s.members.value(idx);
if d isa cpp.vardecl {
var f0 := d.get_name;
var f1 := hashstr.name.prefix(d.get_type.get_name);
var f2 := cpp.dot.make(hasharg0.name,f0,s.ann);
var e := cpp.app.make1(cpp.app.make0(f1,s.ann),f2,s.ann);
hashs := hashs.append(e);
};
idx := idx.next
};
var hashret := cpp.symbol.makestr("0",s.ann);
if hashs.end > 0 {
hashret := cpp.plus.fold_left(hashs,s.ann);
};
hash.body := cpp.retst.make(hashret,s.ann);
hashstr.members := hashstr.members.append(hash);
s.members := s.members.append(hashstr);
}
action add_standard_traits(s:cpp.structdecl) returns (s:cpp.structdecl) = {
s.members := s.members.append(make_cpp_cons(s));
s := add_numeric_cons(s);
s := add_sizet_conv(s);
s := add_is_seq_pred(s);
s := add_eq_pred(s);
s := add_diseq_pred(s);
s := add_is_zero_pred(s);
s := add_hasher(s);
}
action add_virtual_destructor(s:cpp.structdecl) returns (s:cpp.structdecl) = {
s.members := s.members.append(make_upcast_method(s));
s.members := s.members.append(make_virt_destr(s));
}
action add_upcast_method(s:cpp.structdecl) returns (s:cpp.structdecl) = {
s.members := s.members.append(make_upcast_method(s));
}
derived(t value) : base(value) {}
derived(const t& value) : base(value) {}
The latter is produced if the constref parameter is true
action add_derived_cons(s:cpp.structdecl,t:cpp.expr,constref:bool) returns (s:cpp.structdecl) = {
var dcons := make_cpp_cons(s);
var dconsarg0 : cpp.simpletype;
dconsarg0._type := t;
dconsarg0.name := cpp.symbol.makestr("value",dcons.ann);
dconsarg0.is_const := constref;
dconsarg0.is_ref := constref;
dcons.ftype.args := dcons.ftype.args.append(dconsarg0);
dcons.ftype.has_initializer := true;
dcons.ftype.initializer := cpp.app.make1(s.super,dconsarg0.name,s.ann);
s.members := s.members.append(dcons);
}
operator base () const { return (*this); }
action add_base_conversion(s:cpp.structdecl) returns (s:cpp.structdecl) = {
var abc : cpp.funcdecl;
var op : str;
op := op.extend("operator ");
op := op.extend(s.super.get_name.to_str);
abc.ftype.base.name := cpp.symbol.makestr(op,s.ann);
abc.ftype.is_const := true;
abc.has_body := true;
abc.body := cpp.retst.make(cpp.symbol.makestr("(*this)",s.ann),s.ann);
s.members := s.members.append(abc);
}
struct ivytype : cpptype { ivytype(long long value) : cpptype(value) {} ivytype(const cpptype &x) : cpptype(x) {} operator cpptype() { return (*this); } };
That is, we provide a constructor from the base class and a conversion to the
base class. THe latte is used by the C++ compiler when interpreting an expression
like x+y, where x and y are of the derived class. Since the only available
conversion is to the base class, the + operator of the base class is used. On
assignment, the constructor is used.
action add_derived_traits(s:cpp.structdecl) returns (s:cpp.structdecl) = {
s.members := s.members.append(make_cpp_cons(s));
s := add_derived_cons(s,cpp.symbol.makestr("long long",s.ann),false);
s := add_derived_cons(s,s.super,true);
s := add_base_conversion(s);
}
interpret declarations.
They need special processing, because we do not want to avoid clashes
with C++ reserved words.
action native_type_to_cpp(ty:expr,st:tocppst) returns (res:cpp.expr,st:tocppst) = {
st.native := true;
(res,st) := ty.to_cpp(st);
st.native := false;
}
object typedc = { ...
action to_cpp(s:this,st:tocppst) returns (resd:cpp.decl,st:tocppst) = {
var res : cpp.structdecl;
res.ann := s.ann;
var cls : cpp.expr;
(cls,st) := s.sort.to_cpp(st);
res.name := member_name(cls);
if ~st.forward {
if s.has_super {
res.has_super := true;
(res.super,st) := s.super.to_cpp(st)
} else if s.has_spec & (s.spec isa enumspec) {
res.has_super := true;
res.super := make_std_tpl("native_enum",enum_name(res.name),res.ann);
} else if st.cppclasses.mem(s.sort.get_name) {
res.has_super := true;
var itype := st.cppclasses.value(s.sort.get_name);
(res.super,st) := native_type_to_cpp(itype,st);
};
res.has_members := true; # This is not a forward declaration
var members := st.members.get(s.sort.get_name,vector[decl].empty);
var idx := members.begin;
st.in_class := true;
while idx < members.end {
var d : cpp.decl;
(d,st) := members.value(idx).to_cpp(st);
res.members := res.members.append(d);
idx := idx.next
};
st.in_class := false;
if res.has_super & ~s.has_super { # derived from native
res := add_derived_traits(res);
} else {
res := add_standard_traits(res);
};
if s.has_super {
res := add_upcast_method(res);
};
if is_variant_type(s.sort,st) {
res := add_virtual_destructor(res);
};
resd := res;
if s.has_spec & (s.spec isa enumspec) {
(resd,st) := enum_to_cpp(res.name,s.spec,resd,st);
};
} else {
resd := res;
};
resd := add_namespaces(resd,fix_object_clash(s.sort.get_name,st));
}
import action tocpp_show_str(s:str)
action reg_member(s:this,st:tocppst) returns (st:tocppst) = {
if s.has_spec {
if s.spec isa enumspec {
var conss := s.spec.get_elems;
var idx := conss.begin;
while idx < conss.end {
var cons := conss.value(idx);
st.constructors := st.constructors.set(cons.get_name,true);
idx := idx.next
}
}
}
}
}
action is_variant_type(t:expr,st:tocppst) returns (res:bool) = {
if t isa symbol {
if st.subtype_rel.subtypes_of.mem(t.get_name) {
res := true;
}
}
}
t becomes ivy.ptr.
action fix_variant_type(t:expr,st:tocppst) returns (res:cpp.expr,st:tocppst) = {
if is_variant_type(t,st) {
var cppty : cpp.expr;
(cppty,st) := t.to_cpp(st);
var s : cpp.strident;
s.val := "ptr";
s.subscrs := s.subscrs.append(cppty.get_name);
res := cpp.symbol.make(s.prefix(cpp.strident.make("ivy")),t.get_ann);
} else {
(res,st) := t.to_cpp(st);
}
}
object decl = { ...
action func_to_action(s : this) returns (res : decl) = {
res := s;
}
}
object vardc = { ...
action func_to_action(s : vardc) returns (dres : decl) = {
if s.has_def {
var res : actdc;
res.ann := s.ann;
var lhs := s.typing;
var ty : expr;
if lhs isa app {
res.name := lhs.get_func.get_arg(0);
res.inputs := lhs.get_args;
ty := lhs.get_func.get_arg(1).get_arg(1);
} else if lhs isa symbol {
res.name := lhs.get_arg(0);
ty := lhs.get_arg(1);
};
var retv := symbol.makestr("__out",s.ann);
var retty := colon.make(retv,ty,s.ann);
res.outputs := res.outputs.append(retty);
res.has_body := true;
res.body := asgn.make(retty,s.def,s.ann);
dres := res;
} else {
dres := s;
}
}
}
object vardc = { ...
action to_cpp(s:this,st:tocppst) returns (resd:cpp.decl,st:tocppst) = {
var res : cpp.vardecl;
res.ann := s.ann;
var ty := s.typing.get_arg(1);
if s.is_destructor {
ty := ty.curry.get_arg(1);
};
(res.vtype._type,st) := fix_variant_type(ty,st);
var name : cpp.expr;
(name,st) := s.typing.get_arg(0).to_cpp(st);
res.vtype.name := member_name(name);
resd := res; # workaround
if ~st.in_class {
resd := add_namespaces(resd,s.typing.get_arg(0).get_name);
}
}
action reg_member(s:this,st:tocppst) returns (st:tocppst) = {
if s.is_destructor {
var dom := times.unfold_left(s.typing.get_arg(1).get_arg(0));
var type_ident := dom.value(0).get_name;
st := st.add_member(type_ident,s);
}
}
action emitted(s:this,st:tocppst) returns (res:bool) = {
res := ~s.is_destructor;
}
}
object header = { ...
instantiate generic_to_cpp(this,cpp.header,cpp.decl)
action to_cpp_int(s:this,st:tocppst) returns (res:cpp.header,st:tocppst) = {
res.filename := s.filename
}
}
object interpdc = { ...
action reg_member(s:this,st:tocppst) returns (st:tocppst) = {
st.cppclasses := st.cppclasses.set(s.itype.get_name,s.ctype);
}
action emitted(s:this,st:tocppst) returns (res:bool) = {
res := false;
}
}
object objectdc = { ...
action reg_member(s:this,st:tocppst) returns (st:tocppst) = {
st.objects := st.objects.set(s.name.get_name,true);
}
action emitted(s:this,st:tocppst) returns (res:bool) = {
res := false;
}
}
object prog = { ...
action to_cpp(sp:this) returns (res:cpp.prog) = {
var s := sp; # workaround
var st:tocppst;
st.subtype_rel := s.get_subtypes;
var idx := s.decls.begin;
while idx < s.decls.end {
var d := s.decls.value(idx).func_to_action;
s.decls := s.decls.set(idx,d);
idx := idx.next
};
st.globals := s.get_global_types(true);
idx := s.decls.begin;
while idx < s.decls.end {
st := s.decls.value(idx).reg_member(st);
idx := idx.next
};
st.forward := true;
idx := s.decls.begin;
while idx < s.decls.end {
var ivyd := s.decls.value(idx);
if ivyd isa typedc {
var d : cpp.decl;
(d,st) := ivyd.to_cpp(st);
res.decls := res.decls.append(d);
};
idx := idx.next
};
st.forward := false;
idx := s.decls.begin;
while idx < s.decls.end {
st := s.decls.value(idx).record_prototypes(st);
idx := idx.next
};
st.proto_only := true;
idx := s.decls.begin;
while idx < s.decls.end {
if s.decls.value(idx).emitted(st) {
var d : cpp.decl;
(d,st) := s.decls.value(idx).to_cpp(st);
res.decls := res.decls.append(d);
};
idx := idx.next
};
st.proto_only := false;
idx := s.decls.begin;
while idx < s.decls.end {
var ivyd := s.decls.value(idx);
if (ivyd isa actdc) & s.decls.value(idx).emitted(st) {
var d : cpp.decl;
(d,st) := ivyd.to_cpp(st);
res.decls := res.decls.append(d);
};
idx := idx.next
};
var main : cpp.funcdecl;
main.ftype.base._type := cpp.inttype(main.ann);
main.ftype.base.name := cpp.symbol.makestr("main",main.ann);
var mainarg0 : cpp.simpletype;
var mainarg1 : cpp.simpletype;
mainarg0._type := cpp.inttype(main.ann);
mainarg0.name := cpp.symbol.makestr("argc",main.ann);
mainarg1._type := cpp.symbol.makestr("char **",main.ann);
mainarg1.name := cpp.symbol.makestr("argv",main.ann);
main.ftype.args := main.ftype.args.append(mainarg0);
main.ftype.args := main.ftype.args.append(mainarg1);
main.has_body := true;
main.body := cpp.retst.make(cpp.symbol.makestr("0",main.ann),main.ann);
idx := s.decls.end;
while idx > s.decls.begin {
idx := idx.prev;
var decl := s.decls.value(idx);
if decl isa initdc {
var cbody : cpp.stmt;
(cbody,st) := decl.get_body.to_cpp(st);
main.body := cpp.sequence.make(cbody,main.body,decl.get_ann);
}
};
var argv := cpp.asgn.make(cpp.symbol.makestr("ivy::__argv",main.ann),cpp.symbol.makestr("argv",main.ann),main.ann);
main.body := cpp.sequence.make(argv,main.body,main.ann);
var argc := cpp.asgn.make(cpp.symbol.makestr("ivy::__argc",main.ann),cpp.symbol.makestr("argc",main.ann),main.ann);
main.body := cpp.sequence.make(argc,main.body,main.ann);
res.decls := res.decls.append(main);
}
import action show_prog(s:str)
action file_to_cpp(name:str) = {
var p := prog.read_file(name);
Flattening pass
if errors.end = 0 {
p := p.flat;
};
Type inference pass
if errors.end = 0 {
p := p.typeinfer;
};
var cpp_prog : cpp.prog;
if errors.end = 0 {
cpp_prog := p.to_cpp;
};
if errors.end = 0 {
var cpp_name := path.change_extension(name,"cpp");
call write_file(cpp_name,cpp_prog.enc);
}
}
}
}