diff options
Diffstat (limited to 'compiler')
33 files changed, 2404 insertions, 2027 deletions
diff --git a/compiler/ast.nim b/compiler/ast.nim index c43bef926..eb12c977f 100644 --- a/compiler/ast.nim +++ b/compiler/ast.nim @@ -275,6 +275,7 @@ const sfDirty* = sfPure # template is not hygienic (old styled template) + # module, compiled from a dirty-buffer sfAnon* = sfDiscardable # symbol name that was generated by the compiler @@ -680,7 +681,6 @@ type size*: BiggestInt # the size of the type in bytes # -1 means that the size is unkwown align*: int # the type's alignment requirements - containerID*: int # used for type checking of generics loc*: TLoc TPair*{.final.} = object @@ -768,6 +768,9 @@ const nkCommand, nkCallStrLit, nkHiddenCallConv} nkLambdaKinds* = {nkLambda, nkDo} + declarativeDefs* = {nkProcDef, nkMethodDef, nkIteratorDef, nkConverterDef} + procDefs* = nkLambdaKinds + declarativeDefs + nkSymChoices* = {nkClosedSymChoice, nkOpenSymChoice} nkStrKinds* = {nkStrLit..nkTripleStrLit} @@ -1017,7 +1020,6 @@ proc assignType(dest, src: PType) = dest.n = src.n dest.size = src.size dest.align = src.align - dest.containerID = src.containerID dest.destructor = src.destructor # this fixes 'type TLock = TSysLock': if src.sym != nil: diff --git a/compiler/c2nim/clex.nim b/compiler/c2nim/clex.nim index db8527aa0..3681fa8e1 100644 --- a/compiler/c2nim/clex.nim +++ b/compiler/c2nim/clex.nim @@ -13,7 +13,7 @@ import - options, msgs, strutils, platform, lexbase, llstream + options, msgs, strutils, platform, nimlexbase, llstream const MaxLineLength* = 80 # lines longer than this lead to a warning @@ -338,8 +338,8 @@ proc getNumber(L: var TLexer, tok: var TToken) = proc HandleCRLF(L: var TLexer, pos: int): int = case L.buf[pos] - of CR: result = lexbase.HandleCR(L, pos) - of LF: result = lexbase.HandleLF(L, pos) + of CR: result = nimlexbase.HandleCR(L, pos) + of LF: result = nimlexbase.HandleLF(L, pos) else: result = pos proc escape(L: var TLexer, tok: var TToken, allowEmpty=false) = @@ -405,10 +405,10 @@ proc getString(L: var TLexer, tok: var TToken) = Inc(pos) break of CR: - pos = lexbase.HandleCR(L, pos) + pos = nimlexbase.HandleCR(L, pos) buf = L.buf of LF: - pos = lexbase.HandleLF(L, pos) + pos = nimlexbase.HandleLF(L, pos) buf = L.buf of lexbase.EndOfFile: var line2 = L.linenumber @@ -449,7 +449,7 @@ proc scanLineComment(L: var TLexer, tok: var TToken) = while true: inc(pos, 2) # skip // add(tok.s, '#') - while not (buf[pos] in {CR, LF, lexbase.EndOfFile}): + while not (buf[pos] in {CR, LF, nimlexbase.EndOfFile}): add(tok.s, buf[pos]) inc(pos) pos = handleCRLF(L, pos) @@ -490,7 +490,7 @@ proc scanStarComment(L: var TLexer, tok: var TToken) = break else: add(tok.s, '*') - of lexbase.EndOfFile: + of nimlexbase.EndOfFile: lexMessage(L, errTokenExpected, "*/") else: add(tok.s, buf[pos]) @@ -743,7 +743,7 @@ proc getTok(L: var TLexer, tok: var TToken) = getDirective(L, tok) of '"': getString(L, tok) of '\'': getCharLit(L, tok) - of lexbase.EndOfFile: + of nimlexbase.EndOfFile: tok.xkind = pxEof else: tok.s = $c diff --git a/compiler/ccgexprs.nim b/compiler/ccgexprs.nim index 6a2ffe752..2f7804384 100644 --- a/compiler/ccgexprs.nim +++ b/compiler/ccgexprs.nim @@ -9,9 +9,6 @@ # included from cgen.nim -proc lenField: PRope {.inline.} = - result = toRope(if gCmd != cmdCompileToCpp: "Sup.len" else: "len") - # -------------------------- constant expressions ------------------------ proc intLiteral(i: biggestInt): PRope = @@ -207,8 +204,6 @@ proc asgnComplexity(n: PNode): int = result += asgnComplexity(t) else: nil -proc genAssignment(p: BProc, dest, src: TLoc, flags: TAssignmentFlags) - proc optAsgnLoc(a: TLoc, t: PType, field: PRope): TLoc = result.k = locField result.s = a.s @@ -345,11 +340,6 @@ proc genAssignment(p: BProc, dest, src: TLoc, flags: TAssignmentFlags) = linefmt(p, cpsStmts, "$1 = $2;$n", rdLoc(dest), rdLoc(src)) else: InternalError("genAssignment(" & $ty.kind & ')') -proc expr(p: BProc, e: PNode, d: var TLoc) -proc initLocExpr(p: BProc, e: PNode, result: var TLoc) = - initLoc(result, locNone, e.typ, OnUnknown) - expr(p, e, result) - proc getDestLoc(p: BProc, d: var TLoc, typ: PType) = if d.k == locNone: getTemp(p, typ, d) @@ -377,15 +367,15 @@ proc putIntoDest(p: BProc, d: var TLoc, t: PType, r: PRope) = d.a = -1 proc binaryStmt(p: BProc, e: PNode, d: var TLoc, frmt: string) = - var b: TLoc + var a, b: TLoc if d.k != locNone: InternalError(e.info, "binaryStmt") - InitLocExpr(p, e.sons[1], d) + InitLocExpr(p, e.sons[1], a) InitLocExpr(p, e.sons[2], b) - lineCg(p, cpsStmts, frmt, rdLoc(d), rdLoc(b)) + lineCg(p, cpsStmts, frmt, rdLoc(a), rdLoc(b)) proc unaryStmt(p: BProc, e: PNode, d: var TLoc, frmt: string) = var a: TLoc - if (d.k != locNone): InternalError(e.info, "unaryStmt") + if d.k != locNone: InternalError(e.info, "unaryStmt") InitLocExpr(p, e.sons[1], a) lineCg(p, cpsStmts, frmt, [rdLoc(a)]) @@ -840,44 +830,6 @@ proc genAndOr(p: BProc, e: PNode, d: var TLoc, m: TMagic) = else: genAssignment(p, d, tmp, {}) # no need for deep copying -proc genIfExpr(p: BProc, n: PNode, d: var TLoc) = - # - # if (!expr1) goto L1; - # thenPart - # goto LEnd - # L1: - # if (!expr2) goto L2; - # thenPart2 - # goto LEnd - # L2: - # elsePart - # Lend: - # - var - it: PNode - a, tmp: TLoc - Lend, Lelse: TLabel - getTemp(p, n.typ, tmp) # force it into a temp! - Lend = getLabel(p) - for i in countup(0, sonsLen(n) - 1): - it = n.sons[i] - case it.kind - of nkElifExpr: - 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: - expr(p, it.sons[0], tmp) - else: internalError(n.info, "genIfExpr()") - fixLabel(p, Lend) - if d.k == locNone: - d = tmp - else: - genAssignment(p, d, tmp, {}) # no need for deep copying - proc genEcho(p: BProc, n: PNode) = # this unusal way of implementing it ensures that e.g. ``echo("hallo", 45)`` # is threadsafe. @@ -889,8 +841,6 @@ proc genEcho(p: BProc, n: PNode) = linefmt(p, cpsStmts, "printf($1$2);$n", makeCString(repeatStr(n.len-1, "%s") & tnl), args) -include ccgcalls - proc genStrConcat(p: BProc, e: PNode, d: var TLoc) = # <Nimrod code> # s = 'Hello ' & name & ', how do you feel?' & 'z' @@ -1791,11 +1741,10 @@ proc exprComplexConst(p: BProc, n: PNode, d: var TLoc) = else: putIntoDest(p, d, t, tmp) -proc genBlock(p: BProc, t: PNode, d: var TLoc) -proc expr(p: BProc, e: PNode, d: var TLoc) = - case e.kind +proc expr(p: BProc, n: PNode, d: var TLoc) = + case n.kind of nkSym: - var sym = e.sym + var sym = n.sym case sym.Kind of skMethod: if sym.getBody.kind == nkEmpty or sfDispatcher in sym.flags: @@ -1808,22 +1757,22 @@ proc expr(p: BProc, e: PNode, d: var TLoc) = of skProc, skConverter, skIterator: genProc(p.module, sym) if sym.loc.r == nil or sym.loc.t == nil: - InternalError(e.info, "expr: proc not init " & sym.name.s) + InternalError(n.info, "expr: proc not init " & sym.name.s) putLocIntoDest(p, d, sym.loc) of skConst: if sfFakeConst in sym.flags: if sfGlobal in sym.flags: genVarPrototype(p.module, sym) putLocIntoDest(p, d, sym.loc) elif isSimpleConst(sym.typ): - putIntoDest(p, d, e.typ, genLiteral(p, sym.ast, sym.typ)) + putIntoDest(p, d, n.typ, genLiteral(p, sym.ast, sym.typ)) else: genComplexConst(p, sym, d) of skEnumField: - putIntoDest(p, d, e.typ, toRope(sym.position)) + putIntoDest(p, d, n.typ, toRope(sym.position)) of skVar, skForVar, skResult, skLet: if sfGlobal in sym.flags: genVarPrototype(p.module, sym) if sym.loc.r == nil or sym.loc.t == nil: - InternalError(e.info, "expr: var not init " & sym.name.s) + InternalError(n.info, "expr: var not init " & sym.name.s) if sfThread in sym.flags: AccessThreadLocalVar(p, sym) if emulatedThreadVars(): @@ -1834,75 +1783,139 @@ proc expr(p: BProc, e: PNode, d: var TLoc) = putLocIntoDest(p, d, sym.loc) of skTemp: if sym.loc.r == nil or sym.loc.t == nil: - InternalError(e.info, "expr: temp not init " & sym.name.s) + InternalError(n.info, "expr: temp not init " & sym.name.s) putLocIntoDest(p, d, sym.loc) of skParam: if sym.loc.r == nil or sym.loc.t == nil: - InternalError(e.info, "expr: param not init " & sym.name.s) + InternalError(n.info, "expr: param not init " & sym.name.s) putLocIntoDest(p, d, sym.loc) - else: InternalError(e.info, "expr(" & $sym.kind & "); unknown symbol") + else: InternalError(n.info, "expr(" & $sym.kind & "); unknown symbol") + of nkNilLit: + if not isEmptyType(n.typ): + putIntoDest(p, d, n.typ, genLiteral(p, n)) of nkStrLit..nkTripleStrLit, nkIntLit..nkUInt64Lit, - nkFloatLit..nkFloat128Lit, nkNilLit, nkCharLit: - putIntoDest(p, d, e.typ, genLiteral(p, e)) + nkFloatLit..nkFloat128Lit, nkCharLit: + putIntoDest(p, d, n.typ, genLiteral(p, n)) of nkCall, nkHiddenCallConv, nkInfix, nkPrefix, nkPostfix, nkCommand, nkCallStrLit: - if e.sons[0].kind == nkSym and e.sons[0].sym.magic != mNone: - genMagicExpr(p, e, d, e.sons[0].sym.magic) + genLineDir(p, n) + let op = n.sons[0] + if n.typ.isNil: + # discard the value: + var a: TLoc + if op.kind == nkSym and op.sym.magic != mNone: + genMagicExpr(p, n, a, op.sym.magic) + else: + genCall(p, n, a) else: - genCall(p, e, d) + # load it into 'd': + if op.kind == nkSym and op.sym.magic != mNone: + genMagicExpr(p, n, d, op.sym.magic) + else: + genCall(p, n, d) of nkCurly: - if isDeepConstExpr(e) and e.len != 0: - putIntoDest(p, d, e.typ, genSetNode(p, e)) + if isDeepConstExpr(n) and n.len != 0: + putIntoDest(p, d, n.typ, genSetNode(p, n)) else: - genSetConstr(p, e, d) + genSetConstr(p, n, d) of nkBracket: - if isDeepConstExpr(e) and e.len != 0: - exprComplexConst(p, e, d) - elif skipTypes(e.typ, abstractVarRange).kind == tySequence: - genSeqConstr(p, e, d) + if isDeepConstExpr(n) and n.len != 0: + exprComplexConst(p, n, d) + elif skipTypes(n.typ, abstractVarRange).kind == tySequence: + genSeqConstr(p, n, d) else: - genArrayConstr(p, e, d) + genArrayConstr(p, n, d) of nkPar: - if isDeepConstExpr(e) and e.len != 0: - exprComplexConst(p, e, d) + if isDeepConstExpr(n) and n.len != 0: + exprComplexConst(p, n, d) else: - genTupleConstr(p, e, d) - of nkObjConstr: genObjConstr(p, e, d) - of nkCast: genCast(p, e, d) - of nkHiddenStdConv, nkHiddenSubConv, nkConv: genConv(p, e, d) - of nkHiddenAddr, nkAddr: genAddr(p, e, d) + genTupleConstr(p, n, d) + of nkObjConstr: genObjConstr(p, n, d) + of nkCast: genCast(p, n, d) + of nkHiddenStdConv, nkHiddenSubConv, nkConv: genConv(p, n, d) + of nkHiddenAddr, nkAddr: genAddr(p, n, d) of nkBracketExpr: - var ty = skipTypes(e.sons[0].typ, abstractVarRange) + var ty = skipTypes(n.sons[0].typ, abstractVarRange) if ty.kind in {tyRef, tyPtr}: ty = skipTypes(ty.sons[0], abstractVarRange) case ty.kind - of tyArray, tyArrayConstr: genArrayElem(p, e, d) - of tyOpenArray, tyVarargs: genOpenArrayElem(p, e, d) - of tySequence, tyString: genSeqElem(p, e, d) - of tyCString: genCStringElem(p, e, d) - of tyTuple: genTupleElem(p, e, d) - else: InternalError(e.info, "expr(nkBracketExpr, " & $ty.kind & ')') - of nkDerefExpr, nkHiddenDeref: genDeref(p, e, d) - of nkDotExpr: genRecordField(p, e, d) - of nkCheckedFieldExpr: genCheckedRecordField(p, e, d) - of nkBlockExpr: genBlock(p, e, d) - of nkStmtListExpr: genStmtListExpr(p, e, d) - of nkIfExpr: genIfExpr(p, e, d) - of nkObjDownConv: downConv(p, e, d) - of nkObjUpConv: upConv(p, e, d) - of nkChckRangeF: genRangeChck(p, e, d, "chckRangeF") - of nkChckRange64: genRangeChck(p, e, d, "chckRange64") - of nkChckRange: genRangeChck(p, e, d, "chckRange") - of nkStringToCString: convStrToCStr(p, e, d) - of nkCStringToString: convCStrToStr(p, e, d) + of tyArray, tyArrayConstr: genArrayElem(p, n, d) + of tyOpenArray, tyVarargs: genOpenArrayElem(p, n, d) + of tySequence, tyString: genSeqElem(p, n, d) + of tyCString: genCStringElem(p, n, d) + of tyTuple: genTupleElem(p, n, d) + else: InternalError(n.info, "expr(nkBracketExpr, " & $ty.kind & ')') + of nkDerefExpr, nkHiddenDeref: genDeref(p, n, d) + of nkDotExpr: genRecordField(p, n, d) + of nkCheckedFieldExpr: genCheckedRecordField(p, n, d) + of nkBlockExpr, nkBlockStmt: genBlock(p, n, d) + of nkStmtListExpr: genStmtListExpr(p, n, d) + of nkStmtList: + for i in countup(0, sonsLen(n) - 1): genStmts(p, n.sons[i]) + of nkIfExpr, nkIfStmt: genIf(p, n, d) + of nkObjDownConv: downConv(p, n, d) + of nkObjUpConv: upConv(p, n, d) + of nkChckRangeF: genRangeChck(p, n, d, "chckRangeF") + of nkChckRange64: genRangeChck(p, n, d, "chckRange64") + of nkChckRange: genRangeChck(p, n, d, "chckRange") + of nkStringToCString: convStrToCStr(p, n, d) + of nkCStringToString: convCStrToStr(p, n, d) of nkLambdaKinds: - var sym = e.sons[namePos].sym + var sym = n.sons[namePos].sym genProc(p.module, sym) if sym.loc.r == nil or sym.loc.t == nil: - InternalError(e.info, "expr: proc not init " & sym.name.s) + InternalError(n.info, "expr: proc not init " & sym.name.s) putLocIntoDest(p, d, sym.loc) - of nkClosure: genClosure(p, e, d) - of nkMetaNode: expr(p, e.sons[0], d) - else: InternalError(e.info, "expr(" & $e.kind & "); unknown node kind") + of nkClosure: genClosure(p, n, d) + of nkMetaNode: expr(p, n.sons[0], d) + + of nkEmpty: nil + of nkWhileStmt: genWhileStmt(p, n) + of nkVarSection, nkLetSection: genVarStmt(p, n) + of nkConstSection: genConstStmt(p, n) + of nkForStmt: internalError(n.info, "for statement not eliminated") + of nkCaseStmt: genCase(p, n, d) + of nkReturnStmt: genReturnStmt(p, n) + of nkBreakStmt: genBreakStmt(p, n) + of nkAsgn: genAsgn(p, n, fastAsgn=false) + of nkFastAsgn: + # transf is overly aggressive with 'nkFastAsgn', so we work around here. + # See tests/run/tcnstseq3 for an example that would fail otherwise. + genAsgn(p, n, fastAsgn=p.prc != nil) + of nkDiscardStmt: + if n.sons[0].kind != nkEmpty: + var a: TLoc + genLineDir(p, n) + initLocExpr(p, n.sons[0], a) + of nkAsmStmt: genAsmStmt(p, n) + of nkTryStmt: + if gCmd == cmdCompileToCpp: genTryCpp(p, n, d) + else: genTry(p, n, d) + of nkRaiseStmt: genRaiseStmt(p, n) + of nkTypeSection: + # we have to emit the type information for object types here to support + # separate compilation: + genTypeSection(p.module, n) + of nkCommentStmt, nkIteratorDef, nkIncludeStmt, + nkImportStmt, nkImportExceptStmt, nkExportStmt, nkExportExceptStmt, + nkFromStmt, nkTemplateDef, nkMacroDef: + nil + of nkPragma: genPragma(p, n) + of nkProcDef, nkMethodDef, nkConverterDef: + if (n.sons[genericParamsPos].kind == nkEmpty): + var prc = n.sons[namePos].sym + if (optDeadCodeElim notin gGlobalOptions and + sfDeadCodeElim notin getModule(prc).flags) or + ({sfExportc, sfCompilerProc} * prc.flags == {sfExportc}) or + (sfExportc in prc.flags and lfExportLib in prc.loc.flags) or + (prc.kind == skMethod): + # we have not only the header: + if prc.getBody.kind != nkEmpty or lfDynamicLib in prc.loc.flags: + genProc(p.module, prc) + of nkParForStmt: genParForStmt(p, n) + of nkState: genState(p, n) + of nkGotoState: genGotoState(p, n) + of nkBreakState: genBreakState(p, n) + else: InternalError(n.info, "expr(" & $n.kind & "); unknown node kind") proc genNamedConstExpr(p: BProc, n: PNode): PRope = if n.kind == nkExprColonExpr: result = genConstExpr(p, n.sons[1]) diff --git a/compiler/ccgstmts.nim b/compiler/ccgstmts.nim index e11678861..b25ad613c 100644 --- a/compiler/ccgstmts.nim +++ b/compiler/ccgstmts.nim @@ -102,6 +102,11 @@ proc genSimpleBlock(p: BProc, stmts: PNode) {.inline.} = genStmts(p, stmts) endBlock(p) +proc exprBlock(p: BProc, n: PNode, d: var TLoc) = + startBlock(p) + expr(p, n, d) + endBlock(p) + template preserveBreakIdx(body: stmt): stmt {.immediate.} = var oldBreakIdx = p.breakIdx body @@ -212,42 +217,48 @@ proc genConstStmt(p: BProc, t: PNode) = appf(p.module.s[cfsData], "NIM_CONST $1 $2 = $3;$n", [getTypeDesc(p.module, c.typ), c.loc.r, genConstExpr(p, c.ast)]) -proc genIfStmt(p: BProc, n: PNode) = +proc genIf(p: BProc, n: PNode, d: var TLoc) = # - # if (!expr1) goto L1; - # { thenPart } + # { if (!expr1) goto L1; + # thenPart } # goto LEnd # L1: - # if (!expr2) goto L2; - # { thenPart2 } + # { if (!expr2) goto L2; + # thenPart2 } # goto LEnd # L2: # { elsePart } # Lend: - var + var a: TLoc Lelse: TLabel + if not isEmptyType(n.typ) and d.k == locNone: + getTemp(p, n.typ, d) genLineDir(p, n) - var Lend = getLabel(p) + let Lend = getLabel(p) for i in countup(0, sonsLen(n) - 1): - var it = n.sons[i] - case it.kind - of nkElifBranch: + let it = n.sons[i] + if it.len == 2: + when newScopeForIf: startBlock(p) initLocExpr(p, it.sons[0], a) Lelse = getLabel(p) inc(p.labels) lineFF(p, cpsStmts, "if (!$1) goto $2;$n", - "br i1 $1, label %LOC$3, label %$2$n" & "LOC$3: $n", + "br i1 $1, label %LOC$3, label %$2$nLOC$3: $n", [rdLoc(a), Lelse, toRope(p.labels)]) - genSimpleBlock(p, it.sons[1]) - if sonsLen(n) > 1: + when not newScopeForIf: startBlock(p) + expr(p, it.sons[1], d) + endBlock(p) + if sonsLen(n) > 1: lineFF(p, cpsStmts, "goto $1;$n", "br label %$1$n", [Lend]) fixLabel(p, Lelse) - of nkElse: - genSimpleBlock(p, it.sons[0]) - else: internalError(n.info, "genIfStmt()") + elif it.len == 1: + startBlock(p) + expr(p, it.sons[0], d) + endBlock(p) + else: internalError(n.info, "genIf()") if sonsLen(n) > 1: fixLabel(p, Lend) - + proc blockLeaveActions(p: BProc, howMany: int) = var L = p.nestedTryStmts.len # danger of endless recursion! we workaround this here by a temp stack @@ -310,16 +321,15 @@ proc genWhileStmt(p: BProc, t: PNode) = proc genBlock(p: BProc, t: PNode, d: var TLoc) = preserveBreakIdx: p.breakIdx = startBlock(p) - if t.sons[0].kind != nkEmpty: + if t.sons[0].kind != nkEmpty: # named block? assert(t.sons[0].kind == nkSym) var sym = t.sons[0].sym sym.loc.k = locOther sym.loc.a = p.breakIdx - if t.kind == nkBlockExpr: genStmtListExpr(p, t.sons[1], d) - else: genStmts(p, t.sons[1]) + expr(p, t.sons[1], d) endBlock(p) - + proc genParForStmt(p: BProc, t: PNode) = assert(sonsLen(t) == 3) inc(p.withinLoop) @@ -409,42 +419,45 @@ proc genCaseGenericBranch(p: BProc, b: PNode, e: TLoc, initLocExpr(p, b.sons[i], x) lineCg(p, cpsStmts, eqFormat, [rdCharLoc(e), rdCharLoc(x), labl]) -proc genCaseSecondPass(p: BProc, t: PNode, labId, until: int): TLabel = +proc genCaseSecondPass(p: BProc, t: PNode, d: var TLoc, + labId, until: int): TLabel = var Lend = getLabel(p) - for i in 1..until: + for i in 1..until: lineF(p, cpsStmts, "LA$1: ;$n", [toRope(labId + i)]) if t.sons[i].kind == nkOfBranch: var length = sonsLen(t.sons[i]) - genSimpleBlock(p, t.sons[i].sons[length - 1]) + exprBlock(p, t.sons[i].sons[length - 1], d) lineF(p, cpsStmts, "goto $1;$n", [Lend]) - else: - genSimpleBlock(p, t.sons[i].sons[0]) + else: + exprBlock(p, t.sons[i].sons[0], d) result = Lend -proc genIfForCaseUntil(p: BProc, t: PNode, rangeFormat, eqFormat: TFormatStr, - until: int, a: TLoc): TLabel = +proc genIfForCaseUntil(p: BProc, t: PNode, d: var TLoc, + rangeFormat, eqFormat: TFormatStr, + until: int, a: TLoc): TLabel = # generate a C-if statement for a Nimrod case statement var labId = p.labels - for i in 1..until: + for i in 1..until: inc(p.labels) if t.sons[i].kind == nkOfBranch: # else statement - genCaseGenericBranch(p, t.sons[i], a, rangeFormat, eqFormat, + genCaseGenericBranch(p, t.sons[i], a, rangeFormat, eqFormat, con("LA", toRope(p.labels))) - else: + else: lineF(p, cpsStmts, "goto LA$1;$n", [toRope(p.labels)]) - if until < t.len-1: + if until < t.len-1: inc(p.labels) var gotoTarget = p.labels lineF(p, cpsStmts, "goto LA$1;$n", [toRope(gotoTarget)]) - result = genCaseSecondPass(p, t, labId, until) + result = genCaseSecondPass(p, t, d, labId, until) lineF(p, cpsStmts, "LA$1: ;$n", [toRope(gotoTarget)]) else: - result = genCaseSecondPass(p, t, labId, until) + result = genCaseSecondPass(p, t, d, labId, until) -proc genCaseGeneric(p: BProc, t: PNode, rangeFormat, eqFormat: TFormatStr) = +proc genCaseGeneric(p: BProc, t: PNode, d: var TLoc, + rangeFormat, eqFormat: TFormatStr) = var a: TLoc initLocExpr(p, t.sons[0], a) - var Lend = genIfForCaseUntil(p, t, rangeFormat, eqFormat, sonsLen(t)-1, a) + var Lend = genIfForCaseUntil(p, t, d, rangeFormat, eqFormat, sonsLen(t)-1, a) fixLabel(p, Lend) proc genCaseStringBranch(p: BProc, b: PNode, e: TLoc, labl: TLabel, @@ -459,12 +472,12 @@ proc genCaseStringBranch(p: BProc, b: PNode, e: TLoc, labl: TLabel, appcg(p.module, branches[j], "if (#eqStrings($1, $2)) goto $3;$n", [rdLoc(e), rdLoc(x), labl]) -proc genStringCase(p: BProc, t: PNode) = +proc genStringCase(p: BProc, t: PNode, d: var TLoc) = # count how many constant strings there are in the case: var strings = 0 - for i in countup(1, sonsLen(t) - 1): + for i in countup(1, sonsLen(t) - 1): if t.sons[i].kind == nkOfBranch: inc(strings, sonsLen(t.sons[i]) - 1) - if strings > stringCaseThreshold: + if strings > stringCaseThreshold: var bitMask = math.nextPowerOfTwo(strings) - 1 var branches: seq[PRope] newSeq(branches, bitMask + 1) @@ -482,23 +495,17 @@ proc genStringCase(p: BProc, t: PNode) = linefmt(p, cpsStmts, "switch (#hashString($1) & $2) {$n", rdLoc(a), toRope(bitMask)) for j in countup(0, high(branches)): - when false: - let interior = cast[int](interiorAllocatedPtr(addr(branches[0])))+ - 2*sizeof(pointer) - let brn = cast[int](cast[pointer](branches)) - if interior != brn: - echo "BUG! ", interior, "-", brn if branches[j] != nil: lineF(p, cpsStmts, "case $1: $n$2break;$n", [intLiteral(j), branches[j]]) lineF(p, cpsStmts, "}$n") # else statement: - if t.sons[sonsLen(t) - 1].kind != nkOfBranch: + if t.sons[sonsLen(t)-1].kind != nkOfBranch: lineF(p, cpsStmts, "goto LA$1;$n", [toRope(p.labels)]) # third pass: generate statements - var Lend = genCaseSecondPass(p, t, labId, sonsLen(t)-1) + var Lend = genCaseSecondPass(p, t, d, labId, sonsLen(t)-1) fixLabel(p, Lend) - else: - genCaseGeneric(p, t, "", "if (#eqStrings($1, $2)) goto $3;$n") + else: + genCaseGeneric(p, t, d, "", "if (#eqStrings($1, $2)) goto $3;$n") proc branchHasTooBigRange(b: PNode): bool = for i in countup(0, sonsLen(b)-2): @@ -533,14 +540,14 @@ proc genCaseRange(p: BProc, branch: PNode) = else: lineF(p, cpsStmts, "case $1:$n", [genLiteral(p, branch[j])]) -proc genOrdinalCase(p: BProc, n: PNode) = +proc genOrdinalCase(p: BProc, n: PNode, d: var TLoc) = # analyse 'case' statement: var splitPoint = IfSwitchSplitPoint(p, n) # generate if part (might be empty): var a: TLoc initLocExpr(p, n.sons[0], a) - var Lend = if splitPoint > 0: genIfForCaseUntil(p, n, + var Lend = if splitPoint > 0: genIfForCaseUntil(p, n, d, rangeFormat = "if ($1 >= $2 && $1 <= $3) goto $4;$n", eqFormat = "if ($1 == $2) goto $3;$n", splitPoint, a) else: nil @@ -553,28 +560,29 @@ proc genOrdinalCase(p: BProc, n: PNode) = var branch = n[i] if branch.kind == nkOfBranch: genCaseRange(p, branch) - genSimpleBlock(p, branch.lastSon) else: # else part of case statement: lineF(p, cpsStmts, "default:$n") - genSimpleBlock(p, branch[0]) hasDefault = true + exprBlock(p, branch.lastSon, d) lineF(p, cpsStmts, "break;$n") if (hasAssume in CC[ccompiler].props) and not hasDefault: lineF(p, cpsStmts, "default: __assume(0);$n") lineF(p, cpsStmts, "}$n") if Lend != nil: fixLabel(p, Lend) -proc genCaseStmt(p: BProc, t: PNode) = +proc genCase(p: BProc, t: PNode, d: var TLoc) = genLineDir(p, t) + if not isEmptyType(t.typ) and d.k == locNone: + getTemp(p, t.typ, d) case skipTypes(t.sons[0].typ, abstractVarRange).kind - of tyString: - genStringCase(p, t) + of tyString: + genStringCase(p, t, d) of tyFloat..tyFloat128: - genCaseGeneric(p, t, "if ($1 >= $2 && $1 <= $3) goto $4;$n", - "if ($1 == $2) goto $3;$n") - else: - genOrdinalCase(p, t) + genCaseGeneric(p, t, d, "if ($1 >= $2 && $1 <= $3) goto $4;$n", + "if ($1 == $2) goto $3;$n") + else: + genOrdinalCase(p, t, d) proc hasGeneralExceptSection(t: PNode): bool = var length = sonsLen(t) @@ -586,7 +594,7 @@ proc hasGeneralExceptSection(t: PNode): bool = inc(i) result = false -proc genTryStmtCpp(p: BProc, t: PNode) = +proc genTryCpp(p: BProc, t: PNode, d: var TLoc) = # code to generate: # # XXX: There should be a standard dispatch algorithm @@ -607,6 +615,8 @@ proc genTryStmtCpp(p: BProc, t: PNode) = # } # } # finallyPart(); + if not isEmptyType(t.typ) and d.k == locNone: + getTemp(p, t.typ, d) var exc: PRope i, length, blen: int @@ -615,7 +625,7 @@ proc genTryStmtCpp(p: BProc, t: PNode) = discard cgsym(p.module, "E_Base") add(p.nestedTryStmts, t) startBlock(p, "try {$n") - genStmts(p, t.sons[0]) + expr(p, t.sons[0], d) length = sonsLen(t) endBlock(p, ropecg(p.module, "} catch (NimException& $1) {$n", [exc])) if optStackTrace in p.Options: @@ -629,7 +639,7 @@ proc genTryStmtCpp(p: BProc, t: PNode) = if blen == 1: # general except section: catchAllPresent = true - genSimpleBlock(p, t.sons[i].sons[0]) + exprBlock(p, t.sons[i].sons[0], d) else: var orExpr: PRope = nil for j in countup(0, blen - 2): @@ -639,7 +649,7 @@ proc genTryStmtCpp(p: BProc, t: PNode) = "#isObj($1.exp->m_type, $2)", [exc, genTypeInfo(p.module, t.sons[i].sons[j].typ)]) lineF(p, cpsStmts, "if ($1) ", [orExpr]) - genSimpleBlock(p, t.sons[i].sons[blen-1]) + exprBlock(p, t.sons[i].sons[blen-1], d) inc(i) # reraise the exception if there was no catch all @@ -649,7 +659,7 @@ proc genTryStmtCpp(p: BProc, t: PNode) = startBlock(p) var finallyBlock = t.lastSon if finallyBlock.kind == nkFinally: - genStmts(p, finallyBlock.sons[0]) + expr(p, finallyBlock.sons[0], d) line(p, cpsStmts, ~"throw;$n") endBlock(p) @@ -658,9 +668,9 @@ proc genTryStmtCpp(p: BProc, t: PNode) = discard pop(p.nestedTryStmts) if (i < length) and (t.sons[i].kind == nkFinally): - genSimpleBlock(p, t.sons[i].sons[0]) + exprBlock(p, t.sons[i].sons[0], d) -proc genTryStmt(p: BProc, t: PNode) = +proc genTry(p: BProc, t: PNode, d: var TLoc) = # code to generate: # # XXX: There should be a standard dispatch algorithm @@ -689,6 +699,8 @@ proc genTryStmt(p: BProc, t: PNode) = # if (exception not cleared) # propagateCurrentException(); # + if not isEmptyType(t.typ) and d.k == locNone: + getTemp(p, t.typ, d) genLineDir(p, t) var safePoint = getTempName() discard cgsym(p.module, "E_Base") @@ -698,7 +710,7 @@ proc genTryStmt(p: BProc, t: PNode) = startBlock(p, "if ($1.status == 0) {$n", [safePoint]) var length = sonsLen(t) add(p.nestedTryStmts, t) - genStmts(p, t.sons[0]) + expr(p, t.sons[0], d) linefmt(p, cpsStmts, "#popSafePoint();$n") endBlock(p) startBlock(p, "else {$n") @@ -714,7 +726,7 @@ proc genTryStmt(p: BProc, t: PNode) = if i > 1: lineF(p, cpsStmts, "else") startBlock(p) linefmt(p, cpsStmts, "$1.status = 0;$n", safePoint) - genStmts(p, t.sons[i].sons[0]) + expr(p, t.sons[i].sons[0], d) linefmt(p, cpsStmts, "#popCurrentException();$n") endBlock(p) else: @@ -728,7 +740,7 @@ proc genTryStmt(p: BProc, t: PNode) = if i > 1: line(p, cpsStmts, "else ") startBlock(p, "if ($1) {$n", [orExpr]) linefmt(p, cpsStmts, "$1.status = 0;$n", safePoint) - genStmts(p, t.sons[i].sons[blen-1]) + expr(p, t.sons[i].sons[blen-1], d) linefmt(p, cpsStmts, "#popCurrentException();$n") endBlock(p) inc(i) @@ -736,7 +748,7 @@ proc genTryStmt(p: BProc, t: PNode) = discard pop(p.nestedTryStmts) endBlock(p) # end of else block if i < length and t.sons[i].kind == nkFinally: - genSimpleBlock(p, t.sons[i].sons[0]) + exprBlock(p, t.sons[i].sons[0], d) linefmt(p, cpsStmts, "if ($1.status != 0) #reraiseException();$n", safePoint) proc genAsmOrEmitStmt(p: BProc, t: PNode): PRope = @@ -860,61 +872,5 @@ proc genAsgn(p: BProc, e: PNode, fastAsgn: bool) = proc genStmts(p: BProc, t: PNode) = var a: TLoc - case t.kind - of nkEmpty: - nil - of nkStmtList: - for i in countup(0, sonsLen(t) - 1): genStmts(p, t.sons[i]) - of nkBlockStmt: genBlock(p, t, a) - of nkIfStmt: genIfStmt(p, t) - of nkWhileStmt: genWhileStmt(p, t) - of nkVarSection, nkLetSection: genVarStmt(p, t) - of nkConstSection: genConstStmt(p, t) - of nkForStmt: internalError(t.info, "for statement not eliminated") - of nkCaseStmt: genCaseStmt(p, t) - of nkReturnStmt: genReturnStmt(p, t) - of nkBreakStmt: genBreakStmt(p, t) - of nkCall, nkHiddenCallConv, nkInfix, nkPrefix, nkPostfix, nkCommand, - nkCallStrLit, nkClosure: - genLineDir(p, t) - initLocExpr(p, t, a) - of nkAsgn: genAsgn(p, t, fastAsgn=false) - of nkFastAsgn: - # transf is overly aggressive with 'nkFastAsgn', so we work around here. - # See tests/run/tcnstseq3 for an example that would fail otherwise. - genAsgn(p, t, fastAsgn=p.prc != nil) - of nkDiscardStmt: - if t.sons[0].kind != nkEmpty: - genLineDir(p, t) - initLocExpr(p, t.sons[0], a) - of nkAsmStmt: genAsmStmt(p, t) - of nkTryStmt: - if gCmd == cmdCompileToCpp: genTryStmtCpp(p, t) - else: genTryStmt(p, t) - of nkRaiseStmt: genRaiseStmt(p, t) - of nkTypeSection: - # we have to emit the type information for object types here to support - # separate compilation: - genTypeSection(p.module, t) - of nkCommentStmt, nkNilLit, nkIteratorDef, nkIncludeStmt, - nkImportStmt, nkImportExceptStmt, nkExportStmt, nkExportExceptStmt, - nkFromStmt, nkTemplateDef, nkMacroDef: - nil - of nkPragma: genPragma(p, t) - of nkProcDef, nkMethodDef, nkConverterDef: - if (t.sons[genericParamsPos].kind == nkEmpty): - var prc = t.sons[namePos].sym - if (optDeadCodeElim notin gGlobalOptions and - sfDeadCodeElim notin getModule(prc).flags) or - ({sfExportc, sfCompilerProc} * prc.flags == {sfExportc}) or - (sfExportc in prc.flags and lfExportLib in prc.loc.flags) or - (prc.kind == skMethod): - # we have not only the header: - if prc.getBody.kind != nkEmpty or lfDynamicLib in prc.loc.flags: - genProc(p.module, prc) - of nkParForStmt: genParForStmt(p, t) - of nkState: genState(p, t) - of nkGotoState: genGotoState(p, t) - of nkBreakState: genBreakState(p, t) - else: internalError(t.info, "genStmts(" & $t.kind & ')') - + expr(p, t, a) + InternalAssert a.k in {locNone, locTemp} diff --git a/compiler/ccgtypes.nim b/compiler/ccgtypes.nim index d37e79d6b..6f54f7e2f 100644 --- a/compiler/ccgtypes.nim +++ b/compiler/ccgtypes.nim @@ -137,17 +137,6 @@ proc mangleName(s: PSym): PRope = app(result, toRope(s.id)) s.loc.r = result -proc isCompileTimeOnly(t: PType): bool = - result = t.kind in {tyTypedesc, tyExpr} - -proc containsCompileTimeOnly(t: PType): bool = - if isCompileTimeOnly(t): return true - if t.sons != nil: - for i in 0 .. <t.sonsLen: - if t.sons[i] != nil and isCompileTimeOnly(t.sons[i]): - return true - return false - proc typeName(typ: PType): PRope = result = if typ.sym != nil: typ.sym.name.s.mangle.toRope else: ~"TY" diff --git a/compiler/cgen.nim b/compiler/cgen.nim index 753576aa0..f034f6675 100644 --- a/compiler/cgen.nim +++ b/compiler/cgen.nim @@ -552,9 +552,21 @@ proc genVarPrototype(m: BModule, sym: PSym) proc requestConstImpl(p: BProc, sym: PSym) proc genProc(m: BModule, prc: PSym) proc genStmts(p: BProc, t: PNode) +proc expr(p: BProc, n: PNode, d: var TLoc) proc genProcPrototype(m: BModule, sym: PSym) +proc putLocIntoDest(p: BProc, d: var TLoc, s: TLoc) +proc genAssignment(p: BProc, dest, src: TLoc, flags: TAssignmentFlags) +proc intLiteral(i: biggestInt): PRope +proc genLiteral(p: BProc, n: PNode): PRope -include "ccgexprs.nim", "ccgstmts.nim" +proc initLocExpr(p: BProc, e: PNode, result: var TLoc) = + initLoc(result, locNone, e.typ, OnUnknown) + expr(p, e, result) + +proc lenField: PRope {.inline.} = + result = toRope(if gCmd != cmdCompileToCpp: "Sup.len" else: "len") + +include ccgcalls, "ccgstmts.nim", "ccgexprs.nim" # ----------------------------- dynamic library handling ----------------- # We don't finalize dynamic libs as this does the OS for us. @@ -588,6 +600,7 @@ proc loadDynamicLib(m: BModule, lib: PLib) = [loadlib, getStrLit(m, lib.path.strVal)]) else: var p = newProc(nil, m) + p.options = p.options - {optStackTrace, optEndb} var dest: TLoc initLocExpr(p, lib.path, dest) app(m.s[cfsVars], p.s(cpsLocals)) @@ -1266,10 +1279,6 @@ proc updateCachedModule(m: BModule) = addFileToLink(cfilenoext) -proc updateCachedModules* = - for m in cgenModules(): - if m.fromCache: m.updateCachedModule - proc myClose(b: PPassContext, n: PNode): PNode = result = n if b == nil or passes.skipCodegen(n): return @@ -1279,24 +1288,28 @@ proc myClose(b: PPassContext, n: PNode): PNode = genStmts(m.initProc, n) # cached modules need to registered too: registerModuleToMain(m.module) - + if sfMainModule in m.module.flags: var disp = generateMethodDispatchers() for i in 0..sonsLen(disp)-1: genProcAux(m, disp.sons[i].sym) - genMainProc(m) - # we need to process the transitive closure because recursive module - # deps are allowed (and the system module is processed in the wrong - # order anyway) - if generatedHeader != nil: finishModule(generatedHeader) - while gForwardedProcsCounter > 0: - for m in cgenModules(): - if not m.fromCache: - finishModule(m) + genMainProc(m) + +proc cgenWriteModules* = + # we need to process the transitive closure because recursive module + # deps are allowed (and the system module is processed in the wrong + # order anyway) + if generatedHeader != nil: finishModule(generatedHeader) + while gForwardedProcsCounter > 0: for m in cgenModules(): if not m.fromCache: - writeModule(m, pending=true) - writeMapping(gMapping) - if generatedHeader != nil: writeHeader(generatedHeader) + finishModule(m) + for m in cgenModules(): + if m.fromCache: + m.updateCachedModule + else: + m.writeModule(pending=true) + writeMapping(gMapping) + if generatedHeader != nil: writeHeader(generatedHeader) const cgenPass* = makePass(myOpen, myOpenCached, myProcess, myClose) diff --git a/compiler/commands.nim b/compiler/commands.nim index 27da03bbe..63704b328 100644 --- a/compiler/commands.nim +++ b/compiler/commands.nim @@ -207,6 +207,22 @@ proc processPath(path: string, notRelativeToProj = false): string = "projectname", options.gProjectName, "projectpath", options.gProjectPath]) +proc trackDirty(arg: string, info: TLineInfo) = + var a = arg.split(',') + if a.len != 4: LocalError(info, errTokenExpected, + "DIRTY_BUFFER,ORIGINAL_FILE,LINE,COLUMN") + var line, column: int + if parseUtils.parseInt(a[2], line) <= 0: + LocalError(info, errInvalidNumber, a[1]) + if parseUtils.parseInt(a[3], column) <= 0: + LocalError(info, errInvalidNumber, a[2]) + + gDirtyBufferIdx = a[0].fileInfoIdx + gDirtyOriginalIdx = a[1].fileInfoIdx + + optTrackPos = newLineInfo(gDirtyBufferIdx, line, column) + msgs.addCheckpoint(optTrackPos) + proc track(arg: string, info: TLineInfo) = var a = arg.split(',') if a.len != 3: LocalError(info, errTokenExpected, "FILE,LINE,COLUMN") @@ -215,7 +231,8 @@ proc track(arg: string, info: TLineInfo) = LocalError(info, errInvalidNumber, a[1]) if parseUtils.parseInt(a[2], column) <= 0: LocalError(info, errInvalidNumber, a[2]) - msgs.addCheckpoint(newLineInfo(a[0], line, column)) + optTrackPos = newLineInfo(a[0], line, column) + msgs.addCheckpoint(optTrackPos) proc dynlibOverride(switch, arg: string, pass: TCmdlinePass, info: TLineInfo) = if pass in {passCmd2, passPP}: @@ -469,6 +486,9 @@ proc processSwitch(switch, arg: string, pass: TCmdlinePass, info: TLineInfo) = of "track": expectArg(switch, arg, pass, info) track(arg, info) + of "trackdirty": + expectArg(switch, arg, pass, info) + trackDirty(arg, info) of "suggest": expectNoArg(switch, arg, pass, info) incl(gGlobalOptions, optSuggest) diff --git a/compiler/docgen.nim b/compiler/docgen.nim index 2b7c567c6..9929b4bd9 100644 --- a/compiler/docgen.nim +++ b/compiler/docgen.nim @@ -237,7 +237,7 @@ proc genItem(d: PDoc, n, nameNode: PNode, k: TSymKind) = of tkSymbol: dispA(result, "<span class=\"Identifier\">$1</span>", "\\spanIdentifier{$1}", [toRope(esc(d.target, literal))]) - of tkInd, tkSad, tkDed, tkSpaces, tkInvalid: + of tkSpaces, tkInvalid: app(result, literal) of tkParLe, tkParRi, tkBracketLe, tkBracketRi, tkCurlyLe, tkCurlyRi, tkBracketDotLe, tkBracketDotRi, tkCurlyDotLe, tkCurlyDotRi, tkParDotLe, diff --git a/compiler/extccomp.nim b/compiler/extccomp.nim index bb2f09151..44396100b 100644 --- a/compiler/extccomp.nim +++ b/compiler/extccomp.nim @@ -579,7 +579,7 @@ proc CallCCompiler*(projectfile: string) = else: rawMessage(errGenerated, " execution of an external program failed; " & "rerun with --parallelBuild:1 to see the error message") - if optNoLinking notin gGlobalOptions: + if optNoLinking notin gGlobalOptions and cmds.len > 0: # call the linker: var it = PStrEntry(toLink.head) var objfiles = "" @@ -603,7 +603,9 @@ proc CallCCompiler*(projectfile: string) = if optGenGuiApp in gGlobalOptions: buildGui = cc[c].buildGui else: buildGui = "" var exefile: string - if optGenDynLib in gGlobalOptions: + if options.outFile.len > 0: + exefile = options.outFile + elif optGenDynLib in gGlobalOptions: exefile = platform.os[targetOS].dllFrmt % splitFile(projectFile).name buildDll = cc[c].buildDll else: diff --git a/compiler/jsgen.nim b/compiler/jsgen.nim index f2e317f47..a76f8a883 100644 --- a/compiler/jsgen.nim +++ b/compiler/jsgen.nim @@ -8,18 +8,17 @@ # # This is the EMCAScript (also known as JavaScript) code generator. -# **Invariant: each expression occurs only once in the generated -# code!** +# Soon also a Luajit code generator. ;-) -import +import ast, astalgo, strutils, hashes, trees, platform, magicsys, extccomp, options, nversion, nimsets, msgs, crc, bitsets, idents, lists, types, os, times, ropes, math, passes, ccgutils, wordrecg, renderer, rodread, rodutils, intsets, cgmeth -# implementation - -type +type + TTarget = enum + targetJS, targetLua TJSGen = object of TPassContext module: PSym @@ -34,36 +33,45 @@ type etyString, # JavaScript's string etyObject, # JavaScript's reference to an object etyBaseIndex # base + index needed - TCompRes{.final.} = object - kind: TJSTypeKind - com: PRope # computation part - # address if this is a (address, index)-tuple + TResKind = enum + resNone, # not set + resExpr, # is some complex expression + resVal # is a temporary/value/l-value + TCompRes = object + kind: TResKind + typ: TJSTypeKind res: PRope # result part; index if this is an # (address, index)-tuple + address: PRope # address of an (address, index)-tuple - TBlock{.final.} = object + TBlock = object id: int # the ID of the label; positive means that it # has been used (i.e. the label should be emitted) isLoop: bool # whether it's a 'block' or 'while' - TGlobals{.final.} = object + TGlobals = object typeInfo, code: PRope forwarded: seq[PSym] generatedSyms: TIntSet typeInfoGenerated: TIntSet PGlobals = ref TGlobals - TProc{.final.} = object + PProc = ref TProc + TProc = object procDef: PNode prc: PSym - locals: PRope + locals, body: PRope options: TOptions module: BModule g: PGlobals BeforeRetNeeded: bool - unique: int + target: TTarget # duplicated here for faster dispatching + unique: int # for temp identifier generation blocks: seq[TBlock] + up: PProc # up the call chain; required for closure support +template `|`(a, b: expr): expr {.immediate.} = + (if p.target == targetJS: a else: b) proc newGlobals(): PGlobals = new(result) @@ -72,25 +80,35 @@ proc newGlobals(): PGlobals = result.typeInfoGenerated = initIntSet() proc initCompRes(r: var TCompRes) = - r.com = nil + r.address = nil r.res = nil - r.kind = etyNone - -proc initProc(p: var TProc, globals: PGlobals, module: BModule, procDef: PNode, - options: TOptions) = - p.blocks = @[] - p.options = options - p.module = module - p.procDef = procDef - p.g = globals - if procDef != nil: p.prc = procDef.sons[namePos].sym + r.typ = etyNone + r.kind = resNone + +proc rdLoc(a: TCompRes): PRope {.inline.} = + result = a.res + when false: + if a.typ != etyBaseIndex: + result = a.res + else: + result = ropef("$1[$2]", a.address, a.res) + +proc newProc(globals: PGlobals, module: BModule, procDef: PNode, + options: TOptions): PProc = + result = PProc( + blocks: @[], + options: options, + module: module, + procDef: procDef, + g: globals) + if procDef != nil: result.prc = procDef.sons[namePos].sym const MappedToObject = {tyObject, tyArray, tyArrayConstr, tyTuple, tyOpenArray, tySet, tyVar, tyRef, tyPtr, tyBigNum, tyVarargs} proc mapType(typ: PType): TJSTypeKind = - var t = skipTypes(typ, abstractInst) + let t = skipTypes(typ, abstractInst) case t.kind of tyVar, tyRef, tyPtr: if skipTypes(t.sons[0], abstractInst).kind in mappedToObject: @@ -140,173 +158,20 @@ proc mangleName(s: PSym): PRope = proc makeJSString(s: string): PRope = strutils.escape(s).toRope -proc genTypeInfo(p: var TProc, typ: PType): PRope -proc genObjectFields(p: var TProc, typ: PType, n: PNode): PRope = - var - s, u: PRope - length: int - field: PSym - b: PNode - result = nil - case n.kind - of nkRecList: - length = sonsLen(n) - if length == 1: - result = genObjectFields(p, typ, n.sons[0]) - else: - s = nil - for i in countup(0, length - 1): - if i > 0: app(s, ", " & tnl) - app(s, genObjectFields(p, typ, n.sons[i])) - result = ropef("{kind: 2, len: $1, offset: 0, " & - "typ: null, name: null, sons: [$2]}", [toRope(length), s]) - of nkSym: - field = n.sym - s = genTypeInfo(p, field.typ) - result = ropef("{kind: 1, offset: \"$1\", len: 0, " & - "typ: $2, name: $3, sons: null}", - [mangleName(field), s, makeJSString(field.name.s)]) - of nkRecCase: - length = sonsLen(n) - if (n.sons[0].kind != nkSym): InternalError(n.info, "genObjectFields") - field = n.sons[0].sym - s = genTypeInfo(p, field.typ) - for i in countup(1, length - 1): - b = n.sons[i] # branch - u = nil - case b.kind - of nkOfBranch: - if sonsLen(b) < 2: - internalError(b.info, "genObjectFields; nkOfBranch broken") - for j in countup(0, sonsLen(b) - 2): - if u != nil: app(u, ", ") - if b.sons[j].kind == nkRange: - appf(u, "[$1, $2]", [toRope(getOrdValue(b.sons[j].sons[0])), - toRope(getOrdValue(b.sons[j].sons[1]))]) - else: - app(u, toRope(getOrdValue(b.sons[j]))) - of nkElse: - u = toRope(lengthOrd(field.typ)) - else: internalError(n.info, "genObjectFields(nkRecCase)") - if result != nil: app(result, ", " & tnl) - appf(result, "[SetConstr($1), $2]", - [u, genObjectFields(p, typ, lastSon(b))]) - result = ropef("{kind: 3, offset: \"$1\", len: $3, " & - "typ: $2, name: $4, sons: [$5]}", [mangleName(field), s, - toRope(lengthOrd(field.typ)), makeJSString(field.name.s), result]) - else: internalError(n.info, "genObjectFields") - -proc genObjectInfo(p: var TProc, typ: PType, name: PRope) = - var s = ropef("var $1 = {size: 0, kind: $2, base: null, node: null, " & - "finalizer: null};$n", [name, toRope(ord(typ.kind))]) - prepend(p.g.typeInfo, s) - appf(p.g.typeInfo, "var NNI$1 = $2;$n", - [toRope(typ.id), genObjectFields(p, typ, typ.n)]) - appf(p.g.typeInfo, "$1.node = NNI$2;$n", [name, toRope(typ.id)]) - if (typ.kind == tyObject) and (typ.sons[0] != nil): - appf(p.g.typeInfo, "$1.base = $2;$n", - [name, genTypeInfo(p, typ.sons[0])]) - -proc genTupleFields(p: var TProc, typ: PType): PRope = - var s: PRope = nil - for i in 0 .. <typ.len: - if i > 0: app(s, ", " & tnl) - s.appf("{kind: 1, offset: \"Field$1\", len: 0, " & - "typ: $2, name: \"Field$1\", sons: null}", - [i.toRope, genTypeInfo(p, typ.sons[i])]) - result = ropef("{kind: 2, len: $1, offset: 0, " & - "typ: null, name: null, sons: [$2]}", [toRope(typ.len), s]) - -proc genTupleInfo(p: var TProc, typ: PType, name: PRope) = - var s = ropef("var $1 = {size: 0, kind: $2, base: null, node: null, " & - "finalizer: null};$n", [name, toRope(ord(typ.kind))]) - prepend(p.g.typeInfo, s) - appf(p.g.typeInfo, "var NNI$1 = $2;$n", - [toRope(typ.id), genTupleFields(p, typ)]) - appf(p.g.typeInfo, "$1.node = NNI$2;$n", [name, toRope(typ.id)]) - -proc genEnumInfo(p: var TProc, typ: PType, name: PRope) = - let length = sonsLen(typ.n) - var s: PRope = nil - for i in countup(0, length - 1): - if (typ.n.sons[i].kind != nkSym): InternalError(typ.n.info, "genEnumInfo") - let field = typ.n.sons[i].sym - if i > 0: app(s, ", " & tnl) - let extName = if field.ast == nil: field.name.s else: field.ast.strVal - appf(s, "{kind: 1, offset: $1, typ: $2, name: $3, len: 0, sons: null}", - [toRope(field.position), name, makeJSString(extName)]) - var n = ropef("var NNI$1 = {kind: 2, offset: 0, typ: null, " & - "name: null, len: $2, sons: [$3]};$n", [toRope(typ.id), toRope(length), s]) - s = ropef("var $1 = {size: 0, kind: $2, base: null, node: null, " & - "finalizer: null};$n", [name, toRope(ord(typ.kind))]) - prepend(p.g.typeInfo, s) - app(p.g.typeInfo, n) - appf(p.g.typeInfo, "$1.node = NNI$2;$n", [name, toRope(typ.id)]) - if typ.sons[0] != nil: - appf(p.g.typeInfo, "$1.base = $2;$n", - [name, genTypeInfo(p, typ.sons[0])]) - -proc genTypeInfo(p: var TProc, typ: PType): PRope = - var t = typ - if t.kind == tyGenericInst: t = lastSon(t) - result = ropef("NTI$1", [toRope(t.id)]) - if ContainsOrIncl(p.g.TypeInfoGenerated, t.id): return - case t.kind - of tyDistinct: - result = genTypeInfo(p, typ.sons[0]) - of tyPointer, tyProc, tyBool, tyChar, tyCString, tyString, tyInt..tyFloat128: - var s = ropef( - "var $1 = {size: 0,kind: $2,base: null,node: null,finalizer: null};$n", - [result, toRope(ord(t.kind))]) - prepend(p.g.typeInfo, s) - of tyVar, tyRef, tyPtr, tySequence, tyRange, tySet: - var s = ropef( - "var $1 = {size: 0,kind: $2,base: null,node: null,finalizer: null};$n", - [result, toRope(ord(t.kind))]) - prepend(p.g.typeInfo, s) - appf(p.g.typeInfo, "$1.base = $2;$n", - [result, genTypeInfo(p, typ.sons[0])]) - of tyArrayConstr, tyArray: - var s = ropef( - "var $1 = {size: 0,kind: $2,base: null,node: null,finalizer: null};$n", - [result, toRope(ord(t.kind))]) - prepend(p.g.typeInfo, s) - appf(p.g.typeInfo, "$1.base = $2;$n", - [result, genTypeInfo(p, typ.sons[1])]) - of tyEnum: genEnumInfo(p, t, result) - of tyObject: genObjectInfo(p, t, result) - of tyTuple: genTupleInfo(p, t, result) - else: InternalError("genTypeInfo(" & $t.kind & ')') - -proc gen(p: var TProc, n: PNode, r: var TCompRes) -proc genStmt(p: var TProc, n: PNode, r: var TCompRes) -proc genProc(oldProc: var TProc, prc: PSym, r: var TCompRes) -proc genConstant(p: var TProc, c: PSym, r: var TCompRes) - -proc mergeExpr(a, b: PRope): PRope = - if (a != nil): - if b != nil: result = ropef("($1, $2)", [a, b]) - else: result = a - else: - result = b +include jstypes -proc mergeExpr(r: TCompRes): PRope = - result = mergeExpr(r.com, r.res) +proc gen(p: PProc, n: PNode, r: var TCompRes) +proc genStmt(p: PProc, n: PNode) +proc genProc(oldProc: PProc, prc: PSym): PRope +proc genConstant(p: PProc, c: PSym) -proc mergeStmt(r: TCompRes): PRope = - if r.res == nil: result = r.com - elif r.com == nil: result = r.res - else: result = ropef("$1$2", [r.com, r.res]) - -proc useMagic(p: var TProc, name: string) = +proc useMagic(p: PProc, name: string) = if name.len == 0: return var s = magicsys.getCompilerProc(name) if s != nil: internalAssert s.kind in {skProc, skMethod, skConverter} if not p.g.generatedSyms.containsOrIncl(s.id): - var r: TCompRes - genProc(p, s, r) - app(p.g.code, mergeStmt(r)) + app(p.g.code, genProc(p, s)) else: # we used to exclude the system module from this check, but for DLL # generation support this sloppyness leads to hard to detect bugs, so @@ -314,19 +179,67 @@ proc useMagic(p: var TProc, name: string) = if p.prc != nil: GlobalError(p.prc.info, errSystemNeeds, name) else: rawMessage(errSystemNeeds, name) -proc genAnd(p: var TProc, a, b: PNode, r: var TCompRes) = - var x, y: TCompRes - gen(p, a, x) - gen(p, b, y) - r.res = ropef("($1 && $2)", [mergeExpr(x), mergeExpr(y)]) +proc isSimpleExpr(n: PNode): bool = + # calls all the way down --> can stay expression based + if n.kind in nkCallKinds+{nkBracketExpr, nkBracket, nkCurly, nkDotExpr, nkPar, + nkObjConstr}: + for c in n: + if not c.isSimpleExpr: return false + result = true + elif n.isAtom: + result = true + +proc getTemp(p: PProc): PRope = + inc(p.unique) + result = ropef("Tmp$1", [toRope(p.unique)]) + appf(p.locals, "var $1;$n" | "local $1;$n", [result]) -proc genOr(p: var TProc, a, b: PNode, r: var TCompRes) = +proc genAnd(p: PProc, a, b: PNode, r: var TCompRes) = + assert r.kind == resNone var x, y: TCompRes - gen(p, a, x) - gen(p, b, y) - r.res = ropef("($1 || $2)", [mergeExpr(x), mergeExpr(y)]) - -type + if a.isSimpleExpr and b.isSimpleExpr: + gen(p, a, x) + gen(p, b, y) + r.kind = resExpr + r.res = ropef("($1 && $2)" | "($1 and $2)", [x.rdLoc, y.rdLoc]) + else: + r.res = p.getTemp + r.kind = resVal + # while a and b: + # --> + # while true: + # aa + # if not a: tmp = false + # else: + # bb + # tmp = b + # tmp + gen(p, a, x) + p.body.appf("if (!$1) $2 = false; else {" | + "if not $1 then $2 = false; else", x.rdLoc, r.rdLoc) + gen(p, b, y) + p.body.appf("$2 = $1; }" | + "$2 = $1 end", y.rdLoc, r.rdLoc) + +proc genOr(p: PProc, a, b: PNode, r: var TCompRes) = + assert r.kind == resNone + var x, y: TCompRes + if a.isSimpleExpr and b.isSimpleExpr: + gen(p, a, x) + gen(p, b, y) + r.kind = resExpr + r.res = ropef("($1 || $2)" | "($1 or $2)", [x.rdLoc, y.rdLoc]) + else: + r.res = p.getTemp + r.kind = resVal + gen(p, a, x) + p.body.appf("if ($1) $2 = true; else {" | + "if $1 then $2 = true; else", x.rdLoc, r.rdLoc) + gen(p, b, y) + p.body.appf("$2 = $1; }" | + "$2 = $1 end", y.rdLoc, r.rdLoc) + +type TMagicFrmt = array[0..3, string] const # magic checked op; magic unchecked op; checked op; unchecked op @@ -430,86 +343,84 @@ const # magic checked op; magic unchecked op; checked op; unchecked op ["cstrToNimstr", "cstrToNimstr", "cstrToNimstr($1)", "cstrToNimstr($1)"], ["", "", "$1", "$1"]] -proc binaryExpr(p: var TProc, n: PNode, r: var TCompRes, magic, frmt: string) = +proc binaryExpr(p: PProc, n: PNode, r: var TCompRes, magic, frmt: string) = var x, y: TCompRes useMagic(p, magic) gen(p, n.sons[1], x) gen(p, n.sons[2], y) - r.res = ropef(frmt, [x.res, y.res]) - r.com = mergeExpr(x.com, y.com) + r.res = ropef(frmt, [x.rdLoc, y.rdLoc]) + r.kind = resExpr -proc binaryStmt(p: var TProc, n: PNode, r: var TCompRes, magic, frmt: string) = - var x, y: TCompRes - useMagic(p, magic) - gen(p, n.sons[1], x) - gen(p, n.sons[2], y) - if x.com != nil: appf(r.com, "$1;$n", [x.com]) - if y.com != nil: appf(r.com, "$1;$n", [y.com]) - appf(r.com, frmt, [x.res, y.res]) - -proc unaryExpr(p: var TProc, n: PNode, r: var TCompRes, magic, frmt: string) = +proc unaryExpr(p: PProc, n: PNode, r: var TCompRes, magic, frmt: string) = useMagic(p, magic) gen(p, n.sons[1], r) - r.res = ropef(frmt, [r.res]) + r.res = ropef(frmt, [r.rdLoc]) + r.kind = resExpr -proc arith(p: var TProc, n: PNode, r: var TCompRes, op: TMagic) = - var +proc arith(p: PProc, n: PNode, r: var TCompRes, op: TMagic) = + var x, y: TCompRes - i: int - if optOverflowCheck in p.options: i = 0 - else: i = 1 + let i = ord(optOverflowCheck notin p.options) useMagic(p, ops[op][i]) - if sonsLen(n) > 2: + if sonsLen(n) > 2: gen(p, n.sons[1], x) gen(p, n.sons[2], y) - r.res = ropef(ops[op][i + 2], [x.res, y.res]) - r.com = mergeExpr(x.com, y.com) - else: + r.res = ropef(ops[op][i + 2], [x.rdLoc, y.rdLoc]) + else: gen(p, n.sons[1], r) - r.res = ropef(ops[op][i + 2], [r.res]) + r.res = ropef(ops[op][i + 2], [r.rdLoc]) + r.kind = resExpr -proc genLineDir(p: var TProc, n: PNode, r: var TCompRes) = - var line: int - line = toLinenumber(n.info) - if optLineDir in p.Options: - appf(r.com, "// line $2 \"$1\"$n", +proc genLineDir(p: PProc, n: PNode) = + let line = toLinenumber(n.info) + if optLineDir in p.Options: + appf(p.body, "// line $2 \"$1\"$n", [toRope(toFilename(n.info)), toRope(line)]) - if ({optStackTrace, optEndb} * p.Options == {optStackTrace, optEndb}) and - ((p.prc == nil) or not (sfPure in p.prc.flags)): + if {optStackTrace, optEndb} * p.Options == {optStackTrace, optEndb} and + ((p.prc == nil) or sfPure notin p.prc.flags): useMagic(p, "endb") - appf(r.com, "endb($1);$n", [toRope(line)]) + appf(p.body, "endb($1);$n", [toRope(line)]) elif ({optLineTrace, optStackTrace} * p.Options == {optLineTrace, optStackTrace}) and ((p.prc == nil) or not (sfPure in p.prc.flags)): - appf(r.com, "F.line = $1;$n", [toRope(line)]) + appf(p.body, "F.line = $1;$n", [toRope(line)]) -proc genWhileStmt(p: var TProc, n: PNode, r: var TCompRes) = - var - cond, stmt: TCompRes - length, labl: int - genLineDir(p, n, r) +proc genWhileStmt(p: PProc, n: PNode) = + var + cond: TCompRes + internalAssert isEmptyType(n.typ) + genLineDir(p, n) inc(p.unique) - length = len(p.blocks) + var length = len(p.blocks) setlen(p.blocks, length + 1) p.blocks[length].id = - p.unique p.blocks[length].isLoop = true - labl = p.unique + let labl = p.unique.toRope + appf(p.body, "L$1: while (true) {$n" | "while true do$n", labl) gen(p, n.sons[0], cond) - genStmt(p, n.sons[1], stmt) - if p.blocks[length].id > 0: - appf(r.com, "L$3: while ($1) {$n$2}$n", - [mergeExpr(cond), mergeStmt(stmt), toRope(labl)]) - else: - appf(r.com, "while ($1) {$n$2}$n", [mergeExpr(cond), mergeStmt(stmt)]) + appf(p.body, "if (!$1) break L$2;$n" | "if not $1 then goto ::L$2:: end;$n", + [cond.res, labl]) + genStmt(p, n.sons[1]) + appf(p.body, "}$n" | "end$n", []) setlen(p.blocks, length) -proc genTryStmt(p: var TProc, n: PNode, r: var TCompRes) = +proc moveInto(p: PProc, src: var TCompRes, dest: TCompRes) = + if src.kind != resNone: + if dest.kind != resNone: + p.body.appf("$1 = $2;$n", dest.rdLoc, src.rdLoc) + else: + p.body.appf("$1;$n", src.rdLoc) + src.kind = resNone + src.res = nil + +proc genTry(p: PProc, n: PNode, r: var TCompRes) = # code to generate: # # var sp = {prev: excHandler, exc: null}; # excHandler = sp; # try { # stmts; + # TMP = e # } catch (e) { # if (e.typ && e.typ == NTI433 || e.typ == NTI2321) { # stmts; @@ -522,116 +433,113 @@ proc genTryStmt(p: var TProc, n: PNode, r: var TCompRes) = # stmts; # excHandler = excHandler.prev; # } - # - var - i, length, blen: int - safePoint, orExpr, epart: PRope - a: TCompRes - genLineDir(p, n, r) + genLineDir(p, n) inc(p.unique) - safePoint = ropef("Tmp$1", [toRope(p.unique)]) - appf(r.com, + var safePoint = ropef("Tmp$1", [toRope(p.unique)]) + appf(p.body, "var $1 = {prev: excHandler, exc: null};$n" & "excHandler = $1;$n", [safePoint]) - if optStackTrace in p.Options: app(r.com, "framePtr = F;" & tnl) - app(r.com, "try {" & tnl) - length = sonsLen(n) - genStmt(p, n.sons[0], a) - app(r.com, mergeStmt(a)) - i = 1 - epart = nil - while (i < length) and (n.sons[i].kind == nkExceptBranch): - blen = sonsLen(n.sons[i]) + if optStackTrace in p.Options: app(p.body, "framePtr = F;" & tnl) + app(p.body, "try {" & tnl) + var length = sonsLen(n) + var a: TCompRes + gen(p, n.sons[0], a) + if not isEmptyType(n.typ): + r.kind = resVal + r.res = getTemp(p) + moveInto(p, a, r) + var i = 1 + if length > 1 and n.sons[i].kind == nkExceptBranch: + appf(p.body, "} catch (EXC) {$n") + while i < length and n.sons[i].kind == nkExceptBranch: + let blen = sonsLen(n.sons[i]) if blen == 1: # general except section: - if i > 1: app(epart, "else {" & tnl) - genStmt(p, n.sons[i].sons[0], a) - app(epart, mergeStmt(a)) - if i > 1: app(epart, '}' & tnl) - else: - orExpr = nil + if i > 1: app(p.body, "else {" & tnl) + gen(p, n.sons[i].sons[0], a) + moveInto(p, a, r) + if i > 1: app(p.body, '}' & tnl) + else: + var orExpr: PRope = nil useMagic(p, "isObj") for j in countup(0, blen - 2): - if (n.sons[i].sons[j].kind != nkType): + if n.sons[i].sons[j].kind != nkType: InternalError(n.info, "genTryStmt") if orExpr != nil: app(orExpr, "||") appf(orExpr, "isObj($1.exc.m_type, $2)", [safePoint, genTypeInfo(p, n.sons[i].sons[j].typ)]) - if i > 1: app(epart, "else ") - appf(epart, "if ($1.exc && $2) {$n", [safePoint, orExpr]) - genStmt(p, n.sons[i].sons[blen - 1], a) - appf(epart, "$1}$n", [mergeStmt(a)]) + if i > 1: app(p.body, "else ") + appf(p.body, "if ($1.exc && ($2)) {$n", [safePoint, orExpr]) + gen(p, n.sons[i].sons[blen - 1], a) + moveInto(p, a, r) + appf(p.body, "}$n") inc(i) - if epart != nil: appf(r.com, "} catch (EXC) {$n$1", [epart]) - app(r.com, "} finally {" & tnl & "excHandler = excHandler.prev;" & tnl) - if (i < length) and (n.sons[i].kind == nkFinally): - genStmt(p, n.sons[i].sons[0], a) - app(r.com, mergeStmt(a)) - app(r.com, '}' & tnl) - -proc genRaiseStmt(p: var TProc, n: PNode, r: var TCompRes) = - var - a: TCompRes - typ: PType - genLineDir(p, n, r) - if n.sons[0].kind != nkEmpty: + app(p.body, "} finally {" & tnl & "excHandler = excHandler.prev;" & tnl) + if i < length and n.sons[i].kind == nkFinally: + gen(p, n.sons[i].sons[0], a) + moveInto(p, a, r) + app(p.body, '}' & tnl) + +proc genRaiseStmt(p: PProc, n: PNode) = + genLineDir(p, n) + if n.sons[0].kind != nkEmpty: + var a: TCompRes gen(p, n.sons[0], a) - if a.com != nil: appf(r.com, "$1;$n", [a.com]) - typ = skipTypes(n.sons[0].typ, abstractPtrs) + let typ = skipTypes(n.sons[0].typ, abstractPtrs) useMagic(p, "raiseException") - appf(r.com, "raiseException($1, $2);$n", - [a.res, makeJSString(typ.sym.name.s)]) - else: + appf(p.body, "raiseException($1, $2);$n", + [a.rdLoc, makeJSString(typ.sym.name.s)]) + else: useMagic(p, "reraiseException") - app(r.com, "reraiseException();" & tnl) + app(p.body, "reraiseException();" & tnl) -proc genCaseStmt(p: var TProc, n: PNode, r: var TCompRes) = - var +proc genCase(p: PProc, n: PNode, r: var TCompRes) = + var cond, stmt: TCompRes - it, e, v: PNode - stringSwitch: bool - genLineDir(p, n, r) + genLineDir(p, n) gen(p, n.sons[0], cond) - if cond.com != nil: appf(r.com, "$1;$n", [cond.com]) - stringSwitch = skipTypes(n.sons[0].typ, abstractVar).kind == tyString + let stringSwitch = skipTypes(n.sons[0].typ, abstractVar).kind == tyString if stringSwitch: useMagic(p, "toJSStr") - appf(r.com, "switch (toJSStr($1)) {$n", [cond.res]) - else: - appf(r.com, "switch ($1) {$n", [cond.res]) + appf(p.body, "switch (toJSStr($1)) {$n", [cond.rdLoc]) + else: + appf(p.body, "switch ($1) {$n", [cond.rdLoc]) + if not isEmptyType(n.typ): + r.kind = resVal + r.res = getTemp(p) for i in countup(1, sonsLen(n) - 1): - it = n.sons[i] + let it = n.sons[i] case it.kind of nkOfBranch: for j in countup(0, sonsLen(it) - 2): - e = it.sons[j] + let e = it.sons[j] if e.kind == nkRange: - v = copyNode(e.sons[0]) - while (v.intVal <= e.sons[1].intVal): + var v = copyNode(e.sons[0]) + while v.intVal <= e.sons[1].intVal: gen(p, v, cond) - if cond.com != nil: internalError(v.info, "jsgen.genCaseStmt") - appf(r.com, "case $1: ", [cond.res]) + appf(p.body, "case $1: ", [cond.rdLoc]) Inc(v.intVal) - else: - gen(p, e, cond) - if cond.com != nil: internalError(e.info, "jsgen.genCaseStmt") + else: if stringSwitch: case e.kind - of nkStrLit..nkTripleStrLit: appf(r.com, "case $1: ", + of nkStrLit..nkTripleStrLit: appf(p.body, "case $1: ", [makeJSString(e.strVal)]) else: InternalError(e.info, "jsgen.genCaseStmt: 2") else: - appf(r.com, "case $1: ", [cond.res]) - genStmt(p, lastSon(it), stmt) - appf(r.com, "$n$1break;$n", [mergeStmt(stmt)]) - of nkElse: - genStmt(p, it.sons[0], stmt) - appf(r.com, "default: $n$1break;$n", [mergeStmt(stmt)]) + gen(p, e, cond) + appf(p.body, "case $1: ", [cond.rdLoc]) + gen(p, lastSon(it), stmt) + moveInto(p, stmt, r) + appf(p.body, "$nbreak;$n") + of nkElse: + appf(p.body, "default: $n") + gen(p, it.sons[0], stmt) + moveInto(p, stmt, r) + appf(p.body, "break;$n") else: internalError(it.info, "jsgen.genCaseStmt") - appf(r.com, "}$n", []) + appf(p.body, "}$n") -proc genStmtListExpr(p: var TProc, n: PNode, r: var TCompRes) -proc genBlock(p: var TProc, n: PNode, r: var TCompRes) = +proc genBlock(p: PProc, n: PNode, r: var TCompRes) = var idx, labl: int sym: PSym @@ -646,22 +554,18 @@ proc genBlock(p: var TProc, n: PNode, r: var TCompRes) = setlen(p.blocks, idx + 1) p.blocks[idx].id = - p.unique # negative because it isn't used yet labl = p.unique - if n.kind == nkBlockExpr: genStmtListExpr(p, n.sons[1], r) - else: genStmt(p, n.sons[1], r) - if p.blocks[idx].id > 0: - # label has been used: - r.com = ropef("L$1: do {$n$2} while(false);$n", [toRope(labl), r.com]) + appf(p.body, "L$1: do {$n", toRope(labl)) + gen(p, n.sons[1], r) + appf(p.body, "} while(false);$n") setlen(p.blocks, idx) -proc genBreakStmt(p: var TProc, n: PNode, r: var TCompRes) = - var - idx: int - sym: PSym - genLineDir(p, n, r) +proc genBreakStmt(p: PProc, n: PNode) = + var idx: int + genLineDir(p, n) if n.sons[0].kind != nkEmpty: # named break? assert(n.sons[0].kind == nkSym) - sym = n.sons[0].sym + let sym = n.sons[0].sym assert(sym.loc.k == locOther) idx = sym.loc.a else: @@ -671,73 +575,48 @@ proc genBreakStmt(p: var TProc, n: PNode, r: var TCompRes) = if idx < 0 or not p.blocks[idx].isLoop: InternalError(n.info, "no loop to break") p.blocks[idx].id = abs(p.blocks[idx].id) # label is used - appf(r.com, "break L$1;$n", [toRope(p.blocks[idx].id)]) + appf(p.body, "break L$1;$n", [toRope(p.blocks[idx].id)]) -proc genAsmStmt(p: var TProc, n: PNode, r: var TCompRes) = - genLineDir(p, n, r) +proc genAsmStmt(p: PProc, n: PNode) = + genLineDir(p, n) assert(n.kind == nkAsmStmt) for i in countup(0, sonsLen(n) - 1): case n.sons[i].Kind - of nkStrLit..nkTripleStrLit: app(r.com, n.sons[i].strVal) - of nkSym: app(r.com, mangleName(n.sons[i].sym)) + of nkStrLit..nkTripleStrLit: app(p.body, n.sons[i].strVal) + of nkSym: app(p.body, mangleName(n.sons[i].sym)) else: InternalError(n.sons[i].info, "jsgen: genAsmStmt()") -proc genIfStmt(p: var TProc, n: PNode, r: var TCompRes) = - var - toClose: int - cond, stmt: TCompRes - it: PNode - toClose = 0 +proc genIf(p: PProc, n: PNode, r: var TCompRes) = + var cond, stmt: TCompRes + var toClose = 0 + if not isEmptyType(n.typ): + r.kind = resVal + r.res = getTemp(p) for i in countup(0, sonsLen(n) - 1): - it = n.sons[i] + let it = n.sons[i] if sonsLen(it) != 1: - gen(p, it.sons[0], cond) - genStmt(p, it.sons[1], stmt) - if i > 0: - appf(r.com, "else {$n", []) + if i > 0: + appf(p.body, "else {$n", []) inc(toClose) - if cond.com != nil: appf(r.com, "$1;$n", [cond.com]) - appf(r.com, "if ($1) {$n$2}", [cond.res, mergeStmt(stmt)]) - else: - # else part: - genStmt(p, it.sons[0], stmt) - appf(r.com, "else {$n$1}$n", [mergeStmt(stmt)]) - app(r.com, repeatChar(toClose, '}') & tnl) - -proc genIfExpr(p: var TProc, n: PNode, r: var TCompRes) = - var - toClose: int - cond, stmt: TCompRes - it: PNode - toClose = 0 - for i in countup(0, sonsLen(n) - 1): - it = n.sons[i] - if sonsLen(it) != 1: gen(p, it.sons[0], cond) + appf(p.body, "if ($1) {$n", cond.rdLoc) gen(p, it.sons[1], stmt) - if i > 0: - app(r.res, ": (") - inc(toClose) - r.com = mergeExpr(r.com, cond.com) - r.com = mergeExpr(r.com, stmt.com) - appf(r.res, "($1) ? ($2)", [cond.res, stmt.res]) - else: + else: # else part: + appf(p.body, "else {$n") gen(p, it.sons[0], stmt) - r.com = mergeExpr(r.com, stmt.com) - appf(r.res, ": ($1)", [stmt.res]) - app(r.res, repeatChar(toClose, ')')) + moveInto(p, stmt, r) + appf(p.body, "}$n") + app(p.body, repeatChar(toClose, '}') & tnl) -proc generateHeader(p: var TProc, typ: PType): PRope = - var - param: PSym - name: PRope +proc generateHeader(p: PProc, typ: PType): PRope = result = nil - for i in countup(1, sonsLen(typ.n) - 1): + for i in countup(1, sonsLen(typ.n) - 1): if result != nil: app(result, ", ") assert(typ.n.sons[i].kind == nkSym) - param = typ.n.sons[i].sym - name = mangleName(param) + var param = typ.n.sons[i].sym + if isCompileTimeOnly(param.typ): continue + var name = mangleName(param) app(result, name) if mapType(param.typ) == etyBaseIndex: app(result, ", ") @@ -754,56 +633,49 @@ proc needsNoCopy(y: PNode): bool = result = (y.kind in nodeKindsNeedNoCopy) or (skipTypes(y.typ, abstractInst).kind in {tyRef, tyPtr, tyVar}) -proc genAsgnAux(p: var TProc, x, y: PNode, r: var TCompRes, - noCopyNeeded: bool) = +proc genAsgnAux(p: PProc, x, y: PNode, noCopyNeeded: bool) = var a, b: TCompRes gen(p, x, a) gen(p, y, b) case mapType(x.typ) - of etyObject: - if a.com != nil: appf(r.com, "$1;$n", [a.com]) - if b.com != nil: appf(r.com, "$1;$n", [b.com]) - if needsNoCopy(y) or noCopyNeeded: - appf(r.com, "$1 = $2;$n", [a.res, b.res]) - else: + of etyObject: + if needsNoCopy(y) or noCopyNeeded: + appf(p.body, "$1 = $2;$n", [a.rdLoc, b.rdLoc]) + else: useMagic(p, "NimCopy") - appf(r.com, "$1 = NimCopy($2, $3);$n", + appf(p.body, "$1 = NimCopy($2, $3);$n", [a.res, b.res, genTypeInfo(p, y.typ)]) of etyBaseIndex: - if (a.kind != etyBaseIndex) or (b.kind != etyBaseIndex): + if a.typ != etyBaseIndex or b.typ != etyBaseIndex: internalError(x.info, "genAsgn") - appf(r.com, "$1 = $2; $3 = $4;$n", [a.com, b.com, a.res, b.res]) - else: - if a.com != nil: appf(r.com, "$1;$n", [a.com]) - if b.com != nil: appf(r.com, "$1;$n", [b.com]) - appf(r.com, "$1 = $2;$n", [a.res, b.res]) + appf(p.body, "$1 = $2; $3 = $4;$n", [a.address, b.address, a.res, b.res]) + else: + appf(p.body, "$1 = $2;$n", [a.res, b.res]) -proc genAsgn(p: var TProc, n: PNode, r: var TCompRes) = - genLineDir(p, n, r) - genAsgnAux(p, n.sons[0], n.sons[1], r, false) +proc genAsgn(p: PProc, n: PNode) = + genLineDir(p, n) + genAsgnAux(p, n.sons[0], n.sons[1], noCopyNeeded=false) -proc genFastAsgn(p: var TProc, n: PNode, r: var TCompRes) = - genLineDir(p, n, r) - genAsgnAux(p, n.sons[0], n.sons[1], r, true) +proc genFastAsgn(p: PProc, n: PNode) = + genLineDir(p, n) + genAsgnAux(p, n.sons[0], n.sons[1], noCopyNeeded=true) -proc genSwap(p: var TProc, n: PNode, r: var TCompRes) = +proc genSwap(p: PProc, n: PNode) = var a, b: TCompRes gen(p, n.sons[1], a) gen(p, n.sons[2], b) inc(p.unique) - var tmp = ropef("Tmp$1", [toRope(p.unique)]) + let tmp = ropef("Tmp$1", [toRope(p.unique)]) case mapType(skipTypes(n.sons[1].typ, abstractVar)) of etyBaseIndex: inc(p.unique) - var tmp2 = ropef("Tmp$1", [toRope(p.unique)]) - if (a.kind != etyBaseIndex) or (b.kind != etyBaseIndex): + let tmp2 = ropef("Tmp$1", [toRope(p.unique)]) + if a.typ != etyBaseIndex or b.typ != etyBaseIndex: internalError(n.info, "genSwap") - appf(r.com, "var $1 = $2; $2 = $3; $3 = $1;$n", [tmp, a.com, b.com]) - appf(r.com, "var $1 = $2; $2 = $3; $3 = $1", [tmp2, a.res, b.res]) - else: - if a.com != nil: appf(r.com, "$1;$n", [a.com]) - if b.com != nil: appf(r.com, "$1;$n", [b.com]) - appf(r.com, "var $1 = $2; $2 = $3; $3 = $1", [tmp, a.res, b.res]) + appf(p.body, "var $1 = $2; $2 = $3; $3 = $1;$n", [tmp, a.address, b.address]) + appf(p.body, "var $1 = $2; $2 = $3; $3 = $1", [tmp2, a.res, b.res]) + else: + appf(p.body, "var $1 = $2; $2 = $3; $3 = $1", [tmp, a.res, b.res]) proc getFieldPosition(f: PNode): int = case f.kind @@ -811,10 +683,10 @@ proc getFieldPosition(f: PNode): int = of nkSym: result = f.sym.position else: InternalError(f.info, "genFieldPosition") -proc genFieldAddr(p: var TProc, n: PNode, r: var TCompRes) = +proc genFieldAddr(p: PProc, n: PNode, r: var TCompRes) = var a: TCompRes - r.kind = etyBaseIndex - var b = if n.kind == nkHiddenAddr: n.sons[0] else: n + r.typ = etyBaseIndex + let b = if n.kind == nkHiddenAddr: n.sons[0] else: n gen(p, b.sons[0], a) if skipTypes(b.sons[0].typ, abstractVarRange).kind == tyTuple: r.res = makeJSString("Field" & $getFieldPosition(b.sons[1])) @@ -823,10 +695,12 @@ proc genFieldAddr(p: var TProc, n: PNode, r: var TCompRes) = var f = b.sons[1].sym if f.loc.r == nil: f.loc.r = mangleName(f) r.res = makeJSString(ropeToStr(f.loc.r)) - r.com = mergeExpr(a) + InternalAssert a.typ != etyBaseIndex + r.address = a.res + r.kind = resExpr -proc genFieldAccess(p: var TProc, n: PNode, r: var TCompRes) = - r.kind = etyNone +proc genFieldAccess(p: PProc, n: PNode, r: var TCompRes) = + r.typ = etyNone gen(p, n.sons[0], r) if skipTypes(n.sons[0].typ, abstractVarRange).kind == tyTuple: r.res = ropef("$1.Field$2", [r.res, getFieldPosition(n.sons[1]).toRope]) @@ -835,34 +709,37 @@ proc genFieldAccess(p: var TProc, n: PNode, r: var TCompRes) = var f = n.sons[1].sym if f.loc.r == nil: f.loc.r = mangleName(f) r.res = ropef("$1.$2", [r.res, f.loc.r]) + r.kind = resExpr -proc genCheckedFieldAddr(p: var TProc, n: PNode, r: var TCompRes) = +proc genCheckedFieldAddr(p: PProc, n: PNode, r: var TCompRes) = genFieldAddr(p, n.sons[0], r) # XXX -proc genCheckedFieldAccess(p: var TProc, n: PNode, r: var TCompRes) = +proc genCheckedFieldAccess(p: PProc, n: PNode, r: var TCompRes) = genFieldAccess(p, n.sons[0], r) # XXX -proc genArrayAddr(p: var TProc, n: PNode, r: var TCompRes) = +proc genArrayAddr(p: PProc, n: PNode, r: var TCompRes) = var a, b: TCompRes first: biggestInt - r.kind = etyBaseIndex + r.typ = etyBaseIndex gen(p, n.sons[0], a) gen(p, n.sons[1], b) - r.com = mergeExpr(a) + InternalAssert a.typ != etyBaseIndex and b.typ != etyBaseIndex + r.address = a.res var typ = skipTypes(n.sons[0].typ, abstractPtrs) if typ.kind in {tyArray, tyArrayConstr}: first = FirstOrd(typ.sons[0]) else: first = 0 if optBoundsCheck in p.options and not isConstExpr(n.sons[1]): useMagic(p, "chckIndx") - b.res = ropef("chckIndx($1, $2, $3.length)-$2", - [b.res, toRope(first), a.res]) - # XXX: BUG: a.res evaluated twice! - elif first != 0: - b.res = ropef("($1)-$2", [b.res, toRope(first)]) - r.res = mergeExpr(b) - -proc genArrayAccess(p: var TProc, n: PNode, r: var TCompRes) = + r.res = ropef("chckIndx($1, $2, $3.length)-$2", + [b.res, toRope(first), a.res]) + elif first != 0: + r.res = ropef("($1)-$2", [b.res, toRope(first)]) + else: + r.res = b.res + r.kind = resExpr + +proc genArrayAccess(p: PProc, n: PNode, r: var TCompRes) = var ty = skipTypes(n.sons[0].typ, abstractVarRange) if ty.kind in {tyRef, tyPtr}: ty = skipTypes(ty.sons[0], abstractVarRange) case ty.kind @@ -872,38 +749,40 @@ proc genArrayAccess(p: var TProc, n: PNode, r: var TCompRes) = of tyTuple: genFieldAddr(p, n, r) else: InternalError(n.info, "expr(nkBracketExpr, " & $ty.kind & ')') - r.kind = etyNone - r.res = ropef("$1[$2]", [r.com, r.res]) - r.com = nil + r.typ = etyNone + if r.res == nil: InternalError(n.info, "genArrayAccess") + r.res = ropef("$1[$2]", [r.address, r.res]) + r.address = nil + r.kind = resExpr -proc genAddr(p: var TProc, n: PNode, r: var TCompRes) = - var s: PSym +proc genAddr(p: PProc, n: PNode, r: var TCompRes) = case n.sons[0].kind - of nkSym: - s = n.sons[0].sym + of nkSym: + let s = n.sons[0].sym if s.loc.r == nil: InternalError(n.info, "genAddr: 3") case s.kind - of skVar, skLet, skResult: - if mapType(n.typ) == etyObject: + of skVar, skLet, skResult: + r.kind = resExpr + if mapType(n.typ) == etyObject: # make addr() a no-op: - r.kind = etyNone + r.typ = etyNone r.res = s.loc.r - r.com = nil - elif sfGlobal in s.flags: + r.address = nil + elif sfGlobal in s.flags: # globals are always indirect accessible - r.kind = etyBaseIndex - r.com = toRope("Globals") + r.typ = etyBaseIndex + r.address = toRope("Globals") r.res = makeJSString(ropeToStr(s.loc.r)) - elif sfAddrTaken in s.flags: - r.kind = etyBaseIndex - r.com = s.loc.r + elif sfAddrTaken in s.flags: + r.typ = etyBaseIndex + r.address = s.loc.r r.res = toRope("0") - else: + else: InternalError(n.info, "genAddr: 4") else: InternalError(n.info, "genAddr: 2") - of nkCheckedFieldExpr: + of nkCheckedFieldExpr: genCheckedFieldAddr(p, n, r) - of nkDotExpr: + of nkDotExpr: genFieldAddr(p, n, r) of nkBracketExpr: var ty = skipTypes(n.sons[0].typ, abstractVarRange) @@ -917,27 +796,27 @@ proc genAddr(p: var TProc, n: PNode, r: var TCompRes) = else: InternalError(n.info, "expr(nkBracketExpr, " & $ty.kind & ')') else: InternalError(n.info, "genAddr") -proc genSym(p: var TProc, n: PNode, r: var TCompRes) = +proc genSym(p: PProc, n: PNode, r: var TCompRes) = var s = n.sym case s.kind - of skVar, skLet, skParam, skTemp, skResult: - if s.loc.r == nil: + of skVar, skLet, skParam, skTemp, skResult: + if s.loc.r == nil: InternalError(n.info, "symbol has no generated name: " & s.name.s) var k = mapType(s.typ) - if k == etyBaseIndex: - r.kind = etyBaseIndex - if {sfAddrTaken, sfGlobal} * s.flags != {}: - r.com = ropef("$1[0]", [s.loc.r]) + if k == etyBaseIndex: + r.typ = etyBaseIndex + if {sfAddrTaken, sfGlobal} * s.flags != {}: + r.address = ropef("$1[0]", [s.loc.r]) r.res = ropef("$1[1]", [s.loc.r]) - else: - r.com = s.loc.r + else: + r.address = s.loc.r r.res = con(s.loc.r, "_Idx") - elif (k != etyObject) and (sfAddrTaken in s.flags): + elif k != etyObject and sfAddrTaken in s.flags: r.res = ropef("$1[0]", [s.loc.r]) - else: + else: r.res = s.loc.r of skConst: - genConstant(p, s, r) + genConstant(p, s) if s.loc.r == nil: InternalError(n.info, "symbol has no generated name: " & s.name.s) r.res = s.loc.r @@ -953,64 +832,72 @@ proc genSym(p: var TProc, n: PNode, r: var TCompRes) = elif sfForward in s.flags: p.g.forwarded.add(s) elif not p.g.generatedSyms.containsOrIncl(s.id): - var r2: TCompRes - genProc(p, s, r2) - app(p.locals, mergeStmt(r2)) - #app(r.com, mergeStmt(r2)) + let newp = genProc(p, s) + var owner = p + while owner != nil and owner.prc != s.owner: + owner = owner.up + if owner != nil: app(owner.locals, newp) + else: app(p.g.code, newp) else: if s.loc.r == nil: InternalError(n.info, "symbol has no generated name: " & s.name.s) r.res = s.loc.r + r.kind = resVal -proc genDeref(p: var TProc, n: PNode, r: var TCompRes) = - var a: TCompRes +proc genDeref(p: PProc, n: PNode, r: var TCompRes) = if mapType(n.sons[0].typ) == etyObject: gen(p, n.sons[0], r) - else: + else: + var a: TCompRes gen(p, n.sons[0], a) - if a.kind != etyBaseIndex: InternalError(n.info, "genDeref") - r.res = ropef("$1[$2]", [a.com, a.res]) + if a.typ != etyBaseIndex: InternalError(n.info, "genDeref") + r.res = ropef("$1[$2]", [a.address, a.res]) -proc genArg(p: var TProc, n: PNode, r: var TCompRes) = +proc genArg(p: PProc, n: PNode, r: var TCompRes) = var a: TCompRes gen(p, n, a) - if a.kind == etyBaseIndex: - app(r.res, a.com) + if a.typ == etyBaseIndex: + app(r.res, a.address) app(r.res, ", ") app(r.res, a.res) else: - app(r.res, mergeExpr(a)) + app(r.res, a.res) -proc genArgs(p: var TProc, n: PNode, r: var TCompRes) = +proc genArgs(p: PProc, n: PNode, r: var TCompRes) = app(r.res, "(") for i in countup(1, sonsLen(n) - 1): + let it = n.sons[i] + if it.typ.isCompileTimeOnly: continue if i > 1: app(r.res, ", ") - genArg(p, n.sons[i], r) + genArg(p, it, r) app(r.res, ")") + r.kind = resExpr -proc genCall(p: var TProc, n: PNode, r: var TCompRes) = +proc genCall(p: PProc, n: PNode, r: var TCompRes) = gen(p, n.sons[0], r) genArgs(p, n, r) -proc genInfixCall(p: var TProc, n: PNode, r: var TCompRes) = +proc genInfixCall(p: PProc, n: PNode, r: var TCompRes) = gen(p, n.sons[1], r) - if r.kind == etyBaseIndex: - if r.com == nil: + if r.typ == etyBaseIndex: + if r.address == nil: GlobalError(n.info, "cannot invoke with infix syntax") - r.res = ropef("$1[0]", [r.res, r.com]) - r.com = nil + r.res = ropef("$1[$2]", [r.address, r.res]) + r.address = nil + r.typ = etyNone app(r.res, ".") var op: TCompRes gen(p, n.sons[0], op) - app(r.res, mergeExpr(op)) + app(r.res, op.res) app(r.res, "(") for i in countup(2, sonsLen(n) - 1): if i > 2: app(r.res, ", ") genArg(p, n.sons[i], r) app(r.res, ")") + r.kind = resExpr -proc genEcho(p: var TProc, n: PNode, r: var TCompRes) = +proc genEcho(p: PProc, n: PNode, r: var TCompRes) = useMagic(p, "rawEcho") app(r.res, "rawEcho") genArgs(p, n, r) @@ -1019,8 +906,8 @@ proc putToSeq(s: string, indirect: bool): PRope = result = toRope(s) if indirect: result = ropef("[$1]", [result]) -proc createVar(p: var TProc, typ: PType, indirect: bool): PRope -proc createRecordVarAux(p: var TProc, rec: PNode, c: var int): PRope = +proc createVar(p: PProc, typ: PType, indirect: bool): PRope +proc createRecordVarAux(p: PProc, rec: PNode, c: var int): PRope = result = nil case rec.kind of nkRecList: @@ -1038,7 +925,7 @@ proc createRecordVarAux(p: var TProc, rec: PNode, c: var int): PRope = inc(c) else: InternalError(rec.info, "createRecordVarAux") -proc createVar(p: var TProc, typ: PType, indirect: bool): PRope = +proc createVar(p: PProc, typ: PType, indirect: bool): PRope = var t = skipTypes(typ, abstractInst) case t.kind of tyInt..tyInt64, tyEnum, tyChar: @@ -1075,7 +962,7 @@ proc createVar(p: var TProc, typ: PType, indirect: bool): PRope = of tyObject: result = toRope("{") var c = 0 - if not (tfFinal in t.flags) or (t.sons[0] != nil): + if tfFinal notin t.flags or t.sons[0] != nil: inc(c) appf(result, "m_type: $1", [genTypeInfo(p, t)]) while t != nil: @@ -1095,38 +982,37 @@ proc isIndirect(v: PSym): bool = result = (sfAddrTaken in v.flags) and (mapType(v.typ) != etyObject) and v.kind notin {skProc, skConverter, skMethod, skIterator} -proc genVarInit(p: var TProc, v: PSym, n: PNode, r: var TCompRes) = +proc genVarInit(p: PProc, v: PSym, n: PNode) = var a: TCompRes s: PRope if n.kind == nkEmpty: - appf(r.com, "var $1 = $2;$n", + appf(p.body, "var $1 = $2;$n", [mangleName(v), createVar(p, v.typ, isIndirect(v))]) else: discard mangleName(v) gen(p, n, a) case mapType(v.typ) of etyObject: - if a.com != nil: appf(r.com, "$1;$n", [a.com]) if needsNoCopy(n): s = a.res else: useMagic(p, "NimCopy") s = ropef("NimCopy($1, $2)", [a.res, genTypeInfo(p, n.typ)]) of etyBaseIndex: - if (a.kind != etyBaseIndex): InternalError(n.info, "genVarInit") + if (a.typ != etyBaseIndex): InternalError(n.info, "genVarInit") if {sfAddrTaken, sfGlobal} * v.flags != {}: - appf(r.com, "var $1 = [$2, $3];$n", [v.loc.r, a.com, a.res]) + appf(p.body, "var $1 = [$2, $3];$n", [v.loc.r, a.address, a.res]) else: - appf(r.com, "var $1 = $2; var $1_Idx = $3;$n", [v.loc.r, a.com, a.res]) - return - else: - if a.com != nil: appf(r.com, "$1;$n", [a.com]) + appf(p.body, "var $1 = $2; var $1_Idx = $3;$n", [ + v.loc.r, a.address, a.res]) + return + else: s = a.res - if isIndirect(v): appf(r.com, "var $1 = [$2];$n", [v.loc.r, s]) - else: appf(r.com, "var $1 = $2;$n", [v.loc.r, s]) + if isIndirect(v): appf(p.body, "var $1 = [$2];$n", [v.loc.r, s]) + else: appf(p.body, "var $1 = $2;$n", [v.loc.r, s]) -proc genVarStmt(p: var TProc, n: PNode, r: var TCompRes) = +proc genVarStmt(p: PProc, n: PNode) = for i in countup(0, sonsLen(n) - 1): var a = n.sons[i] if a.kind == nkCommentStmt: continue @@ -1134,56 +1020,43 @@ proc genVarStmt(p: var TProc, n: PNode, r: var TCompRes) = assert(a.sons[0].kind == nkSym) var v = a.sons[0].sym if lfNoDecl in v.loc.flags: continue - genLineDir(p, a, r) - genVarInit(p, v, a.sons[2], r) + genLineDir(p, a) + genVarInit(p, v, a.sons[2]) -proc genConstant(p: var TProc, c: PSym, r: var TCompRes) = +proc genConstant(p: PProc, c: PSym) = if lfNoDecl notin c.loc.flags and not p.g.generatedSyms.containsOrIncl(c.id): - genLineDir(p, c.ast, r) - genVarInit(p, c, c.ast, r) - app(p.g.code, r.com) - r.com = nil - -when false: - proc genConstStmt(p: var TProc, n: PNode, r: var TCompRes) = - genLineDir(p, n, r) - for i in countup(0, sonsLen(n) - 1): - if n.sons[i].kind == nkCommentStmt: continue - assert(n.sons[i].kind == nkConstDef) - var c = n.sons[i].sons[0].sym - if c.ast != nil and c.typ.kind in ConstantDataTypes and - lfNoDecl notin c.loc.flags: - genLineDir(p, n.sons[i], r) - genVarInit(p, c, c.ast, r) - -proc genNew(p: var TProc, n: PNode, r: var TCompRes) = + let oldBody = p.body + p.body = nil + genLineDir(p, c.ast) + genVarInit(p, c, c.ast) + app(p.g.code, p.body) + p.body = oldBody + +proc genNew(p: PProc, n: PNode) = var a: TCompRes gen(p, n.sons[1], a) var t = skipTypes(n.sons[1].typ, abstractVar).sons[0] - if a.com != nil: appf(r.com, "$1;$n", [a.com]) - appf(r.com, "$1 = $2;$n", [a.res, createVar(p, t, true)]) + appf(p.body, "$1 = $2;$n", [a.res, createVar(p, t, true)]) -proc genNewSeq(p: var TProc, n: PNode, r: var TCompRes) = +proc genNewSeq(p: PProc, n: PNode) = var x, y: TCompRes gen(p, n.sons[1], x) gen(p, n.sons[2], y) - if x.com != nil: appf(r.com, "$1;$n", [x.com]) - if y.com != nil: appf(r.com, "$1;$n", [y.com]) - var t = skipTypes(n.sons[1].typ, abstractVar).sons[0] - appf(r.com, "$1 = new Array($2); for (var i=0;i<$2;++i) {$1[i]=$3;}", [ - x.res, y.res, createVar(p, t, false)]) + let t = skipTypes(n.sons[1].typ, abstractVar).sons[0] + appf(p.body, "$1 = new Array($2); for (var i=0;i<$2;++i) {$1[i]=$3;}", [ + x.rdLoc, y.rdLoc, createVar(p, t, false)]) -proc genOrd(p: var TProc, n: PNode, r: var TCompRes) = +proc genOrd(p: PProc, n: PNode, r: var TCompRes) = case skipTypes(n.sons[1].typ, abstractVar).kind of tyEnum, tyInt..tyInt64, tyChar: gen(p, n.sons[1], r) of tyBool: unaryExpr(p, n, r, "", "($1 ? 1:0)") else: InternalError(n.info, "genOrd") -proc genConStrStr(p: var TProc, n: PNode, r: var TCompRes) = +proc genConStrStr(p: PProc, n: PNode, r: var TCompRes) = var a: TCompRes gen(p, n.sons[1], a) - r.com = mergeExpr(r.com, a.com) + r.kind = resExpr if skipTypes(n.sons[1].typ, abstractVarRange).kind == tyChar: r.res.app(ropef("[$1].concat(", [a.res])) else: @@ -1191,21 +1064,18 @@ proc genConStrStr(p: var TProc, n: PNode, r: var TCompRes) = for i in countup(2, sonsLen(n) - 2): gen(p, n.sons[i], a) - r.com = mergeExpr(r.com, a.com) - if skipTypes(n.sons[i].typ, abstractVarRange).kind == tyChar: r.res.app(ropef("[$1],", [a.res])) else: r.res.app(ropef("$1.slice(0,-1),", [a.res])) gen(p, n.sons[sonsLen(n) - 1], a) - r.com = mergeExpr(r.com, a.com) if skipTypes(n.sons[sonsLen(n) - 1].typ, abstractVarRange).kind == tyChar: r.res.app(ropef("[$1, 0])", [a.res])) else: r.res.app(ropef("$1)", [a.res])) -proc genRepr(p: var TProc, n: PNode, r: var TCompRes) = +proc genRepr(p: PProc, n: PNode, r: var TCompRes) = var t = skipTypes(n.sons[1].typ, abstractVarRange) case t.kind of tyInt..tyUInt64: @@ -1213,13 +1083,14 @@ proc genRepr(p: var TProc, n: PNode, r: var TCompRes) = of tyEnum, tyOrdinal: gen(p, n.sons[1], r) useMagic(p, "cstrToNimstr") + r.kind = resExpr r.res = ropef("cstrToNimstr($1.node.sons[$2].name)", [genTypeInfo(p, t), r.res]) else: # XXX: internalError(n.info, "genRepr: Not implemented") -proc genOf(p: var TProc, n: PNode, r: var TCompRes) = +proc genOf(p: PProc, n: PNode, r: var TCompRes) = var x: TCompRes let t = skipTypes(n.sons[2].typ, abstractVarRange+{tyRef, tyPtr, tyTypeDesc}) gen(p, n.sons[1], x) @@ -1228,17 +1099,16 @@ proc genOf(p: var TProc, n: PNode, r: var TCompRes) = else: useMagic(p, "isObj") r.res = ropef("isObj($1.m_type, $2)", [x.res, genTypeInfo(p, t)]) - r.com = mergeExpr(r.com, x.com) + r.kind = resExpr -proc genReset(p: var TProc, n: PNode, r: var TCompRes) = +proc genReset(p: PProc, n: PNode) = var x: TCompRes useMagic(p, "genericReset") gen(p, n.sons[1], x) - r.res = ropef("$1 = genericReset($1, $2)", [x.res, + appf(p.body, "$1 = genericReset($1, $2);$n", [x.res, genTypeInfo(p, n.sons[1].typ)]) - r.com = mergeExpr(r.com, x.com) -proc genMagic(p: var TProc, n: PNode, r: var TCompRes) = +proc genMagic(p: PProc, n: PNode, r: var TCompRes) = var a: TCompRes line, filen: PRope @@ -1248,7 +1118,7 @@ proc genMagic(p: var TProc, n: PNode, r: var TCompRes) = of mAnd: genAnd(p, n.sons[1], n.sons[2], r) of mAddi..mStrToStr: arith(p, n, r, op) of mRepr: genRepr(p, n, r) - of mSwap: genSwap(p, n, r) + of mSwap: genSwap(p, n) of mUnaryLt: # XXX: range checking? if not (optOverflowCheck in p.Options): unaryExpr(p, n, r, "", "$1 - 1") @@ -1261,21 +1131,21 @@ proc genMagic(p: var TProc, n: PNode, r: var TCompRes) = # XXX: range checking? if not (optOverflowCheck in p.Options): binaryExpr(p, n, r, "", "$1 - $2") else: binaryExpr(p, n, r, "addInt", "addInt($1, $2)") - of mAppendStrCh: binaryStmt(p, n, r, "addChar", "$1 = addChar($1, $2)") + of mAppendStrCh: binaryExpr(p, n, r, "addChar", "addChar($1, $2)") of mAppendStrStr: if skipTypes(n.sons[1].typ, abstractVarRange).kind == tyCString: - binaryStmt(p, n, r, "", "$1 += $2") + binaryExpr(p, n, r, "", "$1 += $2") else: - binaryStmt(p, n, r, "", "$1 = ($1.slice(0,-1)).concat($2)") + binaryExpr(p, n, r, "", "$1 = ($1.slice(0,-1)).concat($2)") # XXX: make a copy of $2, because of Javascript's sucking semantics - of mAppendSeqElem: binaryStmt(p, n, r, "", "$1.push($2)") + of mAppendSeqElem: binaryExpr(p, n, r, "", "$1.push($2)") of mConStrStr: genConStrStr(p, n, r) of mEqStr: binaryExpr(p, n, r, "eqStrings", "eqStrings($1, $2)") of mLeStr: binaryExpr(p, n, r, "cmpStrings", "(cmpStrings($1, $2) <= 0)") of mLtStr: binaryExpr(p, n, r, "cmpStrings", "(cmpStrings($1, $2) < 0)") of mIsNil: unaryExpr(p, n, r, "", "$1 == null") of mEnumToStr: genRepr(p, n, r) - of mNew, mNewFinalize: genNew(p, n, r) + of mNew, mNewFinalize: genNew(p, n) of mSizeOf: r.res = toRope(getSize(n.sons[1].typ)) of mChr, mArrToSeq: gen(p, n.sons[1], r) # nothing to do of mOrd: genOrd(p, n, r) @@ -1288,13 +1158,13 @@ proc genMagic(p: var TProc, n: PNode, r: var TCompRes) = else: unaryExpr(p, n, r, "", "($1.length-1)") of mInc: - if not (optOverflowCheck in p.Options): binaryStmt(p, n, r, "", "$1 += $2") - else: binaryStmt(p, n, r, "addInt", "$1 = addInt($1, $2)") + if not (optOverflowCheck in p.Options): binaryExpr(p, n, r, "", "$1 += $2") + else: binaryExpr(p, n, r, "addInt", "$1 = addInt($1, $2)") of ast.mDec: - if not (optOverflowCheck in p.Options): binaryStmt(p, n, r, "", "$1 -= $2") - else: binaryStmt(p, n, r, "subInt", "$1 = subInt($1, $2)") - of mSetLengthStr: binaryStmt(p, n, r, "", "$1.length = ($2)-1") - of mSetLengthSeq: binaryStmt(p, n, r, "", "$1.length = $2") + if not (optOverflowCheck in p.Options): binaryExpr(p, n, r, "", "$1 -= $2") + else: binaryExpr(p, n, r, "subInt", "$1 = subInt($1, $2)") + of mSetLengthStr: binaryExpr(p, n, r, "", "$1.length = ($2)-1") + of mSetLengthSeq: binaryExpr(p, n, r, "", "$1.length = $2") of mCard: unaryExpr(p, n, r, "SetCard", "SetCard($1)") of mLtSet: binaryExpr(p, n, r, "SetLt", "SetLt($1, $2)") of mLeSet: binaryExpr(p, n, r, "SetLe", "SetLe($1, $2)") @@ -1302,14 +1172,14 @@ proc genMagic(p: var TProc, n: PNode, r: var TCompRes) = of mMulSet: binaryExpr(p, n, r, "SetMul", "SetMul($1, $2)") of mPlusSet: binaryExpr(p, n, r, "SetPlus", "SetPlus($1, $2)") of mMinusSet: binaryExpr(p, n, r, "SetMinus", "SetMinus($1, $2)") - of mIncl: binaryStmt(p, n, r, "", "$1[$2] = true") - of mExcl: binaryStmt(p, n, r, "", "delete $1[$2]") + of mIncl: binaryExpr(p, n, r, "", "$1[$2] = true") + of mExcl: binaryExpr(p, n, r, "", "delete $1[$2]") of mInSet: binaryExpr(p, n, r, "", "($1[$2] != undefined)") of mNLen..mNError: localError(n.info, errCannotGenerateCodeForX, n.sons[0].sym.name.s) - of mNewSeq: genNewSeq(p, n, r) + of mNewSeq: genNewSeq(p, n) of mOf: genOf(p, n, r) - of mReset: genReset(p, n, r) + of mReset: genReset(p, n) of mEcho: genEcho(p, n, r) of mSlurp, mStaticExec: localError(n.info, errXMustBeCompileTime, n.sons[0].sym.name.s) @@ -1317,94 +1187,95 @@ proc genMagic(p: var TProc, n: PNode, r: var TCompRes) = genCall(p, n, r) #else internalError(e.info, 'genMagic: ' + magicToStr[op]); -proc genSetConstr(p: var TProc, n: PNode, r: var TCompRes) = - var +proc genSetConstr(p: PProc, n: PNode, r: var TCompRes) = + var a, b: TCompRes useMagic(p, "SetConstr") r.res = toRope("SetConstr(") + r.kind = resExpr for i in countup(0, sonsLen(n) - 1): if i > 0: app(r.res, ", ") var it = n.sons[i] if it.kind == nkRange: gen(p, it.sons[0], a) gen(p, it.sons[1], b) - r.com = mergeExpr(r.com, mergeExpr(a.com, b.com)) appf(r.res, "[$1, $2]", [a.res, b.res]) else: gen(p, it, a) - r.com = mergeExpr(r.com, a.com) app(r.res, a.res) app(r.res, ")") -proc genArrayConstr(p: var TProc, n: PNode, r: var TCompRes) = +proc genArrayConstr(p: PProc, n: PNode, r: var TCompRes) = var a: TCompRes r.res = toRope("[") + r.kind = resExpr for i in countup(0, sonsLen(n) - 1): if i > 0: app(r.res, ", ") gen(p, n.sons[i], a) - r.com = mergeExpr(r.com, a.com) app(r.res, a.res) app(r.res, "]") -proc genTupleConstr(p: var TProc, n: PNode, r: var TCompRes) = +proc genTupleConstr(p: PProc, n: PNode, r: var TCompRes) = var a: TCompRes r.res = toRope("{") + r.kind = resExpr for i in countup(0, sonsLen(n) - 1): if i > 0: app(r.res, ", ") var it = n.sons[i] if it.kind == nkExprColonExpr: it = it.sons[1] gen(p, it, a) - r.com = mergeExpr(r.com, a.com) appf(r.res, "Field$1: $2", [i.toRope, a.res]) r.res.app("}") -proc genObjConstr(p: var TProc, n: PNode, r: var TCompRes) = +proc genObjConstr(p: PProc, n: PNode, r: var TCompRes) = # XXX inheritance? var a: TCompRes r.res = toRope("{") + r.kind = resExpr for i in countup(0, sonsLen(n) - 1): if i > 0: app(r.res, ", ") var it = n.sons[i] InternalAssert it.kind == nkExprColonExpr gen(p, it.sons[1], a) - r.com = mergeExpr(r.com, a.com) var f = it.sons[0].sym if f.loc.r == nil: f.loc.r = mangleName(f) appf(r.res, "$1: $2", [f.loc.r, a.res]) r.res.app("}") -proc genConv(p: var TProc, n: PNode, r: var TCompRes) = +proc genConv(p: PProc, n: PNode, r: var TCompRes) = var dest = skipTypes(n.typ, abstractVarRange) var src = skipTypes(n.sons[1].typ, abstractVarRange) gen(p, n.sons[1], r) if (dest.kind != src.kind) and (src.kind == tyBool): r.res = ropef("(($1)? 1:0)", [r.res]) + r.kind = resExpr -proc upConv(p: var TProc, n: PNode, r: var TCompRes) = +proc upConv(p: PProc, n: PNode, r: var TCompRes) = gen(p, n.sons[0], r) # XXX -proc genRangeChck(p: var TProc, n: PNode, r: var TCompRes, magic: string) = +proc genRangeChck(p: PProc, n: PNode, r: var TCompRes, magic: string) = var a, b: TCompRes gen(p, n.sons[0], r) if optRangeCheck in p.options: gen(p, n.sons[1], a) gen(p, n.sons[2], b) - r.com = mergeExpr(r.com, mergeExpr(a.com, b.com)) useMagic(p, "chckRange") r.res = ropef("chckRange($1, $2, $3)", [r.res, a.res, b.res]) + r.kind = resExpr -proc convStrToCStr(p: var TProc, n: PNode, r: var TCompRes) = +proc convStrToCStr(p: PProc, n: PNode, r: var TCompRes) = # we do an optimization here as this is likely to slow down # much of the code otherwise: if n.sons[0].kind == nkCStringToString: gen(p, n.sons[0].sons[0], r) - else: + else: gen(p, n.sons[0], r) if r.res == nil: InternalError(n.info, "convStrToCStr") useMagic(p, "toJSStr") r.res = ropef("toJSStr($1)", [r.res]) + r.kind = resExpr -proc convCStrToStr(p: var TProc, n: PNode, r: var TCompRes) = +proc convCStrToStr(p: PProc, n: PNode, r: var TCompRes) = # we do an optimization here as this is likely to slow down # much of the code otherwise: if n.sons[0].kind == nkStringToCString: @@ -1414,44 +1285,44 @@ proc convCStrToStr(p: var TProc, n: PNode, r: var TCompRes) = if r.res == nil: InternalError(n.info, "convCStrToStr") useMagic(p, "cstrToNimstr") r.res = ropef("cstrToNimstr($1)", [r.res]) + r.kind = resExpr -proc genReturnStmt(p: var TProc, n: PNode, r: var TCompRes) = - var a: TCompRes +proc genReturnStmt(p: PProc, n: PNode) = if p.procDef == nil: InternalError(n.info, "genReturnStmt") p.BeforeRetNeeded = true if (n.sons[0].kind != nkEmpty): - genStmt(p, n.sons[0], a) - if a.com != nil: appf(r.com, "$1;$n", mergeStmt(a)) - else: - genLineDir(p, n, r) - app(r.com, "break BeforeRet;" & tnl) + genStmt(p, n.sons[0]) + else: + genLineDir(p, n) + app(p.body, "break BeforeRet;" & tnl) -proc genProcBody(p: var TProc, prc: PSym, r: TCompRes): PRope = - if optStackTrace in prc.options: +proc genProcBody(p: PProc, prc: PSym): PRope = + if optStackTrace in prc.options: result = ropef("var F={procname:$1,prev:framePtr,filename:$2,line:0};$n" & "framePtr = F;$n", [makeJSString(prc.owner.name.s & '.' & prc.name.s), makeJSString(toFilename(prc.info))]) - else: + else: result = nil - if p.beforeRetNeeded: - appf(result, "BeforeRet: do {$n$1} while (false); $n", [mergeStmt(r)]) + if p.beforeRetNeeded: + appf(result, "BeforeRet: do {$n$1} while (false); $n", [p.body]) else: - app(result, mergeStmt(r)) + app(result, p.body) if prc.typ.callConv == ccSysCall: result = ropef("try {$n$1} catch (e) {$n" & " alert(\"Unhandled exception:\\n\" + e.message + \"\\n\"$n}", [result]) if optStackTrace in prc.options: app(result, "framePtr = framePtr.prev;" & tnl) -proc genProc(oldProc: var TProc, prc: PSym, r: var TCompRes) = - var - p: TProc +proc genProc(oldProc: PProc, prc: PSym): PRope = + var resultSym: PSym name, returnStmt, resultAsgn, header: PRope a: TCompRes #if gVerbosity >= 3: # echo "BEGIN generating code for: " & prc.name.s - initProc(p, oldProc.g, oldProc.module, prc.ast, prc.options) + var p = newProc(oldProc.g, oldProc.module, prc.ast, prc.options) + p.target = oldProc.target + p.up = oldProc returnStmt = nil resultAsgn = nil name = mangleName(prc) @@ -1461,112 +1332,58 @@ proc genProc(oldProc: var TProc, prc: PSym, r: var TCompRes) = resultAsgn = ropef("var $# = $#;$n", [mangleName(resultSym), createVar(p, resultSym.typ, isIndirect(resultSym))]) gen(p, prc.ast.sons[resultPos], a) - if a.com != nil: appf(returnStmt, "$1;$n", [a.com]) returnStmt = ropef("return $#;$n", [a.res]) - genStmt(p, prc.getBody, r) - r.com = ropef("function $#($#) {$n$#$#$#$#}$n", + genStmt(p, prc.getBody) + result = ropef("function $#($#) {$n$#$#$#$#}$n", [name, header, p.locals, resultAsgn, - genProcBody(p, prc, r), returnStmt]) - r.res = nil + genProcBody(p, prc), returnStmt]) #if gVerbosity >= 3: # echo "END generated code for: " & prc.name.s - -proc genStmtListExpr(p: var TProc, n: PNode, r: var TCompRes) = - var a: TCompRes - # watch out this trick: ``function () { stmtList; return expr; }()`` - r.res = toRope("function () {") - for i in countup(0, sonsLen(n) - 2): - genStmt(p, n.sons[i], a) - app(r.res, mergeStmt(a)) - gen(p, lastSon(n), a) - if a.com != nil: appf(r.res, "$1;$n", [a.com]) - appf(r.res, "return $1; }()", [a.res]) - -proc genStmt(p: var TProc, n: PNode, r: var TCompRes) = - var a: TCompRes - r.kind = etyNone - r.com = nil - r.res = nil - case n.kind - of nkNilLit, nkEmpty: nil - of nkStmtList: - for i in countup(0, sonsLen(n) - 1): - genStmt(p, n.sons[i], a) - app(r.com, mergeStmt(a)) - of nkBlockStmt: genBlock(p, n, r) - of nkIfStmt: genIfStmt(p, n, r) - of nkWhileStmt: genWhileStmt(p, n, r) - of nkVarSection, nkLetSection: genVarStmt(p, n, r) - of nkConstSection: nil - of nkForStmt, nkParForStmt: - internalError(n.info, "for statement not eliminated") - of nkCaseStmt: genCaseStmt(p, n, r) - of nkReturnStmt: genReturnStmt(p, n, r) - of nkBreakStmt: genBreakStmt(p, n, r) - of nkAsgn: genAsgn(p, n, r) - of nkFastAsgn: genFastAsgn(p, n, r) - of nkDiscardStmt: - if n.sons[0].kind != nkEmpty: - genLineDir(p, n, r) - gen(p, n.sons[0], r) - app(r.res, ';' & tnl) - of nkAsmStmt: genAsmStmt(p, n, r) - of nkTryStmt: genTryStmt(p, n, r) - of nkRaiseStmt: genRaiseStmt(p, n, r) - of nkTypeSection, nkCommentStmt, nkIteratorDef, nkIncludeStmt, - nkImportStmt, nkImportExceptStmt, nkExportStmt, nkExportExceptStmt, - nkFromStmt, nkTemplateDef, nkMacroDef, nkPragma: nil - of nkProcDef, nkMethodDef, nkConverterDef: - var s = n.sons[namePos].sym - if {sfExportc, sfCompilerProc} * s.flags == {sfExportc}: - #var r2: TCompRes - genSym(p, n.sons[namePos], r) - r.res = nil - of nkGotoState, nkState: - internalError(n.info, "first class iterators not implemented") - else: - genLineDir(p, n, r) - gen(p, n, r) - app(r.res, ';' & tnl) - -proc gen(p: var TProc, n: PNode, r: var TCompRes) = - var f: BiggestFloat - r.kind = etyNone - r.com = nil + +proc genStmt(p: PProc, n: PNode) = + var r: TCompRes + gen(p, n, r) + if r.res != nil: appf(p.body, "$#;$n", r.res) + +proc gen(p: PProc, n: PNode, r: var TCompRes) = + r.typ = etyNone + r.kind = resNone + #r.address = nil r.res = nil case n.kind - of nkSym: + of nkSym: genSym(p, n, r) - of nkCharLit..nkInt64Lit: + of nkCharLit..nkInt64Lit: r.res = toRope(n.intVal) - of nkNilLit: - if mapType(n.typ) == etyBaseIndex: - r.kind = etyBaseIndex - r.com = toRope"null" + of nkNilLit: + if isEmptyType(n.typ): + nil + elif mapType(n.typ) == etyBaseIndex: + r.typ = etyBaseIndex + r.address = toRope"null" r.res = toRope"0" - else: + else: r.res = toRope"null" - of nkStrLit..nkTripleStrLit: + of nkStrLit..nkTripleStrLit: if skipTypes(n.typ, abstractVarRange).kind == tyString: useMagic(p, "cstrToNimstr") r.res = ropef("cstrToNimstr($1)", [makeJSString(n.strVal)]) else: r.res = makeJSString(n.strVal) + r.kind = resExpr of nkFloatLit..nkFloat64Lit: - f = n.floatVal + let f = n.floatVal if f != f: r.res = toRope"NaN" elif f == 0.0: r.res = toRope"0.0" elif f == 0.5 * f: if f > 0.0: r.res = toRope"Infinity" else: r.res = toRope"-Infinity" else: r.res = toRope(f.ToStrMaxPrecision) - of nkBlockExpr: genBlock(p, n, r) - of nkIfExpr: genIfExpr(p, n, r) of nkCallKinds: if (n.sons[0].kind == nkSym) and (n.sons[0].sym.magic != mNone): genMagic(p, n, r) elif n.sons[0].kind == nkSym and sfInfixCall in n.sons[0].sym.flags and - n.len >= 2: + n.len >= 2: genInfixCall(p, n, r) else: genCall(p, n, r) @@ -1588,7 +1405,6 @@ proc gen(p: var TProc, n: PNode, r: var TCompRes) = of nkChckRange: genRangeChck(p, n, r, "chckRange") of nkStringToCString: convStrToCStr(p, n, r) of nkCStringToString: convCStrToStr(p, n, r) - of nkStmtListExpr: genStmtListExpr(p, n, r) of nkEmpty: nil of nkLambdaKinds: let s = n.sons[namePos].sym @@ -1596,11 +1412,46 @@ proc gen(p: var TProc, n: PNode, r: var TCompRes) = r.res = s.loc.r if lfNoDecl in s.loc.flags or s.magic != mNone or isGenericRoutine(s): nil elif not p.g.generatedSyms.containsOrIncl(s.id): - var r2: TCompRes - genProc(p, s, r2) - app(r.com, mergeStmt(r2)) + app(p.locals, genProc(p, s)) of nkMetaNode: gen(p, n.sons[0], r) of nkType: r.res = genTypeInfo(p, n.typ) + of nkStmtList, nkStmtListExpr: + # this shows the distinction is nice for backends and should be kept + # in the frontend + let isExpr = not isEmptyType(n.typ) + for i in countup(0, sonsLen(n) - 1 - isExpr.ord): + genStmt(p, n.sons[i]) + if isExpr: + gen(p, lastSon(n), r) + of nkBlockStmt, nkBlockExpr: genBlock(p, n, r) + of nkIfStmt, nkIfExpr: genIf(p, n, r) + of nkWhileStmt: genWhileStmt(p, n) + of nkVarSection, nkLetSection: genVarStmt(p, n) + of nkConstSection: nil + of nkForStmt, nkParForStmt: + internalError(n.info, "for statement not eliminated") + of nkCaseStmt: genCase(p, n, r) + of nkReturnStmt: genReturnStmt(p, n) + of nkBreakStmt: genBreakStmt(p, n) + of nkAsgn: genAsgn(p, n) + of nkFastAsgn: genFastAsgn(p, n) + of nkDiscardStmt: + if n.sons[0].kind != nkEmpty: + genLineDir(p, n) + gen(p, n.sons[0], r) + of nkAsmStmt: genAsmStmt(p, n) + of nkTryStmt: genTry(p, n, r) + of nkRaiseStmt: genRaiseStmt(p, n) + of nkTypeSection, nkCommentStmt, nkIteratorDef, nkIncludeStmt, + nkImportStmt, nkImportExceptStmt, nkExportStmt, nkExportExceptStmt, + nkFromStmt, nkTemplateDef, nkMacroDef, nkPragma: nil + of nkProcDef, nkMethodDef, nkConverterDef: + var s = n.sons[namePos].sym + if {sfExportc, sfCompilerProc} * s.flags == {sfExportc}: + genSym(p, n.sons[namePos], r) + r.res = nil + of nkGotoState, nkState: + internalError(n.info, "first class iterators not implemented") else: InternalError(n.info, "gen: unknown node type: " & $n.kind) var globals: PGlobals @@ -1616,26 +1467,25 @@ proc genHeader(): PRope = "var framePtr = null;$n" & "var excHandler = null;$n", [toRope(versionAsString)]) -proc genModule(p: var TProc, n: PNode, r: var TCompRes) = - genStmt(p, n, r) - if optStackTrace in p.options: - r.com = ropef("var F = {procname:$1,prev:framePtr,filename:$2,line:0};$n" & - "framePtr = F;$n" & "$3" & "framePtr = framePtr.prev;$n", [ +proc genModule(p: PProc, n: PNode) = + if optStackTrace in p.options: + appf(p.body, "var F = {procname:$1,prev:framePtr,filename:$2,line:0};$n" & + "framePtr = F;$n", [ makeJSString("module " & p.module.module.name.s), - makeJSString(toFilename(p.module.module.info)), r.com]) + makeJSString(toFilename(p.module.module.info))]) + genStmt(p, n) + if optStackTrace in p.options: + appf(p.body, "framePtr = framePtr.prev;$n") proc myProcess(b: PPassContext, n: PNode): PNode = if passes.skipCodegen(n): return n - var - p: TProc - r: TCompRes result = n var m = BModule(b) if m.module == nil: InternalError(n.info, "myProcess") - initProc(p, globals, m, nil, m.module.options) - genModule(p, n, r) + var p = newProc(globals, m, nil, m.module.options) + genModule(p, n) app(p.g.code, p.locals) - app(p.g.code, mergeStmt(r)) + app(p.g.code, p.body) proc myClose(b: PPassContext, n: PNode): PNode = if passes.skipCodegen(n): return n @@ -1644,23 +1494,15 @@ proc myClose(b: PPassContext, n: PNode): PNode = if sfMainModule in m.module.flags: for prc in globals.forwarded: if not globals.generatedSyms.containsOrIncl(prc.id): - var - p: TProc - r: TCompRes - initProc(p, globals, m, nil, m.module.options) - genProc(p, prc, r) - app(p.g.code, mergeStmt(r)) + var p = newProc(globals, m, nil, m.module.options) + app(p.g.code, genProc(p, prc)) var disp = generateMethodDispatchers() for i in 0..sonsLen(disp)-1: let prc = disp.sons[i].sym if not globals.generatedSyms.containsOrIncl(prc.id): - var - p: TProc - r: TCompRes - initProc(p, globals, m, nil, m.module.options) - genProc(p, prc, r) - app(p.g.code, mergeStmt(r)) + var p = newProc(globals, m, nil, m.module.options) + app(p.g.code, genProc(p, prc)) # write the file: var code = con(globals.typeInfo, globals.code) diff --git a/compiler/jstypes.nim b/compiler/jstypes.nim new file mode 100644 index 000000000..0be1e99dc --- /dev/null +++ b/compiler/jstypes.nim @@ -0,0 +1,148 @@ +# +# +# The Nimrod Compiler +# (c) Copyright 2013 Andreas Rumpf +# +# See the file "copying.txt", included in this +# distribution, for details about the copyright. +# + +## Type info generation for the JS backend. + +proc genTypeInfo(p: PProc, typ: PType): PRope +proc genObjectFields(p: PProc, typ: PType, n: PNode): PRope = + var + s, u: PRope + length: int + field: PSym + b: PNode + result = nil + case n.kind + of nkRecList: + length = sonsLen(n) + if length == 1: + result = genObjectFields(p, typ, n.sons[0]) + else: + s = nil + for i in countup(0, length - 1): + if i > 0: app(s, ", " & tnl) + app(s, genObjectFields(p, typ, n.sons[i])) + result = ropef("{kind: 2, len: $1, offset: 0, " & + "typ: null, name: null, sons: [$2]}", [toRope(length), s]) + of nkSym: + field = n.sym + s = genTypeInfo(p, field.typ) + result = ropef("{kind: 1, offset: \"$1\", len: 0, " & + "typ: $2, name: $3, sons: null}", + [mangleName(field), s, makeJSString(field.name.s)]) + of nkRecCase: + length = sonsLen(n) + if (n.sons[0].kind != nkSym): InternalError(n.info, "genObjectFields") + field = n.sons[0].sym + s = genTypeInfo(p, field.typ) + for i in countup(1, length - 1): + b = n.sons[i] # branch + u = nil + case b.kind + of nkOfBranch: + if sonsLen(b) < 2: + internalError(b.info, "genObjectFields; nkOfBranch broken") + for j in countup(0, sonsLen(b) - 2): + if u != nil: app(u, ", ") + if b.sons[j].kind == nkRange: + appf(u, "[$1, $2]", [toRope(getOrdValue(b.sons[j].sons[0])), + toRope(getOrdValue(b.sons[j].sons[1]))]) + else: + app(u, toRope(getOrdValue(b.sons[j]))) + of nkElse: + u = toRope(lengthOrd(field.typ)) + else: internalError(n.info, "genObjectFields(nkRecCase)") + if result != nil: app(result, ", " & tnl) + appf(result, "[SetConstr($1), $2]", + [u, genObjectFields(p, typ, lastSon(b))]) + result = ropef("{kind: 3, offset: \"$1\", len: $3, " & + "typ: $2, name: $4, sons: [$5]}", [mangleName(field), s, + toRope(lengthOrd(field.typ)), makeJSString(field.name.s), result]) + else: internalError(n.info, "genObjectFields") + +proc genObjectInfo(p: PProc, typ: PType, name: PRope) = + var s = ropef("var $1 = {size: 0, kind: $2, base: null, node: null, " & + "finalizer: null};$n", [name, toRope(ord(typ.kind))]) + prepend(p.g.typeInfo, s) + appf(p.g.typeInfo, "var NNI$1 = $2;$n", + [toRope(typ.id), genObjectFields(p, typ, typ.n)]) + appf(p.g.typeInfo, "$1.node = NNI$2;$n", [name, toRope(typ.id)]) + if (typ.kind == tyObject) and (typ.sons[0] != nil): + appf(p.g.typeInfo, "$1.base = $2;$n", + [name, genTypeInfo(p, typ.sons[0])]) + +proc genTupleFields(p: PProc, typ: PType): PRope = + var s: PRope = nil + for i in 0 .. <typ.len: + if i > 0: app(s, ", " & tnl) + s.appf("{kind: 1, offset: \"Field$1\", len: 0, " & + "typ: $2, name: \"Field$1\", sons: null}", + [i.toRope, genTypeInfo(p, typ.sons[i])]) + result = ropef("{kind: 2, len: $1, offset: 0, " & + "typ: null, name: null, sons: [$2]}", [toRope(typ.len), s]) + +proc genTupleInfo(p: PProc, typ: PType, name: PRope) = + var s = ropef("var $1 = {size: 0, kind: $2, base: null, node: null, " & + "finalizer: null};$n", [name, toRope(ord(typ.kind))]) + prepend(p.g.typeInfo, s) + appf(p.g.typeInfo, "var NNI$1 = $2;$n", + [toRope(typ.id), genTupleFields(p, typ)]) + appf(p.g.typeInfo, "$1.node = NNI$2;$n", [name, toRope(typ.id)]) + +proc genEnumInfo(p: PProc, typ: PType, name: PRope) = + let length = sonsLen(typ.n) + var s: PRope = nil + for i in countup(0, length - 1): + if (typ.n.sons[i].kind != nkSym): InternalError(typ.n.info, "genEnumInfo") + let field = typ.n.sons[i].sym + if i > 0: app(s, ", " & tnl) + let extName = if field.ast == nil: field.name.s else: field.ast.strVal + appf(s, "{kind: 1, offset: $1, typ: $2, name: $3, len: 0, sons: null}", + [toRope(field.position), name, makeJSString(extName)]) + var n = ropef("var NNI$1 = {kind: 2, offset: 0, typ: null, " & + "name: null, len: $2, sons: [$3]};$n", [toRope(typ.id), toRope(length), s]) + s = ropef("var $1 = {size: 0, kind: $2, base: null, node: null, " & + "finalizer: null};$n", [name, toRope(ord(typ.kind))]) + prepend(p.g.typeInfo, s) + app(p.g.typeInfo, n) + appf(p.g.typeInfo, "$1.node = NNI$2;$n", [name, toRope(typ.id)]) + if typ.sons[0] != nil: + appf(p.g.typeInfo, "$1.base = $2;$n", + [name, genTypeInfo(p, typ.sons[0])]) + +proc genTypeInfo(p: PProc, typ: PType): PRope = + var t = typ + if t.kind == tyGenericInst: t = lastSon(t) + result = ropef("NTI$1", [toRope(t.id)]) + if ContainsOrIncl(p.g.TypeInfoGenerated, t.id): return + case t.kind + of tyDistinct: + result = genTypeInfo(p, typ.sons[0]) + of tyPointer, tyProc, tyBool, tyChar, tyCString, tyString, tyInt..tyFloat128: + var s = ropef( + "var $1 = {size: 0,kind: $2,base: null,node: null,finalizer: null};$n", + [result, toRope(ord(t.kind))]) + prepend(p.g.typeInfo, s) + of tyVar, tyRef, tyPtr, tySequence, tyRange, tySet: + var s = ropef( + "var $1 = {size: 0,kind: $2,base: null,node: null,finalizer: null};$n", + [result, toRope(ord(t.kind))]) + prepend(p.g.typeInfo, s) + appf(p.g.typeInfo, "$1.base = $2;$n", + [result, genTypeInfo(p, typ.sons[0])]) + of tyArrayConstr, tyArray: + var s = ropef( + "var $1 = {size: 0,kind: $2,base: null,node: null,finalizer: null};$n", + [result, toRope(ord(t.kind))]) + prepend(p.g.typeInfo, s) + appf(p.g.typeInfo, "$1.base = $2;$n", + [result, genTypeInfo(p, typ.sons[1])]) + of tyEnum: genEnumInfo(p, t, result) + of tyObject: genObjectInfo(p, t, result) + of tyTuple: genTupleInfo(p, t, result) + else: InternalError("genTypeInfo(" & $t.kind & ')') diff --git a/compiler/lambdalifting.nim b/compiler/lambdalifting.nim index ebcd67046..8d4946ab5 100644 --- a/compiler/lambdalifting.nim +++ b/compiler/lambdalifting.nim @@ -104,8 +104,6 @@ discard """ """ const - declarativeDefs* = {nkProcDef, nkMethodDef, nkIteratorDef, nkConverterDef} - procDefs* = nkLambdaKinds + declarativeDefs upName* = ":up" # field name for the 'up' reference paramName* = ":env" envName* = ":env" diff --git a/compiler/lexer.nim b/compiler/lexer.nim index bf9bf5343..6660ff65c 100644 --- a/compiler/lexer.nim +++ b/compiler/lexer.nim @@ -1,7 +1,7 @@ # # # The Nimrod Compiler -# (c) Copyright 2012 Andreas Rumpf +# (c) Copyright 2013 Andreas Rumpf # # See the file "copying.txt", included in this # distribution, for details about the copyright. @@ -58,8 +58,7 @@ type tkParDotLe, tkParDotRi, # (. and .) tkComma, tkSemiColon, tkColon, tkColonColon, tkEquals, tkDot, tkDotDot, - tkOpr, tkComment, tkAccent, tkInd, tkSad, - tkDed, # pseudo token types used by the source renderers: + tkOpr, tkComment, tkAccent, tkSpaces, tkInfixOpr, tkPrefixOpr, tkPostfixOpr, TTokTypes* = set[TTokType] @@ -91,8 +90,8 @@ const ")", "[", "]", "{", "}", "[.", ".]", "{.", ".}", "(.", ".)", ",", ";", ":", "::", "=", ".", "..", - "tkOpr", "tkComment", "`", "[new indentation]", - "[same indentation]", "[dedentation]", "tkSpaces", "tkInfixOpr", + "tkOpr", "tkComment", "`", + "tkSpaces", "tkInfixOpr", "tkPrefixOpr", "tkPostfixOpr"] type @@ -102,7 +101,8 @@ type base2, base8, base16 TToken* = object # a Nimrod token tokType*: TTokType # the type of the token - indent*: int # the indentation; only valid if tokType = tkIndent + indent*: int # the indentation; != -1 if the token has been + # preceeded with indentation ident*: PIdent # the parsed identifier iNumber*: BiggestInt # the parsed integer literal fNumber*: BiggestFloat # the parsed floating point literal @@ -113,8 +113,6 @@ type TLexer* = object of TBaseLexer fileIdx*: int32 - indentStack*: seq[int] # the indentation stack - dedent*: int # counter for DED token generation indentAhead*: int # if > 0 an indendation has already been read # this is needed because scanning comments # needs so much look-ahead @@ -122,9 +120,6 @@ type var gLinesCompiled*: int # all lines that have been compiled -proc pushInd*(L: var TLexer, indent: int) - -proc popInd*(L: var TLexer) proc isKeyword*(kind: TTokType): bool proc openLexer*(lex: var TLexer, fileidx: int32, inputstream: PLLStream) proc rawGetTok*(L: var TLexer, tok: var TToken) @@ -154,31 +149,14 @@ proc isNimrodIdentifier*(s: string): bool = inc(i) result = true -proc pushInd(L: var TLexer, indent: int) = - var length = len(L.indentStack) - setlen(L.indentStack, length + 1) - if (indent > L.indentStack[length - 1]): - L.indentstack[length] = indent - else: - InternalError("pushInd") - -proc popInd(L: var TLexer) = - var length = len(L.indentStack) - setlen(L.indentStack, length - 1) - -proc findIdent(L: TLexer, indent: int): bool = - for i in countdown(len(L.indentStack) - 1, 0): - if L.indentStack[i] == indent: - return true - proc tokToStr*(tok: TToken): string = case tok.tokType of tkIntLit..tkInt64Lit: result = $tok.iNumber of tkFloatLit..tkFloat64Lit: result = $tok.fNumber of tkInvalid, tkStrLit..tkCharLit, tkComment: result = tok.literal - of tkParLe..tkColon, tkEof, tkInd, tkSad, tkDed, tkAccent: + of tkParLe..tkColon, tkEof, tkAccent: result = tokTypeToStr[tok.tokType] - else: + else: if tok.ident != nil: result = tok.ident.s else: @@ -216,7 +194,6 @@ proc fillToken(L: var TToken) = proc openLexer(lex: var TLexer, fileIdx: int32, inputstream: PLLStream) = openBaseLexer(lex, inputstream) - lex.indentStack = @[0] lex.fileIdx = fileIdx lex.indentAhead = - 1 inc(lex.Linenumber, inputstream.lineOffset) @@ -434,9 +411,10 @@ proc GetNumber(L: var TLexer): TToken = result.tokType = tkInt64Lit elif result.tokType != tkInt64Lit: lexMessage(L, errInvalidNumber, result.literal) - except EInvalidValue: lexMessage(L, errInvalidNumber, result.literal) - except EOverflow: lexMessage(L, errNumberOutOfRange, result.literal) - except EOutOfRange: lexMessage(L, errNumberOutOfRange, result.literal) + except EInvalidValue: + lexMessage(L, errInvalidNumber, result.literal) + except EOverflow, EOutOfRange: + lexMessage(L, errNumberOutOfRange, result.literal) L.bufpos = endpos proc handleHexChar(L: var TLexer, xi: var int) = @@ -651,24 +629,6 @@ proc getOperator(L: var TLexer, tok: var TToken) = Inc(pos) endOperator(L, tok, pos, h) -proc handleIndentation(L: var TLexer, tok: var TToken, indent: int) = - tok.indent = indent - var i = high(L.indentStack) - if indent > L.indentStack[i]: - tok.tokType = tkInd - elif indent == L.indentStack[i]: - tok.tokType = tkSad - else: - # check we have the indentation somewhere in the stack: - while (i >= 0) and (indent != L.indentStack[i]): - dec(i) - inc(L.dedent) - dec(L.dedent) - tok.tokType = tkDed - if i < 0: - tok.tokType = tkSad # for the parser it is better as SAD - lexMessage(L, errInvalidIndentation) - proc scanComment(L: var TLexer, tok: var TToken) = var pos = L.bufpos var buf = L.buf @@ -705,53 +665,45 @@ proc scanComment(L: var TLexer, tok: var TToken) = else: if buf[pos] > ' ': L.indentAhead = indent - inc(L.dedent) - break + break L.bufpos = pos -proc skip(L: var TLexer, tok: var TToken) = +proc skip(L: var TLexer, tok: var TToken) = var pos = L.bufpos var buf = L.buf - while true: + while true: case buf[pos] - of ' ': + of ' ': Inc(pos) - of Tabulator: + of Tabulator: lexMessagePos(L, errTabulatorsAreNotAllowed, pos) - inc(pos) # BUGFIX - of CR, LF: + inc(pos) + of CR, LF: pos = HandleCRLF(L, pos) buf = L.buf var indent = 0 - while buf[pos] == ' ': + while buf[pos] == ' ': Inc(pos) Inc(indent) - if (buf[pos] > ' '): - handleIndentation(L, tok, indent) - break - else: + if buf[pos] > ' ': + tok.indent = indent + break + else: break # EndOfFile also leaves the loop L.bufpos = pos -proc rawGetTok(L: var TLexer, tok: var TToken) = +proc rawGetTok(L: var TLexer, tok: var TToken) = fillToken(tok) - if L.dedent > 0: - dec(L.dedent) - if L.indentAhead >= 0: - handleIndentation(L, tok, L.indentAhead) - L.indentAhead = - 1 - else: - tok.tokType = tkDed - return + if L.indentAhead >= 0: + tok.indent = L.indentAhead + L.indentAhead = -1 + else: + tok.indent = -1 skip(L, tok) - # got an documentation comment or tkIndent, return that: - if tok.toktype != tkInvalid: return var c = L.buf[L.bufpos] - if c in SymStartChars - {'r', 'R', 'l'}: + if c in SymStartChars - {'r', 'R', 'l'}: getSymbol(L, tok) - elif c in {'0'..'9'}: - tok = getNumber(L) - else: + else: case c of '#': scanComment(L, tok) @@ -769,10 +721,10 @@ proc rawGetTok(L: var TLexer, tok: var TToken) = of 'l': # if we parsed exactly one character and its a small L (l), this # is treated as a warning because it may be confused with the number 1 - if not (L.buf[L.bufpos + 1] in (SymChars + {'_'})): + if L.buf[L.bufpos+1] notin (SymChars + {'_'}): lexMessage(L, warnSmallLshouldNotBeUsed) getSymbol(L, tok) - of 'r', 'R': + of 'r', 'R': if L.buf[L.bufPos + 1] == '\"': Inc(L.bufPos) getString(L, tok, true) @@ -780,7 +732,7 @@ proc rawGetTok(L: var TLexer, tok: var TToken) = getSymbol(L, tok) of '(': Inc(L.bufpos) - if (L.buf[L.bufPos] == '.') and (L.buf[L.bufPos + 1] != '.'): + if L.buf[L.bufPos] == '.' and L.buf[L.bufPos+1] != '.': tok.toktype = tkParDotLe Inc(L.bufpos) else: @@ -790,29 +742,29 @@ proc rawGetTok(L: var TLexer, tok: var TToken) = Inc(L.bufpos) of '[': Inc(L.bufpos) - if (L.buf[L.bufPos] == '.') and (L.buf[L.bufPos + 1] != '.'): + if L.buf[L.bufPos] == '.' and L.buf[L.bufPos+1] != '.': tok.toktype = tkBracketDotLe Inc(L.bufpos) - else: + else: tok.toktype = tkBracketLe - of ']': + of ']': tok.toktype = tkBracketRi Inc(L.bufpos) - of '.': - if L.buf[L.bufPos + 1] == ']': + of '.': + if L.buf[L.bufPos+1] == ']': tok.tokType = tkBracketDotRi Inc(L.bufpos, 2) - elif L.buf[L.bufPos + 1] == '}': + elif L.buf[L.bufPos+1] == '}': tok.tokType = tkCurlyDotRi Inc(L.bufpos, 2) - elif L.buf[L.bufPos + 1] == ')': + elif L.buf[L.bufPos+1] == ')': tok.tokType = tkParDotRi Inc(L.bufpos, 2) else: getOperator(L, tok) of '{': Inc(L.bufpos) - if (L.buf[L.bufPos] == '.') and (L.buf[L.bufPos+1] != '.'): + if L.buf[L.bufPos] == '.' and L.buf[L.bufPos+1] != '.': tok.toktype = tkCurlyDotLe Inc(L.bufpos) else: @@ -838,13 +790,16 @@ proc rawGetTok(L: var TLexer, tok: var TToken) = tok.tokType = tkCharLit getCharacter(L, tok) tok.tokType = tkCharLit + of '0'..'9': + tok = getNumber(L) else: if c in OpChars: getOperator(L, tok) elif c == nimlexbase.EndOfFile: tok.toktype = tkEof + tok.indent = 0 else: - tok.literal = c & "" + tok.literal = $c tok.tokType = tkInvalid lexMessage(L, errInvalidToken, c & " (\\" & $(ord(c)) & ')') Inc(L.bufpos) diff --git a/compiler/main.nim b/compiler/main.nim index 46ef65e81..2ff7691d8 100644 --- a/compiler/main.nim +++ b/compiler/main.nim @@ -16,7 +16,7 @@ import wordrecg, sem, semdata, idents, passes, docgen, extccomp, cgen, jsgen, cgendata, json, nversion, platform, nimconf, importer, passaux, depends, evals, types, idgen, - tables, docgen2, service, magicsys, parser, crc, ccgutils + tables, docgen2, service, magicsys, parser, crc, ccgutils, sigmatch const has_LLVM_Backend = false @@ -64,7 +64,7 @@ proc crcChanged(fileIdx: int32): bool = gMemCacheData[fileIdx].crcStatus = if result: crcHasChanged else: crcNotChanged # echo "TESTING CRC: ", fileIdx.toFilename, " ", result - + case gMemCacheData[fileIdx].crcStatus: of crcHasChanged: result = true @@ -89,38 +89,38 @@ proc addDep(x: Psym, dep: int32) = growCache gMemCacheData, dep gMemCacheData[x.position].deps.safeAdd(dep) -proc ResetModule(fileIdx: int32) = - echo "HARD RESETTING ", fileIdx.toFilename +proc resetModule(fileIdx: int32) = + # echo "HARD RESETTING ", fileIdx.toFilename gMemCacheData[fileIdx].needsRecompile = Yes gCompiledModules[fileIdx] = nil cgendata.gModules[fileIdx] = nil + resetSourceMap(fileIdx) -proc ResetAllModules = +proc resetAllModules = for i in 0..gCompiledModules.high: if gCompiledModules[i] != nil: - ResetModule(i.int32) + resetModule(i.int32) - for m in cgenModules(): - echo "CGEN MODULE FOUND" + # for m in cgenModules(): echo "CGEN MODULE FOUND" proc checkDepMem(fileIdx: int32): TNeedRecompile = template markDirty = - ResetModule(fileIdx) + resetModule(fileIdx) return Yes if gMemCacheData[fileIdx].needsRecompile != Maybe: return gMemCacheData[fileIdx].needsRecompile if optForceFullMake in gGlobalOptions or - curCaasCmd != lastCaasCmd or - crcChanged(fileIdx): markDirty + crcChanged(fileIdx): + markDirty if gMemCacheData[fileIdx].deps != nil: gMemCacheData[fileIdx].needsRecompile = Probing for dep in gMemCacheData[fileIdx].deps: let d = checkDepMem(dep) if d in { Yes, Recompiled }: - echo fileIdx.toFilename, " depends on ", dep.toFilename, " ", d + # echo fileIdx.toFilename, " depends on ", dep.toFilename, " ", d markDirty gMemCacheData[fileIdx].needsRecompile = No @@ -213,6 +213,9 @@ proc rodPass = if optSymbolFiles in gGlobalOptions: registerPass(rodwritePass) +proc codegenPass = + registerPass cgenPass + proc semanticPasses = registerPass verbosePass registerPass semPass @@ -251,14 +254,11 @@ proc CommandCompileToC = # echo "CHECK DEP COMPLETE" compileProject() - - if compilationCachePresent: - updateCachedModules() - + cgenWriteModules() if gCmd != cmdRun: extccomp.CallCCompiler(changeFileExt(gProjectFull, "")) - if optCaasEnabled in gGlobalOptions: + if isServing: # caas will keep track only of the compilation commands lastCaasCmd = curCaasCmd resetCgenModules() @@ -377,10 +377,23 @@ proc CommandScan = rawMessage(errCannotOpenFile, f) proc CommandSuggest = - msgs.gErrorMax = high(int) # do not stop after first error - semanticPasses() - rodPass() - compileProject() + if isServing: + # XXX: hacky work-around ahead + # Currently, it's possible to issue a idetools command, before + # issuing the first compile command. This will leave the compiler + # cache in a state where "no recompilation is necessary", but the + # cgen pass was never executed at all. + CommandCompileToC() + if gDirtyBufferIdx != 0: + discard compileModule(gDirtyBufferIdx, {sfDirty}) + resetModule(gDirtyBufferIdx) + if optDef in gGlobalOptions: + defFromSourceMap(optTrackPos) + else: + msgs.gErrorMax = high(int) # do not stop after first error + semanticPasses() + rodPass() + compileProject() proc wantMainModule = if gProjectFull.len == 0: @@ -404,7 +417,7 @@ proc requireMainModuleOption = proc resetMemory = resetCompilationLists() ccgutils.resetCaches() - ResetAllModules() + resetAllModules() resetRopeCache() resetSysTypes() gOwners = @[] diff --git a/compiler/msgs.nim b/compiler/msgs.nim index 6062ebd7f..16ef06b61 100644 --- a/compiler/msgs.nim +++ b/compiler/msgs.nim @@ -526,13 +526,17 @@ proc SuggestWriteln*(s: string) = else: Writeln(stdout, s) stdoutSocket.send(s & "\c\L") - + proc SuggestQuit*() = - if not isServing: quit(0) - elif not isNil(stdoutSocket): - stdoutSocket.send("\c\L") + if not isServing: + quit(0) + elif isWorkingWithDirtyBuffer: + # No need to compile the rest if we are working with a + # throw-away buffer. Incomplete dot expressions frequently + # found in dirty buffers will result in errors few steps + # from now anyway. raise newException(ESuggestDone, "suggest done") - + # this format is understood by many text editors: it is the same that # Borland and Freepascal use const @@ -606,6 +610,7 @@ proc `??`* (info: TLineInfo, filename: string): bool = result = filename in info.toFilename var checkPoints*: seq[TLineInfo] = @[] +var optTrackPos*: TLineInfo proc addCheckpoint*(info: TLineInfo) = checkPoints.add(info) @@ -714,7 +719,7 @@ var proc writeSurroundingSrc(info: TLineInfo) = const indent = " " - MsgWriteln(indent & info.sourceLine.data) + MsgWriteln(indent & info.sourceLine.ropeToStr) MsgWriteln(indent & repeatChar(info.col, ' ') & '^') proc liMessage(info: TLineInfo, msg: TMsgKind, arg: string, @@ -789,8 +794,9 @@ proc sourceLine*(i: TLineInfo): PRope = for line in lines(i.toFullPath): addSourceLine i.fileIndex, line.string - InternalAssert i.fileIndex < fileInfos.len and - i.line <= fileInfos[i.fileIndex].lines.len + InternalAssert i.fileIndex < fileInfos.len + # can happen if the error points to EOF: + if i.line > fileInfos[i.fileIndex].lines.len: return nil result = fileInfos[i.fileIndex].lines[i.line-1] diff --git a/compiler/nimconf.nim b/compiler/nimconf.nim index 0f0b76827..3bd97ccb2 100644 --- a/compiler/nimconf.nim +++ b/compiler/nimconf.nim @@ -19,7 +19,7 @@ import proc ppGetTok(L: var TLexer, tok: var TToken) = # simple filter rawGetTok(L, tok) - while tok.tokType in {tkInd, tkSad, tkDed, tkComment}: rawGetTok(L, tok) + while tok.tokType in {tkComment}: rawGetTok(L, tok) proc parseExpr(L: var TLexer, tok: var TToken): bool proc parseAtom(L: var TLexer, tok: var TToken): bool = diff --git a/compiler/options.nim b/compiler/options.nim index a7d513dc5..f67ded163 100644 --- a/compiler/options.nim +++ b/compiler/options.nim @@ -7,13 +7,14 @@ # distribution, for details about the copyright. # -import +import os, lists, strutils, strtabs const hasTinyCBackend* = defined(tinyc) useEffectSystem* = true hasFFI* = defined(useFFI) + newScopeForIf* = false # XXX activate for 0.9.4 type # please make sure we have under 32 options # (improves code efficiency a lot!) @@ -106,10 +107,16 @@ var gLastCmdTime*: float # when caas is enabled, we measure each command gListFullPaths*: bool isServing*: bool = false + gDirtyBufferIdx* = 0'i32 # indicates the fileIdx of the dirty version of + # the tracked source X, saved by the CAAS client. + gDirtyOriginalIdx* = 0'i32 # the original source file of the dirtified buffer. proc importantComments*(): bool {.inline.} = gCmd in {cmdDoc, cmdIdeTools} proc usesNativeGC*(): bool {.inline.} = gSelectedGC >= gcRefc +template isWorkingWithDirtyBuffer*: expr = + gDirtyBufferIdx != 0 + template compilationCachePresent*: expr = {optCaasEnabled, optSymbolFiles} * gGlobalOptions != {} diff --git a/compiler/parser.nim b/compiler/parser.nim index 769aa7a3e..e2167f460 100644 --- a/compiler/parser.nim +++ b/compiler/parser.nim @@ -1,7 +1,7 @@ # # # The Nimrod Compiler -# (c) Copyright 2012 Andreas Rumpf +# (c) Copyright 2013 Andreas Rumpf # # See the file "copying.txt", included in this # distribution, for details about the copyright. @@ -12,17 +12,28 @@ # it uses several helper routines to keep the parser small. A special # efficient algorithm is used for the precedence levels. The parser here can # be seen as a refinement of the grammar, as it specifies how the AST is built -# from the grammar and how comments belong to the AST. +# from the grammar and how comments belong to the AST. + + +# In fact the grammar is generated from this file: +when isMainModule: + import pegs + var outp = open("compiler/grammar.txt", fmWrite) + for line in lines("compiler/parser.nim"): + if line =~ peg" \s* '#| ' {.*}": + outp.writeln matches[0] + outp.close import - llstream, lexer, idents, strutils, ast, msgs + llstream, lexer, idents, strutils, ast, astalgo, msgs type TParser*{.final.} = object # a TParser object represents a module that # is being parsed + currInd: int # current indentation + firstTok: bool lex*: TLexer # the lexer that is used for parsing tok*: TToken # the current token - proc ParseAll*(p: var TParser): PNode proc openParser*(p: var TParser, filename: string, inputstream: PLLStream) @@ -68,6 +79,7 @@ proc OpenParser*(p: var TParser, fileIdx: int32, inputStream: PLLStream) = initToken(p.tok) OpenLexer(p.lex, fileIdx, inputstream) getTok(p) # read the first token + p.firstTok = true proc OpenParser*(p: var TParser, filename: string, inputStream: PLLStream) = openParser(p, filename.fileInfoIdx, inputStream) @@ -81,51 +93,64 @@ proc parMessage(p: TParser, msg: TMsgKind, arg: string = "") = proc parMessage(p: TParser, msg: TMsgKind, tok: TToken) = lexMessage(p.lex, msg, prettyTok(tok)) -proc skipComment(p: var TParser, node: PNode) = - if p.tok.tokType == tkComment: - if node != nil: +template withInd(p: expr, body: stmt) {.immediate.} = + let oldInd = p.currInd + p.currInd = p.tok.indent + body + p.currInd = oldInd + +template realInd(p): bool = p.tok.indent > p.currInd +template sameInd(p): bool = p.tok.indent == p.currInd +template sameOrNoInd(p): bool = p.tok.indent == p.currInd or p.tok.indent < 0 + +proc rawSkipComment(p: var TParser, node: PNode) = + if p.tok.tokType == tkComment: + if node != nil: if node.comment == nil: node.comment = "" add(node.comment, p.tok.literal) - else: + else: parMessage(p, errInternal, "skipComment") getTok(p) -proc skipInd(p: var TParser) = - if p.tok.tokType == tkInd: getTok(p) - -proc optPar(p: var TParser) = - if p.tok.tokType == tkSad or p.tok.tokType == tkInd: getTok(p) - -proc optInd(p: var TParser, n: PNode) = +proc skipComment(p: var TParser, node: PNode) = + if p.tok.indent < 0: rawSkipComment(p, node) + +proc skipInd(p: var TParser) = + if p.tok.indent >= 0: + if not realInd(p): parMessage(p, errInvalidIndentation) + +proc optPar(p: var TParser) = + if p.tok.indent >= 0: + if p.tok.indent < p.currInd: parMessage(p, errInvalidIndentation) + +proc optInd(p: var TParser, n: PNode) = skipComment(p, n) skipInd(p) -proc ExpectNl(p: TParser) = - if p.tok.tokType notin {tkEof, tkSad, tkInd, tkDed, tkComment}: - lexMessage(p.lex, errNewlineExpected, prettyTok(p.tok)) +proc getTokNoInd(p: var TParser) = + getTok(p) + if p.tok.indent >= 0: parMessage(p, errInvalidIndentation) -proc expectIdentOrKeyw(p: TParser) = - if p.tok.tokType != tkSymbol and not isKeyword(p.tok.tokType): +proc expectIdentOrKeyw(p: TParser) = + if p.tok.tokType != tkSymbol and not isKeyword(p.tok.tokType): lexMessage(p.lex, errIdentifierExpected, prettyTok(p.tok)) -proc ExpectIdent(p: TParser) = - if p.tok.tokType != tkSymbol: +proc ExpectIdent(p: TParser) = + if p.tok.tokType != tkSymbol: lexMessage(p.lex, errIdentifierExpected, prettyTok(p.tok)) -proc Eat(p: var TParser, TokType: TTokType) = +proc Eat(p: var TParser, TokType: TTokType) = if p.tok.TokType == TokType: getTok(p) else: lexMessage(p.lex, errTokenExpected, TokTypeToStr[tokType]) -proc parLineInfo(p: TParser): TLineInfo = +proc parLineInfo(p: TParser): TLineInfo = result = getLineInfo(p.lex) -proc indAndComment(p: var TParser, n: PNode) = - if p.tok.tokType == tkInd: - var info = parLineInfo(p) - getTok(p) - if p.tok.tokType == tkComment: skipComment(p, n) - else: LocalError(info, errInvalidIndentation) - else: +proc indAndComment(p: var TParser, n: PNode) = + if p.tok.indent > p.currInd: + if p.tok.tokType == tkComment: rawSkipComment(p, n) + else: parMessage(p, errInvalidIndentation) + else: skipComment(p, n) proc newNodeP(kind: TNodeKind, p: TParser): PNode = @@ -195,7 +220,41 @@ proc getPrecedence(tok: TToken): int = proc isOperator(tok: TToken): bool = result = getPrecedence(tok) >= 0 -proc parseSymbol(p: var TParser): PNode = +#| module = stmt ^* (';' / IND{=}) +#| +#| comma = ',' COMMENT? +#| semicolon = ';' COMMENT? +#| colon = ':' COMMENT? +#| colcom = ':' COMMENT? +#| +#| operator = OP0 | OP1 | OP2 | OP3 | OP4 | OP5 | OP6 | OP7 | OP8 | OP9 +#| | 'or' | 'xor' | 'and' +#| | 'is' | 'isnot' | 'in' | 'notin' | 'of' +#| | 'div' | 'mod' | 'shl' | 'shr' | 'not' | 'addr' | 'static' | '..' +#| +#| prefixOperator = operator +#| +#| optInd = COMMENT? +#| optPar = (IND{>} | IND{=})? +#| +#| simpleExpr = assignExpr (OP0 optInd assignExpr)* +#| assignExpr = orExpr (OP1 optInd orExpr)* +#| orExpr = andExpr (OP2 optInd andExpr)* +#| andExpr = cmpExpr (OP3 optInd cmpExpr)* +#| cmpExpr = sliceExpr (OP4 optInd sliceExpr)* +#| sliceExpr = ampExpr (OP5 optInd ampExpr)* +#| ampExpr = plusExpr (OP6 optInd plusExpr)* +#| plusExpr = mulExpr (OP7 optInd mulExpr)* +#| mulExpr = dollarExpr (OP8 optInd dollarExpr)* +#| dollarExpr = primary (OP9 optInd primary)* + +proc colcom(p: var TParser, n: PNode) = + eat(p, tkColon) + skipComment(p, n) + +proc parseSymbol(p: var TParser): PNode = + #| symbol = '`' (KEYW|IDENT|operator|'(' ')'|'[' ']'|'{' '}'|'='|literal)+ '`' + #| | IDENT case p.tok.tokType of tkSymbol: result = newIdentNodeP(p.tok.ident, p) @@ -231,31 +290,32 @@ proc parseSymbol(p: var TParser): PNode = parMessage(p, errIdentifierExpected, p.tok) break eat(p, tkAccent) - else: + else: parMessage(p, errIdentifierExpected, p.tok) getTok(p) # BUGFIX: We must consume a token here to prevent endless loops! result = ast.emptyNode proc indexExpr(p: var TParser): PNode = + #| indexExpr = expr result = parseExpr(p) proc indexExprList(p: var TParser, first: PNode, k: TNodeKind, endToken: TTokType): PNode = + #| indexExprList = indexExpr ^+ comma result = newNodeP(k, p) addSon(result, first) getTok(p) optInd(p, result) - while p.tok.tokType notin {endToken, tkEof, tkSad}: + while p.tok.tokType notin {endToken, tkEof}: var a = indexExpr(p) addSon(result, a) if p.tok.tokType != tkComma: break getTok(p) - optInd(p, a) + skipComment(p, a) optPar(p) eat(p, endToken) -proc exprColonEqExpr(p: var TParser): PNode = - var a = parseExpr(p) +proc colonOrEquals(p: var TParser, a: PNode): PNode = if p.tok.tokType == tkColon: result = newNodeP(nkExprColonExpr, p) getTok(p) @@ -271,7 +331,13 @@ 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) optInd(p, result) while (p.tok.tokType != endTok) and (p.tok.tokType != tkEof): @@ -283,6 +349,7 @@ proc exprList(p: var TParser, endTok: TTokType, result: PNode) = eat(p, endTok) proc dotExpr(p: var TParser, a: PNode): PNode = + #| dotExpr = expr '.' optInd ('type' | 'addr' | symbol) var info = p.lex.getlineInfo getTok(p) optInd(p, a) @@ -301,40 +368,31 @@ proc dotExpr(p: var TParser, a: PNode): PNode = addSon(result, parseSymbol(p)) proc qualifiedIdent(p: var TParser): PNode = - result = parseSymbol(p) #optInd(p, result); + #| qualifiedIdent = symbol ('.' optInd ('type' | 'addr' | symbol))? + result = parseSymbol(p) if p.tok.tokType == tkDot: result = dotExpr(p, result) -proc qualifiedIdentListAux(p: var TParser, endTok: TTokType, result: PNode) = - getTok(p) - optInd(p, result) - while (p.tok.tokType != endTok) and (p.tok.tokType != tkEof): - var a = qualifiedIdent(p) - addSon(result, a) #optInd(p, a); - if p.tok.tokType != tkComma: break - getTok(p) - optInd(p, a) - eat(p, endTok) - -proc exprColonEqExprListAux(p: var TParser, endTok: TTokType, result: PNode) = +proc exprColonEqExprListAux(p: var TParser, endTok: TTokType, result: PNode) = assert(endTok in {tkCurlyRi, tkCurlyDotRi, tkBracketRi, tkParRi}) getTok(p) optInd(p, result) - while (p.tok.tokType != endTok) and (p.tok.tokType != tkEof) and - (p.tok.tokType != tkSad) and (p.tok.tokType != tkInd): + while p.tok.tokType != endTok and p.tok.tokType != tkEof: var a = exprColonEqExpr(p) addSon(result, a) if p.tok.tokType != tkComma: break getTok(p) - optInd(p, a) + skipComment(p, a) optPar(p) eat(p, endTok) -proc exprColonEqExprList(p: var TParser, kind: TNodeKind, - endTok: TTokType): PNode = +proc exprColonEqExprList(p: var TParser, kind: TNodeKind, + endTok: TTokType): PNode = + #| exprColonEqExprList = exprColonEqExpr (comma exprColonEqExpr)* (comma)? result = newNodeP(kind, p) exprColonEqExprListAux(p, endTok, result) proc setOrTableConstr(p: var TParser): PNode = + #| setOrTableConstr = '{' ((exprColonEqExpr comma)* | ':' ) '}' result = newNodeP(nkCurly, p) getTok(p) # skip '{' optInd(p, result) @@ -342,17 +400,18 @@ proc setOrTableConstr(p: var TParser): PNode = getTok(p) # skip ':' result.kind = nkTableConstr else: - while p.tok.tokType notin {tkCurlyRi, tkEof, tkSad, tkInd}: + while p.tok.tokType notin {tkCurlyRi, tkEof}: var a = exprColonEqExpr(p) if a.kind == nkExprColonExpr: result.kind = nkTableConstr addSon(result, a) if p.tok.tokType != tkComma: break getTok(p) - optInd(p, a) + skipComment(p, a) optPar(p) eat(p, tkCurlyRi) # skip '}' proc parseCast(p: var TParser): PNode = + #| castExpr = 'cast' '[' optInd typeDesc optPar ']' '(' optInd expr optPar ')' result = newNodeP(nkCast, p) getTok(p) eat(p, tkBracketLe) @@ -366,15 +425,6 @@ proc parseCast(p: var TParser): PNode = optPar(p) eat(p, tkParRi) -proc parseAddr(p: var TParser): PNode = - result = newNodeP(nkAddr, p) - getTok(p) - eat(p, tkParLe) - optInd(p, result) - addSon(result, parseExpr(p)) - optPar(p) - eat(p, tkParRi) - proc setBaseFlags(n: PNode, base: TNumericalBase) = case base of base10: nil @@ -396,16 +446,98 @@ proc parseGStrLit(p: var TParser, a: PNode): PNode = getTok(p) else: result = a - -proc identOrLiteral(p: var TParser): PNode = + +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) = + 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' | '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, + tkMixin}: + # XXX 'bind' used to be an expression, so we exclude it here; + # tests/reject/tbind2 fails otherwise. + semiStmtList(p, result) + elif p.tok.tokType == tkSemicolon: + # '(;' enforces 'stmt' context: + getTok(p) + optInd(p, result) + 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 + #| | FLOAT_LIT | FLOAT32_LIT | FLOAT64_LIT + #| | STR_LIT | RSTR_LIT | TRIPLESTR_LIT + #| | CHAR_LIT + #| | NIL + #| | par | arrayConstr | setOrTableConstr + #| | castExpr + #| tupleConstr = '(' optInd (exprColonEqExpr comma?)* optPar ')' + #| arrayConstr = '[' optInd (exprColonEqExpr comma?)* optPar ']' case p.tok.tokType - of tkSymbol: + of tkSymbol: result = newIdentNodeP(p.tok.ident, p) getTok(p) result = parseGStrLit(p, result) of tkAccent: result = parseSymbol(p) # literals - of tkIntLit: + of tkIntLit: result = newIntNodeP(nkIntLit, p.tok.iNumber, p) setBaseFlags(result, p.tok.base) getTok(p) @@ -478,7 +610,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) @@ -493,8 +628,13 @@ proc identOrLiteral(p: var TParser): PNode = result = ast.emptyNode proc primarySuffix(p: var TParser, r: PNode): PNode = + #| primarySuffix = '(' (exprColonEqExpr comma?)* ')' doBlocks? + #| | doBlocks + #| | '.' optInd ('type' | 'addr' | symbol) generalizedLit? + #| | '[' optInd indexExprList optPar ']' + #| | '{' optInd indexExprList optPar '}' result = r - while true: + while p.tok.indent < 0: case p.tok.tokType of tkParLe: var a = result @@ -519,69 +659,70 @@ 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 lowestExprAux(p: var TParser, limit: int, mode: TPrimaryMode): PNode = +proc simpleExprAux(p: var TParser, limit: int, mode: TPrimaryMode): PNode = result = primary(p, mode) # expand while operators have priorities higher than 'limit' var opPrec = getPrecedence(p.tok) let modeB = if mode == pmTypeDef: pmTypeDesc else: mode - while opPrec >= limit: + # the operator itself must not start on a new line: + while opPrec >= limit and p.tok.indent < 0: var leftAssoc = ord(IsLeftAssociative(p.tok)) var a = newNodeP(nkInfix, p) var opNode = newIdentNodeP(p.tok.ident, p) # skip operator: getTok(p) - optInd(p, opNode) + optInd(p, opNode) # read sub-expression with higher priority: - var b = lowestExprAux(p, opPrec + leftAssoc, modeB) + var b = simpleExprAux(p, opPrec + leftAssoc, modeB) addSon(a, opNode) addSon(a, result) addSon(a, b) result = a opPrec = getPrecedence(p.tok) -proc lowestExpr(p: var TParser, mode = pmNormal): PNode = - result = lowestExprAux(p, -1, mode) - -proc parseIfExpr(p: var TParser, kind: TNodeKind): PNode = +proc simpleExpr(p: var TParser, mode = pmNormal): PNode = + result = simpleExprAux(p, -1, mode) + +proc parseIfExpr(p: var TParser, kind: TNodeKind): PNode = + #| condExpr = expr colcom expr optInd + #| ('elif' expr colcom expr optInd)* + #| 'else' colcom expr + #| ifExpr = 'if' condExpr + #| whenExpr = 'when' condExpr result = newNodeP(kind, p) - while true: + while true: getTok(p) # skip `if`, `elif` var branch = newNodeP(nkElifExpr, p) addSon(branch, parseExpr(p)) - eat(p, tkColon) - optInd(p, branch) + colcom(p, branch) addSon(branch, parseExpr(p)) optInd(p, branch) addSon(result, branch) if p.tok.tokType != tkElif: break var branch = newNodeP(nkElseExpr, p) eat(p, tkElse) - eat(p, tkColon) - optInd(p, branch) + colcom(p, branch) addSon(branch, parseExpr(p)) addSon(result, branch) -proc parsePragma(p: var TParser): PNode = +proc parsePragma(p: var TParser): PNode = + #| pragma = '{.' optInd (exprColonExpr comma?)* optPar ('.}' | '}') result = newNodeP(nkPragma, p) getTok(p) optInd(p, result) - while (p.tok.tokType != tkCurlyDotRi) and (p.tok.tokType != tkCurlyRi) and - (p.tok.tokType != tkEof) and (p.tok.tokType != tkSad): + while p.tok.tokType notin {tkCurlyDotRi, tkCurlyRi, tkEof}: var a = exprColonEqExpr(p) addSon(result, a) - if p.tok.tokType == tkComma: + if p.tok.tokType == tkComma: getTok(p) - optInd(p, a) + skipComment(p, a) optPar(p) if p.tok.tokType in {tkCurlyDotRi, tkCurlyRi}: getTok(p) else: parMessage(p, errTokenExpected, ".}") proc identVis(p: var TParser): PNode = - # identifier with visability + #| identVis = symbol opr? # postfix position var a = parseSymbol(p) if p.tok.tokType == tkOpr: result = newNodeP(nkPostfix, p) @@ -592,6 +733,7 @@ proc identVis(p: var TParser): PNode = result = a proc identWithPragma(p: var TParser): PNode = + #| identWithPragma = identVis pragma? var a = identVis(p) if p.tok.tokType == tkCurlyDotLe: result = newNodeP(nkPragmaExpr, p) @@ -599,14 +741,18 @@ proc identWithPragma(p: var TParser): PNode = addSon(result, parsePragma(p)) else: result = a - -type + +type TDeclaredIdentFlag = enum withPragma, # identifier may have pragma withBothOptional # both ':' and '=' parts are optional TDeclaredIdentFlags = set[TDeclaredIdentFlag] proc parseIdentColonEquals(p: var TParser, flags: TDeclaredIdentFlags): PNode = + #| declColonEquals = identWithPragma (comma identWithPragma)* comma? + #| (':' optInd typeDesc)? ('=' optInd expr)? + #| identColonEquals = ident (comma ident)* comma? + #| (':' optInd typeDesc)? ('=' optInd expr)?) var a: PNode result = newNodeP(nkIdentDefs, p) while true: @@ -635,53 +781,53 @@ proc parseIdentColonEquals(p: var TParser, flags: TDeclaredIdentFlags): PNode = else: addSon(result, ast.emptyNode) -proc parseTuple(p: var TParser, indentAllowed = false): PNode = +proc parseTuple(p: var TParser, indentAllowed = false): PNode = + #| inlTupleDecl = 'tuple' + #| [' optInd (identColonEquals (comma/semicolon)?)* optPar ']' + #| extTupleDecl = 'tuple' + #| COMMENT? (IND{>} identColonEquals (IND{=} identColonEquals)*)? result = newNodeP(nkTupleTy, p) getTok(p) if p.tok.tokType == tkBracketLe: getTok(p) optInd(p, result) - while (p.tok.tokType == tkSymbol) or (p.tok.tokType == tkAccent): + while p.tok.tokType in {tkSymbol, tkAccent}: var a = parseIdentColonEquals(p, {}) addSon(result, a) - if p.tok.tokType notin {tkComma, tkSemicolon}: break + if p.tok.tokType notin {tkComma, tkSemicolon}: break getTok(p) - optInd(p, a) + skipComment(p, a) optPar(p) eat(p, tkBracketRi) elif indentAllowed: skipComment(p, result) - if p.tok.tokType == tkInd: - pushInd(p.lex, p.tok.indent) - getTok(p) - skipComment(p, result) - while true: - case p.tok.tokType - of tkSad: - getTok(p) - of tkSymbol, tkAccent: - var a = parseIdentColonEquals(p, {}) - skipComment(p, a) - addSon(result, a) - of tkDed: - getTok(p) - break - of tkEof: - break - else: - parMessage(p, errIdentifierExpected, p.tok) - break - popInd(p.lex) - -proc parseParamList(p: var TParser, retColon = true): PNode = + if realInd(p): + withInd(p): + skipComment(p, result) + while true: + case p.tok.tokType + of tkSymbol, tkAccent: + var a = parseIdentColonEquals(p, {}) + skipComment(p, a) + addSon(result, a) + of tkEof: break + else: + parMessage(p, errIdentifierExpected, p.tok) + break + if not sameInd(p): break + +proc parseParamList(p: var TParser, retColon = true): PNode = + #| paramList = '(' identColonEquals ^* (comma/semicolon) ')' + #| paramListArrow = paramList? ('->' optInd typeDesc)? + #| paramListColon = paramList? (':' optInd typeDesc)? var a: PNode result = newNodeP(nkFormalParams, p) addSon(result, ast.emptyNode) # return type - if p.tok.tokType == tkParLe: + if p.tok.tokType == tkParLe and p.tok.indent < 0: getTok(p) optInd(p, result) - while true: - case p.tok.tokType #optInd(p, a); + while true: + case p.tok.tokType of tkSymbol, tkAccent: a = parseIdentColonEquals(p, {withBothOptional}) of tkParRi: @@ -692,21 +838,24 @@ proc parseParamList(p: var TParser, retColon = true): PNode = addSon(result, a) if p.tok.tokType notin {tkComma, tkSemicolon}: break getTok(p) - optInd(p, a) + skipComment(p, a) optPar(p) eat(p, tkParRi) let hasRet = if retColon: p.tok.tokType == tkColon else: p.tok.tokType == tkOpr and IdentEq(p.tok.ident, "->") - if hasRet: + if hasRet and p.tok.indent < 0: getTok(p) optInd(p, result) result.sons[0] = parseTypeDesc(p) proc optPragmas(p: var TParser): PNode = - if p.tok.tokType == tkCurlyDotLe: result = parsePragma(p) - else: result = ast.emptyNode + if p.tok.tokType == tkCurlyDotLe and (p.tok.indent < 0 or realInd(p)): + result = parsePragma(p) + else: + result = ast.emptyNode proc parseDoBlock(p: var TParser): PNode = + #| doBlock = 'do' paramListArrow pragmas? colcom stmt let info = parLineInfo(p) getTok(p) let params = parseParamList(p, retColon=false) @@ -718,26 +867,27 @@ proc parseDoBlock(p: var TParser): PNode = pragmas = pragmas) proc parseDoBlocks(p: var TParser, call: PNode) = - while p.tok.tokType == tkDo: + #| doBlocks = doBlock ^* IND{=} + if p.tok.tokType == tkDo: addSon(call, parseDoBlock(p)) - + while sameInd(p) and p.tok.tokType == tkDo: + addSon(call, parseDoBlock(p)) + proc parseProcExpr(p: var TParser, isExpr: bool): PNode = + #| procExpr = 'proc' paramListColon pragmas? ('=' COMMENT? stmt)? # either a proc type or a anonymous proc - var - pragmas, params: PNode - info: TLineInfo - info = parLineInfo(p) + let info = parLineInfo(p) getTok(p) - let hasSignature = p.tok.tokType in {tkParLe, tkColon} - params = parseParamList(p) - pragmas = optPragmas(p) + let hasSignature = p.tok.tokType in {tkParLe, tkColon} and p.tok.indent < 0 + let params = parseParamList(p) + let pragmas = optPragmas(p) if p.tok.tokType == tkEquals and isExpr: getTok(p) skipComment(p, result) result = newProcNode(nkLambda, info, parseStmt(p), params = params, pragmas = pragmas) - else: + else: result = newNodeI(nkProcTy, info) if hasSignature: addSon(result, params) @@ -752,7 +902,8 @@ proc isExprStart(p: TParser): bool = result = true else: result = false -proc parseTypeDescKAux(p: var TParser, kind: TNodeKind, mode: TPrimaryMode): PNode = +proc parseTypeDescKAux(p: var TParser, kind: TNodeKind, + mode: TPrimaryMode): PNode = result = newNodeP(kind, p) getTok(p) optInd(p, result) @@ -760,25 +911,30 @@ proc parseTypeDescKAux(p: var TParser, kind: TNodeKind, mode: TPrimaryMode): PNo addSon(result, primary(p, mode)) proc parseExpr(p: var TParser): PNode = - # - #expr ::= lowestExpr - # | 'if' expr ':' expr ('elif' expr ':' expr)* 'else' ':' expr - # | 'when' expr ':' expr ('elif' expr ':' expr)* 'else' ':' expr - # + #| expr = (ifExpr + #| | whenExpr + #| | caseExpr + #| | tryStmt) + #| / simpleExpr case p.tok.tokType: of tkIf: result = parseIfExpr(p, nkIfExpr) of tkWhen: result = parseIfExpr(p, nkWhenExpr) of tkCase: result = parseCase(p) - else: result = lowestExpr(p) - # XXX needs proper support: - #of tkTry: result = parseTry(p) + of tkTry: result = parseTry(p) + else: result = simpleExpr(p) proc parseObject(p: var TParser): PNode proc parseDistinct(p: var TParser): PNode proc parseEnum(p: var TParser): PNode proc primary(p: var TParser, mode: TPrimaryMode): PNode = - # prefix operator? + #| typeKeyw = 'var' | 'ref' | 'ptr' | 'shared' | 'type' | 'tuple' + #| | 'proc' | 'iterator' | 'distinct' | 'object' | 'enum' + #| primary = typeKeyw typeDescK + #| / prefixOperator* identOrLiteral primarySuffix* + #| / 'addr' primary + #| / 'static' primary + #| / 'bind' primary if isOperator(p.tok): let isSigil = IsSigilLike(p.tok) result = newNodeP(nkPrefix, p) @@ -831,30 +987,48 @@ proc primary(p: var TParser, mode: TPrimaryMode): PNode = getTok(p) of tkAddr: result = newNodeP(nkAddr, p) - getTok(p) + getTokNoInd(p) addSon(result, primary(p, pmNormal)) of tkStatic: result = newNodeP(nkStaticExpr, p) - getTok(p) + getTokNoInd(p) addSon(result, primary(p, pmNormal)) - of tkBind: + of tkBind: result = newNodeP(nkBind, p) getTok(p) optInd(p, result) addSon(result, primary(p, pmNormal)) else: - result = identOrLiteral(p) + result = identOrLiteral(p, mode) if mode != pmSkipSuffix: result = primarySuffix(p, result) -proc parseTypeDesc(p: var TParser): PNode = - result = lowestExpr(p, pmTypeDesc) +proc parseTypeDesc(p: var TParser): PNode = + #| typeDesc = simpleExpr + result = simpleExpr(p, pmTypeDesc) proc parseTypeDefAux(p: var TParser): PNode = - result = lowestExpr(p, pmTypeDef) + #| typeDefAux = simpleExpr + result = simpleExpr(p, pmTypeDef) + +proc makeCall(n: PNode): PNode = + if n.kind in nkCallKinds: + result = n + else: + result = newNodeI(nkCall, n.info) + result.add n proc parseExprStmt(p: var TParser): PNode = - var a = lowestExpr(p) + #| exprStmt = simpleExpr + #| (( '=' optInd expr ) + #| / ( expr ^+ comma + #| doBlocks + #| / ':' stmt? ( IND{=} 'of' exprList ':' stmt + #| | IND{=} 'elif' expr ':' stmt + #| | IND{=} 'except' exprList ':' stmt + #| | IND{=} 'else' ':' stmt )* + #| ))? + var a = simpleExpr(p) if p.tok.tokType == tkEquals: getTok(p) optInd(p, result) @@ -863,33 +1037,31 @@ proc parseExprStmt(p: var TParser): PNode = addSon(result, a) addSon(result, b) else: - var call = if a.kind == nkCall: a - else: newNode(nkCommand, a.info, @[a]) - while true: - if not isExprStart(p): break - var e = parseExpr(p) - addSon(call, e) - if p.tok.tokType != tkComma: break - getTok(p) - optInd(p, a) - if p.tok.tokType == tkDo: - parseDoBlocks(p, call) - return - result = if call.sonsLen <= 1: a - else: call - if p.tok.tokType == tkColon: - result = call + if p.tok.indent < 0 and isExprStart(p): + result = newNode(nkCommand, a.info, @[a]) + while true: + var e = parseExpr(p) + addSon(result, e) + if p.tok.tokType != tkComma: break + getTok(p) + optInd(p, result) + else: + result = a + if p.tok.tokType == tkDo and p.tok.indent < 0: + result = makeCall(result) + parseDoBlocks(p, result) + return result + if p.tok.tokType == tkColon and p.tok.indent < 0: + result = makeCall(result) getTok(p) skipComment(p, result) - if p.tok.tokType == tkSad: getTok(p) if p.tok.TokType notin {tkOf, tkElif, tkElse, tkExcept}: let body = parseStmt(p) addSon(result, newProcNode(nkDo, body.info, body)) - while true: - if p.tok.tokType == tkSad: getTok(p) + while sameInd(p): var b: PNode case p.tok.tokType - of tkOf: + of tkOf: b = newNodeP(nkOfBranch, p) exprList(p, tkColon, b) of tkElif: @@ -900,7 +1072,7 @@ proc parseExprStmt(p: var TParser): PNode = eat(p, tkColon) of tkExcept: b = newNodeP(nkExceptBranch, p) - qualifiedIdentListAux(p, tkColon, b) + exprList(p, tkColon, b) skipComment(p, b) of tkElse: b = newNodeP(nkElse, p) @@ -912,6 +1084,9 @@ proc parseExprStmt(p: var TParser): PNode = if b.kind == nkElse: break proc parseImport(p: var TParser, kind: TNodeKind): PNode = + #| importStmt = 'import' optInd expr + #| ((comma expr)* + #| / 'except' optInd (expr ^+ comma)) result = newNodeP(kind, p) getTok(p) # skip `import` or `export` optInd(p, result) @@ -922,29 +1097,33 @@ proc parseImport(p: var TParser, kind: TNodeKind): PNode = result.kind = succ(kind) getTok(p) optInd(p, result) - while p.tok.tokType notin {tkEof, tkSad, tkDed}: + while true: + # was: while p.tok.tokType notin {tkEof, tkSad, tkDed}: a = parseExpr(p) if a.kind == nkEmpty: break addSon(result, a) if p.tok.tokType != tkComma: break getTok(p) optInd(p, a) - expectNl(p) + #expectNl(p) proc parseIncludeStmt(p: var TParser): PNode = + #| includeStmt = 'include' optInd expr ^+ comma result = newNodeP(nkIncludeStmt, p) getTok(p) # skip `import` or `include` optInd(p, result) - while p.tok.tokType notin {tkEof, tkSad, tkDed}: + while true: + # was: while p.tok.tokType notin {tkEof, tkSad, tkDed}: var a = parseExpr(p) if a.kind == nkEmpty: break addSon(result, a) if p.tok.tokType != tkComma: break getTok(p) optInd(p, a) - expectNl(p) + #expectNl(p) -proc parseFromStmt(p: var TParser): PNode = +proc parseFromStmt(p: var TParser): PNode = + #| fromStmt = 'from' expr 'import' optInd expr (comma expr)* result = newNodeP(nkFromStmt, p) getTok(p) # skip `from` optInd(p, result) @@ -952,38 +1131,41 @@ proc parseFromStmt(p: var TParser): PNode = addSon(result, a) #optInd(p, a); eat(p, tkImport) optInd(p, result) - while p.tok.tokType notin {tkEof, tkSad, tkDed}: + while true: + # p.tok.tokType notin {tkEof, tkSad, tkDed}: a = parseExpr(p) if a.kind == nkEmpty: break addSon(result, a) if p.tok.tokType != tkComma: break getTok(p) optInd(p, a) - expectNl(p) + #expectNl(p) proc parseReturnOrRaise(p: var TParser, kind: TNodeKind): PNode = + #| returnStmt = 'return' optInd expr? + #| raiseStmt = 'raise' optInd expr? + #| yieldStmt = 'yield' optInd expr? + #| discardStmt = 'discard' optInd expr? + #| breakStmt = 'break' optInd expr? + #| continueStmt = 'break' optInd expr? result = newNodeP(kind, p) getTok(p) - optInd(p, result) - case p.tok.tokType - of tkEof, tkSad, tkDed: addSon(result, ast.emptyNode) - else: addSon(result, parseExpr(p)) - -proc parseYieldOrDiscard(p: var TParser, kind: TNodeKind): PNode = - result = newNodeP(kind, p) - getTok(p) - optInd(p, result) - addSon(result, parseExpr(p)) - -proc parseBreakOrContinue(p: var TParser, kind: TNodeKind): PNode = - result = newNodeP(kind, p) - getTok(p) - optInd(p, result) - case p.tok.tokType - of tkEof, tkSad, tkDed: addSon(result, ast.emptyNode) - else: addSon(result, parseSymbol(p)) + if p.tok.tokType == tkComment: + skipComment(p, result) + addSon(result, ast.emptyNode) + elif p.tok.indent >= 0 and p.tok.indent <= p.currInd or + p.tok.tokType == tkEof: + # NL terminates: + addSon(result, ast.emptyNode) + else: + addSon(result, parseExpr(p)) proc parseIfOrWhen(p: var TParser, kind: TNodeKind): PNode = + #| condStmt = expr colcom stmt COMMENT? + #| (IND{=} 'elif' expr colcom stmt)* + #| (IND{=} 'else' colcom stmt)? + #| ifStmt = 'if' condStmt + #| whenStmt = 'when' condStmt result = newNodeP(kind, p) while true: getTok(p) # skip `if`, `when`, `elif` @@ -995,8 +1177,8 @@ proc parseIfOrWhen(p: var TParser, kind: TNodeKind): PNode = addSon(branch, parseStmt(p)) skipComment(p, branch) addSon(result, branch) - if p.tok.tokType != tkElif: break - if p.tok.tokType == tkElse: + if p.tok.tokType != tkElif or not sameOrNoInd(p): break + if p.tok.tokType == tkElse and sameOrNoInd(p): var branch = newNodeP(nkElse, p) eat(p, tkElse) eat(p, tkColon) @@ -1004,17 +1186,24 @@ proc parseIfOrWhen(p: var TParser, kind: TNodeKind): PNode = addSon(branch, parseStmt(p)) addSon(result, branch) -proc parseWhile(p: var TParser): PNode = +proc parseWhile(p: var TParser): PNode = + #| whileStmt = 'while' expr colcom stmt result = newNodeP(nkWhileStmt, p) getTok(p) optInd(p, result) addSon(result, parseExpr(p)) - eat(p, tkColon) - skipComment(p, result) + colcom(p, result) addSon(result, parseStmt(p)) -proc parseCase(p: var TParser): PNode = - var +proc parseCase(p: var TParser): PNode = + #| ofBranch = 'of' exprList colcom stmt + #| ofBranches = ofBranch (IND{=} ofBranch)* + #| (IND{=} 'elif' expr colcom stmt)* + #| (IND{=} 'else' colcom stmt)? + #| caseStmt = 'case' expr ':'? COMMENT? + #| (IND{>} ofBranches DED + #| | IND{=} ofBranches) + var b: PNode inElif= false wasIndented = false @@ -1024,57 +1213,57 @@ proc parseCase(p: var TParser): PNode = if p.tok.tokType == tkColon: getTok(p) skipComment(p, result) - if p.tok.tokType == tkInd: - pushInd(p.lex, p.tok.indent) - getTok(p) + let oldInd = p.currInd + if realInd(p): + p.currInd = p.tok.indent wasIndented = true - while true: - if p.tok.tokType == tkSad: getTok(p) + while sameInd(p): case p.tok.tokType - of tkOf: - if inElif: break + of tkOf: + if inElif: break b = newNodeP(nkOfBranch, p) exprList(p, tkColon, b) - of tkElif: + of tkElif: inElif = true b = newNodeP(nkElifBranch, p) getTok(p) optInd(p, b) addSon(b, parseExpr(p)) eat(p, tkColon) - of tkElse: + of tkElse: b = newNodeP(nkElse, p) getTok(p) eat(p, tkColon) - else: break + else: break skipComment(p, b) addSon(b, parseStmt(p)) addSon(result, b) if b.kind == nkElse: break if wasIndented: - if p.tok.tokType != tkEof: eat(p, tkDed) - popInd(p.lex) + p.currInd = oldInd -proc parseTry(p: var TParser): PNode = +proc parseTry(p: var TParser): PNode = + #| tryStmt = 'try' colcom stmt &(IND{=}? 'except'|'finally') + #| (IND{=}? 'except' exprList colcom stmt)* + #| (IND{=}? 'finally' colcom stmt)? result = newNodeP(nkTryStmt, p) getTok(p) eat(p, tkColon) skipComment(p, result) addSon(result, parseStmt(p)) var b: PNode = nil - while true: - if p.tok.tokType == tkSad: getTok(p) + while sameOrNoInd(p): case p.tok.tokType of tkExcept: b = newNodeP(nkExceptBranch, p) - qualifiedIdentListAux(p, tkColon, b) + exprList(p, tkColon, b) of tkFinally: b = newNodeP(nkFinally, p) - getTok(p) + getTokNoInd(p) eat(p, tkColon) - else: break + else: break skipComment(p, b) addSon(b, parseStmt(p)) addSon(result, b) @@ -1082,52 +1271,48 @@ proc parseTry(p: var TParser): PNode = if b == nil: parMessage(p, errTokenExpected, "except") proc parseExceptBlock(p: var TParser, kind: TNodeKind): PNode = + #| exceptBlock = 'except' colcom stmt result = newNodeP(kind, p) - getTok(p) - eat(p, tkColon) - skipComment(p, result) + getTokNoInd(p) + colcom(p, result) addSon(result, parseStmt(p)) -proc parseFor(p: var TParser): PNode = +proc parseFor(p: var TParser): PNode = + #| forStmt = 'for' symbol (comma symbol)* 'in' expr colcom stmt result = newNodeP(nkForStmt, p) - getTok(p) - optInd(p, result) + getTokNoInd(p) var a = parseSymbol(p) addSon(result, a) - while p.tok.tokType == tkComma: + while p.tok.tokType == tkComma: getTok(p) optInd(p, a) a = parseSymbol(p) addSon(result, a) eat(p, tkIn) addSon(result, parseExpr(p)) - eat(p, tkColon) - skipComment(p, result) + colcom(p, result) addSon(result, parseStmt(p)) proc parseBlock(p: var TParser): PNode = + #| blockStmt = 'block' symbol? colcom stmt result = newNodeP(nkBlockStmt, p) - getTok(p) - optInd(p, result) - case p.tok.tokType - of tkEof, tkSad, tkDed, tkColon: addSon(result, ast.emptyNode) + getTokNoInd(p) + if p.tok.tokType == tkColon: addSon(result, ast.emptyNode) else: addSon(result, parseSymbol(p)) - eat(p, tkColon) - skipComment(p, result) + colcom(p, result) addSon(result, parseStmt(p)) proc parseStatic(p: var TParser): PNode = + #| staticStmt = 'static' colcom stmt result = newNodeP(nkStaticStmt, p) - getTok(p) - optInd(p, result) - eat(p, tkColon) - skipComment(p, result) + getTokNoInd(p) + colcom(p, result) addSon(result, parseStmt(p)) -proc parseAsm(p: var TParser): PNode = +proc parseAsm(p: var TParser): PNode = + #| asmStmt = 'asm' pragma? (STR_LIT | RSTR_LIT | TRIPLE_STR_LIT) result = newNodeP(nkAsmStmt, p) - getTok(p) - optInd(p, result) + getTokNoInd(p) if p.tok.tokType == tkCurlyDotLe: addSon(result, parsePragma(p)) else: addSon(result, ast.emptyNode) case p.tok.tokType @@ -1141,7 +1326,8 @@ proc parseAsm(p: var TParser): PNode = return getTok(p) -proc parseGenericParam(p: var TParser): PNode = +proc parseGenericParam(p: var TParser): PNode = + #| genericParam = symbol (comma symbol)* (colon expr)? ('=' optInd expr)? var a: PNode result = newNodeP(nkIdentDefs, p) while true: @@ -1168,89 +1354,96 @@ proc parseGenericParam(p: var TParser): PNode = addSon(result, ast.emptyNode) proc parseGenericParamList(p: var TParser): PNode = + #| genericParamList = '[' optInd + #| genericParam ^* (comma/semicolon) optPar ']' result = newNodeP(nkGenericParams, p) getTok(p) optInd(p, result) - while (p.tok.tokType == tkSymbol) or (p.tok.tokType == tkAccent): + while p.tok.tokType in {tkSymbol, tkAccent}: var a = parseGenericParam(p) addSon(result, a) if p.tok.tokType notin {tkComma, tkSemicolon}: break getTok(p) - optInd(p, a) + skipComment(p, a) optPar(p) eat(p, tkBracketRi) proc parsePattern(p: var TParser): PNode = + #| pattern = '{' stmt '}' eat(p, tkCurlyLe) result = parseStmt(p) eat(p, tkCurlyRi) +proc validInd(p: var TParser): bool = + result = p.tok.indent < 0 or p.tok.indent > p.currInd + proc parseRoutine(p: var TParser, kind: TNodeKind): PNode = + #| indAndComment = (IND{>} COMMENT)? | COMMENT? + #| routine = optInd identVis pattern? genericParamList? + #| paramListColon pragma? ('=' COMMENT? stmt)? indAndComment result = newNodeP(kind, p) getTok(p) optInd(p, result) addSon(result, identVis(p)) - if p.tok.tokType == tkCurlyLe: addSon(result, parsePattern(p)) + if p.tok.tokType == tkCurlyLe and p.validInd: addSon(result, p.parsePattern) else: addSon(result, ast.emptyNode) - if p.tok.tokType == tkBracketLe: addSon(result, parseGenericParamList(p)) - else: addSon(result, ast.emptyNode) - addSon(result, parseParamList(p)) - if p.tok.tokType == tkCurlyDotLe: addSon(result, parsePragma(p)) + if p.tok.tokType == tkBracketLe and p.validInd: + result.add(p.parseGenericParamList) + else: + addSon(result, ast.emptyNode) + addSon(result, p.parseParamList) + if p.tok.tokType == tkCurlyDotLe and p.validInd: addSon(result, p.parsePragma) else: addSon(result, ast.emptyNode) # empty exception tracking: addSon(result, ast.emptyNode) - if p.tok.tokType == tkEquals: + if p.tok.tokType == tkEquals and p.validInd: getTok(p) skipComment(p, result) addSon(result, parseStmt(p)) - else: + else: addSon(result, ast.emptyNode) - indAndComment(p, result) # XXX: document this in the grammar! + indAndComment(p, result) proc newCommentStmt(p: var TParser): PNode = + #| commentStmt = COMMENT result = newNodeP(nkCommentStmt, p) result.info.line = result.info.line - int16(1) - int16(p.tok.iNumber) + result.comment = p.tok.literal + getTok(p) -type +type TDefParser = proc (p: var TParser): PNode {.nimcall.} -proc parseSection(p: var TParser, kind: TNodeKind, - defparser: TDefParser): PNode = +proc parseSection(p: var TParser, kind: TNodeKind, + defparser: TDefParser): PNode = + #| section(p) = COMMENT? p / (IND{>} (p / COMMENT)^+IND{=} DED) result = newNodeP(kind, p) getTok(p) skipComment(p, result) - case p.tok.tokType - of tkInd: - pushInd(p.lex, p.tok.indent) - getTok(p) - skipComment(p, result) - while true: - case p.tok.tokType - of tkSad: - getTok(p) - of tkSymbol, tkAccent: - var a = defparser(p) - skipComment(p, a) - addSon(result, a) - of tkDed: - getTok(p) - break - of tkEof: - break # BUGFIX - of tkComment: - var a = newCommentStmt(p) - skipComment(p, a) - addSon(result, a) - else: - parMessage(p, errIdentifierExpected, p.tok) - break - popInd(p.lex) - of tkSymbol, tkAccent, tkParLe: + if realInd(p): + withInd(p): + skipComment(p, result) + while sameInd(p): + case p.tok.tokType + of tkSymbol, tkAccent: + var a = defparser(p) + skipComment(p, a) + addSon(result, a) + of tkComment: + var a = newCommentStmt(p) + addSon(result, a) + else: + parMessage(p, errIdentifierExpected, p.tok) + break + if result.len == 0: parMessage(p, errIdentifierExpected, p.tok) + elif p.tok.tokType in {tkSymbol, tkAccent, tkParLe} and p.tok.indent < 0: # tkParLe is allowed for ``var (x, y) = ...`` tuple parsing addSon(result, defparser(p)) - else: parMessage(p, errIdentifierExpected, p.tok) + else: + parMessage(p, errIdentifierExpected, p.tok) -proc parseConstant(p: var TParser): PNode = +proc parseConstant(p: var TParser): PNode = + #| constant = identWithPragma (colon typedesc)? '=' optInd expr indAndComment result = newNodeP(nkConstDef, p) addSon(result, identWithPragma(p)) if p.tok.tokType == tkColon: @@ -1262,60 +1455,73 @@ proc parseConstant(p: var TParser): PNode = eat(p, tkEquals) optInd(p, result) addSon(result, parseExpr(p)) - indAndComment(p, result) # XXX: special extension! + indAndComment(p, result) proc parseEnum(p: var TParser): PNode = - var a, b: PNode + #| enum = 'enum' optInd (symbol optInd ('=' optInd expr COMMENT?)? comma?)+ result = newNodeP(nkEnumTy, p) - a = nil getTok(p) addSon(result, ast.emptyNode) optInd(p, result) - while true: - case p.tok.tokType - of tkEof, tkSad, tkDed: break - else: a = parseSymbol(p) - optInd(p, a) - if p.tok.tokType == tkEquals: + while true: + var a = parseSymbol(p) + if p.tok.indent >= 0 and p.tok.indent <= p.currInd: + add(result, a) + break + if p.tok.tokType == tkEquals and p.tok.indent < 0: getTok(p) optInd(p, a) - b = a + var b = a a = newNodeP(nkEnumFieldDef, p) addSon(a, b) addSon(a, parseExpr(p)) skipComment(p, a) - if p.tok.tokType == tkComma: + if p.tok.tokType == tkComma and p.tok.indent < 0: getTok(p) - optInd(p, a) + rawSkipComment(p, a) + else: + skipComment(p, a) addSon(result, a) + if p.tok.indent >= 0 and p.tok.indent <= p.currInd or + p.tok.tokType == tkEof: + break if result.len <= 1: lexMessage(p.lex, errIdentifierExpected, prettyTok(p.tok)) proc parseObjectPart(p: var TParser): PNode proc parseObjectWhen(p: var TParser): PNode = + #| objectWhen = 'when' expr colcom objectPart COMMENT? + #| ('elif' expr colcom objectPart COMMENT?)* + #| ('else' colcom objectPart COMMENT?)? result = newNodeP(nkRecWhen, p) - while true: + while sameInd(p): getTok(p) # skip `when`, `elif` var branch = newNodeP(nkElifBranch, p) optInd(p, branch) addSon(branch, parseExpr(p)) - eat(p, tkColon) - skipComment(p, branch) + colcom(p, branch) addSon(branch, parseObjectPart(p)) skipComment(p, branch) addSon(result, branch) - if p.tok.tokType != tkElif: break - if p.tok.tokType == tkElse: + if p.tok.tokType != tkElif: break + if p.tok.tokType == tkElse and sameInd(p): var branch = newNodeP(nkElse, p) eat(p, tkElse) - eat(p, tkColon) - skipComment(p, branch) + colcom(p, branch) addSon(branch, parseObjectPart(p)) + skipComment(p, branch) addSon(result, branch) proc parseObjectCase(p: var TParser): PNode = + #| objectBranch = 'of' exprList colcom objectPart + #| objectBranches = objectBranch (IND{=} objectBranch)* + #| (IND{=} 'elif' expr colcom objectPart)* + #| (IND{=} 'else' colcom objectPart)? + #| objectCase = 'case' identWithPragma ':' typeDesc ':'? COMMENT? + #| (IND{>} objectBranches DED + #| | IND{=} objectBranches) result = newNodeP(nkRecCase, p) - getTok(p) + getTokNoInd(p) var a = newNodeP(nkIdentDefs, p) addSon(a, identWithPragma(p)) eat(p, tkColon) @@ -1325,12 +1531,11 @@ proc parseObjectCase(p: var TParser): PNode = if p.tok.tokType == tkColon: getTok(p) skipComment(p, result) var wasIndented = false - if p.tok.tokType == tkInd: - pushInd(p.lex, p.tok.indent) - getTok(p) + let oldInd = p.currInd + if realInd(p): + p.currInd = p.tok.indent wasIndented = true - while true: - if p.tok.tokType == tkSad: getTok(p) + while sameInd(p): var b: PNode case p.tok.tokType of tkOf: @@ -1348,72 +1553,79 @@ proc parseObjectCase(p: var TParser): PNode = fields = newNodeP(nkNilLit, p) # don't break further semantic checking addSon(b, fields) addSon(result, b) - if b.kind == nkElse: break + if b.kind == nkElse: break if wasIndented: - eat(p, tkDed) - popInd(p.lex) + p.currInd = oldInd proc parseObjectPart(p: var TParser): PNode = - case p.tok.tokType - of tkInd: + #| objectPart = IND{>} objectPart^+IND{=} DED + #| / objectWhen / objectCase / 'nil' / declColonEquals + if realInd(p): result = newNodeP(nkRecList, p) - pushInd(p.lex, p.tok.indent) - getTok(p) - skipComment(p, result) - while true: - case p.tok.tokType - of tkSad: - getTok(p) - of tkCase, tkWhen, tkSymbol, tkAccent, tkNil: - addSon(result, parseObjectPart(p)) - of tkDed: - getTok(p) - break - of tkEof: - break - else: - parMessage(p, errIdentifierExpected, p.tok) - break - popInd(p.lex) - of tkWhen: - result = parseObjectWhen(p) - of tkCase: - result = parseObjectCase(p) - of tkSymbol, tkAccent: - result = parseIdentColonEquals(p, {withPragma}) - skipComment(p, result) - of tkNil: - result = newNodeP(nkNilLit, p) - getTok(p) - else: result = ast.emptyNode + withInd(p): + rawSkipComment(p, result) + while sameInd(p): + case p.tok.tokType + of tkCase, tkWhen, tkSymbol, tkAccent, tkNil: + addSon(result, parseObjectPart(p)) + else: + parMessage(p, errIdentifierExpected, p.tok) + break + else: + case p.tok.tokType + of tkWhen: + result = parseObjectWhen(p) + of tkCase: + result = parseObjectCase(p) + of tkSymbol, tkAccent: + result = parseIdentColonEquals(p, {withPragma}) + skipComment(p, result) + of tkNil: + result = newNodeP(nkNilLit, p) + getTok(p) + else: + result = ast.emptyNode proc parseObject(p: var TParser): PNode = + #| object = 'object' pragma? ('of' typeDesc)? COMMENT? objectPart result = newNodeP(nkObjectTy, p) getTok(p) - if p.tok.tokType == tkCurlyDotLe: addSon(result, parsePragma(p)) - else: addSon(result, ast.emptyNode) - if p.tok.tokType == tkOf: + if p.tok.tokType == tkCurlyDotLe and p.validInd: + addSon(result, parsePragma(p)) + else: + addSon(result, ast.emptyNode) + if p.tok.tokType == tkOf and p.tok.indent < 0: var a = newNodeP(nkOfInherit, p) getTok(p) addSon(a, parseTypeDesc(p)) addSon(result, a) else: addSon(result, ast.emptyNode) - skipComment(p, result) + if p.tok.tokType == tkComment: + skipComment(p, result) + # an initial IND{>} HAS to follow: + if not realInd(p): + addSon(result, emptyNode) + return addSon(result, parseObjectPart(p)) proc parseDistinct(p: var TParser): PNode = + #| distinct = 'distinct' optInd typeDesc result = newNodeP(nkDistinctTy, p) getTok(p) optInd(p, result) addSon(result, parseTypeDesc(p)) proc parseTypeDef(p: var TParser): PNode = + #| typeDef = identWithPragma genericParamList? '=' optInd typeDefAux + #| indAndComment? result = newNodeP(nkTypeDef, p) addSon(result, identWithPragma(p)) - if p.tok.tokType == tkBracketLe: addSon(result, parseGenericParamList(p)) - else: addSon(result, ast.emptyNode) - if p.tok.tokType == tkEquals: + if p.tok.tokType == tkBracketLe and p.validInd: + addSon(result, parseGenericParamList(p)) + else: + addSon(result, ast.emptyNode) + if p.tok.tokType == tkEquals: getTok(p) optInd(p, result) addSon(result, parseTypeDefAux(p)) @@ -1421,16 +1633,17 @@ proc parseTypeDef(p: var TParser): PNode = addSon(result, ast.emptyNode) indAndComment(p, result) # special extension! -proc parseVarTuple(p: var TParser): PNode = +proc parseVarTuple(p: var TParser): PNode = + #| varTuple = '(' optInd identWithPragma ^+ comma optPar ')' '=' optInd expr result = newNodeP(nkVarTuple, p) getTok(p) # skip '(' optInd(p, result) - while (p.tok.tokType == tkSymbol) or (p.tok.tokType == tkAccent): + while p.tok.tokType in {tkSymbol, tkAccent}: var a = identWithPragma(p) addSon(result, a) if p.tok.tokType != tkComma: break getTok(p) - optInd(p, a) + skipComment(p, a) addSon(result, ast.emptyNode) # no type desc optPar(p) eat(p, tkParRi) @@ -1438,12 +1651,15 @@ proc parseVarTuple(p: var TParser): PNode = optInd(p, result) addSon(result, parseExpr(p)) -proc parseVariable(p: var TParser): PNode = +proc parseVariable(p: var TParser): PNode = + #| variable = (varTuple / identColonEquals) indAndComment if p.tok.tokType == tkParLe: result = parseVarTuple(p) else: result = parseIdentColonEquals(p, {withPragma}) - indAndComment(p, result) # special extension! + indAndComment(p, result) proc parseBind(p: var TParser, k: TNodeKind): PNode = + #| bindStmt = 'bind' optInd qualifiedIdent ^+ comma + #| mixinStmt = 'mixin' optInd qualifiedIdent ^+ comma result = newNodeP(k, p) getTok(p) optInd(p, result) @@ -1452,26 +1668,32 @@ proc parseBind(p: var TParser, k: TNodeKind): PNode = addSon(result, a) if p.tok.tokType != tkComma: break getTok(p) - optInd(p, a) - expectNl(p) + optInd(p, a) + #expectNl(p) proc parseStmtPragma(p: var TParser): PNode = + #| pragmaStmt = pragma (':' COMMENT? stmt)? result = parsePragma(p) - if p.tok.tokType == tkColon: + if p.tok.tokType == tkColon and p.tok.indent < 0: let a = result result = newNodeI(nkPragmaBlock, a.info) getTok(p) + skipComment(p, result) result.add a result.add parseStmt(p) proc simpleStmt(p: var TParser): PNode = + #| simpleStmt = ((returnStmt | raiseStmt | yieldStmt | discardStmt | breakStmt + #| | continueStmt | pragmaStmt | importStmt | exportStmt | fromStmt + #| | includeStmt | commentStmt) / exprStmt) COMMENT? + #| case p.tok.tokType of tkReturn: result = parseReturnOrRaise(p, nkReturnStmt) of tkRaise: result = parseReturnOrRaise(p, nkRaiseStmt) of tkYield: result = parseReturnOrRaise(p, nkYieldStmt) of tkDiscard: result = parseReturnOrRaise(p, nkDiscardStmt) - of tkBreak: result = parseBreakOrContinue(p, nkBreakStmt) - of tkContinue: result = parseBreakOrContinue(p, nkContinueStmt) + of tkBreak: result = parseReturnOrRaise(p, nkBreakStmt) + of tkContinue: result = parseReturnOrRaise(p, nkContinueStmt) of tkCurlyDotLe: result = parseStmtPragma(p) of tkImport: result = parseImport(p, nkImportStmt) of tkExport: result = parseImport(p, nkExportStmt) @@ -1481,9 +1703,23 @@ proc simpleStmt(p: var TParser): PNode = else: if isExprStart(p): result = parseExprStmt(p) else: result = ast.emptyNode - if result.kind != nkEmpty: skipComment(p, result) + if result.kind notin {nkEmpty, nkCommentStmt}: skipComment(p, result) -proc complexOrSimpleStmt(p: var TParser): PNode = +proc complexOrSimpleStmt(p: var TParser): PNode = + #| complexOrSimpleStmt = (ifStmt | whenStmt | whileStmt + #| | tryStmt | finallyStmt | exceptStmt | forStmt + #| | blockStmt | staticStmt | asmStmt + #| | 'proc' routine + #| | 'method' routine + #| | 'iterator' routine + #| | 'macro' routine + #| | 'template' routine + #| | 'converter' routine + #| | 'type' section(typeDef) + #| | 'const' section(constant) + #| | ('let' | 'var') section(variable) + #| | bindStmt | mixinStmt) + #| / simpleStmt case p.tok.tokType of tkIf: result = parseIfOrWhen(p, nkIfStmt) of tkWhile: result = parseWhile(p) @@ -1510,26 +1746,31 @@ proc complexOrSimpleStmt(p: var TParser): PNode = of tkMixin: result = parseBind(p, nkMixinStmt) else: result = simpleStmt(p) -proc parseStmt(p: var TParser): PNode = - if p.tok.tokType == tkInd: +proc parseStmt(p: var TParser): PNode = + #| stmt = (IND{>} complexOrSimpleStmt^+(IND{=} / ';') DED) + #| / simpleStmt + if p.tok.indent > p.currInd: result = newNodeP(nkStmtList, p) - pushInd(p.lex, p.tok.indent) - getTok(p) - while true: - case p.tok.tokType - of tkSad, tkSemicolon: getTok(p) - of tkEof: break - of tkDed: - getTok(p) - break - else: + withInd(p): + while true: + if p.tok.indent == p.currInd: + nil + elif p.tok.tokType == tkSemicolon: + while p.tok.tokType == tkSemicolon: getTok(p) + else: + if p.tok.indent > p.currInd: + parMessage(p, errInvalidIndentation) + break + if p.tok.toktype in {tkCurlyRi, tkParRi, tkCurlyDotRi, tkBracketRi}: + # XXX this ensures tnamedparamanonproc still compiles; + # deprecate this syntax later + break var a = complexOrSimpleStmt(p) - if a.kind == nkEmpty: - # XXX this needs a proper analysis; - if isKeyword(p.tok.tokType): parMessage(p, errInvalidIndentation) - break - addSon(result, a) - popInd(p.lex) + if a.kind != nkEmpty: + addSon(result, a) + else: + parMessage(p, errExprExpected, p.tok) + getTok(p) else: # the case statement is only needed for better error messages: case p.tok.tokType @@ -1538,38 +1779,35 @@ proc parseStmt(p: var TParser): PNode = parMessage(p, errComplexStmtRequiresInd) result = ast.emptyNode else: + if p.tok.indent >= 0: parMessage(p, errInvalidIndentation) result = simpleStmt(p) if result.kind == nkEmpty: parMessage(p, errExprExpected, p.tok) - if p.tok.tokType == tkSemicolon: getTok(p) - if p.tok.tokType == tkSad: getTok(p) + #while p.tok.tokType == tkSemicolon: getTok(p) proc parseAll(p: var TParser): PNode = result = newNodeP(nkStmtList, p) - while true: - case p.tok.tokType - of tkSad: getTok(p) - of tkDed, tkInd: - parMessage(p, errInvalidIndentation) + while p.tok.tokType != tkEof: + var a = complexOrSimpleStmt(p) + if a.kind != nkEmpty: + addSon(result, a) + else: + parMessage(p, errExprExpected, p.tok) + # bugfix: consume a token here to prevent an endless loop: getTok(p) - of tkEof: break - else: - var a = complexOrSimpleStmt(p) - if a.kind == nkEmpty: - parMessage(p, errExprExpected, p.tok) - # bugfix: consume a token here to prevent an endless loop: - getTok(p) - addSon(result, a) + if p.tok.indent != 0: + parMessage(p, errInvalidIndentation) -proc parseTopLevelStmt(p: var TParser): PNode = +proc parseTopLevelStmt(p: var TParser): PNode = result = ast.emptyNode - while true: + while true: + if p.tok.indent != 0: + if p.firstTok and p.tok.indent < 0: nil + else: parMessage(p, errInvalidIndentation) + p.firstTok = false case p.tok.tokType - of tkSad, tkSemicolon: getTok(p) - of tkDed, tkInd: - parMessage(p, errInvalidIndentation) - getTok(p) - of tkEof: break - else: + of tkSemicolon: getTok(p) + of tkEof: break + else: result = complexOrSimpleStmt(p) if result.kind == nkEmpty: parMessage(p, errExprExpected, p.tok) break @@ -1583,4 +1821,3 @@ proc parseString(s: string, filename: string = "", line: int = 0): PNode = result = parser.parseAll CloseParser(parser) - diff --git a/compiler/pas2nim/paslex.nim b/compiler/pas2nim/paslex.nim index 598a27158..94e664832 100644 --- a/compiler/pas2nim/paslex.nim +++ b/compiler/pas2nim/paslex.nim @@ -11,7 +11,7 @@ # the scanner module. import - hashes, options, msgs, strutils, platform, idents, lexbase, llstream + hashes, options, msgs, strutils, platform, idents, nimlexbase, llstream const MaxLineLength* = 80 # lines longer than this lead to a warning @@ -273,8 +273,8 @@ proc getNumber10(L: var TLexer, tok: var TToken) = proc HandleCRLF(L: var TLexer, pos: int): int = case L.buf[pos] - of CR: result = lexbase.HandleCR(L, pos) - of LF: result = lexbase.HandleLF(L, pos) + of CR: result = nimlexbase.HandleCR(L, pos) + of LF: result = nimlexbase.HandleLF(L, pos) else: result = pos proc getString(L: var TLexer, tok: var TToken) = @@ -286,7 +286,7 @@ proc getString(L: var TLexer, tok: var TToken) = inc(pos) while true: case buf[pos] - of CR, LF, lexbase.EndOfFile: + of CR, LF, nimlexbase.EndOfFile: lexMessage(L, errClosingQuoteExpected) break of '\'': @@ -362,7 +362,7 @@ proc scanLineComment(L: var TLexer, tok: var TToken) = while true: inc(pos, 2) # skip // add(tok.literal, '#') - while not (buf[pos] in {CR, LF, lexbase.EndOfFile}): + while not (buf[pos] in {CR, LF, nimlexbase.EndOfFile}): add(tok.literal, buf[pos]) inc(pos) pos = handleCRLF(L, pos) @@ -391,7 +391,7 @@ proc scanCurlyComment(L: var TLexer, tok: var TToken) = of '}': inc(pos) break - of lexbase.EndOfFile: lexMessage(L, errTokenExpected, "}") + of nimlexbase.EndOfFile: lexMessage(L, errTokenExpected, "}") else: add(tok.literal, buf[pos]) inc(pos) @@ -415,7 +415,7 @@ proc scanStarComment(L: var TLexer, tok: var TToken) = break else: add(tok.literal, '*') - of lexbase.EndOfFile: + of nimlexbase.EndOfFile: lexMessage(L, errTokenExpected, "*)") else: add(tok.literal, buf[pos]) @@ -561,7 +561,7 @@ proc getTok(L: var TLexer, tok: var TToken) = getNumber16(L, tok) of '%': getNumber2(L, tok) - of lexbase.EndOfFile: + of nimlexbase.EndOfFile: tok.xkind = pxEof else: tok.literal = c & "" diff --git a/compiler/renderer.nim b/compiler/renderer.nim index b4ef52100..d68d2e549 100644 --- a/compiler/renderer.nim +++ b/compiler/renderer.nim @@ -81,13 +81,13 @@ proc addTok(g: var TSrcGen, kind: TTokType, s: string) = proc addPendingNL(g: var TSrcGen) = if g.pendingNL >= 0: - addTok(g, tkInd, "\n" & repeatChar(g.pendingNL)) + addTok(g, tkSpaces, "\n" & repeatChar(g.pendingNL)) g.lineLen = g.pendingNL g.pendingNL = - 1 proc putNL(g: var TSrcGen, indent: int) = if g.pendingNL >= 0: addPendingNL(g) - else: addTok(g, tkInd, "\n") + else: addTok(g, tkSpaces, "\n") g.pendingNL = indent g.lineLen = indent @@ -939,10 +939,9 @@ proc gsub(g: var TSrcGen, n: PNode, c: TContext) = gsub(g, n.sons[0]) put(g, tkDotDot, "..") gsub(g, n.sons[1]) - of nkDerefExpr: + of nkDerefExpr: gsub(g, n.sons[0]) - putWithSpace(g, tkOpr, "^") - # unfortunately this requires a space, because ^. would be only one opr + put(g, tkOpr, "[]") of nkAccQuoted: put(g, tkAccent, "`") if n.len > 0: gsub(g, n.sons[0]) diff --git a/compiler/rodread.nim b/compiler/rodread.nim index d0a63cd99..562eaebab 100644 --- a/compiler/rodread.nim +++ b/compiler/rodread.nim @@ -328,9 +328,6 @@ proc decodeType(r: PRodReader, info: TLineInfo): PType = result.align = decodeVInt(r.s, r.pos) else: result.align = 2 - if r.s[r.pos] == '@': - inc(r.pos) - result.containerID = decodeVInt(r.s, r.pos) decodeLoc(r, result.loc, info) while r.s[r.pos] == '^': inc(r.pos) @@ -1012,9 +1009,6 @@ proc writeType(f: TFile; t: PType) = if t.align != 2: f.write('=') f.write($t.align) - if t.containerID != 0: - f.write('@') - f.write($t.containerID) for i in countup(0, sonsLen(t) - 1): if t.sons[i] == nil: f.write("^()") diff --git a/compiler/rodwrite.nim b/compiler/rodwrite.nim index b64347ba9..0221977bf 100644 --- a/compiler/rodwrite.nim +++ b/compiler/rodwrite.nim @@ -233,9 +233,6 @@ proc encodeType(w: PRodWriter, t: PType, result: var string) = if t.align != 2: add(result, '=') encodeVInt(t.align, result) - if t.containerID != 0: - add(result, '@') - encodeVInt(t.containerID, result) encodeLoc(w, t.loc, result) for i in countup(0, sonsLen(t) - 1): if t.sons[i] == nil: diff --git a/compiler/sem.nim b/compiler/sem.nim index 805af9e31..92b25b1ba 100644 --- a/compiler/sem.nim +++ b/compiler/sem.nim @@ -58,6 +58,46 @@ 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 x + if y == nil: return y + var a = skipTypes(x, {tyGenericInst}) + var b = skipTypes(y, {tyGenericInst}) + result = x + if a.kind in {tyExpr, tyNil}: result = y + elif b.kind in {tyExpr, tyNil}: result = x + elif a.kind == tyStmt: result = a + elif b.kind == tyStmt: result = b + elif a.kind == tyTypeDesc: + # turn any concrete typedesc into the abstract typedesc type + if a.sons == nil: result = a + else: result = newType(tyTypeDesc, a.owner) + elif b.kind in {tyArray, tyArrayConstr, tySet, tySequence} and + a.kind == b.kind: + # check for seq[empty] vs. seq[int] + let idx = ord(b.kind in {tyArray, tyArrayConstr}) + if a.sons[idx].kind == tyEmpty: return y + #elif b.sons[idx].kind == tyEmpty: return x + else: + var k = tyNone + if a.kind in {tyRef, tyPtr}: + k = a.kind + if b.kind != a.kind: return x + a = a.sons[0] + b = b.sons[0] + if a.kind == tyObject and b.kind == tyObject: + result = commonSuperclass(a, b) + # this will trigger an error later: + if result.isNil: return x + if k != tyNone: + let r = result + result = NewType(k, r.owner) + result.addSonSkipIntLit(r) + proc isTopLevel(c: PContext): bool {.inline.} = result = c.tab.tos <= 2 @@ -66,7 +106,7 @@ proc newSymS(kind: TSymKind, n: PNode, c: PContext): PSym = proc newSymG*(kind: TSymKind, n: PNode, c: PContext): PSym = # like newSymS, but considers gensym'ed symbols - if n.kind == nkSym: + if n.kind == nkSym: result = n.sym InternalAssert sfGenSym in result.flags InternalAssert result.kind == kind diff --git a/compiler/semdata.nim b/compiler/semdata.nim index 4981b64c9..afce365f9 100644 --- a/compiler/semdata.nim +++ b/compiler/semdata.nim @@ -230,7 +230,7 @@ proc markIndirect*(c: PContext, s: PSym) {.inline.} = incl(s.flags, sfAddrTaken) # XXX add to 'c' for global analysis -proc illFormedAst*(n: PNode) = +proc illFormedAst*(n: PNode) = GlobalError(n.info, errIllFormedAstX, renderTree(n, {renderNoComments})) proc checkSonsLen*(n: PNode, length: int) = diff --git a/compiler/semdestruct.nim b/compiler/semdestruct.nim new file mode 100644 index 000000000..2383ac649 --- /dev/null +++ b/compiler/semdestruct.nim @@ -0,0 +1,213 @@ +# +# +# The Nimrod Compiler +# (c) Copyright 2013 Andreas Rumpf +# +# See the file "copying.txt", included in this +# distribution, for details about the copyright. +# + +## This module implements destructors. + + +# special marker values that indicates that we are +# 1) AnalyzingDestructor: currently analyzing the type for destructor +# generation (needed for recursive types) +# 2) DestructorIsTrivial: completed the analysis before and determined +# that the type has a trivial destructor +var AnalyzingDestructor, DestructorIsTrivial: PSym +new(AnalyzingDestructor) +new(DestructorIsTrivial) + +var + destructorName = getIdent"destroy_" + destructorParam = getIdent"this_" + destructorPragma = newIdentNode(getIdent"destructor", UnknownLineInfo()) + rangeDestructorProc*: PSym + +proc instantiateDestructor(c: PContext, typ: PType): bool + +proc doDestructorStuff(c: PContext, s: PSym, n: PNode) = + let t = s.typ.sons[1].skipTypes({tyVar}) + t.destructor = s + # automatically insert calls to base classes' destructors + if n.sons[bodyPos].kind != nkEmpty: + for i in countup(0, t.sonsLen - 1): + # when inheriting directly from object + # there will be a single nil son + if t.sons[i] == nil: continue + if instantiateDestructor(c, t.sons[i]): + n.sons[bodyPos].addSon(newNode(nkCall, t.sym.info, @[ + useSym(t.sons[i].destructor), + n.sons[paramsPos][1][0]])) + +proc destroyField(c: PContext, field: PSym, holder: PNode): PNode = + if instantiateDestructor(c, field.typ): + result = newNode(nkCall, field.info, @[ + useSym(field.typ.destructor), + newNode(nkDotExpr, field.info, @[holder, useSym(field)])]) + +proc destroyCase(c: PContext, n: PNode, holder: PNode): PNode = + var nonTrivialFields = 0 + result = newNode(nkCaseStmt, n.info, @[]) + # case x.kind + result.addSon(newNode(nkDotExpr, n.info, @[holder, n.sons[0]])) + for i in countup(1, n.len - 1): + # of A, B: + var caseBranch = newNode(n[i].kind, n[i].info, n[i].sons[0 .. -2]) + let recList = n[i].lastSon + var destroyRecList = newNode(nkStmtList, n[i].info, @[]) + template addField(f: expr): stmt = + let stmt = destroyField(c, f, holder) + if stmt != nil: + destroyRecList.addSon(stmt) + inc nonTrivialFields + + case recList.kind + of nkSym: + addField(recList.sym) + of nkRecList: + for j in countup(0, recList.len - 1): + addField(recList[j].sym) + else: + internalAssert false + + caseBranch.addSon(destroyRecList) + result.addSon(caseBranch) + # maybe no fields were destroyed? + if nonTrivialFields == 0: + result = nil + +proc generateDestructor(c: PContext, t: PType): PNode = + ## generate a destructor for a user-defined object or tuple type + ## returns nil if the destructor turns out to be trivial + + template addLine(e: expr): stmt = + if result == nil: result = newNode(nkStmtList) + result.addSon(e) + + # XXX: This may be true for some C-imported types such as + # Tposix_spawnattr + if t.n == nil or t.n.sons == nil: return + internalAssert t.n.kind == nkRecList + let destructedObj = newIdentNode(destructorParam, UnknownLineInfo()) + # call the destructods of all fields + for s in countup(0, t.n.sons.len - 1): + case t.n.sons[s].kind + of nkRecCase: + let stmt = destroyCase(c, t.n.sons[s], destructedObj) + if stmt != nil: addLine(stmt) + of nkSym: + let stmt = destroyField(c, t.n.sons[s].sym, destructedObj) + if stmt != nil: addLine(stmt) + else: + internalAssert false + # base classes' destructors will be automatically called by + # semProcAux for both auto-generated and user-defined destructors + +proc instantiateDestructor(c: PContext, typ: PType): bool = + # returns true if the type already had a user-defined + # destructor or if the compiler generated a default + # member-wise one + var t = skipTypes(typ, {tyConst, tyMutable}) + + if t.destructor != nil: + # XXX: This is not entirely correct for recursive types, but we need + # it temporarily to hide the "destroy is already defined" problem + return t.destructor notin [AnalyzingDestructor, DestructorIsTrivial] + + case t.kind + of tySequence, tyArray, tyArrayConstr, tyOpenArray, tyVarargs: + if instantiateDestructor(c, t.sons[0]): + if rangeDestructorProc == nil: + rangeDestructorProc = SymtabGet(c.tab, getIdent"nimDestroyRange") + t.destructor = rangeDestructorProc + return true + else: + return false + of tyTuple, tyObject: + t.destructor = AnalyzingDestructor + let generated = generateDestructor(c, t) + if generated != nil: + internalAssert t.sym != nil + var i = t.sym.info + let fullDef = newNode(nkProcDef, i, @[ + newIdentNode(destructorName, i), + emptyNode, + emptyNode, + newNode(nkFormalParams, i, @[ + emptyNode, + newNode(nkIdentDefs, i, @[ + newIdentNode(destructorParam, i), + useSym(t.sym), + emptyNode]), + ]), + newNode(nkPragma, i, @[destructorPragma]), + emptyNode, + generated + ]) + discard semProc(c, fullDef) + internalAssert t.destructor != nil + return true + else: + t.destructor = DestructorIsTrivial + return false + else: + return false + +proc insertDestructors(c: PContext, + varSection: PNode): tuple[outer, inner: PNode] = + # Accepts a var or let section. + # + # When a var section has variables with destructors + # the var section is split up and finally blocks are inserted + # immediately after all "destructable" vars + # + # In case there were no destrucable variables, the proc returns + # (nil, nil) and the enclosing stmt-list requires no modifications. + # + # Otherwise, after the try blocks are created, the rest of the enclosing + # stmt-list should be inserted in the most `inner` such block (corresponding + # to the last variable). + # + # `outer` is a statement list that should replace the original var section. + # It will include the new truncated var section followed by the outermost + # try block. + let totalVars = varSection.sonsLen + for j in countup(0, totalVars - 1): + let + varId = varSection[j][0] + varTyp = varId.sym.typ + info = varId.info + + if varTyp != nil and instantiateDestructor(c, varTyp) and + sfGlobal notin varId.sym.flags: + var tryStmt = newNodeI(nkTryStmt, info) + + if j < totalVars - 1: + var remainingVars = newNodeI(varSection.kind, info) + remainingVars.sons = varSection.sons[(j+1)..(-1)] + let (outer, inner) = insertDestructors(c, remainingVars) + if outer != nil: + tryStmt.addSon(outer) + result.inner = inner + else: + result.inner = newNodeI(nkStmtList, info) + result.inner.addSon(remainingVars) + tryStmt.addSon(result.inner) + else: + result.inner = newNodeI(nkStmtList, info) + tryStmt.addSon(result.inner) + + tryStmt.addSon( + newNode(nkFinally, info, @[ + semStmt(c, newNode(nkCall, info, @[ + useSym(varTyp.destructor), + useSym(varId.sym)]))])) + + result.outer = newNodeI(nkStmtList, info) + varSection.sons.setLen(j+1) + result.outer.addSon(varSection) + result.outer.addSon(tryStmt) + + return diff --git a/compiler/semexprs.nim b/compiler/semexprs.nim index 8b1e5aea1..93dfc2f37 100644 --- a/compiler/semexprs.nim +++ b/compiler/semexprs.nim @@ -19,24 +19,6 @@ proc semTemplateExpr(c: PContext, n: PNode, s: PSym, semCheck = true): PNode = proc semFieldAccess(c: PContext, n: PNode, flags: TExprFlags = {}): PNode -proc performProcvarCheck(c: PContext, n: PNode, s: PSym) = - var smoduleId = getModule(s).id - if sfProcVar notin s.flags and s.typ.callConv == ccDefault and - smoduleId != c.module.id and smoduleId != c.friendModule.id: - LocalError(n.info, errXCannotBePassedToProcVar, s.name.s) - -proc semProcvarCheck(c: PContext, n: PNode) = - let n = n.skipConv - if n.kind == nkSym and n.sym.kind in {skProc, skMethod, skIterator, - skConverter}: - performProcvarCheck(c, n, n.sym) - -proc semDestructorCheck(c: PContext, n: PNode, flags: TExprFlags) {.inline.} = - if efAllowDestructor notin flags and n.kind in nkCallKinds+{nkObjConstr}: - if instantiateDestructor(c, n.typ): - LocalError(n.info, errGenerated, - "usage of a type with a destructor in a non destructible context") - proc semOperand(c: PContext, n: PNode, flags: TExprFlags = {}): PNode = # same as 'semExprWithType' but doesn't check for proc vars result = semExpr(c, n, flags) @@ -420,10 +402,10 @@ proc semArrayConstr(c: PContext, n: PNode, flags: TExprFlags): PNode = indexType = idx.typ x = x.sons[1] - addSon(result, semExprWithType(c, x)) - var typ = skipTypes(result.sons[0].typ, {tyGenericInst, tyVar, tyOrdinal}) - # turn any concrete typedesc into the absract typedesc type - if typ.kind == tyTypeDesc: typ.sons = nil + let yy = semExprWithType(c, x) + var typ = yy.typ + addSon(result, yy) + #var typ = skipTypes(result.sons[0].typ, {tyGenericInst, tyVar, tyOrdinal}) for i in countup(1, sonsLen(n) - 1): x = n.sons[i] if x.kind == nkExprColonExpr and sonsLen(x) == 2: @@ -433,10 +415,15 @@ proc semArrayConstr(c: PContext, n: PNode, flags: TExprFlags): PNode = localError(x.info, errInvalidOrderInArrayConstructor) x = x.sons[1] - n.sons[i] = semExprWithType(c, x, flags*{efAllowDestructor}) - addSon(result, fitNode(c, typ, n.sons[i])) + let xx = semExprWithType(c, x, flags*{efAllowDestructor}) + result.add xx + typ = commonType(typ, xx.typ) + #n.sons[i] = semExprWithType(c, x, flags*{efAllowDestructor}) + #addSon(result, fitNode(c, typ, n.sons[i])) inc(lastIndex) addSonSkipIntLit(result.typ, typ) + for i in 0 .. <result.len: + result.sons[i] = fitNode(c, typ, result.sons[i]) result.typ.sons[0] = makeRangeType(c, 0, sonsLen(result) - 1, n.info) proc fixAbstractType(c: PContext, n: PNode) = @@ -781,16 +768,6 @@ proc buildEchoStmt(c: PContext, n: PNode): PNode = # and check 'arg' for semantics again: addSon(result, semExpr(c, arg)) -proc discardCheck(result: PNode) = - if result.typ != nil and result.typ.kind notin {tyStmt, tyEmpty}: - if result.kind == nkNilLit: - # XXX too much work and fixing would break bootstrapping: - #Message(n.info, warnNilStatement) - result.typ = nil - elif not ImplicitlyDiscardable(result) and result.typ.kind != tyError and - gCmd != cmdInteractive: - localError(result.info, errDiscardValue) - proc semExprNoType(c: PContext, n: PNode): PNode = result = semExpr(c, n, {efWantStmt}) discardCheck(result) @@ -1125,14 +1102,16 @@ proc semAsgn(c: PContext, n: PNode): PNode = var rhs = semExprWithType(c, n.sons[1], if lhsIsResult: {efAllowDestructor} else: {}) - if lhsIsResult and lhs.sym.typ.kind == tyGenericParam: - if matchTypeClass(lhs.typ, rhs.typ): - InternalAssert c.p.resultSym != nil - lhs.typ = rhs.typ - c.p.resultSym.typ = rhs.typ - c.p.owner.typ.sons[0] = rhs.typ - else: - typeMismatch(n, lhs.typ, rhs.typ) + if lhsIsResult: + n.typ = EnforceVoidContext + if lhs.sym.typ.kind == tyGenericParam: + if matchTypeClass(lhs.typ, rhs.typ): + InternalAssert c.p.resultSym != nil + lhs.typ = rhs.typ + c.p.resultSym.typ = rhs.typ + c.p.owner.typ.sons[0] = rhs.typ + else: + typeMismatch(n, lhs.typ, rhs.typ) n.sons[1] = fitNode(c, le, rhs) fixAbstractType(c, n) @@ -1167,11 +1146,16 @@ proc semProcBody(c: PContext, n: PNode): PNode = # ``result``: if result.kind == nkSym and result.sym == c.p.resultSym: nil - elif result.kind == nkNilLit or ImplicitlyDiscardable(result): - # intended semantic: if it's 'discardable' and the context allows for it, - # discard it. This is bad for chaining but nicer for C wrappers. - # ambiguous :-( + elif result.kind == nkNilLit: + # or ImplicitlyDiscardable(result): + # new semantic: 'result = x' triggers the void context result.typ = nil + elif result.kind == nkStmtListExpr and result.typ.kind == tyNil: + # to keep backwards compatibility bodies like: + # nil + # # comment + # are not expressions: + fixNilType(result) else: var a = newNodeI(nkAsgn, n.info, 2) a.sons[0] = newSymNode(c.p.resultSym) @@ -1458,26 +1442,35 @@ 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(nkEmpty, 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 semSetConstr(c: PContext, n: PNode): PNode = result = newNodeI(nkCurly, n.info) @@ -1644,26 +1637,21 @@ proc semObjConstr(c: PContext, n: PNode, flags: TExprFlags): PNode = it.sons[1] = e # XXX object field name check for 'case objects' if the kind is static? -proc semStmtListExpr(c: PContext, n: PNode): PNode = - result = n - checkMinSonsLen(n, 1) - var length = sonsLen(n) - for i in countup(0, length - 2): - n.sons[i] = semStmt(c, n.sons[i]) - if length > 0: - n.sons[length - 1] = semExprWithType(c, n.sons[length - 1]) - n.typ = n.sons[length - 1].typ - -proc semBlockExpr(c: PContext, n: PNode): PNode = +proc semBlock(c: PContext, n: PNode): PNode = result = n Inc(c.p.nestedBlockCounter) checkSonsLen(n, 2) openScope(c.tab) # BUGFIX: label is in the scope of block! - if n.sons[0].kind notin {nkEmpty, nkSym}: - # nkSym for gensym'ed labels: - addDecl(c, newSymS(skLabel, n.sons[0], c)) - n.sons[1] = semStmtListExpr(c, n.sons[1]) + if n.sons[0].kind != nkEmpty: + var labl = newSymG(skLabel, n.sons[0], c) + if sfGenSym notin labl.flags: + addDecl(c, labl) + n.sons[0] = newSymNode(labl, n.sons[0].info) + suggestSym(n.sons[0], labl) + n.sons[1] = semExpr(c, n.sons[1]) n.typ = n.sons[1].typ + if isEmptyType(n.typ): n.kind = nkBlockStmt + else: n.kind = nkBlockExpr closeScope(c.tab) Dec(c.p.nestedBlockCounter) @@ -1683,63 +1671,11 @@ proc buildCall(n: PNode): PNode = else: result = n -proc semCaseExpr(c: PContext, caseStmt: PNode): PNode = - # The case expression is simply rewritten to a StmtListExpr: - # var res {.noInit, genSym.}: type(values) - # - # case E - # of X: res = value1 - # of Y: res = value2 - # - # res - var - info = caseStmt.info - resVar = newSym(skVar, idAnon, getCurrOwner(), info) - resNode = newSymNode(resVar, info) - resType: PType - - resVar.flags = { sfGenSym, sfNoInit } - - for i in countup(1, caseStmt.len - 1): - var cs = caseStmt[i] - case cs.kind - of nkOfBranch, nkElifBranch, nkElse: - # the value is always the last son regardless of the branch kind - cs.checkMinSonsLen 1 - var value = cs{-1} - if value.kind == nkStmtList: value.kind = nkStmtListExpr - - value = semExprWithType(c, value) - if resType == nil: - resType = value.typ - elif not sameType(resType, value.typ): - # XXX: semeType is a bit too harsh. - # work on finding a common base type. - # this will be useful for arrays/seq too: - # [ref DerivedA, ref DerivedB, ref Base] - typeMismatch(cs, resType, value.typ) - - cs{-1} = newNode(nkAsgn, cs.info, @[resNode, value]) - else: - IllFormedAst(caseStmt) - - result = newNode(nkStmtListExpr, info, @[ - newNode(nkVarSection, info, @[ - newNode(nkIdentDefs, info, @[ - resNode, - symNodeFromType(c, resType, info), - emptyNode])]), - caseStmt, - resNode]) - - result = semStmtListExpr(c, result) - proc fixImmediateParams(n: PNode): PNode = # XXX: Temporary work-around until we carry out # 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 = @@ -1899,7 +1835,7 @@ proc semExpr(c: PContext, n: PNode, flags: TExprFlags = {}): PNode = of nkObjConstr: result = semObjConstr(c, n, flags) of nkLambdaKinds: result = semLambda(c, n, flags) of nkDerefExpr: result = semDeref(c, n) - of nkAddr: + of nkAddr: result = n checkSonsLen(n, 1) n.sons[0] = semExprWithType(c, n.sons[0]) @@ -1910,9 +1846,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 nkStmtListExpr: result = semStmtListExpr(c, n) - of nkBlockExpr: result = semBlockExpr(c, n) + of nkIfExpr, nkIfStmt: result = semIf(c, n) of nkHiddenStdConv, nkHiddenSubConv, nkConv, nkHiddenCallConv: checkSonsLen(n, 2) of nkStringToCString, nkCStringToString, nkObjDownConv, nkObjUpConv: @@ -1929,22 +1863,19 @@ proc semExpr(c: PContext, n: PNode, flags: TExprFlags = {}): PNode = of nkStaticExpr: result = semStaticExpr(c, n) of nkAsgn: result = semAsgn(c, n) - of nkBlockStmt: result = semBlock(c, n) - of nkStmtList: result = semStmtList(c, n) + of nkBlockStmt, nkBlockExpr: result = semBlock(c, n) + of nkStmtList, nkStmtListExpr: result = semStmtList(c, n) of nkRaiseStmt: result = semRaise(c, n) of nkVarSection: result = semVarOrLet(c, n, skVar) 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) of nkBreakStmt, nkContinueStmt: result = semBreakOrContinue(c, n) of nkForStmt, nkParForStmt: result = semFor(c, n) - of nkCaseStmt: - if efWantStmt in flags: result = semCase(c, n) - else: result = semCaseExpr(c, n) + of nkCaseStmt: result = semCase(c, n) of nkReturnStmt: result = semReturn(c, n) of nkAsmStmt: result = semAsm(c, n) of nkYieldStmt: result = semYield(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..2c3adfeda 100644 --- a/compiler/semstmts.nim +++ b/compiler/semstmts.nim @@ -10,54 +10,10 @@ ## this module does the semantic checking of statements # included from sem.nim +var EnforceVoidContext = PType(kind: tyStmt) + 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 @@ -86,21 +42,6 @@ proc semBreakOrContinue(c: PContext, n: PNode): PNode = elif (c.p.nestedLoopCounter <= 0) and (c.p.nestedBlockCounter <= 0): localError(n.info, errInvalidControlFlowX, renderTree(n, {renderNoComments})) - -proc semBlock(c: PContext, n: PNode): PNode = - result = n - Inc(c.p.nestedBlockCounter) - checkSonsLen(n, 2) - openScope(c.tab) # BUGFIX: label is in the scope of block! - if n.sons[0].kind != nkEmpty: - var labl = newSymG(skLabel, n.sons[0], c) - if sfGenSym notin labl.flags: - addDecl(c, labl) - n.sons[0] = newSymNode(labl, n.sons[0].info) - suggestSym(n.sons[0], labl) - n.sons[1] = semStmt(c, n.sons[1]) - closeScope(c.tab) - Dec(c.p.nestedBlockCounter) proc semAsm(con: PContext, n: PNode): PNode = checkSonsLen(n, 2) @@ -117,6 +58,8 @@ proc semWhile(c: PContext, n: PNode): PNode = n.sons[1] = semStmt(c, n.sons[1]) dec(c.p.nestedLoopCounter) closeScope(c.tab) + if n.sons[1].typ == EnforceVoidContext: + result.typ = EnforceVoidContext proc toCover(t: PType): biggestInt = var t2 = skipTypes(t, abstractVarRange-{tyTypeDesc}) @@ -125,20 +68,126 @@ proc toCover(t: PType): biggestInt = else: result = lengthOrd(skipTypes(t, abstractVar-{tyTypeDesc})) -proc semCase(c: PContext, n: PNode): PNode = - # check selector: +proc performProcvarCheck(c: PContext, n: PNode, s: PSym) = + var smoduleId = getModule(s).id + if sfProcVar notin s.flags and s.typ.callConv == ccDefault and + smoduleId != c.module.id and smoduleId != c.friendModule.id: + LocalError(n.info, errXCannotBePassedToProcVar, s.name.s) + +proc semProcvarCheck(c: PContext, n: PNode) = + let n = n.skipConv + if n.kind == nkSym and n.sym.kind in {skProc, skMethod, skIterator, + skConverter}: + performProcvarCheck(c, n, n.sym) + +proc semProc(c: PContext, n: PNode): PNode + +include semdestruct + +proc semDestructorCheck(c: PContext, n: PNode, flags: TExprFlags) {.inline.} = + if efAllowDestructor notin flags and n.kind in nkCallKinds+{nkObjConstr}: + if instantiateDestructor(c, n.typ): + LocalError(n.info, errGenerated, + "usage of a type with a destructor in a non destructible context") + +proc newDeref(n: PNode): PNode {.inline.} = + result = newNodeIT(nkHiddenDeref, n.info, n.typ.sons[0]) + addSon(result, n) + +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 semExprBranchScope(c: PContext, n: PNode): PNode = + openScope(c.tab) + result = semExprBranch(c, n) + closeScope(c.tab) + +const + skipForDiscardable = {nkIfStmt, nkIfExpr, nkCaseStmt, nkOfBranch, + nkElse, nkStmtListExpr, nkTryStmt, nkFinally, nkExceptBranch, + nkElifBranch, nkElifExpr, nkElseExpr, nkBlockStmt, nkBlockExpr} + +proc ImplicitlyDiscardable(n: PNode): bool = + var n = n + while n.kind in skipForDiscardable: n = n.lastSon + result = isCallExpr(n) and n.sons[0].kind == nkSym and + sfDiscardable in n.sons[0].sym.flags + +proc fixNilType(n: PNode) = + if isAtom(n): + if n.kind != nkNilLit and n.typ != nil: + localError(n.info, errDiscardValue) + elif n.kind in {nkStmtList, nkStmtListExpr}: + n.kind = nkStmtList + for it in n: fixNilType(it) + n.typ = nil + +proc discardCheck(result: PNode) = + if result.typ != nil and result.typ.kind notin {tyStmt, tyEmpty}: + if result.kind == nkNilLit: + result.typ = nil + elif ImplicitlyDiscardable(result): + var n = result + result.typ = nil + while n.kind in skipForDiscardable: + n = n.lastSon + n.typ = nil + elif result.typ.kind != tyError and gCmd != cmdInteractive: + if result.typ.kind == tyNil: + fixNilType(result) + else: + localError(result.info, errDiscardValue) + +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: + when newScopeForIf: openScope(c.tab) + it.sons[0] = forceBool(c, semExprWithType(c, it.sons[0])) + when not newScopeForIf: 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 + it.sons[0] = semExprBranchScope(c, it.sons[0]) + typ = commonType(typ, it.sons[0].typ) + else: illFormedAst(it) + if isEmptyType(typ) or typ.kind == tyNil or not hasElse: + for it in n: discardCheck(it.lastSon) + result.kind = nkIfStmt + # propagate any enforced VoidContext: + if typ == EnforceVoidContext: result.typ = EnforceVoidContext + 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 semCase(c: PContext, n: PNode): PNode = result = n checkMinSonsLen(n, 2) openScope(c.tab) n.sons[0] = semExprWithType(c, n.sons[0]) var chckCovered = false var covered: biggestint = 0 + var typ = CommonTypeBegin + var hasElse = false case skipTypes(n.sons[0].Typ, abstractVarRange-{tyTypeDesc}).Kind - of tyInt..tyInt64, tyChar, tyEnum: + of tyInt..tyInt64, tyChar, tyEnum: chckCovered = true - of tyFloat..tyFloat128, tyString, tyError: + of tyFloat..tyFloat128, tyString, tyError: nil - else: + else: LocalError(n.info, errSelectorMustBeOfCertainTypes) return for i in countup(1, sonsLen(n) - 1): @@ -147,21 +196,89 @@ proc semCase(c: PContext, n: PNode): PNode = of nkOfBranch: checkMinSonsLen(x, 2) semCaseBranch(c, n, x, i, covered) - var length = sonsLen(x) - x.sons[length - 1] = semStmtScope(c, x.sons[length - 1]) - of nkElifBranch: + var last = sonsLen(x)-1 + x.sons[last] = semExprBranchScope(c, x.sons[last]) + typ = commonType(typ, x.sons[last].typ) + of nkElifBranch: chckCovered = false checkSonsLen(x, 2) + when newScopeForIf: openScope(c.tab) x.sons[0] = forceBool(c, semExprWithType(c, x.sons[0])) - x.sons[1] = semStmtScope(c, x.sons[1]) - of nkElse: + when not newScopeForIf: openScope(c.tab) + x.sons[1] = semExprBranch(c, x.sons[1]) + typ = commonType(typ, x.sons[1].typ) + closeScope(c.tab) + of nkElse: chckCovered = false checkSonsLen(x, 1) - x.sons[0] = semStmtScope(c, x.sons[0]) - else: illFormedAst(x) - if chckCovered and (covered != toCover(n.sons[0].typ)): - localError(n.info, errNotAllCasesCovered) + x.sons[0] = semExprBranchScope(c, x.sons[0]) + typ = commonType(typ, x.sons[0].typ) + hasElse = true + else: + illFormedAst(x) + if chckCovered: + if covered == toCover(n.sons[0].typ): + hasElse = true + else: + localError(n.info, errNotAllCasesCovered) closeScope(c.tab) + if isEmptyType(typ) or typ.kind == tyNil or not hasElse: + for i in 1..n.len-1: discardCheck(n.sons[i].lastSon) + # propagate any enforced VoidContext: + if typ == EnforceVoidContext: + result.typ = EnforceVoidContext + else: + for i in 1..n.len-1: + var it = n.sons[i] + let j = it.len-1 + it.sons[j] = fitNode(c, typ, it.sons[j]) + result.typ = typ + +proc semTry(c: PContext, n: PNode): PNode = + result = n + inc c.p.inTryStmt + checkMinSonsLen(n, 2) + var typ = CommonTypeBegin + n.sons[0] = semExprBranchScope(c, n.sons[0]) + typ = commonType(typ, n.sons[0].typ) + var check = initIntSet() + for i in countup(1, sonsLen(n) - 1): + var a = n.sons[i] + checkMinSonsLen(a, 1) + var length = sonsLen(a) + if a.kind == nkExceptBranch: + # XXX what does this do? so that ``except [a, b, c]`` is supported? + if length == 2 and a.sons[0].kind == nkBracket: + a.sons[0..0] = a.sons[0].sons + length = a.sonsLen + + for j in countup(0, length-2): + var typ = semTypeNode(c, a.sons[j], nil) + if typ.kind == tyRef: typ = typ.sons[0] + if typ.kind != tyObject: + LocalError(a.sons[j].info, errExprCannotBeRaised) + a.sons[j] = newNodeI(nkType, a.sons[j].info) + a.sons[j].typ = typ + if ContainsOrIncl(check, typ.id): + localError(a.sons[j].info, errExceptionAlreadyHandled) + elif a.kind != nkFinally: + illFormedAst(n) + # last child of an nkExcept/nkFinally branch is a statement: + a.sons[length-1] = semExprBranchScope(c, a.sons[length-1]) + typ = commonType(typ, a.sons[length-1].typ) + dec c.p.inTryStmt + if isEmptyType(typ) or typ.kind == tyNil: + discardCheck(n.sons[0]) + for i in 1..n.len-1: discardCheck(n.sons[i].lastSon) + if typ == EnforceVoidContext: + result.typ = EnforceVoidContext + else: + n.sons[0] = fitNode(c, typ, n.sons[0]) + for i in 1..n.len-1: + var it = n.sons[i] + let j = it.len-1 + it.sons[j] = fitNode(c, typ, it.sons[j]) + result.typ = typ proc fitRemoveHiddenConv(c: PContext, typ: Ptype, n: PNode): PNode = result = fitNode(c, typ, n) @@ -484,10 +601,6 @@ proc semForVars(c: PContext, n: PNode): PNode = n.sons[length-1] = SemStmt(c, n.sons[length-1]) Dec(c.p.nestedLoopCounter) -proc newDeref(n: PNode): PNode {.inline.} = - result = newNodeIT(nkHiddenDeref, n.info, n.typ.sons[0]) - addSon(result, n) - proc implicitIterator(c: PContext, it: string, arg: PNode): PNode = result = newNodeI(nkCall, arg.info) result.add(newIdentNode(it.getIdent, arg.info)) @@ -524,6 +637,9 @@ proc semFor(c: PContext, n: PNode): PNode = result = semForFields(c, n, call.sons[0].sym.magic) else: result = semForVars(c, n) + # propagate any enforced VoidContext: + if n.sons[length-1].typ == EnforceVoidContext: + result.typ = EnforceVoidContext closeScope(c.tab) proc semRaise(c: PContext, n: PNode): PNode = @@ -535,36 +651,6 @@ proc semRaise(c: PContext, n: PNode): PNode = if typ.kind != tyRef or typ.sons[0].kind != tyObject: localError(n.info, errExprCannotBeRaised) -proc semTry(c: PContext, n: PNode): PNode = - result = n - inc c.p.inTryStmt - checkMinSonsLen(n, 2) - n.sons[0] = semStmtScope(c, n.sons[0]) - var check = initIntSet() - for i in countup(1, sonsLen(n) - 1): - var a = n.sons[i] - checkMinSonsLen(a, 1) - var length = sonsLen(a) - if a.kind == nkExceptBranch: - if length == 2 and a.sons[0].kind == nkBracket: - a.sons[0..0] = a.sons[0].sons - length = a.sonsLen - - for j in countup(0, length - 2): - var typ = semTypeNode(c, a.sons[j], nil) - if typ.kind == tyRef: typ = typ.sons[0] - if typ.kind != tyObject: - LocalError(a.sons[j].info, errExprCannotBeRaised) - a.sons[j] = newNodeI(nkType, a.sons[j].info) - a.sons[j].typ = typ - if ContainsOrIncl(check, typ.id): - localError(a.sons[j].info, errExceptionAlreadyHandled) - elif a.kind != nkFinally: - illFormedAst(n) - # last child of an nkExcept/nkFinally branch is a statement: - a.sons[length - 1] = semStmtScope(c, a.sons[length - 1]) - dec c.p.inTryStmt - proc addGenericParamListToScope(c: PContext, n: PNode) = if n.kind != nkGenericParams: illFormedAst(n) for i in countup(0, sonsLen(n)-1): @@ -607,9 +693,6 @@ proc typeSectionRightSidePass(c: PContext, n: PNode) = openScope(c.tab) pushOwner(s) if s.magic == mNone: s.typ.kind = tyGenericBody - if s.typ.containerID != 0: - InternalError(a.info, "semTypeSection: containerID") - s.typ.containerID = s.typ.id # XXX for generic type aliases this is not correct! We need the # underlying Id really: # @@ -678,6 +761,9 @@ proc SemTypeSection(c: PContext, n: PNode): PNode = proc semParamList(c: PContext, n, genericParams: PNode, s: PSym) = s.typ = semProcTypeNode(c, n, genericParams, nil, s.kind) + if s.kind notin {skMacro, skTemplate}: + if s.typ.sons[0] != nil and s.typ.sons[0].kind == tyStmt: + localError(n.info, errGenerated, "invalid return type: 'stmt'") proc addParams(c: PContext, n: PNode, kind: TSymKind) = for i in countup(1, sonsLen(n)-1): @@ -792,22 +878,6 @@ proc activate(c: PContext, n: PNode) = else: nil -proc instantiateDestructor*(c: PContext, typ: PType): bool - -proc doDestructorStuff(c: PContext, s: PSym, n: PNode) = - let t = s.typ.sons[1].skipTypes({tyVar}) - t.destructor = s - # automatically insert calls to base classes' destructors - if n.sons[bodyPos].kind != nkEmpty: - for i in countup(0, t.sonsLen - 1): - # when inheriting directly from object - # there will be a single nil son - if t.sons[i] == nil: continue - if instantiateDestructor(c, t.sons[i]): - n.sons[bodyPos].addSon(newNode(nkCall, t.sym.info, @[ - useSym(t.sons[i].destructor), - n.sons[paramsPos][1][0]])) - proc maybeAddResult(c: PContext, s: PSym, n: PNode) = if s.typ.sons[0] != nil and (s.kind != skIterator or s.typ.callConv == ccClosure): @@ -1018,203 +1088,34 @@ proc semStaticStmt(c: PContext, n: PNode): PNode = result = newNodeI(nkDiscardStmt, n.info, 1) result.sons[0] = emptyNode -# special marker values that indicates that we are -# 1) AnalyzingDestructor: currently analyzing the type for destructor -# generation (needed for recursive types) -# 2) DestructorIsTrivial: completed the analysis before and determined -# that the type has a trivial destructor -var AnalyzingDestructor, DestructorIsTrivial: PSym -new(AnalyzingDestructor) -new(DestructorIsTrivial) - -var - destructorName = getIdent"destroy_" - destructorParam = getIdent"this_" - destructorPragma = newIdentNode(getIdent"destructor", UnknownLineInfo()) - rangeDestructorProc*: PSym - -proc destroyField(c: PContext, field: PSym, holder: PNode): PNode = - if instantiateDestructor(c, field.typ): - result = newNode(nkCall, field.info, @[ - useSym(field.typ.destructor), - newNode(nkDotExpr, field.info, @[holder, useSym(field)])]) - -proc destroyCase(c: PContext, n: PNode, holder: PNode): PNode = - var nonTrivialFields = 0 - result = newNode(nkCaseStmt, n.info, @[]) - # case x.kind - result.addSon(newNode(nkDotExpr, n.info, @[holder, n.sons[0]])) - for i in countup(1, n.len - 1): - # of A, B: - var caseBranch = newNode(n[i].kind, n[i].info, n[i].sons[0 .. -2]) - let recList = n[i].lastSon - var destroyRecList = newNode(nkStmtList, n[i].info, @[]) - template addField(f: expr): stmt = - let stmt = destroyField(c, f, holder) - if stmt != nil: - destroyRecList.addSon(stmt) - inc nonTrivialFields - - case recList.kind - of nkSym: - addField(recList.sym) - of nkRecList: - for j in countup(0, recList.len - 1): - addField(recList[j].sym) - else: - internalAssert false - - caseBranch.addSon(destroyRecList) - result.addSon(caseBranch) - # maybe no fields were destroyed? - if nonTrivialFields == 0: - result = nil - -proc generateDestructor(c: PContext, t: PType): PNode = - ## generate a destructor for a user-defined object or tuple type - ## returns nil if the destructor turns out to be trivial - - template addLine(e: expr): stmt = - if result == nil: result = newNode(nkStmtList) - result.addSon(e) - - # XXX: This may be true for some C-imported types such as - # Tposix_spawnattr - if t.n == nil or t.n.sons == nil: return - internalAssert t.n.kind == nkRecList - let destructedObj = newIdentNode(destructorParam, UnknownLineInfo()) - # call the destructods of all fields - for s in countup(0, t.n.sons.len - 1): - case t.n.sons[s].kind - of nkRecCase: - let stmt = destroyCase(c, t.n.sons[s], destructedObj) - if stmt != nil: addLine(stmt) - of nkSym: - let stmt = destroyField(c, t.n.sons[s].sym, destructedObj) - if stmt != nil: addLine(stmt) - else: - internalAssert false - - # base classes' destructors will be automatically called by - # semProcAux for both auto-generated and user-defined destructors - -proc instantiateDestructor*(c: PContext, typ: PType): bool = - # returns true if the type already had a user-defined - # destructor or if the compiler generated a default - # member-wise one - var t = skipTypes(typ, {tyConst, tyMutable}) - - if t.destructor != nil: - # XXX: This is not entirely correct for recursive types, but we need - # it temporarily to hide the "destroy is already defined" problem - return t.destructor notin [AnalyzingDestructor, DestructorIsTrivial] - - case t.kind - of tySequence, tyArray, tyArrayConstr, tyOpenArray, tyVarargs: - if instantiateDestructor(c, t.sons[0]): - if rangeDestructorProc == nil: - rangeDestructorProc = SymtabGet(c.tab, getIdent"nimDestroyRange") - t.destructor = rangeDestructorProc - return true - else: - return false - of tyTuple, tyObject: - t.destructor = AnalyzingDestructor - let generated = generateDestructor(c, t) - if generated != nil: - internalAssert t.sym != nil - var i = t.sym.info - let fullDef = newNode(nkProcDef, i, @[ - newIdentNode(destructorName, i), - emptyNode, - emptyNode, - newNode(nkFormalParams, i, @[ - emptyNode, - newNode(nkIdentDefs, i, @[ - newIdentNode(destructorParam, i), - useSym(t.sym), - emptyNode]), - ]), - newNode(nkPragma, i, @[destructorPragma]), - emptyNode, - generated - ]) - discard semProc(c, fullDef) - internalAssert t.destructor != nil - return true +proc usesResult(n: PNode): bool = + # nkStmtList(expr) properly propagates the void context, + # so we don't need to process that all over again: + if n.kind notin {nkStmtList, nkStmtListExpr} + procDefs: + if isAtom(n): + result = n.kind == nkSym and n.sym.kind == skResult + elif n.kind == nkReturnStmt: + result = true else: - t.destructor = DestructorIsTrivial - return false - else: - return false - -proc insertDestructors(c: PContext, - varSection: PNode): tuple[outer, inner: PNode] = - # Accepts a var or let section. - # - # When a var section has variables with destructors - # the var section is split up and finally blocks are inserted - # immediately after all "destructable" vars - # - # In case there were no destrucable variables, the proc returns - # (nil, nil) and the enclosing stmt-list requires no modifications. - # - # Otherwise, after the try blocks are created, the rest of the enclosing - # stmt-list should be inserted in the most `inner` such block (corresponding - # to the last variable). - # - # `outer` is a statement list that should replace the original var section. - # It will include the new truncated var section followed by the outermost - # try block. - let totalVars = varSection.sonsLen - for j in countup(0, totalVars - 1): - let - varId = varSection[j][0] - varTyp = varId.sym.typ - info = varId.info - - if varTyp != nil and instantiateDestructor(c, varTyp) and - sfGlobal notin varId.sym.flags: - var tryStmt = newNodeI(nkTryStmt, info) - - if j < totalVars - 1: - var remainingVars = newNodeI(varSection.kind, info) - remainingVars.sons = varSection.sons[(j+1)..(-1)] - let (outer, inner) = insertDestructors(c, remainingVars) - if outer != nil: - tryStmt.addSon(outer) - result.inner = inner - else: - result.inner = newNodeI(nkStmtList, info) - result.inner.addSon(remainingVars) - tryStmt.addSon(result.inner) - else: - result.inner = newNodeI(nkStmtList, info) - tryStmt.addSon(result.inner) - - tryStmt.addSon( - newNode(nkFinally, info, @[ - semStmt(c, newNode(nkCall, info, @[ - useSym(varTyp.destructor), - useSym(varId.sym)]))])) - - result.outer = newNodeI(nkStmtList, info) - varSection.sons.setLen(j+1) - result.outer.addSon(varSection) - result.outer.addSon(tryStmt) - - return - -proc ImplicitlyDiscardable(n: PNode): bool = - result = isCallExpr(n) and n.sons[0].kind == nkSym and - sfDiscardable in n.sons[0].sym.flags + for c in n: + if usesResult(c): return true proc semStmtList(c: PContext, n: PNode): PNode = # these must be last statements in a block: const LastBlockStmts = {nkRaiseStmt, nkReturnStmt, nkBreakStmt, nkContinueStmt} result = n + result.kind = nkStmtList var length = sonsLen(n) + var voidContext = false + var last = length-1 + # by not allowing for nkCommentStmt etc. we ensure nkStmtListExpr actually + # really *ends* in the expression that produces the type: The compiler now + # relies on this fact and it's too much effort to change that. And arguably + # 'R(); #comment' shouldn't produce R's type anyway. + #while last > 0 and n.sons[last].kind in {nkPragma, nkCommentStmt, + # nkNilLit, nkEmpty}: + # dec last for i in countup(0, length - 1): case n.sons[i].kind of nkFinally, nkExceptBranch: @@ -1236,7 +1137,16 @@ proc semStmtList(c: PContext, n: PNode): PNode = n.sons.setLen(i+1) return else: - n.sons[i] = semStmt(c, n.sons[i]) + n.sons[i] = semExpr(c, n.sons[i]) + if n.sons[i].typ == EnforceVoidContext or usesResult(n.sons[i]): + voidContext = true + n.typ = EnforceVoidContext + elif i != last or voidContext: + discardCheck(n.sons[i]) + else: + n.typ = n.sons[i].typ + if not isEmptyType(n.typ): + n.kind = nkStmtListExpr case n.sons[i].kind of nkVarSection, nkLetSection: let (outer, inner) = insertDestructors(c, n.sons[i]) @@ -1252,15 +1162,17 @@ proc semStmtList(c: PContext, n: PNode): PNode = of nkPragma, nkCommentStmt, nkNilLit, nkEmpty: nil else: localError(n.sons[j].info, errStmtInvalidAfterReturn) else: nil - - # a statement list (s; e) has the type 'e': - if result.kind == nkStmtList and result.len > 0: - var lastStmt = lastSon(result) - if lastStmt.kind != nkNilLit and not ImplicitlyDiscardable(lastStmt): - result.typ = lastStmt.typ - #localError(lastStmt.info, errGenerated, - # "Last expression must be explicitly returned if it " & - # "is discardable or discarded") + if result.len == 1: + result = result.sons[0] + when false: + # a statement list (s; e) has the type 'e': + if result.kind == nkStmtList and result.len > 0: + var lastStmt = lastSon(result) + if lastStmt.kind != nkNilLit and not ImplicitlyDiscardable(lastStmt): + result.typ = lastStmt.typ + #localError(lastStmt.info, errGenerated, + # "Last expression must be explicitly returned if it " & + # "is discardable or discarded") proc SemStmt(c: PContext, n: PNode): PNode = # now: simply an alias: diff --git a/compiler/semtypes.nim b/compiler/semtypes.nim index b3a82c413..3abc994ef 100644 --- a/compiler/semtypes.nim +++ b/compiler/semtypes.nim @@ -770,9 +770,6 @@ proc semGeneric(c: PContext, n: PNode, s: PSym, prev: PType): PType = return newOrPrevType(tyError, prev, c) elif s.typ.kind != tyGenericBody: isConcrete = false - elif s.typ.containerID == 0: - InternalError(n.info, "semtypes.semGeneric") - return newOrPrevType(tyError, prev, c) elif sonsLen(n) != sonsLen(s.typ): LocalError(n.info, errWrongNumberOfArguments) return newOrPrevType(tyError, prev, c) diff --git a/compiler/service.nim b/compiler/service.nim index b3c7fbc83..8e8fe20bf 100644 --- a/compiler/service.nim +++ b/compiler/service.nim @@ -65,6 +65,9 @@ proc serve*(action: proc (){.nimcall.}) = curCaasCmd = cmd processCmdLine(passCmd2, cmd) action() + gDirtyBufferIdx = 0 + gDirtyOriginalIdx = 0 + gErrorCounter = 0 let typ = getConfigVar("server.type") case typ diff --git a/compiler/sigmatch.nim b/compiler/sigmatch.nim index ddda493ee..e1882e1fb 100644 --- a/compiler/sigmatch.nim +++ b/compiler/sigmatch.nim @@ -559,11 +559,10 @@ proc typeRel(c: var TCandidate, f, a: PType): TTypeRelation = # simply no match for now: nil elif x.kind == tyGenericInst and - (f.sons[0].containerID == x.sons[0].containerID) and - (sonsLen(x) - 1 == sonsLen(f)): - assert(x.sons[0].kind == tyGenericBody) - for i in countup(1, sonsLen(f) - 1): - if x.sons[i].kind == tyGenericParam: + (f.sons[0] == x.sons[0]) and + (sonsLen(x) - 1 == sonsLen(f)): + for i in countup(1, sonsLen(f) - 1): + if x.sons[i].kind == tyGenericParam: InternalError("wrong instantiated type!") elif typeRel(c, f.sons[i], x.sons[i]) <= isSubtype: return result = isGeneric diff --git a/compiler/suggest.nim b/compiler/suggest.nim index 130666f4d..18e6dbddf 100644 --- a/compiler/suggest.nim +++ b/compiler/suggest.nim @@ -9,7 +9,9 @@ ## This file implements features required for IDE support. -# imported from sigmatch.nim +# included from sigmatch.nim + +import algorithm const sep = '\t' @@ -238,12 +240,69 @@ proc findDefinition(node: PNode, s: PSym) = SuggestWriteln(SymToStr(s, isLocal=false, sectionDef)) SuggestQuit() +type + TSourceMap = object + lines: seq[TLineMap] + + TEntry = object + pos: int + sym: PSym + + TLineMap = object + entries: seq[TEntry] + +var + gSourceMaps: seq[TSourceMap] = @[] + +proc ensureIdx[T](x: var T, y: int) = + if x.len <= y: x.setLen(y+1) + +proc ensureSeq[T](x: var seq[T]) = + if x == nil: newSeq(x, 0) + +proc resetSourceMap*(fileIdx: int32) = + ensureIdx(gSourceMaps, fileIdx) + gSourceMaps[fileIdx].lines = @[] + +proc addToSourceMap(sym: Psym, info: TLineInfo) = + ensureIdx(gSourceMaps, info.fileIndex) + ensureSeq(gSourceMaps[info.fileIndex].lines) + ensureIdx(gSourceMaps[info.fileIndex].lines, info.line) + ensureSeq(gSourceMaps[info.fileIndex].lines[info.line].entries) + gSourceMaps[info.fileIndex].lines[info.line].entries.add(TEntry(pos: info.col, sym: sym)) + +proc defFromLine(entries: var seq[TEntry], col: int32) = + if entries == nil: return + # The sorting is done lazily here on purpose. + # No need to pay the price for it unless the user requests + # "goto definition" on a particular line + sort(entries) do (a,b: TEntry) -> int: + return cmp(a.pos, b.pos) + + for e in entries: + # currently, the line-infos for most expressions point to + # one position past the end of the expression. This means + # that the first expr that ends after the cursor column is + # the one we are looking for. + if e.pos >= col: + SuggestWriteln(SymToStr(e.sym, isLocal=false, sectionDef)) + return + +proc defFromSourceMap*(i: TLineInfo) = + if not ((i.fileIndex < gSourceMaps.len) and + (gSourceMaps[i.fileIndex].lines != nil) and + (i.line < gSourceMaps[i.fileIndex].lines.len)): return + + defFromLine(gSourceMaps[i.fileIndex].lines[i.line].entries, i.col) + proc suggestSym*(n: PNode, s: PSym) {.inline.} = ## misnamed: should be 'symDeclared' if optUsages in gGlobalOptions: findUsages(n, s) if optDef in gGlobalOptions: findDefinition(n, s) + if isServing: + addToSourceMap(s, n.info) proc markUsed(n: PNode, s: PSym) = incl(s.flags, sfUsed) diff --git a/compiler/types.nim b/compiler/types.nim index 4b528d9a2..6f47a7f2d 100644 --- a/compiler/types.nim +++ b/compiler/types.nim @@ -486,7 +486,9 @@ proc TypeToString(typ: PType, prefer: TPreferedDesc = preferName): string = of tyPtr, tyRef, tyVar, tyMutable, tyConst: result = typeToStr[t.kind] & typeToString(t.sons[0]) of tyRange: - result = "range " & rangeToStr(t.n) & "(" & typeToString(t.sons[0]) & ")" + result = "range " & rangeToStr(t.n) + if prefer != preferExported: + result.add("(" & typeToString(t.sons[0]) & ")") of tyProc: result = if tfIterator in t.flags: "iterator (" else: "proc (" for i in countup(1, sonsLen(t) - 1): @@ -904,6 +906,26 @@ proc inheritanceDiff*(a, b: PType): int = inc(result) result = high(int) +proc commonSuperclass*(a, b: PType): PType = + # quick check: are they the same? + if sameObjectTypes(a, b): return a + + # simple algorithm: we store all ancestors of 'a' in a ID-set and walk 'b' + # up until the ID is found: + assert a.kind == tyObject + assert b.kind == tyObject + var x = a + var ancestors = initIntSet() + while x != nil: + x = skipTypes(x, skipPtrs) + ancestors.incl(x.id) + x = x.sons[0] + var y = b + while y != nil: + y = skipTypes(y, skipPtrs) + if ancestors.contains(y.id): return y + y = y.sons[0] + proc typeAllowedAux(marker: var TIntSet, typ: PType, kind: TSymKind): bool proc typeAllowedNode(marker: var TIntSet, n: PNode, kind: TSymKind): bool = result = true @@ -1261,3 +1283,14 @@ proc compatibleEffects*(formal, actual: PType): bool = result = compatibleEffectsAux(st, real.sons[tagEffects]) if not result: return result = true + +proc isCompileTimeOnly*(t: PType): bool {.inline.} = + result = t.kind in {tyTypedesc, tyExpr} + +proc containsCompileTimeOnly*(t: PType): bool = + if isCompileTimeOnly(t): return true + if t.sons != nil: + for i in 0 .. <t.sonsLen: + if t.sons[i] != nil and isCompileTimeOnly(t.sons[i]): + return true + return false |