diff options
-rw-r--r-- | compiler/ccgexprs.nim | 5 | ||||
-rw-r--r-- | compiler/ccgstmts.nim | 5 | ||||
-rw-r--r-- | compiler/parser.nim | 94 | ||||
-rw-r--r-- | compiler/sem.nim | 5 | ||||
-rw-r--r-- | compiler/semexprs.nim | 79 | ||||
-rw-r--r-- | compiler/semfold.nim | 7 | ||||
-rw-r--r-- | compiler/semstmts.nim | 46 | ||||
-rw-r--r-- | 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 .. <safeLen(n): if n[i].kind == nkDo: n.sons[i] = n[i][bodyPos] - result = n proc semExport(c: PContext, n: PNode): PNode = @@ -1910,7 +1956,7 @@ proc semExpr(c: PContext, n: PNode, flags: TExprFlags = {}): PNode = checkSonsLen(n, 1) n.sons[0] = semExpr(c, n.sons[0], flags) of nkCast: result = semCast(c, n) - of nkIfExpr: result = semIfExpr(c, n) + of nkIfExpr, nkIfStmt: result = semIf(c, n) of nkStmtListExpr: result = semStmtListExpr(c, n) of nkBlockExpr: result = semBlockExpr(c, n) of nkHiddenStdConv, nkHiddenSubConv, nkConv, nkHiddenCallConv: @@ -1936,7 +1982,6 @@ proc semExpr(c: PContext, n: PNode, flags: TExprFlags = {}): PNode = of nkLetSection: result = semVarOrLet(c, n, skLet) of nkConstSection: result = semConst(c, n) of nkTypeSection: result = SemTypeSection(c, n) - of nkIfStmt: result = SemIf(c, n) of nkDiscardStmt: result = semDiscard(c, n) of nkWhileStmt: result = semWhile(c, n) of nkTryStmt: result = semTry(c, n) diff --git a/compiler/semfold.nim b/compiler/semfold.nim index 8142b5e03..cc1f4b5ee 100644 --- a/compiler/semfold.nim +++ b/compiler/semfold.nim @@ -378,18 +378,17 @@ proc getConstIfExpr(c: PSym, n: PNode): PNode = result = nil for i in countup(0, sonsLen(n) - 1): var it = n.sons[i] - case it.kind - of nkElifExpr: + if it.len == 2: var e = getConstExpr(c, it.sons[0]) if e == nil: return nil if getOrdValue(e) != 0: if result == nil: result = getConstExpr(c, it.sons[1]) if result == nil: return - of nkElseExpr: + elif it.len == 1: if result == nil: result = getConstExpr(c, it.sons[0]) else: internalError(it.info, "getConstIfExpr()") - + proc partialAndExpr(c: PSym, n: PNode): PNode = # partial evaluation result = n diff --git a/compiler/semstmts.nim b/compiler/semstmts.nim index 9e9de7260..e1d8ed5a8 100644 --- a/compiler/semstmts.nim +++ b/compiler/semstmts.nim @@ -12,52 +12,6 @@ proc semCommand(c: PContext, n: PNode): PNode = result = semExprNoType(c, n) - -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 nkElifBranch, nkElifExpr: - checkSonsLen(it, 2) - 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) - if result == nil: - setResult(it.sons[0]) - else: illFormedAst(n) - 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 semIf(c: PContext, n: PNode): PNode = - result = n - for i in countup(0, sonsLen(n) - 1): - var it = n.sons[i] - case it.kind - of nkElifBranch: - checkSonsLen(it, 2) - it.sons[0] = forceBool(c, semExprWithType(c, it.sons[0])) - openScope(c.tab) - it.sons[1] = semStmt(c, it.sons[1]) - closeScope(c.tab) - of nkElse: - if sonsLen(it) == 1: it.sons[0] = semStmtScope(c, it.sons[0]) - else: illFormedAst(it) - else: illFormedAst(n) proc semDiscard(c: PContext, n: PNode): PNode = result = n diff --git a/todo.txt b/todo.txt index 8b1ecd358..b827b56cb 100644 --- a/todo.txt +++ b/todo.txt @@ -10,18 +10,16 @@ version 0.9.2 - parser/grammar: * check that of branches can only receive even simpler expressions, don't allow 'of (var x = 23; nkIdent)' - * allow (var x = 12; for i in ... ; x) construct - * try except as an expression + * document (var x = 12; for i in ... ; x) construct - make use of commonType relation in expressions - further expr/stmt unification: - - nkIfStmt vs nkIfExpr - - start with JS backend and support exprs everywhere - - then enhance C backend - - OR: do the temp stuff in transf + - rewrite nkCaseExpr handling + - try except as an expression Bugs ==== +- new parser breaks docgen - docgen: sometimes effects are listed twice - 'result' is not properly cleaned for NRVO - instantiated generics are listed in error messages |