From 0a0fec4a5c6dcd3c6ac007877ef297914657b7f2 Mon Sep 17 00:00:00 2001 From: Vincent Burns Date: Sun, 12 Jan 2014 12:48:06 -0500 Subject: [PATCH 01/25] Added spliceHeader option to c2nim parse a header file first, then the source. completing a c 'module' --- compiler/c2nim/c2nim.nim | 22 +++++++++++++++++----- 1 file changed, 17 insertions(+), 5 deletions(-) diff --git a/compiler/c2nim/c2nim.nim b/compiler/c2nim/c2nim.nim index 1c701a386c..c4fc8ad672 100644 --- a/compiler/c2nim/c2nim.nim +++ b/compiler/c2nim/c2nim.nim @@ -34,25 +34,36 @@ Options: --skipcomments do not copy comments --ignoreRValueRefs translate C++'s ``T&&`` to ``T`` instead ``of var T`` --keepBodies keep C++'s method bodies + --spliceHeader parse and emit header before source file -v, --version write c2nim's version -h, --help show this help """ -proc main(infile, outfile: string, options: PParserOptions) = - var start = getTime() +proc parse(infile: string, options: PParserOptions): PNode = var stream = llStreamOpen(infile, fmRead) if stream == nil: rawMessage(errCannotOpenFile, infile) var p: TParser openParser(p, infile, stream, options) - var module = parseUnit(p) + result = parseUnit(p) closeParser(p) - renderModule(module, outfile) + +proc main(infile, outfile: string, options: PParserOptions, spliceHeader: bool) = + var start = getTime() + if spliceHeader and infile[infile.len-2 .. infile.len] == ".c" and existsFile(infile[0 .. infile.len-2] & "h"): + var header_module = parse(infile[0 .. infile.len-2] & "h", options) + var source_module = parse(infile, options) + for n in source_module: + addson(header_module, n) + renderModule(header_module, outfile) + else: + renderModule(parse(infile, options), outfile) rawMessage(hintSuccessX, [$gLinesCompiled, $(getTime() - start), formatSize(getTotalMem())]) var infile = "" outfile = "" + spliceHeader = false parserOptions = newParserOptions() for kind, key, val in getopt(): case kind @@ -66,6 +77,7 @@ for kind, key, val in getopt(): stdout.write(Version & "\n") quit(0) of "o", "out": outfile = val + of "spliceheader": spliceHeader = true else: if not parserOptions.setOption(key, val): stdout.writeln("[Error] unknown option: " & key) @@ -77,4 +89,4 @@ else: if outfile.len == 0: outfile = changeFileExt(infile, "nim") infile = addFileExt(infile, "h") - main(infile, outfile, parserOptions) + main(infile, outfile, parserOptions, spliceHeader) From 2dc91cb4d51a628aff300116c1f7a377b310a198 Mon Sep 17 00:00:00 2001 From: Vincent Burns Date: Sun, 12 Jan 2014 12:53:25 -0500 Subject: [PATCH 02/25] Lex '\xHH' character constants --- compiler/c2nim/clex.nim | 17 +++++++++++++++++ 1 file changed, 17 insertions(+) diff --git a/compiler/c2nim/clex.nim b/compiler/c2nim/clex.nim index 7e5526a10f..fe7fe92d32 100644 --- a/compiler/c2nim/clex.nim +++ b/compiler/c2nim/clex.nim @@ -382,6 +382,23 @@ proc escape(L: var TLexer, tok: var TToken, allowEmpty=false) = xi = (xi shl 3) or (ord(L.buf[L.bufpos]) - ord('0')) inc(L.bufpos) add(tok.s, chr(xi)) + of 'x': + var xi = 0 + inc(L.bufpos) + while true: + case L.buf[L.bufpos] + of '0'..'9': + xi = `shl`(xi, 4) or (ord(L.buf[L.bufpos]) - ord('0')) + inc(L.bufpos) + of 'a'..'f': + xi = `shl`(xi, 4) or (ord(L.buf[L.bufpos]) - ord('a') + 10) + inc(L.bufpos) + of 'A'..'F': + xi = `shl`(xi, 4) or (ord(L.buf[L.bufpos]) - ord('A') + 10) + inc(L.bufpos) + else: + break + add(tok.s, chr(xi)) elif not allowEmpty: lexMessage(L, errInvalidCharacterConstant) From c5bd98b7dba674b43ddff9ef9b4ff8358d327f3a Mon Sep 17 00:00:00 2001 From: Vincent Burns Date: Sun, 12 Jan 2014 13:23:52 -0500 Subject: [PATCH 03/25] Properly lex floating constants digit-sequence? '.' digit-sequence exponent-part? digit-sequence '.' exponent-part? exponent-part: [eE] [+-]? digit-sequence --- compiler/c2nim/clex.nim | 25 ++++++++++++++++++++----- 1 file changed, 20 insertions(+), 5 deletions(-) diff --git a/compiler/c2nim/clex.nim b/compiler/c2nim/clex.nim index fe7fe92d32..3934eea63d 100644 --- a/compiler/c2nim/clex.nim +++ b/compiler/c2nim/clex.nim @@ -315,13 +315,28 @@ proc getNumber16(L: var TLexer, tok: var TToken) = else: tok.xkind = pxIntLit L.bufpos = pos +proc getFloating(L: var TLexer, tok: var TToken) = + matchUnderscoreChars(L, tok, {'0'..'9'}) + if L.buf[L.bufpos] in {'e', 'E'}: + add(tok.s, L.buf[L.bufpos]) + inc(L.bufpos) + if L.buf[L.bufpos] in {'+', '-'}: + add(tok.s, L.buf[L.bufpos]) + inc(L.bufpos) + matchUnderscoreChars(L, tok, {'0'..'9'}) + proc getNumber(L: var TLexer, tok: var TToken) = tok.base = base10 - matchUnderscoreChars(L, tok, {'0'..'9'}) - if (L.buf[L.bufpos] == '.') and (L.buf[L.bufpos + 1] in {'0'..'9'}): - add(tok.s, '.') + if L.buf[L.bufpos] == '.': + add(tok.s, "0.") inc(L.bufpos) - matchUnderscoreChars(L, tok, {'e', 'E', '+', '-', '0'..'9'}) + getFloating(L, tok) + else: + matchUnderscoreChars(L, tok, {'0'..'9'}) + if L.buf[L.bufpos] == '.': + add(tok.s, '.') + inc(L.bufpos) + getFloating(L, tok) try: if isFloatLiteral(tok.s): tok.fnumber = parseFloat(tok.s) @@ -576,7 +591,7 @@ proc getTok(L: var TLexer, tok: var TToken) = of 'b', 'B': getNumber2(L, tok) of '1'..'7': getNumber8(L, tok) else: getNumber(L, tok) - elif c in {'1'..'9'}: + elif c in {'1'..'9'} or (c == '.' and L.buf[L.bufpos+1] in {'0'..'9'}): getNumber(L, tok) else: case c From 570f8b21e196d8f8046e1e4c4d59db2aef2f0ea9 Mon Sep 17 00:00:00 2001 From: Vincent Burns Date: Sun, 12 Jan 2014 17:13:23 -0500 Subject: [PATCH 04/25] New expression parser tests pass --- compiler/c2nim/cparse.nim | 620 +++++++++++++++++--------------------- 1 file changed, 272 insertions(+), 348 deletions(-) diff --git a/compiler/c2nim/cparse.nim b/compiler/c2nim/cparse.nim index 3adab0f44e..bf785b13fa 100644 --- a/compiler/c2nim/cparse.nim +++ b/compiler/c2nim/cparse.nim @@ -420,9 +420,9 @@ proc markTypeIdent(p: var TParser, typ: PNode) = # avoids to build a symbol table, which can't be done reliably anyway for our # purposes. -proc expression(p: var TParser): PNode -proc constantExpression(p: var TParser): PNode -proc assignmentExpression(p: var TParser): PNode +proc expression(p: var TParser, rbp: int = 0): PNode +proc constantExpression(p: var TParser): PNode = expression(p, 40) +proc assignmentExpression(p: var TParser): PNode = expression(p, 30) proc compoundStatement(p: var TParser): PNode proc statement(p: var TParser): PNode @@ -1134,224 +1134,27 @@ proc setBaseFlags(n: PNode, base: TNumericalBase) = of base8: incl(n.flags, nfBase8) of base16: incl(n.flags, nfBase16) -proc unaryExpression(p: var TParser): PNode - -proc isDefinitelyAType(p: var TParser): bool = - var starFound = false - var words = 0 - while true: - case p.tok.xkind - of pxSymbol: - if declKeyword(p, p.tok.s): return true - elif starFound: return false - else: inc(words) - of pxStar, pxAmp, pxAmpAmp: - starFound = true - of pxParRi: return words == 0 or words > 1 or starFound - else: return false - getTok(p, nil) - -proc castExpression(p: var TParser): PNode = - if p.tok.xkind == pxParLe: - saveContext(p) - result = newNodeP(nkCast, p) - getTok(p, result) - var t = isDefinitelyAType(p) - backtrackContext(p) - if t: - eat(p, pxParLe, result) - var a = typeDesc(p) - eat(p, pxParRi, result) - addSon(result, a) - addSon(result, castExpression(p)) - else: - # else it is just an expression in (): - result = newNodeP(nkPar, p) - eat(p, pxParLe, result) - addSon(result, expression(p)) - if p.tok.xkind != pxParRi: - # ugh, it is a cast, even though it does not look like one: - result.kind = nkCast - addSon(result, castExpression(p)) - eat(p, pxParRi, result) - #result = unaryExpression(p) - else: - result = unaryExpression(p) - -proc primaryExpression(p: var TParser): PNode = - case p.tok.xkind - of pxSymbol: - if p.tok.s == "NULL": - result = newNodeP(nkNilLit, p) - else: - result = mangledIdent(p.tok.s, p) - getTok(p, result) - result = optScope(p, result) - of pxIntLit: - result = newIntNodeP(nkIntLit, p.tok.iNumber, p) - setBaseFlags(result, p.tok.base) - getTok(p, result) - of pxInt64Lit: - result = newIntNodeP(nkInt64Lit, p.tok.iNumber, p) - setBaseFlags(result, p.tok.base) - getTok(p, result) - of pxFloatLit: - result = newFloatNodeP(nkFloatLit, p.tok.fNumber, p) - setBaseFlags(result, p.tok.base) - getTok(p, result) - of pxStrLit: - # Ansi C allows implicit string literal concatenations: - result = newStrNodeP(nkStrLit, p.tok.s, p) - getTok(p, result) - while p.tok.xkind == pxStrLit: - add(result.strVal, p.tok.s) - getTok(p, result) - of pxCharLit: - result = newIntNodeP(nkCharLit, ord(p.tok.s[0]), p) - getTok(p, result) - of pxParLe: - result = castExpression(p) - else: - result = ast.emptyNode - -proc multiplicativeExpression(p: var TParser): PNode = - result = castExpression(p) - while true: - case p.tok.xkind - of pxStar: - var a = result - result = newNodeP(nkInfix, p) - addSon(result, newIdentNodeP("*", p), a) - getTok(p, result) - var b = castExpression(p) - addSon(result, b) - of pxSlash: - var a = result - result = newNodeP(nkInfix, p) - addSon(result, newIdentNodeP("div", p), a) - getTok(p, result) - var b = castExpression(p) - addSon(result, b) - of pxMod: - var a = result - result = newNodeP(nkInfix, p) - addSon(result, newIdentNodeP("mod", p), a) - getTok(p, result) - var b = castExpression(p) - addSon(result, b) - else: break - -proc additiveExpression(p: var TParser): PNode = - result = multiplicativeExpression(p) - while true: - case p.tok.xkind - of pxPlus: - var a = result - result = newNodeP(nkInfix, p) - addSon(result, newIdentNodeP("+", p), a) - getTok(p, result) - var b = multiplicativeExpression(p) - addSon(result, b) - of pxMinus: - var a = result - result = newNodeP(nkInfix, p) - addSon(result, newIdentNodeP("-", p), a) - getTok(p, result) - var b = multiplicativeExpression(p) - addSon(result, b) - else: break - -proc incdec(p: var TParser, opr: string): PNode = - result = newNodeP(nkCall, p) - addSon(result, newIdentNodeP(opr, p)) - getTok(p, result) - addSon(result, unaryExpression(p)) - -proc unaryOp(p: var TParser, kind: TNodeKind): PNode = - result = newNodeP(kind, p) - getTok(p, result) - addSon(result, castExpression(p)) - -proc prefixCall(p: var TParser, opr: string): PNode = - result = newNodeP(nkPrefix, p) - addSon(result, newIdentNodeP(opr, p)) - getTok(p, result) - addSon(result, castExpression(p)) - -proc postfixExpression(p: var TParser): PNode = - result = primaryExpression(p) - while true: - case p.tok.xkind - of pxBracketLe: - var a = result - result = newNodeP(nkBracketExpr, p) - addSon(result, a) - getTok(p, result) - var b = expression(p) - addSon(result, b) - eat(p, pxBracketRi, result) - of pxParLe: - var a = result - result = newNodeP(nkCall, p) - addSon(result, a) - getTok(p, result) - if p.tok.xkind != pxParRi: - a = assignmentExpression(p) - addSon(result, a) - while p.tok.xkind == pxComma: - getTok(p, a) - a = assignmentExpression(p) - addSon(result, a) - eat(p, pxParRi, result) - of pxDot, pxArrow: - var a = result - result = newNodeP(nkDotExpr, p) - addSon(result, a) - getTok(p, result) - addSon(result, skipIdent(p)) - of pxPlusPlus: - var a = result - result = newNodeP(nkCall, p) - addSon(result, newIdentNodeP("inc", p)) - getTok(p, result) - addSon(result, a) - of pxMinusMinus: - var a = result - result = newNodeP(nkCall, p) - addSon(result, newIdentNodeP("dec", p)) - getTok(p, result) - addSon(result, a) - of pxLt: - if isTemplateAngleBracket(p): - result = optAngle(p, result) - else: break - else: break - -proc unaryExpression(p: var TParser): PNode = - case p.tok.xkind - of pxPlusPlus: result = incdec(p, "inc") - of pxMinusMinus: result = incdec(p, "dec") - of pxAmp: result = unaryOp(p, nkAddr) - of pxStar: result = unaryOp(p, nkBracketExpr) - of pxPlus: result = prefixCall(p, "+") - of pxMinus: result = prefixCall(p, "-") - of pxTilde: result = prefixCall(p, "not") - of pxNot: result = prefixCall(p, "not") +proc startExpression(p : var TParser, tok : TToken) : PNode = + #echo "nud ", $tok + case tok.xkind: of pxSymbol: - if p.tok.s == "sizeof": + if tok.s == "NULL": + result = newNodeP(nkNilLit, p) + elif tok.s == "sizeof": result = newNodeP(nkCall, p) addSon(result, newIdentNodeP("sizeof", p)) - getTok(p, result) - if p.tok.xkind == pxParLe: - getTok(p, result) + saveContext(p) + try: + addSon(result, expression(p, 139)) + closeContext(p) + except: + backtrackContext(p) + eat(p, pxParLe) addSon(result, typeDesc(p)) - eat(p, pxParRi, result) - else: - addSon(result, unaryExpression(p)) - elif p.tok.s == "new" or p.tok.s == "delete" and pfCpp in p.options.flags: - var opr = p.tok.s + eat(p, pxParRi) + elif tok.s == "new" or tok.s == "delete" and pfCpp in p.options.flags: + var opr = tok.s result = newNodeP(nkCall, p) - getTok(p, result) if p.tok.xkind == pxBracketLe: getTok(p) eat(p, pxBracketRi) @@ -1362,148 +1165,269 @@ proc unaryExpression(p: var TParser): PNode = addSon(result, typeDesc(p)) eat(p, pxParRi, result) else: - addSon(result, unaryExpression(p)) + addSon(result, expression(p, 139)) else: - result = postfixExpression(p) - else: result = postfixExpression(p) - -proc expression(p: var TParser): PNode = - # we cannot support C's ``,`` operator - result = assignmentExpression(p) - if p.tok.xkind == pxComma: - getTok(p, result) - parMessage(p, errOperatorExpected, ",") - -proc conditionalExpression(p: var TParser): PNode - -proc constantExpression(p: var TParser): PNode = - result = conditionalExpression(p) - -proc lvalue(p: var TParser): PNode = - result = unaryExpression(p) - -proc asgnExpr(p: var TParser, opr: string, a: PNode): PNode = - closeContext(p) - getTok(p, a) - var b = assignmentExpression(p) - result = newNodeP(nkAsgn, p) - addSon(result, a, newBinary(opr, copyTree(a), b, p)) - -proc incdec(p: var TParser, opr: string, a: PNode): PNode = - closeContext(p) - getTok(p, a) - var b = assignmentExpression(p) - result = newNodeP(nkCall, p) - addSon(result, newIdentNodeP(getIdent(opr), p), a, b) - -proc assignmentExpression(p: var TParser): PNode = - saveContext(p) - var a = lvalue(p) - case p.tok.xkind - of pxAsgn: - closeContext(p) - getTok(p, a) - var b = assignmentExpression(p) - result = newNodeP(nkAsgn, p) - addSon(result, a, b) - of pxPlusAsgn: result = incdec(p, "inc", a) - of pxMinusAsgn: result = incdec(p, "dec", a) - of pxStarAsgn: result = asgnExpr(p, "*", a) - of pxSlashAsgn: result = asgnExpr(p, "/", a) - of pxModAsgn: result = asgnExpr(p, "mod", a) - of pxShlAsgn: result = asgnExpr(p, "shl", a) - of pxShrAsgn: result = asgnExpr(p, "shr", a) - of pxAmpAsgn: result = asgnExpr(p, "and", a) - of pxHatAsgn: result = asgnExpr(p, "xor", a) - of pxBarAsgn: result = asgnExpr(p, "or", a) + result = mangledIdent(tok.s, p) + result = optScope(p, result) + result = optAngle(p, result) + of pxIntLit: + result = newIntNodeP(nkIntLit, tok.iNumber, p) + setBaseFlags(result, tok.base) + of pxInt64Lit: + result = newIntNodeP(nkInt64Lit, tok.iNumber, p) + setBaseFlags(result, tok.base) + of pxFloatLit: + result = newFloatNodeP(nkFloatLit, tok.fNumber, p) + setBaseFlags(result, tok.base) + of pxStrLit: + result = newStrNodeP(nkStrLit, tok.s, p) + while p.tok.xkind == pxStrLit: + add(result.strVal, p.tok.s) + getTok(p, result) + of pxCharLit: + result = newIntNodeP(nkCharLit, ord(tok.s[0]), p) + of pxParLe: + try: + saveContext(p) + result = newNodeP(nkPar, p) + addSon(result, expression(p, 0)) + if p.tok.xkind != pxParRi: + raise + getTok(p, result) + if p.tok.xkind in {pxSymbol, pxIntLit, pxFloatLit, pxStrLit, pxCharLit}: + raise + closeContext(p) + except: + backtrackContext(p) + result = newNodeP(nkCast, p) + addSon(result, typeDesc(p)) + eat(p, pxParRi, result) + addSon(result, expression(p, 139)) + of pxPlusPlus: + result = newNodeP(nkCall, p) + addSon(result, newIdentNodeP("inc", p)) + addSon(result, expression(p, 139)) + of pxMinusMinus: + result = newNodeP(nkCall, p) + addSon(result, newIdentNodeP("dec", p)) + addSon(result, expression(p, 139)) + of pxAmp: + result = newNodeP(nkAddr, p) + addSon(result, expression(p, 139)) + of pxStar: + result = newNodeP(nkBracketExpr, p) + addSon(result, expression(p, 139)) + of pxPlus: + result = newNodeP(nkPrefix, p) + addSon(result, newIdentNodeP("+", p)) + addSon(result, expression(p, 139)) + of pxMinus: + result = newNodeP(nkPrefix, p) + addSon(result, newIdentNodeP("-", p)) + addSon(result, expression(p, 139)) + of pxTilde: + result = newNodeP(nkPrefix, p) + addSon(result, newIdentNodeP("not", p)) + addSon(result, expression(p, 139)) + of pxNot: + result = newNodeP(nkPrefix, p) + addSon(result, newIdentNodeP("not", p)) + addSon(result, expression(p, 139)) else: - backtrackContext(p) - result = conditionalExpression(p) - -proc shiftExpression(p: var TParser): PNode = - result = additiveExpression(p) - while p.tok.xkind in {pxShl, pxShr}: - var op = if p.tok.xkind == pxShl: "shl" else: "shr" - getTok(p, result) - var a = result - var b = additiveExpression(p) - result = newBinary(op, a, b, p) + # probably from a failed sub expression attempt, try a type cast + raise newException(E_Base, "not " & $tok) -proc relationalExpression(p: var TParser): PNode = - result = shiftExpression(p) - # Nimrod uses ``<`` and ``<=``, etc. too: - while p.tok.xkind in {pxLt, pxLe, pxGt, pxGe}: - var op = tokKindToStr(p.tok.xkind) - getTok(p, result) - var a = result - var b = shiftExpression(p) - result = newBinary(op, a, b, p) +proc leftBindingPower(p : var TParser, tok : ref TToken) : int = + #echo "lbp ", $tok[] + case tok.xkind: + of pxComma: + return 10 + # throw == 20 + of pxAsgn, pxPlusAsgn, pxMinusAsgn, pxStarAsgn, pxSlashAsgn, pxModAsgn, pxShlAsgn, pxShrAsgn, pxAmpAsgn, pxHatAsgn, pxBarAsgn: + return 30 + of pxConditional: + return 40 + of pxBarBar: + return 50 + of pxAmpAmp: + return 60 + of pxBar: + return 70 + of pxHat: + return 80 + of pxAmp: + return 90 + of pxEquals, pxNeq: + return 100 + of pxLt, pxLe, pxGt, pxGe: + return 110 + of pxShl, pxShr: + return 120 + of pxPlus, pxMinus: + return 130 + of pxStar, pxSlash, pxMod: + return 140 + # .* ->* == 150 + of pxPlusPlus, pxMinusMinus, pxParLe, pxDot, pxArrow, pxBracketLe: + return 160 + # :: == 170 + else: + return 0 -proc equalityExpression(p: var TParser): PNode = - result = relationalExpression(p) - # Nimrod uses ``==`` and ``!=`` too: - while p.tok.xkind in {pxEquals, pxNeq}: - var op = tokKindToStr(p.tok.xkind) - getTok(p, result) - var a = result - var b = relationalExpression(p) - result = newBinary(op, a, b, p) +proc buildStmtList(a: PNode): PNode -proc andExpression(p: var TParser): PNode = - result = equalityExpression(p) - while p.tok.xkind == pxAmp: - getTok(p, result) - var a = result - var b = equalityExpression(p) - result = newBinary("and", a, b, p) - -proc exclusiveOrExpression(p: var TParser): PNode = - result = andExpression(p) - while p.tok.xkind == pxHat: - getTok(p, result) - var a = result - var b = andExpression(p) - result = newBinary("^", a, b, p) - -proc inclusiveOrExpression(p: var TParser): PNode = - result = exclusiveOrExpression(p) - while p.tok.xkind == pxBar: - getTok(p, result) - var a = result - var b = exclusiveOrExpression(p) - result = newBinary("or", a, b, p) - -proc logicalAndExpression(p: var TParser): PNode = - result = inclusiveOrExpression(p) - while p.tok.xkind == pxAmpAmp: - getTok(p, result) - var a = result - var b = inclusiveOrExpression(p) - result = newBinary("and", a, b, p) - -proc logicalOrExpression(p: var TParser): PNode = - result = logicalAndExpression(p) - while p.tok.xkind == pxBarBar: - getTok(p, result) - var a = result - var b = logicalAndExpression(p) - result = newBinary("or", a, b, p) - -proc conditionalExpression(p: var TParser): PNode = - result = logicalOrExpression(p) - if p.tok.xkind == pxConditional: - getTok(p, result) # skip '?' - var a = result - var b = expression(p) - eat(p, pxColon, b) - var c = conditionalExpression(p) +proc leftExpression(p : var TParser, tok : TToken, left : PNode) : PNode = + #echo "led ", $tok + case tok.xkind: + of pxComma: # 10 + # not supported as an expression, turns into a statement list + result = buildStmtList(left) + addSon(result, expression(p, 0)) + # throw == 20 + of pxAsgn: # 30 + result = newNodeP(nkAsgn, p) + addSon(result, left, expression(p, 29)) + of pxPlusAsgn: # 30 + result = newNodeP(nkCall, p) + addSon(result, newIdentNodeP(getIdent("inc"), p), left, expression(p, 29)) + of pxMinusAsgn: # 30 + result = newNodeP(nkCall, p) + addSon(result, newIdentNodeP(getIdent("dec"), p), left, expression(p, 29)) + of pxStarAsgn: # 30 + result = newNodeP(nkAsgn, p) + var right = expression(p, 29) + addSon(result, left, newBinary("*", copyTree(left), right, p)) + of pxSlashAsgn: # 30 + result = newNodeP(nkAsgn, p) + var right = expression(p, 29) + addSon(result, left, newBinary("/", copyTree(left), right, p)) + of pxModAsgn: # 30 + result = newNodeP(nkAsgn, p) + var right = expression(p, 29) + addSon(result, left, newBinary("mod", copyTree(left), right, p)) + of pxShlAsgn: # 30 + result = newNodeP(nkAsgn, p) + var right = expression(p, 29) + addSon(result, left, newBinary("shl", copyTree(left), right, p)) + of pxShrAsgn: # 30 + result = newNodeP(nkAsgn, p) + var right = expression(p, 29) + addSon(result, left, newBinary("shr", copyTree(left), right, p)) + of pxAmpAsgn: # 30 + result = newNodeP(nkAsgn, p) + var right = expression(p, 29) + addSon(result, left, newBinary("and", copyTree(left), right, p)) + of pxHatAsgn: # 30 + result = newNodeP(nkAsgn, p) + var right = expression(p, 29) + addSon(result, left, newBinary("xor", copyTree(left), right, p)) + of pxBarAsgn: # 30 + result = newNodeP(nkAsgn, p) + var right = expression(p, 29) + addSon(result, left, newBinary("or", copyTree(left), right, p)) + of pxConditional: # 40 + var a = expression(p, 0) + eat(p, pxColon, a) + var b = expression(p, 39) result = newNodeP(nkIfExpr, p) var branch = newNodeP(nkElifExpr, p) - addSon(branch, a, b) + addSon(branch, left, a) addSon(result, branch) branch = newNodeP(nkElseExpr, p) - addSon(branch, c) + addSon(branch, b) addSon(result, branch) + of pxBarBar: # 50 + result = newBinary("or", left, expression(p, 50), p) + of pxAmpAmp: # 60 + result = newBinary("and", left, expression(p, 60), p) + of pxBar: # 70 + result = newBinary("or", left, expression(p, 70), p) + of pxHat: # 80 + result = newBinary("^", left, expression(p, 80), p) + of pxAmp: # 90 + result = newBinary("and", left, expression(p, 90), p) + of pxEquals: # 100 + result = newBinary("==", left, expression(p, 100), p) + of pxNeq: # 100 + result = newBinary("!=", left, expression(p, 100), p) + of pxLt: # 110 + result = newBinary("<", left, expression(p, 110), p) + of pxLe: # 110 + result = newBinary("<=", left, expression(p, 110), p) + of pxGt: # 110 + result = newBinary(">", left, expression(p, 110), p) + of pxGe: # 110 + result = newBinary(">=", left, expression(p, 110), p) + of pxShl: # 120 + result = newBinary("shl", left, expression(p, 120), p) + of pxShr: # 120 + result = newBinary("shr", left, expression(p, 120), p) + of pxPlus: # 130 + result = newNodeP(nkInfix, p) + addSon(result, newIdentNodeP("+", p), left) + addSon(result, expression(p, 130)) + of pxMinus: # 130 + result = newNodeP(nkInfix, p) + addSon(result, newIdentNodeP("+", p), left) + addSon(result, expression(p, 130)) + of pxStar: # 140 + result = newNodeP(nkInfix, p) + addSon(result, newIdentNodeP("*", p), left) + addSon(result, expression(p, 140)) + of pxSlash: # 140 + result = newNodeP(nkInfix, p) + addSon(result, newIdentNodeP("div", p), left) + addSon(result, expression(p, 140)) + of pxMod: # 140 + result = newNodeP(nkInfix, p) + addSon(result, newIdentNodeP("mod", p), left) + addSon(result, expression(p, 140)) + # .* ->* == 150 + of pxPlusPlus: # 160 + result = newNodeP(nkCall, p) + addSon(result, newIdentNodeP("inc", p), left) + of pxMinusMinus: # 160 + result = newNodeP(nkCall, p) + addSon(result, newIdentNodeP("dec", p), left) + of pxParLe: # 160 + result = newNodeP(nkCall, p) + addSon(result, left) + while p.tok.xkind != pxParRi: + var a = expression(p, 29) + addSon(result, a) + while p.tok.xkind == pxComma: + getTok(p, a) + a = expression(p, 29) + addSon(result, a) + eat(p, pxParRi, result) + of pxDot: # 160 + result = newNodeP(nkDotExpr, p) + addSon(result, left) + addSon(result, skipIdent(p)) + of pxArrow: # 160 + result = newNodeP(nkDotExpr, p) + addSon(result, left) + addSon(result, skipIdent(p)) + of pxBracketLe: # 160 + result = newNodeP(nkBracketExpr, p) + addSon(result, left, expression(p)) + eat(p, pxBracketRi, result) + # :: == 170 + else: + result = left + +proc expression*(p : var TParser, rbp : int = 0) : PNode = + var tok : TToken + + tok = p.tok[] + getTok(p, result) + + result = startExpression(p, tok) + + while rbp < leftBindingPower(p, p.tok): + tok = p.tok[] + getTok(p, result) + result = leftExpression(p, tok, result) # Statements From 97eaeb3aec42b2bf33190a8949d4673a547cdac6 Mon Sep 17 00:00:00 2001 From: Vincent Burns Date: Mon, 13 Jan 2014 01:16:24 -0500 Subject: [PATCH 05/25] for statements support comma expressions --- compiler/c2nim/cparse.nim | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/compiler/c2nim/cparse.nim b/compiler/c2nim/cparse.nim index bf785b13fa..cfda511670 100644 --- a/compiler/c2nim/cparse.nim +++ b/compiler/c2nim/cparse.nim @@ -1583,6 +1583,8 @@ proc parseStandaloneStruct(p: var TParser, isUnion: bool): PNode = backtrackContext(p) result = declaration(p) +proc embedStmts(sl, a: PNode) + proc parseFor(p: var TParser, result: PNode) = # 'for' '(' expression_statement expression_statement expression? ')' # statement @@ -1590,7 +1592,7 @@ proc parseFor(p: var TParser, result: PNode) = eat(p, pxParLe, result) var initStmt = declarationOrStatement(p) if initStmt.kind != nkEmpty: - addSon(result, initStmt) + embedStmts(result, initStmt) var w = newNodeP(nkWhileStmt, p) var condition = expressionStatement(p) if condition.kind == nkEmpty: condition = newIdentNodeP("true", p) @@ -1600,7 +1602,7 @@ proc parseFor(p: var TParser, result: PNode) = var loopBody = nestedStatement(p) if step.kind != nkEmpty: loopBody = buildStmtList(loopBody) - addSon(loopBody, step) + embedStmts(loopBody, step) addSon(w, loopBody) addSon(result, w) From 58855c2fc71294bd08ca6637473a33389ecc7de5 Mon Sep 17 00:00:00 2001 From: Vincent Burns Date: Mon, 13 Jan 2014 01:42:26 -0500 Subject: [PATCH 06/25] Support more proper do..while statements --- compiler/c2nim/cparse.nim | 48 +++++++++++++++++++++++++++++++-------- 1 file changed, 39 insertions(+), 9 deletions(-) diff --git a/compiler/c2nim/cparse.nim b/compiler/c2nim/cparse.nim index cfda511670..aa2dfa3e12 100644 --- a/compiler/c2nim/cparse.nim +++ b/compiler/c2nim/cparse.nim @@ -1498,19 +1498,51 @@ proc parseWhile(p: var TParser): PNode = eat(p, pxParRi, result) addSon(result, nestedStatement(p)) +proc embedStmts(sl, a: PNode) + proc parseDoWhile(p: var TParser): PNode = - # we only support ``do stmt while (0)`` as an idiom for - # ``block: stmt`` - result = newNodeP(nkBlockStmt, p) - getTok(p, result) # skip "do" - addSon(result, ast.emptyNode, nestedStatement(p)) + # parsing + result = newNodeP(nkWhileStmt, p) + getTok(p, result) + var stm = nestedStatement(p) eat(p, "while", result) eat(p, pxParLe, result) - if p.tok.xkind == pxIntLit and p.tok.iNumber == 0: getTok(p, result) - else: parMessage(p, errTokenExpected, "0") + var exp = expression(p) eat(p, pxParRi, result) if p.tok.xkind == pxSemicolon: getTok(p) + # while true: + # stmt + # if not expr: + # break + addSon(result, newIdentNodeP("true", p)) + + stm = buildStmtList(stm) + + # get the last exp if it is a stmtlist + var cleanedExp = exp + if exp.kind == nkStmtList: + cleanedExp = exp.sons[exp.len-1] + exp.sons = exp.sons[0..exp.len-2] + embedStmts(stm, exp) + + var notExp = newNodeP(nkPrefix, p) + addSon(notExp, newIdentNodeP("not", p)) + addSon(notExp, cleanedExp) + + var brkStm = newNodeP(nkBreakStmt, p) + addSon(brkStm, ast.emptyNode) + + var ifStm = newNodeP(nkIfStmt, p) + var ifBranch = newNodeP(nkElifBranch, p) + addSon(ifBranch, notExp) + addSon(ifBranch, brkStm) + addSon(ifStm, ifBranch) + + embedStmts(stm, ifStm) + + addSon(result, stm) + proc declarationOrStatement(p: var TParser): PNode = if p.tok.xkind != pxSymbol: result = expressionStatement(p) @@ -1583,8 +1615,6 @@ proc parseStandaloneStruct(p: var TParser, isUnion: bool): PNode = backtrackContext(p) result = declaration(p) -proc embedStmts(sl, a: PNode) - proc parseFor(p: var TParser, result: PNode) = # 'for' '(' expression_statement expression_statement expression? ')' # statement From 5f905865bed2080c49b29dd48decdd24c4702cc9 Mon Sep 17 00:00:00 2001 From: Vincent Burns Date: Mon, 13 Jan 2014 01:51:36 -0500 Subject: [PATCH 07/25] Fix for some comments during if statements added test files --- compiler/c2nim/cparse.nim | 2 +- compiler/c2nim/tests/vincent.c | 21 +++++++++++++++++++++ compiler/c2nim/tests/vincent.h | 3 +++ 3 files changed, 25 insertions(+), 1 deletion(-) create mode 100644 compiler/c2nim/tests/vincent.c create mode 100644 compiler/c2nim/tests/vincent.h diff --git a/compiler/c2nim/cparse.nim b/compiler/c2nim/cparse.nim index aa2dfa3e12..30cee180a7 100644 --- a/compiler/c2nim/cparse.nim +++ b/compiler/c2nim/cparse.nim @@ -1473,12 +1473,12 @@ proc parseIf(p: var TParser): PNode = while true: getTok(p) # skip ``if`` var branch = newNodeP(nkElifBranch, p) - skipCom(p, branch) eat(p, pxParLe, branch) addSon(branch, expression(p)) eat(p, pxParRi, branch) addSon(branch, nestedStatement(p)) addSon(result, branch) + skipCom(p, branch) if p.tok.s == "else": getTok(p, result) if p.tok.s != "if": diff --git a/compiler/c2nim/tests/vincent.c b/compiler/c2nim/tests/vincent.c new file mode 100644 index 0000000000..b9436c39b3 --- /dev/null +++ b/compiler/c2nim/tests/vincent.c @@ -0,0 +1,21 @@ +#include +#include + +int rand(void); + +int main() { + float f = .2, + g = 2., + h = 1.0+rand(), + i = 1.0e+3; + int j, a; + for(j = 0, a = 10; j < 0; j++, a++) ; + do { + printf("howdy"); + } while(--i, 0); + if(1) + printf("1"); // error from this comment + else + printf("2"); + return '\x00'; +} diff --git a/compiler/c2nim/tests/vincent.h b/compiler/c2nim/tests/vincent.h new file mode 100644 index 0000000000..b4e761ee14 --- /dev/null +++ b/compiler/c2nim/tests/vincent.h @@ -0,0 +1,3 @@ +struct foo { + int x,y,z; +}; From d9a61c13dd4a355a3898b4b852d078a2e1b5f0fd Mon Sep 17 00:00:00 2001 From: Vincent Burns Date: Mon, 13 Jan 2014 02:01:10 -0500 Subject: [PATCH 08/25] Fix for expression parsing, 'new' is a valid C symbol --- compiler/c2nim/cparse.nim | 15 +++++++++------ 1 file changed, 9 insertions(+), 6 deletions(-) diff --git a/compiler/c2nim/cparse.nim b/compiler/c2nim/cparse.nim index 30cee180a7..df263ee4e8 100644 --- a/compiler/c2nim/cparse.nim +++ b/compiler/c2nim/cparse.nim @@ -1152,7 +1152,7 @@ proc startExpression(p : var TParser, tok : TToken) : PNode = eat(p, pxParLe) addSon(result, typeDesc(p)) eat(p, pxParRi) - elif tok.s == "new" or tok.s == "delete" and pfCpp in p.options.flags: + elif (tok.s == "new" or tok.s == "delete") and pfCpp in p.options.flags: var opr = tok.s result = newNodeP(nkCall, p) if p.tok.xkind == pxBracketLe: @@ -2096,9 +2096,12 @@ proc statement(p: var TParser): PNode = assert result != nil proc parseUnit(p: var TParser): PNode = - result = newNodeP(nkStmtList, p) - getTok(p) # read first token - while p.tok.xkind != pxEof: - var s = statement(p) - if s.kind != nkEmpty: embedStmts(result, s) + try: + result = newNodeP(nkStmtList, p) + getTok(p) # read first token + while p.tok.xkind != pxEof: + var s = statement(p) + if s.kind != nkEmpty: embedStmts(result, s) + except: + parMessage(p, errGenerated, "Uncaught exception raised during parsing") From d35dedf041d1ded5843a064c855e8e36dc194221 Mon Sep 17 00:00:00 2001 From: Vincent Burns Date: Tue, 14 Jan 2014 11:22:59 -0500 Subject: [PATCH 09/25] Slightly better type parsing for parameters and cast expressions --- compiler/c2nim/cparse.nim | 48 +++++++++++++++++++++++++++++----- compiler/c2nim/tests/vincent.c | 5 ++++ 2 files changed, 46 insertions(+), 7 deletions(-) diff --git a/compiler/c2nim/cparse.nim b/compiler/c2nim/cparse.nim index df263ee4e8..aa9929139c 100644 --- a/compiler/c2nim/cparse.nim +++ b/compiler/c2nim/cparse.nim @@ -651,6 +651,22 @@ proc parseTypeSuffix(p: var TParser, typ: PNode): PNode = proc typeDesc(p: var TParser): PNode = result = pointer(p, typeAtom(p)) +proc abstractDeclarator(p: var TParser, a: PNode): PNode + +proc directAbstractDeclarator(p: var TParser, a: PNode): PNode = + if p.tok.xkind == pxParLe: + getTok(p, a) + if p.tok.xkind in {pxStar, pxAmp, pxAmpAmp}: + result = abstractDeclarator(p, a) + eat(p, pxParRi, result) + return parseTypeSuffix(p, a) + +proc abstractDeclarator(p: var TParser, a: PNode): PNode = + return directAbstractDeclarator(p, pointer(p, a)) + +proc typeName(p: var TParser): PNode = + return abstractDeclarator(p, typeAtom(p)) + proc parseField(p: var TParser, kind: TNodeKind): PNode = if p.tok.xkind == pxParLe: getTok(p, nil) @@ -716,18 +732,36 @@ proc parseStruct(p: var TParser, isUnion: bool): PNode = else: addSon(result, newNodeP(nkRecList, p)) +proc declarator(p: var TParser, a: PNode, ident: ptr PNode): PNode + +proc directDeclarator(p: var TParser, a: PNode, ident: ptr PNode): PNode = + case p.tok.xkind + of pxSymbol: + ident[] = skipIdent(p) + of pxParLe: + getTok(p, a) + if p.tok.xkind in {pxStar, pxAmp, pxAmpAmp, pxSymbol}: + result = declarator(p, a, ident) + eat(p, pxParRi, result) + else: + nil + return parseTypeSuffix(p, a) + +proc declarator(p: var TParser, a: PNode, ident: ptr PNode): PNode = + return directDeclarator(p, pointer(p, a), ident) + +# parameter-declaration +# declaration-specifiers declarator +# declaration-specifiers asbtract-declarator(opt) proc parseParam(p: var TParser, params: PNode) = var typ = typeDesc(p) # support for ``(void)`` parameter list: if typ.kind == nkNilLit and p.tok.xkind == pxParRi: return var name: PNode - if p.tok.xkind == pxSymbol: - name = skipIdent(p) - else: - # generate a name for the formal parameter: + typ = declarator(p, typ, addr name) + if name == nil: var idx = sonsLen(params)+1 name = newIdentNodeP("a" & $idx, p) - typ = parseTypeSuffix(p, typ) var x = newNodeP(nkIdentDefs, p) addSon(x, name, typ) if p.tok.xkind == pxAsgn: @@ -1150,7 +1184,7 @@ proc startExpression(p : var TParser, tok : TToken) : PNode = except: backtrackContext(p) eat(p, pxParLe) - addSon(result, typeDesc(p)) + addSon(result, typeName(p)) eat(p, pxParRi) elif (tok.s == "new" or tok.s == "delete") and pfCpp in p.options.flags: var opr = tok.s @@ -1200,7 +1234,7 @@ proc startExpression(p : var TParser, tok : TToken) : PNode = except: backtrackContext(p) result = newNodeP(nkCast, p) - addSon(result, typeDesc(p)) + addSon(result, typeName(p)) eat(p, pxParRi, result) addSon(result, expression(p, 139)) of pxPlusPlus: diff --git a/compiler/c2nim/tests/vincent.c b/compiler/c2nim/tests/vincent.c index b9436c39b3..a30077f77d 100644 --- a/compiler/c2nim/tests/vincent.c +++ b/compiler/c2nim/tests/vincent.c @@ -3,6 +3,11 @@ int rand(void); +int id(void (*f)(void)) { + f(); + ((void (*)(int))f)(10); +} + int main() { float f = .2, g = 2., From 53953475825e1ab743a8d02ca0fca6b4eeee3ff4 Mon Sep 17 00:00:00 2001 From: Vincent Burns Date: Tue, 14 Jan 2014 12:05:14 -0500 Subject: [PATCH 10/25] removed hack for return statement --- compiler/c2nim/cparse.nim | 16 +++++++--------- compiler/c2nim/tests/vincent.c | 7 +++++++ 2 files changed, 14 insertions(+), 9 deletions(-) diff --git a/compiler/c2nim/cparse.nim b/compiler/c2nim/cparse.nim index aa9929139c..8a8eeeb059 100644 --- a/compiler/c2nim/cparse.nim +++ b/compiler/c2nim/cparse.nim @@ -2038,6 +2038,10 @@ proc parseStandaloneClass(p: var TParser, isStruct: bool): PNode = result = declaration(p) p.currentClass = oldClass +proc unwrap(a: PNode): PNode = + if a.kind == nkPar: + return a.sons[0] + return a include cpp @@ -2072,16 +2076,10 @@ proc statement(p: var TParser): PNode = of "return": result = newNodeP(nkReturnStmt, p) getTok(p) - # special case for ``return (expr)`` because I hate the redundant - # parenthesis ;-) - if p.tok.xkind == pxParLe: - getTok(p, result) - addSon(result, expression(p)) - eat(p, pxParRi, result) - elif p.tok.xkind != pxSemicolon: - addSon(result, expression(p)) - else: + if p.tok.xkind == pxSemicolon: addSon(result, ast.emptyNode) + else: + addSon(result, unwrap(expression(p))) eat(p, pxSemicolon) of "enum": result = enumSpecifier(p) of "typedef": result = parseTypeDef(p) diff --git a/compiler/c2nim/tests/vincent.c b/compiler/c2nim/tests/vincent.c index a30077f77d..24c6d6425f 100644 --- a/compiler/c2nim/tests/vincent.c +++ b/compiler/c2nim/tests/vincent.c @@ -3,9 +3,16 @@ int rand(void); +int id2(void) { + return (int *)1; +} + int id(void (*f)(void)) { f(); ((void (*)(int))f)(10); + return 10; + return (20+1); + return (int *)id; } int main() { From aec9195c95b62a5f496c878cc3e40c03b0c7104f Mon Sep 17 00:00:00 2001 From: Vincent Burns Date: Tue, 14 Jan 2014 16:35:00 -0500 Subject: [PATCH 11/25] Applied Araq's suggestions for c2nim --- compiler/c2nim/c2nim.nim | 4 ++-- compiler/c2nim/cparse.nim | 16 +++++++++------- 2 files changed, 11 insertions(+), 9 deletions(-) diff --git a/compiler/c2nim/c2nim.nim b/compiler/c2nim/c2nim.nim index c4fc8ad672..9b12b9e47f 100644 --- a/compiler/c2nim/c2nim.nim +++ b/compiler/c2nim/c2nim.nim @@ -49,8 +49,8 @@ proc parse(infile: string, options: PParserOptions): PNode = proc main(infile, outfile: string, options: PParserOptions, spliceHeader: bool) = var start = getTime() - if spliceHeader and infile[infile.len-2 .. infile.len] == ".c" and existsFile(infile[0 .. infile.len-2] & "h"): - var header_module = parse(infile[0 .. infile.len-2] & "h", options) + if spliceHeader and infile.splitFile.ext == ".c" and existsFile(infile.changeFileExt(".h")): + var header_module = parse(infile.changeFileExt(".h"), options) var source_module = parse(infile, options) for n in source_module: addson(header_module, n) diff --git a/compiler/c2nim/cparse.nim b/compiler/c2nim/cparse.nim index 8a8eeeb059..ffab05788f 100644 --- a/compiler/c2nim/cparse.nim +++ b/compiler/c2nim/cparse.nim @@ -61,6 +61,8 @@ type TReplaceTuple* = array[0..1, string] + ERetryParsing = object of ESynch + proc newParserOptions*(): PParserOptions = new(result) result.prefixes = @[] @@ -1181,7 +1183,7 @@ proc startExpression(p : var TParser, tok : TToken) : PNode = try: addSon(result, expression(p, 139)) closeContext(p) - except: + except ERetryParsing: backtrackContext(p) eat(p, pxParLe) addSon(result, typeName(p)) @@ -1226,12 +1228,12 @@ proc startExpression(p : var TParser, tok : TToken) : PNode = result = newNodeP(nkPar, p) addSon(result, expression(p, 0)) if p.tok.xkind != pxParRi: - raise + raise newException(ERetryParsing, "expected a ')'") getTok(p, result) if p.tok.xkind in {pxSymbol, pxIntLit, pxFloatLit, pxStrLit, pxCharLit}: - raise + raise newException(ERetryParsing, "expected a non literal token") closeContext(p) - except: + except ERetryParsing: backtrackContext(p) result = newNodeP(nkCast, p) addSon(result, typeName(p)) @@ -1269,7 +1271,7 @@ proc startExpression(p : var TParser, tok : TToken) : PNode = addSon(result, expression(p, 139)) else: # probably from a failed sub expression attempt, try a type cast - raise newException(E_Base, "not " & $tok) + raise newException(ERetryParsing, "did not expect " & $tok) proc leftBindingPower(p : var TParser, tok : ref TToken) : int = #echo "lbp ", $tok[] @@ -2134,6 +2136,6 @@ proc parseUnit(p: var TParser): PNode = while p.tok.xkind != pxEof: var s = statement(p) if s.kind != nkEmpty: embedStmts(result, s) - except: - parMessage(p, errGenerated, "Uncaught exception raised during parsing") + except ERetryParsing: + parMessage(p, errGenerated, "Uncaught parsing exception raised") From db7d0e6a66f1c9fe45d4e584403f450a22fd90a3 Mon Sep 17 00:00:00 2001 From: Grzegorz Adam Hankiewicz Date: Mon, 30 Dec 2013 12:18:46 +0100 Subject: [PATCH 12/25] Adds using statement to the one and only true index. --- doc/manual.txt | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/doc/manual.txt b/doc/manual.txt index 2d8feca17c..88a7f38157 100644 --- a/doc/manual.txt +++ b/doc/manual.txt @@ -2206,12 +2206,12 @@ Instead of: Using statement --------------- -The using statement provides syntactic convenience for procs that heavily use a -single contextual parameter. When applied to a variable or a constant, it will -instruct Nimrod to automatically consider the used symbol as a hidden leading -parameter for any procedure calls, following the using statement in the current -scope. Thus, it behaves much like the hidden `this` parameter available in some -object-oriented programming languages. +The `using statement`:idx: provides syntactic convenience for procs that +heavily use a single contextual parameter. When applied to a variable or a +constant, it will instruct Nimrod to automatically consider the used symbol as +a hidden leading parameter for any procedure calls, following the using +statement in the current scope. Thus, it behaves much like the hidden `this` +parameter available in some object-oriented programming languages. .. code-block:: nimrod From 338a93f1197fe135e31b13865ab0ef6aa9ee9864 Mon Sep 17 00:00:00 2001 From: Grzegorz Adam Hankiewicz Date: Mon, 30 Dec 2013 12:47:01 +0100 Subject: [PATCH 13/25] Adds docstrings to lines() iterators. --- lib/system.nim | 26 ++++++++++++++++++++++++-- 1 file changed, 24 insertions(+), 2 deletions(-) diff --git a/lib/system.nim b/lib/system.nim index 56bb6fe75c..0257670f4c 100644 --- a/lib/system.nim +++ b/lib/system.nim @@ -2236,8 +2236,19 @@ when not defined(JS): #and not defined(NimrodVM): when hostOS != "standalone": iterator lines*(filename: string): TaintedString {.tags: [FReadIO].} = - ## Iterate over any line in the file named `filename`. - ## If the file does not exist `EIO` is raised. + ## Iterates over any line in the file named `filename`. + ## + ## If the file does not exist `EIO` is raised. The iterated lines will be + ## stripped off the trailing newline character(s). Example: + ## + ## .. code-block:: nimrod + ## import strutils + ## + ## proc transformLetters(filename: string) = + ## var buffer = "" + ## for line in filename.lines: + ## buffer.add(line.replace("a", "0") & '\x0A') + ## writeFile(filename, buffer) var f = open(filename) var res = TaintedString(newStringOfCap(80)) while f.readLine(res): yield res @@ -2245,6 +2256,17 @@ when not defined(JS): #and not defined(NimrodVM): iterator lines*(f: TFile): TaintedString {.tags: [FReadIO].} = ## Iterate over any line in the file `f`. + ## + ## The iterated lines will be stripped off the trailing newline + ## character(s). Example: + ## + ## .. code-block:: nimrod + ## proc countZeros(filename: TFile): tuple[lines, zeros: int] = + ## for line in filename.lines: + ## for letter in line: + ## if letter == '0': + ## result.zeros += 1 + ## result.lines += 1 var res = TaintedString(newStringOfCap(80)) while f.readLine(res): yield res From 9602349f308dd860f94b720f8f03476edf9cccaf Mon Sep 17 00:00:00 2001 From: Grzegorz Adam Hankiewicz Date: Mon, 30 Dec 2013 13:11:06 +0100 Subject: [PATCH 14/25] Adds note about procs and multiple variable assignment. --- doc/tut1.txt | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/doc/tut1.txt b/doc/tut1.txt index 2070c69d60..817bc69054 100644 --- a/doc/tut1.txt +++ b/doc/tut1.txt @@ -202,6 +202,12 @@ statement and all the variables will have the same value: echo "x ", x # outputs "x 42" echo "y ", y # outputs "y 3" +Note that declaring multiple variables with a single assignment which calls a +procedure can have unexpected results: the compiler will *unroll* the +assignments and end up calling the procedure several times. If the result of +the procedure depends on side effects, your variables may end up having +different values! For safety use only constant values. + Constants ========= From 0029832ba1797cf6c78defd9ece3d972929864b6 Mon Sep 17 00:00:00 2001 From: Grzegorz Adam Hankiewicz Date: Mon, 30 Dec 2013 13:29:28 +0100 Subject: [PATCH 15/25] Adds note about iterators having same signature as procs. --- doc/tut1.txt | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/doc/tut1.txt b/doc/tut1.txt index 817bc69054..91bff41d74 100644 --- a/doc/tut1.txt +++ b/doc/tut1.txt @@ -813,7 +813,11 @@ important differences: However, you can also use a ``closure`` iterator to get a different set of restrictions. See `first class iterators `_ -for details. +for details. Iterators can have the same name and parameters as a proc, +essentially they have their own namespace. Therefore it is common practice to +wrap iterators in procs of the same name which accumulate the result of the +iterator and return it as a sequence, like ``split`` from the `strutils module +`_. Basic types From 3cf46c2defb9ef92c7f7321349e41fdd3f93cc5f Mon Sep 17 00:00:00 2001 From: Grzegorz Adam Hankiewicz Date: Mon, 30 Dec 2013 13:34:20 +0100 Subject: [PATCH 16/25] Documents wrapping named arguments in curly braces. --- doc/subexes.txt | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/doc/subexes.txt b/doc/subexes.txt index 3565dbf433..10e0f4cc11 100644 --- a/doc/subexes.txt +++ b/doc/subexes.txt @@ -14,7 +14,9 @@ Thanks to its conditional construct ``$[0|1|2|else]`` it supports Notation meaning ===================== ===================================================== ``$#`` use first or next argument -``$name`` use named argument +``$name`` use named argument, you can wrap the named argument + in curly braces (eg. ``${name}``) to separate it from + the next characters. ``$1`` use first argument ``$-1`` use last argument ``${1..3}`` use arguments 1 to 3 From 74f94482cc61956bd61457ba9c7b9fd1b8c67ec9 Mon Sep 17 00:00:00 2001 From: Grzegorz Adam Hankiewicz Date: Mon, 30 Dec 2013 14:01:00 +0100 Subject: [PATCH 17/25] Adds parseopt2 module to documentation index. --- doc/lib.txt | 7 ++++++- lib/pure/parseopt.nim | 4 ++-- web/nimrod.ini | 2 +- 3 files changed, 9 insertions(+), 4 deletions(-) diff --git a/doc/lib.txt b/doc/lib.txt index 92a4a7c83c..e9aa9e857d 100644 --- a/doc/lib.txt +++ b/doc/lib.txt @@ -227,7 +227,12 @@ Parsers ------- * `parseopt `_ - The ``parseopt`` module implements a command line option parser. This + The ``parseopt`` module implements a command line option parser. + **Deprecated since version 0.9.3:** Use the `parseopt2 + `_ module instead. + +* `parseopt2 `_ + The ``parseopt2`` module implements a command line option parser. This supports long and short command options with optional values and command line arguments. diff --git a/lib/pure/parseopt.nim b/lib/pure/parseopt.nim index 6b2ee62825..68ae537c77 100644 --- a/lib/pure/parseopt.nim +++ b/lib/pure/parseopt.nim @@ -11,8 +11,8 @@ ## It supports one convenience iterator over all command line options and some ## lower-level features. ## -## DEPRECATED. Use parseopt2 instead as this version has issues with spaces -## in arguments. +## **Deprecated since version 0.9.3:** Use the `parseopt2 `_ +## module instead as this version has issues with spaces in arguments. {.deprecated.} {.push debugger: off.} diff --git a/web/nimrod.ini b/web/nimrod.ini index f10a4b2f21..6942f20a99 100644 --- a/web/nimrod.ini +++ b/web/nimrod.ini @@ -45,7 +45,7 @@ srcdoc2: "impure/re;pure/sockets" srcdoc: "system/threads.nim;system/channels.nim;js/dom" srcdoc2: "pure/os;pure/strutils;pure/math;pure/matchers;pure/algorithm" srcdoc2: "pure/complex;pure/times;pure/osproc;pure/pegs;pure/dynlib" -srcdoc2: "pure/parseopt;pure/hashes;pure/strtabs;pure/lexbase" +srcdoc2: "pure/parseopt;pure/parseopt2;pure/hashes;pure/strtabs;pure/lexbase" srcdoc2: "pure/parsecfg;pure/parsexml;pure/parsecsv;pure/parsesql" srcdoc2: "pure/streams;pure/terminal;pure/cgi;impure/web;pure/unicode" srcdoc2: "impure/zipfiles;pure/htmlgen;pure/parseutils;pure/browsers" From 457497980ce9b3ac1c0da7060a65d22b825e1f9c Mon Sep 17 00:00:00 2001 From: Grzegorz Adam Hankiewicz Date: Mon, 30 Dec 2013 16:34:39 +0100 Subject: [PATCH 18/25] Moves mongodb module to lower level wrapper group. --- doc/lib.txt | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/doc/lib.txt b/doc/lib.txt index e9aa9e857d..dbe8c6a043 100644 --- a/doc/lib.txt +++ b/doc/lib.txt @@ -386,9 +386,6 @@ Database support * `db_mongo `_ A higher level **mongodb** wrapper. -* `mongodb `_ - Lower level wrapper for the **mongodb** client C library. - Other ----- @@ -588,6 +585,8 @@ Database support Contains a wrapper for the mySQL API. * `sqlite3 `_ Contains a wrapper for SQLite 3 API. +* `mongodb `_ + Lower level wrapper for the **mongodb** client C library. * `odbcsql `_ interface to the ODBC driver. * `sphinx `_ From f3273757ed1a638f89d6b5f16258f929f434db1d Mon Sep 17 00:00:00 2001 From: Grzegorz Adam Hankiewicz Date: Mon, 30 Dec 2013 16:46:41 +0100 Subject: [PATCH 19/25] Removes links to modules recently removed from stdlib. --- doc/lib.txt | 102 +--------------------------------------------------- 1 file changed, 1 insertion(+), 101 deletions(-) diff --git a/doc/lib.txt b/doc/lib.txt index dbe8c6a043..ba0cb0a905 100644 --- a/doc/lib.txt +++ b/doc/lib.txt @@ -448,45 +448,6 @@ UNIX specific * `posix `_ Contains a wrapper for the POSIX standard. -* `cursorfont `_ - Part of the wrapper for X11. -* `keysym `_ - Part of the wrapper for X11. -* `x `_ - Part of the wrapper for X11. -* `xatom `_ - Part of the wrapper for X11. -* `xcms `_ - Part of the wrapper for X11. -* `xf86dga `_ - Part of the wrapper for X11. -* `xf86vmode `_ - Part of the wrapper for X11. -* `xi `_ - Part of the wrapper for X11. -* `xinerama `_ - Part of the wrapper for X11. -* `xkb `_ - Part of the wrapper for X11. -* `xkblib `_ - Part of the wrapper for X11. -* `xlib `_ - Part of the wrapper for X11. -* `xrandr `_ - Part of the wrapper for X11. -* `xrender `_ - Part of the wrapper for X11. -* `xresource `_ - Part of the wrapper for X11. -* `xshm `_ - Part of the wrapper for X11. -* `xutil `_ - Part of the wrapper for X11. -* `xv `_ - Part of the wrapper for X11. -* `xvlib `_ - Part of the wrapper for X11. - * `readline `_ Part of the wrapper for the GNU readline library. * `history `_ @@ -507,15 +468,6 @@ Regular expressions Graphics libraries ------------------ -* `cairo `_ - Wrapper for the cairo library. -* `cairoft `_ - Wrapper for the cairoft library. -* `cairowin32 `_ - Wrapper for the cairowin32 library. -* `cairoxlib `_ - Wrapper for the cairoxlib library. - * `sdl `_ Part of the wrapper for SDL. * `sdl_gfx `_ @@ -531,47 +483,10 @@ Graphics libraries * `smpeg `_ Part of the wrapper for SDL. -* `gl `_ - Part of the wrapper for OpenGL. -* `glext `_ - Part of the wrapper for OpenGL. -* `glu `_ - Part of the wrapper for OpenGL. -* `glut `_ - Part of the wrapper for OpenGL. -* `glx `_ - Part of the wrapper for OpenGL. -* `wingl `_ - Part of the wrapper for OpenGL. - -* `opengl `_ - New wrapper for OpenGL supporting up to version 4.2. - GUI libraries ------------- -* `atk `_ - Wrapper for the atk library. -* `gdk2 `_ - Wrapper for the gdk2 library. -* `gdk2pixbuf `_ - Wrapper for the gdk2pixbuf library. -* `gdkglext `_ - Wrapper for the gdkglext library. -* `glib2 `_ - Wrapper for the glib2 library. -* `gtk2 `_ - Wrapper for the gtk2 library. -* `gtkglext `_ - Wrapper for the gtkglext library. -* `gtkhtml `_ - Wrapper for the gtkhtml library. -* `libglade2 `_ - Wrapper for the libglade2 library. -* `pango `_ - Wrapper for the pango library. -* `pangoutils `_ - Wrapper for the pangoutils library. + * `iup `_ Wrapper of the IUP GUI library. @@ -616,21 +531,6 @@ Network Programming and Internet Protocols Wrapper for OpenSSL. -Scripting languages -------------------- - -* `lua `_ - Part of the wrapper for Lua. -* `lualib `_ - Part of the wrapper for Lua. -* `lauxlib `_ - Part of the wrapper for Lua. -* `tcl `_ - Wrapper for the TCL programming language. -* `python `_ - Wrapper for the Python programming language. - - Data Compression and Archiving ------------------------------ From 077b8327ecefb201f0937263fc21e4a9f28c16a8 Mon Sep 17 00:00:00 2001 From: Grzegorz Adam Hankiewicz Date: Fri, 3 Jan 2014 22:59:57 +0100 Subject: [PATCH 20/25] Documents rstgen index related procs. --- lib/packages/docutils/rstgen.nim | 13 ++++++++++++- 1 file changed, 12 insertions(+), 1 deletion(-) diff --git a/lib/packages/docutils/rstgen.nim b/lib/packages/docutils/rstgen.nim index 988338da19..5afd703192 100644 --- a/lib/packages/docutils/rstgen.nim +++ b/lib/packages/docutils/rstgen.nim @@ -44,7 +44,7 @@ type splitAfter*: int # split too long entries in the TOC tocPart*: seq[TTocEntry] hasToc*: bool - theIndex: string + theIndex: string # Contents of the index file to be dumped at the end. options*: TRstParseOptions findFile*: TFindFileHandler msgHandler*: TMsgHandler @@ -111,6 +111,10 @@ proc initRstGenerator*(g: var TRstGenerator, target: TOutputTarget, for i in low(g.meta)..high(g.meta): g.meta[i] = "" proc writeIndexFile*(g: var TRstGenerator, outfile: string) = + ## Writes the current index buffer to the specified output file. + ## + ## You previously need to add entries to the index with the ``setIndexTerm`` + ## proc. If the index is empty the file won't be created. if g.theIndex.len > 0: writeFile(outfile, g.theIndex) proc addXmlChar(dest: var string, c: char) = @@ -224,6 +228,13 @@ proc renderAux(d: PDoc, n: PRstNode, frmtA, frmtB: string, result: var string) = # ---------------- index handling -------------------------------------------- proc setIndexTerm*(d: var TRstGenerator, id, term: string) = + ## Adds a `term` to the index using the specified hyperlink identifier. + ## + ## The ``d.theIndex`` string will be used to append the term in the format + ## ``termfile#id``. The anchor will be the based on the name of the file + ## currently being parsed plus the `id`, which will be appended after a hash. + ## + ## The index won't be written to disk unless you call ``writeIndexFile``. d.theIndex.add(term) d.theIndex.add('\t') let htmlFile = changeFileExt(extractFilename(d.filename), HtmlExt) From 7aa263bebf946339a71cb7d153ea98d3c8302839 Mon Sep 17 00:00:00 2001 From: Grzegorz Adam Hankiewicz Date: Mon, 6 Jan 2014 20:41:42 +0100 Subject: [PATCH 21/25] Duplicates string literal table for character literals. Hopefully the index spamming will lead more people here. --- doc/manual.txt | 31 +++++++++++++++++++++++++++---- 1 file changed, 27 insertions(+), 4 deletions(-) diff --git a/doc/manual.txt b/doc/manual.txt index 88a7f38157..d9849f0441 100644 --- a/doc/manual.txt +++ b/doc/manual.txt @@ -312,13 +312,36 @@ Character literals ------------------ Character literals are enclosed in single quotes ``''`` and can contain the -same escape sequences as strings - with one exception: ``\n`` is not allowed -as it may be wider than one character (often it is the pair CR/LF for example). +same escape sequences as strings - with one exception: `newline`:idx: (``\n``) +is not allowed as it may be wider than one character (often it is the pair +CR/LF for example). Here are the valid `escape sequences`:idx: for character +literals: + +================== =================================================== + Escape sequence Meaning +================== =================================================== + ``\r``, ``\c`` `carriage return`:idx: + ``\l`` `line feed`:idx: + ``\f`` `form feed`:idx: + ``\t`` `tabulator`:idx: + ``\v`` `vertical tabulator`:idx: + ``\\`` `backslash`:idx: + ``\"`` `quotation mark`:idx: + ``\'`` `apostrophe`:idx: + ``\`` '0'..'9'+ `character with decimal value d`:idx:; + all decimal digits directly + following are used for the character + ``\a`` `alert`:idx: + ``\b`` `backspace`:idx: + ``\e`` `escape`:idx: `[ESC]`:idx: + ``\x`` HH `character with hex value HH`:idx:; + exactly two hex digits are allowed +================== =================================================== + A character is not an Unicode character but a single byte. The reason for this is efficiency: for the overwhelming majority of use-cases, the resulting programs will still handle UTF-8 properly as UTF-8 was specially designed for -this. -Another reason is that Nimrod can thus support ``array[char, int]`` or +this. Another reason is that Nimrod can thus support ``array[char, int]`` or ``set[char]`` efficiently as many algorithms rely on this feature. From 63ab238f5d00a7d4c9501ae635efcbb28f2e4e1a Mon Sep 17 00:00:00 2001 From: Grzegorz Adam Hankiewicz Date: Mon, 6 Jan 2014 20:52:30 +0100 Subject: [PATCH 22/25] Adds note about conflicts with using as a statement. --- doc/manual.txt | 18 ++++++++++++++++++ 1 file changed, 18 insertions(+) diff --git a/doc/manual.txt b/doc/manual.txt index d9849f0441..c8b1c079c2 100644 --- a/doc/manual.txt +++ b/doc/manual.txt @@ -2257,6 +2257,24 @@ from different modules having the same name. import windows, sdl using sdl.SetTimer +Note that ``using`` only *adds* to the current context, it doesn't remove or +replace, **neither** does it create a new scope. What this means is that if you +apply this to multiple variables the compiler will find conflicts in what +variable to use: + +.. code-block:: nimrod + var a, b = "kill it" + using a + add(" with fire") + using b + add(" with water") + echo a + echo b + +When the compiler reaches the second ``add`` call, both ``a`` and ``b`` could +be used with the proc, so you get ``Error: expression '(a|b)' has no type (or +is ambiguous)``. To solve this you would need to nest ``using`` with a +``block`` statement so as to control the reach of the ``using`` statement. If expression ------------- From 2c2174f2d0415f975adb0e1679822afe394eee7d Mon Sep 17 00:00:00 2001 From: Grzegorz Adam Hankiewicz Date: Tue, 14 Jan 2014 10:38:03 +0100 Subject: [PATCH 23/25] Clarifies system.lines() docstring. Amends c087f905134b249cf20cbabc4066fbfa62dd668a. --- lib/system.nim | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/lib/system.nim b/lib/system.nim index 0257670f4c..70c8a529a3 100644 --- a/lib/system.nim +++ b/lib/system.nim @@ -2238,8 +2238,8 @@ when not defined(JS): #and not defined(NimrodVM): iterator lines*(filename: string): TaintedString {.tags: [FReadIO].} = ## Iterates over any line in the file named `filename`. ## - ## If the file does not exist `EIO` is raised. The iterated lines will be - ## stripped off the trailing newline character(s). Example: + ## If the file does not exist `EIO` is raised. The trailing newline + ## character(s) are removed from the iterated lines. Example: ## ## .. code-block:: nimrod ## import strutils @@ -2257,8 +2257,8 @@ when not defined(JS): #and not defined(NimrodVM): iterator lines*(f: TFile): TaintedString {.tags: [FReadIO].} = ## Iterate over any line in the file `f`. ## - ## The iterated lines will be stripped off the trailing newline - ## character(s). Example: + ## The trailing newline character(s) are removed from the iterated lines. + ## Example: ## ## .. code-block:: nimrod ## proc countZeros(filename: TFile): tuple[lines, zeros: int] = From 42f152569db36ea6894caf11189be8f6fd06a482 Mon Sep 17 00:00:00 2001 From: Grzegorz Adam Hankiewicz Date: Tue, 14 Jan 2014 10:46:31 +0100 Subject: [PATCH 24/25] References TRune, links unicode modules where mentioned. Amends 0f3941b0013ea5d390586719f930fcf02b929f4d. --- doc/manual.txt | 11 +++++++---- 1 file changed, 7 insertions(+), 4 deletions(-) diff --git a/doc/manual.txt b/doc/manual.txt index c8b1c079c2..f6dd1f521e 100644 --- a/doc/manual.txt +++ b/doc/manual.txt @@ -342,7 +342,9 @@ A character is not an Unicode character but a single byte. The reason for this is efficiency: for the overwhelming majority of use-cases, the resulting programs will still handle UTF-8 properly as UTF-8 was specially designed for this. Another reason is that Nimrod can thus support ``array[char, int]`` or -``set[char]`` efficiently as many algorithms rely on this feature. +``set[char]`` efficiently as many algorithms rely on this feature. The `TRune` +type is used for Unicode characters, it can represent any Unicode character. +``TRune`` is declared in the `unicode module `_. Numerical constants @@ -773,7 +775,8 @@ designed for this. Another reason is that Nimrod can support ``array[char, int]`` or ``set[char]`` efficiently as many algorithms rely on this feature. The `TRune` type is used for Unicode characters, it can represent any Unicode -character. ``TRune`` is declared in the ``unicode`` module. +character. ``TRune`` is declared in the `unicode module `_. + @@ -870,8 +873,8 @@ arrays, they can be used in case statements: Per convention, all strings are UTF-8 strings, but this is not enforced. For example, when reading strings from binary files, they are merely a sequence of bytes. The index operation ``s[i]`` means the i-th *char* of ``s``, not the -i-th *unichar*. The iterator ``runes`` from the ``unicode`` -module can be used for iteration over all Unicode characters. +i-th *unichar*. The iterator ``runes`` from the `unicode module +`_ can be used for iteration over all Unicode characters. CString type From 74af6351b2b1888cc1437e158cf29851f8e6465e Mon Sep 17 00:00:00 2001 From: Hitesh Jasani Date: Thu, 16 Jan 2014 14:42:12 -0500 Subject: [PATCH 25/25] Add docs for constraining and qualifying imports. --- doc/tut1.txt | 35 +++++++++++++++++++++++++++++++++++ 1 file changed, 35 insertions(+) diff --git a/doc/tut1.txt b/doc/tut1.txt index 2070c69d60..52d46d9bd6 100644 --- a/doc/tut1.txt +++ b/doc/tut1.txt @@ -1582,6 +1582,17 @@ rules apply: write(stdout, x(3)) # ambiguous: which `x` is to call? +Excluding symbols +----------------- + +The normal ``import`` statement will bring in all exported symbols. +These can be limited by naming symbols which should be excluded with +the ``except`` qualifier. + +.. code-block:: nimrod + import mymodule except y + + From statement -------------- @@ -1592,6 +1603,30 @@ exported symbols. An alternative that only imports listed symbols is the .. code-block:: nimrod from mymodule import x, y, z +The ``from`` statement can also force namespace qualification on +symbols, thereby making symbols available, but needing to be qualified +to be used. + +.. code-block:: nimrod + from mymodule import x, y, z + + x() # use x without any qualification + +.. code-block:: nimrod + from mymodule import nil + + mymodule.x() # must qualify x with the module name as prefix + + x() # using x here without qualification is a compile error + +Since module names are generally long to be descriptive, you can also +define a shorter alias to use when qualifying symbols. + +.. code-block:: nimrod + from mymodule as m import nil + + m.x() # m is aliasing mymodule + Include statement -----------------