// // // The Nimrod Compiler // (c) Copyright 2008 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, nkQualified, 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; begin if templ = nil then begin result := nil; exit end; case templ.kind of nkSym: begin if (templ.sym.kind = skParam) and (templ.sym.owner.id = sym.id) then result := copyTree(actual.sons[templ.sym.position+1]) // BUGFIX: +1 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 evalTemplate(c: PContext; n: PNode; sym: PSym): PNode; var r: PNode; begin inc(evalTemplateCounter); if evalTemplateCounter > 100 then liMessage(n.info, errTemplateInstantiationTooNested); // replace each param by the corresponding node: r := sym.ast.sons[paramsPos].sons[0]; if (r.kind <> nkIdent) then InternalError(r.info, 'evalTemplate'); result := evalTemplateAux(c, sym.ast.sons[codePos], n, sym); if r.ident.id = ord(wExpr) then result := semExpr(c, result) else result := semStmt(c, result); dec(evalTemplateCounter); end; function resolveTemplateParams(c: PContext; n: PNode): PNode; var i: int; s: PSym; begin if n = nil then begin result := nil; exit end; case n.kind of nkIdent: begin s := SymTabLocalGet(c.Tab, n.ident); if (s <> nil) then begin result := newSymNode(s); result.info := n.info end else result := n end; nkSym..nkNilLit: // atom result := n; else begin result := n; for i := 0 to sonsLen(n)-1 do result.sons[i] := resolveTemplateParams(c, n.sons[i]); end end end; function semTemplateParamKind(c: PContext; n, p: PNode): PNode; begin if (p = nil) or (p.kind <> nkIdent) then liMessage(n.info, errInvalidParamKindX, renderTree(p)) else begin case p.ident.id of ord(wExpr), ord(wStmt), ord(wTypeDesc): begin end; else liMessage(p.info, errInvalidParamKindX, p.ident.s) end end; result := p; 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, param: PSym; i, j, len, counter: int; params, p, paramKind: PNode; 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); params := n.sons[paramsPos]; counter := 0; for i := 1 to sonsLen(params)-1 do begin p := params.sons[i]; len := sonsLen(p); paramKind := semTemplateParamKind(c, p, p.sons[len-2]); if (p.sons[len-1] <> nil) then liMessage(p.sons[len-1].info, errDefaultArgumentInvalid); for j := 0 to len-3 do begin param := newSymS(skParam, p.sons[j], c); param.position := counter; param.ast := paramKind; inc(counter); addDecl(c, param) end; end; params.sons[0] := semTemplateParamKind(c, params, params.sons[0]); 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'); // resolve parameters: n.sons[codePos] := resolveTemplateParams(c, n.sons[codePos]); if params.sons[0].ident.id = ord(wExpr) 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;