lib: Split collections into readable and writable
[nit.git] / src / compiling / compiling_methods.nit
index 6c9ce56..1253a51 100644 (file)
@@ -73,12 +73,12 @@ redef class CompilerVisitor
        # Generate an fprintf to display an error location
        meth printf_locate_error(node: PNode): String
        do
-               var s = "fprintf(stderr, \""
+               var s = new Buffer.from("fprintf(stderr, \"")
                if nmc != null then s.append(" in %s")
                s.append(" (%s:%d)\\n\", ")
                if nmc != null then s.append("LOCATE_{nmc.method.cname}, ")
                s.append("LOCATE_{module.name}, {node.line_number});")
-               return s
+               return s.to_s
        end
 
        redef init(module: MMSrcModule)
@@ -302,7 +302,7 @@ redef class MMMethod
                        cargs.add("init_table /*YYY*/")
                end
 
-               var m = "(({cname}_t)CALL({cargs[0]},{global.color_id}))"
+               var m = "{global.meth_call}({cargs[0]})"
                var vcall = "{m}({cargs.join(", ")}) /*{local_class}::{name}*/"
                if name == ee then
                        vcall = "UNTAG_Bool({vcall})"
@@ -331,7 +331,7 @@ redef class MMMethod
        # Compile a call as call-next-method on self with given args
        meth compile_super_call(v: CompilerVisitor, cargs: Array[String]): String
        do
-               var m = "(({cname}_t)CALL({cargs[0]},{color_id_for_super}))"
+               var m = "{super_meth_call}({cargs[0]})"
                var vcall = "{m}({cargs.join(", ")}) /*super {local_class}::{name}*/"
                return vcall
        end
@@ -372,7 +372,7 @@ redef class MMSrcMethod
                var first_closure_index = signature.arity + 1 # Wich parameter is the first closure
                for i in [0..signature.closures.length[ do
                        var closcn = closure_cname(i)
-                       var cs = signature.closures[i] # Closure signature
+                       var cs = signature.closures[i].signature # Closure signature
                        var subparams = new Array[String] # Parameters of the closure
                        subparams.add("struct {closcn}*")
                        for j in [0..cs.arity[ do
@@ -530,44 +530,54 @@ redef class AMethPropdef
        meth do_compile_inside(v: CompilerVisitor, method: MMSrcMethod, params: Array[String]): String is abstract
 end
 
+redef class PSignature
+       meth compile_parameters(v: CompilerVisitor, orig_sig: MMSignature, params: Array[String]) is abstract
+end
+
+redef class ASignature
+       redef meth compile_parameters(v: CompilerVisitor, orig_sig: MMSignature, params: Array[String])
+       do
+               for ap in n_params do
+                       var cname = v.cfc.register_variable(ap.variable)
+                       v.nmc.method_params.add(ap.variable)
+                       var orig_type = orig_sig[ap.position]
+                       if not orig_type < ap.variable.stype then
+                               # FIXME: do not test always
+                               # FIXME: handle formal types
+                               v.add_instr("/* check if p<{ap.variable.stype} with p:{orig_type} */")
+                               ap.variable.stype.compile_type_check(v, params[ap.position], ap)
+                       end
+                       v.add_assignment(cname, params[ap.position])
+               end
+               for i in [0..n_closure_decls.length[ do
+                       var wd = n_closure_decls[i]
+                       var cname = v.cfc.register_closurevariable(wd.variable)
+                       wd.variable.ctypename = "struct {v.nmc.method.closure_cname(i)} *"
+                       v.add_assignment(cname, "{params[orig_sig.arity + i]}")
+               end
+       end
+end
+
 redef class AConcreteMethPropdef
        redef meth do_compile_inside(v, method, params)
        do
                var old_nmc = v.nmc
                v.nmc = new NitMethodContext(method)
 
-               var cname = v.cfc.register_variable(self_var)
-               v.add_assignment(cname, params[0])
+               var selfcname = v.cfc.register_variable(self_var)
+               v.add_assignment(selfcname, params[0])
+               params.shift
                v.nmc.method_params = [self_var]
 
-               var orig_meth: MMLocalProperty = method.global.intro
-               var orig_sig = orig_meth.signature_for(method.signature.recv)
                if n_signature != null then
-                       var sig = n_signature
-                       assert sig isa ASignature
-                       for ap in sig.n_params do
-                               var cname = v.cfc.register_variable(ap.variable)
-                               v.nmc.method_params.add(ap.variable)
-                               var orig_type = orig_sig[ap.position]
-                               if not orig_type < ap.variable.stype then
-                                       # FIXME: do not test always
-                                       # FIXME: handle formal types
-                                       v.add_instr("/* check if p<{ap.variable.stype} with p:{orig_type} */")
-                                       ap.variable.stype.compile_type_check(v, params[ap.position + 1], ap)
-                               end
-                               v.add_assignment(cname, params[ap.position + 1])
-                       end
-                       for i in [0..sig.n_closure_decls.length[ do
-                               var wd = sig.n_closure_decls[i]
-                               var cname = v.cfc.register_closurevariable(wd.variable)
-                               wd.variable.ctypename = "struct {method.closure_cname(i)} *"
-                               v.add_assignment(cname, "{params[orig_sig.arity + i + 1]}")
-                       end
+                       var orig_meth: MMLocalProperty = method.global.intro
+                       var orig_sig = orig_meth.signature_for(method.signature.recv)
+                       n_signature.compile_parameters(v, orig_sig, params)
                end
 
                var itpos: String = null
                if self isa AConcreteInitPropdef then
-                       itpos = "VAL2OBJ({params[0]})->vft[{method.local_class.global.init_table_pos_id}].i"
+                       itpos = "VAL2OBJ({selfcname})->vft[{method.local_class.global.init_table_pos_id}].i"
                        # v.add_instr("printf(\"{method.full_name}: inittable[%d] = %d\\n\", {itpos}, init_table[{itpos}]);")
                        v.add_instr("if (init_table[{itpos}]) return;")
                end
@@ -992,37 +1002,17 @@ redef class AForVardeclExpr
        redef meth compile_stmt(v)
        do
                var e = v.compile_expr(n_expr)
-               var prop = n_expr.stype.local_class.select_method(once "iterator".to_symbol)
-               if prop == null then
-                       printl("No iterator")
-                       return
-               end
-               var ittype = prop.signature.return_type
+               var ittype = meth_iterator.signature.return_type
                v.cfc.free_var(e)
                var iter = v.cfc.get_var
-               v.add_assignment(iter, prop.compile_call(v, [e]))
-               var prop2 = ittype.local_class.select_method(once "is_ok".to_symbol)
-               if prop2 == null then
-                       printl("No is_ok")
-                       return
-               end
-               var prop3 = ittype.local_class.select_method(once "item".to_symbol)
-               if prop3 == null then
-                       printl("No item")
-                       return
-               end
-               var prop4 = ittype.local_class.select_method(once "next".to_symbol)
-               if prop4 == null then
-                       printl("No next")
-                       return
-               end
+               v.add_assignment(iter, meth_iterator.compile_call(v, [e]))
                v.add_instr("while (true) \{ /*for*/")
                v.indent
                var ok = v.cfc.get_var
-               v.add_assignment(ok, prop2.compile_call(v, [iter]))
+               v.add_assignment(ok, meth_is_ok.compile_call(v, [iter]))
                v.add_instr("if (!UNTAG_Bool({ok})) break; /*for*/")
                v.cfc.free_var(ok)
-               var e = prop3.compile_call(v, [iter])
+               var e = meth_item.compile_call(v, [iter])
                e = v.ensure_var(e)
                var cname = v.cfc.register_variable(variable)
                v.add_assignment(cname, e)
@@ -1033,7 +1023,7 @@ redef class AForVardeclExpr
                        v.compile_stmt(n_block)
                end
                v.add_instr("{v.nmc.continue_label}: while(0);")
-               e = prop4.compile_call(v, [iter])
+               e = meth_next.compile_call(v, [iter])
                assert e == null
                v.unindent
                v.add_instr("}")
@@ -1186,9 +1176,8 @@ end
 redef class AStringFormExpr
        redef meth compile_expr(v)
        do
-               var prop = stype.local_class.select_method(once "with_native".to_symbol)
                compute_string_info
-               return prop.compile_constructor_call(v, stype , ["BOX_NativeString(\"{_cstring}\")", "TAG_Int({_cstring_length})"])
+               return meth_with_native.compile_constructor_call(v, stype , ["BOX_NativeString(\"{_cstring}\")", "TAG_Int({_cstring_length})"])
        end
 
        # The raw string value
@@ -1205,7 +1194,7 @@ redef class AStringFormExpr
        do
                var len = 0
                var str = string_text
-               var res = new String
+               var res = new Buffer
                var i = 0
                while i < str.length do
                        var c = str[i]
@@ -1221,7 +1210,7 @@ redef class AStringFormExpr
                        res.add(c)
                        i = i + 1
                end
-               _cstring = res
+               _cstring = res.to_s
                _cstring_length = len
        end
 end
@@ -1242,21 +1231,17 @@ end
 redef class ASuperstringExpr
        redef meth compile_expr(v)
        do
-               var prop = stype.local_class.select_method(once "init".to_symbol)
-               var recv = prop.compile_constructor_call(v, stype, new Array[String]) 
+               var array = meth_with_capacity.compile_constructor_call(v, atype, ["TAG_Int({n_exprs.length})"])
 
-               var prop2 = stype.local_class.select_method(once "append".to_symbol)
-
-               var prop3 = stype.local_class.select_method(once "to_s".to_symbol)
                for ne in n_exprs do
                        var e = v.ensure_var(v.compile_expr(ne))
                        if ne.stype != stype then
-                               v.add_assignment(e, prop3.compile_call(v, [e]))
+                               v.add_assignment(e, meth_to_s.compile_call(v, [e]))
                        end
-                       prop2.compile_call(v, [recv, e])
+                       meth_add.compile_call(v, [array, e])
                end
 
-               return recv
+               return meth_to_s.compile_call(v, [array])
        end
 end
 
@@ -1270,13 +1255,11 @@ end
 redef class AArrayExpr
        redef meth compile_expr(v)
        do
-               var prop = stype.local_class.select_method(once "with_capacity".to_symbol)
-               var recv = prop.compile_constructor_call(v, stype, ["TAG_Int({n_exprs.length})"])
+               var recv = meth_with_capacity.compile_constructor_call(v, stype, ["TAG_Int({n_exprs.length})"])
 
-               var prop2 = stype.local_class.select_method(once "add".to_symbol)
                for ne in n_exprs do
                        var e = v.compile_expr(ne)
-                       prop2.compile_call(v, [recv, e])
+                       meth_add.compile_call(v, [recv, e])
                end
                return recv
        end
@@ -1285,20 +1268,10 @@ end
 redef class ARangeExpr
        redef meth compile_expr(v)
        do
-               var prop = stype.local_class.select_method(propname)
                var e = v.compile_expr(n_expr)
                var e2 = v.compile_expr(n_expr2)
-               return prop.compile_constructor_call(v, stype, [e, e2])
+               return meth_init.compile_constructor_call(v, stype, [e, e2])
        end
-       # The constructor that must be used for the range
-       protected meth propname: Symbol is abstract
-end
-
-redef class ACrangeExpr
-       redef meth propname do return once "init".to_symbol
-end
-redef class AOrangeExpr
-       redef meth propname do return once "without_last".to_symbol
 end
 
 redef class ASuperExpr
@@ -1372,11 +1345,13 @@ redef class ASendExpr
                        cargs.add(v.compile_expr(a))
                end
 
-               var cd = closure_defs
                var e: String
-               if cd == null then
+               if prop_signature.closures.is_empty then
                        e = prop.compile_call(v, cargs)
                else
+                       var cd = closure_defs
+                       var arity = 0
+                       if cd != null then arity = cd.length
                        var closcns = new Array[String]
                        var ve: String = null
 
@@ -1387,11 +1362,14 @@ redef class ASendExpr
                        v.nmc.break_value = ve
 
                        # Compile closure to c function
-                       for i in [0..cd.length[ do
+                       for i in [0..arity[ do
                                var cn = cd[i].compile_closure(v, prop.closure_cname(i))
                                closcns.add(cn)
                                cargs.add(cn)
                        end
+                       for i in [arity..prop_signature.closures.length[ do
+                               cargs.add("NULL")
+                       end
 
                        v.nmc.break_value = old_bv
 
@@ -1403,7 +1381,7 @@ redef class ASendExpr
                        end
 
                        # Intercept returns and breaks
-                       for i in [0..cd.length[ do
+                       for i in [0..arity[ do
                                # A break or a return is intercepted
                                v.add_instr("if ({closcns[i]}->has_broke != NULL) \{")
                                v.indent
@@ -1502,7 +1480,7 @@ redef class AClosureDef
                var cname = "OC_{v.nmc.method.cname}_{v.out_contexts.length}"
                _cname = cname
                var args = new Array[String]
-               for i in [0..signature.arity[ do
+               for i in [0..closure.signature.arity[ do
                        args.add(" param{i}")
                end
 
@@ -1555,12 +1533,12 @@ redef class AClosureDef
        do
                var params = new Array[String]
                params.add("struct {closcn}* closctx")
-               for i in [0..signature.arity[ do
+               for i in [0..closure.signature.arity[ do
                        var p = "val_t {args[i]}"
                        params.add(p)
                end
                var ret: String
-               if signature.return_type != null then
+               if closure.signature.return_type != null then
                        ret = "val_t"
                else
                        ret = "void"
@@ -1593,7 +1571,38 @@ redef class AClosureDef
                v.add_instr("{v.nmc.continue_label}: while(false);")
 
                var ret: String = null
-               if signature.return_type != null then ret = v.nmc.continue_value
+               if closure.signature.return_type != null then ret = v.nmc.continue_value
+
+               v.nmc.continue_value = old_cv
+               v.nmc.continue_label = old_cl
+               v.nmc.break_label = old_bl
+
+               return ret
+       end
+end
+
+redef class PClosureDecl
+       meth do_compile_inside(v: CompilerVisitor, params: Array[String]): String is abstract
+end
+redef class AClosureDecl
+       redef meth do_compile_inside(v, params)
+       do
+               if n_signature != null then n_signature.compile_parameters(v, variable.closure.signature, params)
+
+               var old_cv = v.nmc.continue_value
+               var old_cl = v.nmc.continue_label
+               var old_bl = v.nmc.break_label
+
+               v.nmc.continue_value = v.cfc.get_var
+               v.nmc.continue_label = "continue_label{v.new_number}"
+               v.nmc.break_label = v.nmc.return_label
+
+               if n_expr != null then v.compile_stmt(n_expr)
+
+               v.add_instr("{v.nmc.continue_label}: while(false);")
+
+               var ret: String = null
+               if variable.closure.signature.return_type != null then ret = v.nmc.continue_value
 
                v.nmc.continue_value = old_cv
                v.nmc.continue_label = old_cl
@@ -1607,15 +1616,27 @@ redef class AClosureCallExpr
        redef meth compile_expr(v)
        do
                var cargs = new Array[String]
-               var ivar = "(({variable.ctypename})({v.cfc.varname(variable)}))"
-               cargs.add(ivar)
-               for a in arguments do
-                       cargs.add(v.compile_expr(a))
-               end
-               var s = "({ivar}->fun({cargs.join(", ")})) /* Invoke closure {variable} */"
+               for a in arguments do cargs.add(v.compile_expr(a))
                var va: String = null
-               if variable.signature.return_type != null then
-                       va = v.cfc.get_var
+               if variable.closure.signature.return_type != null then va = v.cfc.get_var
+
+               if variable.closure.is_optional then
+                       v.add_instr("if({v.cfc.varname(variable)}==NULL) \{")
+                       v.indent
+                       var n = variable.decl
+                       assert n isa AClosureDecl
+                       var s = n.do_compile_inside(v, cargs)
+                       if s != null then v.add_assignment(va, s)
+                       v.unindent
+                       v.add_instr("} else \{")
+                       v.indent
+               end
+
+               var ivar = "(({variable.ctypename})({v.cfc.varname(variable)}))"
+               var cargs2 = [ivar]
+               cargs2.append(cargs)
+               var s = "({ivar}->fun({cargs2.join(", ")})) /* Invoke closure {variable} */"
+               if va != null then
                        v.add_assignment(va, s)
                else
                        v.add_instr("{s};")
@@ -1629,6 +1650,11 @@ redef class AClosureCallExpr
                v.add_instr("goto {v.nmc.return_label};")
                v.unindent
                v.add_instr("\}")
+
+               if variable.closure.is_optional then
+                       v.unindent
+                       v.add_instr("\}")
+               end
                return va
        end
 end