From 2afadc5c9ceb1f8eb85a914da93f9ade2cb0d367 Mon Sep 17 00:00:00 2001 From: Araq Date: Tue, 30 Apr 2013 02:38:49 +0200 Subject: first steps to the expr/stmt unification --- compiler/ccgexprs.nim | 5 ++- compiler/ccgstmts.nim | 5 ++- compiler/parser.nim | 94 ++++++++++++++++++++++++++++++++++++++++++++------- compiler/sem.nim | 5 ++- compiler/semexprs.nim | 79 +++++++++++++++++++++++++++++++++---------- compiler/semfold.nim | 7 ++-- compiler/semstmts.nim | 46 ------------------------- todo.txt | 10 +++--- 8 files changed, 159 insertions(+), 92 deletions(-) diff --git a/compiler/ccgexprs.nim b/compiler/ccgexprs.nim index 6a2ffe752..78173b5e8 100644 --- a/compiler/ccgexprs.nim +++ b/compiler/ccgexprs.nim @@ -861,15 +861,14 @@ proc genIfExpr(p: BProc, n: PNode, d: var TLoc) = Lend = getLabel(p) for i in countup(0, sonsLen(n) - 1): it = n.sons[i] - case it.kind - of nkElifExpr: + if it.len == 2: initLocExpr(p, it.sons[0], a) Lelse = getLabel(p) lineF(p, cpsStmts, "if (!$1) goto $2;$n", [rdLoc(a), Lelse]) expr(p, it.sons[1], tmp) lineF(p, cpsStmts, "goto $1;$n", [Lend]) fixLabel(p, Lelse) - of nkElseExpr: + elif it.len == 1: expr(p, it.sons[0], tmp) else: internalError(n.info, "genIfExpr()") fixLabel(p, Lend) diff --git a/compiler/ccgstmts.nim b/compiler/ccgstmts.nim index e11678861..1f0ffd656 100644 --- a/compiler/ccgstmts.nim +++ b/compiler/ccgstmts.nim @@ -231,8 +231,7 @@ proc genIfStmt(p: BProc, n: PNode) = var Lend = getLabel(p) for i in countup(0, sonsLen(n) - 1): var it = n.sons[i] - case it.kind - of nkElifBranch: + if it.len == 2: initLocExpr(p, it.sons[0], a) Lelse = getLabel(p) inc(p.labels) @@ -243,7 +242,7 @@ proc genIfStmt(p: BProc, n: PNode) = if sonsLen(n) > 1: lineFF(p, cpsStmts, "goto $1;$n", "br label %$1$n", [Lend]) fixLabel(p, Lelse) - of nkElse: + elif it.len == 1: genSimpleBlock(p, it.sons[0]) else: internalError(n.info, "genIfStmt()") if sonsLen(n) > 1: fixLabel(p, Lend) diff --git a/compiler/parser.nim b/compiler/parser.nim index 517297a87..f9f7f7fad 100644 --- a/compiler/parser.nim +++ b/compiler/parser.nim @@ -315,9 +315,7 @@ proc indexExprList(p: var TParser, first: PNode, k: TNodeKind, optPar(p) eat(p, endToken) -proc exprColonEqExpr(p: var TParser): PNode = - #| exprColonEqExpr = expr (':'|'=' expr)? - var a = parseExpr(p) +proc colonOrEquals(p: var TParser, a: PNode): PNode = if p.tok.tokType == tkColon: result = newNodeP(nkExprColonExpr, p) getTok(p) @@ -333,6 +331,11 @@ proc exprColonEqExpr(p: var TParser): PNode = else: result = a +proc exprColonEqExpr(p: var TParser): PNode = + #| exprColonEqExpr = expr (':'|'=' expr)? + var a = parseExpr(p) + result = colonOrEquals(p, a) + proc exprList(p: var TParser, endTok: TTokType, result: PNode) = #| exprList = expr ^+ comma getTok(p) @@ -443,9 +446,76 @@ proc parseGStrLit(p: var TParser, a: PNode): PNode = getTok(p) else: result = a - -proc identOrLiteral(p: var TParser): PNode = - #| generalizedLit ::= GENERALIZED_STR_LIT | GENERALIZED_TRIPLESTR_LIT + +type + TPrimaryMode = enum pmNormal, pmTypeDesc, pmTypeDef, pmSkipSuffix + +proc complexOrSimpleStmt(p: var TParser): PNode +proc simpleExpr(p: var TParser, mode = pmNormal): PNode + +proc semiStmtList(p: var TParser, result: PNode) = + if p.tok.tokType == tkSemicolon: + # '(;' enforces 'stmt' context: + getTok(p) + optInd(p, result) + result.add(complexOrSimpleStmt(p)) + while p.tok.tokType == tkSemicolon: + getTok(p) + optInd(p, result) + result.add(complexOrSimpleStmt(p)) + result.kind = nkStmtListExpr + +proc parsePar(p: var TParser): PNode = + #| parKeyw = 'discard' | 'include' | 'if' | 'while' | 'case' | 'try' + #| | 'finally' | 'except' | 'for' | 'block' | 'const' | 'let' + #| | 'when' | 'var' | 'bind' | 'mixin' + #| par = '(' optInd (&parKeyw complexOrSimpleStmt ^+ ';' + #| | simpleExpr ('=' expr (';' complexOrSimpleStmt ^+ ';' )? )? + #| | (':' expr)? (',' (exprColonEqExpr comma?)*)? )? + #| optPar ')' + # + # unfortunately it's ambiguous: (expr: expr) vs (exprStmt); however a + # leading ';' could be used to enforce a 'stmt' context ... + result = newNodeP(nkPar, p) + getTok(p) + optInd(p, result) + if p.tok.tokType in {tkDiscard, tkInclude, tkIf, tkWhile, tkCase, + tkTry, tkFinally, tkExcept, tkFor, tkBlock, + tkConst, tkLet, tkWhen, tkVar, tkBind, + tkMixin, tkSemicolon}: + semiStmtList(p, result) + elif p.tok.tokType != tkParRi: + var a = simpleExpr(p) + if p.tok.tokType == tkEquals: + # special case: allow assignments + getTok(p) + optInd(p, result) + let b = parseExpr(p) + let asgn = newNodeI(nkAsgn, a.info, 2) + asgn.sons[0] = a + asgn.sons[1] = b + result.add(asgn) + elif p.tok.tokType == tkSemicolon: + # stmt context: + result.add(a) + semiStmtList(p, result) + else: + a = colonOrEquals(p, a) + result.add(a) + if p.tok.tokType == tkComma: + getTok(p) + skipComment(p, a) + while p.tok.tokType != tkParRi and p.tok.tokType != tkEof: + var a = exprColonEqExpr(p) + addSon(result, a) + if p.tok.tokType != tkComma: break + getTok(p) + skipComment(p, a) + optPar(p) + eat(p, tkParRi) + +proc identOrLiteral(p: var TParser, mode: TPrimaryMode): PNode = + #| generalizedLit = GENERALIZED_STR_LIT | GENERALIZED_TRIPLESTR_LIT #| identOrLiteral = generalizedLit | symbol #| | INT_LIT | INT8_LIT | INT16_LIT | INT32_LIT | INT64_LIT #| | UINT_LIT | UINT8_LIT | UINT16_LIT | UINT32_LIT | UINT64_LIT @@ -453,7 +523,7 @@ proc identOrLiteral(p: var TParser): PNode = #| | STR_LIT | RSTR_LIT | TRIPLESTR_LIT #| | CHAR_LIT #| | NIL - #| | tupleConstr | arrayConstr | setOrTableConstr + #| | par | arrayConstr | setOrTableConstr #| | castExpr #| tupleConstr = '(' optInd (exprColonEqExpr comma?)* optPar ')' #| arrayConstr = '[' optInd (exprColonEqExpr comma?)* optPar ']' @@ -537,7 +607,10 @@ proc identOrLiteral(p: var TParser): PNode = getTok(p) of tkParLe: # () constructor - result = exprColonEqExprList(p, nkPar, tkParRi) + if mode in {pmTypeDesc, pmTypeDef}: + result = exprColonEqExprList(p, nkPar, tkParRi) + else: + result = parsePar(p) of tkCurlyLe: # {} constructor result = setOrTableConstr(p) @@ -583,9 +656,6 @@ proc primarySuffix(p: var TParser, r: PNode): PNode = result = indexExprList(p, result, nkCurlyExpr, tkCurlyRi) else: break -type - TPrimaryMode = enum pmNormal, pmTypeDesc, pmTypeDef, pmSkipSuffix - proc primary(p: var TParser, mode: TPrimaryMode): PNode proc simpleExprAux(p: var TParser, limit: int, mode: TPrimaryMode): PNode = @@ -926,7 +996,7 @@ proc primary(p: var TParser, mode: TPrimaryMode): PNode = optInd(p, result) addSon(result, primary(p, pmNormal)) else: - result = identOrLiteral(p) + result = identOrLiteral(p, mode) if mode != pmSkipSuffix: result = primarySuffix(p, result) diff --git a/compiler/sem.nim b/compiler/sem.nim index 88249fedb..0e4b65d9f 100644 --- a/compiler/sem.nim +++ b/compiler/sem.nim @@ -58,10 +58,13 @@ proc fitNode(c: PContext, formal: PType, arg: PNode): PNode = result = copyNode(arg) result.typ = formal +var CommonTypeBegin = PType(kind: tyExpr) + proc commonType*(x, y: PType): PType = # new type relation that is used for array constructors, # if expressions, etc.: - if x == nil: return y + if x == nil: return x + if y == nil: return y var a = skipTypes(x, {tyGenericInst}) var b = skipTypes(y, {tyGenericInst}) result = x diff --git a/compiler/semexprs.nim b/compiler/semexprs.nim index 8b1e5aea1..9b7318e62 100644 --- a/compiler/semexprs.nim +++ b/compiler/semexprs.nim @@ -1458,26 +1458,73 @@ proc semMagic(c: PContext, n: PNode, s: PSym, flags: TExprFlags): PNode = of mQuoteAst: result = semQuoteAst(c, n) else: result = semDirectOp(c, n, flags) -proc semIfExpr(c: PContext, n: PNode): PNode = - result = n - checkMinSonsLen(n, 2) - var typ: PType = nil +proc semWhen(c: PContext, n: PNode, semCheck = true): PNode = + # If semCheck is set to false, ``when`` will return the verbatim AST of + # the correct branch. Otherwise the AST will be passed through semStmt. + result = nil + + template setResult(e: expr) = + if semCheck: result = semStmt(c, e) # do not open a new scope! + else: result = e + for i in countup(0, sonsLen(n) - 1): var it = n.sons[i] case it.kind - of nkElifExpr: + of nkElifBranch, nkElifExpr: checkSonsLen(it, 2) - it.sons[0] = forceBool(c, semExprWithType(c, it.sons[0])) - it.sons[1] = semExprWithType(c, it.sons[1]) - if typ == nil: typ = it.sons[1].typ - else: it.sons[1] = fitNode(c, typ, it.sons[1]) - of nkElseExpr: + var e = semConstExpr(c, it.sons[0]) + if e.kind != nkIntLit: InternalError(n.info, "semWhen") + elif e.intVal != 0 and result == nil: + setResult(it.sons[1]) + of nkElse, nkElseExpr: checkSonsLen(it, 1) - it.sons[0] = semExprWithType(c, it.sons[0]) - if typ != nil: it.sons[0] = fitNode(c, typ, it.sons[0]) - else: InternalError(it.info, "semIfExpr") + if result == nil: + setResult(it.sons[0]) else: illFormedAst(n) - result.typ = typ + if result == nil: + result = newNodeI(nkNilLit, n.info) + # The ``when`` statement implements the mechanism for platform dependent + # code. Thus we try to ensure here consistent ID allocation after the + # ``when`` statement. + IDsynchronizationPoint(200) + + +proc semExprBranch(c: PContext, n: PNode): PNode = + result = semExpr(c, n) + if result.typ != nil: + # XXX tyGenericInst here? + semProcvarCheck(c, result) + if result.typ.kind == tyVar: result = newDeref(result) + semDestructorCheck(c, result, {}) + +proc semIf(c: PContext, n: PNode): PNode = + result = n + var typ = CommonTypeBegin + var hasElse = false + for i in countup(0, sonsLen(n) - 1): + var it = n.sons[i] + if it.len == 2: + it.sons[0] = forceBool(c, semExprWithType(c, it.sons[0])) + openScope(c.tab) + it.sons[1] = semExprBranch(c, it.sons[1]) + typ = commonType(typ, it.sons[1].typ) + closeScope(c.tab) + elif it.len == 1: + hasElse = true + openScope(c.tab) + it.sons[0] = semExprBranch(c, it.sons[0]) + typ = commonType(typ, it.sons[0].typ) + closeScope(c.tab) + else: illFormedAst(it) + if isEmptyType(typ) or not hasElse: + for it in n: discardCheck(it.lastSon) + result.kind = nkIfStmt + else: + for it in n: + let j = it.len-1 + it.sons[j] = fitNode(c, typ, it.sons[j]) + result.kind = nkIfExpr + result.typ = typ proc semSetConstr(c: PContext, n: PNode): PNode = result = newNodeI(nkCurly, n.info) @@ -1739,7 +1786,6 @@ proc fixImmediateParams(n: PNode): PNode = # the planned overload resolution reforms for i in 1 ..