summary refs log blame commit diff stats
path: root/nim/semtempl.pas
blob: fc7e12a73e892034552c9f3b88119a2d5ad4089c (plain) (tree)
1
2
3
4


                                
                                          












                                                    
                                    










                                           
                                               





                                                  
                                   










                                               
                                                                              

         
          



                                                    


                                                           

















                                                                            


















                                                                        
                                               








                                                                          

                                                               
              

                           
                                   

                                                         

                                                                 


                           














                                                          
                   

            
                                                                   





                                                                  

                                                                   


     

                                                                       






                                                
                                                                             








                                            
                                       
                                               
         


                            
            
                                                                  


                                   
                                                                                  



       
















                                                      
                           











                                                           
          
                  
     
                                         






                                                               
               
                   
                                   






                                                                 
















                                                                  
                        

                                                                              
                                                          


                                                                
                    
             








                                                           
//
//
//           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;