From 2199f8328059ffa62400acc7342b36dd50aed7e2 Mon Sep 17 00:00:00 2001 From: Andreas Rumpf Date: Tue, 6 Feb 2018 09:15:54 +0100 Subject: improve error message for twrongcolon --- compiler/parser.nim | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) (limited to 'compiler/parser.nim') diff --git a/compiler/parser.nim b/compiler/parser.nim index b1cfd7609..4974abcc3 100644 --- a/compiler/parser.nim +++ b/compiler/parser.nim @@ -914,7 +914,7 @@ proc parseIdentColonEquals(p: var TParser, flags: TDeclaredIdentFlags): PNode = optInd(p, result) addSon(result, parseTypeDesc(p)) else: - addSon(result, ast.emptyNode) + addSon(result, newNodeP(nkEmpty, p)) if p.tok.tokType != tkEquals and withBothOptional notin flags: parMessage(p, errColonOrEqualsExpected, p.tok) if p.tok.tokType == tkEquals: @@ -922,7 +922,7 @@ proc parseIdentColonEquals(p: var TParser, flags: TDeclaredIdentFlags): PNode = optInd(p, result) addSon(result, parseExpr(p)) else: - addSon(result, ast.emptyNode) + addSon(result, newNodeP(nkEmpty, p)) proc parseTuple(p: var TParser, indentAllowed = false): PNode = #| inlTupleDecl = 'tuple' -- cgit 1.4.1-2-gfad0 From ac10a3813a272ae25b3a9702b2e6facc00b023e4 Mon Sep 17 00:00:00 2001 From: Robert Hencke Date: Mon, 12 Mar 2018 04:27:36 -0400 Subject: Fix casing in rule 'typeDesc' (#7324) Also, leave a note in grammar.txt that it is generated. --- compiler/parser.nim | 4 +++- doc/grammar.txt | 3 ++- 2 files changed, 5 insertions(+), 2 deletions(-) (limited to 'compiler/parser.nim') diff --git a/compiler/parser.nim b/compiler/parser.nim index 4974abcc3..1ae7646cd 100644 --- a/compiler/parser.nim +++ b/compiler/parser.nim @@ -17,6 +17,8 @@ # In fact the grammar is generated from this file: when isMainModule: + # Leave a note in grammar.txt that it is generated: + #| # This file is generated by compiler/parser.nim. import pegs var outp = open("doc/grammar.txt", fmWrite) for line in lines("compiler/parser.nim"): @@ -1675,7 +1677,7 @@ proc parseSection(p: var TParser, kind: TNodeKind, parMessage(p, errIdentifierExpected, p.tok) proc parseConstant(p: var TParser): PNode = - #| constant = identWithPragma (colon typedesc)? '=' optInd expr indAndComment + #| constant = identWithPragma (colon typeDesc)? '=' optInd expr indAndComment result = newNodeP(nkConstDef, p) addSon(result, identWithPragma(p)) if p.tok.tokType == tkColon: diff --git a/doc/grammar.txt b/doc/grammar.txt index eae3694a0..f25b079f4 100644 --- a/doc/grammar.txt +++ b/doc/grammar.txt @@ -1,3 +1,4 @@ +# This file is generated by compiler/parser.nim. module = stmt ^* (';' / IND{=}) comma = ',' COMMENT? semicolon = ';' COMMENT? @@ -155,7 +156,7 @@ routine = optInd identVis pattern? genericParamList? paramListColon pragma? ('=' COMMENT? stmt)? indAndComment commentStmt = COMMENT section(p) = COMMENT? p / (IND{>} (p / COMMENT)^+IND{=} DED) -constant = identWithPragma (colon typedesc)? '=' optInd expr indAndComment +constant = identWithPragma (colon typeDesc)? '=' optInd expr indAndComment enum = 'enum' optInd (symbol optInd ('=' optInd expr COMMENT?)? comma?)+ objectWhen = 'when' expr colcom objectPart COMMENT? ('elif' expr colcom objectPart COMMENT?)* -- cgit 1.4.1-2-gfad0 From b0994c7f929395217b3caeb2699add29f35ff61e Mon Sep 17 00:00:00 2001 From: Araq Date: Sun, 18 Mar 2018 12:04:22 +0100 Subject: better error messages --- compiler/parser.nim | 13 +++++++++++-- compiler/semexprs.nim | 4 ++++ compiler/semtypes.nim | 3 ++- 3 files changed, 17 insertions(+), 3 deletions(-) (limited to 'compiler/parser.nim') diff --git a/compiler/parser.nim b/compiler/parser.nim index 1ae7646cd..debb0b34d 100644 --- a/compiler/parser.nim +++ b/compiler/parser.nim @@ -820,7 +820,6 @@ proc parseIfExpr(p: var TParser, kind: TNodeKind): PNode = if realInd(p): p.currInd = p.tok.indent wasIndented = true - echo result.info, " yes ", p.currInd addSon(branch, parseExpr(p)) result.add branch while sameInd(p) or not wasIndented: @@ -964,6 +963,8 @@ proc parseTuple(p: var TParser, indentAllowed = false): PNode = parMessage(p, errIdentifierExpected, p.tok) break if not sameInd(p): break + elif p.tok.tokType == tkParLe: + parMessage(p, errGenerated, "the syntax for tuple types is 'tuple[...]', not 'tuple(...)'") else: result = newNodeP(nkTupleClassTy, p) @@ -985,6 +986,9 @@ proc parseParamList(p: var TParser, retColon = true): PNode = a = parseIdentColonEquals(p, {withBothOptional, withPragma}) of tkParRi: break + of tkVar: + parMessage(p, errGenerated, "the syntax is 'parameter: var T', not 'var parameter: T'") + break else: parMessage(p, errTokenExpected, ")") break @@ -2132,7 +2136,12 @@ proc parseTopLevelStmt(p: var TParser): PNode = if p.tok.indent != 0: if p.firstTok and p.tok.indent < 0: discard elif p.tok.tokType != tkSemiColon: - parMessage(p, errInvalidIndentation) + # special casing for better error messages: + if p.tok.tokType == tkOpr and p.tok.ident.s == "*": + parMessage(p, errGenerated, + "invalid indentation; an export marker '*' follows the declared identifier") + else: + parMessage(p, errInvalidIndentation) p.firstTok = false case p.tok.tokType of tkSemiColon: diff --git a/compiler/semexprs.nim b/compiler/semexprs.nim index 8e3aeffbe..49beadc8b 100644 --- a/compiler/semexprs.nim +++ b/compiler/semexprs.nim @@ -192,6 +192,10 @@ proc semConv(c: PContext, n: PNode): PNode = result.addSon copyTree(n.sons[0]) + # special case to make MyObject(x = 3) produce a nicer error message: + if n[1].kind == nkExprEqExpr and + targetType.skipTypes(abstractPtrs).kind == tyObject: + localError(n.info, "object contruction uses ':', not '='") var op = semExprWithType(c, n.sons[1]) if targetType.isMetaType: let final = inferWithMetatype(c, targetType, op, true) diff --git a/compiler/semtypes.nim b/compiler/semtypes.nim index d097a373f..be6eac052 100644 --- a/compiler/semtypes.nim +++ b/compiler/semtypes.nim @@ -720,7 +720,8 @@ proc semObjectNode(c: PContext, n: PNode, prev: PType): PType = addInheritedFields(c, check, pos, concreteBase) else: if concreteBase.kind != tyError: - localError(n.sons[1].info, errInheritanceOnlyWithNonFinalObjects) + localError(n.sons[1].info, "inheritance only works with non-final objects; " & + "to enable inheritance write '" & typeToString(realBase) & " of RootObj'") base = nil realBase = nil if n.kind != nkObjectTy: internalError(n.info, "semObjectNode") -- cgit 1.4.1-2-gfad0 From 212fdc5946a5ba8f72c83b427987d17bd67218a1 Mon Sep 17 00:00:00 2001 From: Araq Date: Fri, 6 Apr 2018 22:05:47 +0200 Subject: added the 'x.p[:T]' notation for explicit generic instantiations in combination with the ddot calling syntax --- changelog.md | 3 +++ compiler/docgen.nim | 3 ++- compiler/lexer.nim | 7 +++-- compiler/parser.nim | 41 ++++++++++++++++++---------- doc/grammar.txt | 5 ++-- doc/manual/generics.txt | 64 ++++++++++++++++++++++---------------------- doc/manual/lexing.txt | 2 +- doc/manual/procs.txt | 9 ++++--- tests/parser/tprecedence.nim | 8 +++++- 9 files changed, 85 insertions(+), 57 deletions(-) (limited to 'compiler/parser.nim') diff --git a/changelog.md b/changelog.md index 3ede1ceea..7a817fd81 100644 --- a/changelog.md +++ b/changelog.md @@ -27,6 +27,9 @@ ### Language additions +- Dot calls combined with explicit generic instantiations can now be written + as ``x.y[:z]``. ``x.y[:z]`` that is transformed into ``y[z](x)`` in the parser. + ### Language changes - The `importcpp` pragma now allows importing the listed fields of generic diff --git a/compiler/docgen.nim b/compiler/docgen.nim index 65dcb73c9..67ff9edf1 100644 --- a/compiler/docgen.nim +++ b/compiler/docgen.nim @@ -257,7 +257,8 @@ proc nodeToHighlightedHtml(d: PDoc; n: PNode; result: var Rope; renderFlags: TRe tkBracketDotLe, tkBracketDotRi, tkParDotLe, tkParDotRi, tkComma, tkSemiColon, tkColon, tkEquals, tkDot, tkDotDot, tkAccent, tkColonColon, - tkGStrLit, tkGTripleStrLit, tkInfixOpr, tkPrefixOpr, tkPostfixOpr: + tkGStrLit, tkGTripleStrLit, tkInfixOpr, tkPrefixOpr, tkPostfixOpr, + tkBracketLeColon: dispA(result, "$1", "\\spanOther{$1}", [rope(esc(d.target, literal))]) diff --git a/compiler/lexer.nim b/compiler/lexer.nim index afa7642dc..e55da2f35 100644 --- a/compiler/lexer.nim +++ b/compiler/lexer.nim @@ -60,7 +60,7 @@ type tkCurlyDotLe, tkCurlyDotRi, # {. and .} tkParDotLe, tkParDotRi, # (. and .) tkComma, tkSemiColon, - tkColon, tkColonColon, tkEquals, tkDot, tkDotDot, + tkColon, tkColonColon, tkEquals, tkDot, tkDotDot, tkBracketLeColon, tkOpr, tkComment, tkAccent, tkSpaces, tkInfixOpr, tkPrefixOpr, tkPostfixOpr @@ -98,7 +98,7 @@ const "tkTripleStrLit", "tkGStrLit", "tkGTripleStrLit", "tkCharLit", "(", ")", "[", "]", "{", "}", "[.", ".]", "{.", ".}", "(.", ".)", ",", ";", - ":", "::", "=", ".", "..", + ":", "::", "=", ".", "..", "[:", "tkOpr", "tkComment", "`", "tkSpaces", "tkInfixOpr", "tkPrefixOpr", "tkPostfixOpr"] @@ -1119,6 +1119,9 @@ proc rawGetTok*(L: var TLexer, tok: var TToken) = if L.buf[L.bufpos] == '.' and L.buf[L.bufpos+1] != '.': tok.tokType = tkBracketDotLe inc(L.bufpos) + elif L.buf[L.bufpos] == ':': + tok.tokType = tkBracketLeColon + inc(L.bufpos) else: tok.tokType = tkBracketLe of ']': diff --git a/compiler/parser.nim b/compiler/parser.nim index debb0b34d..78a89f6c6 100644 --- a/compiler/parser.nim +++ b/compiler/parser.nim @@ -389,20 +389,6 @@ proc exprList(p: var TParser, endTok: TTokType, result: PNode) = getTok(p) optInd(p, a) -proc dotExpr(p: var TParser, a: PNode): PNode = - #| dotExpr = expr '.' optInd symbol - var info = p.parLineInfo - getTok(p) - result = newNodeI(nkDotExpr, info) - optInd(p, result) - addSon(result, a) - addSon(result, parseSymbol(p, smAfterDot)) - -proc qualifiedIdent(p: var TParser): PNode = - #| qualifiedIdent = symbol ('.' optInd symbol)? - result = parseSymbol(p) - if p.tok.tokType == tkDot: result = dotExpr(p, result) - proc exprColonEqExprListAux(p: var TParser, endTok: TTokType, result: PNode) = assert(endTok in {tkCurlyRi, tkCurlyDotRi, tkBracketRi, tkParRi}) getTok(p) @@ -423,6 +409,33 @@ proc exprColonEqExprList(p: var TParser, kind: TNodeKind, result = newNodeP(kind, p) exprColonEqExprListAux(p, endTok, result) +proc dotExpr(p: var TParser, a: PNode): PNode = + #| dotExpr = expr '.' optInd (symbol | '[:' exprList ']') + #| explicitGenericInstantiation = '[:' exprList ']' ( '(' exprColonEqExpr ')' )? + var info = p.parLineInfo + getTok(p) + result = newNodeI(nkDotExpr, info) + optInd(p, result) + addSon(result, a) + addSon(result, parseSymbol(p, smAfterDot)) + if p.tok.tokType == tkBracketLeColon and p.tok.strongSpaceA <= 0: + var x = newNodeI(nkBracketExpr, p.parLineInfo) + # rewrite 'x.y[:z]()' to 'y[z](x)' + x.add result[1] + exprList(p, tkBracketRi, x) + eat(p, tkBracketRi) + var y = newNodeI(nkCall, p.parLineInfo) + y.add x + y.add result[0] + if p.tok.tokType == tkParLe and p.tok.strongSpaceA <= 0: + exprColonEqExprListAux(p, tkParRi, y) + result = y + +proc qualifiedIdent(p: var TParser): PNode = + #| qualifiedIdent = symbol ('.' optInd symbol)? + result = parseSymbol(p) + if p.tok.tokType == tkDot: result = dotExpr(p, result) + proc setOrTableConstr(p: var TParser): PNode = #| setOrTableConstr = '{' ((exprColonEqExpr comma)* | ':' ) '}' result = newNodeP(nkCurly, p) diff --git a/doc/grammar.txt b/doc/grammar.txt index f25b079f4..e06ebd5d9 100644 --- a/doc/grammar.txt +++ b/doc/grammar.txt @@ -26,9 +26,10 @@ symbol = '`' (KEYW|IDENT|literal|(operator|'('|')'|'['|']'|'{'|'}'|'=')+)+ '`' | IDENT | KEYW exprColonEqExpr = expr (':'|'=' expr)? exprList = expr ^+ comma -dotExpr = expr '.' optInd symbol -qualifiedIdent = symbol ('.' optInd symbol)? exprColonEqExprList = exprColonEqExpr (comma exprColonEqExpr)* (comma)? +dotExpr = expr '.' optInd (symbol | '[:' exprList ']') +explicitGenericInstantiation = '[:' exprList ']' ( '(' exprColonEqExpr ')' )? +qualifiedIdent = symbol ('.' optInd symbol)? setOrTableConstr = '{' ((exprColonEqExpr comma)* | ':' ) '}' castExpr = 'cast' '[' optInd typeDesc optPar ']' '(' optInd expr optPar ')' parKeyw = 'discard' | 'include' | 'if' | 'while' | 'case' | 'try' diff --git a/doc/manual/generics.txt b/doc/manual/generics.txt index 4c908fefe..01f76b591 100644 --- a/doc/manual/generics.txt +++ b/doc/manual/generics.txt @@ -335,8 +335,8 @@ The concept types can be parametric just like the regular generic types: AnyTransform3D* = AnyMatrix[4, 4, float] proc transposed*(m: AnyMatrix): m.TransposedType = - for r in 0 .. `_. +The ``[: ]`` notation has been designed to mitigate this issue: ``x.p[:T]`` +is rewritten by the parser to ``p[T](x)``, ``x.p[:T](y)`` is rewritten to +``p[T](x, y)``. Note that ``[: ]`` has no AST representation, the rewrite +is performed directly in the parsing step. + Properties ---------- diff --git a/tests/parser/tprecedence.nim b/tests/parser/tprecedence.nim index d2c6d0b30..d586f14a3 100644 --- a/tests/parser/tprecedence.nim +++ b/tests/parser/tprecedence.nim @@ -1,6 +1,7 @@ discard """ output: '''holla -true''' +true +defabc 4''' """ # Test top level semicolon works properly: @@ -13,3 +14,8 @@ proc `\*` (x, y: int): int = result = x * y echo 5 \+ 1 \* 9 == 6*9 +proc foo[S, T](x: S, y: T): T = x & y + +proc bar[T](x: T): T = x + +echo "def".foo[:string, string]("abc"), " ", 4.bar[:int] -- cgit 1.4.1-2-gfad0 From d6793ded27537b51bbcc1dbdfacaf6f7655bf289 Mon Sep 17 00:00:00 2001 From: Oscar NihlgÄrd Date: Fri, 6 Apr 2018 22:44:54 +0200 Subject: Fix parser bug with type classes (#7480) --- compiler/parser.nim | 1 + tests/parser/ttypeclasses.nim | 17 +++++++++++++++++ 2 files changed, 18 insertions(+) create mode 100644 tests/parser/ttypeclasses.nim (limited to 'compiler/parser.nim') diff --git a/compiler/parser.nim b/compiler/parser.nim index 78a89f6c6..c14330ec2 100644 --- a/compiler/parser.nim +++ b/compiler/parser.nim @@ -1080,6 +1080,7 @@ proc parseTypeDescKAux(p: var TParser, kind: TNodeKind, #| distinct = 'distinct' optInd typeDesc result = newNodeP(kind, p) getTok(p) + if p.tok.indent != -1 and p.tok.indent <= p.currInd: return optInd(p, result) if not isOperator(p.tok) and isExprStart(p): addSon(result, primary(p, mode)) diff --git a/tests/parser/ttypeclasses.nim b/tests/parser/ttypeclasses.nim new file mode 100644 index 000000000..9f487c7a8 --- /dev/null +++ b/tests/parser/ttypeclasses.nim @@ -0,0 +1,17 @@ +discard """ + action: run +""" + +type + R = ref + V = var + D = distinct + P = ptr + +var x: ref int +var y: distinct int +var z: ptr int + +doAssert x is ref +doAssert y is distinct +doAssert z is ptr \ No newline at end of file -- cgit 1.4.1-2-gfad0 From 47335aab4148b2cd5b28cd3012d6d6e0a0c82db7 Mon Sep 17 00:00:00 2001 From: Andreas Rumpf Date: Fri, 13 Apr 2018 17:06:46 +0200 Subject: introduce nkTupleConstr AST node for unary tuple construction; breaking change --- changelog.md | 4 ++++ compiler/ast.nim | 3 ++- compiler/ccgexprs.nim | 4 ++-- compiler/dfa.nim | 2 +- compiler/evalffi.nim | 6 +++--- compiler/jsgen.nim | 8 ++++---- compiler/parser.nim | 6 ++++++ compiler/pragmas.nim | 4 ++-- compiler/renderer.nim | 8 ++++++++ compiler/sem.nim | 2 +- compiler/semexprs.nim | 20 +++++++++++--------- compiler/semfold.nim | 6 +++--- compiler/semmacrosanity.nim | 2 +- compiler/semmagic.nim | 2 +- compiler/semstmts.nim | 4 ++-- compiler/semtypes.nim | 5 +++-- compiler/transf.nim | 6 +++--- compiler/trees.nim | 2 +- compiler/vm.nim | 18 +++++++++--------- compiler/vmdeps.nim | 2 +- compiler/vmgen.nim | 6 +++--- compiler/vmmarshal.nim | 2 +- compiler/vmops.nim | 4 ++-- compiler/writetracking.nim | 4 ++-- lib/core/macros.nim | 3 ++- tests/tuples/tanontuples.nim | 14 +++++++++++++- 26 files changed, 91 insertions(+), 56 deletions(-) (limited to 'compiler/parser.nim') diff --git a/changelog.md b/changelog.md index ce2fc08f3..364047d05 100644 --- a/changelog.md +++ b/changelog.md @@ -5,6 +5,10 @@ - The stdlib module ``future`` has been renamed to ``sugar``. - ``macros.callsite`` is now deprecated. Since the introduction of ``varargs`` parameters this became unnecessary. +- Anonymous tuples with a single element can now be written as ``(1,)`` with a + trailing comma. The underlying AST is ``nnkTupleConst(newLit 1)`` for this + example. ``nnkTupleConstr`` is a new node kind your macros need to be able + to deal with! #### Breaking changes in the standard library diff --git a/compiler/ast.nim b/compiler/ast.nim index aa7250513..da7e828f2 100644 --- a/compiler/ast.nim +++ b/compiler/ast.nim @@ -221,7 +221,8 @@ type nkGotoState, # used for the state machine (for iterators) nkState, # give a label to a code section (for iterators) nkBreakState, # special break statement for easier code generation - nkFuncDef # a func + nkFuncDef, # a func + nkTupleConstr # a tuple constructor TNodeKinds* = set[TNodeKind] diff --git a/compiler/ccgexprs.nim b/compiler/ccgexprs.nim index 5888f6430..7e3c2632a 100644 --- a/compiler/ccgexprs.nim +++ b/compiler/ccgexprs.nim @@ -2226,7 +2226,7 @@ proc expr(p: BProc, n: PNode, d: var TLoc) = genSeqConstr(p, n, d) else: genArrayConstr(p, n, d) - of nkPar: + of nkPar, nkTupleConstr: if isDeepConstExpr(n) and n.len != 0: exprComplexConst(p, n, d) else: @@ -2458,7 +2458,7 @@ proc genConstExpr(p: BProc, n: PNode): Rope = var cs: TBitSet toBitSet(n, cs) result = genRawSetData(cs, int(getSize(n.typ))) - of nkBracket, nkPar, nkClosure: + of nkBracket, nkPar, nkTupleConstr, nkClosure: var t = skipTypes(n.typ, abstractInst) if t.kind == tySequence: result = genConstSeq(p, n, n.typ) diff --git a/compiler/dfa.nim b/compiler/dfa.nim index b648995f4..bc9d13870 100644 --- a/compiler/dfa.nim +++ b/compiler/dfa.nim @@ -323,7 +323,7 @@ proc gen(c: var Con; n: PNode) = of nkBreakStmt: genBreak(c, n) of nkTryStmt: genTry(c, n) of nkStmtList, nkStmtListExpr, nkChckRangeF, nkChckRange64, nkChckRange, - nkBracket, nkCurly, nkPar, nkClosure, nkObjConstr: + nkBracket, nkCurly, nkPar, nkTupleConstr, nkClosure, nkObjConstr: for x in n: gen(c, x) of nkPragmaBlock: gen(c, n.lastSon) of nkDiscardStmt: gen(c, n.sons[0]) diff --git a/compiler/evalffi.nim b/compiler/evalffi.nim index 5bf8f358a..0e3d0609d 100644 --- a/compiler/evalffi.nim +++ b/compiler/evalffi.nim @@ -151,7 +151,7 @@ proc getField(n: PNode; position: int): PSym = else: discard proc packObject(x: PNode, typ: PType, res: pointer) = - internalAssert x.kind in {nkObjConstr, nkPar} + internalAssert x.kind in {nkObjConstr, nkPar, nkTupleConstr} # compute the field's offsets: discard typ.getSize for i in countup(ord(x.kind == nkObjConstr), sonsLen(x) - 1): @@ -260,14 +260,14 @@ proc unpackObject(x: pointer, typ: PType, n: PNode): PNode = # iterate over any actual field of 'n' ... if n is nil we need to create # the nkPar node: if n.isNil: - result = newNode(nkPar) + result = newNode(nkTupleConstr) result.typ = typ if typ.n.isNil: internalError("cannot unpack unnamed tuple") unpackObjectAdd(x, typ.n, result) else: result = n - if result.kind notin {nkObjConstr, nkPar}: + if result.kind notin {nkObjConstr, nkPar, nkTupleConstr}: globalError(n.info, "cannot map value from FFI") if typ.n.isNil: globalError(n.info, "cannot unpack unnamed tuple") diff --git a/compiler/jsgen.nim b/compiler/jsgen.nim index dc74fa933..357708bb9 100644 --- a/compiler/jsgen.nim +++ b/compiler/jsgen.nim @@ -307,7 +307,7 @@ proc useMagic(p: PProc, name: string) = proc isSimpleExpr(p: PProc; n: PNode): bool = # calls all the way down --> can stay expression based - if n.kind in nkCallKinds+{nkBracketExpr, nkDotExpr, nkPar} or + if n.kind in nkCallKinds+{nkBracketExpr, nkDotExpr, nkPar, nkTupleConstr} or (p.target == targetJS and n.kind in {nkObjConstr, nkBracket, nkCurly}): for c in n: if not p.isSimpleExpr(c): return false @@ -894,7 +894,7 @@ proc countJsParams(typ: PType): int = const nodeKindsNeedNoCopy = {nkCharLit..nkInt64Lit, nkStrLit..nkTripleStrLit, - nkFloatLit..nkFloat64Lit, nkCurly, nkPar, nkObjConstr, nkStringToCString, + nkFloatLit..nkFloat64Lit, nkCurly, nkPar, nkTupleConstr, nkObjConstr, nkStringToCString, nkCStringToString, nkCall, nkPrefix, nkPostfix, nkInfix, nkCommand, nkHiddenCallConv, nkCallStrLit} @@ -1714,7 +1714,7 @@ proc genToArray(p: PProc; n: PNode; r: var TCompRes) = if x.kind == nkBracket: for i in countup(0, x.len - 1): let it = x[i] - if it.kind == nkPar and it.len == 2: + if it.kind in {nkPar, nkTupleConstr} and it.len == 2: if i > 0: r.res.add(", ") gen(p, it[0], a) gen(p, it[1], b) @@ -2309,7 +2309,7 @@ proc gen(p: PProc, n: PNode, r: var TCompRes) = of nkClosure: gen(p, n[0], r) of nkCurly: genSetConstr(p, n, r) of nkBracket: genArrayConstr(p, n, r) - of nkPar: genTupleConstr(p, n, r) + of nkPar, nkTupleConstr: genTupleConstr(p, n, r) of nkObjConstr: genObjConstr(p, n, r) of nkHiddenStdConv, nkHiddenSubConv, nkConv: genConv(p, n, r) of nkAddr, nkHiddenAddr: diff --git a/compiler/parser.nim b/compiler/parser.nim index c14330ec2..c8edd5136 100644 --- a/compiler/parser.nim +++ b/compiler/parser.nim @@ -399,6 +399,9 @@ proc exprColonEqExprListAux(p: var TParser, endTok: TTokType, result: PNode) = addSon(result, a) if p.tok.tokType != tkComma: break getTok(p) + # (1,) produces a tuple expression + if endTok == tkParRi and p.tok.tokType == tkParRi: + result.kind = nkTupleConstr skipComment(p, a) optPar(p) eat(p, endTok) @@ -566,6 +569,9 @@ proc parsePar(p: var TParser): PNode = if p.tok.tokType == tkComma: getTok(p) skipComment(p, a) + # (1,) produces a tuple expression: + if p.tok.tokType == tkParRi: + result.kind = nkTupleConstr # progress guaranteed while p.tok.tokType != tkParRi and p.tok.tokType != tkEof: var a = exprColonEqExpr(p) diff --git a/compiler/pragmas.nim b/compiler/pragmas.nim index d5fed7640..bb7801f6f 100644 --- a/compiler/pragmas.nim +++ b/compiler/pragmas.nim @@ -423,7 +423,7 @@ proc processCompile(c: PContext, n: PNode) = result = "" let it = if n.kind in nkPragmaCallKinds and n.len == 2: n.sons[1] else: n - if it.kind == nkPar and it.len == 2: + if it.kind in {nkPar, nkTupleConstr} and it.len == 2: let s = getStrLit(c, it, 0) let dest = getStrLit(c, it, 1) var found = parentDir(n.info.toFullPath) / s @@ -530,7 +530,7 @@ proc pragmaLine(c: PContext, n: PNode) = if n.kind in nkPragmaCallKinds and n.len == 2: n.sons[1] = c.semConstExpr(c, n.sons[1]) let a = n.sons[1] - if a.kind == nkPar: + if a.kind in {nkPar, nkTupleConstr}: # unpack the tuple var x = a.sons[0] var y = a.sons[1] diff --git a/compiler/renderer.nim b/compiler/renderer.nim index 0b1b0479f..7d513afb1 100644 --- a/compiler/renderer.nim +++ b/compiler/renderer.nim @@ -437,6 +437,9 @@ proc lsub(g: TSrcGen; n: PNode): int = of nkCommand: result = lsub(g, n.sons[0]) + lcomma(g, n, 1) + 1 of nkExprEqExpr, nkAsgn, nkFastAsgn: result = lsons(g, n) + 3 of nkPar, nkCurly, nkBracket, nkClosure: result = lcomma(g, n) + 2 + of nkTupleConstr: + # assume the trailing comma: + result = lcomma(g, n) + 3 of nkArgList: result = lcomma(g, n) of nkTableConstr: result = if n.len > 0: lcomma(g, n) + 2 else: len("{:}") @@ -1007,6 +1010,11 @@ proc gsub(g: var TSrcGen, n: PNode, c: TContext) = put(g, tkParLe, "(") gcomma(g, n, c) put(g, tkParRi, ")") + of nkTupleConstr: + put(g, tkParLe, "(") + gcomma(g, n, c) + if n.len == 1: put(g, tkComma, ",") + put(g, tkParRi, ")") of nkCurly: put(g, tkCurlyLe, "{") gcomma(g, n, c) diff --git a/compiler/sem.nim b/compiler/sem.nim index 937f1637a..4fef1bc60 100644 --- a/compiler/sem.nim +++ b/compiler/sem.nim @@ -85,7 +85,7 @@ proc fitNode(c: PContext, formal: PType, arg: PNode; info: TLineInfo): PNode = result.typ = formal else: let x = result.skipConv - if x.kind == nkPar and formal.kind != tyExpr: + if x.kind in {nkPar, nkTupleConstr} and formal.kind != tyExpr: changeType(x, formal, check=true) else: result = skipHiddenSubConv(result) diff --git a/compiler/semexprs.nim b/compiler/semexprs.nim index a523bfc9e..4256e0aa6 100644 --- a/compiler/semexprs.nim +++ b/compiler/semexprs.nim @@ -215,7 +215,7 @@ proc semConv(c: PContext, n: PNode): PNode = # handle SomeProcType(SomeGenericProc) if op.kind == nkSym and op.sym.isGenericRoutine: result.sons[1] = fitNode(c, result.typ, result.sons[1], result.info) - elif op.kind == nkPar and targetType.kind == tyTuple: + elif op.kind in {nkPar, nkTupleConstr} and targetType.kind == tyTuple: op = fitNode(c, targetType, op, result.info) of convNotNeedeed: message(n.info, hintConvFromXtoItselfNotNeeded, result.typ.typeToString) @@ -364,7 +364,7 @@ proc changeType(n: PNode, newType: PType, check: bool) = of nkCurly, nkBracket: for i in countup(0, sonsLen(n) - 1): changeType(n.sons[i], elemType(newType), check) - of nkPar: + of nkPar, nkTupleConstr: let tup = newType.skipTypes({tyGenericInst, tyAlias, tySink}) if tup.kind != tyTuple: if tup.kind == tyObject: return @@ -1402,7 +1402,7 @@ proc semAsgn(c: PContext, n: PNode; mode=asgnNormal): PNode = result = buildOverloadedSubscripts(n.sons[0], getIdent"{}=") add(result, n[1]) return semExprNoType(c, result) - of nkPar: + of nkPar, nkTupleConstr: if a.len >= 2: # unfortunately we need to rewrite ``(x, y) = foo()`` already here so # that overloading of the assignment operator still works. Usually we @@ -1521,10 +1521,10 @@ proc semYieldVarResult(c: PContext, n: PNode, restype: PType) = var e = skipTypes(t.sons[i], {tyGenericInst, tyAlias, tySink}) if e.kind in {tyVar, tyLent}: if e.kind == tyVar: e.flags.incl tfVarIsPtr # bugfix for #4048, #4910, #6892 - if n.sons[0].kind == nkPar: + if n.sons[0].kind in {nkPar, nkTupleConstr}: n.sons[0].sons[i] = takeImplicitAddr(c, n.sons[0].sons[i], e.kind == tyLent) elif n.sons[0].kind in {nkHiddenStdConv, nkHiddenSubConv} and - n.sons[0].sons[1].kind == nkPar: + n.sons[0].sons[1].kind in {nkPar, nkTupleConstr}: var a = n.sons[0].sons[1] a.sons[i] = takeImplicitAddr(c, a.sons[i], false) else: @@ -2047,12 +2047,12 @@ proc semTableConstr(c: PContext, n: PNode): PNode = var x = n.sons[i] if x.kind == nkExprColonExpr and sonsLen(x) == 2: for j in countup(lastKey, i-1): - var pair = newNodeI(nkPar, x.info) + var pair = newNodeI(nkTupleConstr, x.info) pair.add(n.sons[j]) pair.add(x[1]) result.add(pair) - var pair = newNodeI(nkPar, x.info) + var pair = newNodeI(nkTupleConstr, x.info) pair.add(x[0]) pair.add(x[1]) result.add(pair) @@ -2072,6 +2072,7 @@ proc checkPar(n: PNode): TParKind = result = paTuplePositions # () elif length == 1: if n.sons[0].kind == nkExprColonExpr: result = paTupleFields + elif n.kind == nkTupleConstr: result = paTuplePositions else: result = paSingle # (expr) else: if n.sons[0].kind == nkExprColonExpr: result = paTupleFields @@ -2088,7 +2089,7 @@ proc checkPar(n: PNode): TParKind = return paNone proc semTupleFieldsConstr(c: PContext, n: PNode, flags: TExprFlags): PNode = - result = newNodeI(nkPar, n.info) + result = newNodeI(nkTupleConstr, n.info) var typ = newTypeS(tyTuple, c) typ.n = newNodeI(nkRecList, n.info) # nkIdentDefs var ids = initIntSet() @@ -2113,6 +2114,7 @@ proc semTupleFieldsConstr(c: PContext, n: PNode, flags: TExprFlags): PNode = proc semTuplePositionsConstr(c: PContext, n: PNode, flags: TExprFlags): PNode = result = n # we don't modify n, but compute the type: + result.kind = nkTupleConstr var typ = newTypeS(tyTuple, c) # leave typ.n nil! for i in countup(0, sonsLen(n) - 1): n.sons[i] = semExprWithType(c, n.sons[i], flags*{efAllowDestructor}) @@ -2344,7 +2346,7 @@ proc semExpr(c: PContext, n: PNode, flags: TExprFlags = {}): PNode = invalidPragma(n) result = semExpr(c, n[0], flags) - of nkPar: + of nkPar, nkTupleConstr: case checkPar(n) of paNone: result = errorNode(c, n) of paTuplePositions: diff --git a/compiler/semfold.nim b/compiler/semfold.nim index 62bab4edb..096fc19e0 100644 --- a/compiler/semfold.nim +++ b/compiler/semfold.nim @@ -427,7 +427,7 @@ proc foldArrayAccess(m: PSym, n: PNode): PNode = var idx = getOrdValue(y) case x.kind - of nkPar: + of nkPar, nkTupleConstr: if idx >= 0 and idx < sonsLen(x): result = x.sons[int(idx)] if result.kind == nkExprColonExpr: result = result.sons[1] @@ -450,7 +450,7 @@ proc foldArrayAccess(m: PSym, n: PNode): PNode = proc foldFieldAccess(m: PSym, n: PNode): PNode = # a real field access; proc calls have already been transformed var x = getConstExpr(m, n.sons[0]) - if x == nil or x.kind notin {nkObjConstr, nkPar}: return + if x == nil or x.kind notin {nkObjConstr, nkPar, nkTupleConstr}: return var field = n.sons[1].sym for i in countup(ord(x.kind == nkObjConstr), sonsLen(x) - 1): @@ -624,7 +624,7 @@ proc getConstExpr(m: PSym, n: PNode): PNode = # if a == nil: return nil # result.sons[i].sons[1] = a # incl(result.flags, nfAllConst) - of nkPar: + of nkPar, nkTupleConstr: # tuple constructor result = copyTree(n) if (sonsLen(n) > 0) and (n.sons[0].kind == nkExprColonExpr): diff --git a/compiler/semmacrosanity.nim b/compiler/semmacrosanity.nim index fe9bb6c8d..f6df67441 100644 --- a/compiler/semmacrosanity.nim +++ b/compiler/semmacrosanity.nim @@ -50,7 +50,7 @@ proc annotateType*(n: PNode, t: PType) = else: internalAssert(n.sons[i].kind == nkExprColonExpr) annotateType(n.sons[i].sons[1], field.typ) - of nkPar: + of nkPar, nkTupleConstr: if x.kind == tyTuple: n.typ = t for i in 0 ..< n.len: diff --git a/compiler/semmagic.nim b/compiler/semmagic.nim index 9031e4640..3f0df0065 100644 --- a/compiler/semmagic.nim +++ b/compiler/semmagic.nim @@ -73,7 +73,7 @@ proc expectIntLit(c: PContext, n: PNode): int = else: localError(n.info, errIntLiteralExpected) proc semInstantiationInfo(c: PContext, n: PNode): PNode = - result = newNodeIT(nkPar, n.info, n.typ) + result = newNodeIT(nkTupleConstr, n.info, n.typ) let idx = expectIntLit(c, n.sons[1]) let useFullPaths = expectIntLit(c, n.sons[2]) let info = getInfoContext(idx) diff --git a/compiler/semstmts.nim b/compiler/semstmts.nim index c53ff9803..3de26344c 100644 --- a/compiler/semstmts.nim +++ b/compiler/semstmts.nim @@ -552,7 +552,7 @@ proc semVarOrLet(c: PContext, n: PNode, symkind: TSymKind): PNode = b.sons[length-2] = a.sons[length-2] # keep type desc for doc generator b.sons[length-1] = def addToVarSection(c, result, n, b) - elif tup.kind == tyTuple and def.kind == nkPar and + elif tup.kind == tyTuple and def.kind in {nkPar, nkTupleConstr} and a.kind == nkIdentDefs and a.len > 3: message(a.info, warnEachIdentIsTuple) @@ -592,7 +592,7 @@ proc semVarOrLet(c: PContext, n: PNode, symkind: TSymKind): PNode = addSon(b, copyTree(def)) addToVarSection(c, result, n, b) else: - if def.kind == nkPar: v.ast = def[j] + if def.kind in {nkPar, nkTupleConstr}: v.ast = def[j] setVarType(v, tup.sons[j]) b.sons[j] = newSymNode(v) checkNilable(v) diff --git a/compiler/semtypes.nim b/compiler/semtypes.nim index a23ee01e1..1fc263617 100644 --- a/compiler/semtypes.nim +++ b/compiler/semtypes.nim @@ -392,8 +392,8 @@ proc semAnonTuple(c: PContext, n: PNode, prev: PType): PType = if sonsLen(n) == 0: localError(n.info, errTypeExpected) result = newOrPrevType(tyTuple, prev, c) - for i in countup(0, sonsLen(n) - 1): - addSonSkipIntLit(result, semTypeNode(c, n.sons[i], nil)) + for it in n: + addSonSkipIntLit(result, semTypeNode(c, it, nil)) proc semTuple(c: PContext, n: PNode, prev: PType): PType = var typ: PType @@ -1341,6 +1341,7 @@ proc semTypeNode(c: PContext, n: PNode, prev: PType): PType = if sonsLen(n) == 1: result = semTypeNode(c, n.sons[0], prev) else: result = semAnonTuple(c, n, prev) + of nkTupleConstr: result = semAnonTuple(c, n, prev) of nkCallKinds: let x = n[0] let ident = case x.kind diff --git a/compiler/transf.nim b/compiler/transf.nim index e6dc69b38..f30f8583a 100644 --- a/compiler/transf.nim +++ b/compiler/transf.nim @@ -334,7 +334,7 @@ proc transformYield(c: PTransf, n: PNode): PTransNode = if skipTypes(e.typ, {tyGenericInst, tyAlias, tySink}).kind == tyTuple and c.transCon.forStmt.len != 3: e = skipConv(e) - if e.kind == nkPar: + if e.kind in {nkPar, nkTupleConstr}: for i in countup(0, sonsLen(e) - 1): var v = e.sons[i] if v.kind == nkExprColonExpr: v = v.sons[1] @@ -500,7 +500,7 @@ proc putArgInto(arg: PNode, formal: PType): TPutArgInto = case arg.kind of nkEmpty..nkNilLit: result = paDirectMapping - of nkPar, nkCurly, nkBracket: + of nkPar, nkTupleConstr, nkCurly, nkBracket: result = paFastAsgn for i in countup(0, sonsLen(arg) - 1): if putArgInto(arg.sons[i], formal) != paDirectMapping: return @@ -745,7 +745,7 @@ proc transformExceptBranch(c: PTransf, n: PNode): PTransNode = proc dontInlineConstant(orig, cnst: PNode): bool {.inline.} = # symbols that expand to a complex constant (array, etc.) should not be # inlined, unless it's the empty array: - result = orig.kind == nkSym and cnst.kind in {nkCurly, nkPar, nkBracket} and + result = orig.kind == nkSym and cnst.kind in {nkCurly, nkPar, nkTupleConstr, nkBracket} and cnst.len != 0 proc commonOptimizations*(c: PSym, n: PNode): PNode = diff --git a/compiler/trees.nim b/compiler/trees.nim index f69108942..fb523de9d 100644 --- a/compiler/trees.nim +++ b/compiler/trees.nim @@ -97,7 +97,7 @@ proc isDeepConstExpr*(n: PNode): bool = result = true of nkExprEqExpr, nkExprColonExpr, nkHiddenStdConv, nkHiddenSubConv: result = isDeepConstExpr(n.sons[1]) - of nkCurly, nkBracket, nkPar, nkObjConstr, nkClosure, nkRange: + of nkCurly, nkBracket, nkPar, nkTupleConstr, nkObjConstr, nkClosure, nkRange: for i in ord(n.kind == nkObjConstr) ..< n.len: if not isDeepConstExpr(n.sons[i]): return false if n.typ.isNil: result = true diff --git a/compiler/vm.nim b/compiler/vm.nim index 33c17eff4..7e2a171a2 100644 --- a/compiler/vm.nim +++ b/compiler/vm.nim @@ -418,14 +418,14 @@ proc setLenSeq(c: PCtx; node: PNode; newLen: int; info: TLineInfo) = let typ = node.typ.skipTypes(abstractInst+{tyRange}-{tyTypeDesc}) let typeEntry = typ.sons[0].skipTypes(abstractInst+{tyRange}-{tyTypeDesc}) let typeKind = case typeEntry.kind - of tyUInt..tyUInt64: nkUIntLit - of tyRange, tyEnum, tyBool, tyChar, tyInt..tyInt64: nkIntLit - of tyFloat..tyFloat128: nkFloatLit - of tyString: nkStrLit - of tyObject: nkObjConstr - of tySequence: nkNilLit - of tyProc, tyTuple: nkPar - else: nkEmpty + of tyUInt..tyUInt64: nkUIntLit + of tyRange, tyEnum, tyBool, tyChar, tyInt..tyInt64: nkIntLit + of tyFloat..tyFloat128: nkFloatLit + of tyString: nkStrLit + of tyObject: nkObjConstr + of tySequence: nkNilLit + of tyProc, tyTuple: nkTupleConstr + else: nkEmpty let oldLen = node.len setLen(node.sons, newLen) @@ -939,7 +939,7 @@ proc rawExecute(c: PCtx, start: int, tos: PStackFrame): TFullReg = let rb = instr.regB let rc = instr.regC let bb = regs[rb].node - let isClosure = bb.kind == nkPar + let isClosure = bb.kind == nkTupleConstr let prc = if not isClosure: bb.sym else: bb.sons[0].sym if prc.offset < -1: # it's a callback: diff --git a/compiler/vmdeps.nim b/compiler/vmdeps.nim index bb6c47324..071cc7706 100644 --- a/compiler/vmdeps.nim +++ b/compiler/vmdeps.nim @@ -186,7 +186,7 @@ proc mapTypeToAstX(t: PType; info: TLineInfo; if inst: # only named tuples have a node, unnamed tuples don't if t.n.isNil: - result = newNodeX(nkPar) + result = newNodeX(nkTupleConstr) for subType in t.sons: result.add mapTypeToAst(subType, info) else: diff --git a/compiler/vmgen.nim b/compiler/vmgen.nim index a8ecfd4ae..c3eaf8946 100644 --- a/compiler/vmgen.nim +++ b/compiler/vmgen.nim @@ -1560,7 +1560,7 @@ proc getNullValue(typ: PType, info: TLineInfo): PNode = if t.callConv != ccClosure: result = newNodeIT(nkNilLit, info, t) else: - result = newNodeIT(nkPar, info, t) + result = newNodeIT(nkTupleConstr, info, t) result.add(newNodeIT(nkNilLit, info, t)) result.add(newNodeIT(nkNilLit, info, t)) of tyObject: @@ -1577,7 +1577,7 @@ proc getNullValue(typ: PType, info: TLineInfo): PNode = for i in countup(0, int(lengthOrd(t)) - 1): addSon(result, getNullValue(elemType(t), info)) of tyTuple: - result = newNodeIT(nkPar, info, t) + result = newNodeIT(nkTupleConstr, info, t) for i in countup(0, sonsLen(t) - 1): addSon(result, getNullValue(t.sons[i], info)) of tySet: @@ -1884,7 +1884,7 @@ proc gen(c: PCtx; n: PNode; dest: var TDest; flags: TGenFlags = {}) = of nkBracket: genArrayConstr(c, n, dest) of nkCurly: genSetConstr(c, n, dest) of nkObjConstr: genObjConstr(c, n, dest) - of nkPar, nkClosure: genTupleConstr(c, n, dest) + of nkPar, nkClosure, nkTupleConstr: genTupleConstr(c, n, dest) of nkCast: if allowCast in c.features: genConv(c, n, n.sons[1], dest, opcCast) diff --git a/compiler/vmmarshal.nim b/compiler/vmmarshal.nim index 5f725994e..d76909443 100644 --- a/compiler/vmmarshal.nim +++ b/compiler/vmmarshal.nim @@ -190,7 +190,7 @@ proc loadAny(p: var JsonParser, t: PType, of tyTuple: if p.kind != jsonObjectStart: raiseParseErr(p, "'{' expected for an object") next(p) - result = newNode(nkPar) + result = newNode(nkTupleConstr) var i = 0 while p.kind != jsonObjectEnd and p.kind != jsonEof: if p.kind != jsonString: diff --git a/compiler/vmops.nim b/compiler/vmops.nim index f7debe2df..7f8bf06c1 100644 --- a/compiler/vmops.nim +++ b/compiler/vmops.nim @@ -74,13 +74,13 @@ proc getCurrentExceptionMsgWrapper(a: VmArgs) {.nimcall.} = proc staticWalkDirImpl(path: string, relative: bool): PNode = result = newNode(nkBracket) for k, f in walkDir(path, relative): - result.add newTree(nkPar, newIntNode(nkIntLit, k.ord), + result.add newTree(nkTupleConstr, newIntNode(nkIntLit, k.ord), newStrNode(nkStrLit, f)) proc gorgeExWrapper(a: VmArgs) {.nimcall.} = let (s, e) = opGorge(getString(a, 0), getString(a, 1), getString(a, 2), a.currentLineInfo) - setResult a, newTree(nkPar, newStrNode(nkStrLit, s), newIntNode(nkIntLit, e)) + setResult a, newTree(nkTupleConstr, newStrNode(nkStrLit, s), newIntNode(nkIntLit, e)) proc getProjectPathWrapper(a: VmArgs) {.nimcall.} = setResult a, gProjectPath diff --git a/compiler/writetracking.nim b/compiler/writetracking.nim index 577db613d..e03d6fb59 100644 --- a/compiler/writetracking.nim +++ b/compiler/writetracking.nim @@ -120,7 +120,7 @@ proc returnsNewExpr*(n: PNode): NewLocation = nkStmtList, nkStmtListExpr, nkBlockStmt, nkBlockExpr, nkOfBranch, nkElifBranch, nkElse, nkExceptBranch, nkFinally, nkCast: result = returnsNewExpr(n.lastSon) - of nkCurly, nkBracket, nkPar, nkObjConstr, nkClosure, + of nkCurly, nkBracket, nkPar, nkTupleConstr, nkObjConstr, nkClosure, nkIfExpr, nkIfStmt, nkWhenStmt, nkCaseStmt, nkTryStmt: result = newLit for i in ord(n.kind == nkObjConstr) ..< n.len: @@ -179,7 +179,7 @@ proc deps(w: var W; n: PNode) = for child in n: let last = lastSon(child) if last.kind == nkEmpty: continue - if child.kind == nkVarTuple and last.kind == nkPar: + if child.kind == nkVarTuple and last.kind in {nkPar, nkTupleConstr}: internalAssert child.len-2 == last.len for i in 0 .. child.len-3: deps(w, child.sons[i], last.sons[i], {}) diff --git a/lib/core/macros.nim b/lib/core/macros.nim index 3bdd29b0a..a4c819a34 100644 --- a/lib/core/macros.nim +++ b/lib/core/macros.nim @@ -82,7 +82,8 @@ type nnkGotoState, nnkState, nnkBreakState, - nnkFuncDef + nnkFuncDef, + nnkTupleConstr NimNodeKinds* = set[NimNodeKind] NimTypeKind* = enum # some types are no longer used, see ast.nim diff --git a/tests/tuples/tanontuples.nim b/tests/tuples/tanontuples.nim index 49803e5ac..f514670d3 100644 --- a/tests/tuples/tanontuples.nim +++ b/tests/tuples/tanontuples.nim @@ -1,7 +1,10 @@ discard """ - output: '''61, 125''' + output: '''61, 125 +(Field0: 0) (Field0: 13)''' """ +import macros + proc `^` (a, b: int): int = result = 1 for i in 1..b: result = result * a @@ -12,3 +15,12 @@ var n = (56, 3) m = (n[0] + m[1], m[1] ^ n[1]) echo m[0], ", ", m[1] + +# also test we can produce unary anon tuples in a macro: +macro mm(): untyped = + result = newTree(nnkTupleConstr, newLit(13)) + +proc nowTuple(): (int,) = + result = (0,) + +echo nowTuple(), " ", mm() -- cgit 1.4.1-2-gfad0 From a30b52eb6410bff3430c6a1786761a6ded4cd88d Mon Sep 17 00:00:00 2001 From: Andreas Rumpf Date: Sat, 14 Apr 2018 15:44:05 +0200 Subject: fixes #7610 --- compiler/parser.nim | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'compiler/parser.nim') diff --git a/compiler/parser.nim b/compiler/parser.nim index c8edd5136..0019b7acb 100644 --- a/compiler/parser.nim +++ b/compiler/parser.nim @@ -400,7 +400,7 @@ proc exprColonEqExprListAux(p: var TParser, endTok: TTokType, result: PNode) = if p.tok.tokType != tkComma: break getTok(p) # (1,) produces a tuple expression - if endTok == tkParRi and p.tok.tokType == tkParRi: + if endTok == tkParRi and p.tok.tokType == tkParRi and result.kind == nkPar: result.kind = nkTupleConstr skipComment(p, a) optPar(p) -- cgit 1.4.1-2-gfad0 From 9bc963508f29de8dd785a02b01f3159530a0ee2e Mon Sep 17 00:00:00 2001 From: Araq Date: Tue, 17 Apr 2018 11:09:23 +0200 Subject: nimpretty: next steps --- compiler/lexer.nim | 4 +++- compiler/msgs.nim | 18 ++++++++++++++++-- compiler/parser.nim | 8 +++++++- compiler/renderer.nim | 17 ++++++----------- tools/nimpretty.nim | 4 ++-- 5 files changed, 34 insertions(+), 17 deletions(-) (limited to 'compiler/parser.nim') diff --git a/compiler/lexer.nim b/compiler/lexer.nim index 1d9279d02..a4a2615bd 100644 --- a/compiler/lexer.nim +++ b/compiler/lexer.nim @@ -1080,7 +1080,9 @@ proc rawGetTok*(L: var TLexer, tok: var TToken) = tok.indent = -1 skip(L, tok) when defined(nimpretty): - if tok.tokType == tkComment: return + if tok.tokType == tkComment: + L.indentAhead = L.currLineIndent + return var c = L.buf[L.bufpos] tok.line = L.lineNumber tok.col = getColNumber(L, L.bufpos) diff --git a/compiler/msgs.nim b/compiler/msgs.nim index 70504cfc9..818ab0c05 100644 --- a/compiler/msgs.nim +++ b/compiler/msgs.nim @@ -495,7 +495,9 @@ type # and parsed; usually 'nil' but is used # for 'nimsuggest' hash*: string # the checksum of the file - + when defined(nimpretty): + fullContent*: string + FileIndex* = int32 # XXX will make this 'distinct' later TLineInfo* = object # This is designed to be as small as possible, # because it is used # in syntax nodes. We save space here by using @@ -503,7 +505,7 @@ type # On 64 bit and on 32 bit systems this is # only 8 bytes. line*, col*: int16 - fileIndex*: int32 + fileIndex*: FileIndex when defined(nimpretty): offsetA*, offsetB*: int commentOffsetA*, commentOffsetB*: int @@ -583,6 +585,18 @@ proc newFileInfo(fullPath, projPath: string): TFileInfo = result.quotedFullName = fullPath.makeCString if optEmbedOrigSrc in gGlobalOptions or true: result.lines = @[] + when defined(nimpretty): + if result.fullPath.len > 0: + try: + result.fullContent = readFile(result.fullPath) + except IOError: + #rawMessage(errCannotOpenFile, result.fullPath) + # XXX fixme + result.fullContent = "" + +when defined(nimpretty): + proc fileSection*(fid: FileIndex; a, b: int): string = + substr(fileInfos[fid].fullContent, a, b) proc fileInfoKnown*(filename: string): bool = var diff --git a/compiler/parser.nim b/compiler/parser.nim index 0019b7acb..3bf75c6f7 100644 --- a/compiler/parser.nim +++ b/compiler/parser.nim @@ -125,7 +125,13 @@ 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) + when defined(nimpretty): + if p.tok.commentOffsetB > p.tok.commentOffsetA: + add node.comment, fileSection(p.lex.fileIdx, p.tok.commentOffsetA, p.tok.commentOffsetB) + else: + add node.comment, p.tok.literal + else: + add(node.comment, p.tok.literal) else: parMessage(p, errInternal, "skipComment") getTok(p) diff --git a/compiler/renderer.nim b/compiler/renderer.nim index 7d513afb1..0e631a898 100644 --- a/compiler/renderer.nim +++ b/compiler/renderer.nim @@ -39,8 +39,7 @@ type inPragma: int when defined(nimpretty): pendingNewlineCount: int - origContent: string - + fid*: FileIndex # We render the source code in a two phases: The first # determines how long the subtree will likely be, the second @@ -354,13 +353,13 @@ proc ulitAux(g: TSrcGen; n: PNode, x: BiggestInt, size: int): string = proc atom(g: TSrcGen; n: PNode): string = when defined(nimpretty): let comment = if n.info.commentOffsetA < n.info.commentOffsetB: - " " & substr(g.origContent, n.info.commentOffsetA, n.info.commentOffsetB) + " " & fileSection(g.fid, n.info.commentOffsetA, n.info.commentOffsetB) else: "" if n.info.offsetA <= n.info.offsetB: # for some constructed tokens this can not be the case and we're better # off to not mess with the offset then. - return substr(g.origContent, n.info.offsetA, n.info.offsetB) & comment + return fileSection(g.fid, n.info.offsetA, n.info.offsetB) & comment var f: float32 case n.kind of nkEmpty: result = "" @@ -1460,17 +1459,13 @@ proc renderTree*(n: PNode, renderFlags: TRenderFlags = {}): string = proc `$`*(n: PNode): string = n.renderTree proc renderModule*(n: PNode, infile, outfile: string, - renderFlags: TRenderFlags = {}) = + renderFlags: TRenderFlags = {}; + fid = FileIndex(-1)) = var f: File g: TSrcGen initSrcGen(g, renderFlags) - when defined(nimpretty): - try: - g.origContent = readFile(infile) - except IOError: - rawMessage(errCannotOpenFile, infile) - + g.fid = fid for i in countup(0, sonsLen(n) - 1): gsub(g, n.sons[i]) optNL(g) diff --git a/tools/nimpretty.nim b/tools/nimpretty.nim index 36d1382cf..396f17b0b 100644 --- a/tools/nimpretty.nim +++ b/tools/nimpretty.nim @@ -24,7 +24,7 @@ const Usage: nimpretty [options] file.nim Options: - --backup:ON|OFF create a backup file before overwritting (default: ON) + --backup:on|off create a backup file before overwritting (default: ON) --version show the version --help show this help """ @@ -43,7 +43,7 @@ proc prettyPrint(infile: string) = let fileIdx = fileInfoIdx(infile) let tree = parseFile(fileIdx, newIdentCache()) let outfile = changeFileExt(infile, ".pretty.nim") - renderModule(tree, infile, outfile, {}) + renderModule(tree, infile, outfile, {}, fileIdx) proc main = var infile: string -- cgit 1.4.1-2-gfad0 From 33b69f0ed0272a4792322d9a0fbaffd5bef2f6e9 Mon Sep 17 00:00:00 2001 From: Andreas Rumpf Date: Sat, 21 Apr 2018 08:13:37 +0200 Subject: refactoring: make FileIndex a distinct type; make line information an uint16; fixes #7654 --- compiler/ast.nim | 10 ++-- compiler/ccgexprs.nim | 2 +- compiler/cgen.nim | 12 ++--- compiler/commands.nim | 2 +- compiler/docgen.nim | 10 ++-- compiler/filter_tmpl.nim | 6 +-- compiler/jsgen.nim | 2 +- compiler/lexer.nim | 18 +++---- compiler/main.nim | 4 +- compiler/modulegraphs.nim | 40 ++++++++------- compiler/modulepaths.nim | 2 +- compiler/modules.nim | 20 ++++---- compiler/msgs.nim | 107 ++++++++++++++++++++------------------- compiler/nimfix/pretty.nim | 10 ++-- compiler/nimfix/prettybase.nim | 16 +++--- compiler/parampatterns.nim | 3 +- compiler/parser.nim | 2 +- compiler/passes.nim | 24 ++------- compiler/pbraces.nim | 2 +- compiler/pragmas.nim | 2 +- compiler/reorder.nim | 20 ++++---- compiler/rod.nim | 112 ++--------------------------------------- compiler/rodread.nim | 47 +++++++++-------- compiler/rodwrite.nim | 10 ++-- compiler/semdata.nim | 2 +- compiler/semstmts.nim | 8 +-- compiler/suggest.nim | 4 +- compiler/syntaxes.nim | 4 +- compiler/vm.nim | 6 +-- 29 files changed, 196 insertions(+), 311 deletions(-) (limited to 'compiler/parser.nim') diff --git a/compiler/ast.nim b/compiler/ast.nim index da7e828f2..4a0a9a20b 100644 --- a/compiler/ast.nim +++ b/compiler/ast.nim @@ -1042,9 +1042,9 @@ proc newNode*(kind: TNodeKind): PNode = new(result) result.kind = kind #result.info = UnknownLineInfo() inlined: - result.info.fileIndex = int32(-1) + result.info.fileIndex = InvalidFileIdx result.info.col = int16(-1) - result.info.line = int16(-1) + result.info.line = uint16(0) when defined(useNodeIds): result.id = gNodeId if result.id == nodeIdToDebug: @@ -1116,13 +1116,13 @@ proc linkTo*(s: PSym, t: PType): PSym {.discardable.} = s.typ = t result = s -template fileIdx*(c: PSym): int32 = +template fileIdx*(c: PSym): FileIndex = # XXX: this should be used only on module symbols - c.position.int32 + c.position.FileIndex template filename*(c: PSym): string = # XXX: this should be used only on module symbols - c.position.int32.toFilename + c.position.FileIndex.toFilename proc appendToModule*(m: PSym, n: PNode) = ## The compiler will use this internally to add nodes that will be diff --git a/compiler/ccgexprs.nim b/compiler/ccgexprs.nim index 7e3c2632a..461a86298 100644 --- a/compiler/ccgexprs.nim +++ b/compiler/ccgexprs.nim @@ -2023,7 +2023,7 @@ template genStmtListExprImpl(exprOrStmt) {.dirty.} = let theMacro = it[0].sym add p.s(cpsStmts), initFrameNoDebug(p, frameName, makeCString theMacro.name.s, - theMacro.info.quotedFilename, it.info.line) + theMacro.info.quotedFilename, it.info.line.int) else: genStmts(p, it) if n.len > 0: exprOrStmt diff --git a/compiler/cgen.nim b/compiler/cgen.nim index 4d3cabd3a..9e1f9349f 100644 --- a/compiler/cgen.nim +++ b/compiler/cgen.nim @@ -223,7 +223,7 @@ proc genLineDir(p: BProc, t: PNode) = line.rope, makeCString(toFilename(tt.info))) elif ({optLineTrace, optStackTrace} * p.options == {optLineTrace, optStackTrace}) and - (p.prc == nil or sfPure notin p.prc.flags) and tt.info.fileIndex >= 0: + (p.prc == nil or sfPure notin p.prc.flags) and tt.info.fileIndex != InvalidFileIDX: if freshLineInfo(p, tt.info): linefmt(p, cpsStmts, "nimln_($1, $2);$n", line.rope, tt.info.quotedFilename) @@ -678,7 +678,7 @@ proc generateHeaders(m: BModule) = proc openNamespaceNim(): Rope = result.add("namespace Nim {" & tnl) - + proc closeNamespaceNim(): Rope = result.add("}" & tnl) @@ -1090,7 +1090,7 @@ proc genMainProc(m: BModule) = appcg(m, m.s[cfsProcs], nimMain, [m.g.mainModInit, initStackBottomCall, rope(m.labels)]) if optNoMain notin gGlobalOptions: - if useNimNamespace: + if useNimNamespace: m.s[cfsProcs].add closeNamespaceNim() & "using namespace Nim;" & tnl appcg(m, m.s[cfsProcs], otherMain, []) @@ -1202,7 +1202,7 @@ proc genModule(m: BModule, cfile: Cfile): Rope = add(result, genSectionStart(i)) add(result, m.s[i]) add(result, genSectionEnd(i)) - if useNimNamespace and i == cfsHeaders: result.add openNamespaceNim() + if useNimNamespace and i == cfsHeaders: result.add openNamespaceNim() add(result, m.s[cfsInitProc]) if useNimNamespace: result.add closeNamespaceNim() @@ -1301,7 +1301,7 @@ proc resetCgenModules*(g: BModuleList) = for m in cgenModules(g): resetModule(m) proc rawNewModule(g: BModuleList; module: PSym): BModule = - result = rawNewModule(g, module, module.position.int32.toFullPath) + result = rawNewModule(g, module, module.position.FileIndex.toFullPath) proc newModule(g: BModuleList; module: PSym): BModule = # we should create only one cgen module for each module sym @@ -1311,7 +1311,7 @@ proc newModule(g: BModuleList; module: PSym): BModule = if (optDeadCodeElim in gGlobalOptions): if (sfDeadCodeElim in module.flags): - internalError("added pending module twice: " & module.filename) + internalError("added pending module twice: " & toFilename(FileIndex module.position)) template injectG(config) {.dirty.} = if graph.backend == nil: diff --git a/compiler/commands.nim b/compiler/commands.nim index ec58706f3..e1dc1aacf 100644 --- a/compiler/commands.nim +++ b/compiler/commands.nim @@ -319,7 +319,7 @@ proc trackDirty(arg: string, info: TLineInfo) = localError(info, errInvalidNumber, a[2]) let dirtyOriginalIdx = a[1].fileInfoIdx - if dirtyOriginalIdx >= 0: + if dirtyOriginalIdx.int32 >= 0: msgs.setDirtyFile(dirtyOriginalIdx, a[0]) gTrackPos = newLineInfo(dirtyOriginalIdx, line, column) diff --git a/compiler/docgen.nim b/compiler/docgen.nim index 74fb305ac..6f3dcde8b 100644 --- a/compiler/docgen.nim +++ b/compiler/docgen.nim @@ -544,7 +544,7 @@ proc genJsonItem(d: PDoc, n, nameNode: PNode, k: TSymKind): JsonNode = initTokRender(r, n, {renderNoBody, renderNoComments, renderDocComments}) - result = %{ "name": %name, "type": %($k), "line": %n.info.line, + result = %{ "name": %name, "type": %($k), "line": %n.info.line.int, "col": %n.info.col} if comm != nil and comm != "": result["description"] = %comm @@ -618,7 +618,7 @@ proc generateJson*(d: PDoc, n: PNode) = of nkCommentStmt: if n.comment != nil and startsWith(n.comment, "##"): let stripped = n.comment.substr(2).strip - d.add %{ "comment": %stripped, "line": %n.info.line, + d.add %{ "comment": %stripped, "line": %n.info.line.int, "col": %n.info.col } of nkProcDef: when useEffectSystem: documentRaises(n) @@ -790,7 +790,7 @@ proc writeOutputJson*(d: PDoc, filename, outExt: string, discard "fixme: error report" proc commandDoc*() = - var ast = parseFile(gProjectMainIdx, newIdentCache()) + var ast = parseFile(gProjectMainIdx.FileIndex, newIdentCache()) if ast == nil: return var d = newDocumentor(gProjectFull, options.gConfigVars) d.hasToc = true @@ -840,7 +840,7 @@ proc commandRst2TeX*() = commandRstAux(gProjectFull, TexExt) proc commandJson*() = - var ast = parseFile(gProjectMainIdx, newIdentCache()) + var ast = parseFile(gProjectMainIdx.FileIndex, newIdentCache()) if ast == nil: return var d = newDocumentor(gProjectFull, options.gConfigVars) d.hasToc = true @@ -855,7 +855,7 @@ proc commandJson*() = writeRope(content, getOutFile(gProjectFull, JsonExt), useWarning = false) proc commandTags*() = - var ast = parseFile(gProjectMainIdx, newIdentCache()) + var ast = parseFile(gProjectMainIdx.FileIndex, newIdentCache()) if ast == nil: return var d = newDocumentor(gProjectFull, options.gConfigVars) d.hasToc = true diff --git a/compiler/filter_tmpl.nim b/compiler/filter_tmpl.nim index ca9a3a801..a1ba9113c 100644 --- a/compiler/filter_tmpl.nim +++ b/compiler/filter_tmpl.nim @@ -35,7 +35,7 @@ const proc newLine(p: var TTmplParser) = llStreamWrite(p.outp, repeat(')', p.emitPar)) p.emitPar = 0 - if p.info.line > int16(1): llStreamWrite(p.outp, "\n") + if p.info.line > uint16(1): llStreamWrite(p.outp, "\n") if p.pendingExprLine: llStreamWrite(p.outp, spaces(2)) p.pendingExprLine = false @@ -212,9 +212,9 @@ proc filterTmpl*(stdin: PLLStream, filename: string, call: PNode): PLLStream = p.x = newStringOfCap(120) # do not process the first line which contains the directive: if llStreamReadLine(p.inp, p.x): - p.info.line = p.info.line + int16(1) + p.info.line = p.info.line + 1'u16 while llStreamReadLine(p.inp, p.x): - p.info.line = p.info.line + int16(1) + p.info.line = p.info.line + 1'u16 parseLine(p) newLine(p) result = p.outp diff --git a/compiler/jsgen.nim b/compiler/jsgen.nim index 475508946..2ae3426ab 100644 --- a/compiler/jsgen.nim +++ b/compiler/jsgen.nim @@ -2261,7 +2261,7 @@ proc myClose(graph: ModuleGraph; b: PPassContext, n: PNode): PNode = var m = BModule(b) if sfMainModule in m.module.flags: let ext = "js" - let f = if globals.classes.len == 0: m.module.filename + let f = if globals.classes.len == 0: toFilename(FileIndex m.module.position) else: "nimsystem" let code = wholeCode(graph, m) let outfile = diff --git a/compiler/lexer.nim b/compiler/lexer.nim index a4a2615bd..0b1090bb1 100644 --- a/compiler/lexer.nim +++ b/compiler/lexer.nim @@ -133,7 +133,7 @@ type TErrorHandler* = proc (info: TLineInfo; msg: TMsgKind; arg: string) TLexer* = object of TBaseLexer - fileIdx*: int32 + fileIdx*: FileIndex indentAhead*: int # if > 0 an indendation has already been read # this is needed because scanning comments # needs so much look-ahead @@ -222,7 +222,7 @@ proc fillToken(L: var TToken) = L.commentOffsetA = 0 L.commentOffsetB = 0 -proc openLexer*(lex: var TLexer, fileIdx: int32, inputstream: PLLStream; +proc openLexer*(lex: var TLexer, fileIdx: FileIndex, inputstream: PLLStream; cache: IdentCache) = openBaseLexer(lex, inputstream) lex.fileIdx = fileidx @@ -274,7 +274,7 @@ template tokenEnd(tok, pos) {.dirty.} = when defined(nimsuggest): let colB = getColNumber(L, pos)+1 if L.fileIdx == gTrackPos.fileIndex and gTrackPos.col in colA..colB and - L.lineNumber == gTrackPos.line and gIdeCmd in {ideSug, ideCon}: + L.lineNumber == gTrackPos.line.int and gIdeCmd in {ideSug, ideCon}: L.cursor = CursorPosition.InToken gTrackPos.col = colA.int16 colA = 0 @@ -285,9 +285,9 @@ template tokenEndIgnore(tok, pos) = when defined(nimsuggest): let colB = getColNumber(L, pos) if L.fileIdx == gTrackPos.fileIndex and gTrackPos.col in colA..colB and - L.lineNumber == gTrackPos.line and gIdeCmd in {ideSug, ideCon}: + L.lineNumber == gTrackPos.line.int and gIdeCmd in {ideSug, ideCon}: gTrackPos.fileIndex = trackPosInvalidFileIdx - gTrackPos.line = -1 + gTrackPos.line = 0'u16 colA = 0 when defined(nimpretty): tok.offsetB = L.offsetBase + pos @@ -299,7 +299,7 @@ template tokenEndPrevious(tok, pos) = # the cursor in a string literal or comment: let colB = getColNumber(L, pos) if L.fileIdx == gTrackPos.fileIndex and gTrackPos.col in colA..colB and - L.lineNumber == gTrackPos.line and gIdeCmd in {ideSug, ideCon}: + L.lineNumber == gTrackPos.line.int and gIdeCmd in {ideSug, ideCon}: L.cursor = CursorPosition.BeforeToken gTrackPos = L.previousToken gTrackPosAttached = true @@ -1066,7 +1066,7 @@ proc rawGetTok*(L: var TLexer, tok: var TToken) = when defined(nimsuggest): # we attach the cursor to the last *strong* token if tok.tokType notin weakTokens: - L.previousToken.line = tok.line.int16 + L.previousToken.line = tok.line.uint16 L.previousToken.col = tok.col.int16 when defined(nimsuggest): @@ -1118,7 +1118,7 @@ proc rawGetTok*(L: var TLexer, tok: var TToken) = tok.tokType = tkParLe when defined(nimsuggest): if L.fileIdx == gTrackPos.fileIndex and tok.col < gTrackPos.col and - tok.line == gTrackPos.line and gIdeCmd == ideCon: + tok.line == gTrackPos.line.int and gIdeCmd == ideCon: gTrackPos.col = tok.col.int16 of ')': tok.tokType = tkParRi @@ -1139,7 +1139,7 @@ proc rawGetTok*(L: var TLexer, tok: var TToken) = of '.': when defined(nimsuggest): if L.fileIdx == gTrackPos.fileIndex and tok.col+1 == gTrackPos.col and - tok.line == gTrackPos.line and gIdeCmd == ideSug: + tok.line == gTrackPos.line.int and gIdeCmd == ideSug: tok.tokType = tkDot L.cursor = CursorPosition.InToken gTrackPos.col = tok.col.int16 diff --git a/compiler/main.nim b/compiler/main.nim index c928c81cd..401099fc3 100644 --- a/compiler/main.nim +++ b/compiler/main.nim @@ -35,7 +35,7 @@ proc writeDepsFile(g: ModuleGraph; project: string) = let f = open(changeFileExt(project, "deps"), fmWrite) for m in g.modules: if m != nil: - f.writeLine(toFullPath(m.position.int32)) + f.writeLine(toFullPath(m.position.FileIndex)) for k in g.inclToMod.keys: if g.getModule(k).isNil: # don't repeat includes which are also modules f.writeLine(k.toFullPath) @@ -265,7 +265,7 @@ proc mainCommand*(graph: ModuleGraph; cache: IdentCache) = of "parse": gCmd = cmdParse wantMainModule() - discard parseFile(gProjectMainIdx, cache) + discard parseFile(FileIndex gProjectMainIdx, cache) of "scan": gCmd = cmdScan wantMainModule() diff --git a/compiler/modulegraphs.nim b/compiler/modulegraphs.nim index 2c59a9097..6c14a46e8 100644 --- a/compiler/modulegraphs.nim +++ b/compiler/modulegraphs.nim @@ -25,7 +25,7 @@ ## - Its dependent module stays the same. ## -import ast, intsets, tables, options, rod +import ast, intsets, tables, options, rod, msgs, hashes type ModuleGraph* = ref object @@ -34,10 +34,10 @@ type deps*: IntSet # the dependency graph or potentially its transitive closure. suggestMode*: bool # whether we are in nimsuggest mode or not. invalidTransitiveClosure: bool - inclToMod*: Table[int32, int32] # mapping of include file to the - # first module that included it - importStack*: seq[int32] # The current import stack. Used for detecting recursive - # module dependencies. + inclToMod*: Table[FileIndex, FileIndex] # mapping of include file to the + # first module that included it + importStack*: seq[FileIndex] # The current import stack. Used for detecting recursive + # module dependencies. backend*: RootRef # minor hack so that a backend can extend this easily config*: ConfigRef doStopCompile*: proc(): bool {.closure.} @@ -45,6 +45,8 @@ type owners*: seq[PSym] methods*: seq[tuple[methods: TSymSeq, dispatcher: PSym]] +proc hash*(x: FileIndex): Hash {.borrow.} + {.this: g.} proc stopCompile*(g: ModuleGraph): bool {.inline.} = @@ -56,7 +58,7 @@ proc newModuleGraph*(config: ConfigRef = nil): ModuleGraph = result.deps = initIntSet() result.modules = @[] result.importStack = @[] - result.inclToMod = initTable[int32, int32]() + result.inclToMod = initTable[FileIndex, FileIndex]() if config.isNil: result.config = newConfigRef() else: @@ -69,35 +71,35 @@ proc resetAllModules*(g: ModuleGraph) = deps = initIntSet() modules = @[] importStack = @[] - inclToMod = initTable[int32, int32]() + inclToMod = initTable[FileIndex, FileIndex]() usageSym = nil owners = @[] methods = @[] -proc getModule*(g: ModuleGraph; fileIdx: int32): PSym = - if fileIdx >= 0 and fileIdx < modules.len: - result = modules[fileIdx] +proc getModule*(g: ModuleGraph; fileIdx: FileIndex): PSym = + if fileIdx.int32 >= 0 and fileIdx.int32 < modules.len: + result = modules[fileIdx.int32] proc dependsOn(a, b: int): int {.inline.} = (a shl 15) + b -proc addDep*(g: ModuleGraph; m: PSym, dep: int32) = - assert m.position == m.info.fileIndex +proc addDep*(g: ModuleGraph; m: PSym, dep: FileIndex) = + assert m.position == m.info.fileIndex.int32 addModuleDep(m.info.fileIndex, dep, isIncludeFile = false) if suggestMode: - deps.incl m.position.dependsOn(dep) + deps.incl m.position.dependsOn(dep.int) # we compute the transitive closure later when quering the graph lazily. # this improve efficiency quite a lot: #invalidTransitiveClosure = true -proc addIncludeDep*(g: ModuleGraph; module, includeFile: int32) = +proc addIncludeDep*(g: ModuleGraph; module, includeFile: FileIndex) = addModuleDep(module, includeFile, isIncludeFile = true) discard hasKeyOrPut(inclToMod, includeFile, module) -proc parentModule*(g: ModuleGraph; fileIdx: int32): int32 = +proc parentModule*(g: ModuleGraph; fileIdx: FileIndex): FileIndex = ## returns 'fileIdx' if the file belonging to this index is ## directly used as a module or else the module that first ## references this include file. - if fileIdx >= 0 and fileIdx < modules.len and modules[fileIdx] != nil: + if fileIdx.int32 >= 0 and fileIdx.int32 < modules.len and modules[fileIdx.int32] != nil: result = fileIdx else: result = inclToMod.getOrDefault(fileIdx) @@ -111,11 +113,11 @@ proc transitiveClosure(g: var IntSet; n: int) = if g.contains(i.dependsOn(k)) and g.contains(k.dependsOn(j)): g.incl i.dependsOn(j) -proc markDirty*(g: ModuleGraph; fileIdx: int32) = +proc markDirty*(g: ModuleGraph; fileIdx: FileIndex) = let m = getModule fileIdx if m != nil: incl m.flags, sfDirty -proc markClientsDirty*(g: ModuleGraph; fileIdx: int32) = +proc markClientsDirty*(g: ModuleGraph; fileIdx: FileIndex) = # we need to mark its dependent modules D as dirty right away because after # nimsuggest is done with this module, the module's dirty flag will be # cleared but D still needs to be remembered as 'dirty'. @@ -126,7 +128,7 @@ proc markClientsDirty*(g: ModuleGraph; fileIdx: int32) = # every module that *depends* on this file is also dirty: for i in 0i32..=% L: result = unknownLineInfo() else: result = msgContext[i] -template toFilename*(fileIdx: int32): string = - (if fileIdx < 0: "???" else: fileInfos[fileIdx].projPath) +template toFilename*(fileIdx: FileIndex): string = + (if fileIdx.int32 < 0: "???" else: fileInfos[fileIdx.int32].projPath) -proc toFullPath*(fileIdx: int32): string = - if fileIdx < 0: result = "???" - else: result = fileInfos[fileIdx].fullPath +proc toFullPath*(fileIdx: FileIndex): string = + if fileIdx.int32 < 0: result = "???" + else: result = fileInfos[fileIdx.int32].fullPath -proc setDirtyFile*(fileIdx: int32; filename: string) = - assert fileIdx >= 0 - fileInfos[fileIdx].dirtyFile = filename +proc setDirtyFile*(fileIdx: FileIndex; filename: string) = + assert fileIdx.int32 >= 0 + fileInfos[fileIdx.int32].dirtyFile = filename -proc setHash*(fileIdx: int32; hash: string) = - assert fileIdx >= 0 - shallowCopy(fileInfos[fileIdx].hash, hash) +proc setHash*(fileIdx: FileIndex; hash: string) = + assert fileIdx.int32 >= 0 + shallowCopy(fileInfos[fileIdx.int32].hash, hash) -proc getHash*(fileIdx: int32): string = - assert fileIdx >= 0 - shallowCopy(result, fileInfos[fileIdx].hash) +proc getHash*(fileIdx: FileIndex): string = + assert fileIdx.int32 >= 0 + shallowCopy(result, fileInfos[fileIdx.int32].hash) -proc toFullPathConsiderDirty*(fileIdx: int32): string = - if fileIdx < 0: +proc toFullPathConsiderDirty*(fileIdx: FileIndex): string = + if fileIdx.int32 < 0: result = "???" - elif not fileInfos[fileIdx].dirtyFile.isNil: - result = fileInfos[fileIdx].dirtyFile + elif not fileInfos[fileIdx.int32].dirtyFile.isNil: + result = fileInfos[fileIdx.int32].dirtyFile else: - result = fileInfos[fileIdx].fullPath + result = fileInfos[fileIdx.int32].fullPath template toFilename*(info: TLineInfo): string = info.fileIndex.toFilename @@ -762,15 +765,15 @@ template toFullPath*(info: TLineInfo): string = info.fileIndex.toFullPath proc toMsgFilename*(info: TLineInfo): string = - if info.fileIndex < 0: + if info.fileIndex.int32 < 0: result = "???" elif gListFullPaths: - result = fileInfos[info.fileIndex].fullPath + result = fileInfos[info.fileIndex.int32].fullPath else: - result = fileInfos[info.fileIndex].projPath + result = fileInfos[info.fileIndex.int32].projPath proc toLinenumber*(info: TLineInfo): int {.inline.} = - result = info.line + result = int info.line proc toColumn*(info: TLineInfo): int {.inline.} = result = info.col @@ -787,7 +790,7 @@ proc `??`* (info: TLineInfo, filename: string): bool = # only for debugging purposes result = filename in info.toFilename -const trackPosInvalidFileIdx* = -2 # special marker so that no suggestions +const trackPosInvalidFileIdx* = FileIndex(-2) # special marker so that no suggestions # are produced within comments and string literals var gTrackPos*: TLineInfo var gTrackPosAttached*: bool ## whether the tracking position was attached to some @@ -926,7 +929,7 @@ proc writeContext(lastinfo: TLineInfo) = else: styledMsgWriteln(styleBright, PosFormat % [toMsgFilename(msgContext[i]), - coordToStr(msgContext[i].line), + coordToStr(msgContext[i].line.int), coordToStr(msgContext[i].col+1)], resetStyle, getMessageStr(errInstantiationFrom, "")) @@ -994,7 +997,7 @@ proc formatMsg*(info: TLineInfo, msg: TMsgKind, arg: string): string = of warnMin..warnMax: WarningTitle of hintMin..hintMax: HintTitle else: ErrorTitle - result = PosFormat % [toMsgFilename(info), coordToStr(info.line), + result = PosFormat % [toMsgFilename(info), coordToStr(info.line.int), coordToStr(info.col+1)] & title & getMessageStr(msg, arg) @@ -1035,7 +1038,7 @@ proc liMessage(info: TLineInfo, msg: TMsgKind, arg: string, # NOTE: currently line info line numbers start with 1, # but column numbers start with 0, however most editors expect # first column to be 1, so we need to +1 here - let x = PosFormat % [toMsgFilename(info), coordToStr(info.line), + let x = PosFormat % [toMsgFilename(info), coordToStr(info.line.int), coordToStr(info.col+1)] let s = getMessageStr(msg, arg) @@ -1093,30 +1096,30 @@ template assertNotNil*(e): untyped = template internalAssert*(e: bool) = if not e: internalError($instantiationInfo()) -proc addSourceLine*(fileIdx: int32, line: string) = - fileInfos[fileIdx].lines.add line.rope +proc addSourceLine*(fileIdx: FileIndex, line: string) = + fileInfos[fileIdx.int32].lines.add line.rope proc sourceLine*(i: TLineInfo): Rope = - if i.fileIndex < 0: return nil + if i.fileIndex.int32 < 0: return nil - if not optPreserveOrigSource and fileInfos[i.fileIndex].lines.len == 0: + if not optPreserveOrigSource and fileInfos[i.fileIndex.int32].lines.len == 0: try: for line in lines(i.toFullPath): addSourceLine i.fileIndex, line.string except IOError: discard - internalAssert i.fileIndex < fileInfos.len + internalAssert i.fileIndex.int32 < fileInfos.len # can happen if the error points to EOF: - if i.line > fileInfos[i.fileIndex].lines.len: return nil + if i.line.int > fileInfos[i.fileIndex.int32].lines.len: return nil - result = fileInfos[i.fileIndex].lines[i.line-1] + result = fileInfos[i.fileIndex.int32].lines[i.line.int-1] proc quotedFilename*(i: TLineInfo): Rope = - internalAssert i.fileIndex >= 0 + internalAssert i.fileIndex.int32 >= 0 if optExcessiveStackTrace in gGlobalOptions: - result = fileInfos[i.fileIndex].quotedFullName + result = fileInfos[i.fileIndex.int32].quotedFullName else: - result = fileInfos[i.fileIndex].quotedName + result = fileInfos[i.fileIndex.int32].quotedName ropes.errorHandler = proc (err: RopesError, msg: string, useWarning: bool) = case err diff --git a/compiler/nimfix/pretty.nim b/compiler/nimfix/pretty.nim index 55603f4cd..4627264dc 100644 --- a/compiler/nimfix/pretty.nim +++ b/compiler/nimfix/pretty.nim @@ -28,7 +28,7 @@ proc overwriteFiles*() = let doStrip = options.getConfigVar("pretty.strip").normalize == "on" for i in 0 .. high(gSourceFiles): if gSourceFiles[i].dirty and not gSourceFiles[i].isNimfixFile and - (not gOnlyMainfile or gSourceFiles[i].fileIdx == gProjectMainIdx): + (not gOnlyMainfile or gSourceFiles[i].fileIdx == gProjectMainIdx.FileIndex): let newFile = if gOverWrite: gSourceFiles[i].fullpath else: gSourceFiles[i].fullpath.changeFileExt(".pretty.nim") try: @@ -95,7 +95,7 @@ proc beautifyName(s: string, k: TSymKind): string = proc replaceInFile(info: TLineInfo; newName: string) = loadFile(info) - let line = gSourceFiles[info.fileIndex].lines[info.line-1] + let line = gSourceFiles[info.fileIndex.int].lines[info.line.int-1] var first = min(info.col.int, line.len) if first < 0: return #inc first, skipIgnoreCase(line, "proc ", first) @@ -107,8 +107,8 @@ proc replaceInFile(info: TLineInfo; newName: string) = if differ(line, first, last, newName): # last-first+1 != newName.len or var x = line.substr(0, first-1) & newName & line.substr(last+1) - system.shallowCopy(gSourceFiles[info.fileIndex].lines[info.line-1], x) - gSourceFiles[info.fileIndex].dirty = true + system.shallowCopy(gSourceFiles[info.fileIndex.int].lines[info.line.int-1], x) + gSourceFiles[info.fileIndex.int].dirty = true proc checkStyle(info: TLineInfo, s: string, k: TSymKind; sym: PSym) = let beau = beautifyName(s, k) @@ -136,7 +136,7 @@ template styleCheckDef*(s: PSym) = styleCheckDef(s.info, s, s.kind) proc styleCheckUseImpl(info: TLineInfo; s: PSym) = - if info.fileIndex < 0: return + if info.fileIndex.int < 0: return # we simply convert it to what it looks like in the definition # for consistency diff --git a/compiler/nimfix/prettybase.nim b/compiler/nimfix/prettybase.nim index f1d24183b..ecb4b0093 100644 --- a/compiler/nimfix/prettybase.nim +++ b/compiler/nimfix/prettybase.nim @@ -16,13 +16,13 @@ type lines*: seq[string] dirty*, isNimfixFile*: bool fullpath*, newline*: string - fileIdx*: int32 + fileIdx*: FileIndex var gSourceFiles*: seq[TSourceFile] = @[] proc loadFile*(info: TLineInfo) = - let i = info.fileIndex + let i = info.fileIndex.int if i >= gSourceFiles.len: gSourceFiles.setLen(i+1) if gSourceFiles[i].lines.isNil: @@ -64,7 +64,7 @@ proc differ*(line: string, a, b: int, x: string): bool = proc replaceDeprecated*(info: TLineInfo; oldSym, newSym: PIdent) = loadFile(info) - let line = gSourceFiles[info.fileIndex].lines[info.line-1] + let line = gSourceFiles[info.fileIndex.int32].lines[info.line.int-1] var first = min(info.col.int, line.len) if first < 0: return #inc first, skipIgnoreCase(line, "proc ", first) @@ -75,8 +75,8 @@ proc replaceDeprecated*(info: TLineInfo; oldSym, newSym: PIdent) = let last = first+identLen(line, first)-1 if cmpIgnoreStyle(line[first..last], oldSym.s) == 0: var x = line.substr(0, first-1) & newSym.s & line.substr(last+1) - system.shallowCopy(gSourceFiles[info.fileIndex].lines[info.line-1], x) - gSourceFiles[info.fileIndex].dirty = true + system.shallowCopy(gSourceFiles[info.fileIndex.int32].lines[info.line.int-1], x) + gSourceFiles[info.fileIndex.int32].dirty = true #if newSym.s == "File": writeStackTrace() proc replaceDeprecated*(info: TLineInfo; oldSym, newSym: PSym) = @@ -85,10 +85,10 @@ proc replaceDeprecated*(info: TLineInfo; oldSym, newSym: PSym) = proc replaceComment*(info: TLineInfo) = loadFile(info) - let line = gSourceFiles[info.fileIndex].lines[info.line-1] + let line = gSourceFiles[info.fileIndex.int32].lines[info.line.int-1] var first = info.col.int if line[first] != '#': inc first var x = line.substr(0, first-1) & "discard " & line.substr(first+1).escape - system.shallowCopy(gSourceFiles[info.fileIndex].lines[info.line-1], x) - gSourceFiles[info.fileIndex].dirty = true + system.shallowCopy(gSourceFiles[info.fileIndex.int32].lines[info.line.int-1], x) + gSourceFiles[info.fileIndex.int32].dirty = true diff --git a/compiler/parampatterns.nim b/compiler/parampatterns.nim index 8540f1b32..02c48c16d 100644 --- a/compiler/parampatterns.nim +++ b/compiler/parampatterns.nim @@ -235,7 +235,8 @@ proc isAssignable*(owner: PSym, n: PNode; isUnsafeAddr=false): TAssignableResult result = arLValue else: result = isAssignable(owner, n.sons[0], isUnsafeAddr) - if result != arNone and sfDiscriminant in n.sons[1].sym.flags: + if result != arNone and n[1].kind == nkSym and + sfDiscriminant in n[1].sym.flags: result = arDiscriminant of nkBracketExpr: if skipTypes(n.sons[0].typ, abstractInst-{tyTypeDesc}).kind in diff --git a/compiler/parser.nim b/compiler/parser.nim index 3bf75c6f7..ac0a57770 100644 --- a/compiler/parser.nim +++ b/compiler/parser.nim @@ -83,7 +83,7 @@ proc getTok(p: var TParser) = rawGetTok(p.lex, p.tok) p.hasProgress = true -proc openParser*(p: var TParser, fileIdx: int32, inputStream: PLLStream, +proc openParser*(p: var TParser, fileIdx: FileIndex, inputStream: PLLStream, cache: IdentCache; strongSpaces=false) = ## Open a parser, using the given arguments to set up its internal state. diff --git a/compiler/passes.nim b/compiler/passes.nim index f079100ea..d7c181676 100644 --- a/compiler/passes.nim +++ b/compiler/passes.nim @@ -52,8 +52,8 @@ proc makePass*(open: TPassOpen = nil, # the semantic checker needs these: var - gImportModule*: proc (graph: ModuleGraph; m: PSym, fileIdx: int32; cache: IdentCache): PSym {.nimcall.} - gIncludeFile*: proc (graph: ModuleGraph; m: PSym, fileIdx: int32; cache: IdentCache): PNode {.nimcall.} + gImportModule*: proc (graph: ModuleGraph; m: PSym, fileIdx: FileIndex; cache: IdentCache): PSym {.nimcall.} + gIncludeFile*: proc (graph: ModuleGraph; m: PSym, fileIdx: FileIndex; cache: IdentCache): PNode {.nimcall.} # implementation @@ -63,20 +63,6 @@ proc skipCodegen*(n: PNode): bool {.inline.} = # error count instead. result = msgs.gErrorCounter > 0 -proc astNeeded*(s: PSym): bool = - # The ``rodwrite`` module uses this to determine if the body of a proc - # needs to be stored. The passes manager frees s.sons[codePos] when - # appropriate to free the procedure body's memory. This is important - # to keep memory usage down. - if (s.kind in {skMethod, skProc, skFunc}) and - ({sfCompilerProc, sfCompileTime} * s.flags == {}) and - (s.typ.callConv != ccInline) and - (s.ast.sons[genericParamsPos].kind == nkEmpty): - result = false - # XXX this doesn't really make sense with excessive CTFE - else: - result = true - const maxPasses = 10 @@ -153,7 +139,7 @@ proc closePassesCached(graph: ModuleGraph; a: var TPassContextArray) = m = gPasses[i].close(graph, a[i], m) a[i] = nil # free the memory here -proc resolveMod(module, relativeTo: string): int32 = +proc resolveMod(module, relativeTo: string): FileIndex = let fullPath = findModule(module, relativeTo) if fullPath.len == 0: result = InvalidFileIDX @@ -166,7 +152,7 @@ proc processImplicits(implicits: seq[string], nodeKind: TNodeKind, let relativeTo = m.info.toFullPath for module in items(implicits): # implicit imports should not lead to a module importing itself - if m.position != resolveMod(module, relativeTo): + if m.position != resolveMod(module, relativeTo).int32: var importStmt = newNodeI(nodeKind, gCmdLineInfo) var str = newStrNode(nkStrLit, module) str.info = gCmdLineInfo @@ -180,7 +166,7 @@ proc processModule*(graph: ModuleGraph; module: PSym, stream: PLLStream, p: TParsers a: TPassContextArray s: PLLStream - fileIdx = module.fileIdx + fileIdx = FileIndex module.fileIdx if module.id < 0: # new module caching mechanism: for i in 0..= gMods.len: setLen(gMods, fileIdx+1) - gMods[fileIdx].hash = result + if fileIdx.int32 >= gMods.len: setLen(gMods, fileIdx.int32+1) + gMods[fileIdx.int32].hash = result template growCache*(cache, pos) = if cache.len <= pos: cache.setLen(pos+1) -proc checkDep(fileIdx: int32; cache: IdentCache): TReasonForRecompile = +proc checkDep(fileIdx: FileIndex; cache: IdentCache): TReasonForRecompile = assert fileIdx != InvalidFileIDX - growCache gMods, fileIdx - if gMods[fileIdx].reason != rrEmpty: + growCache gMods, fileIdx.int32 + if gMods[fileIdx.int32].reason != rrEmpty: # reason has already been computed for this module: - return gMods[fileIdx].reason + return gMods[fileIdx.int32].reason let filename = fileIdx.toFilename var hash = getHash(fileIdx) - gMods[fileIdx].reason = rrNone # we need to set it here to avoid cycles + gMods[fileIdx.int32].reason = rrNone # we need to set it here to avoid cycles result = rrNone var rodfile = toGeneratedFile(filename.withPackageName, RodExt) - var r = newRodReader(rodfile, hash, fileIdx, cache) + var r = newRodReader(rodfile, hash, fileIdx.int32, cache) if r == nil: result = (if existsFile(rodfile): rrRodInvalid else: rrRodDoesNotExist) else: @@ -907,19 +907,18 @@ proc checkDep(fileIdx: int32; cache: IdentCache): TReasonForRecompile = # recompilation is necessary: if r != nil: memfiles.close(r.memfile) r = nil - gMods[fileIdx].rd = r - gMods[fileIdx].reason = result # now we know better + gMods[fileIdx.int32].rd = r + gMods[fileIdx.int32].reason = result # now we know better proc handleSymbolFile*(module: PSym; cache: IdentCache): PRodReader = - let fileIdx = module.fileIdx if gSymbolFiles in {disabledSf, writeOnlySf, v2Sf}: module.id = getID() return nil idgen.loadMaxIds(options.gProjectPath / options.gProjectName) - + let fileIdx = FileIndex module.fileIdx discard checkDep(fileIdx, cache) - if gMods[fileIdx].reason == rrEmpty: internalError("handleSymbolFile") - result = gMods[fileIdx].rd + if gMods[fileIdx.int32].reason == rrEmpty: internalError("handleSymbolFile") + result = gMods[fileIdx.int32].rd if result != nil: module.id = result.moduleID result.syms[module.id] = module @@ -1153,7 +1152,7 @@ proc viewFile(rodfile: string) = if r.s[r.pos] == '\x0A': inc(r.pos) inc(r.line) - outf.write(w, " ", inclHash, "\n") + outf.write(w.int32, " ", inclHash, "\n") if r.s[r.pos] == ')': inc(r.pos) outf.write(")\n") of "DEPS": @@ -1163,7 +1162,7 @@ proc viewFile(rodfile: string) = let v = int32(decodeVInt(r.s, r.pos)) r.modDeps.add(r.files[v]) if r.s[r.pos] == ' ': inc(r.pos) - outf.write(" ", r.files[v]) + outf.write(" ", r.files[v].int32) outf.write("\n") of "INTERF", "COMPILERPROCS": inc r.pos, 2 diff --git a/compiler/rodwrite.nim b/compiler/rodwrite.nim index 96deb1d5a..0ae687268 100644 --- a/compiler/rodwrite.nim +++ b/compiler/rodwrite.nim @@ -55,7 +55,7 @@ proc fileIdx(w: PRodWriter, filename: string): int = w.files[result] = filename template filename*(w: PRodWriter): string = - w.module.filename + toFilename(FileIndex w.module.position) proc newRodWriter(hash: SecureHash, module: PSym; cache: IdentCache): PRodWriter = new(result) @@ -125,14 +125,14 @@ proc encodeNode(w: PRodWriter, fInfo: TLineInfo, n: PNode, result.add('?') encodeVInt(n.info.col, result) result.add(',') - encodeVInt(n.info.line, result) + encodeVInt(int n.info.line, result) result.add(',') encodeVInt(fileIdx(w, toFullPath(n.info)), result) elif fInfo.line != n.info.line: result.add('?') encodeVInt(n.info.col, result) result.add(',') - encodeVInt(n.info.line, result) + encodeVInt(int n.info.line, result) elif fInfo.col != n.info.col: result.add('?') encodeVInt(n.info.col, result) @@ -303,7 +303,7 @@ proc encodeSym(w: PRodWriter, s: PSym, result: var string) = result.add('?') if s.info.col != -1'i16: encodeVInt(s.info.col, result) result.add(',') - if s.info.line != -1'i16: encodeVInt(s.info.line, result) + if s.info.line != 0'u16: encodeVInt(int s.info.line, result) result.add(',') encodeVInt(fileIdx(w, toFullPath(s.info)), result) if s.owner != nil: @@ -642,7 +642,7 @@ proc process(c: PPassContext, n: PNode): PNode = proc myOpen(g: ModuleGraph; module: PSym; cache: IdentCache): PPassContext = if module.id < 0: internalError("rodwrite: module ID not set") - var w = newRodWriter(rodread.getHash module.fileIdx, module, cache) + var w = newRodWriter(rodread.getHash FileIndex module.position, module, cache) rawAddInterfaceSym(w, module) result = w diff --git a/compiler/semdata.nim b/compiler/semdata.nim index 3996188dc..bcc1bba15 100644 --- a/compiler/semdata.nim +++ b/compiler/semdata.nim @@ -144,7 +144,7 @@ proc makeInstPair*(s: PSym, inst: PInstantiation): TInstantiationPair = proc filename*(c: PContext): string = # the module's filename - return c.module.filename + return toFilename(FileIndex c.module.position) proc scopeDepth*(c: PContext): int {.inline.} = result = if c.currentScope != nil: c.currentScope.depthLevel diff --git a/compiler/semstmts.nim b/compiler/semstmts.nim index 75d8cc2e0..8d7747fb4 100644 --- a/compiler/semstmts.nim +++ b/compiler/semstmts.nim @@ -1095,12 +1095,12 @@ proc semAllTypeSections(c: PContext; n: PNode): PNode = for i in 0.. 0: suggestQuit() proc suggestSentinel*(c: PContext) = - if gIdeCmd != ideSug or c.module.position != gTrackPos.fileIndex: return + if gIdeCmd != ideSug or c.module.position != gTrackPos.fileIndex.int32: return if c.compilesContextId > 0: return inc(c.compilesContextId) var outputs: Suggestions = @[] diff --git a/compiler/syntaxes.nim b/compiler/syntaxes.nim index 4745b1ac7..4014d4c58 100644 --- a/compiler/syntaxes.nim +++ b/compiler/syntaxes.nim @@ -138,7 +138,7 @@ proc evalPipe(p: var TParsers, n: PNode, filename: string, else: result = applyFilter(p, n, filename, result) -proc openParsers*(p: var TParsers, fileIdx: int32, inputstream: PLLStream; +proc openParsers*(p: var TParsers, fileIdx: FileIndex, inputstream: PLLStream; cache: IdentCache) = var s: PLLStream p.skin = skinStandard @@ -155,7 +155,7 @@ proc openParsers*(p: var TParsers, fileIdx: int32, inputstream: PLLStream; proc closeParsers*(p: var TParsers) = parser.closeParser(p.parser) -proc parseFile*(fileIdx: int32; cache: IdentCache): PNode {.procvar.} = +proc parseFile*(fileIdx: FileIndex; cache: IdentCache): PNode {.procvar.} = var p: TParsers f: File diff --git a/compiler/vm.nim b/compiler/vm.nim index 5b5ccdce4..70f79c433 100644 --- a/compiler/vm.nim +++ b/compiler/vm.nim @@ -1381,7 +1381,7 @@ proc rawExecute(c: PCtx, start: int, tos: PStackFrame): TFullReg = of opcNGetLine: decodeB(rkNode) let n = regs[rb].node - regs[ra].node = newIntNode(nkIntLit, n.info.line) + regs[ra].node = newIntNode(nkIntLit, n.info.line.int) regs[ra].node.info = n.info regs[ra].node.typ = n.typ of opcNGetColumn: @@ -1521,7 +1521,7 @@ proc rawExecute(c: PCtx, start: int, tos: PStackFrame): TFullReg = let x = newNodeI(TNodeKind(int(k)), if cc.kind != nkNilLit: cc.info - elif c.comesFromHeuristic.line > -1: + elif c.comesFromHeuristic.line != 0'u16: c.comesFromHeuristic elif c.callsite != nil and c.callsite.safeLen > 1: c.callsite[1].info @@ -1748,7 +1748,7 @@ proc evalMacroCall*(module: PSym; cache: IdentCache, n, nOrig: PNode, setupGlobalCtx(module, cache) var c = globalCtx - c.comesFromHeuristic.line = -1 + c.comesFromHeuristic.line = 0'u16 c.callsite = nOrig let start = genProc(c, sym) -- cgit 1.4.1-2-gfad0 From 805402b294c6cb3ee8e0f39245aa2d1366ae76e9 Mon Sep 17 00:00:00 2001 From: Andreas Rumpf Date: Sat, 5 May 2018 14:17:31 +0200 Subject: fixes #7582 --- compiler/parser.nim | 5 +++-- tests/parser/tcommand_as_expr.nim | 13 ++++++++++++- tests/parser/twrongcmdsyntax.nim | 2 +- 3 files changed, 16 insertions(+), 4 deletions(-) (limited to 'compiler/parser.nim') diff --git a/compiler/parser.nim b/compiler/parser.nim index ac0a57770..d7a1a75e3 100644 --- a/compiler/parser.nim +++ b/compiler/parser.nim @@ -744,8 +744,9 @@ proc primarySuffix(p: var TParser, r: PNode, baseIndent: int): PNode = # progress guaranteed somePar() result = namedParams(p, result, nkCurlyExpr, tkCurlyRi) - of tkSymbol, tkAccent, tkIntLit..tkCharLit, tkNil, tkCast, tkAddr, tkType: - if p.inPragma == 0: + of tkSymbol, tkAccent, tkIntLit..tkCharLit, tkNil, tkCast, tkAddr, tkType, + tkOpr, tkDotDot: + if p.inPragma == 0 and (isUnary(p) or p.tok.tokType notin {tkOpr, tkDotDot}): # actually parsing {.push hints:off.} as {.push(hints:off).} is a sweet # solution, but pragmas.nim can't handle that let a = result diff --git a/tests/parser/tcommand_as_expr.nim b/tests/parser/tcommand_as_expr.nim index a244c8767..b25ec4bd8 100644 --- a/tests/parser/tcommand_as_expr.nim +++ b/tests/parser/tcommand_as_expr.nim @@ -2,7 +2,10 @@ discard """ output: '''140 5-120-120 359 -77''' +77 +-4 +-1 +-1''' """ #import math import sequtils @@ -25,3 +28,11 @@ let a = [2,4,8].map do (d:int) -> int: d + 1 echo a[0], a[1], a[2] echo(foo 8, foo 8) + +# bug #7582 +proc f(x: int): int = x + +echo f -4 + +echo int -1 # doesn't compile +echo int `-` 1 # compiles diff --git a/tests/parser/twrongcmdsyntax.nim b/tests/parser/twrongcmdsyntax.nim index affe72c34..c2962bed4 100644 --- a/tests/parser/twrongcmdsyntax.nim +++ b/tests/parser/twrongcmdsyntax.nim @@ -1,5 +1,5 @@ discard """ - errormsg: '''identifier expected, but found 'echo 4''' + errormsg: '''in expression '4 2': identifier expected, but found '4''' line: 6 """ -- cgit 1.4.1-2-gfad0 From 1aa359febba7c666c3a0108d6362ac1c51cfaf4e Mon Sep 17 00:00:00 2001 From: Andreas Rumpf Date: Sat, 5 May 2018 14:58:33 +0200 Subject: warn about inconsistent spacing around binary operators; fixes #7582 --- changelog.md | 5 ++++- compiler/msgs.nim | 6 ++++-- compiler/parser.nim | 8 ++------ compiler/plugins/locals/locals.nim | 2 +- compiler/renderer.nim | 2 +- compiler/semstmts.nim | 2 +- compiler/vmgen.nim | 2 +- lib/pure/algorithm.nim | 2 +- lib/system/assign.nim | 12 ++++++------ lib/system/deepcopy.nim | 10 +++++----- lib/system/gc.nim | 2 +- lib/system/osalloc.nim | 2 +- 12 files changed, 28 insertions(+), 27 deletions(-) (limited to 'compiler/parser.nim') diff --git a/changelog.md b/changelog.md index 6cd0faf52..b50bedb5a 100644 --- a/changelog.md +++ b/changelog.md @@ -17,6 +17,9 @@ - The ``not nil`` type annotation now has to be enabled explicitly via ``{.experimental: "notnil"}`` as we are still not pleased with how this feature works with Nim's containers. +- The parser now warns about inconsistent spacing around binary operators as + these can easily be confused with unary operators. This warning will likely + become an error in the future. #### Breaking changes in the standard library @@ -89,7 +92,7 @@ - Accessing the binary zero terminator in Nim's native strings is now invalid. Internally a Nim string still has the trailing zero for zero-copy interoperability with ``cstring``. Compile your code with the - next switch ``--laxStrings:on`` if you need a transition period. + new switch ``--laxStrings:on`` if you need a transition period. ### Tool changes diff --git a/compiler/msgs.nim b/compiler/msgs.nim index 5da375c1c..088742d18 100644 --- a/compiler/msgs.nim +++ b/compiler/msgs.nim @@ -131,7 +131,7 @@ type warnEachIdentIsTuple, warnShadowIdent, warnProveInit, warnProveField, warnProveIndex, warnGcUnsafe, warnGcUnsafe2, warnUninit, warnGcMem, warnDestructor, warnLockLevel, warnResultShadowed, - warnUser, + warnInconsistentSpacing, warnUser, hintSuccess, hintSuccessX, hintLineTooLong, hintXDeclaredButNotUsed, hintConvToBaseNotNeeded, hintConvFromXtoItselfNotNeeded, hintExprAlwaysX, hintQuitCalled, @@ -416,6 +416,7 @@ const warnDestructor: "usage of a type with a destructor in a non destructible context. This will become a compile time error in the future.", warnLockLevel: "$1", warnResultShadowed: "Special variable 'result' is shadowed.", + warnInconsistentSpacing: "Number of spaces around '$#' is not consistent", warnUser: "$1", hintSuccess: "operation successful", hintSuccessX: "operation successful ($# lines compiled; $# sec total; $#; $#)", @@ -454,7 +455,8 @@ const "TypelessParam", "UseBase", "WriteToForeignHeap", "UnsafeCode", "EachIdentIsTuple", "ShadowIdent", "ProveInit", "ProveField", "ProveIndex", "GcUnsafe", "GcUnsafe2", "Uninit", - "GcMem", "Destructor", "LockLevel", "ResultShadowed", "User"] + "GcMem", "Destructor", "LockLevel", "ResultShadowed", + "Spacing", "User"] HintsToStr* = ["Success", "SuccessX", "LineTooLong", "XDeclaredButNotUsed", "ConvToBaseNotNeeded", "ConvFromXtoItselfNotNeeded", diff --git a/compiler/parser.nim b/compiler/parser.nim index d7a1a75e3..c61519541 100644 --- a/compiler/parser.nim +++ b/compiler/parser.nim @@ -268,13 +268,9 @@ proc isUnary(p: TParser): bool = proc checkBinary(p: TParser) {.inline.} = ## Check if the current parser token is a binary operator. # we don't check '..' here as that's too annoying - if p.strongSpaces and p.tok.tokType == tkOpr: + if p.tok.tokType == tkOpr: if p.tok.strongSpaceB > 0 and p.tok.strongSpaceA != p.tok.strongSpaceB: - parMessage(p, errGenerated, - "Number of spaces around '$#' not consistent" % - prettyTok(p.tok)) - elif p.tok.strongSpaceA notin {0,1,2,4,8}: - parMessage(p, errGenerated, "Number of spaces must be 0,1,2,4 or 8") + parMessage(p, warnInconsistentSpacing, prettyTok(p.tok)) #| module = stmt ^* (';' / IND{=}) #| diff --git a/compiler/plugins/locals/locals.nim b/compiler/plugins/locals/locals.nim index 9cbb61186..ff7f3be58 100644 --- a/compiler/plugins/locals/locals.nim +++ b/compiler/plugins/locals/locals.nim @@ -9,7 +9,7 @@ ## The builtin 'system.locals' implemented as a plugin. -import "../../" / [pluginsupport, ast, astalgo, +import "../../" / [pluginsupport, ast, astalgo, magicsys, lookups, semdata, lowerings] proc semLocals(c: PContext, n: PNode): PNode = diff --git a/compiler/renderer.nim b/compiler/renderer.nim index 95a622d4e..996168412 100644 --- a/compiler/renderer.nim +++ b/compiler/renderer.nim @@ -342,7 +342,7 @@ proc atom(g: TSrcGen; n: PNode): string = of nkIdent: result = n.ident.s of nkSym: result = n.sym.name.s of nkStrLit: result = ""; result.addQuoted(n.strVal) - of nkRStrLit: result = "r\"" & replace(n.strVal, "\"", "\"\"") & '\"' + of nkRStrLit: result = "r\"" & replace(n.strVal, "\"", "\"\"") & '\"' of nkTripleStrLit: result = "\"\"\"" & n.strVal & "\"\"\"" of nkCharLit: result = "\'" diff --git a/compiler/semstmts.nim b/compiler/semstmts.nim index 8b466f1da..f5fdc3445 100644 --- a/compiler/semstmts.nim +++ b/compiler/semstmts.nim @@ -846,7 +846,7 @@ proc checkCovariantParamsUsages(genericType: PType) = if subType != nil: subresult traverseSubTypes(subType) if result: - error("non-invariant type param used in a proc type: " & $t) + error("non-invariant type param used in a proc type: " & $t) of tySequence: return traverseSubTypes(t[0]) diff --git a/compiler/vmgen.nim b/compiler/vmgen.nim index 0544dc311..f7d3237b8 100644 --- a/compiler/vmgen.nim +++ b/compiler/vmgen.nim @@ -790,7 +790,7 @@ proc genIntCast(c: PCtx; n: PNode; dest: var TDest) = elif src.kind in signedIntegers and dst.kind in unsignedIntegers: # cast signed to unsigned integer of same size # unsignedVal = (offset +% signedVal +% 1) and offset - let offset = (1 shl (src_size * 8)) - 1 + let offset = (1 shl (src_size * 8)) - 1 c.gABx(n, opcLdConst, tmp2, mkIntLit(offset)) c.gABx(n, opcLdConst, dest, mkIntLit(offset+1)) c.gABC(n, opcAddu, tmp3, tmp, dest) diff --git a/lib/pure/algorithm.nim b/lib/pure/algorithm.nim index 45e031574..f9a31bc23 100644 --- a/lib/pure/algorithm.nim +++ b/lib/pure/algorithm.nim @@ -395,7 +395,7 @@ proc rotateInternal[T](arg: var openarray[T]; first, middle, last: int): int = swap(arg[mFirst], arg[next]) mFirst += 1 - next += 1 + next += 1 if mFirst == mMiddle: mMiddle = next diff --git a/lib/system/assign.nim b/lib/system/assign.nim index ff1ef31d2..16b56aba7 100644 --- a/lib/system/assign.nim +++ b/lib/system/assign.nim @@ -74,7 +74,7 @@ proc genericAssignAux(dest, src: pointer, mt: PNimType, shallow: bool) = var dst = cast[ByteAddress](cast[PPointer](dest)[]) for i in 0..seq.len-1: genericAssignAux( - cast[pointer](dst +% i*% mt.base.size +% GenericSeqSize), + cast[pointer](dst +% i *% mt.base.size +% GenericSeqSize), cast[pointer](cast[ByteAddress](s2) +% i *% mt.base.size +% GenericSeqSize), mt.base, shallow) @@ -100,8 +100,8 @@ proc genericAssignAux(dest, src: pointer, mt: PNimType, shallow: bool) = genericAssignAux(dest, src, mt.node, shallow) of tyArray, tyArrayConstr: for i in 0..(mt.size div mt.base.size)-1: - genericAssignAux(cast[pointer](d +% i*% mt.base.size), - cast[pointer](s +% i*% mt.base.size), mt.base, shallow) + genericAssignAux(cast[pointer](d +% i *% mt.base.size), + cast[pointer](s +% i *% mt.base.size), mt.base, shallow) of tyRef: unsureAsgnRef(cast[PPointer](dest), cast[PPointer](s)[]) of tyOptAsRef: @@ -166,8 +166,8 @@ proc genericAssignOpenArray(dest, src: pointer, len: int, d = cast[ByteAddress](dest) s = cast[ByteAddress](src) for i in 0..len-1: - genericAssign(cast[pointer](d +% i*% mt.base.size), - cast[pointer](s +% i*% mt.base.size), mt.base) + genericAssign(cast[pointer](d +% i *% mt.base.size), + cast[pointer](s +% i *% mt.base.size), mt.base) proc objectInit(dest: pointer, typ: PNimType) {.compilerProc, benign.} proc objectInitAux(dest: pointer, n: ptr TNimNode) {.benign.} = @@ -235,7 +235,7 @@ proc genericReset(dest: pointer, mt: PNimType) = pint[] = nil of tyArray, tyArrayConstr: for i in 0..(mt.size div mt.base.size)-1: - genericReset(cast[pointer](d +% i*% mt.base.size), mt.base) + genericReset(cast[pointer](d +% i *% mt.base.size), mt.base) else: zeroMem(dest, mt.size) # set raw bits to zero diff --git a/lib/system/deepcopy.nim b/lib/system/deepcopy.nim index 51e138e5e..750da00cf 100644 --- a/lib/system/deepcopy.nim +++ b/lib/system/deepcopy.nim @@ -105,7 +105,7 @@ proc genericDeepCopyAux(dest, src: pointer, mt: PNimType; tab: var PtrTable) = var dst = cast[ByteAddress](cast[PPointer](dest)[]) for i in 0..seq.len-1: genericDeepCopyAux( - cast[pointer](dst +% i*% mt.base.size +% GenericSeqSize), + cast[pointer](dst +% i *% mt.base.size +% GenericSeqSize), cast[pointer](cast[ByteAddress](s2) +% i *% mt.base.size +% GenericSeqSize), mt.base, tab) @@ -122,8 +122,8 @@ proc genericDeepCopyAux(dest, src: pointer, mt: PNimType; tab: var PtrTable) = genericDeepCopyAux(dest, src, mt.node, tab) of tyArray, tyArrayConstr: for i in 0..(mt.size div mt.base.size)-1: - genericDeepCopyAux(cast[pointer](d +% i*% mt.base.size), - cast[pointer](s +% i*% mt.base.size), mt.base, tab) + genericDeepCopyAux(cast[pointer](d +% i *% mt.base.size), + cast[pointer](s +% i *% mt.base.size), mt.base, tab) of tyRef, tyOptAsRef: let s2 = cast[PPointer](src)[] if s2 == nil: @@ -183,5 +183,5 @@ proc genericDeepCopyOpenArray(dest, src: pointer, len: int, d = cast[ByteAddress](dest) s = cast[ByteAddress](src) for i in 0..len-1: - genericDeepCopy(cast[pointer](d +% i*% mt.base.size), - cast[pointer](s +% i*% mt.base.size), mt.base) + genericDeepCopy(cast[pointer](d +% i *% mt.base.size), + cast[pointer](s +% i *% mt.base.size), mt.base) diff --git a/lib/system/gc.nim b/lib/system/gc.nim index 66d49ce1b..425963f3f 100644 --- a/lib/system/gc.nim +++ b/lib/system/gc.nim @@ -535,7 +535,7 @@ proc growObj(old: pointer, newsize: int, gch: var GcHeap): pointer = var oldsize = cast[PGenericSeq](old).len*elemSize + GenericSeqSize copyMem(res, ol, oldsize + sizeof(Cell)) - zeroMem(cast[pointer](cast[ByteAddress](res)+% oldsize +% sizeof(Cell)), + zeroMem(cast[pointer](cast[ByteAddress](res) +% oldsize +% sizeof(Cell)), newsize-oldsize) sysAssert((cast[ByteAddress](res) and (MemAlign-1)) == 0, "growObj: 3") # This can be wrong for intermediate temps that are nevertheless on the diff --git a/lib/system/osalloc.nim b/lib/system/osalloc.nim index 1ad4cf695..9609b6d39 100644 --- a/lib/system/osalloc.nim +++ b/lib/system/osalloc.nim @@ -58,7 +58,7 @@ when defined(emscripten): # Convert pointer to PageSize correct one. var new_pos = cast[ByteAddress](pos) +% (PageSize - (pos %% PageSize)) - if (new_pos-pos)< sizeof(EmscriptenMMapBlock): + if (new_pos-pos) < sizeof(EmscriptenMMapBlock): new_pos = new_pos +% PageSize result = cast[pointer](new_pos) -- cgit 1.4.1-2-gfad0 From b7116a28eea13f4fd82b8b22323f2f75c813794f Mon Sep 17 00:00:00 2001 From: Andreas Rumpf Date: Sat, 5 May 2018 18:47:29 +0200 Subject: compiler refactoring, pass config around explicitly --- compiler/docgen.nim | 6 +- compiler/lexer.nim | 35 +- compiler/main.nim | 13 +- compiler/modules.nim | 2 +- compiler/nimconf.nim | 2 +- compiler/options.nim | 1 + compiler/parser.nim | 16 +- compiler/passes.nim | 2 +- compiler/pbraces.nim | 1790 --------------------------------------------- compiler/reorder.nim | 8 +- compiler/scriptconfig.nim | 2 +- compiler/sem.nim | 6 +- compiler/semexprs.nim | 6 +- compiler/semstmts.nim | 5 +- compiler/syntaxes.nim | 29 +- compiler/vm.nim | 41 +- compiler/vmdef.nim | 8 +- 17 files changed, 93 insertions(+), 1879 deletions(-) delete mode 100644 compiler/pbraces.nim (limited to 'compiler/parser.nim') diff --git a/compiler/docgen.nim b/compiler/docgen.nim index 6f3dcde8b..e1a70a23e 100644 --- a/compiler/docgen.nim +++ b/compiler/docgen.nim @@ -790,7 +790,7 @@ proc writeOutputJson*(d: PDoc, filename, outExt: string, discard "fixme: error report" proc commandDoc*() = - var ast = parseFile(gProjectMainIdx.FileIndex, newIdentCache()) + var ast = parseFile(gProjectMainIdx.FileIndex, newIdentCache(), newConfigRef()) if ast == nil: return var d = newDocumentor(gProjectFull, options.gConfigVars) d.hasToc = true @@ -840,7 +840,7 @@ proc commandRst2TeX*() = commandRstAux(gProjectFull, TexExt) proc commandJson*() = - var ast = parseFile(gProjectMainIdx.FileIndex, newIdentCache()) + var ast = parseFile(gProjectMainIdx.FileIndex, newIdentCache(), newConfigRef()) if ast == nil: return var d = newDocumentor(gProjectFull, options.gConfigVars) d.hasToc = true @@ -855,7 +855,7 @@ proc commandJson*() = writeRope(content, getOutFile(gProjectFull, JsonExt), useWarning = false) proc commandTags*() = - var ast = parseFile(gProjectMainIdx.FileIndex, newIdentCache()) + var ast = parseFile(gProjectMainIdx.FileIndex, newIdentCache(), newConfigRef()) if ast == nil: return var d = newDocumentor(gProjectFull, options.gConfigVars) d.hasToc = true diff --git a/compiler/lexer.nim b/compiler/lexer.nim index 0478ed574..0aeb44ca3 100644 --- a/compiler/lexer.nim +++ b/compiler/lexer.nim @@ -144,13 +144,12 @@ type cache*: IdentCache when defined(nimsuggest): previousToken: TLineInfo + config*: ConfigRef when defined(nimpretty): var gIndentationWidth*: int -var gLinesCompiled*: int # all lines that have been compiled - proc getLineInfo*(L: TLexer, tok: TToken): TLineInfo {.inline.} = result = newLineInfo(L.fileIdx, tok.line, tok.col) when defined(nimpretty): @@ -222,7 +221,7 @@ proc fillToken(L: var TToken) = L.commentOffsetB = 0 proc openLexer*(lex: var TLexer, fileIdx: FileIndex, inputstream: PLLStream; - cache: IdentCache) = + cache: IdentCache; config: ConfigRef) = openBaseLexer(lex, inputstream) lex.fileIdx = fileidx lex.indentAhead = - 1 @@ -231,13 +230,14 @@ proc openLexer*(lex: var TLexer, fileIdx: FileIndex, inputstream: PLLStream; lex.cache = cache when defined(nimsuggest): lex.previousToken.fileIndex = fileIdx + lex.config = config proc openLexer*(lex: var TLexer, filename: string, inputstream: PLLStream; - cache: IdentCache) = - openLexer(lex, filename.fileInfoIdx, inputstream, cache) + cache: IdentCache; config: ConfigRef) = + openLexer(lex, filename.fileInfoIdx, inputstream, cache, config) proc closeLexer*(lex: var TLexer) = - inc(gLinesCompiled, lex.lineNumber) + inc(lex.config.linesCompiled, lex.lineNumber) closeBaseLexer(lex) proc getLineInfo(L: TLexer): TLineInfo = @@ -576,17 +576,18 @@ proc getNumber(L: var TLexer, result: var TToken) = result.iNumber = parseBiggestInt(result.literal) # Explicit bounds checks - let outOfRange = case result.tokType: - of tkInt8Lit: (result.iNumber < int8.low or result.iNumber > int8.high) - of tkUInt8Lit: (result.iNumber < BiggestInt(uint8.low) or - result.iNumber > BiggestInt(uint8.high)) - of tkInt16Lit: (result.iNumber < int16.low or result.iNumber > int16.high) - of tkUInt16Lit: (result.iNumber < BiggestInt(uint16.low) or - result.iNumber > BiggestInt(uint16.high)) - of tkInt32Lit: (result.iNumber < int32.low or result.iNumber > int32.high) - of tkUInt32Lit: (result.iNumber < BiggestInt(uint32.low) or - result.iNumber > BiggestInt(uint32.high)) - else: false + let outOfRange = + case result.tokType + of tkInt8Lit: (result.iNumber < int8.low or result.iNumber > int8.high) + of tkUInt8Lit: (result.iNumber < BiggestInt(uint8.low) or + result.iNumber > BiggestInt(uint8.high)) + of tkInt16Lit: (result.iNumber < int16.low or result.iNumber > int16.high) + of tkUInt16Lit: (result.iNumber < BiggestInt(uint16.low) or + result.iNumber > BiggestInt(uint16.high)) + of tkInt32Lit: (result.iNumber < int32.low or result.iNumber > int32.high) + of tkUInt32Lit: (result.iNumber < BiggestInt(uint32.low) or + result.iNumber > BiggestInt(uint32.high)) + else: false if outOfRange: lexMessageLitNum(L, errNumberOutOfRange, startpos) diff --git a/compiler/main.nim b/compiler/main.nim index 401099fc3..f23a3a88e 100644 --- a/compiler/main.nim +++ b/compiler/main.nim @@ -129,9 +129,10 @@ proc commandEval(graph: ModuleGraph; cache: IdentCache; exp: string) = interactivePasses(graph, cache) compileSystemModule(graph, cache) let echoExp = "echo \"eval\\t\", " & "repr(" & exp & ")" - evalNim(graph, echoExp.parseString(cache), makeStdinModule(graph), cache) + evalNim(graph, echoExp.parseString(cache, graph.config), + makeStdinModule(graph), cache) -proc commandScan(cache: IdentCache) = +proc commandScan(cache: IdentCache, config: ConfigRef) = var f = addFileExt(mainCommandArg(), NimExt) var stream = llStreamOpen(f, fmRead) if stream != nil: @@ -139,7 +140,7 @@ proc commandScan(cache: IdentCache) = L: TLexer tok: TToken initToken(tok) - openLexer(L, f, stream, cache) + openLexer(L, f, stream, cache, config) while true: rawGetTok(L, tok) printTok(tok) @@ -265,11 +266,11 @@ proc mainCommand*(graph: ModuleGraph; cache: IdentCache) = of "parse": gCmd = cmdParse wantMainModule() - discard parseFile(FileIndex gProjectMainIdx, cache) + discard parseFile(FileIndex gProjectMainIdx, cache, graph.config) of "scan": gCmd = cmdScan wantMainModule() - commandScan(cache) + commandScan(cache, graph.config) msgWriteln("Beware: Indentation tokens depend on the parser's state!") of "secret": gCmd = cmdInteractive @@ -291,7 +292,7 @@ proc mainCommand*(graph: ModuleGraph; cache: IdentCache) = let usedMem = formatSize(getMaxMem()) & " peakmem" else: let usedMem = formatSize(getTotalMem()) - rawMessage(hintSuccessX, [$gLinesCompiled, + rawMessage(hintSuccessX, [$graph.config.linesCompiled, formatFloat(epochTime() - gLastCmdTime, ffDecimal, 3), usedMem, if condSyms.isDefined("release"): "Release Build" diff --git a/compiler/modules.nim b/compiler/modules.nim index 56bfdf662..7fe2336dd 100644 --- a/compiler/modules.nim +++ b/compiler/modules.nim @@ -107,7 +107,7 @@ proc importModule*(graph: ModuleGraph; s: PSym, fileIdx: FileIndex; proc includeModule*(graph: ModuleGraph; s: PSym, fileIdx: FileIndex; cache: IdentCache): PNode {.procvar.} = - result = syntaxes.parseFile(fileIdx, cache) + result = syntaxes.parseFile(fileIdx, cache, graph.config) graph.addDep(s, fileIdx) graph.addIncludeDep(s.position.FileIndex, fileIdx) diff --git a/compiler/nimconf.nim b/compiler/nimconf.nim index bdf558134..dc8d082b3 100644 --- a/compiler/nimconf.nim +++ b/compiler/nimconf.nim @@ -205,7 +205,7 @@ proc readConfigFile(filename: string; cache: IdentCache; config: ConfigRef) = stream = llStreamOpen(filename, fmRead) if stream != nil: initToken(tok) - openLexer(L, filename, stream, cache) + openLexer(L, filename, stream, cache, config) tok.tokType = tkEof # to avoid a pointless warning confTok(L, tok, config) # read in the first token while tok.tokType != tkEof: parseAssignment(L, tok, config) diff --git a/compiler/options.nim b/compiler/options.nim index 5baaa1bfd..f8cb735ae 100644 --- a/compiler/options.nim +++ b/compiler/options.nim @@ -113,6 +113,7 @@ type notnil ConfigRef* = ref object ## eventually all global configuration should be moved here + linesCompiled*: int # all lines that have been compiled cppDefines*: HashSet[string] headerFile*: string features*: set[Feature] diff --git a/compiler/parser.nim b/compiler/parser.nim index c61519541..14683e307 100644 --- a/compiler/parser.nim +++ b/compiler/parser.nim @@ -27,7 +27,7 @@ when isMainModule: outp.close import - llstream, lexer, idents, strutils, ast, astalgo, msgs + llstream, lexer, idents, strutils, ast, astalgo, msgs, options type TParser* = object # A TParser object represents a file that @@ -84,20 +84,20 @@ proc getTok(p: var TParser) = p.hasProgress = true proc openParser*(p: var TParser, fileIdx: FileIndex, inputStream: PLLStream, - cache: IdentCache; + cache: IdentCache; config: ConfigRef; strongSpaces=false) = ## Open a parser, using the given arguments to set up its internal state. ## initToken(p.tok) - openLexer(p.lex, fileIdx, inputStream, cache) + openLexer(p.lex, fileIdx, inputStream, cache, config) getTok(p) # read the first token p.firstTok = true p.strongSpaces = strongSpaces proc openParser*(p: var TParser, filename: string, inputStream: PLLStream, - cache: IdentCache; + cache: IdentCache; config: ConfigRef; strongSpaces=false) = - openParser(p, filename.fileInfoIdx, inputStream, cache, strongSpaces) + openParser(p, filename.fileInfoIdx, inputStream, cache, config, strongSpaces) proc closeParser(p: var TParser) = ## Close a parser, freeing up its resources. @@ -2178,8 +2178,8 @@ proc parseTopLevelStmt(p: var TParser): PNode = if result.kind == nkEmpty: parMessage(p, errExprExpected, p.tok) break -proc parseString*(s: string; cache: IdentCache; filename: string = ""; - line: int = 0; +proc parseString*(s: string; cache: IdentCache; config: ConfigRef; + filename: string = ""; line: int = 0; errorHandler: TErrorHandler = nil): PNode = ## Parses a string into an AST, returning the top node. ## `filename` and `line`, although optional, provide info so that the @@ -2192,7 +2192,7 @@ proc parseString*(s: string; cache: IdentCache; filename: string = ""; # XXX for now the builtin 'parseStmt/Expr' functions do not know about strong # spaces... parser.lex.errorHandler = errorHandler - openParser(parser, filename, stream, cache, false) + openParser(parser, filename, stream, cache, config, false) result = parser.parseAll closeParser(parser) diff --git a/compiler/passes.nim b/compiler/passes.nim index 6ff3f2bb5..b104cd132 100644 --- a/compiler/passes.nim +++ b/compiler/passes.nim @@ -207,7 +207,7 @@ proc processModule*(graph: ModuleGraph; module: PSym, stream: PLLStream, else: s = stream while true: - openParsers(p, fileIdx, s, cache) + openParsers(p, fileIdx, s, cache, graph.config) if sfSystemModule notin module.flags: # XXX what about caching? no processing then? what if I change the diff --git a/compiler/pbraces.nim b/compiler/pbraces.nim deleted file mode 100644 index fe438d58b..000000000 --- a/compiler/pbraces.nim +++ /dev/null @@ -1,1790 +0,0 @@ -# -# -# The Nim Compiler -# (c) Copyright 2017 Andreas Rumpf -# -# See the file "copying.txt", included in this -# distribution, for details about the copyright. -# - -# This module implements the parser of the braces Nim syntax. - -import - llstream, lexer, idents, strutils, ast, astalgo, msgs - -from parser import TParser - -proc getTok(p: var TParser) = - ## Get the next token from the parser's lexer, and store it in the parser's - ## `tok` member. - rawGetTok(p.lex, p.tok) - -proc openParser*(p: var TParser, fileIdx: FileIndex, inputStream: PLLStream; - cache: IdentCache) = - ## Open a parser, using the given arguments to set up its internal state. - ## - initToken(p.tok) - openLexer(p.lex, fileIdx, inputStream, cache) - getTok(p) # read the first token - p.lex.allowTabs = true - -proc openParser*(p: var TParser, filename: string, inputStream: PLLStream; - cache: IdentCache) = - openParser(p, filename.fileInfoIdx, inputStream, cache) - -proc closeParser*(p: var TParser) = - ## Close a parser, freeing up its resources. - closeLexer(p.lex) - -proc parMessage(p: TParser, msg: TMsgKind, arg = "") = - ## Produce and emit the parser message `arg` to output. - lexMessageTok(p.lex, msg, p.tok, arg) - -proc parMessage(p: TParser, msg: TMsgKind, tok: TToken) = - ## Produce and emit a parser message to output about the token `tok` - parMessage(p, msg, prettyTok(tok)) - -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: - parMessage(p, errInternal, "skipComment") - getTok(p) - -proc skipComment(p: var TParser, node: PNode) = - rawSkipComment(p, node) - -proc flexComment(p: var TParser, node: PNode) = - rawSkipComment(p, node) - -proc skipInd(p: var TParser) = discard -proc optPar(p: var TParser) = discard - -proc optInd(p: var TParser, n: PNode) = - skipComment(p, n) - -proc getTokNoInd(p: var TParser) = - getTok(p) - -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: - lexMessage(p.lex, errIdentifierExpected, prettyTok(p.tok)) - -proc eat(p: var TParser, tokType: TTokType) = - ## Move the parser to the next token if the current token is of type - ## `tokType`, otherwise error. - if p.tok.tokType == tokType: - getTok(p) - else: - lexMessageTok(p.lex, errTokenExpected, p.tok, TokTypeToStr[tokType]) - -proc parLineInfo(p: TParser): TLineInfo = - ## Retrieve the line information associated with the parser's current state. - result = getLineInfo(p.lex, p.tok) - -proc indAndComment(p: var TParser, n: PNode) = - rawSkipComment(p, n) - -proc newNodeP(kind: TNodeKind, p: TParser): PNode = - result = newNodeI(kind, parLineInfo(p)) - -proc newIntNodeP(kind: TNodeKind, intVal: BiggestInt, p: TParser): PNode = - result = newNodeP(kind, p) - result.intVal = intVal - -proc newFloatNodeP(kind: TNodeKind, floatVal: BiggestFloat, - p: TParser): PNode = - result = newNodeP(kind, p) - result.floatVal = floatVal - -proc newStrNodeP(kind: TNodeKind, strVal: string, p: TParser): PNode = - result = newNodeP(kind, p) - result.strVal = strVal - -proc newIdentNodeP(ident: PIdent, p: TParser): PNode = - result = newNodeP(nkIdent, p) - result.ident = ident - -proc parseExpr(p: var TParser): PNode -proc parseStmt(p: var TParser): PNode -proc parseTypeDesc(p: var TParser): PNode -proc parseDoBlocks(p: var TParser, call: PNode) -proc parseParamList(p: var TParser, retColon = true): PNode -proc parseStmtPragma(p: var TParser): PNode -proc parseCase(p: var TParser): PNode -proc parseTry(p: var TParser): PNode - -proc isSigilLike(tok: TToken): bool {.inline.} = - result = tok.tokType == tkOpr and tok.ident.s[0] == '@' - -proc isAt(tok: TToken): bool {.inline.} = - tok.tokType == tkOpr and tok.ident.s == "@" and tok.strongSpaceB == 0 - -proc isRightAssociative(tok: TToken): bool {.inline.} = - ## Determines whether the token is right assocative. - result = tok.tokType == tkOpr and tok.ident.s[0] == '^' - # or (let L = tok.ident.s.len; L > 1 and tok.ident.s[L-1] == '>')) - -proc getPrecedence(tok: TToken): int = - ## Calculates the precedence of the given token. - template considerStrongSpaces(x): untyped = x - - case tok.tokType - of tkOpr: - let L = tok.ident.s.len - let relevantChar = tok.ident.s[0] - - # arrow like? - if L > 1 and tok.ident.s[L-1] == '>' and - tok.ident.s[L-2] in {'-', '~', '='}: return considerStrongSpaces(1) - - template considerAsgn(value: untyped) = - result = if tok.ident.s[L-1] == '=': 1 else: value - - case relevantChar - of '$', '^': considerAsgn(10) - of '*', '%', '/', '\\': considerAsgn(9) - of '~': result = 8 - of '+', '-', '|': considerAsgn(8) - of '&': considerAsgn(7) - of '=', '<', '>', '!': result = 5 - of '.': considerAsgn(6) - of '?': result = 2 - else: considerAsgn(2) - of tkDiv, tkMod, tkShl, tkShr: result = 9 - of tkIn, tkNotin, tkIs, tkIsnot, tkNot, tkOf, tkAs: result = 5 - of tkDotDot: result = 6 - of tkAnd: result = 4 - of tkOr, tkXor, tkPtr, tkRef: result = 3 - else: return -10 - result = considerStrongSpaces(result) - -proc isOperator(tok: TToken): bool = - ## Determines if the given token is an operator type token. - tok.tokType in {tkOpr, tkDiv, tkMod, tkShl, tkShr, tkIn, tkNotin, tkIs, - tkIsnot, tkNot, tkOf, tkAs, tkDotDot, tkAnd, tkOr, tkXor} - -proc isUnary(p: TParser): bool = - ## Check if the current parser token is a unary operator - if p.tok.tokType in {tkOpr, tkDotDot}: - result = true - -proc checkBinary(p: TParser) {.inline.} = - ## Check if the current parser token is a binary operator. - # we don't check '..' here as that's too annoying - discard - -#| 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' | 'static' | '..' -#| -#| prefixOperator = operator -#| -#| optInd = COMMENT? -#| optPar = (IND{>} | IND{=})? -#| -#| simpleExpr = arrowExpr (OP0 optInd arrowExpr)* -#| arrowExpr = assignExpr (OP1 optInd assignExpr)* -#| assignExpr = orExpr (OP2 optInd orExpr)* -#| orExpr = andExpr (OP3 optInd andExpr)* -#| andExpr = cmpExpr (OP4 optInd cmpExpr)* -#| cmpExpr = sliceExpr (OP5 optInd sliceExpr)* -#| sliceExpr = ampExpr (OP6 optInd ampExpr)* -#| ampExpr = plusExpr (OP7 optInd plusExpr)* -#| plusExpr = mulExpr (OP8 optInd mulExpr)* -#| mulExpr = dollarExpr (OP9 optInd dollarExpr)* -#| dollarExpr = primary (OP10 optInd primary)* - -proc colcom(p: var TParser, n: PNode) = - skipComment(p, n) - -proc parseSymbol(p: var TParser, allowNil = false): PNode = - #| symbol = '`' (KEYW|IDENT|literal|(operator|'('|')'|'['|']'|'{'|'}'|'=')+)+ '`' - #| | IDENT | 'addr' | 'type' - case p.tok.tokType - of tkSymbol, tkAddr, tkType: - result = newIdentNodeP(p.tok.ident, p) - getTok(p) - of tkAccent: - result = newNodeP(nkAccQuoted, p) - getTok(p) - while true: - case p.tok.tokType - of tkAccent: - if result.len == 0: - parMessage(p, errIdentifierExpected, p.tok) - break - of tkOpr, tkDot, tkDotDot, tkEquals, tkParLe..tkParDotRi: - var accm = "" - while p.tok.tokType in {tkOpr, tkDot, tkDotDot, tkEquals, - tkParLe..tkParDotRi}: - accm.add(tokToStr(p.tok)) - getTok(p) - result.add(newIdentNodeP(p.lex.cache.getIdent(accm), p)) - of tokKeywordLow..tokKeywordHigh, tkSymbol, tkIntLit..tkCharLit: - result.add(newIdentNodeP(p.lex.cache.getIdent(tokToStr(p.tok)), p)) - getTok(p) - else: - parMessage(p, errIdentifierExpected, p.tok) - break - eat(p, tkAccent) - else: - if allowNil and p.tok.tokType == tkNil: - result = newNodeP(nkNilLit, p) - getTok(p) - else: - parMessage(p, errIdentifierExpected, p.tok) - # BUGFIX: We must consume a token here to prevent endless loops! - # But: this really sucks for idetools and keywords, so we don't do it - # if it is a keyword: - if not isKeyword(p.tok.tokType): getTok(p) - result = ast.emptyNode - -proc colonOrEquals(p: var TParser, a: PNode): PNode = - if p.tok.tokType == tkColon: - result = newNodeP(nkExprColonExpr, p) - getTok(p) - #optInd(p, result) - addSon(result, a) - addSon(result, parseExpr(p)) - elif p.tok.tokType == tkEquals: - result = newNodeP(nkExprEqExpr, p) - getTok(p) - #optInd(p, result) - addSon(result, a) - addSon(result, parseExpr(p)) - 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): - var a = parseExpr(p) - addSon(result, a) - if p.tok.tokType != tkComma: break - getTok(p) - optInd(p, a) - -proc dotExpr(p: var TParser, a: PNode): PNode = - #| dotExpr = expr '.' optInd symbol - var info = p.parLineInfo - getTok(p) - result = newNodeI(nkDotExpr, info) - optInd(p, result) - addSon(result, a) - addSon(result, parseSymbol(p)) - -proc qualifiedIdent(p: var TParser): PNode = - #| qualifiedIdent = symbol ('.' optInd symbol)? - result = parseSymbol(p) - if p.tok.tokType == tkDot: result = dotExpr(p, result) - -proc exprColonEqExprListAux(p: var TParser, endTok: TTokType, result: PNode) = - assert(endTok in {tkCurlyLe, tkCurlyRi, tkCurlyDotRi, tkBracketRi, tkParRi}) - getTok(p) - optInd(p, result) - 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) - skipComment(p, a) - optPar(p) - eat(p, endTok) - -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 = - result = newNodeP(nkCurly, p) - getTok(p) - optInd(p, result) - if p.tok.tokType == tkColon: - getTok(p) # skip ':' - result.kind = nkTableConstr - else: - while p.tok.tokType notin {tkBracketDotRi, tkEof}: - var a = exprColonEqExpr(p) - if a.kind == nkExprColonExpr: result.kind = nkTableConstr - addSon(result, a) - if p.tok.tokType != tkComma: break - getTok(p) - skipComment(p, a) - optPar(p) - eat(p, tkBracketDotRi) - -proc parseCast(p: var TParser): PNode = - #| castExpr = 'cast' '[' optInd typeDesc optPar ']' '(' optInd expr optPar ')' - result = newNodeP(nkCast, p) - getTok(p) - eat(p, tkBracketLe) - optInd(p, result) - addSon(result, parseTypeDesc(p)) - optPar(p) - eat(p, tkBracketRi) - 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: discard - of base2: incl(n.flags, nfBase2) - of base8: incl(n.flags, nfBase8) - of base16: incl(n.flags, nfBase16) - -proc parseGStrLit(p: var TParser, a: PNode): PNode = - case p.tok.tokType - of tkGStrLit: - result = newNodeP(nkCallStrLit, p) - addSon(result, a) - addSon(result, newStrNodeP(nkRStrLit, p.tok.literal, p)) - getTok(p) - of tkGTripleStrLit: - result = newNodeP(nkCallStrLit, p) - addSon(result, a) - addSon(result, newStrNodeP(nkTripleStrLit, p.tok.literal, p)) - getTok(p) - else: - result = a - -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) = - inc p.inSemiStmtList - result.add(complexOrSimpleStmt(p)) - while p.tok.tokType == tkSemiColon: - getTok(p) - optInd(p, result) - result.add(complexOrSimpleStmt(p)) - dec p.inSemiStmtList - 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 ^+ ';' - #| | ';' complexOrSimpleStmt ^+ ';' - #| | pragmaStmt - #| | simpleExpr ( ('=' expr (';' complexOrSimpleStmt ^+ ';' )? ) - #| | (':' expr (',' exprColonEqExpr ^+ ',' )? ) ) ) - #| 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, tkDefer, 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 == tkCurlyDotLe: - result.add(parseStmtPragma(p)) - 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) - if p.tok.tokType == tkSemiColon: - semiStmtList(p, result) - 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 = - #| literal = | 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 - #| generalizedLit = GENERALIZED_STR_LIT | GENERALIZED_TRIPLESTR_LIT - #| identOrLiteral = generalizedLit | symbol | literal - #| | par | arrayConstr | setOrTableConstr - #| | castExpr - #| tupleConstr = '(' optInd (exprColonEqExpr comma?)* optPar ')' - #| arrayConstr = '[' optInd (exprColonEqExpr comma?)* optPar ']' - case p.tok.tokType - of tkSymbol, tkType, tkAddr: - result = newIdentNodeP(p.tok.ident, p) - getTok(p) - result = parseGStrLit(p, result) - of tkAccent: - result = parseSymbol(p) # literals - of tkIntLit: - result = newIntNodeP(nkIntLit, p.tok.iNumber, p) - setBaseFlags(result, p.tok.base) - getTok(p) - of tkInt8Lit: - result = newIntNodeP(nkInt8Lit, p.tok.iNumber, p) - setBaseFlags(result, p.tok.base) - getTok(p) - of tkInt16Lit: - result = newIntNodeP(nkInt16Lit, p.tok.iNumber, p) - setBaseFlags(result, p.tok.base) - getTok(p) - of tkInt32Lit: - result = newIntNodeP(nkInt32Lit, p.tok.iNumber, p) - setBaseFlags(result, p.tok.base) - getTok(p) - of tkInt64Lit: - result = newIntNodeP(nkInt64Lit, p.tok.iNumber, p) - setBaseFlags(result, p.tok.base) - getTok(p) - of tkUIntLit: - result = newIntNodeP(nkUIntLit, p.tok.iNumber, p) - setBaseFlags(result, p.tok.base) - getTok(p) - of tkUInt8Lit: - result = newIntNodeP(nkUInt8Lit, p.tok.iNumber, p) - setBaseFlags(result, p.tok.base) - getTok(p) - of tkUInt16Lit: - result = newIntNodeP(nkUInt16Lit, p.tok.iNumber, p) - setBaseFlags(result, p.tok.base) - getTok(p) - of tkUInt32Lit: - result = newIntNodeP(nkUInt32Lit, p.tok.iNumber, p) - setBaseFlags(result, p.tok.base) - getTok(p) - of tkUInt64Lit: - result = newIntNodeP(nkUInt64Lit, p.tok.iNumber, p) - setBaseFlags(result, p.tok.base) - getTok(p) - of tkFloatLit: - result = newFloatNodeP(nkFloatLit, p.tok.fNumber, p) - setBaseFlags(result, p.tok.base) - getTok(p) - of tkFloat32Lit: - result = newFloatNodeP(nkFloat32Lit, p.tok.fNumber, p) - setBaseFlags(result, p.tok.base) - getTok(p) - of tkFloat64Lit: - result = newFloatNodeP(nkFloat64Lit, p.tok.fNumber, p) - setBaseFlags(result, p.tok.base) - getTok(p) - of tkFloat128Lit: - result = newFloatNodeP(nkFloat128Lit, p.tok.fNumber, p) - setBaseFlags(result, p.tok.base) - getTok(p) - of tkStrLit: - result = newStrNodeP(nkStrLit, p.tok.literal, p) - getTok(p) - of tkRStrLit: - result = newStrNodeP(nkRStrLit, p.tok.literal, p) - getTok(p) - of tkTripleStrLit: - result = newStrNodeP(nkTripleStrLit, p.tok.literal, p) - getTok(p) - of tkCharLit: - result = newIntNodeP(nkCharLit, ord(p.tok.literal[0]), p) - getTok(p) - of tkNil: - result = newNodeP(nkNilLit, p) - getTok(p) - of tkParLe: - # () constructor - if mode in {pmTypeDesc, pmTypeDef}: - result = exprColonEqExprList(p, nkPar, tkParRi) - else: - result = parsePar(p) - of tkBracketDotLe: - # {} constructor - result = setOrTableConstr(p) - of tkBracketLe: - # [] constructor - result = exprColonEqExprList(p, nkBracket, tkBracketRi) - of tkCast: - result = parseCast(p) - else: - parMessage(p, errExprExpected, p.tok) - getTok(p) # we must consume a token here to prevend endless loops! - result = ast.emptyNode - -proc namedParams(p: var TParser, callee: PNode, - kind: TNodeKind, endTok: TTokType): PNode = - let a = callee - result = newNodeP(kind, p) - addSon(result, a) - exprColonEqExprListAux(p, endTok, result) - -proc parseMacroColon(p: var TParser, x: PNode): PNode -proc primarySuffix(p: var TParser, r: PNode): PNode = - #| primarySuffix = '(' (exprColonEqExpr comma?)* ')' doBlocks? - #| | doBlocks - #| | '.' optInd symbol generalizedLit? - #| | '[' optInd indexExprList optPar ']' - #| | '{' optInd indexExprList optPar '}' - #| | &( '`'|IDENT|literal|'cast'|'addr'|'type') expr # command syntax - result = r - - template somePar() = discard - while p.tok.indent < 0: - case p.tok.tokType - of tkParLe: - somePar() - result = namedParams(p, result, nkCall, tkParRi) - if result.len > 1 and result.sons[1].kind == nkExprColonExpr: - result.kind = nkObjConstr - else: - parseDoBlocks(p, result) - of tkDo: - var a = result - result = newNodeP(nkCall, p) - addSon(result, a) - parseDoBlocks(p, result) - of tkDot: - result = dotExpr(p, result) - result = parseGStrLit(p, result) - of tkBracketLe: - somePar() - result = namedParams(p, result, nkBracketExpr, tkBracketRi) - of tkBracketDotLe: - somePar() - result = namedParams(p, result, nkCurlyExpr, tkBracketDotRi) - of tkSymbol, tkAccent, tkIntLit..tkCharLit, tkNil, tkCast, tkAddr, tkType: - if p.inPragma == 0: - # actually parsing {.push hints:off.} as {.push(hints:off).} is a sweet - # solution, but pragmas.nim can't handle that - let a = result - result = newNodeP(nkCommand, p) - addSon(result, a) - when true: - addSon result, parseExpr(p) - else: - while p.tok.tokType != tkEof: - let x = parseExpr(p) - addSon(result, x) - if p.tok.tokType != tkComma: break - getTok(p) - optInd(p, x) - if p.tok.tokType == tkDo: - parseDoBlocks(p, result) - else: - result = parseMacroColon(p, result) - break - else: - break - -proc primary(p: var TParser, mode: TPrimaryMode): PNode -proc simpleExprAux(p: var TParser, limit: int, mode: TPrimaryMode): PNode - -proc parseOperators(p: var TParser, headNode: PNode, - limit: int, mode: TPrimaryMode): PNode = - result = headNode - # expand while operators have priorities higher than 'limit' - var opPrec = getPrecedence(p.tok) - let modeB = if mode == pmTypeDef: pmTypeDesc else: mode - # the operator itself must not start on a new line: - while opPrec >= limit and p.tok.indent < 0 and not isAt(p.tok): - checkBinary(p) - var leftAssoc = 1-ord(isRightAssociative(p.tok)) - var a = newNodeP(nkInfix, p) - var opNode = newIdentNodeP(p.tok.ident, p) # skip operator: - getTok(p) - optInd(p, a) - # read sub-expression with higher priority: - var b = simpleExprAux(p, opPrec + leftAssoc, modeB) - addSon(a, opNode) - addSon(a, result) - addSon(a, b) - result = a - opPrec = getPrecedence(p.tok) - -proc simpleExprAux(p: var TParser, limit: int, mode: TPrimaryMode): PNode = - result = primary(p, mode) - result = parseOperators(p, result, limit, mode) - -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: - getTok(p) # skip `if`, `elif` - var branch = newNodeP(nkElifExpr, p) - addSon(branch, parseExpr(p)) - 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) - colcom(p, branch) - addSon(branch, parseExpr(p)) - addSon(result, branch) - -proc parsePragma(p: var TParser): PNode = - result = newNodeP(nkPragma, p) - inc p.inPragma - if isAt(p.tok): - while isAt(p.tok): - getTok(p) - var a = parseExpr(p) - optInd(p, a) - if a.kind in nkCallKinds and a.len == 2: - let repaired = newNodeI(nkExprColonExpr, a.info) - repaired.add a[0] - repaired.add a[1] - a = repaired - addSon(result, a) - skipComment(p, a) - else: - getTok(p) - optInd(p, result) - while p.tok.tokType notin {tkCurlyDotRi, tkCurlyRi, tkEof}: - var a = exprColonEqExpr(p) - addSon(result, a) - if p.tok.tokType == tkComma: - getTok(p) - skipComment(p, a) - optPar(p) - if p.tok.tokType in {tkCurlyDotRi, tkCurlyRi}: getTok(p) - else: parMessage(p, errTokenExpected, ".}") - dec p.inPragma - -proc identVis(p: var TParser; allowDot=false): PNode = - #| identVis = symbol opr? # postfix position - #| identVisDot = symbol '.' optInd symbol opr? - var a = parseSymbol(p) - if p.tok.tokType == tkOpr: - result = newNodeP(nkPostfix, p) - addSon(result, newIdentNodeP(p.tok.ident, p)) - addSon(result, a) - getTok(p) - elif p.tok.tokType == tkDot and allowDot: - result = dotExpr(p, a) - else: - result = a - -proc identWithPragma(p: var TParser; allowDot=false): PNode = - #| identWithPragma = identVis pragma? - #| identWithPragmaDot = identVisDot pragma? - var a = identVis(p, allowDot) - if p.tok.tokType == tkCurlyDotLe or isAt(p.tok): - result = newNodeP(nkPragmaExpr, p) - addSon(result, a) - addSon(result, parsePragma(p)) - else: - result = a - -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: - case p.tok.tokType - of tkSymbol, tkAccent: - if withPragma in flags: a = identWithPragma(p) - else: a = parseSymbol(p) - if a.kind == nkEmpty: return - else: break - addSon(result, a) - if p.tok.tokType != tkComma: break - getTok(p) - optInd(p, a) - if p.tok.tokType == tkColon: - getTok(p) - optInd(p, result) - addSon(result, parseTypeDesc(p)) - else: - addSon(result, ast.emptyNode) - if p.tok.tokType != tkEquals and withBothOptional notin flags: - parMessage(p, errColonOrEqualsExpected, p.tok) - if p.tok.tokType == tkEquals: - getTok(p) - optInd(p, result) - addSon(result, parseExpr(p)) - else: - addSon(result, ast.emptyNode) - -proc parseTuple(p: var TParser): PNode = - result = newNodeP(nkTupleTy, p) - getTok(p) - if p.tok.tokType in {tkBracketLe, tkCurlyLe}: - let usedCurly = p.tok.tokType == tkCurlyLe - getTok(p) - optInd(p, result) - while p.tok.tokType in {tkSymbol, tkAccent}: - var a = parseIdentColonEquals(p, {}) - addSon(result, a) - if p.tok.tokType notin {tkComma, tkSemiColon}: break - getTok(p) - skipComment(p, a) - optPar(p) - if usedCurly: eat(p, tkCurlyRi) - else: eat(p, tkBracketRi) - else: - result = newNodeP(nkTupleClassTy, p) - -proc parseParamList(p: var TParser, retColon = true): PNode = - #| paramList = '(' declColonEquals ^* (comma/semicolon) ')' - #| paramListArrow = paramList? ('->' optInd typeDesc)? - #| paramListColon = paramList? (':' optInd typeDesc)? - var a: PNode - result = newNodeP(nkFormalParams, p) - addSon(result, ast.emptyNode) # return type - let hasParLe = p.tok.tokType == tkParLe and p.tok.indent < 0 - if hasParLe: - getTok(p) - optInd(p, result) - while true: - case p.tok.tokType - of tkSymbol, tkAccent: - a = parseIdentColonEquals(p, {withBothOptional, withPragma}) - of tkParRi: - break - else: - parMessage(p, errTokenExpected, ")") - break - addSon(result, a) - if p.tok.tokType notin {tkComma, tkSemiColon}: break - getTok(p) - skipComment(p, a) - optPar(p) - eat(p, tkParRi) - let hasRet = if retColon: p.tok.tokType == tkColon - else: p.tok.tokType == tkOpr and p.tok.ident.s == "->" - if hasRet and p.tok.indent < 0: - getTok(p) - optInd(p, result) - result.sons[0] = parseTypeDesc(p) - elif not retColon and not hasParle: - # Mark as "not there" in order to mark for deprecation in the semantic pass: - result = ast.emptyNode - -proc optPragmas(p: var TParser): PNode = - if p.tok.tokType == tkCurlyDotLe or isAt(p.tok): - 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) - let pragmas = optPragmas(p) - colcom(p, result) - result = newProcNode(nkDo, info, parseStmt(p), - params = params, - pragmas = pragmas) - -proc parseDoBlocks(p: var TParser, call: PNode) = - #| doBlocks = doBlock ^* IND{=} - while p.tok.tokType == tkDo: - addSon(call, parseDoBlock(p)) - -proc parseCurlyStmt(p: var TParser): PNode = - result = newNodeP(nkStmtList, p) - eat(p, tkCurlyLe) - result.add parseStmt(p) - while p.tok.tokType notin {tkEof, tkCurlyRi}: - if p.tok.tokType == tkSemicolon: getTok(p) - elif p.tok.indent < 0: break - result.add parseStmt(p) - eat(p, tkCurlyRi) - -proc parseProcExpr(p: var TParser, isExpr: bool): PNode = - #| procExpr = 'proc' paramListColon pragmas? ('=' COMMENT? stmt)? - # either a proc type or a anonymous proc - let info = parLineInfo(p) - getTok(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 == tkCurlyLe and isExpr: - result = newProcNode(nkLambda, info, parseCurlyStmt(p), - params = params, - pragmas = pragmas) - else: - result = newNodeI(nkProcTy, info) - if hasSignature: - addSon(result, params) - addSon(result, pragmas) - -proc isExprStart(p: TParser): bool = - case p.tok.tokType - of tkSymbol, tkAccent, tkOpr, tkNot, tkNil, tkCast, tkIf, - tkProc, tkIterator, tkBind, tkAddr, - tkParLe, tkBracketLe, tkCurlyLe, tkIntLit..tkCharLit, tkVar, tkRef, tkPtr, - tkTuple, tkObject, tkType, tkWhen, tkCase, tkOut: - result = true - else: result = false - -proc parseSymbolList(p: var TParser, result: PNode, allowNil = false) = - while true: - var s = parseSymbol(p, allowNil) - if s.kind == nkEmpty: break - addSon(result, s) - if p.tok.tokType != tkComma: break - getTok(p) - optInd(p, s) - -proc parseTypeDescKAux(p: var TParser, kind: TNodeKind, - mode: TPrimaryMode): PNode = - #| distinct = 'distinct' optInd typeDesc - result = newNodeP(kind, p) - getTok(p) - optInd(p, result) - if not isOperator(p.tok) and isExprStart(p): - addSon(result, primary(p, mode)) - if kind == nkDistinctTy and p.tok.tokType == tkSymbol: - var nodeKind: TNodeKind - if p.tok.ident.s == "with": - nodeKind = nkWith - elif p.tok.ident.s == "without": - nodeKind = nkWithout - else: - return result - getTok(p) - let list = newNodeP(nodeKind, p) - result.addSon list - parseSymbolList(p, list, allowNil = true) - -proc parseExpr(p: var TParser): PNode = - #| expr = (ifExpr - #| | whenExpr - #| | caseExpr - #| | tryExpr) - #| / simpleExpr - case p.tok.tokType: - of tkIf: result = parseIfExpr(p, nkIfExpr) - of tkWhen: result = parseIfExpr(p, nkWhenExpr) - of tkCase: result = parseCase(p) - of tkTry: result = parseTry(p) - else: result = simpleExpr(p) - -proc parseEnum(p: var TParser): PNode -proc parseObject(p: var TParser): PNode -proc parseTypeClass(p: var TParser): PNode - -proc primary(p: var TParser, mode: TPrimaryMode): PNode = - #| typeKeyw = 'var' | 'out' | 'ref' | 'ptr' | 'shared' | 'tuple' - #| | 'proc' | 'iterator' | 'distinct' | 'object' | 'enum' - #| primary = typeKeyw typeDescK - #| / prefixOperator* identOrLiteral primarySuffix* - #| / 'static' primary - #| / 'bind' primary - if isOperator(p.tok): - let isSigil = isSigilLike(p.tok) - result = newNodeP(nkPrefix, p) - var a = newIdentNodeP(p.tok.ident, p) - addSon(result, a) - getTok(p) - optInd(p, a) - if isSigil: - #XXX prefix operators - addSon(result, primary(p, pmSkipSuffix)) - result = primarySuffix(p, result) - else: - addSon(result, primary(p, pmNormal)) - return - - case p.tok.tokType: - of tkTuple: result = parseTuple(p) - of tkProc: result = parseProcExpr(p, mode notin {pmTypeDesc, pmTypeDef}) - of tkIterator: - result = parseProcExpr(p, mode notin {pmTypeDesc, pmTypeDef}) - if result.kind == nkLambda: result.kind = nkIteratorDef - else: result.kind = nkIteratorTy - of tkEnum: - if mode == pmTypeDef: - result = parseEnum(p) - else: - result = newNodeP(nkEnumTy, p) - getTok(p) - of tkObject: - if mode == pmTypeDef: - result = parseObject(p) - else: - result = newNodeP(nkObjectTy, p) - getTok(p) - of tkConcept: - if mode == pmTypeDef: - result = parseTypeClass(p) - else: - parMessage(p, errInvalidToken, p.tok) - of tkStatic: - let info = parLineInfo(p) - getTokNoInd(p) - let next = primary(p, pmNormal) - if next.kind == nkBracket and next.sonsLen == 1: - result = newNode(nkStaticTy, info, @[next.sons[0]]) - else: - result = newNode(nkStaticExpr, info, @[next]) - of tkBind: - result = newNodeP(nkBind, p) - getTok(p) - optInd(p, result) - addSon(result, primary(p, pmNormal)) - of tkVar: result = parseTypeDescKAux(p, nkVarTy, mode) - of tkOut: result = parseTypeDescKAux(p, nkVarTy, mode) - of tkRef: result = parseTypeDescKAux(p, nkRefTy, mode) - of tkPtr: result = parseTypeDescKAux(p, nkPtrTy, mode) - of tkDistinct: result = parseTypeDescKAux(p, nkDistinctTy, mode) - else: - result = identOrLiteral(p, mode) - if mode != pmSkipSuffix: - result = primarySuffix(p, result) - -proc parseTypeDesc(p: var TParser): PNode = - #| typeDesc = simpleExpr - result = simpleExpr(p, pmTypeDesc) - -proc parseTypeDefAux(p: var TParser): PNode = - #| typeDefAux = simpleExpr - #| | 'concept' typeClass - result = simpleExpr(p, pmTypeDef) - -proc makeCall(n: PNode): PNode = - ## Creates a call if the given node isn't already a call. - if n.kind in nkCallKinds: - result = n - else: - result = newNodeI(nkCall, n.info) - result.add n - -proc parseMacroColon(p: var TParser, x: PNode): PNode = - #| macroColon = ':' stmt? ( IND{=} 'of' exprList ':' stmt - #| | IND{=} 'elif' expr ':' stmt - #| | IND{=} 'except' exprList ':' stmt - #| | IND{=} 'else' ':' stmt )* - result = x - if p.tok.tokType == tkColon and p.tok.indent < 0: - result = makeCall(result) - getTok(p) - skipComment(p, result) - let stmtList = newNodeP(nkStmtList, p) - if p.tok.tokType notin {tkOf, tkElif, tkElse, tkExcept}: - let body = parseStmt(p) - stmtList.add body - #addSon(result, makeStmtList(body)) - while true: - var b: PNode - case p.tok.tokType - of tkOf: - b = newNodeP(nkOfBranch, p) - exprList(p, tkCurlyLe, b) - of tkElif: - b = newNodeP(nkElifBranch, p) - getTok(p) - optInd(p, b) - addSon(b, parseExpr(p)) - of tkExcept: - b = newNodeP(nkExceptBranch, p) - exprList(p, tkCurlyLe, b) - of tkElse: - b = newNodeP(nkElse, p) - getTok(p) - else: break - addSon(b, parseCurlyStmt(p)) - addSon(stmtList, b) - if b.kind == nkElse: break - if stmtList.len == 1 and stmtList[0].kind == nkStmtList: - # to keep backwards compatibility (see tests/vm/tstringnil) - result.add stmtList[0] - else: - result.add stmtList - -proc parseExprStmt(p: var TParser): PNode = - #| exprStmt = simpleExpr - #| (( '=' optInd expr ) - #| / ( expr ^+ comma - #| doBlocks - #| / macroColon - #| ))? - var a = simpleExpr(p) - if p.tok.tokType == tkEquals: - getTok(p) - optInd(p, result) - var b = parseExpr(p) - result = newNodeI(nkAsgn, a.info) - addSon(result, a) - addSon(result, b) - else: - # simpleExpr parsed 'p a' from 'p a, b'? - if p.tok.indent < 0 and p.tok.tokType == tkComma and a.kind == nkCommand: - result = a - while true: - getTok(p) - optInd(p, result) - var e = parseExpr(p) - addSon(result, e) - if p.tok.tokType != tkComma: break - elif p.tok.indent < 0 and isExprStart(p): - if a.kind == nkCommand: - result = a - else: - 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 - result = parseMacroColon(p, result) - -proc parseModuleName(p: var TParser, kind: TNodeKind): PNode = - result = parseExpr(p) - -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) - var a = parseModuleName(p, kind) - addSon(result, a) - if p.tok.tokType in {tkComma, tkExcept}: - if p.tok.tokType == tkExcept: - result.kind = succ(kind) - getTok(p) - optInd(p, result) - while true: - # was: while p.tok.tokType notin {tkEof, tkSad, tkDed}: - a = parseModuleName(p, kind) - if a.kind == nkEmpty: break - addSon(result, a) - if p.tok.tokType != tkComma: break - getTok(p) - optInd(p, a) - #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 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) - -proc parseFromStmt(p: var TParser): PNode = - #| fromStmt = 'from' moduleName 'import' optInd expr (comma expr)* - result = newNodeP(nkFromStmt, p) - getTok(p) # skip `from` - optInd(p, result) - var a = parseModuleName(p, nkImportStmt) - addSon(result, a) #optInd(p, a); - eat(p, tkImport) - optInd(p, result) - 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) - -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) - if p.tok.tokType == tkComment: - skipComment(p, result) - addSon(result, ast.emptyNode) - elif p.tok.indent >= 0 or not isExprStart(p): - # 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` - var branch = newNodeP(nkElifBranch, p) - optInd(p, branch) - addSon(branch, parseExpr(p)) - colcom(p, branch) - addSon(branch, parseCurlyStmt(p)) - skipComment(p, branch) - addSon(result, branch) - if p.tok.tokType != tkElif: break - if p.tok.tokType == tkElse: - var branch = newNodeP(nkElse, p) - eat(p, tkElse) - addSon(branch, parseCurlyStmt(p)) - addSon(result, branch) - -proc parseWhile(p: var TParser): PNode = - #| whileStmt = 'while' expr colcom stmt - result = newNodeP(nkWhileStmt, p) - getTok(p) - optInd(p, result) - addSon(result, parseExpr(p)) - colcom(p, result) - addSon(result, parseCurlyStmt(p)) - -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 - result = newNodeP(nkCaseStmt, p) - getTok(p) - addSon(result, parseExpr(p)) - eat(p, tkCurlyLe) - skipComment(p, result) - - while true: - case p.tok.tokType - of tkOf: - if inElif: break - b = newNodeP(nkOfBranch, p) - exprList(p, tkCurlyLe, b) - of tkElif: - inElif = true - b = newNodeP(nkElifBranch, p) - getTok(p) - optInd(p, b) - addSon(b, parseExpr(p)) - of tkElse: - b = newNodeP(nkElse, p) - getTok(p) - else: break - skipComment(p, b) - addSon(b, parseCurlyStmt(p)) - addSon(result, b) - if b.kind == nkElse: break - eat(p, tkCurlyRi) - -proc parseTry(p: var TParser): PNode = - #| tryStmt = 'try' colcom stmt &(IND{=}? 'except'|'finally') - #| (IND{=}? 'except' exprList colcom stmt)* - #| (IND{=}? 'finally' colcom stmt)? - #| tryExpr = 'try' colcom stmt &(optInd 'except'|'finally') - #| (optInd 'except' exprList colcom stmt)* - #| (optInd 'finally' colcom stmt)? - result = newNodeP(nkTryStmt, p) - getTok(p) - colcom(p, result) - addSon(result, parseCurlyStmt(p)) - var b: PNode = nil - while true: - case p.tok.tokType - of tkExcept: - b = newNodeP(nkExceptBranch, p) - exprList(p, tkCurlyLe, b) - of tkFinally: - b = newNodeP(nkFinally, p) - getTok(p) - else: break - skipComment(p, b) - addSon(b, parseCurlyStmt(p)) - addSon(result, b) - if b.kind == nkFinally: break - if b == nil: parMessage(p, errTokenExpected, "except") - -proc parseFor(p: var TParser): PNode = - #| forStmt = 'for' (identWithPragma ^+ comma) 'in' expr colcom stmt - result = newNodeP(nkForStmt, p) - getTokNoInd(p) - var a = identWithPragma(p) - addSon(result, a) - while p.tok.tokType == tkComma: - getTok(p) - optInd(p, a) - a = identWithPragma(p) - addSon(result, a) - eat(p, tkIn) - addSon(result, parseExpr(p)) - colcom(p, result) - addSon(result, parseCurlyStmt(p)) - -proc parseBlock(p: var TParser): PNode = - #| blockStmt = 'block' symbol? colcom stmt - result = newNodeP(nkBlockStmt, p) - getTokNoInd(p) - if p.tok.tokType == tkCurlyLe: addSon(result, ast.emptyNode) - else: addSon(result, parseSymbol(p)) - colcom(p, result) - addSon(result, parseCurlyStmt(p)) - -proc parseStaticOrDefer(p: var TParser; k: TNodeKind): PNode = - #| staticStmt = 'static' colcom stmt - #| deferStmt = 'defer' colcom stmt - result = newNodeP(k, p) - getTok(p) - colcom(p, result) - addSon(result, parseCurlyStmt(p)) - -proc parseAsm(p: var TParser): PNode = - #| asmStmt = 'asm' pragma? (STR_LIT | RSTR_LIT | TRIPLE_STR_LIT) - result = newNodeP(nkAsmStmt, p) - getTokNoInd(p) - if p.tok.tokType == tkCurlyDotLe or isAt(p.tok): addSon(result, parsePragma(p)) - else: addSon(result, ast.emptyNode) - case p.tok.tokType - of tkStrLit: addSon(result, newStrNodeP(nkStrLit, p.tok.literal, p)) - of tkRStrLit: addSon(result, newStrNodeP(nkRStrLit, p.tok.literal, p)) - of tkTripleStrLit: addSon(result, - newStrNodeP(nkTripleStrLit, p.tok.literal, p)) - else: - parMessage(p, errStringLiteralExpected) - addSon(result, ast.emptyNode) - return - getTok(p) - -proc parseGenericParam(p: var TParser): PNode = - #| genericParam = symbol (comma symbol)* (colon expr)? ('=' optInd expr)? - var a: PNode - result = newNodeP(nkIdentDefs, p) - while true: - case p.tok.tokType - of tkIn, tkOut: - let t = p.tok.tokType - getTok(p) - expectIdent(p) - a = parseSymbol(p) - of tkSymbol, tkAccent: - a = parseSymbol(p) - if a.kind == nkEmpty: return - else: break - addSon(result, a) - if p.tok.tokType != tkComma: break - getTok(p) - optInd(p, a) - if p.tok.tokType == tkColon: - getTok(p) - optInd(p, result) - addSon(result, parseExpr(p)) - else: - addSon(result, ast.emptyNode) - if p.tok.tokType == tkEquals: - getTok(p) - optInd(p, result) - addSon(result, parseExpr(p)) - else: - 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 in {tkSymbol, tkAccent}: - var a = parseGenericParam(p) - addSon(result, a) - if p.tok.tokType notin {tkComma, tkSemiColon}: break - getTok(p) - skipComment(p, a) - optPar(p) - eat(p, tkBracketRi) - -proc parsePattern(p: var TParser): PNode = - eat(p, tkBracketDotLe) - result = parseStmt(p) - eat(p, tkBracketDotRi) - -proc validInd(p: TParser): bool = p.tok.indent < 0 - -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 == tkBracketDotLe and p.validInd: - addSon(result, p.parsePattern) - else: - addSon(result, ast.emptyNode) - 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 or isAt(p.tok)) and p.validInd: - addSon(result, p.parsePragma) - else: - addSon(result, ast.emptyNode) - # empty exception tracking: - addSon(result, ast.emptyNode) - if p.tok.tokType == tkCurlyLe: - addSon(result, parseCurlyStmt(p)) - else: - addSon(result, ast.emptyNode) - indAndComment(p, result) - -proc newCommentStmt(p: var TParser): PNode = - #| commentStmt = COMMENT - result = newNodeP(nkCommentStmt, p) - result.comment = p.tok.literal - getTok(p) - -type - TDefParser = proc (p: var TParser): PNode {.nimcall.} - -proc parseSection(p: var TParser, kind: TNodeKind, - defparser: TDefParser): PNode = - #| section(p) = COMMENT? p / (IND{>} (p / COMMENT)^+IND{=} DED) - result = newNodeP(kind, p) - if kind != nkTypeSection: getTok(p) - skipComment(p, result) - if p.tok.tokType == tkParLe: - getTok(p) - skipComment(p, result) - while true: - case p.tok.tokType - of tkSymbol, tkAccent, tkParLe: - var a = defparser(p) - skipComment(p, a) - addSon(result, a) - of tkComment: - var a = newCommentStmt(p) - addSon(result, a) - of tkParRi: break - else: - parMessage(p, errIdentifierExpected, p.tok) - break - eat(p, tkParRi) - if result.len == 0: parMessage(p, errIdentifierExpected, p.tok) - elif p.tok.tokType in {tkSymbol, tkAccent, tkBracketLe}: - # tkBracketLe is allowed for ``var [x, y] = ...`` tuple parsing - addSon(result, defparser(p)) - else: - parMessage(p, errIdentifierExpected, p.tok) - -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: - getTok(p) - optInd(p, result) - addSon(result, parseTypeDesc(p)) - else: - addSon(result, ast.emptyNode) - eat(p, tkEquals) - optInd(p, result) - addSon(result, parseExpr(p)) - indAndComment(p, result) - -proc parseEnum(p: var TParser): PNode = - #| enum = 'enum' optInd (symbol optInd ('=' optInd expr COMMENT?)? comma?)+ - result = newNodeP(nkEnumTy, p) - getTok(p) - addSon(result, ast.emptyNode) - optInd(p, result) - flexComment(p, result) - eat(p, tkCurlyLe) - optInd(p, result) - while p.tok.tokType notin {tkEof, tkCurlyRi}: - var a = parseSymbol(p) - if a.kind == nkEmpty: return - if p.tok.tokType == tkEquals: - getTok(p) - optInd(p, a) - var b = a - a = newNodeP(nkEnumFieldDef, p) - addSon(a, b) - addSon(a, parseExpr(p)) - if p.tok.indent < 0: - rawSkipComment(p, a) - if p.tok.tokType == tkComma: - getTok(p) - rawSkipComment(p, a) - addSon(result, a) - eat(p, tkCurlyRi) - if result.len <= 1: - lexMessageTok(p.lex, errIdentifierExpected, p.tok, prettyTok(p.tok)) - -proc parseObjectPart(p: var TParser; needsCurly: bool): PNode -proc parseObjectWhen(p: var TParser): PNode = - result = newNodeP(nkRecWhen, p) - while true: - getTok(p) # skip `when`, `elif` - var branch = newNodeP(nkElifBranch, p) - optInd(p, branch) - addSon(branch, parseExpr(p)) - colcom(p, branch) - addSon(branch, parseObjectPart(p, true)) - flexComment(p, branch) - addSon(result, branch) - if p.tok.tokType != tkElif: break - if p.tok.tokType == tkElse: - var branch = newNodeP(nkElse, p) - eat(p, tkElse) - colcom(p, branch) - addSon(branch, parseObjectPart(p, true)) - flexComment(p, branch) - addSon(result, branch) - -proc parseObjectCase(p: var TParser): PNode = - result = newNodeP(nkRecCase, p) - getTokNoInd(p) - var a = newNodeP(nkIdentDefs, p) - addSon(a, identWithPragma(p)) - eat(p, tkColon) - addSon(a, parseTypeDesc(p)) - addSon(a, ast.emptyNode) - addSon(result, a) - eat(p, tkCurlyLe) - flexComment(p, result) - while true: - var b: PNode - case p.tok.tokType - of tkOf: - b = newNodeP(nkOfBranch, p) - exprList(p, tkColon, b) - of tkElse: - b = newNodeP(nkElse, p) - getTok(p) - else: break - colcom(p, b) - var fields = parseObjectPart(p, true) - if fields.kind == nkEmpty: - parMessage(p, errIdentifierExpected, p.tok) - fields = newNodeP(nkNilLit, p) # don't break further semantic checking - addSon(b, fields) - addSon(result, b) - if b.kind == nkElse: break - eat(p, tkCurlyRi) - -proc parseObjectPart(p: var TParser; needsCurly: bool): PNode = - if p.tok.tokType == tkCurlyLe: - result = newNodeP(nkRecList, p) - getTok(p) - rawSkipComment(p, result) - while true: - case p.tok.tokType - of tkCase, tkWhen, tkSymbol, tkAccent, tkNil, tkDiscard: - addSon(result, parseObjectPart(p, false)) - of tkCurlyRi: break - else: - parMessage(p, errIdentifierExpected, p.tok) - break - eat(p, tkCurlyRi) - else: - if needsCurly: - parMessage(p, errTokenExpected, "{") - case p.tok.tokType - of tkWhen: - result = parseObjectWhen(p) - of tkCase: - result = parseObjectCase(p) - of tkSymbol, tkAccent: - result = parseIdentColonEquals(p, {withPragma}) - if p.tok.indent < 0: rawSkipComment(p, result) - of tkNil, tkDiscard: - result = newNodeP(nkNilLit, p) - getTok(p) - else: - result = ast.emptyNode - -proc parseObject(p: var TParser): PNode = - result = newNodeP(nkObjectTy, p) - getTok(p) - if (p.tok.tokType == tkCurlyDotLe or isAt(p.tok)) 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) - # an initial IND{>} HAS to follow: - addSon(result, parseObjectPart(p, true)) - -proc parseTypeClassParam(p: var TParser): PNode = - if p.tok.tokType in {tkOut, tkVar}: - result = newNodeP(nkVarTy, p) - getTok(p) - result.addSon(p.parseSymbol) - else: - result = p.parseSymbol - -proc parseTypeClass(p: var TParser): PNode = - #| typeClassParam = ('var' | 'out')? symbol - #| typeClass = typeClassParam ^* ',' (pragma)? ('of' typeDesc ^* ',')? - #| &IND{>} stmt - result = newNodeP(nkTypeClassTy, p) - getTok(p) - var args = newNodeP(nkArgList, p) - addSon(result, args) - addSon(args, p.parseTypeClassParam) - while p.tok.tokType == tkComma: - getTok(p) - addSon(args, p.parseTypeClassParam) - if (p.tok.tokType == tkCurlyDotLe or isAt(p.tok)) 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) - while true: - addSon(a, parseTypeDesc(p)) - if p.tok.tokType != tkComma: break - getTok(p) - addSon(result, a) - else: - addSon(result, ast.emptyNode) - if p.tok.tokType == tkComment: - skipComment(p, result) - addSon(result, parseCurlyStmt(p)) - -proc parseTypeDef(p: var TParser): PNode = - #| - #| typeDef = identWithPragmaDot genericParamList? '=' optInd typeDefAux - #| indAndComment? - result = newNodeP(nkTypeDef, p) - addSon(result, identWithPragma(p, allowDot=true)) - 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)) - else: - addSon(result, ast.emptyNode) - indAndComment(p, result) # special extension! - -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 in {tkSymbol, tkAccent}: - var a = identWithPragma(p) - addSon(result, a) - if p.tok.tokType != tkComma: break - getTok(p) - skipComment(p, a) - addSon(result, ast.emptyNode) # no type desc - optPar(p) - eat(p, tkBracketRi) - eat(p, tkEquals) - optInd(p, result) - addSon(result, parseExpr(p)) - -proc parseVariable(p: var TParser): PNode = - #| variable = (varTuple / identColonEquals) indAndComment - if p.tok.tokType == tkBracketLe: result = parseVarTuple(p) - else: result = parseIdentColonEquals(p, {withPragma}) - 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) - while true: - var a = qualifiedIdent(p) - addSon(result, a) - if p.tok.tokType != tkComma: break - getTok(p) - optInd(p, a) - -proc parseStmtPragma(p: var TParser): PNode = - result = parsePragma(p) - if p.tok.tokType == tkCurlyLe: - let a = result - result = newNodeI(nkPragmaBlock, a.info) - getTok(p) - skipComment(p, result) - result.add a - result.add parseStmt(p) - eat(p, tkCurlyRi) - -proc simpleStmt(p: var TParser): PNode = - 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 = 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) - of tkFrom: result = parseFromStmt(p) - of tkInclude: result = parseIncludeStmt(p) - of tkComment: result = newCommentStmt(p) - else: - if isExprStart(p): result = parseExprStmt(p) - else: result = ast.emptyNode - if result.kind notin {nkEmpty, nkCommentStmt}: skipComment(p, result) - -proc complexOrSimpleStmt(p: var TParser): PNode = - case p.tok.tokType - of tkIf: result = parseIfOrWhen(p, nkIfStmt) - of tkWhile: result = parseWhile(p) - of tkCase: result = parseCase(p) - of tkTry: result = parseTry(p) - of tkFor: result = parseFor(p) - of tkBlock: result = parseBlock(p) - of tkStatic: result = parseStaticOrDefer(p, nkStaticStmt) - of tkDefer: result = parseStaticOrDefer(p, nkDefer) - of tkAsm: result = parseAsm(p) - of tkProc: result = parseRoutine(p, nkProcDef) - of tkMethod: result = parseRoutine(p, nkMethodDef) - of tkIterator: result = parseRoutine(p, nkIteratorDef) - of tkMacro: result = parseRoutine(p, nkMacroDef) - of tkTemplate: result = parseRoutine(p, nkTemplateDef) - of tkConverter: result = parseRoutine(p, nkConverterDef) - of tkType: - getTok(p) - if p.tok.tokType == tkBracketLe: - getTok(p) - result = newNodeP(nkTypeOfExpr, p) - result.addSon(primary(p, pmTypeDesc)) - eat(p, tkBracketRi) - result = parseOperators(p, result, -1, pmNormal) - else: - result = parseSection(p, nkTypeSection, parseTypeDef) - of tkConst: result = parseSection(p, nkConstSection, parseConstant) - of tkLet: result = parseSection(p, nkLetSection, parseVariable) - of tkWhen: result = parseIfOrWhen(p, nkWhenStmt) - of tkVar: result = parseSection(p, nkVarSection, parseVariable) - of tkBind: result = parseBind(p, nkBindStmt) - of tkMixin: result = parseBind(p, nkMixinStmt) - of tkUsing: result = parseSection(p, nkUsingStmt, parseVariable) - else: result = simpleStmt(p) - -proc parseStmt(p: var TParser): PNode = - result = complexOrSimpleStmt(p) - -proc parseAll*(p: var TParser): PNode = - ## Parses the rest of the input stream held by the parser into a PNode. - result = newNodeP(nkStmtList, p) - 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) - -proc parseTopLevelStmt*(p: var TParser): PNode = - ## Implements an iterator which, when called repeatedly, returns the next - ## top-level statement or emptyNode if end of stream. - result = ast.emptyNode - while true: - case p.tok.tokType - of tkSemiColon: getTok(p) - of tkEof: break - else: - result = complexOrSimpleStmt(p) - if result.kind == nkEmpty: parMessage(p, errExprExpected, p.tok) - break diff --git a/compiler/reorder.nim b/compiler/reorder.nim index 56d8d5886..2542a08ea 100644 --- a/compiler/reorder.nim +++ b/compiler/reorder.nim @@ -137,7 +137,7 @@ proc hasIncludes(n:PNode): bool = proc includeModule*(graph: ModuleGraph; s: PSym, fileIdx: FileIndex; cache: IdentCache): PNode {.procvar.} = - result = syntaxes.parseFile(fileIdx, cache) + result = syntaxes.parseFile(fileIdx, cache, graph.config) graph.addDep(s, fileIdx) graph.addIncludeDep(FileIndex s.position, fileIdx) @@ -273,9 +273,9 @@ proc hasCommand(n: PNode): bool = of nkStmtList, nkStmtListExpr, nkWhenStmt, nkElifBranch, nkElse, nkStaticStmt, nkLetSection, nkConstSection, nkVarSection, nkIdentDefs: - for a in n: - if a.hasCommand: - return true + for a in n: + if a.hasCommand: + return true else: return false diff --git a/compiler/scriptconfig.nim b/compiler/scriptconfig.nim index c533f4cb4..6ef42f15e 100644 --- a/compiler/scriptconfig.nim +++ b/compiler/scriptconfig.nim @@ -28,7 +28,7 @@ proc listDirs(a: VmArgs, filter: set[PathComponent]) = proc setupVM*(module: PSym; cache: IdentCache; scriptName: string; config: ConfigRef): PEvalContext = # For Nimble we need to export 'setupVM'. - result = newCtx(module, cache) + result = newCtx(module, cache, config) result.mode = emRepl registerAdditionalOps(result) diff --git a/compiler/sem.nim b/compiler/sem.nim index 52282d0e4..d8e5b7f20 100644 --- a/compiler/sem.nim +++ b/compiler/sem.nim @@ -313,7 +313,7 @@ proc tryConstExpr(c: PContext, n: PNode): PNode = msgs.gErrorMax = high(int) try: - result = evalConstExpr(c.module, c.cache, e) + result = evalConstExpr(c.module, c.cache, c.graph.config, e) if result == nil or result.kind == nkEmpty: result = nil else: @@ -334,7 +334,7 @@ proc semConstExpr(c: PContext, n: PNode): PNode = result = getConstExpr(c.module, e) if result == nil: #if e.kind == nkEmpty: globalError(n.info, errConstExprExpected) - result = evalConstExpr(c.module, c.cache, e) + result = evalConstExpr(c.module, c.cache, c.graph.config, e) if result == nil or result.kind == nkEmpty: if e.info != n.info: pushInfoContext(n.info) @@ -439,7 +439,7 @@ proc semMacroExpr(c: PContext, n, nOrig: PNode, sym: PSym, #if c.evalContext == nil: # c.evalContext = c.createEvalContext(emStatic) - result = evalMacroCall(c.module, c.cache, n, nOrig, sym) + result = evalMacroCall(c.module, c.cache, c.graph.config, n, nOrig, sym) if efNoSemCheck notin flags: result = semAfterMacroCall(c, n, result, sym, flags) result = wrapInComesFrom(nOrig.info, sym, result) diff --git a/compiler/semexprs.nim b/compiler/semexprs.nim index feca087fc..4a3672aa0 100644 --- a/compiler/semexprs.nim +++ b/compiler/semexprs.nim @@ -605,12 +605,12 @@ proc evalAtCompileTime(c: PContext, n: PNode): PNode = call.add(a) #echo "NOW evaluating at compile time: ", call.renderTree if sfCompileTime in callee.flags: - result = evalStaticExpr(c.module, c.cache, call, c.p.owner) + result = evalStaticExpr(c.module, c.cache, c.graph.config, call, c.p.owner) if result.isNil: localError(n.info, errCannotInterpretNodeX, renderTree(call)) else: result = fixupTypeAfterEval(c, result, n) else: - result = evalConstExpr(c.module, c.cache, call) + result = evalConstExpr(c.module, c.cache, c.graph.config, call) if result.isNil: result = n else: result = fixupTypeAfterEval(c, result, n) #if result != n: @@ -619,7 +619,7 @@ proc evalAtCompileTime(c: PContext, n: PNode): PNode = proc semStaticExpr(c: PContext, n: PNode): PNode = let a = semExpr(c, n.sons[0]) if a.findUnresolvedStatic != nil: return a - result = evalStaticExpr(c.module, c.cache, a, c.p.owner) + result = evalStaticExpr(c.module, c.cache, c.graph.config, a, c.p.owner) if result.isNil: localError(n.info, errCannotInterpretNodeX, renderTree(n)) result = emptyNode diff --git a/compiler/semstmts.nim b/compiler/semstmts.nim index f5fdc3445..b0bd4e0f6 100644 --- a/compiler/semstmts.nim +++ b/compiler/semstmts.nim @@ -545,7 +545,8 @@ proc semVarOrLet(c: PContext, n: PNode, symkind: TSymKind): PNode = b.sons[j] = newSymNode(v) checkNilable(v) if sfCompileTime in v.flags: hasCompileTime = true - if hasCompileTime: vm.setupCompileTimeVar(c.module, c.cache, result) + if hasCompileTime: + vm.setupCompileTimeVar(c.module, c.cache, c.graph.config, result) proc semConst(c: PContext, n: PNode): PNode = result = copyNode(n) @@ -1755,7 +1756,7 @@ proc semStaticStmt(c: PContext, n: PNode): PNode = #writeStackTrace() let a = semStmt(c, n.sons[0]) n.sons[0] = a - evalStaticStmt(c.module, c.cache, a, c.p.owner) + evalStaticStmt(c.module, c.cache, c.graph.config, a, c.p.owner) result = newNodeI(nkDiscardStmt, n.info, 1) result.sons[0] = emptyNode diff --git a/compiler/syntaxes.nim b/compiler/syntaxes.nim index 5413565e6..974df50fb 100644 --- a/compiler/syntaxes.nim +++ b/compiler/syntaxes.nim @@ -11,17 +11,17 @@ import strutils, llstream, ast, astalgo, idents, lexer, options, msgs, parser, - pbraces, filters, filter_tmpl, renderer + filters, filter_tmpl, renderer type TFilterKind* = enum filtNone, filtTemplate, filtReplace, filtStrip TParserKind* = enum - skinStandard, skinStrongSpaces, skinBraces, skinEndX + skinStandard, skinStrongSpaces, skinEndX const parserNames*: array[TParserKind, string] = ["standard", "strongspaces", - "braces", "endx"] + "endx"] filterNames*: array[TFilterKind, string] = ["none", "stdtmpl", "replace", "strip"] @@ -34,8 +34,6 @@ proc parseAll*(p: var TParsers): PNode = case p.skin of skinStandard, skinStrongSpaces: result = parser.parseAll(p.parser) - of skinBraces: - result = pbraces.parseAll(p.parser) of skinEndX: internalError("parser to implement") result = ast.emptyNode @@ -44,8 +42,6 @@ proc parseTopLevelStmt*(p: var TParsers): PNode = case p.skin of skinStandard, skinStrongSpaces: result = parser.parseTopLevelStmt(p.parser) - of skinBraces: - result = pbraces.parseTopLevelStmt(p.parser) of skinEndX: internalError("parser to implement") result = ast.emptyNode @@ -62,7 +58,8 @@ proc containsShebang(s: string, i: int): bool = while j < s.len and s[j] in Whitespace: inc(j) result = s[j] == '/' -proc parsePipe(filename: string, inputStream: PLLStream; cache: IdentCache): PNode = +proc parsePipe(filename: string, inputStream: PLLStream; cache: IdentCache; + config: ConfigRef): PNode = result = ast.emptyNode var s = llStreamOpen(filename, fmRead) if s != nil: @@ -78,7 +75,7 @@ proc parsePipe(filename: string, inputStream: PLLStream; cache: IdentCache): PNo inc(i, 2) while i < line.len and line[i] in Whitespace: inc(i) var q: TParser - parser.openParser(q, filename, llStreamOpen(substr(line, i)), cache) + parser.openParser(q, filename, llStreamOpen(substr(line, i)), cache, config) result = parser.parseAll(q) parser.closeParser(q) llStreamClose(s) @@ -139,23 +136,23 @@ proc evalPipe(p: var TParsers, n: PNode, filename: string, result = applyFilter(p, n, filename, result) proc openParsers*(p: var TParsers, fileIdx: FileIndex, inputstream: PLLStream; - cache: IdentCache) = + cache: IdentCache; config: ConfigRef) = var s: PLLStream p.skin = skinStandard let filename = fileIdx.toFullPathConsiderDirty - var pipe = parsePipe(filename, inputstream, cache) + var pipe = parsePipe(filename, inputstream, cache, config) if pipe != nil: s = evalPipe(p, pipe, filename, inputstream) else: s = inputstream case p.skin - of skinStandard, skinBraces, skinEndX: - parser.openParser(p.parser, fileIdx, s, cache, false) + of skinStandard, skinEndX: + parser.openParser(p.parser, fileIdx, s, cache, config, false) of skinStrongSpaces: - parser.openParser(p.parser, fileIdx, s, cache, true) + parser.openParser(p.parser, fileIdx, s, cache, config, true) proc closeParsers*(p: var TParsers) = parser.closeParser(p.parser) -proc parseFile*(fileIdx: FileIndex; cache: IdentCache): PNode {.procvar.} = +proc parseFile*(fileIdx: FileIndex; cache: IdentCache; config: ConfigRef): PNode {.procvar.} = var p: TParsers f: File @@ -163,6 +160,6 @@ proc parseFile*(fileIdx: FileIndex; cache: IdentCache): PNode {.procvar.} = if not open(f, filename): rawMessage(errCannotOpenFile, filename) return - openParsers(p, fileIdx, llStreamOpen(f), cache) + openParsers(p, fileIdx, llStreamOpen(f), cache, config) result = parseAll(p) closeParsers(p) diff --git a/compiler/vm.nim b/compiler/vm.nim index 32db8f378..6c36a1458 100644 --- a/compiler/vm.nim +++ b/compiler/vm.nim @@ -1341,8 +1341,8 @@ proc rawExecute(c: PCtx, start: int, tos: PStackFrame): TFullReg = decodeB(rkNode) # c.debug[pc].line.int - countLines(regs[rb].strVal) ? var error: string - let ast = parseString(regs[rb].node.strVal, c.cache, c.debug[pc].toFullPath, - c.debug[pc].line.int, + let ast = parseString(regs[rb].node.strVal, c.cache, c.config, + c.debug[pc].toFullPath, c.debug[pc].line.int, proc (info: TLineInfo; msg: TMsgKind; arg: string) = if error.isNil and msg <= msgs.errMax: error = formatMsg(info, msg, arg)) @@ -1355,8 +1355,8 @@ proc rawExecute(c: PCtx, start: int, tos: PStackFrame): TFullReg = of opcParseStmtToAst: decodeB(rkNode) var error: string - let ast = parseString(regs[rb].node.strVal, c.cache, c.debug[pc].toFullPath, - c.debug[pc].line.int, + let ast = parseString(regs[rb].node.strVal, c.cache, c.config, + c.debug[pc].toFullPath, c.debug[pc].line.int, proc (info: TLineInfo; msg: TMsgKind; arg: string) = if error.isNil and msg <= msgs.errMax: error = formatMsg(info, msg, arg)) @@ -1643,9 +1643,9 @@ include vmops var globalCtx*: PCtx -proc setupGlobalCtx(module: PSym; cache: IdentCache) = +proc setupGlobalCtx(module: PSym; cache: IdentCache; config: ConfigRef) = if globalCtx.isNil: - globalCtx = newCtx(module, cache) + globalCtx = newCtx(module, cache, config) registerAdditionalOps(globalCtx) else: refresh(globalCtx, module) @@ -1656,7 +1656,7 @@ proc myOpen(graph: ModuleGraph; module: PSym; cache: IdentCache): PPassContext = #pushStackFrame(c, newStackFrame()) # XXX produce a new 'globals' environment here: - setupGlobalCtx(module, cache) + setupGlobalCtx(module, cache, graph.config) result = globalCtx when hasFFI: globalCtx.features = {allowFFI, allowCast} @@ -1677,10 +1677,11 @@ proc myClose(graph: ModuleGraph; c: PPassContext, n: PNode): PNode = const evalPass* = makePass(myOpen, nil, myProcess, myClose) -proc evalConstExprAux(module: PSym; cache: IdentCache; prc: PSym, n: PNode, +proc evalConstExprAux(module: PSym; cache: IdentCache; + config: ConfigRef; prc: PSym, n: PNode, mode: TEvalMode): PNode = let n = transformExpr(module, n) - setupGlobalCtx(module, cache) + setupGlobalCtx(module, cache, config) var c = globalCtx let oldMode = c.mode defer: c.mode = oldMode @@ -1695,17 +1696,17 @@ proc evalConstExprAux(module: PSym; cache: IdentCache; prc: PSym, n: PNode, result = rawExecute(c, start, tos).regToNode if result.info.col < 0: result.info = n.info -proc evalConstExpr*(module: PSym; cache: IdentCache, e: PNode): PNode = - result = evalConstExprAux(module, cache, nil, e, emConst) +proc evalConstExpr*(module: PSym; cache: IdentCache, config: ConfigRef; e: PNode): PNode = + result = evalConstExprAux(module, cache, config, nil, e, emConst) -proc evalStaticExpr*(module: PSym; cache: IdentCache, e: PNode, prc: PSym): PNode = - result = evalConstExprAux(module, cache, prc, e, emStaticExpr) +proc evalStaticExpr*(module: PSym; cache: IdentCache, config: ConfigRef; e: PNode, prc: PSym): PNode = + result = evalConstExprAux(module, cache, config, prc, e, emStaticExpr) -proc evalStaticStmt*(module: PSym; cache: IdentCache, e: PNode, prc: PSym) = - discard evalConstExprAux(module, cache, prc, e, emStaticStmt) +proc evalStaticStmt*(module: PSym; cache: IdentCache, config: ConfigRef; e: PNode, prc: PSym) = + discard evalConstExprAux(module, cache, config, prc, e, emStaticStmt) -proc setupCompileTimeVar*(module: PSym; cache: IdentCache, n: PNode) = - discard evalConstExprAux(module, cache, nil, n, emStaticStmt) +proc setupCompileTimeVar*(module: PSym; cache: IdentCache, config: ConfigRef; n: PNode) = + discard evalConstExprAux(module, cache, config, nil, n, emStaticStmt) proc setupMacroParam(x: PNode, typ: PType): TFullReg = case typ.kind @@ -1733,8 +1734,8 @@ iterator genericParamsInMacroCall*(macroSym: PSym, call: PNode): (PSym, PNode) = const evalMacroLimit = 1000 var evalMacroCounter: int -proc evalMacroCall*(module: PSym; cache: IdentCache, n, nOrig: PNode, - sym: PSym): PNode = +proc evalMacroCall*(module: PSym; cache: IdentCache; config: ConfigRef; + n, nOrig: PNode, sym: PSym): PNode = # XXX globalError() is ugly here, but I don't know a better solution for now inc(evalMacroCounter) if evalMacroCounter > evalMacroLimit: @@ -1746,7 +1747,7 @@ proc evalMacroCall*(module: PSym; cache: IdentCache, n, nOrig: PNode, globalError(n.info, "in call '$#' got $#, but expected $# argument(s)" % [ n.renderTree, $(n.safeLen-1), $(sym.typ.len-1)]) - setupGlobalCtx(module, cache) + setupGlobalCtx(module, cache, config) var c = globalCtx c.comesFromHeuristic.line = 0'u16 diff --git a/compiler/vmdef.nim b/compiler/vmdef.nim index 66bc8dfd2..b0a559d2c 100644 --- a/compiler/vmdef.nim +++ b/compiler/vmdef.nim @@ -10,7 +10,7 @@ ## This module contains the type definitions for the new evaluation engine. ## An instruction is 1-3 int32s in memory, it is a register based VM. -import ast, passes, msgs, idents, intsets +import ast, passes, msgs, idents, intsets, options const byteExcess* = 128 # we use excess-K for immediates @@ -206,17 +206,19 @@ type callbacks*: seq[tuple[key: string, value: VmCallback]] errorFlag*: string cache*: IdentCache + config*: ConfigRef TPosition* = distinct int PEvalContext* = PCtx -proc newCtx*(module: PSym; cache: IdentCache): PCtx = +proc newCtx*(module: PSym; cache: IdentCache; config: ConfigRef = nil): PCtx = + let conf = if config != nil: config else: newConfigRef() PCtx(code: @[], debug: @[], globals: newNode(nkStmtListExpr), constants: newNode(nkStmtList), types: @[], prc: PProc(blocks: @[]), module: module, loopIterations: MaxLoopIterations, comesFromHeuristic: unknownLineInfo(), callbacks: @[], errorFlag: "", - cache: cache) + cache: cache, config: conf) proc refresh*(c: PCtx, module: PSym) = c.module = module -- cgit 1.4.1-2-gfad0 From b0d85b0adf950685e2f7d88665a3d5b03cd06cfd Mon Sep 17 00:00:00 2001 From: Zahary Karadjov Date: Sun, 29 Apr 2018 13:30:08 +0300 Subject: Backwards-compatible support for keyword arguments in the command syntax --- changelog.md | 2 ++ compiler/parser.nim | 17 +++++++++++++---- tests/overload/tparam_forwarding.nim | 5 +++-- 3 files changed, 18 insertions(+), 6 deletions(-) (limited to 'compiler/parser.nim') diff --git a/changelog.md b/changelog.md index 9af2aae83..fe935106a 100644 --- a/changelog.md +++ b/changelog.md @@ -95,11 +95,13 @@ - ``nil`` for strings/seqs is finally gone. Instead the default value for these is ``"" / @[]``. + - Accessing the binary zero terminator in Nim's native strings is now invalid. Internally a Nim string still has the trailing zero for zero-copy interoperability with ``cstring``. Compile your code with the new switch ``--laxStrings:on`` if you need a transition period. +- The command syntax now supports keyword arguments after the first comma. ### Tool changes diff --git a/compiler/parser.nim b/compiler/parser.nim index 14683e307..0a3815f13 100644 --- a/compiler/parser.nim +++ b/compiler/parser.nim @@ -702,10 +702,17 @@ proc namedParams(p: var TParser, callee: PNode, # progress guaranteed exprColonEqExprListAux(p, endTok, result) -proc commandParam(p: var TParser): PNode = +proc commandParam(p: var TParser, isFirstParam: var bool): PNode = result = parseExpr(p) if p.tok.tokType == tkDo: result = postExprBlocks(p, result) + elif p.tok.tokType == tkEquals and not isFirstParam: + let lhs = result + result = newNodeP(nkExprEqExpr, p) + getTok(p) + addSon(result, lhs) + addSon(result, parseExpr(p)) + isFirstParam = false proc primarySuffix(p: var TParser, r: PNode, baseIndent: int): PNode = #| primarySuffix = '(' (exprColonEqExpr comma?)* ')' doBlocks? @@ -748,10 +755,11 @@ proc primarySuffix(p: var TParser, r: PNode, baseIndent: int): PNode = let a = result result = newNodeP(nkCommand, p) addSon(result, a) + var isFirstParam = true when true: # progress NOT guaranteed p.hasProgress = false - addSon result, commandParam(p) + addSon result, commandParam(p, isFirstParam) if not p.hasProgress: break else: while p.tok.tokType != tkEof: @@ -1303,17 +1311,18 @@ proc parseExprStmt(p: var TParser): PNode = addSon(result, b) else: # simpleExpr parsed 'p a' from 'p a, b'? + var isFirstParam = false if p.tok.indent < 0 and p.tok.tokType == tkComma and a.kind == nkCommand: result = a while true: getTok(p) optInd(p, result) - addSon(result, commandParam(p)) + addSon(result, commandParam(p, isFirstParam)) if p.tok.tokType != tkComma: break elif p.tok.indent < 0 and isExprStart(p): result = newNode(nkCommand, a.info, @[a]) while true: - addSon(result, commandParam(p)) + addSon(result, commandParam(p, isFirstParam)) if p.tok.tokType != tkComma: break getTok(p) optInd(p, result) diff --git a/tests/overload/tparam_forwarding.nim b/tests/overload/tparam_forwarding.nim index cd3de32e3..b0eea42c7 100644 --- a/tests/overload/tparam_forwarding.nim +++ b/tests/overload/tparam_forwarding.nim @@ -46,7 +46,8 @@ proc hasRegularArgs(x: int, y: string) = echo "x: ", x, ", y: ", y templateForwarding(hasRegularArgs, true, 1, "test 1") -templateForwarding(hasKeywordArgs, true, 2, "test 2") +templateForwarding hasKeywordArgs, true, 2, "test 2" + templateForwarding(hasKeywordArgs, true, y = "test 3") -templateForwarding(hasKeywordArgs, true, y = "test 4", x = 4) +templateForwarding hasKeywordArgs, true, y = "test 4", x = 4 -- cgit 1.4.1-2-gfad0 From 61e57cfa137544ebbb915ce2ed5da9afae4375ef Mon Sep 17 00:00:00 2001 From: Andreas Rumpf Date: Thu, 10 May 2018 10:49:51 +0200 Subject: big refactoring: parser compiles again --- compiler/ast.nim | 20 +- compiler/astalgo.nim | 8 +- compiler/commands.nim | 535 ++++++++++++++++++----------------- compiler/condsyms.nim | 71 +---- compiler/configuration.nim | 389 ++++++++++++++++++++++++++ compiler/extccomp.nim | 296 ++++++++++---------- compiler/idgen.nim | 12 +- compiler/lexer.nim | 65 ++--- compiler/msgs.nim | 674 ++++++--------------------------------------- compiler/nim.nim | 2 +- compiler/nimblecmd.nim | 25 +- compiler/nimconf.nim | 105 +++---- compiler/options.nim | 137 ++++++--- compiler/parser.nim | 43 +-- compiler/scriptconfig.nim | 4 +- 15 files changed, 1157 insertions(+), 1229 deletions(-) create mode 100644 compiler/configuration.nim (limited to 'compiler/parser.nim') diff --git a/compiler/ast.nim b/compiler/ast.nim index b8202abe6..6785702f1 100644 --- a/compiler/ast.nim +++ b/compiler/ast.nim @@ -1564,15 +1564,17 @@ proc getInt*(a: PNode): BiggestInt = case a.kind of nkCharLit..nkUInt64Lit: result = a.intVal else: - internalError(a.info, "getInt") - result = 0 + #internalError(a.info, "getInt") + doAssert false, "getInt" + #result = 0 proc getFloat*(a: PNode): BiggestFloat = case a.kind of nkFloatLiterals: result = a.floatVal else: - internalError(a.info, "getFloat") - result = 0.0 + doAssert false, "getFloat" + #internalError(a.info, "getFloat") + #result = 0.0 proc getStr*(a: PNode): string = case a.kind @@ -1581,16 +1583,18 @@ proc getStr*(a: PNode): string = # let's hope this fixes more problems than it creates: result = nil else: - internalError(a.info, "getStr") - result = "" + doAssert false, "getStr" + #internalError(a.info, "getStr") + #result = "" proc getStrOrChar*(a: PNode): string = case a.kind of nkStrLit..nkTripleStrLit: result = a.strVal of nkCharLit..nkUInt64Lit: result = $chr(int(a.intVal)) else: - internalError(a.info, "getStrOrChar") - result = "" + doAssert false, "getStrOrChar" + #internalError(a.info, "getStrOrChar") + #result = "" proc isGenericRoutine*(s: PSym): bool = case s.kind diff --git a/compiler/astalgo.nim b/compiler/astalgo.nim index 196ac8690..f9311d4ce 100644 --- a/compiler/astalgo.nim +++ b/compiler/astalgo.nim @@ -180,7 +180,7 @@ proc lookupInRecord(n: PNode, field: PIdent): PSym = result = lookupInRecord(n.sons[i], field) if result != nil: return of nkRecCase: - if (n.sons[0].kind != nkSym): internalError(n.info, "lookupInRecord") + if (n.sons[0].kind != nkSym): return nil result = lookupInRecord(n.sons[0], field) if result != nil: return for i in countup(1, sonsLen(n) - 1): @@ -188,10 +188,10 @@ proc lookupInRecord(n: PNode, field: PIdent): PSym = of nkOfBranch, nkElse: result = lookupInRecord(lastSon(n.sons[i]), field) if result != nil: return - else: internalError(n.info, "lookupInRecord(record case branch)") + else: return nil of nkSym: if n.sym.name.id == field.id: result = n.sym - else: internalError(n.info, "lookupInRecord()") + else: return nil proc getModule(s: PSym): PSym = result = s @@ -203,7 +203,7 @@ proc getSymFromList(list: PNode, ident: PIdent, start: int = 0): PSym = if list.sons[i].kind == nkSym: result = list.sons[i].sym if result.name.id == ident.id: return - else: internalError(list.info, "getSymFromList") + else: return nil result = nil proc hashNode(p: RootRef): Hash = diff --git a/compiler/commands.nim b/compiler/commands.nim index 204baaa16..46ade667e 100644 --- a/compiler/commands.nim +++ b/compiler/commands.nim @@ -18,7 +18,6 @@ template bootSwitch(name, expr, userString) = bootSwitch(usedRelease, defined(release), "-d:release") bootSwitch(usedGnuReadline, defined(useLinenoise), "-d:useLinenoise") -bootSwitch(usedNoCaas, defined(noCaas), "-d:noCaas") bootSwitch(usedBoehm, defined(boehmgc), "--gc:boehm") bootSwitch(usedMarkAndSweep, defined(gcmarkandsweep), "--gc:markAndSweep") bootSwitch(usedGenerational, defined(gcgenerational), "--gc:generational") @@ -27,7 +26,7 @@ bootSwitch(usedNoGC, defined(nogc), "--gc:none") import os, msgs, options, nversion, condsyms, strutils, extccomp, platform, - wordrecg, parseutils, nimblecmd, idents, parseopt, sequtils + wordrecg, parseutils, nimblecmd, idents, parseopt, sequtils, configuration # but some have deps to imported modules. Yay. bootSwitch(usedTinyC, hasTinyCBackend, "-d:tinyc") @@ -36,24 +35,15 @@ bootSwitch(usedNativeStacktrace, "-d:nativeStackTrace") bootSwitch(usedFFI, hasFFI, "-d:useFFI") - -proc writeCommandLineUsage*() - type TCmdLinePass* = enum passCmd1, # first pass over the command line passCmd2, # second pass over the command line passPP # preprocessor called processCommand() -proc processCommand*(switch: string, pass: TCmdLinePass; config: ConfigRef) -proc processSwitch*(switch, arg: string, pass: TCmdLinePass, info: TLineInfo; - config: ConfigRef) - -# implementation - const HelpMessage = "Nim Compiler Version $1 [$2: $3]\n" & - "Compiled at $4 $5\n" & + "Compiled at $4\n" & "Copyright (c) 2006-" & copyrightYear & " by Andreas Rumpf\n" const @@ -68,7 +58,7 @@ const proc getCommandLineDesc(): string = result = (HelpMessage % [VersionAsString, platform.OS[platform.hostOS].name, - CPU[platform.hostCPU].name, CompileDate, CompileTime]) & + CPU[platform.hostCPU].name, CompileDate]) & Usage proc helpOnError(pass: TCmdLinePass) = @@ -80,7 +70,7 @@ proc writeAdvancedUsage(pass: TCmdLinePass) = if pass == passCmd1: msgWriteln(`%`(HelpMessage, [VersionAsString, platform.OS[platform.hostOS].name, - CPU[platform.hostCPU].name, CompileDate, CompileTime]) & + CPU[platform.hostCPU].name, CompileDate]) & AdvancedUsage, {msgStdout}) msgQuit(0) @@ -89,7 +79,7 @@ proc writeFullhelp(pass: TCmdLinePass) = if pass == passCmd1: msgWriteln(`%`(HelpMessage, [VersionAsString, platform.OS[platform.hostOS].name, - CPU[platform.hostCPU].name, CompileDate, CompileTime]) & + CPU[platform.hostCPU].name, CompileDate]) & Usage & AdvancedUsage, {msgStdout}) msgQuit(0) @@ -98,7 +88,7 @@ proc writeVersionInfo(pass: TCmdLinePass) = if pass == passCmd1: msgWriteln(`%`(HelpMessage, [VersionAsString, platform.OS[platform.hostOS].name, - CPU[platform.hostCPU].name, CompileDate, CompileTime]), + CPU[platform.hostCPU].name, CompileDate]), {msgStdout}) const gitHash = gorge("git log -n 1 --format=%H").strip @@ -106,15 +96,12 @@ proc writeVersionInfo(pass: TCmdLinePass) = msgWriteln("git hash: " & gitHash, {msgStdout}) msgWriteln("active boot switches:" & usedRelease & - usedTinyC & usedGnuReadline & usedNativeStacktrace & usedNoCaas & + usedTinyC & usedGnuReadline & usedNativeStacktrace & usedFFI & usedBoehm & usedMarkAndSweep & usedGenerational & usedGoGC & usedNoGC, {msgStdout}) msgQuit(0) -var - helpWritten: bool - -proc writeCommandLineUsage() = +proc writeCommandLineUsage*(helpWritten: var bool) = if not helpWritten: msgWriteln(getCommandLineDesc(), {msgStdout}) helpWritten = true @@ -123,11 +110,16 @@ proc addPrefix(switch: string): string = if len(switch) == 1: result = "-" & switch else: result = "--" & switch -proc invalidCmdLineOption(pass: TCmdLinePass, switch: string, info: TLineInfo) = - if switch == " ": localError(info, errInvalidCmdLineOption, "-") - else: localError(info, errInvalidCmdLineOption, addPrefix(switch)) +const + errInvalidCmdLineOption = "invalid command line option: '$1'" + errOnOrOffExpectedButXFound = "'on' or 'off' expected, but '$1' found" + errOnOffOrListExpectedButXFound = "'on', 'off' or 'list' expected, but '$1' found" + +proc invalidCmdLineOption(conf: ConfigRef; pass: TCmdLinePass, switch: string, info: TLineInfo) = + if switch == " ": localError(conf, info, errInvalidCmdLineOption % "-") + else: localError(conf, info, errInvalidCmdLineOption % addPrefix(switch)) -proc splitSwitch(switch: string, cmd, arg: var string, pass: TCmdLinePass, +proc splitSwitch(conf: ConfigRef; switch: string, cmd, arg: var string, pass: TCmdLinePass, info: TLineInfo) = cmd = "" var i = 0 @@ -140,46 +132,41 @@ proc splitSwitch(switch: string, cmd, arg: var string, pass: TCmdLinePass, inc(i) if i >= len(switch): arg = "" elif switch[i] in {':', '=', '['}: arg = substr(switch, i + 1) - else: invalidCmdLineOption(pass, switch, info) + else: invalidCmdLineOption(conf, pass, switch, info) -proc processOnOffSwitch(op: TOptions, arg: string, pass: TCmdLinePass, +proc processOnOffSwitch(conf: ConfigRef; op: TOptions, arg: string, pass: TCmdLinePass, info: TLineInfo) = case arg.normalize of "on": gOptions = gOptions + op of "off": gOptions = gOptions - op - else: localError(info, errOnOrOffExpectedButXFound, arg) + else: localError(conf, info, errOnOrOffExpectedButXFound % arg) -proc processOnOffSwitchOrList(op: TOptions, arg: string, pass: TCmdLinePass, +proc processOnOffSwitchOrList(conf: ConfigRef; op: TOptions, arg: string, pass: TCmdLinePass, info: TLineInfo): bool = result = false case arg.normalize of "on": gOptions = gOptions + op of "off": gOptions = gOptions - op - else: - if arg == "list": - result = true - else: - localError(info, errOnOffOrListExpectedButXFound, arg) + of "list": result = true + else: localError(conf, info, errOnOffOrListExpectedButXFound % arg) -proc processOnOffSwitchG(op: TGlobalOptions, arg: string, pass: TCmdLinePass, +proc processOnOffSwitchG(conf: ConfigRef; op: TGlobalOptions, arg: string, pass: TCmdLinePass, info: TLineInfo) = case arg.normalize of "on": gGlobalOptions = gGlobalOptions + op of "off": gGlobalOptions = gGlobalOptions - op - else: localError(info, errOnOrOffExpectedButXFound, arg) - -proc expectArg(switch, arg: string, pass: TCmdLinePass, info: TLineInfo) = - if arg == "": localError(info, errCmdLineArgExpected, addPrefix(switch)) + else: localError(conf, info, errOnOrOffExpectedButXFound % arg) -proc expectNoArg(switch, arg: string, pass: TCmdLinePass, info: TLineInfo) = - if arg != "": localError(info, errCmdLineNoArgExpected, addPrefix(switch)) +proc expectArg(conf: ConfigRef; switch, arg: string, pass: TCmdLinePass, info: TLineInfo) = + if arg == "": + localError(conf, info, "argument for command line option expected: '$1'" % addPrefix(switch)) -var - enableNotes: TNoteKinds - disableNotes: TNoteKinds +proc expectNoArg(conf: ConfigRef; switch, arg: string, pass: TCmdLinePass, info: TLineInfo) = + if arg != "": + localError(conf, info, "invalid argument for command line option: '$1'" % addPrefix(switch)) proc processSpecificNote*(arg: string, state: TSpecialWord, pass: TCmdLinePass, - info: TLineInfo; orig: string) = + info: TLineInfo; orig: string; conf: ConfigRef) = var id = "" # arg = "X]:on|off" var i = 0 var n = hintMin @@ -187,35 +174,40 @@ proc processSpecificNote*(arg: string, state: TSpecialWord, pass: TCmdLinePass, add(id, arg[i]) inc(i) if i < len(arg) and (arg[i] == ']'): inc(i) - else: invalidCmdLineOption(pass, orig, info) + else: invalidCmdLineOption(conf, pass, orig, info) if i < len(arg) and (arg[i] in {':', '='}): inc(i) - else: invalidCmdLineOption(pass, orig, info) + else: invalidCmdLineOption(conf, pass, orig, info) if state == wHint: - let x = findStr(msgs.HintsToStr, id) + let x = findStr(configuration.HintsToStr, id) if x >= 0: n = TNoteKind(x + ord(hintMin)) - else: localError(info, "unknown hint: " & id) + else: localError(conf, info, "unknown hint: " & id) else: - let x = findStr(msgs.WarningsToStr, id) + let x = findStr(configuration.WarningsToStr, id) if x >= 0: n = TNoteKind(x + ord(warnMin)) - else: localError(info, "unknown warning: " & id) + else: localError(conf, info, "unknown warning: " & id) case substr(arg, i).normalize of "on": - incl(gNotes, n) - incl(gMainPackageNotes, n) - incl(enableNotes, n) + incl(conf.notes, n) + incl(conf.mainPackageNotes, n) + incl(conf.enableNotes, n) of "off": - excl(gNotes, n) - excl(gMainPackageNotes, n) - incl(disableNotes, n) - excl(ForeignPackageNotes, n) - else: localError(info, errOnOrOffExpectedButXFound, arg) - -proc processCompile(filename: string) = - var found = findFile(filename) + excl(conf.notes, n) + excl(conf.mainPackageNotes, n) + incl(conf.disableNotes, n) + excl(conf.foreignPackageNotes, n) + else: localError(conf, info, errOnOrOffExpectedButXFound % arg) + +proc processCompile(conf: ConfigRef; filename: string) = + var found = findFile(conf, filename) if found == "": found = filename - extccomp.addExternalFileToCompile(found) + extccomp.addExternalFileToCompile(conf, found) + +const + errNoneBoehmRefcExpectedButXFound = "'none', 'boehm' or 'refc' expected, but '$1' found" + errNoneSpeedOrSizeExpectedButXFound = "'none', 'speed' or 'size' expected, but '$1' found" + errGuiConsoleOrLibExpectedButXFound = "'gui', 'console' or 'lib' expected, but '$1' found" -proc testCompileOptionArg*(switch, arg: string, info: TLineInfo): bool = +proc testCompileOptionArg*(conf: ConfigRef; switch, arg: string, info: TLineInfo): bool = case switch.normalize of "gc": case arg.normalize @@ -227,13 +219,13 @@ proc testCompileOptionArg*(switch, arg: string, info: TLineInfo): bool = of "go": result = gSelectedGC == gcGo of "none": result = gSelectedGC == gcNone of "stack", "regions": result = gSelectedGC == gcRegions - else: localError(info, errNoneBoehmRefcExpectedButXFound, arg) + else: localError(conf, info, errNoneBoehmRefcExpectedButXFound % arg) of "opt": case arg.normalize of "speed": result = contains(gOptions, optOptimizeSpeed) of "size": result = contains(gOptions, optOptimizeSize) of "none": result = gOptions * {optOptimizeSpeed, optOptimizeSize} == {} - else: localError(info, errNoneSpeedOrSizeExpectedButXFound, arg) + else: localError(conf, info, errNoneSpeedOrSizeExpectedButXFound % arg) of "verbosity": result = $gVerbosity == arg of "app": case arg.normalize @@ -243,12 +235,12 @@ proc testCompileOptionArg*(switch, arg: string, info: TLineInfo): bool = not contains(gGlobalOptions, optGenGuiApp) of "staticlib": result = contains(gGlobalOptions, optGenStaticLib) and not contains(gGlobalOptions, optGenGuiApp) - else: localError(info, errGuiConsoleOrLibExpectedButXFound, arg) + else: localError(conf, info, errGuiConsoleOrLibExpectedButXFound % arg) of "dynliboverride": - result = isDynlibOverride(arg) - else: invalidCmdLineOption(passCmd1, switch, info) + result = isDynlibOverride(conf, arg) + else: invalidCmdLineOption(conf, passCmd1, switch, info) -proc testCompileOption*(switch: string, info: TLineInfo): bool = +proc testCompileOption*(conf: ConfigRef; switch: string, info: TLineInfo): bool = case switch.normalize of "debuginfo": result = contains(gGlobalOptions, optCDebug) of "compileonly", "c": result = contains(gGlobalOptions, optCompileOnly) @@ -286,9 +278,9 @@ proc testCompileOption*(switch: string, info: TLineInfo): bool = of "implicitstatic": result = contains(gOptions, optImplicitStatic) of "patterns": result = contains(gOptions, optPatterns) of "excessivestacktrace": result = contains(gGlobalOptions, optExcessiveStackTrace) - else: invalidCmdLineOption(passCmd1, switch, info) + else: invalidCmdLineOption(conf, passCmd1, switch, info) -proc processPath(path: string, info: TLineInfo, +proc processPath(conf: ConfigRef; path: string, info: TLineInfo, notRelativeToProj = false): string = let p = if os.isAbsolute(path) or '$' in path: path @@ -297,12 +289,12 @@ proc processPath(path: string, info: TLineInfo, else: options.gProjectPath / path try: - result = pathSubs(p, info.toFullPath().splitFile().dir) + result = pathSubs(conf, p, info.toFullPath().splitFile().dir) except ValueError: - localError(info, "invalid path: " & p) + localError(conf, info, "invalid path: " & p) result = p -proc processCfgPath(path: string, info: TLineInfo): string = +proc processCfgPath(conf: ConfigRef; path: string, info: TLineInfo): string = let path = if path[0] == '"': strutils.unescape(path) else: path let basedir = info.toFullPath().splitFile().dir let p = if os.isAbsolute(path) or '$' in path: @@ -310,225 +302,228 @@ proc processCfgPath(path: string, info: TLineInfo): string = else: basedir / path try: - result = pathSubs(p, basedir) + result = pathSubs(conf, p, basedir) except ValueError: - localError(info, "invalid path: " & p) + localError(conf, info, "invalid path: " & p) result = p -proc trackDirty(arg: string, info: TLineInfo) = +const + errInvalidNumber = "$1 is not a valid number" + +proc trackDirty(conf: ConfigRef; arg: string, info: TLineInfo) = var a = arg.split(',') - if a.len != 4: localError(info, errTokenExpected, - "DIRTY_BUFFER,ORIGINAL_FILE,LINE,COLUMN") + if a.len != 4: localError(conf, info, + "DIRTY_BUFFER,ORIGINAL_FILE,LINE,COLUMN expected") var line, column: int if parseUtils.parseInt(a[2], line) <= 0: - localError(info, errInvalidNumber, a[1]) + localError(conf, info, errInvalidNumber % a[1]) if parseUtils.parseInt(a[3], column) <= 0: - localError(info, errInvalidNumber, a[2]) + localError(conf, info, errInvalidNumber % a[2]) - let dirtyOriginalIdx = a[1].fileInfoIdx + let dirtyOriginalIdx = fileInfoIdx(conf, a[1]) if dirtyOriginalIdx.int32 >= 0: msgs.setDirtyFile(dirtyOriginalIdx, a[0]) gTrackPos = newLineInfo(dirtyOriginalIdx, line, column) -proc track(arg: string, info: TLineInfo) = +proc track(conf: ConfigRef; arg: string, info: TLineInfo) = var a = arg.split(',') - if a.len != 3: localError(info, errTokenExpected, "FILE,LINE,COLUMN") + if a.len != 3: localError(conf, info, "FILE,LINE,COLUMN expected") var line, column: int if parseUtils.parseInt(a[1], line) <= 0: - localError(info, errInvalidNumber, a[1]) + localError(conf, info, errInvalidNumber % a[1]) if parseUtils.parseInt(a[2], column) <= 0: - localError(info, errInvalidNumber, a[2]) - gTrackPos = newLineInfo(a[0], line, column) + localError(conf, info, errInvalidNumber % a[2]) + gTrackPos = newLineInfo(conf, a[0], line, column) -proc dynlibOverride(switch, arg: string, pass: TCmdLinePass, info: TLineInfo) = +proc dynlibOverride(conf: ConfigRef; switch, arg: string, pass: TCmdLinePass, info: TLineInfo) = if pass in {passCmd2, passPP}: - expectArg(switch, arg, pass, info) - options.inclDynlibOverride(arg) + expectArg(conf, switch, arg, pass, info) + options.inclDynlibOverride(conf, arg) -proc processSwitch(switch, arg: string, pass: TCmdLinePass, info: TLineInfo; - config: ConfigRef) = +proc processSwitch*(switch, arg: string, pass: TCmdLinePass, info: TLineInfo; + conf: ConfigRef) = var theOS: TSystemOS cpu: TSystemCPU key, val: string case switch.normalize of "path", "p": - expectArg(switch, arg, pass, info) - addPath(if pass == passPP: processCfgPath(arg, info) else: processPath(arg, info), info) + expectArg(conf, switch, arg, pass, info) + addPath(conf, if pass == passPP: processCfgPath(conf, arg, info) else: processPath(conf, arg, info), info) of "nimblepath", "babelpath": # keep the old name for compat if pass in {passCmd2, passPP} and not options.gNoNimblePath: - expectArg(switch, arg, pass, info) - var path = processPath(arg, info, notRelativeToProj=true) + expectArg(conf, switch, arg, pass, info) + var path = processPath(conf, arg, info, notRelativeToProj=true) let nimbleDir = getEnv("NIMBLE_DIR") if nimbleDir.len > 0 and pass == passPP: path = nimbleDir / "pkgs" - nimblePath(path, info) + nimblePath(conf, path, info) of "nonimblepath", "nobabelpath": - expectNoArg(switch, arg, pass, info) - disableNimblePath() + expectNoArg(conf, switch, arg, pass, info) + disableNimblePath(conf) of "excludepath": - expectArg(switch, arg, pass, info) - let path = processPath(arg, info) + expectArg(conf, switch, arg, pass, info) + let path = processPath(conf, arg, info) - options.searchPaths.keepItIf( cmpPaths(it, path) != 0 ) - options.lazyPaths.keepItIf( cmpPaths(it, path) != 0 ) + options.searchPaths.keepItIf(cmpPaths(it, path) != 0) + options.lazyPaths.keepItIf(cmpPaths(it, path) != 0) if (len(path) > 0) and (path[len(path) - 1] == DirSep): let strippedPath = path[0 .. (len(path) - 2)] - options.searchPaths.keepItIf( cmpPaths(it, strippedPath) != 0 ) - options.lazyPaths.keepItIf( cmpPaths(it, strippedPath) != 0 ) + options.searchPaths.keepItIf(cmpPaths(it, strippedPath) != 0) + options.lazyPaths.keepItIf(cmpPaths(it, strippedPath) != 0) of "nimcache": - expectArg(switch, arg, pass, info) - options.nimcacheDir = processPath(arg, info, true) + expectArg(conf, switch, arg, pass, info) + options.nimcacheDir = processPath(conf, arg, info, true) of "out", "o": - expectArg(switch, arg, pass, info) + expectArg(conf, switch, arg, pass, info) options.outFile = arg of "docseesrcurl": - expectArg(switch, arg, pass, info) + expectArg(conf, switch, arg, pass, info) options.docSeeSrcUrl = arg of "mainmodule", "m": discard "allow for backwards compatibility, but don't do anything" of "define", "d": - expectArg(switch, arg, pass, info) + expectArg(conf, switch, arg, pass, info) if {':', '='} in arg: - splitSwitch(arg, key, val, pass, info) - defineSymbol(key, val) + splitSwitch(conf, arg, key, val, pass, info) + defineSymbol(conf.symbols, key, val) else: - defineSymbol(arg) + defineSymbol(conf.symbols, arg) of "undef", "u": - expectArg(switch, arg, pass, info) - undefSymbol(arg) + expectArg(conf, switch, arg, pass, info) + undefSymbol(conf.symbols, arg) of "symbol": - expectArg(switch, arg, pass, info) + expectArg(conf, switch, arg, pass, info) # deprecated, do nothing of "compile": - expectArg(switch, arg, pass, info) - if pass in {passCmd2, passPP}: processCompile(arg) + expectArg(conf, switch, arg, pass, info) + if pass in {passCmd2, passPP}: processCompile(conf, arg) of "link": - expectArg(switch, arg, pass, info) - if pass in {passCmd2, passPP}: addExternalFileToLink(arg) + expectArg(conf, switch, arg, pass, info) + if pass in {passCmd2, passPP}: addExternalFileToLink(conf, arg) of "debuginfo": - expectNoArg(switch, arg, pass, info) + expectNoArg(conf, switch, arg, pass, info) incl(gGlobalOptions, optCDebug) of "embedsrc": - expectNoArg(switch, arg, pass, info) + expectNoArg(conf, switch, arg, pass, info) incl(gGlobalOptions, optEmbedOrigSrc) of "compileonly", "c": - expectNoArg(switch, arg, pass, info) + expectNoArg(conf, switch, arg, pass, info) incl(gGlobalOptions, optCompileOnly) of "nolinking": - expectNoArg(switch, arg, pass, info) + expectNoArg(conf, switch, arg, pass, info) incl(gGlobalOptions, optNoLinking) of "nomain": - expectNoArg(switch, arg, pass, info) + expectNoArg(conf, switch, arg, pass, info) incl(gGlobalOptions, optNoMain) of "forcebuild", "f": - expectNoArg(switch, arg, pass, info) + expectNoArg(conf, switch, arg, pass, info) incl(gGlobalOptions, optForceFullMake) of "project": - expectNoArg(switch, arg, pass, info) + expectNoArg(conf, switch, arg, pass, info) gWholeProject = true of "gc": - expectArg(switch, arg, pass, info) + expectArg(conf, switch, arg, pass, info) case arg.normalize of "boehm": gSelectedGC = gcBoehm - defineSymbol("boehmgc") + defineSymbol(conf.symbols, "boehmgc") of "refc": gSelectedGC = gcRefc of "v2": gSelectedGC = gcV2 of "markandsweep": gSelectedGC = gcMarkAndSweep - defineSymbol("gcmarkandsweep") + defineSymbol(conf.symbols, "gcmarkandsweep") of "generational": gSelectedGC = gcGenerational - defineSymbol("gcgenerational") + defineSymbol(conf.symbols, "gcgenerational") of "go": gSelectedGC = gcGo - defineSymbol("gogc") + defineSymbol(conf.symbols, "gogc") of "none": gSelectedGC = gcNone - defineSymbol("nogc") + defineSymbol(conf.symbols, "nogc") of "stack", "regions": gSelectedGC= gcRegions - defineSymbol("gcregions") - else: localError(info, errNoneBoehmRefcExpectedButXFound, arg) + defineSymbol(conf.symbols, "gcregions") + else: localError(conf, info, errNoneBoehmRefcExpectedButXFound % arg) of "warnings", "w": - if processOnOffSwitchOrList({optWarns}, arg, pass, info): listWarnings() - of "warning": processSpecificNote(arg, wWarning, pass, info, switch) - of "hint": processSpecificNote(arg, wHint, pass, info, switch) + if processOnOffSwitchOrList(conf, {optWarns}, arg, pass, info): listWarnings(conf) + of "warning": processSpecificNote(arg, wWarning, pass, info, switch, conf) + of "hint": processSpecificNote(arg, wHint, pass, info, switch, conf) of "hints": - if processOnOffSwitchOrList({optHints}, arg, pass, info): listHints() - of "threadanalysis": processOnOffSwitchG({optThreadAnalysis}, arg, pass, info) - of "stacktrace": processOnOffSwitch({optStackTrace}, arg, pass, info) - of "excessivestacktrace": processOnOffSwitchG({optExcessiveStackTrace}, arg, pass, info) - of "linetrace": processOnOffSwitch({optLineTrace}, arg, pass, info) + if processOnOffSwitchOrList(conf, {optHints}, arg, pass, info): listHints(conf) + of "threadanalysis": processOnOffSwitchG(conf, {optThreadAnalysis}, arg, pass, info) + of "stacktrace": processOnOffSwitch(conf, {optStackTrace}, arg, pass, info) + of "excessivestacktrace": processOnOffSwitchG(conf, {optExcessiveStackTrace}, arg, pass, info) + of "linetrace": processOnOffSwitch(conf, {optLineTrace}, arg, pass, info) of "debugger": case arg.normalize of "on", "endb": gOptions.incl optEndb - defineSymbol("endb") + defineSymbol(conf.symbols, "endb") of "off": gOptions.excl optEndb - undefSymbol("endb") + undefSymbol(conf.symbols, "endb") of "native", "gdb": incl(gGlobalOptions, optCDebug) gOptions = gOptions + {optLineDir} - {optEndb} - defineSymbol("nimTypeNames", nil) # type names are used in gdb pretty printing - undefSymbol("endb") + defineSymbol(conf.symbols, "nimTypeNames") # type names are used in gdb pretty printing + undefSymbol(conf.symbols, "endb") else: - localError(info, "expected endb|gdb but found " & arg) + localError(conf, info, "expected endb|gdb but found " & arg) of "profiler": - processOnOffSwitch({optProfiler}, arg, pass, info) - if optProfiler in gOptions: defineSymbol("profiler") - else: undefSymbol("profiler") + processOnOffSwitch(conf, {optProfiler}, arg, pass, info) + if optProfiler in gOptions: defineSymbol(conf.symbols, "profiler") + else: undefSymbol(conf.symbols, "profiler") of "memtracker": - processOnOffSwitch({optMemTracker}, arg, pass, info) - if optMemTracker in gOptions: defineSymbol("memtracker") - else: undefSymbol("memtracker") + processOnOffSwitch(conf, {optMemTracker}, arg, pass, info) + if optMemTracker in gOptions: defineSymbol(conf.symbols, "memtracker") + else: undefSymbol(conf.symbols, "memtracker") of "hotcodereloading": - processOnOffSwitch({optHotCodeReloading}, arg, pass, info) - if optHotCodeReloading in gOptions: defineSymbol("hotcodereloading") - else: undefSymbol("hotcodereloading") + processOnOffSwitch(conf, {optHotCodeReloading}, arg, pass, info) + if optHotCodeReloading in gOptions: defineSymbol(conf.symbols, "hotcodereloading") + else: undefSymbol(conf.symbols, "hotcodereloading") of "oldnewlines": case arg.normalize of "on": options.gOldNewlines = true - defineSymbol("nimOldNewlines") + defineSymbol(conf.symbols, "nimOldNewlines") of "off": options.gOldNewlines = false - undefSymbol("nimOldNewlines") + undefSymbol(conf.symbols, "nimOldNewlines") else: - localError(info, errOnOrOffExpectedButXFound, arg) - of "laxstrings": processOnOffSwitch({optLaxStrings}, arg, pass, info) - of "checks", "x": processOnOffSwitch(ChecksOptions, arg, pass, info) + localError(conf, info, errOnOrOffExpectedButXFound % arg) + of "laxstrings": processOnOffSwitch(conf, {optLaxStrings}, arg, pass, info) + of "checks", "x": processOnOffSwitch(conf, ChecksOptions, arg, pass, info) of "floatchecks": - processOnOffSwitch({optNaNCheck, optInfCheck}, arg, pass, info) - of "infchecks": processOnOffSwitch({optInfCheck}, arg, pass, info) - of "nanchecks": processOnOffSwitch({optNaNCheck}, arg, pass, info) - of "nilchecks": processOnOffSwitch({optNilCheck}, arg, pass, info) - of "objchecks": processOnOffSwitch({optObjCheck}, arg, pass, info) - of "fieldchecks": processOnOffSwitch({optFieldCheck}, arg, pass, info) - of "rangechecks": processOnOffSwitch({optRangeCheck}, arg, pass, info) - of "boundchecks": processOnOffSwitch({optBoundsCheck}, arg, pass, info) - of "overflowchecks": processOnOffSwitch({optOverflowCheck}, arg, pass, info) - of "movechecks": processOnOffSwitch({optMoveCheck}, arg, pass, info) - of "linedir": processOnOffSwitch({optLineDir}, arg, pass, info) - of "assertions", "a": processOnOffSwitch({optAssert}, arg, pass, info) + processOnOffSwitch(conf, {optNaNCheck, optInfCheck}, arg, pass, info) + of "infchecks": processOnOffSwitch(conf, {optInfCheck}, arg, pass, info) + of "nanchecks": processOnOffSwitch(conf, {optNaNCheck}, arg, pass, info) + of "nilchecks": processOnOffSwitch(conf, {optNilCheck}, arg, pass, info) + of "objchecks": processOnOffSwitch(conf, {optObjCheck}, arg, pass, info) + of "fieldchecks": processOnOffSwitch(conf, {optFieldCheck}, arg, pass, info) + of "rangechecks": processOnOffSwitch(conf, {optRangeCheck}, arg, pass, info) + of "boundchecks": processOnOffSwitch(conf, {optBoundsCheck}, arg, pass, info) + of "overflowchecks": processOnOffSwitch(conf, {optOverflowCheck}, arg, pass, info) + of "movechecks": processOnOffSwitch(conf, {optMoveCheck}, arg, pass, info) + of "linedir": processOnOffSwitch(conf, {optLineDir}, arg, pass, info) + of "assertions", "a": processOnOffSwitch(conf, {optAssert}, arg, pass, info) of "deadcodeelim": discard # deprecated, dead code elim always on of "threads": - processOnOffSwitchG({optThreads}, arg, pass, info) - #if optThreads in gGlobalOptions: incl(gNotes, warnGcUnsafe) - of "tlsemulation": processOnOffSwitchG({optTlsEmulation}, arg, pass, info) - of "taintmode": processOnOffSwitchG({optTaintMode}, arg, pass, info) + processOnOffSwitchG(conf, {optThreads}, arg, pass, info) + #if optThreads in gGlobalOptions: incl(conf.notes, warnGcUnsafe) + of "tlsemulation": processOnOffSwitchG(conf, {optTlsEmulation}, arg, pass, info) + of "taintmode": processOnOffSwitchG(conf, {optTaintMode}, arg, pass, info) of "implicitstatic": - processOnOffSwitch({optImplicitStatic}, arg, pass, info) + processOnOffSwitch(conf, {optImplicitStatic}, arg, pass, info) of "patterns": - processOnOffSwitch({optPatterns}, arg, pass, info) + processOnOffSwitch(conf, {optPatterns}, arg, pass, info) of "opt": - expectArg(switch, arg, pass, info) + expectArg(conf, switch, arg, pass, info) case arg.normalize of "speed": incl(gOptions, optOptimizeSpeed) @@ -539,99 +534,99 @@ proc processSwitch(switch, arg: string, pass: TCmdLinePass, info: TLineInfo; of "none": excl(gOptions, optOptimizeSpeed) excl(gOptions, optOptimizeSize) - else: localError(info, errNoneSpeedOrSizeExpectedButXFound, arg) + else: localError(conf, info, errNoneSpeedOrSizeExpectedButXFound % arg) of "app": - expectArg(switch, arg, pass, info) + expectArg(conf, switch, arg, pass, info) case arg.normalize of "gui": incl(gGlobalOptions, optGenGuiApp) - defineSymbol("executable") - defineSymbol("guiapp") + defineSymbol(conf.symbols, "executable") + defineSymbol(conf.symbols, "guiapp") of "console": excl(gGlobalOptions, optGenGuiApp) - defineSymbol("executable") - defineSymbol("consoleapp") + defineSymbol(conf.symbols, "executable") + defineSymbol(conf.symbols, "consoleapp") of "lib": incl(gGlobalOptions, optGenDynLib) excl(gGlobalOptions, optGenGuiApp) - defineSymbol("library") - defineSymbol("dll") + defineSymbol(conf.symbols, "library") + defineSymbol(conf.symbols, "dll") of "staticlib": incl(gGlobalOptions, optGenStaticLib) excl(gGlobalOptions, optGenGuiApp) - defineSymbol("library") - defineSymbol("staticlib") - else: localError(info, errGuiConsoleOrLibExpectedButXFound, arg) + defineSymbol(conf.symbols, "library") + defineSymbol(conf.symbols, "staticlib") + else: localError(conf, info, errGuiConsoleOrLibExpectedButXFound % arg) of "passc", "t": - expectArg(switch, arg, pass, info) - if pass in {passCmd2, passPP}: extccomp.addCompileOptionCmd(arg) + expectArg(conf, switch, arg, pass, info) + if pass in {passCmd2, passPP}: extccomp.addCompileOptionCmd(conf, arg) of "passl", "l": - expectArg(switch, arg, pass, info) - if pass in {passCmd2, passPP}: extccomp.addLinkOptionCmd(arg) + expectArg(conf, switch, arg, pass, info) + if pass in {passCmd2, passPP}: extccomp.addLinkOptionCmd(conf, arg) of "cincludes": - expectArg(switch, arg, pass, info) - if pass in {passCmd2, passPP}: cIncludes.add arg.processPath(info) + expectArg(conf, switch, arg, pass, info) + if pass in {passCmd2, passPP}: cIncludes.add processPath(conf, arg, info) of "clibdir": - expectArg(switch, arg, pass, info) - if pass in {passCmd2, passPP}: cLibs.add arg.processPath(info) + expectArg(conf, switch, arg, pass, info) + if pass in {passCmd2, passPP}: cLibs.add processPath(conf, arg, info) of "clib": - expectArg(switch, arg, pass, info) - if pass in {passCmd2, passPP}: cLinkedLibs.add arg.processPath(info) + expectArg(conf, switch, arg, pass, info) + if pass in {passCmd2, passPP}: cLinkedLibs.add processPath(conf, arg, info) of "header": - if config != nil: config.headerFile = arg + if conf != nil: conf.headerFile = arg incl(gGlobalOptions, optGenIndex) of "index": - processOnOffSwitchG({optGenIndex}, arg, pass, info) + processOnOffSwitchG(conf, {optGenIndex}, arg, pass, info) of "import": - expectArg(switch, arg, pass, info) + expectArg(conf, switch, arg, pass, info) if pass in {passCmd2, passPP}: implicitImports.add arg of "include": - expectArg(switch, arg, pass, info) + expectArg(conf, switch, arg, pass, info) if pass in {passCmd2, passPP}: implicitIncludes.add arg of "listcmd": - expectNoArg(switch, arg, pass, info) + expectNoArg(conf, switch, arg, pass, info) incl(gGlobalOptions, optListCmd) of "genmapping": - expectNoArg(switch, arg, pass, info) + expectNoArg(conf, switch, arg, pass, info) incl(gGlobalOptions, optGenMapping) of "os": - expectArg(switch, arg, pass, info) + expectArg(conf, switch, arg, pass, info) if pass in {passCmd1, passPP}: theOS = platform.nameToOS(arg) - if theOS == osNone: localError(info, errUnknownOS, arg) + if theOS == osNone: localError(conf, info, "unknown OS: '$1'" % arg) elif theOS != platform.hostOS: setTarget(theOS, targetCPU) of "cpu": - expectArg(switch, arg, pass, info) + expectArg(conf, switch, arg, pass, info) if pass in {passCmd1, passPP}: cpu = platform.nameToCPU(arg) - if cpu == cpuNone: localError(info, errUnknownCPU, arg) + if cpu == cpuNone: localError(conf, info, "unknown CPU: '$1'" % arg) elif cpu != platform.hostCPU: setTarget(targetOS, cpu) of "run", "r": - expectNoArg(switch, arg, pass, info) + expectNoArg(conf, switch, arg, pass, info) incl(gGlobalOptions, optRun) of "verbosity": - expectArg(switch, arg, pass, info) + expectArg(conf, switch, arg, pass, info) gVerbosity = parseInt(arg) - gNotes = NotesVerbosity[gVerbosity] - incl(gNotes, enableNotes) - excl(gNotes, disableNotes) - gMainPackageNotes = gNotes + conf.notes = NotesVerbosity[gVerbosity] + incl(conf.notes, conf.enableNotes) + excl(conf.notes, conf.disableNotes) + conf.mainPackageNotes = conf.notes of "parallelbuild": - expectArg(switch, arg, pass, info) + expectArg(conf, switch, arg, pass, info) gNumberOfProcessors = parseInt(arg) of "version", "v": - expectNoArg(switch, arg, pass, info) + expectNoArg(conf, switch, arg, pass, info) writeVersionInfo(pass) of "advanced": - expectNoArg(switch, arg, pass, info) + expectNoArg(conf, switch, arg, pass, info) writeAdvancedUsage(pass) of "fullhelp": - expectNoArg(switch, arg, pass, info) + expectNoArg(conf, switch, arg, pass, info) writeFullhelp(pass) of "help", "h": - expectNoArg(switch, arg, pass, info) + expectNoArg(conf, switch, arg, pass, info) helpOnError(pass) of "symbolfiles": case arg.normalize @@ -640,101 +635,103 @@ proc processSwitch(switch, arg: string, pass: TCmdLinePass, info: TLineInfo; of "writeonly": gSymbolFiles = writeOnlySf of "readonly": gSymbolFiles = readOnlySf of "v2": gSymbolFiles = v2Sf - else: localError(info, errOnOrOffExpectedButXFound, arg) + else: localError(conf, info, "invalid option for --symbolFiles: " & arg) of "skipcfg": - expectNoArg(switch, arg, pass, info) + expectNoArg(conf, switch, arg, pass, info) incl(gGlobalOptions, optSkipConfigFile) of "skipprojcfg": - expectNoArg(switch, arg, pass, info) + expectNoArg(conf, switch, arg, pass, info) incl(gGlobalOptions, optSkipProjConfigFile) of "skipusercfg": - expectNoArg(switch, arg, pass, info) + expectNoArg(conf, switch, arg, pass, info) incl(gGlobalOptions, optSkipUserConfigFile) of "skipparentcfg": - expectNoArg(switch, arg, pass, info) + expectNoArg(conf, switch, arg, pass, info) incl(gGlobalOptions, optSkipParentConfigFiles) of "genscript", "gendeps": - expectNoArg(switch, arg, pass, info) + expectNoArg(conf, switch, arg, pass, info) incl(gGlobalOptions, optGenScript) incl(gGlobalOptions, optCompileOnly) - of "colors": processOnOffSwitchG({optUseColors}, arg, pass, info) + of "colors": processOnOffSwitchG(conf, {optUseColors}, arg, pass, info) of "lib": - expectArg(switch, arg, pass, info) - libpath = processPath(arg, info, notRelativeToProj=true) + expectArg(conf, switch, arg, pass, info) + libpath = processPath(conf, arg, info, notRelativeToProj=true) of "putenv": - expectArg(switch, arg, pass, info) - splitSwitch(arg, key, val, pass, info) + expectArg(conf, switch, arg, pass, info) + splitSwitch(conf, arg, key, val, pass, info) os.putEnv(key, val) of "cc": - expectArg(switch, arg, pass, info) - setCC(arg) + expectArg(conf, switch, arg, pass, info) + setCC(conf, arg, info) of "track": - expectArg(switch, arg, pass, info) - track(arg, info) + expectArg(conf, switch, arg, pass, info) + track(conf, arg, info) of "trackdirty": - expectArg(switch, arg, pass, info) - trackDirty(arg, info) + expectArg(conf, switch, arg, pass, info) + trackDirty(conf, arg, info) of "suggest": - expectNoArg(switch, arg, pass, info) + expectNoArg(conf, switch, arg, pass, info) gIdeCmd = ideSug of "def": - expectNoArg(switch, arg, pass, info) + expectNoArg(conf, switch, arg, pass, info) gIdeCmd = ideDef of "eval": - expectArg(switch, arg, pass, info) + expectArg(conf, switch, arg, pass, info) gEvalExpr = arg of "context": - expectNoArg(switch, arg, pass, info) + expectNoArg(conf, switch, arg, pass, info) gIdeCmd = ideCon of "usages": - expectNoArg(switch, arg, pass, info) + expectNoArg(conf, switch, arg, pass, info) gIdeCmd = ideUse of "stdout": - expectNoArg(switch, arg, pass, info) + expectNoArg(conf, switch, arg, pass, info) incl(gGlobalOptions, optStdout) of "listfullpaths": - expectNoArg(switch, arg, pass, info) + expectNoArg(conf, switch, arg, pass, info) gListFullPaths = true of "dynliboverride": - dynlibOverride(switch, arg, pass, info) + dynlibOverride(conf, switch, arg, pass, info) of "dynliboverrideall": - expectNoArg(switch, arg, pass, info) + expectNoArg(conf, switch, arg, pass, info) gDynlibOverrideAll = true of "cs": # only supported for compatibility. Does nothing. - expectArg(switch, arg, pass, info) + expectArg(conf, switch, arg, pass, info) of "experimental": if arg.len == 0: - config.features.incl oldExperimentalFeatures + conf.features.incl oldExperimentalFeatures else: try: - config.features.incl parseEnum[Feature](arg) + conf.features.incl parseEnum[Feature](arg) except ValueError: - localError(info, "unknown experimental feature") + localError(conf, info, "unknown experimental feature") of "nocppexceptions": - expectNoArg(switch, arg, pass, info) + expectNoArg(conf, switch, arg, pass, info) incl(gGlobalOptions, optNoCppExceptions) - defineSymbol("noCppExceptions") + defineSymbol(conf.symbols, "noCppExceptions") of "cppdefine": - expectArg(switch, arg, pass, info) - if config != nil: - config.cppDefine(arg) + expectArg(conf, switch, arg, pass, info) + if conf != nil: + conf.cppDefine(arg) of "newruntime": - expectNoArg(switch, arg, pass, info) - doAssert(config != nil) - incl(config.features, destructor) - defineSymbol("nimNewRuntime") + expectNoArg(conf, switch, arg, pass, info) + doAssert(conf != nil) + incl(conf.features, destructor) + defineSymbol(conf.symbols, "nimNewRuntime") of "cppcompiletonamespace": - expectNoArg(switch, arg, pass, info) + expectNoArg(conf, switch, arg, pass, info) useNimNamespace = true - defineSymbol("cppCompileToNamespace") + defineSymbol(conf.symbols, "cppCompileToNamespace") else: - if strutils.find(switch, '.') >= 0: options.setConfigVar(switch, arg) - else: invalidCmdLineOption(pass, switch, info) + if strutils.find(switch, '.') >= 0: options.setConfigVar(conf, switch, arg) + else: invalidCmdLineOption(conf, pass, switch, info) + +template gCmdLineInfo*(): untyped = newLineInfo(FileIndex(0), 1, 1) -proc processCommand(switch: string, pass: TCmdLinePass; config: ConfigRef) = +proc processCommand*(switch: string, pass: TCmdLinePass; config: ConfigRef) = var cmd, arg: string - splitSwitch(switch, cmd, arg, pass, gCmdLineInfo) + splitSwitch(config, switch, cmd, arg, pass, gCmdLineInfo) processSwitch(cmd, arg, pass, gCmdLineInfo, config) diff --git a/compiler/condsyms.nim b/compiler/condsyms.nim index f8a75e68e..773c0faf9 100644 --- a/compiler/condsyms.nim +++ b/compiler/condsyms.nim @@ -12,77 +12,30 @@ import strtabs, platform, strutils, idents -# We need to use a StringTableRef here as defined symbols are always guaranteed -# to be style insensitive. Otherwise hell would break lose. -var gSymbols: StringTableRef - const catNone = "false" -proc defineSymbol*(symbol: string, value: string = "true") = - gSymbols[symbol] = value - -proc undefSymbol*(symbol: string) = - gSymbols[symbol] = catNone - -proc isDefined*(symbol: string): bool = - if gSymbols.hasKey(symbol): - result = gSymbols[symbol] != catNone - elif cmpIgnoreStyle(symbol, CPU[targetCPU].name) == 0: - result = true - elif cmpIgnoreStyle(symbol, platform.OS[targetOS].name) == 0: - result = true - else: - case symbol.normalize - of "x86": result = targetCPU == cpuI386 - of "itanium": result = targetCPU == cpuIa64 - of "x8664": result = targetCPU == cpuAmd64 - of "posix", "unix": - result = targetOS in {osLinux, osMorphos, osSkyos, osIrix, osPalmos, - osQnx, osAtari, osAix, - osHaiku, osVxWorks, osSolaris, osNetbsd, - osFreebsd, osOpenbsd, osDragonfly, osMacosx, - osAndroid} - of "linux": - result = targetOS in {osLinux, osAndroid} - of "bsd": - result = targetOS in {osNetbsd, osFreebsd, osOpenbsd, osDragonfly} - of "emulatedthreadvars": - result = platform.OS[targetOS].props.contains(ospLacksThreadVars) - of "msdos": result = targetOS == osDos - of "mswindows", "win32": result = targetOS == osWindows - of "macintosh": result = targetOS in {osMacos, osMacosx} - of "sunos": result = targetOS == osSolaris - of "littleendian": result = CPU[targetCPU].endian == platform.littleEndian - of "bigendian": result = CPU[targetCPU].endian == platform.bigEndian - of "cpu8": result = CPU[targetCPU].bit == 8 - of "cpu16": result = CPU[targetCPU].bit == 16 - of "cpu32": result = CPU[targetCPU].bit == 32 - of "cpu64": result = CPU[targetCPU].bit == 64 - of "nimrawsetjmp": - result = targetOS in {osSolaris, osNetbsd, osFreebsd, osOpenbsd, - osDragonfly, osMacosx} - else: discard - -proc isDefined*(symbol: PIdent): bool = isDefined(symbol.s) +proc defineSymbol*(symbols: StringTableRef; symbol: string, value: string = "true") = + symbols[symbol] = value -proc lookupSymbol*(symbol: string): string = - result = if isDefined(symbol): gSymbols[symbol] else: nil +proc undefSymbol*(symbols: StringTableRef; symbol: string) = + symbols[symbol] = catNone -proc lookupSymbol*(symbol: PIdent): string = lookupSymbol(symbol.s) +#proc lookupSymbol*(symbols: StringTableRef; symbol: string): string = +# result = if isDefined(symbol): gSymbols[symbol] else: nil -iterator definedSymbolNames*: string = - for key, val in pairs(gSymbols): +iterator definedSymbolNames*(symbols: StringTableRef): string = + for key, val in pairs(symbols): if val != catNone: yield key -proc countDefinedSymbols*(): int = +proc countDefinedSymbols*(symbols: StringTableRef): int = result = 0 - for key, val in pairs(gSymbols): + for key, val in pairs(symbols): if val != catNone: inc(result) -proc initDefines*() = - gSymbols = newStringTable(modeStyleInsensitive) +proc initDefines*(symbols: StringTableRef) = # for bootstrapping purposes and old code: + template defineSymbol(s) = symbols.defineSymbol(s) defineSymbol("nimhygiene") defineSymbol("niminheritable") defineSymbol("nimmixin") diff --git a/compiler/configuration.nim b/compiler/configuration.nim new file mode 100644 index 000000000..452a99964 --- /dev/null +++ b/compiler/configuration.nim @@ -0,0 +1,389 @@ +# +# +# The Nim Compiler +# (c) Copyright 2018 Andreas Rumpf +# +# See the file "copying.txt", included in this +# distribution, for details about the copyright. +# + +## This module contains the rather excessive configuration object that +## needs to be passed around to everything so that the compiler becomes +## more useful as a library. + +import tables + +const + explanationsBaseUrl* = "https://nim-lang.org/docs/manual" + +type + TMsgKind* = enum + errUnknown, errInternal, errIllFormedAstX, errCannotOpenFile, errGenerated, + errUser, + warnCannotOpenFile, + warnOctalEscape, warnXIsNeverRead, warnXmightNotBeenInit, + warnDeprecated, warnConfigDeprecated, + warnSmallLshouldNotBeUsed, warnUnknownMagic, warnRedefinitionOfLabel, + warnUnknownSubstitutionX, warnLanguageXNotSupported, + warnFieldXNotSupported, warnCommentXIgnored, + warnTypelessParam, + warnUseBase, warnWriteToForeignHeap, warnUnsafeCode, + warnEachIdentIsTuple, warnShadowIdent, + warnProveInit, warnProveField, warnProveIndex, warnGcUnsafe, warnGcUnsafe2, + warnUninit, warnGcMem, warnDestructor, warnLockLevel, warnResultShadowed, + warnInconsistentSpacing, warnUser, + hintSuccess, hintSuccessX, + hintLineTooLong, hintXDeclaredButNotUsed, hintConvToBaseNotNeeded, + hintConvFromXtoItselfNotNeeded, hintExprAlwaysX, hintQuitCalled, + hintProcessing, hintCodeBegin, hintCodeEnd, hintConf, hintPath, + hintConditionAlwaysTrue, hintName, hintPattern, + hintExecuting, hintLinking, hintDependency, + hintSource, hintPerformance, hintStackTrace, hintGCStats, + hintUser, hintUserRaw + +const + MsgKindToStr*: array[TMsgKind, string] = [ + errUnknown: "unknown error", + errInternal: "internal error: $1", + errIllFormedAstX: "illformed AST: $1", + errCannotOpenFile: "cannot open '$1'", + errGenerated: "$1", + errUser: "$1", + warnCannotOpenFile: "cannot open '$1'", + warnOctalEscape: "octal escape sequences do not exist; leading zero is ignored", + warnXIsNeverRead: "'$1' is never read", + warnXmightNotBeenInit: "'$1' might not have been initialized", + warnDeprecated: "$1 is deprecated", + warnConfigDeprecated: "config file '$1' is deprecated", + warnSmallLshouldNotBeUsed: "'l' should not be used as an identifier; may look like '1' (one)", + warnUnknownMagic: "unknown magic '$1' might crash the compiler", + warnRedefinitionOfLabel: "redefinition of label '$1'", + warnUnknownSubstitutionX: "unknown substitution '$1'", + warnLanguageXNotSupported: "language '$1' not supported", + warnFieldXNotSupported: "field '$1' not supported", + warnCommentXIgnored: "comment '$1' ignored", + warnTypelessParam: "'$1' has no type. Typeless parameters are deprecated; only allowed for 'template'", + warnUseBase: "use {.base.} for base methods; baseless methods are deprecated", + warnWriteToForeignHeap: "write to foreign heap", + warnUnsafeCode: "unsafe code: '$1'", + warnEachIdentIsTuple: "each identifier is a tuple", + warnShadowIdent: "shadowed identifier: '$1'", + warnProveInit: "Cannot prove that '$1' is initialized. This will become a compile time error in the future.", + warnProveField: "cannot prove that field '$1' is accessible", + warnProveIndex: "cannot prove index '$1' is valid", + warnGcUnsafe: "not GC-safe: '$1'", + warnGcUnsafe2: "$1", + warnUninit: "'$1' might not have been initialized", + warnGcMem: "'$1' uses GC'ed memory", + warnDestructor: "usage of a type with a destructor in a non destructible context. This will become a compile time error in the future.", + warnLockLevel: "$1", + warnResultShadowed: "Special variable 'result' is shadowed.", + warnInconsistentSpacing: "Number of spaces around '$#' is not consistent", + warnUser: "$1", + hintSuccess: "operation successful", + hintSuccessX: "operation successful ($# lines compiled; $# sec total; $#; $#)", + hintLineTooLong: "line too long", + hintXDeclaredButNotUsed: "'$1' is declared but not used", + hintConvToBaseNotNeeded: "conversion to base object is not needed", + hintConvFromXtoItselfNotNeeded: "conversion from $1 to itself is pointless", + hintExprAlwaysX: "expression evaluates always to '$1'", + hintQuitCalled: "quit() called", + hintProcessing: "$1", + hintCodeBegin: "generated code listing:", + hintCodeEnd: "end of listing", + hintConf: "used config file '$1'", + hintPath: "added path: '$1'", + hintConditionAlwaysTrue: "condition is always true: '$1'", + hintName: "name should be: '$1'", + hintPattern: "$1", + hintExecuting: "$1", + hintLinking: "", + hintDependency: "$1", + hintSource: "$1", + hintPerformance: "$1", + hintStackTrace: "$1", + hintGCStats: "$1", + hintUser: "$1", + hintUserRaw: "$1"] + +const + WarningsToStr* = ["CannotOpenFile", "OctalEscape", + "XIsNeverRead", "XmightNotBeenInit", + "Deprecated", "ConfigDeprecated", + "SmallLshouldNotBeUsed", "UnknownMagic", + "RedefinitionOfLabel", "UnknownSubstitutionX", + "LanguageXNotSupported", "FieldXNotSupported", + "CommentXIgnored", + "TypelessParam", "UseBase", "WriteToForeignHeap", + "UnsafeCode", "EachIdentIsTuple", "ShadowIdent", + "ProveInit", "ProveField", "ProveIndex", "GcUnsafe", "GcUnsafe2", "Uninit", + "GcMem", "Destructor", "LockLevel", "ResultShadowed", + "Spacing", "User"] + + HintsToStr* = ["Success", "SuccessX", "LineTooLong", + "XDeclaredButNotUsed", "ConvToBaseNotNeeded", "ConvFromXtoItselfNotNeeded", + "ExprAlwaysX", "QuitCalled", "Processing", "CodeBegin", "CodeEnd", "Conf", + "Path", "CondTrue", "Name", "Pattern", "Exec", "Link", "Dependency", + "Source", "Performance", "StackTrace", "GCStats", + "User", "UserRaw"] + +const + fatalMin* = errUnknown + fatalMax* = errInternal + errMin* = errUnknown + errMax* = errUser + warnMin* = warnCannotOpenFile + warnMax* = pred(hintSuccess) + hintMin* = hintSuccess + hintMax* = high(TMsgKind) + +static: + doAssert HintsToStr.len == ord(hintMax) - ord(hintMin) + 1 + doAssert WarningsToStr.len == ord(warnMax) - ord(warnMin) + 1 + +type + TNoteKind* = range[warnMin..hintMax] # "notes" are warnings or hints + TNoteKinds* = set[TNoteKind] + +const + NotesVerbosity*: array[0..3, TNoteKinds] = [ + {low(TNoteKind)..high(TNoteKind)} - {warnShadowIdent, warnUninit, + warnProveField, warnProveIndex, + warnGcUnsafe, + hintSuccessX, hintPath, hintConf, + hintProcessing, hintPattern, + hintDependency, + hintExecuting, hintLinking, + hintCodeBegin, hintCodeEnd, + hintSource, hintStackTrace, + hintGCStats}, + {low(TNoteKind)..high(TNoteKind)} - {warnShadowIdent, warnUninit, + warnProveField, warnProveIndex, + warnGcUnsafe, + hintPath, + hintDependency, + hintCodeBegin, hintCodeEnd, + hintSource, hintStackTrace, + hintGCStats}, + {low(TNoteKind)..high(TNoteKind)} - {hintStackTrace, warnUninit}, + {low(TNoteKind)..high(TNoteKind)}] + +#[ +errStringLiteralExpected: "string literal expected", +errIntLiteralExpected: "integer literal expected", +errIdentifierExpected: "identifier expected, but found '$1'", +errNewlineExpected: "newline expected, but found '$1'", +errInvalidModuleName: "invalid module name: '$1'", +errRecursiveDependencyX: "recursive dependency: '$1'", +errOnOrOffExpected: "'on' or 'off' expected", +errNoneSpeedOrSizeExpected: "'none', 'speed' or 'size' expected", +errInvalidPragma: "invalid pragma", +errUnknownPragma: "unknown pragma: '$1'", +errAtPopWithoutPush: "'pop' without a 'push' pragma", +errEmptyAsm: "empty asm statement", +errInvalidIndentation: "invalid indentation", +errExceptionAlreadyHandled: "exception already handled", +errYieldNotAllowedHere: "'yield' only allowed in an iterator", +errYieldNotAllowedInTryStmt: "'yield' cannot be used within 'try' in a non-inlined iterator", +errInvalidNumberOfYieldExpr: "invalid number of 'yield' expressions", +errCannotReturnExpr: "current routine cannot return an expression", +errNoReturnWithReturnTypeNotAllowed: "routines with NoReturn pragma are not allowed to have return type", +errAttemptToRedefine: "redefinition of '$1'", +errStmtInvalidAfterReturn: "statement not allowed after 'return', 'break', 'raise', 'continue' or proc call with noreturn pragma", +errStmtExpected: "statement expected", +errInvalidLabel: "'$1' is no label", +errInvalidCmdLineOption: "invalid command line option: '$1'", +errCmdLineArgExpected: "argument for command line option expected: '$1'", +errCmdLineNoArgExpected: "invalid argument for command line option: '$1'", +errInvalidVarSubstitution: "invalid variable substitution in '$1'", +errUnknownVar: "unknown variable: '$1'", +errUnknownCcompiler: "unknown C compiler: '$1'", +errOnOrOffExpectedButXFound: "'on' or 'off' expected, but '$1' found", +errOnOffOrListExpectedButXFound: "'on', 'off' or 'list' expected, but '$1' found", +errGenOutExpectedButXFound: "'c', 'c++' or 'yaml' expected, but '$1' found", +errArgsNeedRunOption: "arguments can only be given if the '--run' option is selected", +errInvalidMultipleAsgn: "multiple assignment is not allowed", +errColonOrEqualsExpected: "':' or '=' expected, but found '$1'", +errUndeclaredField: "undeclared field: '$1'", +errUndeclaredRoutine: "attempting to call undeclared routine: '$1'", +errUseQualifier: "ambiguous identifier: '$1' -- use a qualifier", +errTypeExpected: "type expected", +errSystemNeeds: "system module needs '$1'", +errExecutionOfProgramFailed: "execution of an external program failed: '$1'", +errNotOverloadable: "overloaded '$1' leads to ambiguous calls", +errInvalidArgForX: "invalid argument for '$1'", +errStmtHasNoEffect: "statement has no effect", +errXExpectsTypeOrValue: "'$1' expects a type or value", +errXExpectsArrayType: "'$1' expects an array type", +errIteratorCannotBeInstantiated: "'$1' cannot be instantiated because its body has not been compiled yet", +errExprXAmbiguous: "expression '$1' ambiguous in this context", +errConstantDivisionByZero: "division by zero", +errOrdinalTypeExpected: "ordinal type expected", +errOrdinalOrFloatTypeExpected: "ordinal or float type expected", +errOverOrUnderflow: "over- or underflow", +errCannotEvalXBecauseIncompletelyDefined: "cannot evaluate '$1' because type is not defined completely", +errChrExpectsRange0_255: "'chr' expects an int in the range 0..255", +errDynlibRequiresExportc: "'dynlib' requires 'exportc'", +errUndeclaredFieldX: "undeclared field: '$1'", +errNilAccess: "attempt to access a nil address", +errIndexOutOfBounds: "index out of bounds", +errIndexTypesDoNotMatch: "index types do not match", +errBracketsInvalidForType: "'[]' operator invalid for this type", +errValueOutOfSetBounds: "value out of set bounds", +errFieldInitTwice: "field initialized twice: '$1'", +errFieldNotInit: "field '$1' not initialized", +errExprXCannotBeCalled: "expression '$1' cannot be called", +errExprHasNoType: "expression has no type", +errExprXHasNoType: "expression '$1' has no type (or is ambiguous)", +errCastNotInSafeMode: "'cast' not allowed in safe mode", +errExprCannotBeCastToX: "expression cannot be cast to $1", +errCommaOrParRiExpected: "',' or ')' expected", +errCurlyLeOrParLeExpected: "'{' or '(' expected", +errSectionExpected: "section ('type', 'proc', etc.) expected", +errRangeExpected: "range expected", +errMagicOnlyInSystem: "'magic' only allowed in system module", +errPowerOfTwoExpected: "power of two expected", +errStringMayNotBeEmpty: "string literal may not be empty", +errCallConvExpected: "calling convention expected", +errProcOnlyOneCallConv: "a proc can only have one calling convention", +errSymbolMustBeImported: "symbol must be imported if 'lib' pragma is used", +errExprMustBeBool: "expression must be of type 'bool'", +errConstExprExpected: "constant expression expected", +errDuplicateCaseLabel: "duplicate case label", +errRangeIsEmpty: "range is empty", +errSelectorMustBeOfCertainTypes: "selector must be of an ordinal type, float or string", +errSelectorMustBeOrdinal: "selector must be of an ordinal type", +errOrdXMustNotBeNegative: "ord($1) must not be negative", +errLenXinvalid: "len($1) must be less than 32768", +errWrongNumberOfVariables: "wrong number of variables", +errExprCannotBeRaised: "only a 'ref object' can be raised", +errBreakOnlyInLoop: "'break' only allowed in loop construct", +errTypeXhasUnknownSize: "type '$1' has unknown size", +errConstNeedsConstExpr: "a constant can only be initialized with a constant expression", +errConstNeedsValue: "a constant needs a value", +errResultCannotBeOpenArray: "the result type cannot be on open array", +errSizeTooBig: "computing the type's size produced an overflow", +errSetTooBig: "set is too large", +errBaseTypeMustBeOrdinal: "base type of a set must be an ordinal", +errInheritanceOnlyWithNonFinalObjects: "inheritance only works with non-final objects", +errInheritanceOnlyWithEnums: "inheritance only works with an enum", +errIllegalRecursionInTypeX: "illegal recursion in type '$1'", +errCannotInstantiateX: "cannot instantiate: '$1'", +errExprHasNoAddress: "expression has no address", +errXStackEscape: "address of '$1' may not escape its stack frame", +errVarForOutParamNeededX: "for a 'var' type a variable needs to be passed; but '$1' is immutable", +errPureTypeMismatch: "type mismatch", +errTypeMismatch: "type mismatch: got <", +errButExpected: "but expected one of: ", +errButExpectedX: "but expected '$1'", +errAmbiguousCallXYZ: "ambiguous call; both $1 and $2 match for: $3", +errWrongNumberOfArguments: "wrong number of arguments", +errWrongNumberOfArgumentsInCall: "wrong number of arguments in call to '$1'", +errMissingGenericParamsForTemplate: "'$1' has unspecified generic parameters", +errXCannotBePassedToProcVar: "'$1' cannot be passed to a procvar", +errPragmaOnlyInHeaderOfProcX: "pragmas are only allowed in the header of a proc; redefinition of $1", +errImplOfXNotAllowed: "implementation of '$1' is not allowed", +errImplOfXexpected: "implementation of '$1' expected", +errNoSymbolToBorrowFromFound: "no symbol to borrow from found", +errDiscardValueX: "value of type '$1' has to be discarded", +errInvalidDiscard: "statement returns no value that can be discarded", +errIllegalConvFromXtoY: "conversion from $1 to $2 is invalid", +errCannotBindXTwice: "cannot bind parameter '$1' twice", +errInvalidOrderInArrayConstructor: "invalid order in array constructor", +errInvalidOrderInEnumX: "invalid order in enum '$1'", +errEnumXHasHoles: "enum '$1' has holes", +errExceptExpected: "'except' or 'finally' expected", +errInvalidTry: "after catch all 'except' or 'finally' no section may follow", +errOptionExpected: "option expected, but found '$1'", +errXisNoLabel: "'$1' is not a label", +errNotAllCasesCovered: "not all cases are covered", +errUnknownSubstitionVar: "unknown substitution variable: '$1'", +errComplexStmtRequiresInd: "complex statement requires indentation", +errXisNotCallable: "'$1' is not callable", +errNoPragmasAllowedForX: "no pragmas allowed for $1", +errNoGenericParamsAllowedForX: "no generic parameters allowed for $1", +errInvalidParamKindX: "invalid param kind: '$1'", +errDefaultArgumentInvalid: "default argument invalid", +errNamedParamHasToBeIdent: "named parameter has to be an identifier", +errNoReturnTypeForX: "no return type allowed for $1", +errConvNeedsOneArg: "a type conversion needs exactly one argument", +errInvalidPragmaX: "invalid pragma: $1", +errXNotAllowedHere: "$1 not allowed here", +errInvalidControlFlowX: "invalid control flow: $1", +errXisNoType: "invalid type: '$1'", +errCircumNeedsPointer: "'[]' needs a pointer or reference type", +errInvalidExpression: "invalid expression", +errInvalidExpressionX: "invalid expression: '$1'", +errEnumHasNoValueX: "enum has no value '$1'", +errNamedExprExpected: "named expression expected", +errNamedExprNotAllowed: "named expression not allowed here", +errXExpectsOneTypeParam: "'$1' expects one type parameter", +errArrayExpectsTwoTypeParams: "array expects two type parameters", +errInvalidVisibilityX: "invalid visibility: '$1'", +errInitHereNotAllowed: "initialization not allowed here", +errXCannotBeAssignedTo: "'$1' cannot be assigned to", +errIteratorNotAllowed: "iterators can only be defined at the module's top level", +errXNeedsReturnType: "$1 needs a return type", +errNoReturnTypeDeclared: "no return type declared", +errNoCommand: "no command given", +errInvalidCommandX: "invalid command: '$1'", +errXOnlyAtModuleScope: "'$1' is only allowed at top level", +errXNeedsParamObjectType: "'$1' needs a parameter that has an object type", +errTemplateInstantiationTooNested: "template instantiation too nested, try --evalTemplateLimit:N", +errMacroInstantiationTooNested: "macro instantiation too nested, try --evalMacroLimit:N", +errInstantiationFrom: "template/generic instantiation from here", +errInvalidIndexValueForTuple: "invalid index value for tuple subscript", +errCommandExpectsFilename: "command expects a filename argument", +errMainModuleMustBeSpecified: "please, specify a main module in the project configuration file", +errXExpected: "'$1' expected", +errTIsNotAConcreteType: "'$1' is not a concrete type.", +errCastToANonConcreteType: "cannot cast to a non concrete type: '$1'", +errInvalidSectionStart: "invalid section start", +errGridTableNotImplemented: "grid table is not implemented", +errGeneralParseError: "general parse error", +errNewSectionExpected: "new section expected", +errWhitespaceExpected: "whitespace expected, got '$1'", +errXisNoValidIndexFile: "'$1' is no valid index file", +errCannotRenderX: "cannot render reStructuredText element '$1'", +errVarVarTypeNotAllowed: "type 'var var' is not allowed", +errInstantiateXExplicitly: "instantiate '$1' explicitly", +errOnlyACallOpCanBeDelegator: "only a call operator can be a delegator", +errUsingNoSymbol: "'$1' is not a variable, constant or a proc name", +errMacroBodyDependsOnGenericTypes: "the macro body cannot be compiled, " & + "because the parameter '$1' has a generic type", +errDestructorNotGenericEnough: "Destructor signature is too specific. " & + "A destructor must be associated will all instantiations of a generic type", +errInlineIteratorsAsProcParams: "inline iterators can be used as parameters only for " & + "templates, macros and other inline iterators", +errXExpectsTwoArguments: "'$1' expects two arguments", +errXExpectsObjectTypes: "'$1' expects object types", +errXcanNeverBeOfThisSubtype: "'$1' can never be of this subtype", +errTooManyIterations: "interpretation requires too many iterations; " & + "if you are sure this is not a bug in your code edit " & + "compiler/vmdef.MaxLoopIterations and rebuild the compiler", +errCannotInterpretNodeX: "cannot evaluate '$1'", +errFieldXNotFound: "field '$1' cannot be found", +errInvalidConversionFromTypeX: "invalid conversion from type '$1'", +errAssertionFailed: "assertion failed", +errCannotGenerateCodeForX: "cannot generate code for '$1'", +errXRequiresOneArgument: "$1 requires one parameter", +errUnhandledExceptionX: "unhandled exception: $1", +errCyclicTree: "macro returned a cyclic abstract syntax tree", +errXisNoMacroOrTemplate: "'$1' is no macro or template", +errXhasSideEffects: "'$1' can have side effects", +errIteratorExpected: "iterator within for loop context expected", +errLetNeedsInit: "'let' symbol requires an initialization", +errThreadvarCannotInit: "a thread var cannot be initialized explicitly; this would only run for the main thread", +errWrongSymbolX: "usage of '$1' is a user-defined error", +errIllegalCaptureX: "illegal capture '$1'", +errXCannotBeClosure: "'$1' cannot have 'closure' calling convention", +errXMustBeCompileTime: "'$1' can only be used in compile-time context", +errCannotInferTypeOfTheLiteral: "cannot infer the type of the $1", +errCannotInferReturnType: "cannot infer the return type of the proc", +errCannotInferStaticParam: "cannot infer the value of the static param `$1`", +errGenericLambdaNotAllowed: "A nested proc can have generic parameters only when " & + "it is used as an operand to another routine and the types " & + "of the generic paramers can be inferred from the expected signature.", +errProcHasNoConcreteType: "'$1' doesn't have a concrete type, due to unspecified generic parameters.", +errInOutFlagNotExtern: "The `$1` modifier can be used only with imported types", +]# diff --git a/compiler/extccomp.nim b/compiler/extccomp.nim index f8938e3af..388bb9c9e 100644 --- a/compiler/extccomp.nim +++ b/compiler/extccomp.nim @@ -14,7 +14,7 @@ import ropes, os, strutils, osproc, platform, condsyms, options, msgs, - std / sha1, streams + configuration, std / sha1, streams #from debuginfo import writeDebugInfo @@ -193,9 +193,9 @@ compiler bcc: pic: "", asmStmtFrmt: "__asm{$n$1$n}$n", structStmtFmt: "$1 $2", - props: {hasSwitchRange, hasComputedGoto, hasCpp, hasGcGuard, + props: {hasSwitchRange, hasComputedGoto, hasCpp, hasGcGuard, hasAttribute}) - + # Digital Mars C Compiler compiler dmc: @@ -376,7 +376,7 @@ proc nameToCC*(name: string): TSystemCC = return i result = ccNone -proc getConfigVar(c: TSystemCC, suffix: string): string = +proc getConfigVar(conf: ConfigRef; c: TSystemCC, suffix: string): string = # use ``cpu.os.cc`` for cross compilation, unless ``--compileOnly`` is given # for niminst support let fullSuffix = @@ -394,62 +394,63 @@ proc getConfigVar(c: TSystemCC, suffix: string): string = let fullCCname = platform.CPU[targetCPU].name & '.' & platform.OS[targetOS].name & '.' & CC[c].name & fullSuffix - result = getConfigVar(fullCCname) + result = getConfigVar(conf, fullCCname) if result.len == 0: # not overriden for this cross compilation setting? - result = getConfigVar(CC[c].name & fullSuffix) + result = getConfigVar(conf, CC[c].name & fullSuffix) else: - result = getConfigVar(CC[c].name & fullSuffix) + result = getConfigVar(conf, CC[c].name & fullSuffix) -proc setCC*(ccname: string) = +proc setCC*(conf: ConfigRef; ccname: string; info: TLineInfo) = cCompiler = nameToCC(ccname) - if cCompiler == ccNone: rawMessage(errUnknownCcompiler, ccname) - compileOptions = getConfigVar(cCompiler, ".options.always") + if cCompiler == ccNone: + localError(conf, info, "unknown C compiler: '$1'" % ccname) + compileOptions = getConfigVar(conf, cCompiler, ".options.always") linkOptions = "" - ccompilerpath = getConfigVar(cCompiler, ".path") - for i in countup(low(CC), high(CC)): undefSymbol(CC[i].name) - defineSymbol(CC[cCompiler].name) + ccompilerpath = getConfigVar(conf, cCompiler, ".path") + for i in countup(low(CC), high(CC)): undefSymbol(conf.symbols, CC[i].name) + defineSymbol(conf.symbols, CC[cCompiler].name) proc addOpt(dest: var string, src: string) = if len(dest) == 0 or dest[len(dest)-1] != ' ': add(dest, " ") add(dest, src) -proc addLinkOption*(option: string) = +proc addLinkOption*(conf: ConfigRef; option: string) = addOpt(linkOptions, option) -proc addCompileOption*(option: string) = +proc addCompileOption*(conf: ConfigRef; option: string) = if strutils.find(compileOptions, option, 0) < 0: addOpt(compileOptions, option) -proc addLinkOptionCmd*(option: string) = +proc addLinkOptionCmd*(conf: ConfigRef; option: string) = addOpt(linkOptionsCmd, option) -proc addCompileOptionCmd*(option: string) = +proc addCompileOptionCmd*(conf: ConfigRef; option: string) = compileOptionsCmd.add(option) -proc initVars*() = +proc initVars*(conf: ConfigRef) = # we need to define the symbol here, because ``CC`` may have never been set! - for i in countup(low(CC), high(CC)): undefSymbol(CC[i].name) - defineSymbol(CC[cCompiler].name) - addCompileOption(getConfigVar(cCompiler, ".options.always")) + for i in countup(low(CC), high(CC)): undefSymbol(conf.symbols, CC[i].name) + defineSymbol(conf.symbols, CC[cCompiler].name) + addCompileOption(conf, getConfigVar(conf, cCompiler, ".options.always")) #addLinkOption(getConfigVar(cCompiler, ".options.linker")) if len(ccompilerpath) == 0: - ccompilerpath = getConfigVar(cCompiler, ".path") + ccompilerpath = getConfigVar(conf, cCompiler, ".path") -proc completeCFilePath*(cfile: string, createSubDir: bool = true): string = - result = completeGeneratedFilePath(cfile, createSubDir) +proc completeCFilePath*(conf: ConfigRef; cfile: string, createSubDir: bool = true): string = + result = completeGeneratedFilePath(conf, cfile, createSubDir) -proc toObjFile*(filename: string): string = +proc toObjFile*(conf: ConfigRef; filename: string): string = # Object file for compilation #if filename.endsWith(".cpp"): # result = changeFileExt(filename, "cpp." & CC[cCompiler].objExt) #else: result = changeFileExt(filename, CC[cCompiler].objExt) -proc addFileToCompile*(cf: Cfile) = +proc addFileToCompile*(conf: ConfigRef; cf: Cfile) = toCompile.add(cf) -proc resetCompilationLists* = +proc resetCompilationLists*(conf: ConfigRef) = toCompile.setLen 0 ## XXX: we must associate these with their originating module # when the module is loaded/unloaded it adds/removes its items @@ -457,107 +458,110 @@ proc resetCompilationLists* = # Maybe we can do that in checkDep on the other hand? externalToLink.setLen 0 -proc addExternalFileToLink*(filename: string) = +proc addExternalFileToLink*(conf: ConfigRef; filename: string) = externalToLink.insert(filename, 0) -proc execWithEcho(cmd: string, msg = hintExecuting): int = - rawMessage(msg, cmd) +proc execWithEcho(conf: ConfigRef; cmd: string, msg = hintExecuting): int = + rawMessage(conf, msg, cmd) result = execCmd(cmd) -proc execExternalProgram*(cmd: string, msg = hintExecuting) = - if execWithEcho(cmd, msg) != 0: - rawMessage(errExecutionOfProgramFailed, cmd) +proc execExternalProgram*(conf: ConfigRef; cmd: string, msg = hintExecuting) = + if execWithEcho(conf, cmd, msg) != 0: + rawMessage(conf, errGenerated, "execution of an external program failed: '$1'" % + cmd) -proc generateScript(projectFile: string, script: Rope) = +proc generateScript(conf: ConfigRef; projectFile: string, script: Rope) = let (dir, name, ext) = splitFile(projectFile) - writeRope(script, getNimcacheDir() / addFileExt("compile_" & name, + writeRope(script, getNimcacheDir(conf) / addFileExt("compile_" & name, platform.OS[targetOS].scriptExt)) - copyFile(libpath / "nimbase.h", getNimcacheDir() / "nimbase.h") + copyFile(libpath / "nimbase.h", getNimcacheDir(conf) / "nimbase.h") -proc getOptSpeed(c: TSystemCC): string = - result = getConfigVar(c, ".options.speed") +proc getOptSpeed(conf: ConfigRef; c: TSystemCC): string = + result = getConfigVar(conf, c, ".options.speed") if result == "": result = CC[c].optSpeed # use default settings from this file -proc getDebug(c: TSystemCC): string = - result = getConfigVar(c, ".options.debug") +proc getDebug(conf: ConfigRef; c: TSystemCC): string = + result = getConfigVar(conf, c, ".options.debug") if result == "": result = CC[c].debug # use default settings from this file -proc getOptSize(c: TSystemCC): string = - result = getConfigVar(c, ".options.size") +proc getOptSize(conf: ConfigRef; c: TSystemCC): string = + result = getConfigVar(conf, c, ".options.size") if result == "": result = CC[c].optSize # use default settings from this file -proc noAbsolutePaths: bool {.inline.} = +proc noAbsolutePaths(conf: ConfigRef): bool {.inline.} = # We used to check current OS != specified OS, but this makes no sense # really: Cross compilation from Linux to Linux for example is entirely # reasonable. # `optGenMapping` is included here for niminst. result = gGlobalOptions * {optGenScript, optGenMapping} != {} -proc cFileSpecificOptions(cfilename: string): string = +proc cFileSpecificOptions(conf: ConfigRef; cfilename: string): string = result = compileOptions for option in compileOptionsCmd: if strutils.find(result, option, 0) < 0: addOpt(result, option) - var trunk = splitFile(cfilename).name + let trunk = splitFile(cfilename).name if optCDebug in gGlobalOptions: - var key = trunk & ".debug" - if existsConfigVar(key): addOpt(result, getConfigVar(key)) - else: addOpt(result, getDebug(cCompiler)) + let key = trunk & ".debug" + if existsConfigVar(conf, key): addOpt(result, getConfigVar(conf, key)) + else: addOpt(result, getDebug(conf, cCompiler)) if optOptimizeSpeed in gOptions: - var key = trunk & ".speed" - if existsConfigVar(key): addOpt(result, getConfigVar(key)) - else: addOpt(result, getOptSpeed(cCompiler)) + let key = trunk & ".speed" + if existsConfigVar(conf, key): addOpt(result, getConfigVar(conf, key)) + else: addOpt(result, getOptSpeed(conf, cCompiler)) elif optOptimizeSize in gOptions: - var key = trunk & ".size" - if existsConfigVar(key): addOpt(result, getConfigVar(key)) - else: addOpt(result, getOptSize(cCompiler)) - var key = trunk & ".always" - if existsConfigVar(key): addOpt(result, getConfigVar(key)) + let key = trunk & ".size" + if existsConfigVar(conf, key): addOpt(result, getConfigVar(conf, key)) + else: addOpt(result, getOptSize(conf, cCompiler)) + let key = trunk & ".always" + if existsConfigVar(conf, key): addOpt(result, getConfigVar(conf, key)) -proc getCompileOptions: string = - result = cFileSpecificOptions("__dummy__") +proc getCompileOptions(conf: ConfigRef): string = + result = cFileSpecificOptions(conf, "__dummy__") -proc getLinkOptions: string = +proc getLinkOptions(conf: ConfigRef): string = result = linkOptions & " " & linkOptionsCmd & " " for linkedLib in items(cLinkedLibs): result.add(CC[cCompiler].linkLibCmd % linkedLib.quoteShell) for libDir in items(cLibs): result.add(join([CC[cCompiler].linkDirCmd, libDir.quoteShell])) -proc needsExeExt(): bool {.inline.} = +proc needsExeExt(conf: ConfigRef): bool {.inline.} = result = (optGenScript in gGlobalOptions and targetOS == osWindows) or (platform.hostOS == osWindows) -proc getCompilerExe(compiler: TSystemCC; cfile: string): string = +proc getCompilerExe(conf: ConfigRef; compiler: TSystemCC; cfile: string): string = result = if gCmd == cmdCompileToCpp and not cfile.endsWith(".c"): CC[compiler].cppCompiler else: CC[compiler].compilerExe if result.len == 0: - rawMessage(errCompilerDoesntSupportTarget, CC[compiler].name) + rawMessage(conf, errGenerated, + "Compiler '$1' doesn't support the requested target" % + CC[compiler].name) -proc getLinkerExe(compiler: TSystemCC): string = +proc getLinkerExe(conf: ConfigRef; compiler: TSystemCC): string = result = if CC[compiler].linkerExe.len > 0: CC[compiler].linkerExe elif gMixedMode and gCmd != cmdCompileToCpp: CC[compiler].cppCompiler - else: compiler.getCompilerExe("") + else: getCompilerExe(conf, compiler, "") -proc getCompileCFileCmd*(cfile: Cfile): string = +proc getCompileCFileCmd*(conf: ConfigRef; cfile: Cfile): string = var c = cCompiler - var options = cFileSpecificOptions(cfile.cname) - var exe = getConfigVar(c, ".exe") - if exe.len == 0: exe = c.getCompilerExe(cfile.cname) + var options = cFileSpecificOptions(conf, cfile.cname) + var exe = getConfigVar(conf, c, ".exe") + if exe.len == 0: exe = getCompilerExe(conf, c, cfile.cname) - if needsExeExt(): exe = addFileExt(exe, "exe") + if needsExeExt(conf): exe = addFileExt(exe, "exe") if optGenDynLib in gGlobalOptions and ospNeedsPIC in platform.OS[targetOS].props: add(options, ' ' & CC[c].pic) var includeCmd, compilePattern: string - if not noAbsolutePaths(): + if not noAbsolutePaths(conf): # compute include paths: includeCmd = CC[c].includeCmd & quoteShell(libpath) @@ -567,18 +571,18 @@ proc getCompileCFileCmd*(cfile: Cfile): string = compilePattern = joinPath(ccompilerpath, exe) else: includeCmd = "" - compilePattern = c.getCompilerExe(cfile.cname) + compilePattern = getCompilerExe(conf, c, cfile.cname) - var cf = if noAbsolutePaths(): extractFilename(cfile.cname) + var cf = if noAbsolutePaths(conf): extractFilename(cfile.cname) else: cfile.cname var objfile = if cfile.obj.len == 0: - if not cfile.flags.contains(CfileFlag.External) or noAbsolutePaths(): - toObjFile(cf) + if not cfile.flags.contains(CfileFlag.External) or noAbsolutePaths(conf): + toObjFile(conf, cf) else: - completeCFilePath(toObjFile(cf)) - elif noAbsolutePaths(): + completeCFilePath(conf, toObjFile(conf, cf)) + elif noAbsolutePaths(conf): extractFilename(cfile.obj) else: cfile.obj @@ -587,30 +591,30 @@ proc getCompileCFileCmd*(cfile: Cfile): string = cf = quoteShell(cf) result = quoteShell(compilePattern % [ "file", cf, "objfile", objfile, "options", options, - "include", includeCmd, "nim", getPrefixDir(), - "nim", getPrefixDir(), "lib", libpath]) + "include", includeCmd, "nim", getPrefixDir(conf), + "nim", getPrefixDir(conf), "lib", libpath]) add(result, ' ') addf(result, CC[c].compileTmpl, [ "file", cf, "objfile", objfile, "options", options, "include", includeCmd, - "nim", quoteShell(getPrefixDir()), - "nim", quoteShell(getPrefixDir()), + "nim", quoteShell(getPrefixDir(conf)), + "nim", quoteShell(getPrefixDir(conf)), "lib", quoteShell(libpath)]) -proc footprint(cfile: Cfile): SecureHash = +proc footprint(conf: ConfigRef; cfile: Cfile): SecureHash = result = secureHash( $secureHashFile(cfile.cname) & platform.OS[targetOS].name & platform.CPU[targetCPU].name & extccomp.CC[extccomp.cCompiler].name & - getCompileCFileCmd(cfile)) + getCompileCFileCmd(conf, cfile)) -proc externalFileChanged(cfile: Cfile): bool = +proc externalFileChanged(conf: ConfigRef; cfile: Cfile): bool = if gCmd notin {cmdCompileToC, cmdCompileToCpp, cmdCompileToOC, cmdCompileToLLVM}: return false - var hashFile = toGeneratedFile(cfile.cname.withPackageName, "sha1") - var currentHash = footprint(cfile) + var hashFile = toGeneratedFile(conf, cfile.cname.withPackageName, "sha1") + var currentHash = footprint(conf, cfile) var f: File if open(f, hashFile, fmRead): let oldHash = parseSecureHash(f.readLine()) @@ -623,23 +627,23 @@ proc externalFileChanged(cfile: Cfile): bool = f.writeLine($currentHash) close(f) -proc addExternalFileToCompile*(c: var Cfile) = - if optForceFullMake notin gGlobalOptions and not externalFileChanged(c): +proc addExternalFileToCompile*(conf: ConfigRef; c: var Cfile) = + if optForceFullMake notin gGlobalOptions and not externalFileChanged(conf, c): c.flags.incl CfileFlag.Cached toCompile.add(c) -proc addExternalFileToCompile*(filename: string) = +proc addExternalFileToCompile*(conf: ConfigRef; filename: string) = var c = Cfile(cname: filename, - obj: toObjFile(completeCFilePath(changeFileExt(filename, ""), false)), + obj: toObjFile(conf, completeCFilePath(conf, changeFileExt(filename, ""), false)), flags: {CfileFlag.External}) - addExternalFileToCompile(c) + addExternalFileToCompile(conf, c) -proc compileCFile(list: CFileList, script: var Rope, cmds: var TStringSeq, +proc compileCFile(conf: ConfigRef; list: CFileList, script: var Rope, cmds: var TStringSeq, prettyCmds: var TStringSeq) = for it in list: # call the C compiler for the .c file: if it.flags.contains(CfileFlag.Cached): continue - var compileCmd = getCompileCFileCmd(it) + var compileCmd = getCompileCFileCmd(conf, it) if optCompileOnly notin gGlobalOptions: add(cmds, compileCmd) let (_, name, _) = splitFile(it.cname) @@ -648,7 +652,7 @@ proc compileCFile(list: CFileList, script: var Rope, cmds: var TStringSeq, add(script, compileCmd) add(script, tnl) -proc getLinkCmd(projectfile, objfiles: string): string = +proc getLinkCmd(conf: ConfigRef; projectfile, objfiles: string): string = if optGenStaticLib in gGlobalOptions: var libname: string if options.outFile.len > 0: @@ -660,11 +664,11 @@ proc getLinkCmd(projectfile, objfiles: string): string = result = CC[cCompiler].buildLib % ["libfile", libname, "objfiles", objfiles] else: - var linkerExe = getConfigVar(cCompiler, ".linkerexe") - if len(linkerExe) == 0: linkerExe = cCompiler.getLinkerExe + var linkerExe = getConfigVar(conf, cCompiler, ".linkerexe") + if len(linkerExe) == 0: linkerExe = getLinkerExe(conf, cCompiler) # bug #6452: We must not use ``quoteShell`` here for ``linkerExe`` - if needsExeExt(): linkerExe = addFileExt(linkerExe, "exe") - if noAbsolutePaths(): result = linkerExe + if needsExeExt(conf): linkerExe = addFileExt(linkerExe, "exe") + if noAbsolutePaths(conf): result = linkerExe else: result = joinPath(ccompilerpath, linkerExe) let buildgui = if optGenGuiApp in gGlobalOptions: CC[cCompiler].buildGui else: "" @@ -679,60 +683,63 @@ proc getLinkCmd(projectfile, objfiles: string): string = exefile = options.outFile.expandTilde if not exefile.isAbsolute(): exefile = getCurrentDir() / exefile - if not noAbsolutePaths(): + if not noAbsolutePaths(conf): if not exefile.isAbsolute(): exefile = joinPath(splitFile(projectfile).dir, exefile) when false: if optCDebug in gGlobalOptions: writeDebugInfo(exefile.changeFileExt("ndb")) exefile = quoteShell(exefile) - let linkOptions = getLinkOptions() & " " & - getConfigVar(cCompiler, ".options.linker") - var linkTmpl = getConfigVar(cCompiler, ".linkTmpl") + let linkOptions = getLinkOptions(conf) & " " & + getConfigVar(conf, cCompiler, ".options.linker") + var linkTmpl = getConfigVar(conf, cCompiler, ".linkTmpl") if linkTmpl.len == 0: linkTmpl = CC[cCompiler].linkTmpl result = quoteShell(result % ["builddll", builddll, "buildgui", buildgui, "options", linkOptions, "objfiles", objfiles, - "exefile", exefile, "nim", getPrefixDir(), "lib", libpath]) + "exefile", exefile, "nim", getPrefixDir(conf), "lib", libpath]) result.add ' ' addf(result, linkTmpl, ["builddll", builddll, "buildgui", buildgui, "options", linkOptions, "objfiles", objfiles, "exefile", exefile, - "nim", quoteShell(getPrefixDir()), + "nim", quoteShell(getPrefixDir(conf)), "lib", quoteShell(libpath)]) -template tryExceptOSErrorMessage(errorPrefix: string = "", body: untyped): typed = +template tryExceptOSErrorMessage(conf: ConfigRef; errorPrefix: string = "", body: untyped): typed = try: body except OSError: let ose = (ref OSError)(getCurrentException()) if errorPrefix.len > 0: - rawMessage(errGenerated, errorPrefix & " " & ose.msg & " " & $ose.errorCode) + rawMessage(conf, errGenerated, errorPrefix & " " & ose.msg & " " & $ose.errorCode) else: - rawMessage(errExecutionOfProgramFailed, ose.msg & " " & $ose.errorCode) + rawMessage(conf, errGenerated, "execution of an external program failed: '$1'" % + (ose.msg & " " & $ose.errorCode)) raise -proc execLinkCmd(linkCmd: string) = - tryExceptOSErrorMessage("invocation of external linker program failed."): - execExternalProgram(linkCmd, +proc execLinkCmd(conf: ConfigRef; linkCmd: string) = + tryExceptOSErrorMessage(conf, "invocation of external linker program failed."): + execExternalProgram(conf, linkCmd, if optListCmd in gGlobalOptions or gVerbosity > 1: hintExecuting else: hintLinking) -proc execCmdsInParallel(cmds: seq[string]; prettyCb: proc (idx: int)) = +proc execCmdsInParallel(conf: ConfigRef; cmds: seq[string]; prettyCb: proc (idx: int)) = let runCb = proc (idx: int, p: Process) = let exitCode = p.peekExitCode if exitCode != 0: - rawMessage(errGenerated, "execution of an external compiler program '" & + rawMessage(conf, errGenerated, "execution of an external compiler program '" & cmds[idx] & "' failed with exit code: " & $exitCode & "\n\n" & p.outputStream.readAll.strip) if gNumberOfProcessors == 0: gNumberOfProcessors = countProcessors() var res = 0 if gNumberOfProcessors <= 1: for i in countup(0, high(cmds)): - tryExceptOSErrorMessage("invocation of external compiler program failed."): - res = execWithEcho(cmds[i]) - if res != 0: rawMessage(errExecutionOfProgramFailed, cmds[i]) + tryExceptOSErrorMessage(conf, "invocation of external compiler program failed."): + res = execWithEcho(conf, cmds[i]) + if res != 0: + rawMessage(conf, errGenerated, "execution of an external program failed: '$1'" % + cmds[i]) else: - tryExceptOSErrorMessage("invocation of external compiler program failed."): + tryExceptOSErrorMessage(conf, "invocation of external compiler program failed."): if optListCmd in gGlobalOptions or gVerbosity > 1: res = execProcesses(cmds, {poEchoCmd, poStdErrToStdOut, poUsePath}, gNumberOfProcessors, afterRunEvent=runCb) @@ -744,9 +751,10 @@ proc execCmdsInParallel(cmds: seq[string]; prettyCb: proc (idx: int)) = gNumberOfProcessors, afterRunEvent=runCb) if res != 0: if gNumberOfProcessors <= 1: - rawMessage(errExecutionOfProgramFailed, cmds.join()) + rawMessage(conf, errGenerated, "execution of an external program failed: '$1'" % + cmds.join()) -proc callCCompiler*(projectfile: string) = +proc callCCompiler*(conf: ConfigRef; projectfile: string) = var linkCmd: string if gGlobalOptions * {optCompileOnly, optGenScript} == {optCompileOnly}: @@ -758,36 +766,36 @@ proc callCCompiler*(projectfile: string) = var prettyCmds: TStringSeq = @[] let prettyCb = proc (idx: int) = echo prettyCmds[idx] - compileCFile(toCompile, script, cmds, prettyCmds) + compileCFile(conf, toCompile, script, cmds, prettyCmds) if optCompileOnly notin gGlobalOptions: - execCmdsInParallel(cmds, prettyCb) + execCmdsInParallel(conf, cmds, prettyCb) if optNoLinking notin gGlobalOptions: # call the linker: var objfiles = "" for it in externalToLink: - let objFile = if noAbsolutePaths(): it.extractFilename else: it + let objFile = if noAbsolutePaths(conf): it.extractFilename else: it add(objfiles, ' ') add(objfiles, quoteShell( addFileExt(objFile, CC[cCompiler].objExt))) for x in toCompile: - let objFile = if noAbsolutePaths(): x.obj.extractFilename else: x.obj + let objFile = if noAbsolutePaths(conf): x.obj.extractFilename else: x.obj add(objfiles, ' ') add(objfiles, quoteShell(objFile)) - linkCmd = getLinkCmd(projectfile, objfiles) + linkCmd = getLinkCmd(conf, projectfile, objfiles) if optCompileOnly notin gGlobalOptions: - execLinkCmd(linkCmd) + execLinkCmd(conf, linkCmd) else: linkCmd = "" if optGenScript in gGlobalOptions: add(script, linkCmd) add(script, tnl) - generateScript(projectfile, script) + generateScript(conf, projectfile, script) #from json import escapeJson import json -proc writeJsonBuildInstructions*(projectfile: string) = +proc writeJsonBuildInstructions*(conf: ConfigRef; projectfile: string) = template lit(x: untyped) = f.write x template str(x: untyped) = when compiles(escapeJson(x, buf)): @@ -797,11 +805,11 @@ proc writeJsonBuildInstructions*(projectfile: string) = else: f.write escapeJson(x) - proc cfiles(f: File; buf: var string; clist: CfileList, isExternal: bool) = + proc cfiles(conf: ConfigRef; f: File; buf: var string; clist: CfileList, isExternal: bool) = var pastStart = false for it in clist: if CfileFlag.Cached in it.flags: continue - let compileCmd = getCompileCFileCmd(it) + let compileCmd = getCompileCFileCmd(conf, it) if pastStart: lit "],\L" lit "[" str it.cname @@ -810,11 +818,11 @@ proc writeJsonBuildInstructions*(projectfile: string) = pastStart = true lit "]\L" - proc linkfiles(f: File; buf, objfiles: var string; clist: CfileList; + proc linkfiles(conf: ConfigRef; f: File; buf, objfiles: var string; clist: CfileList; llist: seq[string]) = var pastStart = false for it in llist: - let objfile = if noAbsolutePaths(): it.extractFilename + let objfile = if noAbsolutePaths(conf): it.extractFilename else: it let objstr = addFileExt(objfile, CC[cCompiler].objExt) add(objfiles, ' ') @@ -835,25 +843,25 @@ proc writeJsonBuildInstructions*(projectfile: string) = var buf = newStringOfCap(50) let file = projectfile.splitFile.name - let jsonFile = toGeneratedFile(file, "json") + let jsonFile = toGeneratedFile(conf, file, "json") var f: File if open(f, jsonFile, fmWrite): lit "{\"compile\":[\L" - cfiles(f, buf, toCompile, false) + cfiles(conf, f, buf, toCompile, false) lit "],\L\"link\":[\L" var objfiles = "" # XXX add every file here that is to link - linkfiles(f, buf, objfiles, toCompile, externalToLink) + linkfiles(conf, f, buf, objfiles, toCompile, externalToLink) lit "],\L\"linkcmd\": " - str getLinkCmd(projectfile, objfiles) + str getLinkCmd(conf, projectfile, objfiles) lit "\L}\L" close(f) -proc runJsonBuildInstructions*(projectfile: string) = +proc runJsonBuildInstructions*(conf: ConfigRef; projectfile: string) = let file = projectfile.splitFile.name - let jsonFile = toGeneratedFile(file, "json") + let jsonFile = toGeneratedFile(conf, file, "json") try: let data = json.parseFile(jsonFile) let toCompile = data["compile"] @@ -870,32 +878,32 @@ proc runJsonBuildInstructions*(projectfile: string) = let prettyCb = proc (idx: int) = echo prettyCmds[idx] - execCmdsInParallel(cmds, prettyCb) + execCmdsInParallel(conf, cmds, prettyCb) let linkCmd = data["linkcmd"] doAssert linkCmd.kind == JString - execLinkCmd(linkCmd.getStr) + execLinkCmd(conf, linkCmd.getStr) except: echo getCurrentException().getStackTrace() quit "error evaluating JSON file: " & jsonFile -proc genMappingFiles(list: CFileList): Rope = +proc genMappingFiles(conf: ConfigRef; list: CFileList): Rope = for it in list: addf(result, "--file:r\"$1\"$N", [rope(it.cname)]) -proc writeMapping*(gSymbolMapping: Rope) = +proc writeMapping*(conf: ConfigRef; symbolMapping: Rope) = if optGenMapping notin gGlobalOptions: return var code = rope("[C_Files]\n") - add(code, genMappingFiles(toCompile)) + add(code, genMappingFiles(conf, toCompile)) add(code, "\n[C_Compiler]\nFlags=") - add(code, strutils.escape(getCompileOptions())) + add(code, strutils.escape(getCompileOptions(conf))) add(code, "\n[Linker]\nFlags=") - add(code, strutils.escape(getLinkOptions() & " " & - getConfigVar(cCompiler, ".options.linker"))) + add(code, strutils.escape(getLinkOptions(conf) & " " & + getConfigVar(conf, cCompiler, ".options.linker"))) add(code, "\n[Environment]\nlibpath=") add(code, strutils.escape(libpath)) - addf(code, "\n[Symbols]$n$1", [gSymbolMapping]) + addf(code, "\n[Symbols]$n$1", [symbolMapping]) writeRope(code, joinPath(gProjectPath, "mapping.txt")) diff --git a/compiler/idgen.nim b/compiler/idgen.nim index c6b1a4d07..7d103ffd7 100644 --- a/compiler/idgen.nim +++ b/compiler/idgen.nim @@ -36,20 +36,20 @@ proc setId*(id: int) {.inline.} = proc idSynchronizationPoint*(idRange: int) = gFrontEndId = (gFrontEndId div idRange + 1) * idRange + 1 -proc toGid(f: string): string = +proc toGid(conf: ConfigRef; f: string): string = # we used to use ``f.addFileExt("gid")`` (aka ``$project.gid``), but this # will cause strange bugs if multiple projects are in the same folder, so # we simply use a project independent name: - result = options.completeGeneratedFilePath("nim.gid") + result = options.completeGeneratedFilePath(conf, "nim.gid") -proc saveMaxIds*(project: string) = - var f = open(project.toGid, fmWrite) +proc saveMaxIds*(conf: ConfigRef; project: string) = + var f = open(toGid(conf, project), fmWrite) f.writeLine($gFrontEndId) f.close() -proc loadMaxIds*(project: string) = +proc loadMaxIds*(conf: ConfigRef; project: string) = var f: File - if open(f, project.toGid, fmRead): + if open(f, toGid(conf, project), fmRead): var line = newStringOfCap(20) if f.readLine(line): var frontEndId = parseInt(line) diff --git a/compiler/lexer.nim b/compiler/lexer.nim index a65587390..68348a73e 100644 --- a/compiler/lexer.nim +++ b/compiler/lexer.nim @@ -17,7 +17,7 @@ import hashes, options, msgs, strutils, platform, idents, nimlexbase, llstream, - wordrecg + wordrecg, configuration const MaxLineLength* = 80 # lines longer than this lead to a warning @@ -131,7 +131,7 @@ type # like 0b01 or r"\L" are unaffected commentOffsetA*, commentOffsetB*: int - TErrorHandler* = proc (info: TLineInfo; msg: TMsgKind; arg: string) + TErrorHandler* = proc (conf: ConfigRef; info: TLineInfo; msg: TMsgKind; arg: string) TLexer* = object of TBaseLexer fileIdx*: FileIndex indentAhead*: int # if > 0 an indendation has already been read @@ -234,7 +234,7 @@ proc openLexer*(lex: var TLexer, fileIdx: FileIndex, inputstream: PLLStream; proc openLexer*(lex: var TLexer, filename: string, inputstream: PLLStream; cache: IdentCache; config: ConfigRef) = - openLexer(lex, filename.fileInfoIdx, inputstream, cache, config) + openLexer(lex, fileInfoIdx(config, filename), inputstream, cache, config) proc closeLexer*(lex: var TLexer) = if lex.config != nil: @@ -246,9 +246,9 @@ proc getLineInfo(L: TLexer): TLineInfo = proc dispMessage(L: TLexer; info: TLineInfo; msg: TMsgKind; arg: string) = if L.errorHandler.isNil: - msgs.message(info, msg, arg) + msgs.message(L.config, info, msg, arg) else: - L.errorHandler(info, msg, arg) + L.errorHandler(L.config, info, msg, arg) proc lexMessage*(L: TLexer, msg: TMsgKind, arg = "") = L.dispMessage(getLineInfo(L), msg, arg) @@ -341,7 +341,8 @@ proc getNumber(L: var TLexer, result: var TToken) = break if buf[pos] == '_': if buf[pos+1] notin chars: - lexMessage(L, errInvalidToken, "_") + lexMessage(L, errGenerated, + "only single underscores may occur in a token: '__' is invalid") break add(tok.literal, '_') inc(pos) @@ -355,7 +356,7 @@ proc getNumber(L: var TLexer, result: var TToken) = inc(pos) L.bufpos = pos - proc lexMessageLitNum(L: var TLexer, msg: TMsgKind, startpos: int) = + proc lexMessageLitNum(L: var TLexer, msg: string, startpos: int) = # Used to get slightly human friendlier err messages. # Note: the erroneous 'O' char in the character set is intentional const literalishChars = {'A'..'F', 'a'..'f', '0'..'9', 'X', 'x', 'o', 'O', @@ -376,7 +377,7 @@ proc getNumber(L: var TLexer, result: var TToken) = add(t.literal, L.buf[L.bufpos]) matchChars(L, t, {'0'..'9'}) L.bufpos = msgPos - lexMessage(L, msg, t.literal) + lexMessage(L, errGenerated, msg % t.literal) var startpos, endpos: int @@ -398,7 +399,7 @@ proc getNumber(L: var TLexer, result: var TToken) = eatChar(L, result, '0') case L.buf[L.bufpos] of 'O': - lexMessageLitNum(L, errInvalidNumberOctalCode, startpos) + lexMessageLitNum(L, "$1 is not a valid number; did you mean octal? Then use one of '0o', '0c' or '0C'.", startpos) of 'x', 'X': eatChar(L, result, 'x') matchUnderscoreChars(L, result, {'0'..'9', 'a'..'f', 'A'..'F'}) @@ -409,7 +410,7 @@ proc getNumber(L: var TLexer, result: var TToken) = eatChar(L, result, 'b') matchUnderscoreChars(L, result, {'0'..'1'}) else: - internalError(getLineInfo(L), "getNumber") + internalError(L.config, getLineInfo(L), "getNumber") else: matchUnderscoreChars(L, result, {'0'..'9'}) if (L.buf[L.bufpos] == '.') and (L.buf[L.bufpos + 1] in {'0'..'9'}): @@ -464,7 +465,7 @@ proc getNumber(L: var TLexer, result: var TToken) = result.tokType = tkInt8Lit inc(postPos) else: - lexMessageLitNum(L, errInvalidNumber, startpos) + lexMessageLitNum(L, "invalid number: '$1'", startpos) of 'u', 'U': inc(postPos) if (L.buf[postPos] == '6') and (L.buf[postPos + 1] == '4'): @@ -482,12 +483,12 @@ proc getNumber(L: var TLexer, result: var TToken) = else: result.tokType = tkUIntLit else: - lexMessageLitNum(L, errInvalidNumber, startpos) + lexMessageLitNum(L, "invalid number: '$1'", startpos) # Is there still a literalish char awaiting? Then it's an error! if L.buf[postPos] in literalishChars or (L.buf[postPos] == '.' and L.buf[postPos + 1] in {'0'..'9'}): - lexMessageLitNum(L, errInvalidNumber, startpos) + lexMessageLitNum(L, "invalid number: '$1'", startpos) # Third stage, extract actual number L.bufpos = startpos # restore position @@ -528,7 +529,7 @@ proc getNumber(L: var TLexer, result: var TToken) = else: break else: - internalError(getLineInfo(L), "getNumber") + internalError(L.config, getLineInfo(L), "getNumber") case result.tokType of tkIntLit, tkInt64Lit: result.iNumber = xi @@ -545,7 +546,7 @@ proc getNumber(L: var TLexer, result: var TToken) = # XXX: Test this on big endian machine! of tkFloat64Lit, tkFloatLit: result.fNumber = (cast[PFloat64](addr(xi)))[] - else: internalError(getLineInfo(L), "getNumber") + else: internalError(L.config, getLineInfo(L), "getNumber") # Bounds checks. Non decimal literals are allowed to overflow the range of # the datatype as long as their pattern don't overflow _bitwise_, hence @@ -561,7 +562,7 @@ proc getNumber(L: var TLexer, result: var TToken) = if outOfRange: #echo "out of range num: ", result.iNumber, " vs ", xi - lexMessageLitNum(L, errNumberOutOfRange, startpos) + lexMessageLitNum(L, "number out of range: '$1'", startpos) else: case result.tokType @@ -590,7 +591,7 @@ proc getNumber(L: var TLexer, result: var TToken) = result.iNumber > BiggestInt(uint32.high)) else: false - if outOfRange: lexMessageLitNum(L, errNumberOutOfRange, startpos) + if outOfRange: lexMessageLitNum(L, "number out of range: '$1'", startpos) # Promote int literal to int64? Not always necessary, but more consistent if result.tokType == tkIntLit: @@ -598,9 +599,9 @@ proc getNumber(L: var TLexer, result: var TToken) = result.tokType = tkInt64Lit except ValueError: - lexMessageLitNum(L, errInvalidNumber, startpos) + lexMessageLitNum(L, "invalid number: '$1'", startpos) except OverflowError, RangeError: - lexMessageLitNum(L, errNumberOutOfRange, startpos) + lexMessageLitNum(L, "number out of range: '$1'", startpos) tokenEnd(result, postPos-1) L.bufpos = postPos @@ -627,7 +628,8 @@ proc getEscapedChar(L: var TLexer, tok: var TToken) = case L.buf[L.bufpos] of 'n', 'N': if gOldNewlines: - if tok.tokType == tkCharLit: lexMessage(L, errNnotAllowedInCharacter) + if tok.tokType == tkCharLit: + lexMessage(L, errGenerated, "\\n not allowed in character literal") add(tok.literal, tnl) else: add(tok.literal, '\L') @@ -696,8 +698,8 @@ proc getEscapedChar(L: var TLexer, tok: var TToken) = var xi = 0 handleDecChars(L, xi) if (xi <= 255): add(tok.literal, chr(xi)) - else: lexMessage(L, errInvalidCharacterConstant) - else: lexMessage(L, errInvalidCharacterConstant) + else: lexMessage(L, errGenerated, "invalid character constant") + else: lexMessage(L, errGenerated, "invalid character constant") proc newString(s: cstring, len: int): string = ## XXX, how come there is no support for this? @@ -761,7 +763,7 @@ proc getString(L: var TLexer, tok: var TToken, rawMode: bool) = tokenEndIgnore(tok, pos) var line2 = L.lineNumber L.lineNumber = line - lexMessagePos(L, errClosingTripleQuoteExpected, L.lineStart) + lexMessagePos(L, errGenerated, L.lineStart, "closing \"\"\" expected, but end of file reached") L.lineNumber = line2 L.bufpos = pos break @@ -784,7 +786,7 @@ proc getString(L: var TLexer, tok: var TToken, rawMode: bool) = break elif c in {CR, LF, nimlexbase.EndOfFile}: tokenEndIgnore(tok, pos) - lexMessage(L, errClosingQuoteExpected) + lexMessage(L, errGenerated, "closing \" expected") break elif (c == '\\') and not rawMode: L.bufpos = pos @@ -800,12 +802,13 @@ proc getCharacter(L: var TLexer, tok: var TToken) = inc(L.bufpos) # skip ' var c = L.buf[L.bufpos] case c - of '\0'..pred(' '), '\'': lexMessage(L, errInvalidCharacterConstant) + of '\0'..pred(' '), '\'': lexMessage(L, errGenerated, "invalid character literal") of '\\': getEscapedChar(L, tok) else: tok.literal = $c inc(L.bufpos) - if L.buf[L.bufpos] != '\'': lexMessage(L, errMissingFinalQuote) + if L.buf[L.bufpos] != '\'': + lexMessage(L, errGenerated, "missing closing ' for character literal") tokenEndIgnore(tok, L.bufpos) inc(L.bufpos) # skip ' @@ -826,7 +829,7 @@ proc getSymbol(L: var TLexer, tok: var TToken) = inc(pos) of '_': if buf[pos+1] notin SymChars: - lexMessage(L, errInvalidToken, "_") + lexMessage(L, errGenerated, "invalid token: trailing underscore") break inc(pos) else: break @@ -1014,7 +1017,7 @@ proc skip(L: var TLexer, tok: var TToken) = inc(pos) inc(tok.strongSpaceA) of '\t': - if not L.allowTabs: lexMessagePos(L, errTabulatorsAreNotAllowed, pos) + if not L.allowTabs: lexMessagePos(L, errGenerated, pos, "tabulators are not allowed") inc(pos) of CR, LF: tokenEndPrevious(tok, pos) @@ -1182,7 +1185,7 @@ proc rawGetTok*(L: var TLexer, tok: var TToken) = else: tok.literal = $c tok.tokType = tkInvalid - lexMessage(L, errInvalidToken, c & " (\\" & $(ord(c)) & ')') + lexMessage(L, errGenerated, "invalid token: " & c & " (\\" & $(ord(c)) & ')') of '\"': # check for extended raw string literal: var rawMode = L.bufpos > 0 and L.buf[L.bufpos-1] in SymChars @@ -1199,7 +1202,7 @@ proc rawGetTok*(L: var TLexer, tok: var TToken) = getNumber(L, tok) let c = L.buf[L.bufpos] if c in SymChars+{'_'}: - lexMessage(L, errInvalidToken, c & " (\\" & $(ord(c)) & ')') + lexMessage(L, errGenerated, "invalid token: no whitespace between number and identifier") else: if c in OpChars: getOperator(L, tok) @@ -1209,6 +1212,6 @@ proc rawGetTok*(L: var TLexer, tok: var TToken) = else: tok.literal = $c tok.tokType = tkInvalid - lexMessage(L, errInvalidToken, c & " (\\" & $(ord(c)) & ')') + lexMessage(L, errGenerated, "invalid token: " & c & " (\\" & $(ord(c)) & ')') inc(L.bufpos) atTokenEnd() diff --git a/compiler/msgs.nim b/compiler/msgs.nim index b6cd05e06..08be515d1 100644 --- a/compiler/msgs.nim +++ b/compiler/msgs.nim @@ -8,481 +8,13 @@ # import - options, strutils, os, tables, ropes, platform, terminal, macros + options, strutils, os, tables, ropes, platform, terminal, macros, + configuration -const - explanationsBaseUrl* = "https://nim-lang.org/docs/manual" +#type +# MsgConfig* = ref object of RootObj type - TMsgKind* = enum - errUnknown, errInternal, errIllFormedAstX, errCannotOpenFile, errGenerated, - errStringLiteralExpected, - errIntLiteralExpected, errInvalidCharacterConstant, - errClosingTripleQuoteExpected, errClosingQuoteExpected, - errTabulatorsAreNotAllowed, errInvalidToken, - errInvalidNumber, errInvalidNumberOctalCode, errNumberOutOfRange, - errNnotAllowedInCharacter, errClosingBracketExpected, errMissingFinalQuote, - errIdentifierExpected, errNewlineExpected, errInvalidModuleName, - errOperatorExpected, errTokenExpected, - errRecursiveDependencyX, errOnOrOffExpected, errNoneSpeedOrSizeExpected, - errInvalidPragma, errUnknownPragma, errInvalidDirectiveX, - errAtPopWithoutPush, errEmptyAsm, errInvalidIndentation, - errExceptionAlreadyHandled, - errYieldNotAllowedHere, errYieldNotAllowedInTryStmt, - errInvalidNumberOfYieldExpr, errCannotReturnExpr, - errNoReturnWithReturnTypeNotAllowed, errAttemptToRedefine, - errStmtInvalidAfterReturn, errStmtExpected, errInvalidLabel, - errInvalidCmdLineOption, errCmdLineArgExpected, errCmdLineNoArgExpected, - errInvalidVarSubstitution, errUnknownVar, errUnknownCcompiler, - errOnOrOffExpectedButXFound, errOnOffOrListExpectedButXFound, - errNoneBoehmRefcExpectedButXFound, - errNoneSpeedOrSizeExpectedButXFound, errGuiConsoleOrLibExpectedButXFound, - errUnknownOS, errUnknownCPU, errGenOutExpectedButXFound, - errArgsNeedRunOption, errInvalidMultipleAsgn, errColonOrEqualsExpected, - errExprExpected, errUndeclaredField, - errUndeclaredRoutine, errUseQualifier, - errTypeExpected, - errSystemNeeds, errExecutionOfProgramFailed, errNotOverloadable, - errInvalidArgForX, errStmtHasNoEffect, errXExpectsTypeOrValue, - errXExpectsArrayType, errIteratorCannotBeInstantiated, errExprXAmbiguous, - errConstantDivisionByZero, errOrdinalTypeExpected, - errOrdinalOrFloatTypeExpected, errOverOrUnderflow, - errCannotEvalXBecauseIncompletelyDefined, errChrExpectsRange0_255, - errDynlibRequiresExportc, errUndeclaredFieldX, errNilAccess, - errIndexOutOfBounds, errIndexTypesDoNotMatch, errBracketsInvalidForType, - errValueOutOfSetBounds, errFieldInitTwice, errFieldNotInit, - errExprXCannotBeCalled, errExprHasNoType, errExprXHasNoType, - errCastNotInSafeMode, errExprCannotBeCastToX, errCommaOrParRiExpected, - errCurlyLeOrParLeExpected, errSectionExpected, errRangeExpected, - errMagicOnlyInSystem, errPowerOfTwoExpected, - errStringMayNotBeEmpty, errCallConvExpected, errProcOnlyOneCallConv, - errSymbolMustBeImported, errExprMustBeBool, errConstExprExpected, - errDuplicateCaseLabel, errRangeIsEmpty, errSelectorMustBeOfCertainTypes, - errSelectorMustBeOrdinal, errOrdXMustNotBeNegative, errLenXinvalid, - errWrongNumberOfVariables, errExprCannotBeRaised, errBreakOnlyInLoop, - errTypeXhasUnknownSize, errConstNeedsConstExpr, errConstNeedsValue, - errResultCannotBeOpenArray, errSizeTooBig, errSetTooBig, - errBaseTypeMustBeOrdinal, errInheritanceOnlyWithNonFinalObjects, - errInheritanceOnlyWithEnums, errIllegalRecursionInTypeX, - errCannotInstantiateX, errExprHasNoAddress, errXStackEscape, - errVarForOutParamNeededX, - errPureTypeMismatch, errTypeMismatch, errButExpected, errButExpectedX, - errAmbiguousCallXYZ, errWrongNumberOfArguments, - errWrongNumberOfArgumentsInCall, - errMissingGenericParamsForTemplate, - errXCannotBePassedToProcVar, - errPragmaOnlyInHeaderOfProcX, errImplOfXNotAllowed, - errImplOfXexpected, errNoSymbolToBorrowFromFound, errDiscardValueX, - errInvalidDiscard, errIllegalConvFromXtoY, errCannotBindXTwice, - errInvalidOrderInArrayConstructor, - errInvalidOrderInEnumX, errEnumXHasHoles, errExceptExpected, errInvalidTry, - errOptionExpected, errXisNoLabel, errNotAllCasesCovered, - errUnknownSubstitionVar, errComplexStmtRequiresInd, errXisNotCallable, - errNoPragmasAllowedForX, errNoGenericParamsAllowedForX, - errInvalidParamKindX, errDefaultArgumentInvalid, errNamedParamHasToBeIdent, - errNoReturnTypeForX, errConvNeedsOneArg, errInvalidPragmaX, - errXNotAllowedHere, errInvalidControlFlowX, - errXisNoType, errCircumNeedsPointer, errInvalidExpression, - errInvalidExpressionX, errEnumHasNoValueX, errNamedExprExpected, - errNamedExprNotAllowed, errXExpectsOneTypeParam, - errArrayExpectsTwoTypeParams, errInvalidVisibilityX, errInitHereNotAllowed, - errXCannotBeAssignedTo, errIteratorNotAllowed, errXNeedsReturnType, - errNoReturnTypeDeclared, - errNoCommand, errInvalidCommandX, errXOnlyAtModuleScope, - errXNeedsParamObjectType, - errTemplateInstantiationTooNested, errMacroInstantiationTooNested, - errInstantiationFrom, - errInvalidIndexValueForTuple, errCommandExpectsFilename, - errMainModuleMustBeSpecified, - errXExpected, - errTIsNotAConcreteType, - errCastToANonConcreteType, - errInvalidSectionStart, errGridTableNotImplemented, errGeneralParseError, - errNewSectionExpected, errWhitespaceExpected, errXisNoValidIndexFile, - errCannotRenderX, errVarVarTypeNotAllowed, errInstantiateXExplicitly, - errOnlyACallOpCanBeDelegator, errUsingNoSymbol, - errMacroBodyDependsOnGenericTypes, - errDestructorNotGenericEnough, - errInlineIteratorsAsProcParams, - errXExpectsTwoArguments, - errXExpectsObjectTypes, errXcanNeverBeOfThisSubtype, errTooManyIterations, - errCannotInterpretNodeX, errFieldXNotFound, errInvalidConversionFromTypeX, - errAssertionFailed, errCannotGenerateCodeForX, errXRequiresOneArgument, - errUnhandledExceptionX, errCyclicTree, errXisNoMacroOrTemplate, - errXhasSideEffects, errIteratorExpected, errLetNeedsInit, - errThreadvarCannotInit, errWrongSymbolX, errIllegalCaptureX, - errXCannotBeClosure, errXMustBeCompileTime, - errCannotInferTypeOfTheLiteral, - errCannotInferReturnType, - errCannotInferStaticParam, - errGenericLambdaNotAllowed, - errProcHasNoConcreteType, - errCompilerDoesntSupportTarget, - errInOutFlagNotExtern, - errUser, - warnCannotOpenFile, - warnOctalEscape, warnXIsNeverRead, warnXmightNotBeenInit, - warnDeprecated, warnConfigDeprecated, - warnSmallLshouldNotBeUsed, warnUnknownMagic, warnRedefinitionOfLabel, - warnUnknownSubstitutionX, warnLanguageXNotSupported, - warnFieldXNotSupported, warnCommentXIgnored, - warnTypelessParam, - warnUseBase, warnWriteToForeignHeap, warnUnsafeCode, - warnEachIdentIsTuple, warnShadowIdent, - warnProveInit, warnProveField, warnProveIndex, warnGcUnsafe, warnGcUnsafe2, - warnUninit, warnGcMem, warnDestructor, warnLockLevel, warnResultShadowed, - warnInconsistentSpacing, warnUser, - hintSuccess, hintSuccessX, - hintLineTooLong, hintXDeclaredButNotUsed, hintConvToBaseNotNeeded, - hintConvFromXtoItselfNotNeeded, hintExprAlwaysX, hintQuitCalled, - hintProcessing, hintCodeBegin, hintCodeEnd, hintConf, hintPath, - hintConditionAlwaysTrue, hintName, hintPattern, - hintExecuting, hintLinking, hintDependency, - hintSource, hintPerformance, hintStackTrace, hintGCStats, - hintUser, hintUserRaw - -const - MsgKindToStr*: array[TMsgKind, string] = [ - errUnknown: "unknown error", - errInternal: "internal error: $1", - errIllFormedAstX: "illformed AST: $1", - errCannotOpenFile: "cannot open '$1'", - errGenerated: "$1", - errStringLiteralExpected: "string literal expected", - errIntLiteralExpected: "integer literal expected", - errInvalidCharacterConstant: "invalid character constant", - errClosingTripleQuoteExpected: "closing \"\"\" expected, but end of file reached", - errClosingQuoteExpected: "closing \" expected", - errTabulatorsAreNotAllowed: "tabulators are not allowed", - errInvalidToken: "invalid token: $1", - errInvalidNumber: "$1 is not a valid number", - errInvalidNumberOctalCode: "$1 is not a valid number; did you mean octal? Then use one of '0o', '0c' or '0C'.", - errNumberOutOfRange: "number $1 out of valid range", - errNnotAllowedInCharacter: "\\n not allowed in character literal", - errClosingBracketExpected: "closing ']' expected, but end of file reached", - errMissingFinalQuote: "missing final ' for character literal", - errIdentifierExpected: "identifier expected, but found '$1'", - errNewlineExpected: "newline expected, but found '$1'", - errInvalidModuleName: "invalid module name: '$1'", - errOperatorExpected: "operator expected, but found '$1'", - errTokenExpected: "'$1' expected", - errRecursiveDependencyX: "recursive dependency: '$1'", - errOnOrOffExpected: "'on' or 'off' expected", - errNoneSpeedOrSizeExpected: "'none', 'speed' or 'size' expected", - errInvalidPragma: "invalid pragma", - errUnknownPragma: "unknown pragma: '$1'", - errInvalidDirectiveX: "invalid directive: '$1'", - errAtPopWithoutPush: "'pop' without a 'push' pragma", - errEmptyAsm: "empty asm statement", - errInvalidIndentation: "invalid indentation", - errExceptionAlreadyHandled: "exception already handled", - errYieldNotAllowedHere: "'yield' only allowed in an iterator", - errYieldNotAllowedInTryStmt: "'yield' cannot be used within 'try' in a non-inlined iterator", - errInvalidNumberOfYieldExpr: "invalid number of 'yield' expressions", - errCannotReturnExpr: "current routine cannot return an expression", - errNoReturnWithReturnTypeNotAllowed: "routines with NoReturn pragma are not allowed to have return type", - errAttemptToRedefine: "redefinition of '$1'", - errStmtInvalidAfterReturn: "statement not allowed after 'return', 'break', 'raise', 'continue' or proc call with noreturn pragma", - errStmtExpected: "statement expected", - errInvalidLabel: "'$1' is no label", - errInvalidCmdLineOption: "invalid command line option: '$1'", - errCmdLineArgExpected: "argument for command line option expected: '$1'", - errCmdLineNoArgExpected: "invalid argument for command line option: '$1'", - errInvalidVarSubstitution: "invalid variable substitution in '$1'", - errUnknownVar: "unknown variable: '$1'", - errUnknownCcompiler: "unknown C compiler: '$1'", - errOnOrOffExpectedButXFound: "'on' or 'off' expected, but '$1' found", - errOnOffOrListExpectedButXFound: "'on', 'off' or 'list' expected, but '$1' found", - errNoneBoehmRefcExpectedButXFound: "'none', 'boehm' or 'refc' expected, but '$1' found", - errNoneSpeedOrSizeExpectedButXFound: "'none', 'speed' or 'size' expected, but '$1' found", - errGuiConsoleOrLibExpectedButXFound: "'gui', 'console' or 'lib' expected, but '$1' found", - errUnknownOS: "unknown OS: '$1'", - errUnknownCPU: "unknown CPU: '$1'", - errGenOutExpectedButXFound: "'c', 'c++' or 'yaml' expected, but '$1' found", - errArgsNeedRunOption: "arguments can only be given if the '--run' option is selected", - errInvalidMultipleAsgn: "multiple assignment is not allowed", - errColonOrEqualsExpected: "':' or '=' expected, but found '$1'", - errExprExpected: "expression expected, but found '$1'", - errUndeclaredField: "undeclared field: '$1'", - errUndeclaredRoutine: "attempting to call undeclared routine: '$1'", - errUseQualifier: "ambiguous identifier: '$1' -- use a qualifier", - errTypeExpected: "type expected", - errSystemNeeds: "system module needs '$1'", - errExecutionOfProgramFailed: "execution of an external program failed: '$1'", - errNotOverloadable: "overloaded '$1' leads to ambiguous calls", - errInvalidArgForX: "invalid argument for '$1'", - errStmtHasNoEffect: "statement has no effect", - errXExpectsTypeOrValue: "'$1' expects a type or value", - errXExpectsArrayType: "'$1' expects an array type", - errIteratorCannotBeInstantiated: "'$1' cannot be instantiated because its body has not been compiled yet", - errExprXAmbiguous: "expression '$1' ambiguous in this context", - errConstantDivisionByZero: "division by zero", - errOrdinalTypeExpected: "ordinal type expected", - errOrdinalOrFloatTypeExpected: "ordinal or float type expected", - errOverOrUnderflow: "over- or underflow", - errCannotEvalXBecauseIncompletelyDefined: "cannot evaluate '$1' because type is not defined completely", - errChrExpectsRange0_255: "'chr' expects an int in the range 0..255", - errDynlibRequiresExportc: "'dynlib' requires 'exportc'", - errUndeclaredFieldX: "undeclared field: '$1'", - errNilAccess: "attempt to access a nil address", - errIndexOutOfBounds: "index out of bounds", - errIndexTypesDoNotMatch: "index types do not match", - errBracketsInvalidForType: "'[]' operator invalid for this type", - errValueOutOfSetBounds: "value out of set bounds", - errFieldInitTwice: "field initialized twice: '$1'", - errFieldNotInit: "field '$1' not initialized", - errExprXCannotBeCalled: "expression '$1' cannot be called", - errExprHasNoType: "expression has no type", - errExprXHasNoType: "expression '$1' has no type (or is ambiguous)", - errCastNotInSafeMode: "'cast' not allowed in safe mode", - errExprCannotBeCastToX: "expression cannot be cast to $1", - errCommaOrParRiExpected: "',' or ')' expected", - errCurlyLeOrParLeExpected: "'{' or '(' expected", - errSectionExpected: "section ('type', 'proc', etc.) expected", - errRangeExpected: "range expected", - errMagicOnlyInSystem: "'magic' only allowed in system module", - errPowerOfTwoExpected: "power of two expected", - errStringMayNotBeEmpty: "string literal may not be empty", - errCallConvExpected: "calling convention expected", - errProcOnlyOneCallConv: "a proc can only have one calling convention", - errSymbolMustBeImported: "symbol must be imported if 'lib' pragma is used", - errExprMustBeBool: "expression must be of type 'bool'", - errConstExprExpected: "constant expression expected", - errDuplicateCaseLabel: "duplicate case label", - errRangeIsEmpty: "range is empty", - errSelectorMustBeOfCertainTypes: "selector must be of an ordinal type, float or string", - errSelectorMustBeOrdinal: "selector must be of an ordinal type", - errOrdXMustNotBeNegative: "ord($1) must not be negative", - errLenXinvalid: "len($1) must be less than 32768", - errWrongNumberOfVariables: "wrong number of variables", - errExprCannotBeRaised: "only a 'ref object' can be raised", - errBreakOnlyInLoop: "'break' only allowed in loop construct", - errTypeXhasUnknownSize: "type '$1' has unknown size", - errConstNeedsConstExpr: "a constant can only be initialized with a constant expression", - errConstNeedsValue: "a constant needs a value", - errResultCannotBeOpenArray: "the result type cannot be on open array", - errSizeTooBig: "computing the type's size produced an overflow", - errSetTooBig: "set is too large", - errBaseTypeMustBeOrdinal: "base type of a set must be an ordinal", - errInheritanceOnlyWithNonFinalObjects: "inheritance only works with non-final objects", - errInheritanceOnlyWithEnums: "inheritance only works with an enum", - errIllegalRecursionInTypeX: "illegal recursion in type '$1'", - errCannotInstantiateX: "cannot instantiate: '$1'", - errExprHasNoAddress: "expression has no address", - errXStackEscape: "address of '$1' may not escape its stack frame", - errVarForOutParamNeededX: "for a 'var' type a variable needs to be passed; but '$1' is immutable", - errPureTypeMismatch: "type mismatch", - errTypeMismatch: "type mismatch: got <", - errButExpected: "but expected one of: ", - errButExpectedX: "but expected '$1'", - errAmbiguousCallXYZ: "ambiguous call; both $1 and $2 match for: $3", - errWrongNumberOfArguments: "wrong number of arguments", - errWrongNumberOfArgumentsInCall: "wrong number of arguments in call to '$1'", - errMissingGenericParamsForTemplate: "'$1' has unspecified generic parameters", - errXCannotBePassedToProcVar: "'$1' cannot be passed to a procvar", - errPragmaOnlyInHeaderOfProcX: "pragmas are only allowed in the header of a proc; redefinition of $1", - errImplOfXNotAllowed: "implementation of '$1' is not allowed", - errImplOfXexpected: "implementation of '$1' expected", - errNoSymbolToBorrowFromFound: "no symbol to borrow from found", - errDiscardValueX: "value of type '$1' has to be discarded", - errInvalidDiscard: "statement returns no value that can be discarded", - errIllegalConvFromXtoY: "conversion from $1 to $2 is invalid", - errCannotBindXTwice: "cannot bind parameter '$1' twice", - errInvalidOrderInArrayConstructor: "invalid order in array constructor", - errInvalidOrderInEnumX: "invalid order in enum '$1'", - errEnumXHasHoles: "enum '$1' has holes", - errExceptExpected: "'except' or 'finally' expected", - errInvalidTry: "after catch all 'except' or 'finally' no section may follow", - errOptionExpected: "option expected, but found '$1'", - errXisNoLabel: "'$1' is not a label", - errNotAllCasesCovered: "not all cases are covered", - errUnknownSubstitionVar: "unknown substitution variable: '$1'", - errComplexStmtRequiresInd: "complex statement requires indentation", - errXisNotCallable: "'$1' is not callable", - errNoPragmasAllowedForX: "no pragmas allowed for $1", - errNoGenericParamsAllowedForX: "no generic parameters allowed for $1", - errInvalidParamKindX: "invalid param kind: '$1'", - errDefaultArgumentInvalid: "default argument invalid", - errNamedParamHasToBeIdent: "named parameter has to be an identifier", - errNoReturnTypeForX: "no return type allowed for $1", - errConvNeedsOneArg: "a type conversion needs exactly one argument", - errInvalidPragmaX: "invalid pragma: $1", - errXNotAllowedHere: "$1 not allowed here", - errInvalidControlFlowX: "invalid control flow: $1", - errXisNoType: "invalid type: '$1'", - errCircumNeedsPointer: "'[]' needs a pointer or reference type", - errInvalidExpression: "invalid expression", - errInvalidExpressionX: "invalid expression: '$1'", - errEnumHasNoValueX: "enum has no value '$1'", - errNamedExprExpected: "named expression expected", - errNamedExprNotAllowed: "named expression not allowed here", - errXExpectsOneTypeParam: "'$1' expects one type parameter", - errArrayExpectsTwoTypeParams: "array expects two type parameters", - errInvalidVisibilityX: "invalid visibility: '$1'", - errInitHereNotAllowed: "initialization not allowed here", - errXCannotBeAssignedTo: "'$1' cannot be assigned to", - errIteratorNotAllowed: "iterators can only be defined at the module's top level", - errXNeedsReturnType: "$1 needs a return type", - errNoReturnTypeDeclared: "no return type declared", - errNoCommand: "no command given", - errInvalidCommandX: "invalid command: '$1'", - errXOnlyAtModuleScope: "'$1' is only allowed at top level", - errXNeedsParamObjectType: "'$1' needs a parameter that has an object type", - errTemplateInstantiationTooNested: "template instantiation too nested, try --evalTemplateLimit:N", - errMacroInstantiationTooNested: "macro instantiation too nested, try --evalMacroLimit:N", - errInstantiationFrom: "template/generic instantiation from here", - errInvalidIndexValueForTuple: "invalid index value for tuple subscript", - errCommandExpectsFilename: "command expects a filename argument", - errMainModuleMustBeSpecified: "please, specify a main module in the project configuration file", - errXExpected: "'$1' expected", - errTIsNotAConcreteType: "'$1' is not a concrete type.", - errCastToANonConcreteType: "cannot cast to a non concrete type: '$1'", - errInvalidSectionStart: "invalid section start", - errGridTableNotImplemented: "grid table is not implemented", - errGeneralParseError: "general parse error", - errNewSectionExpected: "new section expected", - errWhitespaceExpected: "whitespace expected, got '$1'", - errXisNoValidIndexFile: "'$1' is no valid index file", - errCannotRenderX: "cannot render reStructuredText element '$1'", - errVarVarTypeNotAllowed: "type 'var var' is not allowed", - errInstantiateXExplicitly: "instantiate '$1' explicitly", - errOnlyACallOpCanBeDelegator: "only a call operator can be a delegator", - errUsingNoSymbol: "'$1' is not a variable, constant or a proc name", - errMacroBodyDependsOnGenericTypes: "the macro body cannot be compiled, " & - "because the parameter '$1' has a generic type", - errDestructorNotGenericEnough: "Destructor signature is too specific. " & - "A destructor must be associated will all instantiations of a generic type", - errInlineIteratorsAsProcParams: "inline iterators can be used as parameters only for " & - "templates, macros and other inline iterators", - errXExpectsTwoArguments: "'$1' expects two arguments", - errXExpectsObjectTypes: "'$1' expects object types", - errXcanNeverBeOfThisSubtype: "'$1' can never be of this subtype", - errTooManyIterations: "interpretation requires too many iterations; " & - "if you are sure this is not a bug in your code edit " & - "compiler/vmdef.MaxLoopIterations and rebuild the compiler", - errCannotInterpretNodeX: "cannot evaluate '$1'", - errFieldXNotFound: "field '$1' cannot be found", - errInvalidConversionFromTypeX: "invalid conversion from type '$1'", - errAssertionFailed: "assertion failed", - errCannotGenerateCodeForX: "cannot generate code for '$1'", - errXRequiresOneArgument: "$1 requires one parameter", - errUnhandledExceptionX: "unhandled exception: $1", - errCyclicTree: "macro returned a cyclic abstract syntax tree", - errXisNoMacroOrTemplate: "'$1' is no macro or template", - errXhasSideEffects: "'$1' can have side effects", - errIteratorExpected: "iterator within for loop context expected", - errLetNeedsInit: "'let' symbol requires an initialization", - errThreadvarCannotInit: "a thread var cannot be initialized explicitly; this would only run for the main thread", - errWrongSymbolX: "usage of '$1' is a user-defined error", - errIllegalCaptureX: "illegal capture '$1'", - errXCannotBeClosure: "'$1' cannot have 'closure' calling convention", - errXMustBeCompileTime: "'$1' can only be used in compile-time context", - errCannotInferTypeOfTheLiteral: "cannot infer the type of the $1", - errCannotInferReturnType: "cannot infer the return type of the proc", - errCannotInferStaticParam: "cannot infer the value of the static param `$1`", - errGenericLambdaNotAllowed: "A nested proc can have generic parameters only when " & - "it is used as an operand to another routine and the types " & - "of the generic paramers can be inferred from the expected signature.", - errProcHasNoConcreteType: "'$1' doesn't have a concrete type, due to unspecified generic parameters.", - errCompilerDoesntSupportTarget: "The current compiler '$1' doesn't support the requested compilation target", - errInOutFlagNotExtern: "The `$1` modifier can be used only with imported types", - errUser: "$1", - warnCannotOpenFile: "cannot open '$1'", - warnOctalEscape: "octal escape sequences do not exist; leading zero is ignored", - warnXIsNeverRead: "'$1' is never read", - warnXmightNotBeenInit: "'$1' might not have been initialized", - warnDeprecated: "$1 is deprecated", - warnConfigDeprecated: "config file '$1' is deprecated", - warnSmallLshouldNotBeUsed: "'l' should not be used as an identifier; may look like '1' (one)", - warnUnknownMagic: "unknown magic '$1' might crash the compiler", - warnRedefinitionOfLabel: "redefinition of label '$1'", - warnUnknownSubstitutionX: "unknown substitution '$1'", - warnLanguageXNotSupported: "language '$1' not supported", - warnFieldXNotSupported: "field '$1' not supported", - warnCommentXIgnored: "comment '$1' ignored", - warnTypelessParam: "'$1' has no type. Typeless parameters are deprecated; only allowed for 'template'", - warnUseBase: "use {.base.} for base methods; baseless methods are deprecated", - warnWriteToForeignHeap: "write to foreign heap", - warnUnsafeCode: "unsafe code: '$1'", - warnEachIdentIsTuple: "each identifier is a tuple", - warnShadowIdent: "shadowed identifier: '$1'", - warnProveInit: "Cannot prove that '$1' is initialized. This will become a compile time error in the future.", - warnProveField: "cannot prove that field '$1' is accessible", - warnProveIndex: "cannot prove index '$1' is valid", - warnGcUnsafe: "not GC-safe: '$1'", - warnGcUnsafe2: "$1", - warnUninit: "'$1' might not have been initialized", - warnGcMem: "'$1' uses GC'ed memory", - warnDestructor: "usage of a type with a destructor in a non destructible context. This will become a compile time error in the future.", - warnLockLevel: "$1", - warnResultShadowed: "Special variable 'result' is shadowed.", - warnInconsistentSpacing: "Number of spaces around '$#' is not consistent", - warnUser: "$1", - hintSuccess: "operation successful", - hintSuccessX: "operation successful ($# lines compiled; $# sec total; $#; $#)", - hintLineTooLong: "line too long", - hintXDeclaredButNotUsed: "'$1' is declared but not used", - hintConvToBaseNotNeeded: "conversion to base object is not needed", - hintConvFromXtoItselfNotNeeded: "conversion from $1 to itself is pointless", - hintExprAlwaysX: "expression evaluates always to '$1'", - hintQuitCalled: "quit() called", - hintProcessing: "$1", - hintCodeBegin: "generated code listing:", - hintCodeEnd: "end of listing", - hintConf: "used config file '$1'", - hintPath: "added path: '$1'", - hintConditionAlwaysTrue: "condition is always true: '$1'", - hintName: "name should be: '$1'", - hintPattern: "$1", - hintExecuting: "$1", - hintLinking: "", - hintDependency: "$1", - hintSource: "$1", - hintPerformance: "$1", - hintStackTrace: "$1", - hintGCStats: "$1", - hintUser: "$1", - hintUserRaw: "$1"] - -const - WarningsToStr* = ["CannotOpenFile", "OctalEscape", - "XIsNeverRead", "XmightNotBeenInit", - "Deprecated", "ConfigDeprecated", - "SmallLshouldNotBeUsed", "UnknownMagic", - "RedefinitionOfLabel", "UnknownSubstitutionX", - "LanguageXNotSupported", "FieldXNotSupported", - "CommentXIgnored", - "TypelessParam", "UseBase", "WriteToForeignHeap", - "UnsafeCode", "EachIdentIsTuple", "ShadowIdent", - "ProveInit", "ProveField", "ProveIndex", "GcUnsafe", "GcUnsafe2", "Uninit", - "GcMem", "Destructor", "LockLevel", "ResultShadowed", - "Spacing", "User"] - - HintsToStr* = ["Success", "SuccessX", "LineTooLong", - "XDeclaredButNotUsed", "ConvToBaseNotNeeded", "ConvFromXtoItselfNotNeeded", - "ExprAlwaysX", "QuitCalled", "Processing", "CodeBegin", "CodeEnd", "Conf", - "Path", "CondTrue", "Name", "Pattern", "Exec", "Link", "Dependency", - "Source", "Performance", "StackTrace", "GCStats", - "User", "UserRaw"] - -const - fatalMin* = errUnknown - fatalMax* = errInternal - errMin* = errUnknown - errMax* = errUser - warnMin* = warnCannotOpenFile - warnMax* = pred(hintSuccess) - hintMin* = hintSuccess - hintMax* = high(TMsgKind) - -static: - doAssert HintsToStr.len == ord(hintMax) - ord(hintMin) + 1 - doAssert WarningsToStr.len == ord(warnMax) - ord(warnMin) + 1 - -type - TNoteKind* = range[warnMin..hintMax] # "notes" are warnings or hints - TNoteKinds* = set[TNoteKind] - TFileInfo* = object fullPath: string # This is a canonical full filesystem path projPath*: string # This is relative to the project's root @@ -527,35 +59,11 @@ type proc `==`*(a, b: FileIndex): bool {.borrow.} -const - NotesVerbosity*: array[0..3, TNoteKinds] = [ - {low(TNoteKind)..high(TNoteKind)} - {warnShadowIdent, warnUninit, - warnProveField, warnProveIndex, - warnGcUnsafe, - hintSuccessX, hintPath, hintConf, - hintProcessing, hintPattern, - hintDependency, - hintExecuting, hintLinking, - hintCodeBegin, hintCodeEnd, - hintSource, hintStackTrace, - hintGCStats}, - {low(TNoteKind)..high(TNoteKind)} - {warnShadowIdent, warnUninit, - warnProveField, warnProveIndex, - warnGcUnsafe, - hintPath, - hintDependency, - hintCodeBegin, hintCodeEnd, - hintSource, hintStackTrace, - hintGCStats}, - {low(TNoteKind)..high(TNoteKind)} - {hintStackTrace, warnUninit}, - {low(TNoteKind)..high(TNoteKind)}] const InvalidFileIDX* = FileIndex(-1) var - ForeignPackageNotes*: TNoteKinds = {hintProcessing, warnUnknownMagic, - hintQuitCalled, hintExecuting} filenameToIndexTbl = initTable[string, FileIndex]() fileInfos*: seq[TFileInfo] = @[] systemFileIdx*: FileIndex @@ -606,22 +114,22 @@ when defined(nimpretty): proc fileSection*(fid: FileIndex; a, b: int): string = substr(fileInfos[fid.int].fullContent, a, b) -proc fileInfoKnown*(filename: string): bool = +proc fileInfoKnown*(conf: ConfigRef; filename: string): bool = var canon: string try: - canon = canonicalizePath(filename) + canon = canonicalizePath(conf, filename) except: canon = filename result = filenameToIndexTbl.hasKey(canon) -proc fileInfoIdx*(filename: string; isKnownFile: var bool): FileIndex = +proc fileInfoIdx*(conf: ConfigRef; filename: string; isKnownFile: var bool): FileIndex = var canon: string pseudoPath = false try: - canon = canonicalizePath(filename) + canon = canonicalizePath(conf, filename) shallow(canon) except: canon = filename @@ -635,40 +143,33 @@ proc fileInfoIdx*(filename: string; isKnownFile: var bool): FileIndex = isKnownFile = false result = fileInfos.len.FileIndex fileInfos.add(newFileInfo(canon, if pseudoPath: filename - else: canon.shortenDir)) + else: shortenDir(conf, canon))) filenameToIndexTbl[canon] = result -proc fileInfoIdx*(filename: string): FileIndex = +proc fileInfoIdx*(conf: ConfigRef; filename: string): FileIndex = var dummy: bool - result = fileInfoIdx(filename, dummy) + result = fileInfoIdx(conf, filename, dummy) proc newLineInfo*(fileInfoIdx: FileIndex, line, col: int): TLineInfo = result.fileIndex = fileInfoIdx result.line = uint16(line) result.col = int16(col) -proc newLineInfo*(filename: string, line, col: int): TLineInfo {.inline.} = - result = newLineInfo(filename.fileInfoIdx, line, col) +proc newLineInfo*(conf: ConfigRef; filename: string, line, col: int): TLineInfo {.inline.} = + result = newLineInfo(fileInfoIdx(conf, filename), line, col) -fileInfos.add(newFileInfo("", "command line")) -var gCmdLineInfo* = newLineInfo(FileIndex(0), 1, 1) +when false: + fileInfos.add(newFileInfo("", "command line")) + var gCmdLineInfo* = newLineInfo(FileIndex(0), 1, 1) -fileInfos.add(newFileInfo("", "compilation artifact")) -var gCodegenLineInfo* = newLineInfo(FileIndex(1), 1, 1) + fileInfos.add(newFileInfo("", "compilation artifact")) + var gCodegenLineInfo* = newLineInfo(FileIndex(1), 1, 1) proc raiseRecoverableError*(msg: string) {.noinline, noreturn.} = raise newException(ERecoverableError, msg) proc sourceLine*(i: TLineInfo): Rope -var - gNotes*: TNoteKinds = NotesVerbosity[1] # defaults to verbosity of 1 - gErrorCounter*: int = 0 # counts the number of errors - gHintCounter*: int = 0 - gWarnCounter*: int = 0 - gErrorMax*: int = 1 # stop after gErrorMax errors - gMainPackageNotes*: TNoteKinds = NotesVerbosity[1] - proc unknownLineInfo*(): TLineInfo = result.line = uint16(0) result.col = int16(-1) @@ -892,8 +393,8 @@ proc log*(s: string) {.procvar.} = f.writeLine(s) close(f) -proc quit(msg: TMsgKind) = - if defined(debug) or msg == errInternal or hintStackTrace in gNotes: +proc quit(conf: ConfigRef; msg: TMsgKind) = + if defined(debug) or msg == errInternal or hintStackTrace in conf.notes: if stackTraceAvailable() and isNil(writelnHook): writeStackTrace() else: @@ -902,17 +403,17 @@ proc quit(msg: TMsgKind) = options.command & " ") quit 1 -proc handleError(msg: TMsgKind, eh: TErrorHandling, s: string) = +proc handleError(conf: ConfigRef; msg: TMsgKind, eh: TErrorHandling, s: string) = if msg >= fatalMin and msg <= fatalMax: if gCmd == cmdIdeTools: log(s) - quit(msg) + quit(conf, msg) if msg >= errMin and msg <= errMax: - inc(gErrorCounter) + inc(conf.errorCounter) options.gExitcode = 1'i8 - if gErrorCounter >= gErrorMax: - quit(msg) + if conf.errorCounter >= conf.errorMax: + quit(conf, msg) elif eh == doAbort and gCmd != cmdIdeTools: - quit(msg) + quit(conf, msg) elif eh == doRaise: raiseRecoverableError(s) @@ -922,12 +423,13 @@ proc `==`*(a, b: TLineInfo): bool = proc exactEquals*(a, b: TLineInfo): bool = result = a.fileIndex == b.fileIndex and a.line == b.line and a.col == b.col -proc writeContext(lastinfo: TLineInfo) = +proc writeContext(conf: ConfigRef; lastinfo: TLineInfo) = + const instantiationFrom = "template/generic instantiation from here" var info = lastinfo for i in countup(0, len(msgContext) - 1): if msgContext[i] != lastinfo and msgContext[i] != info: if structuredErrorHook != nil: - structuredErrorHook(msgContext[i], getMessageStr(errInstantiationFrom, ""), + structuredErrorHook(msgContext[i], instantiationFrom, Severity.Error) else: styledMsgWriteln(styleBright, @@ -935,13 +437,13 @@ proc writeContext(lastinfo: TLineInfo) = coordToStr(msgContext[i].line.int), coordToStr(msgContext[i].col+1)], resetStyle, - getMessageStr(errInstantiationFrom, "")) + instantiationFrom) info = msgContext[i] -proc ignoreMsgBecauseOfIdeTools(msg: TMsgKind): bool = +proc ignoreMsgBecauseOfIdeTools(conf: ConfigRef; msg: TMsgKind): bool = msg >= errGenerated and gCmd == cmdIdeTools and optIdeDebug notin gGlobalOptions -proc rawMessage*(msg: TMsgKind, args: openArray[string]) = +proc rawMessage*(conf: ConfigRef; msg: TMsgKind, args: openArray[string]) = var title: string color: ForegroundColor @@ -950,43 +452,43 @@ proc rawMessage*(msg: TMsgKind, args: openArray[string]) = case msg of errMin..errMax: sev = Severity.Error - writeContext(unknownLineInfo()) + writeContext(conf, unknownLineInfo()) title = ErrorTitle color = ErrorColor of warnMin..warnMax: sev = Severity.Warning if optWarns notin gOptions: return - if msg notin gNotes: return - writeContext(unknownLineInfo()) + if msg notin conf.notes: return + writeContext(conf, unknownLineInfo()) title = WarningTitle color = WarningColor kind = WarningsToStr[ord(msg) - ord(warnMin)] - inc(gWarnCounter) + inc(conf.warnCounter) of hintMin..hintMax: sev = Severity.Hint if optHints notin gOptions: return - if msg notin gNotes: return + if msg notin conf.notes: return title = HintTitle color = HintColor if msg != hintUserRaw: kind = HintsToStr[ord(msg) - ord(hintMin)] - inc(gHintCounter) + inc(conf.hintCounter) let s = msgKindToString(msg) % args if structuredErrorHook != nil: structuredErrorHook(unknownLineInfo(), s & (if kind != nil: KindFormat % kind else: ""), sev) - if not ignoreMsgBecauseOfIdeTools(msg): + if not ignoreMsgBecauseOfIdeTools(conf, msg): if kind != nil: styledMsgWriteln(color, title, resetStyle, s, KindColor, `%`(KindFormat, kind)) else: styledMsgWriteln(color, title, resetStyle, s) - handleError(msg, doAbort, s) + handleError(conf, msg, doAbort, s) -proc rawMessage*(msg: TMsgKind, arg: string) = - rawMessage(msg, [arg]) +proc rawMessage*(conf: ConfigRef; msg: TMsgKind, arg: string) = + rawMessage(conf, msg, [arg]) -proc resetAttributes* = +proc resetAttributes*(conf: ConfigRef) = if {optUseColors, optStdout} * gGlobalOptions == {optUseColors}: terminal.resetAttributes(stderr) @@ -1005,7 +507,7 @@ proc formatMsg*(info: TLineInfo, msg: TMsgKind, arg: string): string = title & getMessageStr(msg, arg) -proc liMessage(info: TLineInfo, msg: TMsgKind, arg: string, +proc liMessage(conf: ConfigRef; info: TLineInfo, msg: TMsgKind, arg: string, eh: TErrorHandling) = var title: string @@ -1016,7 +518,7 @@ proc liMessage(info: TLineInfo, msg: TMsgKind, arg: string, case msg of errMin..errMax: sev = Severity.Error - writeContext(info) + writeContext(conf, info) title = ErrorTitle color = ErrorColor # we try to filter error messages so that not two error message @@ -1025,19 +527,19 @@ proc liMessage(info: TLineInfo, msg: TMsgKind, arg: string, lastError = info of warnMin..warnMax: sev = Severity.Warning - ignoreMsg = optWarns notin gOptions or msg notin gNotes - if not ignoreMsg: writeContext(info) + ignoreMsg = optWarns notin gOptions or msg notin conf.notes + if not ignoreMsg: writeContext(conf, info) title = WarningTitle color = WarningColor kind = WarningsToStr[ord(msg) - ord(warnMin)] - inc(gWarnCounter) + inc(conf.warnCounter) of hintMin..hintMax: sev = Severity.Hint - ignoreMsg = optHints notin gOptions or msg notin gNotes + ignoreMsg = optHints notin gOptions or msg notin conf.notes title = HintTitle color = HintColor if msg != hintUserRaw: kind = HintsToStr[ord(msg) - ord(hintMin)] - inc(gHintCounter) + inc(conf.hintCounter) # NOTE: currently line info line numbers start with 1, # but column numbers start with 0, however most editors expect # first column to be 1, so we need to +1 here @@ -1048,56 +550,56 @@ proc liMessage(info: TLineInfo, msg: TMsgKind, arg: string, if not ignoreMsg: if structuredErrorHook != nil: structuredErrorHook(info, s & (if kind != nil: KindFormat % kind else: ""), sev) - if not ignoreMsgBecauseOfIdeTools(msg): + if not ignoreMsgBecauseOfIdeTools(conf, msg): if kind != nil: styledMsgWriteln(styleBright, x, resetStyle, color, title, resetStyle, s, KindColor, `%`(KindFormat, kind)) else: styledMsgWriteln(styleBright, x, resetStyle, color, title, resetStyle, s) - if hintSource in gNotes: - info.writeSurroundingSrc - handleError(msg, eh, s) + if hintSource in conf.notes: + info.writeSurroundingSrc() + handleError(conf, msg, eh, s) -proc fatal*(info: TLineInfo, msg: TMsgKind, arg = "") = +proc fatal*(conf: ConfigRef; info: TLineInfo, msg: TMsgKind, arg = "") = # this fixes bug #7080 so that it is at least obvious 'fatal' # was executed. errorOutputs = {eStdOut, eStdErr} - liMessage(info, msg, arg, doAbort) + liMessage(conf, info, msg, arg, doAbort) -proc globalError*(info: TLineInfo, msg: TMsgKind, arg = "") = - liMessage(info, msg, arg, doRaise) +proc globalError*(conf: ConfigRef; info: TLineInfo, msg: TMsgKind, arg = "") = + liMessage(conf, info, msg, arg, doRaise) -proc globalError*(info: TLineInfo, arg: string) = - liMessage(info, errGenerated, arg, doRaise) +proc globalError*(conf: ConfigRef; info: TLineInfo, arg: string) = + liMessage(conf, info, errGenerated, arg, doRaise) -proc localError*(info: TLineInfo, msg: TMsgKind, arg = "") = - liMessage(info, msg, arg, doNothing) +proc localError*(conf: ConfigRef; info: TLineInfo, msg: TMsgKind, arg = "") = + liMessage(conf, info, msg, arg, doNothing) -proc localError*(info: TLineInfo, arg: string) = - liMessage(info, errGenerated, arg, doNothing) +proc localError*(conf: ConfigRef; info: TLineInfo, arg: string) = + liMessage(conf, info, errGenerated, arg, doNothing) -proc localError*(info: TLineInfo, format: string, params: openarray[string]) = - localError(info, format % params) +proc localError*(conf: ConfigRef; info: TLineInfo, format: string, params: openarray[string]) = + localError(conf, info, format % params) -proc message*(info: TLineInfo, msg: TMsgKind, arg = "") = - liMessage(info, msg, arg, doNothing) +proc message*(conf: ConfigRef; info: TLineInfo, msg: TMsgKind, arg = "") = + liMessage(conf, info, msg, arg, doNothing) -proc internalError*(info: TLineInfo, errMsg: string) = +proc internalError*(conf: ConfigRef; info: TLineInfo, errMsg: string) = if gCmd == cmdIdeTools and structuredErrorHook.isNil: return - writeContext(info) - liMessage(info, errInternal, errMsg, doAbort) + writeContext(conf, info) + liMessage(conf, info, errInternal, errMsg, doAbort) -proc internalError*(errMsg: string) = +proc internalError*(conf: ConfigRef; errMsg: string) = if gCmd == cmdIdeTools and structuredErrorHook.isNil: return - writeContext(unknownLineInfo()) - rawMessage(errInternal, errMsg) + writeContext(conf, unknownLineInfo()) + rawMessage(conf, errInternal, errMsg) -template assertNotNil*(e): untyped = - if e == nil: internalError($instantiationInfo()) +template assertNotNil*(conf, e): untyped = + if e == nil: internalError(conf, $instantiationInfo()) e -template internalAssert*(e: bool) = - if not e: internalError($instantiationInfo()) +template internalAssert*(conf, e: bool) = + if not e: internalError(conf, $instantiationInfo()) proc addSourceLine*(fileIdx: FileIndex, line: string) = fileInfos[fileIdx.int32].lines.add line.rope @@ -1111,14 +613,14 @@ proc sourceLine*(i: TLineInfo): Rope = addSourceLine i.fileIndex, line.string except IOError: discard - internalAssert i.fileIndex.int32 < fileInfos.len + assert i.fileIndex.int32 < fileInfos.len # can happen if the error points to EOF: if i.line.int > fileInfos[i.fileIndex.int32].lines.len: return nil result = fileInfos[i.fileIndex.int32].lines[i.line.int-1] proc quotedFilename*(i: TLineInfo): Rope = - internalAssert i.fileIndex.int32 >= 0 + assert i.fileIndex.int32 >= 0 if optExcessiveStackTrace in gGlobalOptions: result = fileInfos[i.fileIndex.int32].quotedFullName else: @@ -1127,24 +629,24 @@ proc quotedFilename*(i: TLineInfo): Rope = ropes.errorHandler = proc (err: RopesError, msg: string, useWarning: bool) = case err of rInvalidFormatStr: - internalError("ropes: invalid format string: " & msg) + internalError(newConfigRef(), "ropes: invalid format string: " & msg) of rCannotOpenFile: - rawMessage(if useWarning: warnCannotOpenFile else: errCannotOpenFile, msg) + rawMessage(newConfigRef(), if useWarning: warnCannotOpenFile else: errCannotOpenFile, msg) -proc listWarnings*() = +proc listWarnings*(conf: ConfigRef) = msgWriteln("Warnings:") for warn in warnMin..warnMax: msgWriteln(" [$1] $2" % [ - if warn in gNotes: "x" else: " ", - msgs.WarningsToStr[ord(warn) - ord(warnMin)] + if warn in conf.notes: "x" else: " ", + configuration.WarningsToStr[ord(warn) - ord(warnMin)] ]) -proc listHints*() = +proc listHints*(conf: ConfigRef) = msgWriteln("Hints:") for hint in hintMin..hintMax: msgWriteln(" [$1] $2" % [ - if hint in gNotes: "x" else: " ", - msgs.HintsToStr[ord(hint) - ord(hintMin)] + if hint in conf.notes: "x" else: " ", + configuration.HintsToStr[ord(hint) - ord(hintMin)] ]) # enable colors by default on terminals diff --git a/compiler/nim.nim b/compiler/nim.nim index 280782330..b39eefd65 100644 --- a/compiler/nim.nim +++ b/compiler/nim.nim @@ -39,7 +39,7 @@ proc prependCurDir(f: string): string = proc handleCmdLine(cache: IdentCache; config: ConfigRef) = if paramCount() == 0: - writeCommandLineUsage() + writeCommandLineUsage(config.helpWritten) else: # Process command line arguments: processCmdLine(passCmd1, "", config) diff --git a/compiler/nimblecmd.nim b/compiler/nimblecmd.nim index 0f9e03352..da83e9499 100644 --- a/compiler/nimblecmd.nim +++ b/compiler/nimblecmd.nim @@ -9,9 +9,10 @@ ## Implements some helper procs for Nimble (Nim's package manager) support. -import parseutils, strutils, strtabs, os, options, msgs, sequtils +import parseutils, strutils, strtabs, os, options, msgs, sequtils, + configuration -proc addPath*(path: string, info: TLineInfo) = +proc addPath*(conf: ConfigRef; path: string, info: TLineInfo) = if not options.searchPaths.contains(path): options.searchPaths.insert(path, 0) @@ -84,7 +85,7 @@ proc getPathVersion*(p: string): tuple[name, version: string] = result.name = p[0 .. sepIdx - 1] result.version = p.substr(sepIdx + 1) -proc addPackage(packages: StringTableRef, p: string; info: TLineInfo) = +proc addPackage(conf: ConfigRef; packages: StringTableRef, p: string; info: TLineInfo) = let (name, ver) = getPathVersion(p) if isValidVersion(ver): let version = newVersion(ver) @@ -92,14 +93,14 @@ proc addPackage(packages: StringTableRef, p: string; info: TLineInfo) = (not packages.hasKey(name)): packages[name] = $version else: - localError(info, "invalid package name: " & p) + localError(conf, info, "invalid package name: " & p) iterator chosen(packages: StringTableRef): string = for key, val in pairs(packages): let res = if val.len == 0: key else: key & '-' & val yield res -proc addNimblePath(p: string, info: TLineInfo) = +proc addNimblePath(conf: ConfigRef; p: string, info: TLineInfo) = var path = p let nimbleLinks = toSeq(walkPattern(p / "*.nimble-link")) if nimbleLinks.len > 0: @@ -112,22 +113,22 @@ proc addNimblePath(p: string, info: TLineInfo) = path = p / path if not contains(options.searchPaths, path): - message(info, hintPath, path) + message(conf, info, hintPath, path) options.lazyPaths.insert(path, 0) -proc addPathRec(dir: string, info: TLineInfo) = +proc addPathRec(conf: ConfigRef; dir: string, info: TLineInfo) = var packages = newStringTable(modeStyleInsensitive) var pos = dir.len-1 if dir[pos] in {DirSep, AltSep}: inc(pos) for k,p in os.walkDir(dir): if k == pcDir and p[pos] != '.': - addPackage(packages, p, info) + addPackage(conf, packages, p, info) for p in packages.chosen: - addNimblePath(p, info) + addNimblePath(conf, p, info) -proc nimblePath*(path: string, info: TLineInfo) = - addPathRec(path, info) - addNimblePath(path, info) +proc nimblePath*(conf: ConfigRef; path: string, info: TLineInfo) = + addPathRec(conf, path, info) + addNimblePath(conf, path, info) when isMainModule: proc v(s: string): Version = s.newVersion diff --git a/compiler/nimconf.nim b/compiler/nimconf.nim index dc8d082b3..9f5031d3b 100644 --- a/compiler/nimconf.nim +++ b/compiler/nimconf.nim @@ -11,7 +11,7 @@ import llstream, nversion, commands, os, strutils, msgs, platform, condsyms, lexer, - options, idents, wordrecg, strtabs + options, idents, wordrecg, strtabs, configuration # ---------------- configuration file parser ----------------------------- # we use Nim's scanner here to save space and work @@ -27,12 +27,12 @@ proc parseAtom(L: var TLexer, tok: var TToken; config: ConfigRef): bool = ppGetTok(L, tok) result = parseExpr(L, tok, config) if tok.tokType == tkParRi: ppGetTok(L, tok) - else: lexMessage(L, errTokenExpected, "\')\'") + else: lexMessage(L, errGenerated, "expected closing ')'") elif tok.ident.id == ord(wNot): ppGetTok(L, tok) result = not parseAtom(L, tok, config) else: - result = isDefined(tok.ident) + result = isDefined(config, tok.ident.s) ppGetTok(L, tok) proc parseAndExpr(L: var TLexer, tok: var TToken; config: ConfigRef): bool = @@ -53,12 +53,12 @@ proc evalppIf(L: var TLexer, tok: var TToken; config: ConfigRef): bool = ppGetTok(L, tok) # skip 'if' or 'elif' result = parseExpr(L, tok, config) if tok.tokType == tkColon: ppGetTok(L, tok) - else: lexMessage(L, errTokenExpected, "\':\'") + else: lexMessage(L, errGenerated, "expected ':'") -var condStack: seq[bool] = @[] +#var condStack: seq[bool] = @[] -proc doEnd(L: var TLexer, tok: var TToken) = - if high(condStack) < 0: lexMessage(L, errTokenExpected, "@if") +proc doEnd(L: var TLexer, tok: var TToken; condStack: var seq[bool]) = + if high(condStack) < 0: lexMessage(L, errGenerated, "expected @if") ppGetTok(L, tok) # skip 'end' setLen(condStack, high(condStack)) @@ -66,20 +66,22 @@ type TJumpDest = enum jdEndif, jdElseEndif -proc jumpToDirective(L: var TLexer, tok: var TToken, dest: TJumpDest; config: ConfigRef) -proc doElse(L: var TLexer, tok: var TToken; config: ConfigRef) = - if high(condStack) < 0: lexMessage(L, errTokenExpected, "@if") +proc jumpToDirective(L: var TLexer, tok: var TToken, dest: TJumpDest; config: ConfigRef; + condStack: var seq[bool]) +proc doElse(L: var TLexer, tok: var TToken; config: ConfigRef; condStack: var seq[bool]) = + if high(condStack) < 0: lexMessage(L, errGenerated, "expected @if") ppGetTok(L, tok) if tok.tokType == tkColon: ppGetTok(L, tok) - if condStack[high(condStack)]: jumpToDirective(L, tok, jdEndif, config) + if condStack[high(condStack)]: jumpToDirective(L, tok, jdEndif, config, condStack) -proc doElif(L: var TLexer, tok: var TToken; config: ConfigRef) = - if high(condStack) < 0: lexMessage(L, errTokenExpected, "@if") +proc doElif(L: var TLexer, tok: var TToken; config: ConfigRef; condStack: var seq[bool]) = + if high(condStack) < 0: lexMessage(L, errGenerated, "expected @if") var res = evalppIf(L, tok, config) - if condStack[high(condStack)] or not res: jumpToDirective(L, tok, jdElseEndif, config) + if condStack[high(condStack)] or not res: jumpToDirective(L, tok, jdElseEndif, config, condStack) else: condStack[high(condStack)] = true -proc jumpToDirective(L: var TLexer, tok: var TToken, dest: TJumpDest; config: ConfigRef) = +proc jumpToDirective(L: var TLexer, tok: var TToken, dest: TJumpDest; config: ConfigRef; + condStack: var seq[bool]) = var nestedIfs = 0 while true: if tok.ident != nil and tok.ident.s == "@": @@ -89,36 +91,36 @@ proc jumpToDirective(L: var TLexer, tok: var TToken, dest: TJumpDest; config: Co inc(nestedIfs) of wElse: if dest == jdElseEndif and nestedIfs == 0: - doElse(L, tok, config) + doElse(L, tok, config, condStack) break of wElif: if dest == jdElseEndif and nestedIfs == 0: - doElif(L, tok, config) + doElif(L, tok, config, condStack) break of wEnd: if nestedIfs == 0: - doEnd(L, tok) + doEnd(L, tok, condStack) break if nestedIfs > 0: dec(nestedIfs) else: discard ppGetTok(L, tok) elif tok.tokType == tkEof: - lexMessage(L, errTokenExpected, "@end") + lexMessage(L, errGenerated, "expected @end") else: ppGetTok(L, tok) -proc parseDirective(L: var TLexer, tok: var TToken; config: ConfigRef) = +proc parseDirective(L: var TLexer, tok: var TToken; config: ConfigRef; condStack: var seq[bool]) = ppGetTok(L, tok) # skip @ case whichKeyword(tok.ident) of wIf: setLen(condStack, len(condStack) + 1) let res = evalppIf(L, tok, config) condStack[high(condStack)] = res - if not res: jumpToDirective(L, tok, jdElseEndif, config) - of wElif: doElif(L, tok, config) - of wElse: doElse(L, tok, config) - of wEnd: doEnd(L, tok) + if not res: jumpToDirective(L, tok, jdElseEndif, config, condStack) + of wElif: doElif(L, tok, config, condStack) + of wElse: doElse(L, tok, config, condStack) + of wEnd: doEnd(L, tok, condStack) of wWrite: ppGetTok(L, tok) msgs.msgWriteln(strtabs.`%`(tokToStr(tok), options.gConfigVars, @@ -144,53 +146,55 @@ proc parseDirective(L: var TLexer, tok: var TToken; config: ConfigRef) = ppGetTok(L, tok) os.putEnv(key, os.getEnv(key) & tokToStr(tok)) ppGetTok(L, tok) - else: lexMessage(L, errInvalidDirectiveX, tokToStr(tok)) + else: + lexMessage(L, errGenerated, "invalid directive: '$1'" % tokToStr(tok)) -proc confTok(L: var TLexer, tok: var TToken; config: ConfigRef) = +proc confTok(L: var TLexer, tok: var TToken; config: ConfigRef; condStack: var seq[bool]) = ppGetTok(L, tok) while tok.ident != nil and tok.ident.s == "@": - parseDirective(L, tok, config) # else: give the token to the parser + parseDirective(L, tok, config, condStack) # else: give the token to the parser proc checkSymbol(L: TLexer, tok: TToken) = if tok.tokType notin {tkSymbol..tkInt64Lit, tkStrLit..tkTripleStrLit}: - lexMessage(L, errIdentifierExpected, tokToStr(tok)) + lexMessage(L, errGenerated, "expected identifier, but got: " & tokToStr(tok)) -proc parseAssignment(L: var TLexer, tok: var TToken; config: ConfigRef) = +proc parseAssignment(L: var TLexer, tok: var TToken; + config: ConfigRef; condStack: var seq[bool]) = if tok.ident.s == "-" or tok.ident.s == "--": - confTok(L, tok, config) # skip unnecessary prefix + confTok(L, tok, config, condStack) # skip unnecessary prefix var info = getLineInfo(L, tok) # save for later in case of an error checkSymbol(L, tok) var s = tokToStr(tok) - confTok(L, tok, config) # skip symbol + confTok(L, tok, config, condStack) # skip symbol var val = "" while tok.tokType == tkDot: add(s, '.') - confTok(L, tok, config) + confTok(L, tok, config, condStack) checkSymbol(L, tok) add(s, tokToStr(tok)) - confTok(L, tok, config) + confTok(L, tok, config, condStack) if tok.tokType == tkBracketLe: # BUGFIX: val, not s! # BUGFIX: do not copy '['! - confTok(L, tok, config) + confTok(L, tok, config, condStack) checkSymbol(L, tok) add(val, tokToStr(tok)) - confTok(L, tok, config) - if tok.tokType == tkBracketRi: confTok(L, tok, config) - else: lexMessage(L, errTokenExpected, "']'") + confTok(L, tok, config, condStack) + if tok.tokType == tkBracketRi: confTok(L, tok, config, condStack) + else: lexMessage(L, errGenerated, "expected closing ']'") add(val, ']') let percent = tok.ident != nil and tok.ident.s == "%=" if tok.tokType in {tkColon, tkEquals} or percent: if len(val) > 0: add(val, ':') - confTok(L, tok, config) # skip ':' or '=' or '%' + confTok(L, tok, config, condStack) # skip ':' or '=' or '%' checkSymbol(L, tok) add(val, tokToStr(tok)) - confTok(L, tok, config) # skip symbol + confTok(L, tok, config, condStack) # skip symbol while tok.ident != nil and tok.ident.s == "&": - confTok(L, tok, config) + confTok(L, tok, config, condStack) checkSymbol(L, tok) add(val, tokToStr(tok)) - confTok(L, tok, config) + confTok(L, tok, config, condStack) if percent: processSwitch(s, strtabs.`%`(val, options.gConfigVars, {useEnvironment, useEmpty}), passPP, info, config) @@ -207,34 +211,35 @@ proc readConfigFile(filename: string; cache: IdentCache; config: ConfigRef) = initToken(tok) openLexer(L, filename, stream, cache, config) tok.tokType = tkEof # to avoid a pointless warning - confTok(L, tok, config) # read in the first token - while tok.tokType != tkEof: parseAssignment(L, tok, config) - if len(condStack) > 0: lexMessage(L, errTokenExpected, "@end") + var condStack: seq[bool] = @[] + confTok(L, tok, config, condStack) # read in the first token + while tok.tokType != tkEof: parseAssignment(L, tok, config, condStack) + if len(condStack) > 0: lexMessage(L, errGenerated, "expected @end") closeLexer(L) - rawMessage(hintConf, filename) + rawMessage(config, hintConf, filename) proc getUserConfigPath(filename: string): string = result = joinPath(getConfigDir(), filename) -proc getSystemConfigPath(filename: string): string = +proc getSystemConfigPath(conf: ConfigRef; filename: string): string = # try standard configuration file (installation did not distribute files # the UNIX way) - let p = getPrefixDir() + let p = getPrefixDir(conf) result = joinPath([p, "config", filename]) when defined(unix): if not existsFile(result): result = joinPath([p, "etc", filename]) if not existsFile(result): result = "/etc/" & filename proc loadConfigs*(cfg: string; cache: IdentCache; config: ConfigRef = nil) = - setDefaultLibpath() + setDefaultLibpath(config) if optSkipConfigFile notin gGlobalOptions: - readConfigFile(getSystemConfigPath(cfg), cache, config) + readConfigFile(getSystemConfigPath(config, cfg), cache, config) if optSkipUserConfigFile notin gGlobalOptions: readConfigFile(getUserConfigPath(cfg), cache, config) - var pd = if gProjectPath.len > 0: gProjectPath else: getCurrentDir() + let pd = if gProjectPath.len > 0: gProjectPath else: getCurrentDir() if optSkipParentConfigFiles notin gGlobalOptions: for dir in parentDirs(pd, fromRoot=true, inclusive=false): readConfigFile(dir / cfg, cache, config) diff --git a/compiler/options.nim b/compiler/options.nim index fe8be5e0c..ec87ed805 100644 --- a/compiler/options.nim +++ b/compiler/options.nim @@ -8,7 +8,7 @@ # import - os, strutils, strtabs, osproc, sets + os, strutils, strtabs, osproc, sets, configuration, platform const hasTinyCBackend* = defined(tinyc) @@ -118,16 +118,71 @@ type features*: set[Feature] arguments*: string ## the arguments to be passed to the program that ## should be run + helpWritten*: bool + enableNotes*: TNoteKinds + disableNotes*: TNoteKinds + foreignPackageNotes*: TNoteKinds + notes*: TNoteKinds + mainPackageNotes*: TNoteKinds + errorCounter*: int + hintCounter*: int + warnCounter*: int + errorMax*: int + symbols*: StringTableRef ## We need to use a StringTableRef here as defined + ## symbols are always guaranteed to be style + ## insensitive. Otherwise hell would break lose. const oldExperimentalFeatures* = {implicitDeref, dotOperators, callOperator, parallel} proc newConfigRef*(): ConfigRef = result = ConfigRef(cppDefines: initSet[string](), - headerFile: "", features: {}) + headerFile: "", features: {}, foreignPackageNotes: {hintProcessing, warnUnknownMagic, + hintQuitCalled, hintExecuting}, + notes: NotesVerbosity[1], mainPackageNotes: NotesVerbosity[1], + symbols: newStringTable(modeStyleInsensitive)) proc cppDefine*(c: ConfigRef; define: string) = c.cppDefines.incl define +proc isDefined*(conf: ConfigRef; symbol: string): bool = + if conf.symbols.hasKey(symbol): + result = conf.symbols[symbol] != "false" + elif cmpIgnoreStyle(symbol, CPU[targetCPU].name) == 0: + result = true + elif cmpIgnoreStyle(symbol, platform.OS[targetOS].name) == 0: + result = true + else: + case symbol.normalize + of "x86": result = targetCPU == cpuI386 + of "itanium": result = targetCPU == cpuIa64 + of "x8664": result = targetCPU == cpuAmd64 + of "posix", "unix": + result = targetOS in {osLinux, osMorphos, osSkyos, osIrix, osPalmos, + osQnx, osAtari, osAix, + osHaiku, osVxWorks, osSolaris, osNetbsd, + osFreebsd, osOpenbsd, osDragonfly, osMacosx, + osAndroid} + of "linux": + result = targetOS in {osLinux, osAndroid} + of "bsd": + result = targetOS in {osNetbsd, osFreebsd, osOpenbsd, osDragonfly} + of "emulatedthreadvars": + result = platform.OS[targetOS].props.contains(ospLacksThreadVars) + of "msdos": result = targetOS == osDos + of "mswindows", "win32": result = targetOS == osWindows + of "macintosh": result = targetOS in {osMacos, osMacosx} + of "sunos": result = targetOS == osSolaris + of "littleendian": result = CPU[targetCPU].endian == platform.littleEndian + of "bigendian": result = CPU[targetCPU].endian == platform.bigEndian + of "cpu8": result = CPU[targetCPU].bit == 8 + of "cpu16": result = CPU[targetCPU].bit == 16 + of "cpu32": result = CPU[targetCPU].bit == 32 + of "cpu64": result = CPU[targetCPU].bit == 64 + of "nimrawsetjmp": + result = targetOS in {osSolaris, osNetbsd, osFreebsd, osOpenbsd, + osDragonfly, osMacosx} + else: discard + var gIdeCmd*: IdeCmd gOldNewlines*: bool @@ -226,20 +281,20 @@ proc mainCommandArg*: string = else: result = gProjectName -proc existsConfigVar*(key: string): bool = +proc existsConfigVar*(conf: ConfigRef; key: string): bool = result = hasKey(gConfigVars, key) -proc getConfigVar*(key: string): string = +proc getConfigVar*(conf: ConfigRef; key: string): string = result = gConfigVars.getOrDefault key -proc setConfigVar*(key, val: string) = +proc setConfigVar*(conf: ConfigRef; key, val: string) = gConfigVars[key] = val -proc getOutFile*(filename, ext: string): string = +proc getOutFile*(conf: ConfigRef; filename, ext: string): string = if options.outFile != "": result = options.outFile else: result = changeFileExt(filename, ext) -proc getPrefixDir*(): string = +proc getPrefixDir*(conf: ConfigRef): string = ## Gets the prefix dir, usually the parent directory where the binary resides. ## ## This is overridden by some tools (namely nimsuggest) via the ``gPrefixDir`` @@ -248,11 +303,11 @@ proc getPrefixDir*(): string = else: result = splitPath(getAppDir()).head -proc setDefaultLibpath*() = +proc setDefaultLibpath*(conf: ConfigRef) = # set default value (can be overwritten): if libpath == "": # choose default libpath: - var prefix = getPrefixDir() + var prefix = getPrefixDir(conf) when defined(posix): if prefix == "/usr": libpath = "/usr/lib/nim" elif prefix == "/usr/local": libpath = "/usr/local/lib/nim" @@ -263,12 +318,12 @@ proc setDefaultLibpath*() = # modules and make use of them. let realNimPath = findExe("nim") # Find out if $nim/../../lib/system.nim exists. - let parentNimLibPath = realNimPath.parentDir().parentDir() / "lib" + let parentNimLibPath = realNimPath.parentDir.parentDir / "lib" if not fileExists(libpath / "system.nim") and fileExists(parentNimlibPath / "system.nim"): libpath = parentNimLibPath -proc canonicalizePath*(path: string): string = +proc canonicalizePath*(conf: ConfigRef; path: string): string = # on Windows, 'expandFilename' calls getFullPathName which doesn't do # case corrections, so we have to use this convoluted way of retrieving # the true filename (see tests/modules and Nimble uses 'import Uri' instead @@ -280,12 +335,12 @@ proc canonicalizePath*(path: string): string = else: result = path.expandFilename -proc shortenDir*(dir: string): string = +proc shortenDir*(conf: ConfigRef; dir: string): string = ## returns the interesting part of a dir var prefix = gProjectPath & DirSep if startsWith(dir, prefix): return substr(dir, len(prefix)) - prefix = getPrefixDir() & DirSep + prefix = getPrefixDir(conf) & DirSep if startsWith(dir, prefix): return substr(dir, len(prefix)) result = dir @@ -296,42 +351,42 @@ proc removeTrailingDirSep*(path: string): string = else: result = path -proc disableNimblePath*() = +proc disableNimblePath*(conf: ConfigRef) = gNoNimblePath = true lazyPaths.setLen(0) include packagehandling -proc getNimcacheDir*: string = - result = if nimcacheDir.len > 0: nimcacheDir else: gProjectPath.shortenDir / +proc getNimcacheDir*(conf: ConfigRef): string = + result = if nimcacheDir.len > 0: nimcacheDir else: shortenDir(conf, gProjectPath) / genSubDir -proc pathSubs*(p, config: string): string = +proc pathSubs*(conf: ConfigRef; p, config: string): string = let home = removeTrailingDirSep(os.getHomeDir()) result = unixToNativePath(p % [ - "nim", getPrefixDir(), + "nim", getPrefixDir(conf), "lib", libpath, "home", home, "config", config, "projectname", options.gProjectName, "projectpath", options.gProjectPath, "projectdir", options.gProjectPath, - "nimcache", getNimcacheDir()]) + "nimcache", getNimcacheDir(conf)]) if "~/" in result: result = result.replace("~/", home & '/') -proc toGeneratedFile*(path, ext: string): string = +proc toGeneratedFile*(conf: ConfigRef; path, ext: string): string = ## converts "/home/a/mymodule.nim", "rod" to "/home/a/nimcache/mymodule.rod" var (head, tail) = splitPath(path) #if len(head) > 0: head = shortenDir(head & dirSep) - result = joinPath([getNimcacheDir(), changeFileExt(tail, ext)]) + result = joinPath([getNimcacheDir(conf), changeFileExt(tail, ext)]) #echo "toGeneratedFile(", path, ", ", ext, ") = ", result -proc completeGeneratedFilePath*(f: string, createSubDir: bool = true): string = +proc completeGeneratedFilePath*(conf: ConfigRef; f: string, createSubDir: bool = true): string = var (head, tail) = splitPath(f) #if len(head) > 0: head = removeTrailingDirSep(shortenDir(head & dirSep)) - var subdir = getNimcacheDir() # / head + var subdir = getNimcacheDir(conf) # / head if createSubDir: try: createDir(subdir) @@ -341,14 +396,14 @@ proc completeGeneratedFilePath*(f: string, createSubDir: bool = true): string = result = joinPath(subdir, tail) #echo "completeGeneratedFilePath(", f, ") = ", result -proc rawFindFile(f: string): string = +proc rawFindFile(conf: ConfigRef; f: string): string = for it in searchPaths: result = joinPath(it, f) if existsFile(result): - return result.canonicalizePath + return canonicalizePath(conf, result) result = "" -proc rawFindFile2(f: string): string = +proc rawFindFile2(conf: ConfigRef; f: string): string = for i, it in lazyPaths: result = joinPath(it, f) if existsFile(result): @@ -356,30 +411,30 @@ proc rawFindFile2(f: string): string = for j in countDown(i,1): swap(lazyPaths[j], lazyPaths[j-1]) - return result.canonicalizePath + return canonicalizePath(conf, result) result = "" -template patchModule() {.dirty.} = +template patchModule(conf: ConfigRef) {.dirty.} = if result.len > 0 and gModuleOverrides.len > 0: let key = getPackageName(result) & "_" & splitFile(result).name if gModuleOverrides.hasKey(key): let ov = gModuleOverrides[key] if ov.len > 0: result = ov -proc findFile*(f: string): string {.procvar.} = +proc findFile*(conf: ConfigRef; f: string): string {.procvar.} = if f.isAbsolute: result = if f.existsFile: f else: "" else: - result = f.rawFindFile + result = rawFindFile(conf, f) if result.len == 0: - result = f.toLowerAscii.rawFindFile + result = rawFindFile(conf, f.toLowerAscii) if result.len == 0: - result = f.rawFindFile2 + result = rawFindFile2(conf, f) if result.len == 0: - result = f.toLowerAscii.rawFindFile2 - patchModule() + result = rawFindFile2(conf, f.toLowerAscii) + patchModule(conf) -proc findModule*(modulename, currentModule: string): string = +proc findModule*(conf: ConfigRef; modulename, currentModule: string): string = # returns path to module when defined(nimfix): # '.nimfix' modules are preferred over '.nim' modules so that specialized @@ -389,16 +444,16 @@ proc findModule*(modulename, currentModule: string): string = let currentPath = currentModule.splitFile.dir result = currentPath / m if not existsFile(result): - result = findFile(m) + result = findFile(conf, m) if existsFile(result): return result let m = addFileExt(modulename, NimExt) let currentPath = currentModule.splitFile.dir result = currentPath / m if not existsFile(result): - result = findFile(m) - patchModule() + result = findFile(conf, m) + patchModule(conf) -proc findProjectNimFile*(pkg: string): string = +proc findProjectNimFile*(conf: ConfigRef; pkg: string): string = const extensions = [".nims", ".cfg", ".nimcfg", ".nimble"] var candidates: seq[string] = @[] for k, f in os.walkDir(pkg, relative=true): @@ -423,10 +478,10 @@ proc canonDynlibName(s: string): string = else: result = s.substr(start) -proc inclDynlibOverride*(lib: string) = +proc inclDynlibOverride*(conf: ConfigRef; lib: string) = gDllOverrides[lib.canonDynlibName] = "true" -proc isDynlibOverride*(lib: string): bool = +proc isDynlibOverride*(conf: ConfigRef; lib: string): bool = result = gDynlibOverrideAll or gDllOverrides.hasKey(lib.canonDynlibName) proc binaryStrSearch*(x: openArray[string], y: string): int = diff --git a/compiler/parser.nim b/compiler/parser.nim index 14683e307..7a23ed6a9 100644 --- a/compiler/parser.nim +++ b/compiler/parser.nim @@ -27,7 +27,7 @@ when isMainModule: outp.close import - llstream, lexer, idents, strutils, ast, astalgo, msgs, options + llstream, lexer, idents, strutils, ast, astalgo, msgs, options, configuration type TParser* = object # A TParser object represents a file that @@ -97,7 +97,7 @@ proc openParser*(p: var TParser, fileIdx: FileIndex, inputStream: PLLStream, proc openParser*(p: var TParser, filename: string, inputStream: PLLStream, cache: IdentCache; config: ConfigRef; strongSpaces=false) = - openParser(p, filename.fileInfoIdx, inputStream, cache, config, strongSpaces) + openParser(p, fileInfoIdx(config, filename), inputStream, cache, config, strongSpaces) proc closeParser(p: var TParser) = ## Close a parser, freeing up its resources. @@ -107,9 +107,13 @@ proc parMessage(p: TParser, msg: TMsgKind, arg = "") = ## Produce and emit the parser message `arg` to output. lexMessageTok(p.lex, msg, p.tok, arg) -proc parMessage(p: TParser, msg: TMsgKind, tok: TToken) = +proc parMessage(p: TParser, msg: string, tok: TToken) = ## Produce and emit a parser message to output about the token `tok` - parMessage(p, msg, prettyTok(tok)) + parMessage(p, errGenerated, msg % prettyTok(tok)) + +proc parMessage(p: TParser, arg: string) = + ## Produce and emit the parser message `arg` to output. + lexMessageTok(p.lex, errGenerated, p.tok, arg) template withInd(p, body: untyped) = let oldInd = p.currInd @@ -142,6 +146,12 @@ proc skipComment(p: var TParser, node: PNode) = proc flexComment(p: var TParser, node: PNode) = if p.tok.indent < 0 or realInd(p): rawSkipComment(p, node) +const + errInvalidIndentation = "invalid indentation" + errIdentifierExpected = "identifier expected, but got '$1'" + errExprExpected = "expression expected, but found '$1'" + errTokenExpected = "'$1' expected" + proc skipInd(p: var TParser) = if p.tok.indent >= 0: if not realInd(p): parMessage(p, errInvalidIndentation) @@ -160,11 +170,11 @@ proc getTokNoInd(p: var TParser) = proc expectIdentOrKeyw(p: TParser) = if p.tok.tokType != tkSymbol and not isKeyword(p.tok.tokType): - lexMessage(p.lex, errIdentifierExpected, prettyTok(p.tok)) + lexMessage(p.lex, errGenerated, errIdentifierExpected % prettyTok(p.tok)) proc expectIdent(p: TParser) = if p.tok.tokType != tkSymbol: - lexMessage(p.lex, errIdentifierExpected, prettyTok(p.tok)) + lexMessage(p.lex, errGenerated, errIdentifierExpected % prettyTok(p.tok)) proc eat(p: var TParser, tokType: TTokType) = ## Move the parser to the next token if the current token is of type @@ -172,7 +182,8 @@ proc eat(p: var TParser, tokType: TTokType) = if p.tok.tokType == tokType: getTok(p) else: - lexMessageTok(p.lex, errTokenExpected, p.tok, TokTypeToStr[tokType]) + lexMessage(p.lex, errGenerated, + "expected " & TokTypeToStr[tokType] & ", but got: " & prettyTok(p.tok)) proc parLineInfo(p: TParser): TLineInfo = ## Retrieve the line information associated with the parser's current state. @@ -878,7 +889,7 @@ proc parsePragma(p: var TParser): PNode = skipComment(p, a) optPar(p) if p.tok.tokType in {tkCurlyDotRi, tkCurlyRi}: getTok(p) - else: parMessage(p, errTokenExpected, ".}") + else: parMessage(p, "expected '.}'") dec p.inPragma proc identVis(p: var TParser; allowDot=false): PNode = @@ -939,7 +950,7 @@ proc parseIdentColonEquals(p: var TParser, flags: TDeclaredIdentFlags): PNode = else: addSon(result, newNodeP(nkEmpty, p)) if p.tok.tokType != tkEquals and withBothOptional notin flags: - parMessage(p, errColonOrEqualsExpected, p.tok) + parMessage(p, "':' or '=' expected, but got '$1'", p.tok) if p.tok.tokType == tkEquals: getTok(p) optInd(p, result) @@ -1012,7 +1023,7 @@ proc parseParamList(p: var TParser, retColon = true): PNode = parMessage(p, errGenerated, "the syntax is 'parameter: var T', not 'var parameter: T'") break else: - parMessage(p, errTokenExpected, ")") + parMessage(p, "expected closing ')'") break addSon(result, a) if p.tok.tokType notin {tkComma, tkSemiColon}: break @@ -1173,7 +1184,7 @@ proc primary(p: var TParser, mode: TPrimaryMode): PNode = if mode == pmTypeDef: result = parseTypeClass(p) else: - parMessage(p, errInvalidToken, p.tok) + parMessage(p, "the 'concept' keyword is only valid in 'type' sections") of tkStatic: let info = parLineInfo(p) getTokNoInd(p) @@ -1283,7 +1294,7 @@ proc postExprBlocks(p: var TParser, x: PNode): PNode = if nextBlock.kind == nkElse: break else: if openingParams.kind != nkEmpty: - parMessage(p, errTokenExpected, ":") + parMessage(p, "expected ':'") proc parseExprStmt(p: var TParser): PNode = #| exprStmt = simpleExpr @@ -1518,7 +1529,7 @@ proc parseTry(p: var TParser; isExpr: bool): PNode = addSon(b, parseStmt(p)) addSon(result, b) if b.kind == nkFinally: break - if b == nil: parMessage(p, errTokenExpected, "except") + if b == nil: parMessage(p, "expected 'except'") proc parseExceptBlock(p: var TParser, kind: TNodeKind): PNode = #| exceptBlock = 'except' colcom stmt @@ -1573,7 +1584,7 @@ proc parseAsm(p: var TParser): PNode = of tkTripleStrLit: addSon(result, newStrNodeP(nkTripleStrLit, p.tok.literal, p)) else: - parMessage(p, errStringLiteralExpected) + parMessage(p, "the 'asm' statement takes a string literal") addSon(result, ast.emptyNode) return getTok(p) @@ -1752,7 +1763,7 @@ proc parseEnum(p: var TParser): PNode = p.tok.tokType == tkEof: break if result.len <= 1: - lexMessageTok(p.lex, errIdentifierExpected, p.tok, prettyTok(p.tok)) + parMessage(p, errIdentifierExpected, p.tok) proc parseObjectPart(p: var TParser): PNode proc parseObjectWhen(p: var TParser): PNode = @@ -2115,7 +2126,7 @@ proc parseStmt(p: var TParser): PNode = case p.tok.tokType of tkIf, tkWhile, tkCase, tkTry, tkFor, tkBlock, tkAsm, tkProc, tkFunc, tkIterator, tkMacro, tkType, tkConst, tkWhen, tkVar: - parMessage(p, errComplexStmtRequiresInd) + parMessage(p, "complex statement requires indentation") result = ast.emptyNode else: if p.inSemiStmtList > 0: diff --git a/compiler/scriptconfig.nim b/compiler/scriptconfig.nim index 6ef42f15e..0cd5d1be2 100644 --- a/compiler/scriptconfig.nim +++ b/compiler/scriptconfig.nim @@ -131,10 +131,10 @@ proc setupVM*(module: PSym; cache: IdentCache; scriptName: string; processSwitch(a.getString 0, a.getString 1, passPP, module.info, config) cbconf hintImpl: processSpecificNote(a.getString 0, wHint, passPP, module.info, - a.getString 1) + a.getString 1, config) cbconf warningImpl: processSpecificNote(a.getString 0, wWarning, passPP, module.info, - a.getString 1) + a.getString 1, config) cbconf patchFile: let key = a.getString(0) & "_" & a.getString(1) var val = a.getString(2).addFileExt(NimExt) -- cgit 1.4.1-2-gfad0 From 02b78d3f94225ab1b204191d0fb199125787f31d Mon Sep 17 00:00:00 2001 From: Araq Date: Mon, 14 May 2018 21:38:18 +0200 Subject: make tests green again --- compiler/parser.nim | 2 +- compiler/semcall.nim | 4 ++-- compiler/seminst.nim | 4 ++-- compiler/sempass2.nim | 2 +- compiler/semstmts.nim | 2 +- compiler/semtypes.nim | 8 ++++---- compiler/semtypinst.nim | 4 ++-- compiler/sigmatch.nim | 4 ++-- compiler/types.nim | 2 +- tests/controlflow/tstatret.nim | 2 +- tests/errmsgs/tinvalidinout.nim | 8 ++++---- tests/lexer/tunderscores.nim | 2 +- tests/misc/tunsignedmisc.nim | 2 +- tests/objvariant/temptycaseobj.nim | 2 +- tests/parser/tinvcolonlocation1.nim | 4 ++-- tests/parser/tinvcolonlocation2.nim | 4 ++-- tests/parser/tinvcolonlocation3.nim | 4 ++-- tests/parser/twhen_in_enum.nim | 2 +- tests/types/tparameterizedparent3.nim | 2 +- tests/types/tparameterizedparent4.nim | 2 +- 20 files changed, 33 insertions(+), 33 deletions(-) (limited to 'compiler/parser.nim') diff --git a/compiler/parser.nim b/compiler/parser.nim index 082dcd9ed..fbc57ebb6 100644 --- a/compiler/parser.nim +++ b/compiler/parser.nim @@ -183,7 +183,7 @@ proc eat(p: var TParser, tokType: TTokType) = getTok(p) else: lexMessage(p.lex, errGenerated, - "expected " & TokTypeToStr[tokType] & ", but got: " & prettyTok(p.tok)) + "expected: '" & TokTypeToStr[tokType] & "', but got: '" & prettyTok(p.tok) & "'") proc parLineInfo(p: TParser): TLineInfo = ## Retrieve the line information associated with the parser's current state. diff --git a/compiler/semcall.nim b/compiler/semcall.nim index b4d4cad7e..e9a31d3d6 100644 --- a/compiler/semcall.nim +++ b/compiler/semcall.nim @@ -507,8 +507,8 @@ proc explicitGenericInstantiation(c: PContext, n: PNode, s: PSym): PNode = # number of generic type parameters: if safeLen(s.ast.sons[genericParamsPos]) != n.len-1: let expected = safeLen(s.ast.sons[genericParamsPos]) - localError(c.config, n.info, errGenerated, "cannot instantiate: " & renderTree(n) & - "; got " & $(n.len-1) & " type(s) but expected " & $expected) + localError(c.config, n.info, errGenerated, "cannot instantiate: '" & renderTree(n) & + "'; got " & $(n.len-1) & " type(s) but expected " & $expected) return n result = explicitGenericSym(c, n, s) if result == nil: result = explicitGenericInstError(c, n) diff --git a/compiler/seminst.nim b/compiler/seminst.nim index 60c655bba..a5f0ca7d8 100644 --- a/compiler/seminst.nim +++ b/compiler/seminst.nim @@ -74,10 +74,10 @@ iterator instantiateGenericParamList(c: PContext, n: PNode, pt: TIdTable): PSym # later by semAsgn in return type inference scenario t = q.typ else: - localError(c.config, a.info, errCannotInstantiateX & s.name.s) + localError(c.config, a.info, errCannotInstantiateX % s.name.s) t = errorType(c) elif t.kind == tyGenericParam: - localError(c.config, a.info, errCannotInstantiateX & q.name.s) + localError(c.config, a.info, errCannotInstantiateX % q.name.s) t = errorType(c) elif t.kind == tyGenericInvocation: #t = instGenericContainer(c, a, t) diff --git a/compiler/sempass2.nim b/compiler/sempass2.nim index 543161524..b66d7d9f2 100644 --- a/compiler/sempass2.nim +++ b/compiler/sempass2.nim @@ -970,7 +970,7 @@ proc trackProc*(g: ModuleGraph; s: PSym, body: PNode) = when false: listGcUnsafety(s, onlyWarning=false, g.config) else: - localError(g.config, s.info, "'$1' has side effects" % s.name.s) + localError(g.config, s.info, "'$1' can have side effects" % s.name.s) if not t.gcUnsafe: s.typ.flags.incl tfGcSafe if not t.hasSideEffect and sfSideEffect notin s.flags: diff --git a/compiler/semstmts.nim b/compiler/semstmts.nim index 2d4b5ae77..3687e50e9 100644 --- a/compiler/semstmts.nim +++ b/compiler/semstmts.nim @@ -233,7 +233,7 @@ proc semCase(c: PContext, n: PNode): PNode = if covered == toCover(n.sons[0].typ): hasElse = true else: - localError(c.config, n.info, "not all cases covered") + localError(c.config, n.info, "not all cases are covered") closeScope(c) if isEmptyType(typ) or typ.kind in {tyNil, tyExpr} or not hasElse: for i in 1..n.len-1: discardCheck(c, n.sons[i].lastSon) diff --git a/compiler/semtypes.nim b/compiler/semtypes.nim index 52afce688..8b5c26f99 100644 --- a/compiler/semtypes.nim +++ b/compiler/semtypes.nim @@ -447,7 +447,7 @@ proc semTuple(c: PContext, n: PNode, prev: PType): PType = field.position = counter inc(counter) if containsOrIncl(check, field.name.id): - localError(c.config, a.sons[j].info, "attempt to redefine: " & field.name.s) + localError(c.config, a.sons[j].info, "attempt to redefine: '" & field.name.s & "'") else: addSon(result.n, newSymNode(field)) addSonSkipIntLit(result, typ) @@ -682,7 +682,7 @@ proc semRecordNodeAux(c: PContext, n: PNode, check: var IntSet, pos: var int, f.flags = f.flags + ({sfImportc, sfExportc} * fieldOwner.flags) inc(pos) if containsOrIncl(check, f.name.id): - localError(c.config, n.sons[i].info, "attempt to redefine: " & f.name.s) + localError(c.config, n.sons[i].info, "attempt to redefine: '" & f.name.s & "'") if a.kind == nkEmpty: addSon(father, newSymNode(f)) else: addSon(a, newSymNode(f)) styleCheckDef(f) @@ -692,7 +692,7 @@ proc semRecordNodeAux(c: PContext, n: PNode, check: var IntSet, pos: var int, # inherited from generic/partial specialized parent second check. # There is no branch validity check here if containsOrIncl(check, n.sym.name.id): - localError(c.config, n.info, "attempt to redefine: " & n.sym.name.s) + localError(c.config, n.info, "attempt to redefine: '" & n.sym.name.s & "'") addSon(father, n) of nkEmpty: discard else: illFormedAst(n, c.config) @@ -1058,7 +1058,7 @@ proc semProcTypeNode(c: PContext, n, genericParams: PNode, inc(counter) if def != nil and def.kind != nkEmpty: arg.ast = copyTree(def) if containsOrIncl(check, arg.name.id): - localError(c.config, a.sons[j].info, "attempt to redefine: " & arg.name.s) + localError(c.config, a.sons[j].info, "attempt to redefine: '" & arg.name.s & "'") addSon(result.n, newSymNode(arg)) rawAddSon(result, finalType) addParamOrResult(c, arg, kind) diff --git a/compiler/semtypinst.nim b/compiler/semtypinst.nim index 4fdc74373..61d92bb19 100644 --- a/compiler/semtypinst.nim +++ b/compiler/semtypinst.nim @@ -243,7 +243,7 @@ proc lookupTypeVar(cl: var TReplTypeVars, t: PType): PType = result = cl.typeMap.lookup(t) if result == nil: if cl.allowMetaTypes or tfRetType in t.flags: return - localError(cl.c.config, t.sym.info, "cannot instantiate: " & typeToString(t)) + localError(cl.c.config, t.sym.info, "cannot instantiate: '" & typeToString(t) & "'") result = errorType(cl.c) # In order to prevent endless recursions, we must remember # this bad lookup and replace it with errorType everywhere. @@ -453,7 +453,7 @@ proc replaceTypeVarsTAux(cl: var TReplTypeVars, t: PType): PType = result.kind = tyUserTypeClassInst of tyGenericBody: - localError(cl.c.config, cl.info, "cannot instantiate: " & typeToString(t)) + localError(cl.c.config, cl.info, "cannot instantiate: '" & typeToString(t) & "'") result = errorType(cl.c) #result = replaceTypeVarsT(cl, lastSon(t)) diff --git a/compiler/sigmatch.nim b/compiler/sigmatch.nim index 1d4515875..41cac2a4a 100644 --- a/compiler/sigmatch.nim +++ b/compiler/sigmatch.nim @@ -2365,7 +2365,7 @@ proc instTypeBoundOp*(c: PContext; dc: PSym; t: PType; info: TLineInfo; var m: TCandidate initCandidate(c, m, dc.typ) if col >= dc.typ.len: - localError(c.config, info, "cannot instantiate '" & dc.name.s & "'") + localError(c.config, info, "cannot instantiate: '" & dc.name.s & "'") return nil var f = dc.typ.sons[col] @@ -2374,7 +2374,7 @@ proc instTypeBoundOp*(c: PContext; dc: PSym; t: PType; info: TLineInfo; else: if f.kind == tyVar: f = f.lastSon if typeRel(m, f, t) == isNone: - localError(c.config, info, "cannot instantiate '" & dc.name.s & "'") + localError(c.config, info, "cannot instantiate: '" & dc.name.s & "'") else: result = c.semGenerateInstance(c, dc, m.bindings, info) if op == attachedDeepCopy: diff --git a/compiler/types.nim b/compiler/types.nim index 9c2ad71c5..b5f4fbf54 100644 --- a/compiler/types.nim +++ b/compiler/types.nim @@ -1640,7 +1640,7 @@ proc typeMismatch*(conf: ConfigRef; info: TLineInfo, formal, actual: PType) = let x = if named == desc: named else: named & " = " & desc var msg = "type mismatch: got <" & typeToString(actual) & "> " & - "but expected " & x & "'" + "but expected '" & x & "'" if formal.kind == tyProc and actual.kind == tyProc: case compatibleEffects(formal, actual) diff --git a/tests/controlflow/tstatret.nim b/tests/controlflow/tstatret.nim index d655f5595..04cac9966 100644 --- a/tests/controlflow/tstatret.nim +++ b/tests/controlflow/tstatret.nim @@ -1,7 +1,7 @@ discard """ file: "tstatret.nim" line: 9 - errormsg: "statement not allowed after" + errormsg: "unreachable statement after 'return'" """ # no statement after return proc main() = diff --git a/tests/errmsgs/tinvalidinout.nim b/tests/errmsgs/tinvalidinout.nim index ce7eb6022..1fa3805ee 100644 --- a/tests/errmsgs/tinvalidinout.nim +++ b/tests/errmsgs/tinvalidinout.nim @@ -1,10 +1,10 @@ discard """ cmd: "nim check $file" -errormsg: "The `in` modifier can be used only with imported types" +errormsg: "the 'in' modifier can be used only with imported types" nimout: ''' -tinvalidinout.nim(14, 7) Error: The `out` modifier can be used only with imported types -tinvalidinout.nim(17, 9) Error: The `in` modifier can be used only with imported types -tinvalidinout.nim(18, 9) Error: The `in` modifier can be used only with imported types +tinvalidinout.nim(14, 7) Error: the 'out' modifier can be used only with imported types +tinvalidinout.nim(17, 9) Error: the 'in' modifier can be used only with imported types +tinvalidinout.nim(18, 9) Error: the 'in' modifier can be used only with imported types ''' """ diff --git a/tests/lexer/tunderscores.nim b/tests/lexer/tunderscores.nim index e718fb40a..f64f36977 100644 --- a/tests/lexer/tunderscores.nim +++ b/tests/lexer/tunderscores.nim @@ -1,7 +1,7 @@ discard """ file: "tunderscores.nim" line: 8 - errormsg: "invalid token: _" + errormsg: "invalid token: trailing underscore" """ # Bug #502670 diff --git a/tests/misc/tunsignedmisc.nim b/tests/misc/tunsignedmisc.nim index 4b8157ddd..fc02eee19 100644 --- a/tests/misc/tunsignedmisc.nim +++ b/tests/misc/tunsignedmisc.nim @@ -1,5 +1,5 @@ discard """ - errormsg: "number 0x123'u8 out of valid range" + errormsg: "number out of range: '0x123'u8'" """ # Bug #1179 diff --git a/tests/objvariant/temptycaseobj.nim b/tests/objvariant/temptycaseobj.nim index 5c012746e..53171e054 100644 --- a/tests/objvariant/temptycaseobj.nim +++ b/tests/objvariant/temptycaseobj.nim @@ -1,6 +1,6 @@ discard """ line: 11 - errormsg: "identifier expected, but found 'keyword of'" + errormsg: "identifier expected, but got 'keyword of'" """ type diff --git a/tests/parser/tinvcolonlocation1.nim b/tests/parser/tinvcolonlocation1.nim index cacde48bd..2fddab2f8 100644 --- a/tests/parser/tinvcolonlocation1.nim +++ b/tests/parser/tinvcolonlocation1.nim @@ -1,8 +1,8 @@ discard """ file: "tinvcolonlocation1.nim" line: 8 - column: 3 - errormsg: "':' expected" + column: 7 + errormsg: "expected: ':', but got: 'echo'" """ try #<- missing ':' echo "try" diff --git a/tests/parser/tinvcolonlocation2.nim b/tests/parser/tinvcolonlocation2.nim index 2b6a92b9d..4251598b9 100644 --- a/tests/parser/tinvcolonlocation2.nim +++ b/tests/parser/tinvcolonlocation2.nim @@ -1,8 +1,8 @@ discard """ file: "tinvcolonlocation2.nim" line: 11 - column: 1 - errormsg: "':' expected" + column: 8 + errormsg: "expected: ':', but got: 'keyword finally'" """ try: echo "try" diff --git a/tests/parser/tinvcolonlocation3.nim b/tests/parser/tinvcolonlocation3.nim index 2b30b1dbe..a8db658eb 100644 --- a/tests/parser/tinvcolonlocation3.nim +++ b/tests/parser/tinvcolonlocation3.nim @@ -1,8 +1,8 @@ discard """ file: "tinvcolonlocation3.nim" line: 12 - column: 3 - errormsg: "':' expected" + column: 7 + errormsg: "expected: ':', but got: 'echo'" """ try: echo "try" diff --git a/tests/parser/twhen_in_enum.nim b/tests/parser/twhen_in_enum.nim index d4a3ea56a..3890b686e 100644 --- a/tests/parser/twhen_in_enum.nim +++ b/tests/parser/twhen_in_enum.nim @@ -1,5 +1,5 @@ discard """ - errormsg: "identifier expected, but found 'keyword when'" + errormsg: "identifier expected, but got 'keyword when'" """ # bug #2123 diff --git a/tests/types/tparameterizedparent3.nim b/tests/types/tparameterizedparent3.nim index 3fc83cb4d..58aaf80ea 100644 --- a/tests/types/tparameterizedparent3.nim +++ b/tests/types/tparameterizedparent3.nim @@ -1,7 +1,7 @@ discard """ file: "tparameterizedparent3.nim" line: 13 - errormsg: "redefinition of 'color'" + errormsg: "attempt to redefine: 'color'" """ # bug #5264 type diff --git a/tests/types/tparameterizedparent4.nim b/tests/types/tparameterizedparent4.nim index fa8b525c1..a37461bb4 100644 --- a/tests/types/tparameterizedparent4.nim +++ b/tests/types/tparameterizedparent4.nim @@ -1,7 +1,7 @@ discard """ file: "tparameterizedparent4.nim" line: 23 - errormsg: "redefinition of 'grain'" + errormsg: "attempt to redefine: 'grain'" """ # bug #5264 type -- cgit 1.4.1-2-gfad0