// // // The Nimrod Compiler // (c) Copyright 2009 Andreas Rumpf // // See the file "copying.txt", included in this // distribution, for details about the copyright. // function isExpr(n: PNode): bool; // returns true if ``n`` looks like an expression var i: int; begin if n = nil then begin result := false; exit end; case n.kind of nkIdent..nkNilLit: result := true; nkCall..nkPassAsOpenArray: begin for i := 0 to sonsLen(n)-1 do if not isExpr(n.sons[i]) then begin result := false; exit end; result := true end else result := false end end; function isTypeDesc(n: PNode): bool; // returns true if ``n`` looks like a type desc var i: int; begin if n = nil then begin result := false; exit end; case n.kind of nkIdent, nkSym, nkType: result := true; nkDotExpr, nkBracketExpr: begin for i := 0 to sonsLen(n)-1 do if not isTypeDesc(n.sons[i]) then begin result := false; exit end; result := true end; nkTypeOfExpr..nkEnumTy: result := true; else result := false end end; function evalTemplateAux(c: PContext; templ, actual: PNode; sym: PSym): PNode; var i: int; p: PSym; begin if templ = nil then begin result := nil; exit end; case templ.kind of nkSym: begin p := templ.sym; if (p.kind = skParam) and (p.owner.id = sym.id) then result := copyTree(actual.sons[p.position]) else result := copyNode(templ) end; nkNone..nkIdent, nkType..nkNilLit: // atom result := copyNode(templ); else begin result := copyNode(templ); newSons(result, sonsLen(templ)); for i := 0 to sonsLen(templ)-1 do result.sons[i] := evalTemplateAux(c, templ.sons[i], actual, sym); end end end; var evalTemplateCounter: int = 0; // to prevend endless recursion in templates // instantation function evalTemplateArgs(c: PContext; n: PNode; s: PSym): PNode; var f, a, i: int; arg: PNode; begin f := sonsLen(s.typ); // if the template has zero arguments, it can be called without ``()`` // `n` is then a nkSym or something similar case n.kind of nkCall, nkInfix, nkPrefix, nkPostfix, nkCommand, nkCallStrLit: a := sonsLen(n); else a := 0 end; if a > f then liMessage(n.info, errWrongNumberOfArguments); result := copyNode(n); for i := 1 to f-1 do begin if i < a then arg := n.sons[i] else arg := copyTree(s.typ.n.sons[i].sym.ast); if arg = nil then liMessage(n.info, errWrongNumberOfArguments); if not (s.typ.sons[i].kind in [tyTypeDesc, tyStmt, tyExpr]) then begin // concrete type means semantic checking for argument: arg := fitNode(c, s.typ.sons[i], semExprWithType(c, arg)); end; addSon(result, arg); end end; function evalTemplate(c: PContext; n: PNode; sym: PSym): PNode; var args: PNode; begin inc(evalTemplateCounter); if evalTemplateCounter > 100 then liMessage(n.info, errTemplateInstantiationTooNested); // replace each param by the corresponding node: args := evalTemplateArgs(c, n, sym); result := evalTemplateAux(c, sym.ast.sons[codePos], args, sym); dec(evalTemplateCounter); end; function symChoice(c: PContext; n: PNode; s: PSym): PNode; var a: PSym; o: TOverloadIter; i: int; begin i := 0; a := initOverloadIter(o, c, n); while a <> nil do begin a := nextOverloadIter(o, c, n); inc(i); end; if i <= 1 then begin result := newSymNode(s); result.info := n.info; markUsed(n, s); end else begin // semantic checking requires a type; ``fitNode`` deals with it // appropriately result := newNodeIT(nkSymChoice, n.info, newTypeS(tyNone, c)); a := initOverloadIter(o, c, n); while a <> nil do begin addSon(result, newSymNode(a)); a := nextOverloadIter(o, c, n); end; //liMessage(n.info, warnUser, s.name.s + ' is here symchoice'); end end; function resolveTemplateParams(c: PContext; n: PNode; withinBind: bool; var toBind: TIntSet): PNode; var i: int; s: PSym; begin if n = nil then begin result := nil; exit end; case n.kind of nkIdent: begin if not withinBind and not IntSetContains(toBind, n.ident.id) then begin s := SymTabLocalGet(c.Tab, n.ident); if (s <> nil) then begin result := newSymNode(s); result.info := n.info end else result := n end else begin IntSetIncl(toBind, n.ident.id); result := symChoice(c, n, lookup(c, n)) end end; nkSym..nkNilLit: // atom result := n; nkBind: result := resolveTemplateParams(c, n.sons[0], true, toBind); else begin result := n; for i := 0 to sonsLen(n)-1 do result.sons[i] := resolveTemplateParams(c, n.sons[i], withinBind, toBind); end end end; function transformToExpr(n: PNode): PNode; var i, realStmt: int; begin result := n; case n.kind of nkStmtList: begin realStmt := -1; for i := 0 to sonsLen(n)-1 do begin case n.sons[i].kind of nkCommentStmt, nkEmpty, nkNilLit: begin end; else begin if realStmt = -1 then realStmt := i else realStmt := -2 end end end; if realStmt >= 0 then result := transformToExpr(n.sons[realStmt]) else n.kind := nkStmtListExpr; end; nkBlockStmt: n.kind := nkBlockExpr; //nkIfStmt: n.kind := nkIfExpr; // this is not correct! else begin end end end; function semTemplateDef(c: PContext; n: PNode): PNode; var s: PSym; toBind: TIntSet; begin if c.p.owner.kind = skModule then begin s := semIdentVis(c, skTemplate, n.sons[0], {@set}[sfStar]); include(s.flags, sfGlobal); end else s := semIdentVis(c, skTemplate, n.sons[0], {@set}[]); if sfStar in s.flags then include(s.flags, sfInInterface); // check parameter list: pushOwner(s); openScope(c.tab); n.sons[namePos] := newSymNode(s); // check that no pragmas exist: if n.sons[pragmasPos] <> nil then liMessage(n.info, errNoPragmasAllowedForX, 'template'); // check that no generic parameters exist: if n.sons[genericParamsPos] <> nil then liMessage(n.info, errNoGenericParamsAllowedForX, 'template'); if (n.sons[paramsPos] = nil) then begin // use ``stmt`` as implicit result type s.typ := newTypeS(tyProc, c); s.typ.n := newNodeI(nkFormalParams, n.info); addSon(s.typ, newTypeS(tyStmt, c)); addSon(s.typ.n, newNodeIT(nkType, n.info, s.typ.sons[0])); end else begin semParamList(c, n.sons[ParamsPos], nil, s); if n.sons[paramsPos].sons[0] = nil then begin // use ``stmt`` as implicit result type s.typ.sons[0] := newTypeS(tyStmt, c); s.typ.n.sons[0] := newNodeIT(nkType, n.info, s.typ.sons[0]); end end; addParams(c, s.typ.n); // resolve parameters: IntSetInit(toBind); n.sons[codePos] := resolveTemplateParams(c, n.sons[codePos], false, toBind); if not (s.typ.sons[0].kind in [tyStmt, tyTypeDesc]) then n.sons[codePos] := transformToExpr(n.sons[codePos]); // only parameters are resolved, no type checking is performed closeScope(c.tab); popOwner(); s.ast := n; result := n; if n.sons[codePos] = nil then liMessage(n.info, errImplOfXexpected, s.name.s); // add identifier of template as a last step to not allow // recursive templates addInterfaceDecl(c, s); end;