diff options
-rw-r--r-- | compiler/parser.nim | 127 | ||||
-rw-r--r-- | compiler/semstmts.nim | 8 | ||||
-rw-r--r-- | doc/grammar.txt | 30 | ||||
-rw-r--r-- | lib/core/macros.nim | 18 | ||||
-rw-r--r-- | lib/pure/pegs.nim | 8 | ||||
-rw-r--r-- | tests/macros/tmemit.nim | 7 | ||||
-rw-r--r-- | tests/parser/tcommand_as_expr.nim | 12 | ||||
-rw-r--r-- | tests/parser/tdomulttest.nim | 17 | ||||
-rw-r--r-- | tests/parser/tinvwhen.nim | 15 | ||||
-rw-r--r-- | tests/patterns/tpatterns.nim (renamed from tests/pattern/tpatterns.nim) | 0 | ||||
-rw-r--r-- | tests/pragmas/tuserpragma.nim (renamed from tests/pragma/tuserpragma.nim) | 0 | ||||
-rw-r--r-- | todo.txt | 4 | ||||
-rw-r--r-- | web/news.txt | 7 |
13 files changed, 173 insertions, 80 deletions
diff --git a/compiler/parser.nim b/compiler/parser.nim index c44036c5f..d255949a4 100644 --- a/compiler/parser.nim +++ b/compiler/parser.nim @@ -18,10 +18,10 @@ # In fact the grammar is generated from this file: when isMainModule: import pegs - var outp = open("compiler/grammar.txt", fmWrite) + var outp = open("doc/grammar.txt", fmWrite) for line in lines("compiler/parser.nim"): if line =~ peg" \s* '#| ' {.*}": - outp.writeln matches[0] + outp.write matches[0], "\L" outp.close import @@ -34,6 +34,7 @@ type firstTok: bool lex*: TLexer # the lexer that is used for parsing tok*: TToken # the current token + inPragma: int proc parseAll*(p: var TParser): PNode proc openParser*(p: var TParser, filename: string, inputstream: PLLStream) @@ -518,14 +519,14 @@ proc parsePar(p: var TParser): PNode = 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 - #| | 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 + #| identOrLiteral = generalizedLit | symbol | literal #| | par | arrayConstr | setOrTableConstr #| | castExpr #| tupleConstr = '(' optInd (exprColonEqExpr comma?)* optPar ')' @@ -634,12 +635,15 @@ proc namedParams(p: var TParser, callee: PNode, 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 ('type' | 'addr' | symbol) generalizedLit? #| | '[' optInd indexExprList optPar ']' #| | '{' optInd indexExprList optPar '}' + #| | &( '`'|IDENT|literal|'cast') expr ^+ ',' # command syntax + #| (doBlock | macroColon)? result = r while p.tok.indent < 0: case p.tok.tokType @@ -661,8 +665,27 @@ proc primarySuffix(p: var TParser, r: PNode): PNode = result = namedParams(p, result, nkBracketExpr, tkBracketRi) of tkCurlyLe: result = namedParams(p, result, nkCurlyExpr, tkCurlyRi) - else: break - + of tkSymbol, tkAccent, tkIntLit..tkCharLit, tkNil, tkCast: + 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) + while p.tok.tokType != tkEof: + let a = parseExpr(p) + addSon(result, a) + if p.tok.tokType != tkComma: break + getTok(p) + optInd(p, a) + 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 = @@ -713,6 +736,7 @@ proc parseIfExpr(p: var TParser, kind: TNodeKind): PNode = proc parsePragma(p: var TParser): PNode = #| pragma = '{.' optInd (exprColonExpr comma?)* optPar ('.}' | '}') result = newNodeP(nkPragma, p) + inc p.inPragma getTok(p) optInd(p, result) while p.tok.tokType notin {tkCurlyDotRi, tkCurlyRi, tkEof}: @@ -724,6 +748,7 @@ proc parsePragma(p: var TParser): PNode = optPar(p) if p.tok.tokType in {tkCurlyDotRi, tkCurlyRi}: getTok(p) else: parMessage(p, errTokenExpected, ".}") + dec p.inPragma proc identVis(p: var TParser): PNode = #| identVis = symbol opr? # postfix position @@ -965,7 +990,7 @@ proc primary(p: var TParser, mode: TPrimaryMode): PNode = of tkTuple: result = parseTuple(p, mode == pmTypeDef) of tkProc: result = parseProcExpr(p, mode notin {pmTypeDesc, pmTypeDef}) of tkIterator: - when true: + when false: if mode in {pmTypeDesc, pmTypeDef}: result = parseProcExpr(p, false) result.kind = nkIteratorTy @@ -976,7 +1001,8 @@ proc primary(p: var TParser, mode: TPrimaryMode): PNode = result = ast.emptyNode else: result = parseProcExpr(p, mode notin {pmTypeDesc, pmTypeDef}) - result.kind = nkIteratorTy + if result.kind == nkLambda: result.kind = nkIteratorDef + else: result.kind = nkIteratorTy of tkEnum: if mode == pmTypeDef: result = parseEnum(p) @@ -1031,15 +1057,50 @@ proc makeCall(n: PNode): PNode = 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) + if p.tok.tokType notin {tkOf, tkElif, tkElse, tkExcept}: + let body = parseStmt(p) + addSon(result, newProcNode(nkDo, body.info, body)) + while sameInd(p): + var b: PNode + case p.tok.tokType + of tkOf: + b = newNodeP(nkOfBranch, p) + exprList(p, tkColon, b) + of tkElif: + b = newNodeP(nkElifBranch, p) + getTok(p) + optInd(p, b) + addSon(b, parseExpr(p)) + eat(p, tkColon) + of tkExcept: + b = newNodeP(nkExceptBranch, p) + exprList(p, tkColon, b) + skipComment(p, b) + of tkElse: + b = newNodeP(nkElse, p) + getTok(p) + eat(p, tkColon) + else: break + addSon(b, parseStmt(p)) + addSon(result, b) + if b.kind == nkElse: break + proc parseExprStmt(p: var TParser): PNode = #| exprStmt = simpleExpr #| (( '=' optInd expr ) #| / ( expr ^+ comma #| doBlocks - #| / ':' stmt? ( IND{=} 'of' exprList ':' stmt - #| | IND{=} 'elif' expr ':' stmt - #| | IND{=} 'except' exprList ':' stmt - #| | IND{=} 'else' ':' stmt )* + #| / macroColon #| ))? var a = simpleExpr(p) if p.tok.tokType == tkEquals: @@ -1064,37 +1125,7 @@ proc parseExprStmt(p: var TParser): PNode = result = makeCall(result) parseDoBlocks(p, result) return result - if p.tok.tokType == tkColon and p.tok.indent < 0: - result = makeCall(result) - getTok(p) - skipComment(p, result) - if p.tok.tokType notin {tkOf, tkElif, tkElse, tkExcept}: - let body = parseStmt(p) - addSon(result, newProcNode(nkDo, body.info, body)) - while sameInd(p): - var b: PNode - case p.tok.tokType - of tkOf: - b = newNodeP(nkOfBranch, p) - exprList(p, tkColon, b) - of tkElif: - b = newNodeP(nkElifBranch, p) - getTok(p) - optInd(p, b) - addSon(b, parseExpr(p)) - eat(p, tkColon) - of tkExcept: - b = newNodeP(nkExceptBranch, p) - exprList(p, tkColon, b) - skipComment(p, b) - of tkElse: - b = newNodeP(nkElse, p) - getTok(p) - eat(p, tkColon) - else: break - addSon(b, parseStmt(p)) - addSon(result, b) - if b.kind == nkElse: break + result = parseMacroColon(p, result) proc parseModuleName(p: var TParser, kind: TNodeKind): PNode = result = parseExpr(p) diff --git a/compiler/semstmts.nim b/compiler/semstmts.nim index 1aa6a793c..fc1706200 100644 --- a/compiler/semstmts.nim +++ b/compiler/semstmts.nim @@ -922,7 +922,7 @@ proc activate(c: PContext, n: PNode) = of nkCallKinds: for i in 1 .. <n.len: activate(c, n[i]) else: - nil + discard proc maybeAddResult(c: PContext, s: PSym, n: PNode) = if s.typ.sons[0] != nil and @@ -951,7 +951,11 @@ proc semProcAux(c: PContext, n: PNode, kind: TSymKind, var typeIsDetermined = false if n[namePos].kind != nkSym: assert phase == stepRegisterSymbol - s = semIdentDef(c, n.sons[0], kind) + + if n[namePos].kind == nkEmpty: + s = newSym(kind, idAnon, getCurrOwner(), n.info) + else: + s = semIdentDef(c, n.sons[0], kind) n.sons[namePos] = newSymNode(s) s.ast = n s.scope = c.currentScope diff --git a/doc/grammar.txt b/doc/grammar.txt index 7fe2b56aa..b002747fa 100644 --- a/doc/grammar.txt +++ b/doc/grammar.txt @@ -42,14 +42,14 @@ par = '(' optInd (&parKeyw complexOrSimpleStmt ^+ ';' | simpleExpr ('=' expr (';' complexOrSimpleStmt ^+ ';' )? )? | (':' expr)? (',' (exprColonEqExpr comma?)*)? )? optPar ')' +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 - | 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 +identOrLiteral = generalizedLit | symbol | literal | par | arrayConstr | setOrTableConstr | castExpr tupleConstr = '(' optInd (exprColonEqExpr comma?)* optPar ')' @@ -59,6 +59,8 @@ primarySuffix = '(' (exprColonEqExpr comma?)* ')' doBlocks? | '.' optInd ('type' | 'addr' | symbol) generalizedLit? | '[' optInd indexExprList optPar ']' | '{' optInd indexExprList optPar '}' + | &( '`'|IDENT|literal|'cast') expr ^+ ',' # command syntax + (doBlock | macroColon)? condExpr = expr colcom expr optInd ('elif' expr colcom expr optInd)* 'else' colcom expr @@ -95,18 +97,18 @@ primary = typeKeyw typeDescK / 'bind' primary typeDesc = simpleExpr typeDefAux = simpleExpr +macroColon = ':' stmt? ( IND{=} 'of' exprList ':' stmt + | IND{=} 'elif' expr ':' stmt + | IND{=} 'except' exprList ':' stmt + | IND{=} 'else' ':' stmt )* exprStmt = simpleExpr (( '=' optInd expr ) / ( expr ^+ comma doBlocks - / ':' stmt? ( IND{=} 'of' exprList ':' stmt - | IND{=} 'elif' expr ':' stmt - | IND{=} 'except' exprList ':' stmt - | IND{=} 'else' ':' stmt )* + / macroColon ))? -moduleName = expr ('as' expr)? -importStmt = 'import' optInd moduleName - ((comma moduleName)* +importStmt = 'import' optInd expr + ((comma expr)* / 'except' optInd (expr ^+ comma)) includeStmt = 'include' optInd expr ^+ comma fromStmt = 'from' moduleName 'import' optInd expr (comma expr)* diff --git a/lib/core/macros.nim b/lib/core/macros.nim index 0356067f1..7cb084653 100644 --- a/lib/core/macros.nim +++ b/lib/core/macros.nim @@ -300,9 +300,12 @@ when not defined(booting): ## that should be inserted verbatim in the program ## Example: ## + ## .. code-block:: nimrod ## emit("echo " & '"' & "hello world".toUpper & '"') ## - eval: result = e.parseStmt + macro payload: stmt {.gensym.} = + result = e.parseStmt + payload() proc expectKind*(n: PNimrodNode, k: TNimrodNodeKind) {.compileTime.} = ## checks that `n` is of kind `k`. If this is not the case, @@ -645,10 +648,13 @@ iterator children*(n: PNimrodNode): PNimrodNode {.inline.}= for i in 0 .. high(n): yield n[i] -template findChild*(n: PNimrodNode; cond: expr): PNimrodNode {.immediate, dirty.} = - ## Find the first child node matching condition (or nil) - ## var res = findChild(n, it.kind == nnkPostfix and it.basename.ident == !"foo") - +template findChild*(n: PNimrodNode; cond: expr): PNimrodNode {. + immediate, dirty.} = + ## Find the first child node matching condition (or nil). + ## + ## .. code-block:: nimrod + ## var res = findChild(n, it.kind == nnkPostfix and + ## it.basename.ident == !"foo") block: var result: PNimrodNode for it in n.children: @@ -736,6 +742,6 @@ proc addIdentIfAbsent*(dest: PNimrodNode, ident: string) {.compiletime.} = if ident.eqIdent($node): return of nnkExprColonExpr: if ident.eqIdent($node[0]): return - else: nil + else: discard dest.add(ident(ident)) diff --git a/lib/pure/pegs.nim b/lib/pure/pegs.nim index a6147a96c..70b617393 100644 --- a/lib/pure/pegs.nim +++ b/lib/pure/pegs.nim @@ -306,7 +306,7 @@ proc backrefIgnoreStyle*(index: range[1..MaxSubPatterns]): TPeg {. proc spaceCost(n: TPeg): int = case n.kind - of pkEmpty: nil + of pkEmpty: discard of pkTerminal, pkTerminalIgnoreCase, pkTerminalIgnoreStyle, pkChar, pkGreedyRepChar, pkCharChoice, pkGreedyRepSet, pkAny..pkWhitespace, pkGreedyAny: @@ -1117,7 +1117,7 @@ proc handleHexChar(c: var TPegLexer, xi: var int) = of 'A'..'F': xi = (xi shl 4) or (ord(c.buf[c.bufpos]) - ord('A') + 10) inc(c.bufpos) - else: nil + else: discard proc getEscapedChar(c: var TPegLexer, tok: var TToken) = inc(c.bufpos) @@ -1347,7 +1347,7 @@ proc getTok(c: var TPegLexer, tok: var TToken) = of "i": tok.modifier = modIgnoreCase of "y": tok.modifier = modIgnoreStyle of "v": tok.modifier = modVerbatim - else: nil + else: discard setLen(tok.literal, 0) if c.buf[c.bufpos] == '$': getDollar(c, tok) @@ -1494,7 +1494,7 @@ proc primary(p: var TPegParser): TPeg = of tkCurlyAt: getTok(p) return !*\primary(p).token(p) - else: nil + else: discard case p.tok.kind of tkIdentifier: if p.identIsVerbatim: diff --git a/tests/macros/tmemit.nim b/tests/macros/tmemit.nim new file mode 100644 index 000000000..e4bb2daed --- /dev/null +++ b/tests/macros/tmemit.nim @@ -0,0 +1,7 @@ +discard """ + out: '''HELLO WORLD''' +""" + +import macros, strutils + +emit("echo " & '"' & "hello world".toUpper & '"') diff --git a/tests/parser/tcommand_as_expr.nim b/tests/parser/tcommand_as_expr.nim new file mode 100644 index 000000000..f6868a2fc --- /dev/null +++ b/tests/parser/tcommand_as_expr.nim @@ -0,0 +1,12 @@ +discard """ + output: "12" +""" + +proc foo(x: int): int = x-1 +proc foo(x, y: int): int = x-y + +let x = foo 7.foo, # comment here + foo(1, foo 8) +# 12 = 6 - -6 +echo x + diff --git a/tests/parser/tdomulttest.nim b/tests/parser/tdomulttest.nim new file mode 100644 index 000000000..4ee6de128 --- /dev/null +++ b/tests/parser/tdomulttest.nim @@ -0,0 +1,17 @@ +discard """ + file: "tdomulttest.nim" + output: "555\ntest\nmulti lines\n99999999\nend" + disabled: true +""" +proc foo(bar, baz: proc (x: int): int) = + echo bar(555) + echo baz(99999999) + +foo do (x: int) -> int: + return x +do (x: int) -> int: + echo("test") + echo("multi lines") + return x + +echo("end") \ No newline at end of file diff --git a/tests/parser/tinvwhen.nim b/tests/parser/tinvwhen.nim new file mode 100644 index 000000000..5ff94cc6c --- /dev/null +++ b/tests/parser/tinvwhen.nim @@ -0,0 +1,15 @@ +discard """ + file: "tinvwhen.nim" + line: 11 + errormsg: "invalid indentation" +""" +# This was parsed even though it should not! + +proc chdir(path: cstring): cint {.importc: "chdir", header: "dirHeader".} + +proc getcwd(buf: cstring, buflen: cint): cstring + when defined(unix): {.importc: "getcwd", header: "<unistd.h>".} #ERROR_MSG invalid indentation + elif defined(windows): {.importc: "getcwd", header: "<direct.h>"} + else: {.error: "os library not ported to your OS. Please help!".} + + diff --git a/tests/pattern/tpatterns.nim b/tests/patterns/tpatterns.nim index 6bc8772e3..6bc8772e3 100644 --- a/tests/pattern/tpatterns.nim +++ b/tests/patterns/tpatterns.nim diff --git a/tests/pragma/tuserpragma.nim b/tests/pragmas/tuserpragma.nim index 784baa176..784baa176 100644 --- a/tests/pragma/tuserpragma.nim +++ b/tests/pragmas/tuserpragma.nim diff --git a/todo.txt b/todo.txt index 21416f279..15ce93df7 100644 --- a/todo.txt +++ b/todo.txt @@ -6,6 +6,7 @@ version 0.9.4 - ensure (ref T)(a, b) works as a type conversion and type constructor - document new templating symbol binding rules - make '--implicitStatic:on' the default +- change comment handling in the AST - special rule for ``[]=`` - ``=`` should be overloadable; requires specialization for ``=``; general @@ -27,8 +28,6 @@ Bugs - docgen: sometimes effects are listed twice - 'result' is not properly cleaned for NRVO --> use uninit checking instead - sneaking with qualifiedLookup() is really broken! -- aporia.nim(968, 5) Error: ambiguous identifier: 'DELETE' -- - use a qualifier - blocks can "export" an identifier but the CCG generates {} for them ... - osproc execProcesses can deadlock if all processes fail (as experienced in c++ mode) @@ -54,7 +53,6 @@ version 0.9.X - implement the missing features wrt inheritance - better support for macros that rewrite procs - macros need access to types and symbols (partially implemented) -- perhaps: change comment handling in the AST - enforce 'simpleExpr' more often --> doesn't work; tkProc is part of primary! - the typeDesc/expr unification is weird and only necessary because of diff --git a/web/news.txt b/web/news.txt index 1ed447009..01b6d18b9 100644 --- a/web/news.txt +++ b/web/news.txt @@ -28,8 +28,8 @@ Changes affecting backwards compatibility require an error code to be passed to them. This error code can be retrieved using the new ``OSLastError`` proc. - ``os.parentDir`` now returns "" if there is no parent dir. -- In CGI scripts stacktraces are shown user only if cgi.setStackTraceStdout - is used. +- In CGI scripts stacktraces are shown to the user only + if ``cgi.setStackTraceStdout`` is used. - The symbol binding rules for clean templates changed: ``bind`` for any symbol that's not a parameter is now the default. ``mixin`` can be used to require instantiation scope for a symbol. @@ -71,8 +71,9 @@ Language Additions - Added a new ``delegator pragma`` for handling calls to missing procs and fields at compile-time. - The overload resolution now supports ``static[T]`` params that must be - evaluatable at compile-time. + evaluable at compile-time. - Support for user-defined type classes has been added. +- The *command syntax* is supported in a lot more contexts. Tools improvements |