MODULE M3CProcActualS; (***************************************************************************) (* Copyright (C) Olivetti 1989 *) (* All Rights reserved *) (* *) (* Use and copy of this software and preparation of derivative works based *) (* upon this software are permitted to any person, provided this same *) (* copyright notice and the following Olivetti warranty disclaimer are *) (* included in any copy of the software or any modification thereof or *) (* derivative work therefrom made by any person. *) (* *) (* This software is made available AS IS and Olivetti disclaims all *) (* warranties with respect to this software, whether expressed or implied *) (* under any law, including all implied warranties of merchantibility and *) (* fitness for any purpose. In no event shall Olivetti be liable for any *) (* damages whatsoever resulting from loss of use, data or profits or *) (* otherwise arising out of or in connection with the use or performance *) (* of this software. *) (***************************************************************************) IMPORT M3AST_LX, M3AST_AS, M3AST_SM, M3ASTNext; IMPORT M3AST_AS_F, M3AST_SM_F; IMPORT SeqM3AST_AS_EXP; IMPORT M3Error, M3CActualUtil, M3CTypeRelation; (* From the report (Sec. 3.2, Procedure call): The list of bindings is rewritten to fit the signature of P's type as follows: First, each positional binding actual is converted into a keyword binding by supplying the name of the i'th formal parameter, where actual is the i'th binding in ActualS. Second, for each parameter that has a default and is not bound after the first step, the binding name := default is added to the list of actuals, where name is the name of the parameter and default is its default value. It is a static error if the rewritten list of actuals binds any name more than once, binds any name that is not a formal parameter, or fails to bind any formal parameter. A procedure call can also have the form: o.m(ActualS) where o is an object and m names one of o's methods. This is equivalent to: (o's m method)(o, ActualS) If the call is not a NEW, we'll build up the sm_actuals as follows: 1. Map the positional binding actuals to their corresponding formals. 2. Go over the remaining formals. If there is a binding for it in the actuals, then use the binding. Otherwise use the default. *) PROCEDURE NotProcedure(call: M3AST_AS.Call) RAISES {}= BEGIN M3Error.Report(call, "calling something that is not a procedure"); END NotProcedure; <*INLINE*> PROCEDURE ProcType( call: M3AST_AS.Call; complain: BOOLEAN) : M3AST_AS.Procedure_type RAISES {}= BEGIN TYPECASE call.as_callexp.sm_exp_type_spec OF | M3AST_AS.Procedure_type(p) => RETURN p; (* NIL case ok *) ELSE IF complain THEN NotProcedure(call) END; RETURN NIL; END; END ProcType; <*INLINE*> PROCEDURE AddParam(call: M3AST_AS.Call; exp: M3AST_AS.EXP) RAISES {}= BEGIN SeqM3AST_AS_EXP.AddRear(call.sm_actual_s, exp); END AddParam; PROCEDURE Set(call: M3AST_AS.Call) RAISES {}= VAR procType := ProcType(call, TRUE); BEGIN (* If 'callExp' does not have its 'sm_exp_type_spec' set to a procedure type then there is nothing we can do (except complain, via 'ProcType') *) IF procType = NIL THEN RETURN END; (* Prepare to iterate formal parameters. Note that if this is a call of the form 'T.m(...)', where 'T' is an object type we have a hidden formal parameter, for which the actual must be specified positionally *) VAR iter := M3ASTNext.NewIterFormal(procType.as_formal_param_s); hiddenFormal := procType.sm_def_id # NIL AND ISTYPE(procType.sm_def_id, M3AST_AS.Type_id); (* Pre process the actuals *) actuals := M3CActualUtil.ActualList(call); formalP: M3AST_AS.Formal_param; formalId: M3AST_AS.FORMAL_ID; id: M3AST_LX.Symbol_rep; actual: M3AST_AS.EXP; BEGIN (* Find all of the positional actuals *) FOR pos := 0 TO M3CActualUtil.PositionalActuals(actuals) - 1 DO IF hiddenFormal THEN hiddenFormal := FALSE; id := NIL; ELSIF M3ASTNext.Formal(iter, formalP, formalId) THEN (* ok *) id := formalId.lx_symrep; ELSE M3CActualUtil.TooManyArguments(call); M3CActualUtil.FindUnmatched(actuals); RETURN; END; AddParam(call, M3CActualUtil.ActualAt(actuals, pos, id)); END; (* If there was supposed to be an argument to match a hidden first formal did we get one? *) IF hiddenFormal THEN M3Error.Report(call, "object required as first positional argument"); END; (* For the remaining FORMALS, see if there is a keyword actual. If there is, use its expression. If not, use the default. *) WHILE M3ASTNext.Formal(iter, formalP, formalId) DO IF NOT M3CActualUtil.ActualByKeyword(actuals, formalId, actual) THEN actual := formalP.as_default; IF actual = NIL AND formalId.lx_symrep # NIL THEN M3Error.ReportWithId(call, "no argument for parameter \'%s\'", formalId.lx_symrep); END; END; AddParam(call, actual); END; M3CActualUtil.FindUnmatched(actuals); END; END Set; PROCEDURE DefaultMethodCall( p: M3AST_AS.Procedure_type; VAR ts: M3AST_SM.TYPE_SPEC_UNSET) : BOOLEAN RAISES {}= BEGIN TYPECASE p.sm_def_id OF | NULL => RETURN FALSE; | M3AST_AS.Type_id(typeId) => ts := typeId.sm_type_spec; RETURN TRUE; ELSE RETURN FALSE; END; END DefaultMethodCall; PROCEDURE TypeCheck(call: M3AST_AS.Call; safe: BOOLEAN) RAISES {}= VAR procType := ProcType(call, FALSE); BEGIN IF procType = NIL THEN RETURN END; (* Prepare to iterate formal parameters. Note that if this is a call of the form 'T.m(...)', where 'T' is an object type we have a hidden formal parameter, for which the actual must be specified positionally *) VAR iterActuals := SeqM3AST_AS_EXP.NewIter(call.sm_actual_s); iterFormals := M3ASTNext.NewIterFormal(procType.as_formal_param_s); formalType: M3AST_SM.TYPE_SPEC_UNSET; hiddenFormal := DefaultMethodCall(procType, formalType); formalP: M3AST_AS.Formal_param; formalId: M3AST_AS.FORMAL_ID; actual: M3AST_AS.EXP; isVar: BOOLEAN; BEGIN WHILE SeqM3AST_AS_EXP.Next(iterActuals, actual) DO IF hiddenFormal THEN (* 'formalType' already set up *) isVar := FALSE; hiddenFormal := FALSE; ELSE IF NOT M3ASTNext.Formal(iterFormals, formalP, formalId) THEN EXIT; ELSE formalType := formalId.sm_type_spec; isVar := ISTYPE(formalId, M3AST_AS.F_Var_id); END; END; IF actual # NIL AND formalType # NIL THEN IF isVar THEN M3CActualUtil.CheckIsVARActual(actual); IF NOT M3CTypeRelation.VARPassable(formalType, actual.sm_exp_type_spec) THEN M3Error.Report(actual, "actual parameter not passable to VAR formal"); END (* if *) ELSE IF NOT M3CActualUtil.Passable(formalType, actual, safe) THEN M3Error.Report( actual, "actual parameter not assignable to formal"); END; (* if *) END; END; END; END; END TypeCheck; BEGIN END M3CProcActualS.