diff options
author | Araq <rumpf_a@web.de> | 2014-07-15 09:37:45 +0200 |
---|---|---|
committer | Araq <rumpf_a@web.de> | 2014-07-15 09:37:45 +0200 |
commit | 6d3b632b47d626f6915dc2cbef8745baf04b8d76 (patch) | |
tree | cd2097090977d241065d1415bc9d0845689063f8 /compiler | |
parent | 41bb0bf9dcccdfcebdb0f823fea8b2853b89ea4e (diff) | |
parent | 5f8ab1653ad38faf395daf1b8a70249f6954b1ba (diff) | |
download | Nim-6d3b632b47d626f6915dc2cbef8745baf04b8d76.tar.gz |
Merge branch 'new_spawn' into devel
Conflicts: todo.txt web/news.txt web/nimrod.ini
Diffstat (limited to 'compiler')
-rw-r--r-- | compiler/ast.nim | 3 | ||||
-rw-r--r-- | compiler/c2nim/c2nim.nim | 92 | ||||
-rw-r--r-- | compiler/c2nim/clex.nim | 787 | ||||
-rw-r--r-- | compiler/c2nim/cparse.nim | 2272 | ||||
-rw-r--r-- | compiler/c2nim/cpp.nim | 347 | ||||
-rw-r--r-- | compiler/c2nim/nimrod.cfg | 4 | ||||
-rw-r--r-- | compiler/c2nim/tests/enum.h | 40 | ||||
-rw-r--r-- | compiler/c2nim/tests/matrix.h | 240 | ||||
-rw-r--r-- | compiler/c2nim/tests/struct_anonym.h | 27 | ||||
-rw-r--r-- | compiler/c2nim/tests/systest.c | 622 | ||||
-rw-r--r-- | compiler/c2nim/tests/systest2.c | 17 | ||||
-rw-r--r-- | compiler/c2nim/tests/vincent.c | 33 | ||||
-rw-r--r-- | compiler/c2nim/tests/vincent.h | 3 | ||||
-rw-r--r-- | compiler/lambdalifting.nim | 745 | ||||
-rw-r--r-- | compiler/lowerings.nim | 43 | ||||
-rw-r--r-- | compiler/magicsys.nim | 2 | ||||
-rw-r--r-- | compiler/pas2nim/nimrod.cfg | 4 | ||||
-rw-r--r-- | compiler/pas2nim/pas2nim.nim | 64 | ||||
-rw-r--r-- | compiler/pas2nim/paslex.nim | 570 | ||||
-rw-r--r-- | compiler/pas2nim/pasparse.nim | 1513 |
20 files changed, 487 insertions, 6941 deletions
diff --git a/compiler/ast.nim b/compiler/ast.nim index 516954b88..9b8218071 100644 --- a/compiler/ast.nim +++ b/compiler/ast.nim @@ -417,6 +417,7 @@ type # efficiency nfTransf, # node has been transformed nfSem # node has been checked for semantics + nfLL # node has gone through lambda lifting nfDotField # the call can use a dot operator nfDotSetter # the call can use a setter dot operarator nfExplicitCall # x.y() was used instead of x.y @@ -1505,7 +1506,7 @@ proc isGenericRoutine*(s: PSym): bool = proc skipGenericOwner*(s: PSym): PSym = internalAssert s.kind in skProcKinds ## Generic instantiations are owned by their originating generic - ## symbol. This proc skips such owners and goes straigh to the owner + ## symbol. This proc skips such owners and goes straight to the owner ## of the generic itself (the module or the enclosing proc). result = if sfFromGeneric in s.flags: s.owner.owner else: s.owner diff --git a/compiler/c2nim/c2nim.nim b/compiler/c2nim/c2nim.nim deleted file mode 100644 index 9b12b9e47..000000000 --- a/compiler/c2nim/c2nim.nim +++ /dev/null @@ -1,92 +0,0 @@ -# -# -# c2nim - C to Nimrod source converter -# (c) Copyright 2013 Andreas Rumpf -# -# See the file "copying.txt", included in this -# distribution, for details about the copyright. -# - -import - strutils, os, times, parseopt, llstream, ast, renderer, options, msgs, - clex, cparse - -const - Version = NimrodVersion - Usage = """ -c2nim - C to Nimrod source converter - (c) 2013 Andreas Rumpf -Usage: c2nim [options] inputfile [options] -Options: - -o, --out:FILE set output filename - --cpp process C++ input file - --dynlib:SYMBOL import from dynlib: SYMBOL will be used for the import - --header:HEADER_FILE import from a HEADER_FILE (discouraged!) - --cdecl annotate procs with ``{.cdecl.}`` - --stdcall annotate procs with ``{.stdcall.}`` - --ref convert typ* to ref typ (default: ptr typ) - --prefix:PREFIX strip prefix for the generated Nimrod identifiers - (multiple --prefix options are supported) - --suffix:SUFFIX strip suffix for the generated Nimrod identifiers - (multiple --suffix options are supported) - --skipinclude do not convert ``#include`` to ``import`` - --typeprefixes generate ``T`` and ``P`` type prefixes - --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 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) - result = parseUnit(p) - closeParser(p) - -proc main(infile, outfile: string, options: PParserOptions, spliceHeader: bool) = - var start = getTime() - 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) - 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 - of cmdArgument: infile = key - of cmdLongOption, cmdShortOption: - case key.toLower - of "help", "h": - stdout.write(Usage) - quit(0) - of "version", "v": - 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) - of cmdEnd: assert(false) -if infile.len == 0: - # no filename has been given, so we show the help: - stdout.write(Usage) -else: - if outfile.len == 0: - outfile = changeFileExt(infile, "nim") - infile = addFileExt(infile, "h") - main(infile, outfile, parserOptions, spliceHeader) diff --git a/compiler/c2nim/clex.nim b/compiler/c2nim/clex.nim deleted file mode 100644 index 3934eea63..000000000 --- a/compiler/c2nim/clex.nim +++ /dev/null @@ -1,787 +0,0 @@ -# -# -# c2nim - C to Nimrod source converter -# (c) Copyright 2012 Andreas Rumpf -# -# See the file "copying.txt", included in this -# distribution, for details about the copyright. -# - -# This module implements an Ansi C scanner. This is an adaption from -# the scanner module. Keywords are not handled here, but in the parser to make -# it more flexible. - - -import - options, msgs, strutils, platform, nimlexbase, llstream - -const - MaxLineLength* = 80 # lines longer than this lead to a warning - numChars*: TCharSet = {'0'..'9', 'a'..'z', 'A'..'Z'} - SymChars*: TCharSet = {'a'..'z', 'A'..'Z', '0'..'9', '_', '\x80'..'\xFF'} - SymStartChars*: TCharSet = {'a'..'z', 'A'..'Z', '_', '\x80'..'\xFF'} - -type - TTokKind* = enum - pxInvalid, pxEof, - pxMacroParam, # fake token: macro parameter (with its index) - pxStarComment, # /* */ comment - pxLineComment, # // comment - pxDirective, # #define, etc. - pxDirectiveParLe, # #define m( with parle (yes, C is that ugly!) - pxDirConc, # ## - pxNewLine, # newline: end of directive - pxAmp, # & - pxAmpAmp, # && - pxAmpAsgn, # &= - pxAmpAmpAsgn, # &&= - pxBar, # | - pxBarBar, # || - pxBarAsgn, # |= - pxBarBarAsgn, # ||= - pxNot, # ! - pxPlusPlus, # ++ - pxMinusMinus, # -- - pxPlus, # + - pxPlusAsgn, # += - pxMinus, # - - pxMinusAsgn, # -= - pxMod, # % - pxModAsgn, # %= - pxSlash, # / - pxSlashAsgn, # /= - pxStar, # * - pxStarAsgn, # *= - pxHat, # ^ - pxHatAsgn, # ^= - pxAsgn, # = - pxEquals, # == - pxDot, # . - pxDotDotDot, # ... - pxLe, # <= - pxLt, # < - pxGe, # >= - pxGt, # > - pxNeq, # != - pxConditional, # ? - pxShl, # << - pxShlAsgn, # <<= - pxShr, # >> - pxShrAsgn, # >>= - pxTilde, # ~ - pxTildeAsgn, # ~= - pxArrow, # -> - pxScope, # :: - - pxStrLit, - pxCharLit, - pxSymbol, # a symbol - pxIntLit, - pxInt64Lit, # long constant like 0x70fffffff or out of int range - pxFloatLit, - pxParLe, pxBracketLe, pxCurlyLe, # this order is important - pxParRi, pxBracketRi, pxCurlyRi, # for macro argument parsing! - pxComma, pxSemiColon, pxColon, - pxAngleRi # '>' but determined to be the end of a - # template's angle bracket - TTokKinds* = set[TTokKind] - -type - TNumericalBase* = enum base10, base2, base8, base16 - TToken* = object - xkind*: TTokKind # the type of the token - s*: string # parsed symbol, char or string literal - iNumber*: BiggestInt # the parsed integer literal; - # if xkind == pxMacroParam: parameter's position - fNumber*: BiggestFloat # the parsed floating point literal - base*: TNumericalBase # the numerical base; only valid for int - # or float literals - next*: ref TToken # for C we need arbitrary look-ahead :-( - - TLexer* = object of TBaseLexer - fileIdx*: int32 - inDirective: bool - -proc getTok*(L: var TLexer, tok: var TToken) -proc printTok*(tok: TToken) -proc `$`*(tok: TToken): string -# implementation - -var - gLinesCompiled*: int - -proc fillToken(L: var TToken) = - L.xkind = pxInvalid - L.iNumber = 0 - L.s = "" - L.fNumber = 0.0 - L.base = base10 - -proc openLexer*(lex: var TLexer, filename: string, inputstream: PLLStream) = - openBaseLexer(lex, inputstream) - lex.fileIdx = filename.fileInfoIdx - -proc closeLexer*(lex: var TLexer) = - inc(gLinesCompiled, lex.LineNumber) - closeBaseLexer(lex) - -proc getColumn*(L: TLexer): int = - result = getColNumber(L, L.bufPos) - -proc getLineInfo*(L: TLexer): TLineInfo = - result = newLineInfo(L.fileIdx, L.linenumber, getColNumber(L, L.bufpos)) - -proc lexMessage*(L: TLexer, msg: TMsgKind, arg = "") = - msgs.GlobalError(getLineInfo(L), msg, arg) - -proc lexMessagePos(L: var TLexer, msg: TMsgKind, pos: int, arg = "") = - var info = newLineInfo(L.fileIdx, L.linenumber, pos - L.lineStart) - msgs.GlobalError(info, msg, arg) - -proc tokKindToStr*(k: TTokKind): string = - case k - of pxEof: result = "[EOF]" - of pxInvalid: result = "[invalid]" - of pxMacroParam: result = "[macro param]" - of pxStarComment, pxLineComment: result = "[comment]" - of pxStrLit: result = "[string literal]" - of pxCharLit: result = "[char literal]" - - of pxDirective, pxDirectiveParLe: result = "#" # #define, etc. - of pxDirConc: result = "##" - of pxNewLine: result = "[NewLine]" - of pxAmp: result = "&" # & - of pxAmpAmp: result = "&&" # && - of pxAmpAsgn: result = "&=" # &= - of pxAmpAmpAsgn: result = "&&=" # &&= - of pxBar: result = "|" # | - of pxBarBar: result = "||" # || - of pxBarAsgn: result = "|=" # |= - of pxBarBarAsgn: result = "||=" # ||= - of pxNot: result = "!" # ! - of pxPlusPlus: result = "++" # ++ - of pxMinusMinus: result = "--" # -- - of pxPlus: result = "+" # + - of pxPlusAsgn: result = "+=" # += - of pxMinus: result = "-" # - - of pxMinusAsgn: result = "-=" # -= - of pxMod: result = "%" # % - of pxModAsgn: result = "%=" # %= - of pxSlash: result = "/" # / - of pxSlashAsgn: result = "/=" # /= - of pxStar: result = "*" # * - of pxStarAsgn: result = "*=" # *= - of pxHat: result = "^" # ^ - of pxHatAsgn: result = "^=" # ^= - of pxAsgn: result = "=" # = - of pxEquals: result = "==" # == - of pxDot: result = "." # . - of pxDotDotDot: result = "..." # ... - of pxLe: result = "<=" # <= - of pxLt: result = "<" # < - of pxGe: result = ">=" # >= - of pxGt: result = ">" # > - of pxNeq: result = "!=" # != - of pxConditional: result = "?" - of pxShl: result = "<<" - of pxShlAsgn: result = "<<=" - of pxShr: result = ">>" - of pxShrAsgn: result = ">>=" - of pxTilde: result = "~" - of pxTildeAsgn: result = "~=" - of pxArrow: result = "->" - of pxScope: result = "::" - - of pxSymbol: result = "[identifier]" - of pxIntLit, pxInt64Lit: result = "[integer literal]" - of pxFloatLit: result = "[floating point literal]" - of pxParLe: result = "(" - of pxParRi: result = ")" - of pxBracketLe: result = "[" - of pxBracketRi: result = "]" - of pxComma: result = "," - of pxSemiColon: result = ";" - of pxColon: result = ":" - of pxCurlyLe: result = "{" - of pxCurlyRi: result = "}" - of pxAngleRi: result = "> [end of template]" - -proc `$`(tok: TToken): string = - case tok.xkind - of pxSymbol, pxInvalid, pxStarComment, pxLineComment, pxStrLit: result = tok.s - of pxIntLit, pxInt64Lit: result = $tok.iNumber - of pxFloatLit: result = $tok.fNumber - else: result = tokKindToStr(tok.xkind) - -proc printTok(tok: TToken) = - writeln(stdout, $tok) - -proc matchUnderscoreChars(L: var TLexer, tok: var TToken, chars: TCharSet) = - # matches ([chars]_)* - var pos = L.bufpos # use registers for pos, buf - var buf = L.buf - while true: - if buf[pos] in chars: - add(tok.s, buf[pos]) - inc(pos) - else: - break - if buf[pos] == '_': - add(tok.s, '_') - inc(pos) - L.bufPos = pos - -proc isFloatLiteral(s: string): bool = - for i in countup(0, len(s)-1): - if s[i] in {'.', 'e', 'E'}: - return true - -proc getNumber2(L: var TLexer, tok: var TToken) = - var pos = L.bufpos + 2 # skip 0b - tok.base = base2 - var xi: BiggestInt = 0 - var bits = 0 - while true: - case L.buf[pos] - of 'A'..'Z', 'a'..'z': - # ignore type suffix: - inc(pos) - of '2'..'9', '.': - lexMessage(L, errInvalidNumber) - inc(pos) - of '_': - inc(pos) - of '0', '1': - xi = `shl`(xi, 1) or (ord(L.buf[pos]) - ord('0')) - inc(pos) - inc(bits) - else: break - tok.iNumber = xi - if (bits > 32): tok.xkind = pxInt64Lit - else: tok.xkind = pxIntLit - L.bufpos = pos - -proc getNumber8(L: var TLexer, tok: var TToken) = - var pos = L.bufpos + 1 # skip 0 - tok.base = base8 - var xi: BiggestInt = 0 - var bits = 0 - while true: - case L.buf[pos] - of 'A'..'Z', 'a'..'z': - # ignore type suffix: - inc(pos) - of '8'..'9', '.': - lexMessage(L, errInvalidNumber) - inc(pos) - of '_': - inc(pos) - of '0'..'7': - xi = `shl`(xi, 3) or (ord(L.buf[pos]) - ord('0')) - inc(pos) - inc(bits) - else: break - tok.iNumber = xi - if (bits > 12): tok.xkind = pxInt64Lit - else: tok.xkind = pxIntLit - L.bufpos = pos - -proc getNumber16(L: var TLexer, tok: var TToken) = - var pos = L.bufpos + 2 # skip 0x - tok.base = base16 - var xi: BiggestInt = 0 - var bits = 0 - while true: - case L.buf[pos] - of 'G'..'Z', 'g'..'z': - # ignore type suffix: - inc(pos) - of '_': inc(pos) - of '0'..'9': - xi = `shl`(xi, 4) or (ord(L.buf[pos]) - ord('0')) - inc(pos) - inc(bits, 4) - of 'a'..'f': - xi = `shl`(xi, 4) or (ord(L.buf[pos]) - ord('a') + 10) - inc(pos) - inc(bits, 4) - of 'A'..'F': - xi = `shl`(xi, 4) or (ord(L.buf[pos]) - ord('A') + 10) - inc(pos) - inc(bits, 4) - else: break - tok.iNumber = xi - if bits > 32: tok.xkind = pxInt64Lit - 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 - if L.buf[L.bufpos] == '.': - add(tok.s, "0.") - inc(L.bufpos) - 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) - tok.xkind = pxFloatLit - else: - tok.iNumber = parseInt(tok.s) - if (tok.iNumber < low(int32)) or (tok.iNumber > high(int32)): - tok.xkind = pxInt64Lit - else: - tok.xkind = pxIntLit - except EInvalidValue: - lexMessage(L, errInvalidNumber, tok.s) - except EOverflow: - lexMessage(L, errNumberOutOfRange, tok.s) - # ignore type suffix: - while L.buf[L.bufpos] in {'A'..'Z', 'a'..'z'}: inc(L.bufpos) - -proc handleCRLF(L: var TLexer, pos: int): int = - case L.buf[pos] - of CR: result = nimlexbase.handleCR(L, pos) - of LF: result = nimlexbase.handleLF(L, pos) - else: result = pos - -proc escape(L: var TLexer, tok: var TToken, allowEmpty=false) = - inc(L.bufpos) # skip \ - case L.buf[L.bufpos] - of 'b', 'B': - add(tok.s, '\b') - inc(L.bufpos) - of 't', 'T': - add(tok.s, '\t') - inc(L.bufpos) - of 'n', 'N': - add(tok.s, '\L') - inc(L.bufpos) - of 'f', 'F': - add(tok.s, '\f') - inc(L.bufpos) - of 'r', 'R': - add(tok.s, '\r') - inc(L.bufpos) - of '\'': - add(tok.s, '\'') - inc(L.bufpos) - of '"': - add(tok.s, '"') - inc(L.bufpos) - of '\\': - add(tok.s, '\b') - inc(L.bufpos) - of '0'..'7': - var xi = ord(L.buf[L.bufpos]) - ord('0') - inc(L.bufpos) - if L.buf[L.bufpos] in {'0'..'7'}: - xi = (xi shl 3) or (ord(L.buf[L.bufpos]) - ord('0')) - inc(L.bufpos) - if L.buf[L.bufpos] in {'0'..'7'}: - 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) - -proc getCharLit(L: var TLexer, tok: var TToken) = - inc(L.bufpos) # skip ' - if L.buf[L.bufpos] == '\\': - escape(L, tok) - else: - add(tok.s, L.buf[L.bufpos]) - inc(L.bufpos) - if L.buf[L.bufpos] == '\'': - inc(L.bufpos) - else: - lexMessage(L, errMissingFinalQuote) - tok.xkind = pxCharLit - -proc getString(L: var TLexer, tok: var TToken) = - var pos = L.bufPos + 1 # skip " - var buf = L.buf # put `buf` in a register - var line = L.linenumber # save linenumber for better error message - while true: - case buf[pos] - of '\"': - inc(pos) - break - of CR: - pos = nimlexbase.HandleCR(L, pos) - buf = L.buf - of LF: - pos = nimlexbase.HandleLF(L, pos) - buf = L.buf - of nimlexbase.EndOfFile: - var line2 = L.linenumber - L.LineNumber = line - lexMessagePos(L, errClosingQuoteExpected, L.lineStart) - L.LineNumber = line2 - break - of '\\': - # we allow an empty \ for line concatenation, but we don't require it - # for line concatenation - L.bufpos = pos - escape(L, tok, allowEmpty=true) - pos = L.bufpos - else: - add(tok.s, buf[pos]) - inc(pos) - L.bufpos = pos - tok.xkind = pxStrLit - -proc getSymbol(L: var TLexer, tok: var TToken) = - var pos = L.bufpos - var buf = L.buf - while true: - var c = buf[pos] - if c notin SymChars: break - add(tok.s, c) - inc(pos) - L.bufpos = pos - tok.xkind = pxSymbol - -proc scanLineComment(L: var TLexer, tok: var TToken) = - var pos = L.bufpos - var buf = L.buf - # a comment ends if the next line does not start with the // on the same - # column after only whitespace - tok.xkind = pxLineComment - var col = getColNumber(L, pos) - while true: - inc(pos, 2) # skip // - add(tok.s, '#') - while not (buf[pos] in {CR, LF, nimlexbase.EndOfFile}): - add(tok.s, buf[pos]) - inc(pos) - pos = handleCRLF(L, pos) - buf = L.buf - var indent = 0 - while buf[pos] == ' ': - inc(pos) - inc(indent) - if (col == indent) and (buf[pos] == '/') and (buf[pos + 1] == '/'): - add(tok.s, "\n") - else: - break - L.bufpos = pos - -proc scanStarComment(L: var TLexer, tok: var TToken) = - var pos = L.bufpos - var buf = L.buf - tok.s = "#" - tok.xkind = pxStarComment - while true: - case buf[pos] - of CR, LF: - pos = handleCRLF(L, pos) - buf = L.buf - add(tok.s, "\n#") - # skip annoying stars as line prefix: (eg. - # /* - # * ugly comment <-- this star - # */ - while buf[pos] in {' ', '\t'}: - add(tok.s, ' ') - inc(pos) - if buf[pos] == '*' and buf[pos+1] != '/': inc(pos) - of '*': - inc(pos) - if buf[pos] == '/': - inc(pos) - break - else: - add(tok.s, '*') - of nimlexbase.EndOfFile: - lexMessage(L, errTokenExpected, "*/") - else: - add(tok.s, buf[pos]) - inc(pos) - L.bufpos = pos - -proc skip(L: var TLexer, tok: var TToken) = - var pos = L.bufpos - var buf = L.buf - while true: - case buf[pos] - of '\\': - # Ignore \ line continuation characters when not inDirective - inc(pos) - if L.inDirective: - while buf[pos] in {' ', '\t'}: inc(pos) - if buf[pos] in {CR, LF}: - pos = handleCRLF(L, pos) - buf = L.buf - of ' ', Tabulator: - inc(pos) # newline is special: - of CR, LF: - pos = handleCRLF(L, pos) - buf = L.buf - if L.inDirective: - tok.xkind = pxNewLine - L.inDirective = false - else: - break # EndOfFile also leaves the loop - L.bufpos = pos - -proc getDirective(L: var TLexer, tok: var TToken) = - var pos = L.bufpos + 1 - var buf = L.buf - while buf[pos] in {' ', '\t'}: inc(pos) - while buf[pos] in SymChars: - add(tok.s, buf[pos]) - inc(pos) - # a HACK: we need to distinguish - # #define x (...) - # from: - # #define x(...) - # - L.bufpos = pos - # look ahead: - while buf[pos] in {' ', '\t'}: inc(pos) - while buf[pos] in SymChars: inc(pos) - if buf[pos] == '(': tok.xkind = pxDirectiveParLe - else: tok.xkind = pxDirective - L.inDirective = true - -proc getTok(L: var TLexer, tok: var TToken) = - tok.xkind = pxInvalid - fillToken(tok) - skip(L, tok) - if tok.xkind == pxNewLine: return - var c = L.buf[L.bufpos] - if c in SymStartChars: - getSymbol(L, tok) - elif c == '0': - case L.buf[L.bufpos+1] - of 'x', 'X': getNumber16(L, tok) - of 'b', 'B': getNumber2(L, tok) - of '1'..'7': getNumber8(L, tok) - else: getNumber(L, tok) - elif c in {'1'..'9'} or (c == '.' and L.buf[L.bufpos+1] in {'0'..'9'}): - getNumber(L, tok) - else: - case c - of ';': - tok.xkind = pxSemicolon - inc(L.bufpos) - of '/': - if L.buf[L.bufpos + 1] == '/': - scanLineComment(L, tok) - elif L.buf[L.bufpos+1] == '*': - inc(L.bufpos, 2) - scanStarComment(L, tok) - elif L.buf[L.bufpos+1] == '=': - inc(L.bufpos, 2) - tok.xkind = pxSlashAsgn - else: - tok.xkind = pxSlash - inc(L.bufpos) - of ',': - tok.xkind = pxComma - inc(L.bufpos) - of '(': - inc(L.bufpos) - tok.xkind = pxParLe - of '*': - inc(L.bufpos) - if L.buf[L.bufpos] == '=': - inc(L.bufpos) - tok.xkind = pxStarAsgn - else: - tok.xkind = pxStar - of ')': - inc(L.bufpos) - tok.xkind = pxParRi - of '[': - inc(L.bufpos) - tok.xkind = pxBracketLe - of ']': - inc(L.bufpos) - tok.xkind = pxBracketRi - of '.': - inc(L.bufpos) - if L.buf[L.bufpos] == '.' and L.buf[L.bufpos+1] == '.': - tok.xkind = pxDotDotDot - inc(L.bufpos, 2) - else: - tok.xkind = pxDot - of '{': - inc(L.bufpos) - tok.xkind = pxCurlyLe - of '}': - inc(L.bufpos) - tok.xkind = pxCurlyRi - of '+': - inc(L.bufpos) - if L.buf[L.bufpos] == '=': - tok.xkind = pxPlusAsgn - inc(L.bufpos) - elif L.buf[L.bufpos] == '+': - tok.xkind = pxPlusPlus - inc(L.bufpos) - else: - tok.xkind = pxPlus - of '-': - inc(L.bufpos) - case L.buf[L.bufpos] - of '>': - tok.xkind = pxArrow - inc(L.bufpos) - of '=': - tok.xkind = pxMinusAsgn - inc(L.bufpos) - of '-': - tok.xkind = pxMinusMinus - inc(L.bufpos) - else: - tok.xkind = pxMinus - of '?': - inc(L.bufpos) - tok.xkind = pxConditional - of ':': - inc(L.bufpos) - if L.buf[L.bufpos] == ':': - tok.xkind = pxScope - inc(L.bufpos) - else: - tok.xkind = pxColon - of '!': - inc(L.bufpos) - if L.buf[L.bufpos] == '=': - tok.xkind = pxNeq - inc(L.bufpos) - else: - tok.xkind = pxNot - of '<': - inc(L.bufpos) - if L.buf[L.bufpos] == '=': - inc(L.bufpos) - tok.xkind = pxLe - elif L.buf[L.bufpos] == '<': - inc(L.bufpos) - if L.buf[L.bufpos] == '=': - inc(L.bufpos) - tok.xkind = pxShlAsgn - else: - tok.xkind = pxShl - else: - tok.xkind = pxLt - of '>': - inc(L.bufpos) - if L.buf[L.bufpos] == '=': - inc(L.bufpos) - tok.xkind = pxGe - elif L.buf[L.bufpos] == '>': - inc(L.bufpos) - if L.buf[L.bufpos] == '=': - inc(L.bufpos) - tok.xkind = pxShrAsgn - else: - tok.xkind = pxShr - else: - tok.xkind = pxGt - of '=': - inc(L.bufpos) - if L.buf[L.bufpos] == '=': - tok.xkind = pxEquals - inc(L.bufpos) - else: - tok.xkind = pxAsgn - of '&': - inc(L.bufpos) - if L.buf[L.bufpos] == '=': - tok.xkind = pxAmpAsgn - inc(L.bufpos) - elif L.buf[L.bufpos] == '&': - inc(L.bufpos) - if L.buf[L.bufpos] == '=': - inc(L.bufpos) - tok.xkind = pxAmpAmpAsgn - else: - tok.xkind = pxAmpAmp - else: - tok.xkind = pxAmp - of '|': - inc(L.bufpos) - if L.buf[L.bufpos] == '=': - tok.xkind = pxBarAsgn - inc(L.bufpos) - elif L.buf[L.bufpos] == '|': - inc(L.bufpos) - if L.buf[L.bufpos] == '=': - inc(L.bufpos) - tok.xkind = pxBarBarAsgn - else: - tok.xkind = pxBarBar - else: - tok.xkind = pxBar - of '^': - inc(L.bufpos) - if L.buf[L.bufpos] == '=': - tok.xkind = pxHatAsgn - inc(L.bufpos) - else: - tok.xkind = pxHat - of '%': - inc(L.bufpos) - if L.buf[L.bufpos] == '=': - tok.xkind = pxModAsgn - inc(L.bufpos) - else: - tok.xkind = pxMod - of '~': - inc(L.bufpos) - if L.buf[L.bufpos] == '=': - tok.xkind = pxTildeAsgn - inc(L.bufpos) - else: - tok.xkind = pxTilde - of '#': - if L.buf[L.bufpos+1] == '#': - inc(L.bufpos, 2) - tok.xkind = pxDirConc - else: - getDirective(L, tok) - of '"': getString(L, tok) - of '\'': getCharLit(L, tok) - of nimlexbase.EndOfFile: - tok.xkind = pxEof - else: - tok.s = $c - tok.xkind = pxInvalid - lexMessage(L, errInvalidToken, c & " (\\" & $(ord(c)) & ')') - inc(L.bufpos) diff --git a/compiler/c2nim/cparse.nim b/compiler/c2nim/cparse.nim deleted file mode 100644 index 2e31af528..000000000 --- a/compiler/c2nim/cparse.nim +++ /dev/null @@ -1,2272 +0,0 @@ -# -# -# c2nim - C to Nimrod source converter -# (c) Copyright 2013 Andreas Rumpf -# -# See the file "copying.txt", included in this -# distribution, for details about the copyright. -# - -## This module implements an Ansi C parser. -## It translates a C source file into a Nimrod AST. Then the renderer can be -## used to convert the AST to its text representation. - -# TODO -# - document 'cpp' mode -# - implement handling of '::': function declarations -# - C++'s "operator" still needs some love -# - support '#if' in classes - -import - os, llstream, renderer, clex, idents, strutils, pegs, ast, astalgo, msgs, - options, strtabs, hashes, algorithm - -type - TParserFlag = enum - pfRefs, ## use "ref" instead of "ptr" for C's typ* - pfCDecl, ## annotate procs with cdecl - pfStdCall, ## annotate procs with stdcall - pfSkipInclude, ## skip all ``#include`` - pfTypePrefixes, ## all generated types start with 'T' or 'P' - pfSkipComments, ## do not generate comments - pfCpp, ## process C++ - pfIgnoreRValueRefs, ## transform C++'s 'T&&' to 'T' - pfKeepBodies ## do not skip C++ method bodies - - TMacro = object - name: string - params: int # number of parameters - body: seq[ref TToken] # can contain pxMacroParam tokens - - TParserOptions = object - flags: set[TParserFlag] - prefixes, suffixes: seq[string] - mangleRules: seq[tuple[pattern: TPeg, frmt: string]] - privateRules: seq[TPeg] - dynlibSym, header: string - macros: seq[TMacro] - toMangle: PStringTable - classes: PStringTable - PParserOptions* = ref TParserOptions - - TParser* = object - lex: TLexer - tok: ref TToken # current token - options: PParserOptions - backtrack: seq[ref TToken] - inTypeDef: int - scopeCounter: int - hasDeadCodeElimPragma: bool - currentClass: PNode # type that needs to be added as 'this' parameter - - TReplaceTuple* = array[0..1, string] - - ERetryParsing = object of ESynch - - - -proc addTypeDef(section, name, t: PNode) -proc parseStruct(p: var TParser, stmtList: PNode, isUnion: bool): PNode -proc parseStructBody(p: var TParser, stmtList: PNode, isUnion: bool, - kind: TNodeKind = nkRecList): PNode - - - -proc newParserOptions*(): PParserOptions = - new(result) - result.prefixes = @[] - result.suffixes = @[] - result.macros = @[] - result.mangleRules = @[] - result.privateRules = @[] - result.flags = {} - result.dynlibSym = "" - result.header = "" - result.toMangle = newStringTable(modeCaseSensitive) - result.classes = newStringTable(modeCaseSensitive) - -proc setOption*(parserOptions: PParserOptions, key: string, val=""): bool = - result = true - case key.normalize - of "ref": incl(parserOptions.flags, pfRefs) - of "dynlib": parserOptions.dynlibSym = val - of "header": parserOptions.header = val - of "cdecl": incl(parserOptions.flags, pfCdecl) - of "stdcall": incl(parserOptions.flags, pfStdCall) - of "prefix": parserOptions.prefixes.add(val) - of "suffix": parserOptions.suffixes.add(val) - of "skipinclude": incl(parserOptions.flags, pfSkipInclude) - of "typeprefixes": incl(parserOptions.flags, pfTypePrefixes) - of "skipcomments": incl(parserOptions.flags, pfSkipComments) - of "cpp": incl(parserOptions.flags, pfCpp) - of "keepbodies": incl(parserOptions.flags, pfKeepBodies) - of "ignorervaluerefs": incl(parserOptions.flags, pfIgnoreRValueRefs) - of "class": parserOptions.classes[val] = "true" - else: result = false - -proc parseUnit*(p: var TParser): PNode -proc openParser*(p: var TParser, filename: string, inputStream: PLLStream, - options = newParserOptions()) -proc closeParser*(p: var TParser) - -# implementation - -proc openParser(p: var TParser, filename: string, - inputStream: PLLStream, options = newParserOptions()) = - openLexer(p.lex, filename, inputStream) - p.options = options - p.backtrack = @[] - new(p.tok) - -proc parMessage(p: TParser, msg: TMsgKind, arg = "") = - #assert false - lexMessage(p.lex, msg, arg) - -proc closeParser(p: var TParser) = closeLexer(p.lex) -proc saveContext(p: var TParser) = p.backtrack.add(p.tok) -proc closeContext(p: var TParser) = discard p.backtrack.pop() -proc backtrackContext(p: var TParser) = p.tok = p.backtrack.pop() - -proc rawGetTok(p: var TParser) = - if p.tok.next != nil: - p.tok = p.tok.next - elif p.backtrack.len == 0: - p.tok.next = nil - getTok(p.lex, p.tok[]) - else: - # We need the next token and must be able to backtrack. So we need to - # allocate a new token. - var t: ref TToken - new(t) - getTok(p.lex, t[]) - p.tok.next = t - p.tok = t - -proc insertAngleRi(currentToken: ref TToken) = - var t: ref TToken - new(t) - t.xkind = pxAngleRi - t.next = currentToken.next - currentToken.next = t - -proc findMacro(p: TParser): int = - for i in 0..high(p.options.macros): - if p.tok.s == p.options.macros[i].name: return i - return -1 - -proc rawEat(p: var TParser, xkind: TTokKind) = - if p.tok.xkind == xkind: rawGetTok(p) - else: parMessage(p, errTokenExpected, tokKindToStr(xkind)) - -proc parseMacroArguments(p: var TParser): seq[seq[ref TToken]] = - result = @[] - result.add(@[]) - var i: array[pxParLe..pxCurlyLe, int] - var L = 0 - saveContext(p) - while true: - var kind = p.tok.xkind - case kind - of pxEof: rawEat(p, pxParRi) - of pxParLe, pxBracketLe, pxCurlyLe: - inc(i[kind]) - result[L].add(p.tok) - of pxParRi: - # end of arguments? - if i[pxParLe] == 0 and i[pxBracketLe] == 0 and i[pxCurlyLe] == 0: break - if i[pxParLe] > 0: dec(i[pxParLe]) - result[L].add(p.tok) - of pxBracketRi, pxCurlyRi: - kind = pred(kind, 3) - if i[kind] > 0: dec(i[kind]) - result[L].add(p.tok) - of pxComma: - if i[pxParLe] == 0 and i[pxBracketLe] == 0 and i[pxCurlyLe] == 0: - # next argument: comma is not part of the argument - result.add(@[]) - inc(L) - else: - # comma does not separate different arguments: - result[L].add(p.tok) - else: - result[L].add(p.tok) - rawGetTok(p) - closeContext(p) - -proc expandMacro(p: var TParser, m: TMacro) = - rawGetTok(p) # skip macro name - var arguments: seq[seq[ref TToken]] - if m.params > 0: - rawEat(p, pxParLe) - arguments = parseMacroArguments(p) - if arguments.len != m.params: parMessage(p, errWrongNumberOfArguments) - rawEat(p, pxParRi) - # insert into the token list: - if m.body.len > 0: - var newList: ref TToken - new(newList) - var lastTok = newList - for tok in items(m.body): - if tok.xkind == pxMacroParam: - for t in items(arguments[int(tok.iNumber)]): - #echo "t: ", t^ - lastTok.next = t - lastTok = t - else: - #echo "tok: ", tok^ - lastTok.next = tok - lastTok = tok - lastTok.next = p.tok - p.tok = newList.next - -proc getTok(p: var TParser) = - rawGetTok(p) - if p.tok.xkind == pxSymbol: - var idx = findMacro(p) - if idx >= 0: - expandMacro(p, p.options.macros[idx]) - -proc parLineInfo(p: TParser): TLineInfo = - result = getLineInfo(p.lex) - -proc skipComAux(p: var TParser, n: PNode) = - if n != nil and n.kind != nkEmpty: - if pfSkipComments notin p.options.flags: - if n.comment == nil: n.comment = p.tok.s - else: add(n.comment, "\n" & p.tok.s) - else: - parMessage(p, warnCommentXIgnored, p.tok.s) - getTok(p) - -proc skipCom(p: var TParser, n: PNode) = - while p.tok.xkind in {pxLineComment, pxStarComment}: skipComAux(p, n) - -proc skipStarCom(p: var TParser, n: PNode) = - while p.tok.xkind == pxStarComment: skipComAux(p, n) - -proc getTok(p: var TParser, n: PNode) = - getTok(p) - skipCom(p, n) - -proc expectIdent(p: TParser) = - if p.tok.xkind != pxSymbol: parMessage(p, errIdentifierExpected, $(p.tok[])) - -proc eat(p: var TParser, xkind: TTokKind, n: PNode) = - if p.tok.xkind == xkind: getTok(p, n) - else: parMessage(p, errTokenExpected, tokKindToStr(xkind)) - -proc eat(p: var TParser, xkind: TTokKind) = - if p.tok.xkind == xkind: getTok(p) - else: parMessage(p, errTokenExpected, tokKindToStr(xkind)) - -proc eat(p: var TParser, tok: string, n: PNode) = - if p.tok.s == tok: getTok(p, n) - else: parMessage(p, errTokenExpected, tok) - -proc opt(p: var TParser, xkind: TTokKind, n: PNode) = - if p.tok.xkind == xkind: getTok(p, n) - -proc addSon(father, a, b: PNode) = - addSon(father, a) - addSon(father, b) - -proc addSon(father, a, b, c: PNode) = - addSon(father, a) - addSon(father, b) - addSon(father, c) - -proc newNodeP(kind: TNodeKind, p: TParser): PNode = - result = newNodeI(kind, getLineInfo(p.lex)) - -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 newIdentNodeP(ident: string, p: TParser): PNode = - result = newIdentNodeP(getIdent(ident), p) - -proc mangleRules(s: string, p: TParser): string = - block mangle: - for pattern, frmt in items(p.options.mangleRules): - if s.match(pattern): - result = s.replacef(pattern, frmt) - break mangle - block prefixes: - for prefix in items(p.options.prefixes): - if s.startsWith(prefix): - result = s.substr(prefix.len) - break prefixes - result = s - block suffixes: - for suffix in items(p.options.suffixes): - if result.endsWith(suffix): - setLen(result, result.len - suffix.len) - break suffixes - -proc mangleName(s: string, p: TParser): string = - if p.options.toMangle.hasKey(s): result = p.options.toMangle[s] - else: result = mangleRules(s, p) - -proc isPrivate(s: string, p: TParser): bool = - for pattern in items(p.options.privateRules): - if s.match(pattern): return true - -proc mangledIdent(ident: string, p: TParser): PNode = - result = newNodeP(nkIdent, p) - result.ident = getIdent(mangleName(ident, p)) - -proc newIdentPair(a, b: string, p: TParser): PNode = - result = newNodeP(nkExprColonExpr, p) - addSon(result, newIdentNodeP(a, p)) - addSon(result, newIdentNodeP(b, p)) - -proc newIdentStrLitPair(a, b: string, p: TParser): PNode = - result = newNodeP(nkExprColonExpr, p) - addSon(result, newIdentNodeP(a, p)) - addSon(result, newStrNodeP(nkStrLit, b, p)) - -proc addImportToPragma(pragmas: PNode, ident: string, p: TParser) = - addSon(pragmas, newIdentStrLitPair("importc", ident, p)) - if p.options.dynlibSym.len > 0: - addSon(pragmas, newIdentPair("dynlib", p.options.dynlibSym, p)) - else: - addSon(pragmas, newIdentStrLitPair("header", p.options.header, p)) - -proc exportSym(p: TParser, i: PNode, origName: string): PNode = - assert i.kind == nkIdent - if p.scopeCounter == 0 and not isPrivate(origName, p): - result = newNodeI(nkPostfix, i.info) - addSon(result, newIdentNode(getIdent("*"), i.info), i) - else: - result = i - -proc varIdent(ident: string, p: TParser): PNode = - result = exportSym(p, mangledIdent(ident, p), ident) - if p.scopeCounter > 0: return - if p.options.dynlibSym.len > 0 or p.options.header.len > 0: - var a = result - result = newNodeP(nkPragmaExpr, p) - var pragmas = newNodeP(nkPragma, p) - addSon(result, a) - addSon(result, pragmas) - addImportToPragma(pragmas, ident, p) - -proc fieldIdent(ident: string, p: TParser): PNode = - result = exportSym(p, mangledIdent(ident, p), ident) - if p.scopeCounter > 0: return - if p.options.header.len > 0: - var a = result - result = newNodeP(nkPragmaExpr, p) - var pragmas = newNodeP(nkPragma, p) - addSon(result, a) - addSon(result, pragmas) - addSon(pragmas, newIdentStrLitPair("importc", ident, p)) - -proc doImport(ident: string, pragmas: PNode, p: TParser) = - if p.options.dynlibSym.len > 0 or p.options.header.len > 0: - addImportToPragma(pragmas, ident, p) - -proc doImportCpp(ident: string, pragmas: PNode, p: TParser) = - if p.options.dynlibSym.len > 0 or p.options.header.len > 0: - addSon(pragmas, newIdentStrLitPair("importcpp", ident, p)) - if p.options.dynlibSym.len > 0: - addSon(pragmas, newIdentPair("dynlib", p.options.dynlibSym, p)) - else: - addSon(pragmas, newIdentStrLitPair("header", p.options.header, p)) - -proc newBinary(opr: string, a, b: PNode, p: TParser): PNode = - result = newNodeP(nkInfix, p) - addSon(result, newIdentNodeP(getIdent(opr), p)) - addSon(result, a) - addSon(result, b) - -proc skipIdent(p: var TParser): PNode = - expectIdent(p) - result = mangledIdent(p.tok.s, p) - getTok(p, result) - -proc skipIdentExport(p: var TParser): PNode = - expectIdent(p) - result = exportSym(p, mangledIdent(p.tok.s, p), p.tok.s) - getTok(p, result) - -proc skipTypeIdentExport(p: var TParser, prefix='T'): PNode = - expectIdent(p) - var n = prefix & mangleName(p.tok.s, p) - p.options.toMangle[p.tok.s] = n - var i = newNodeP(nkIdent, p) - i.ident = getIdent(n) - result = exportSym(p, i, p.tok.s) - getTok(p, result) - -proc markTypeIdent(p: var TParser, typ: PNode) = - if pfTypePrefixes in p.options.flags: - var prefix = "" - if typ == nil or typ.kind == nkEmpty: - prefix = "T" - else: - var t = typ - while t != nil and t.kind in {nkVarTy, nkPtrTy, nkRefTy}: - prefix.add('P') - t = t.sons[0] - if prefix.len == 0: prefix.add('T') - expectIdent(p) - p.options.toMangle[p.tok.s] = prefix & mangleRules(p.tok.s, p) - -# --------------- parser ----------------------------------------------------- -# We use this parsing rule: If it looks like a declaration, it is one. This -# avoids to build a symbol table, which can't be done reliably anyway for our -# purposes. - -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 - -proc declKeyword(p: TParser, s: string): bool = - # returns true if it is a keyword that introduces a declaration - case s - of "extern", "static", "auto", "register", "const", "volatile", "restrict", - "inline", "__inline", "__cdecl", "__stdcall", "__syscall", "__fastcall", - "__safecall", "void", "struct", "union", "enum", "typedef", - "short", "int", "long", "float", "double", "signed", "unsigned", "char": - result = true - of "class": - result = p.options.flags.contains(pfCpp) - -proc stmtKeyword(s: string): bool = - case s - of "if", "for", "while", "do", "switch", "break", "continue", "return", - "goto": - result = true - -# ------------------- type desc ----------------------------------------------- - -proc isIntType(s: string): bool = - case s - of "short", "int", "long", "float", "double", "signed", "unsigned": - result = true - -proc skipConst(p: var TParser) = - while p.tok.xkind == pxSymbol and - (p.tok.s == "const" or p.tok.s == "volatile" or p.tok.s == "restrict"): - getTok(p, nil) - -proc isTemplateAngleBracket(p: var TParser): bool = - if pfCpp notin p.options.flags: return false - saveContext(p) - getTok(p, nil) # skip "<" - var i: array[pxParLe..pxCurlyLe, int] - var angles = 0 - while true: - let kind = p.tok.xkind - case kind - of pxEof: break - of pxParLe, pxBracketLe, pxCurlyLe: inc(i[kind]) - of pxGt, pxAngleRi: - # end of arguments? - if i[pxParLe] == 0 and i[pxBracketLe] == 0 and i[pxCurlyLe] == 0 and - angles == 0: - # mark as end token: - p.tok.xkind = pxAngleRi - result = true; - break - if angles > 0: dec(angles) - of pxShr: - # >> can end a template too: - if i[pxParLe] == 0 and i[pxBracketLe] == 0 and i[pxCurlyLe] == 0 and - angles == 1: - p.tok.xkind = pxAngleRi - insertAngleRi(p.tok) - result = true - break - if angles > 1: dec(angles, 2) - of pxLt: inc(angles) - of pxParRi, pxBracketRi, pxCurlyRi: - let kind = pred(kind, 3) - if i[kind] > 0: dec(i[kind]) - else: break - of pxSemicolon: break - else: discard - getTok(p, nil) - backtrackContext(p) - -proc optAngle(p: var TParser, n: PNode): PNode = - if p.tok.xkind == pxLt and isTemplateAngleBracket(p): - getTok(p) - result = newNodeP(nkBracketExpr, p) - result.add(n) - while true: - let a = assignmentExpression(p) - if not a.isNil: result.add(a) - if p.tok.xkind != pxComma: break - getTok(p) - eat(p, pxAngleRi) - else: - result = n - -proc optScope(p: var TParser, n: PNode): PNode = - result = n - if pfCpp in p.options.flags: - while p.tok.xkind == pxScope: - let a = result - result = newNodeP(nkDotExpr, p) - result.add(a) - getTok(p, result) - expectIdent(p) - result.add(mangledIdent(p.tok.s, p)) - getTok(p, result) - -proc typeAtom(p: var TParser): PNode = - skipConst(p) - expectIdent(p) - case p.tok.s - of "void": - result = newNodeP(nkNilLit, p) # little hack - getTok(p, nil) - of "struct", "union", "enum": - getTok(p, nil) - result = skipIdent(p) - elif isIntType(p.tok.s): - var x = "" - #getTok(p, nil) - var isUnsigned = false - while p.tok.xkind == pxSymbol and (isIntType(p.tok.s) or p.tok.s == "char"): - if p.tok.s == "unsigned": - isUnsigned = true - elif p.tok.s == "signed" or p.tok.s == "int": - discard - else: - add(x, p.tok.s) - getTok(p, nil) - if x.len == 0: x = "int" - let xx = if isUnsigned: "cu" & x else: "c" & x - result = mangledIdent(xx, p) - else: - result = mangledIdent(p.tok.s, p) - getTok(p, result) - result = optScope(p, result) - result = optAngle(p, result) - -proc newPointerTy(p: TParser, typ: PNode): PNode = - if pfRefs in p.options.flags: - result = newNodeP(nkRefTy, p) - else: - result = newNodeP(nkPtrTy, p) - result.addSon(typ) - -proc pointer(p: var TParser, a: PNode): PNode = - result = a - var i = 0 - skipConst(p) - while true: - if p.tok.xkind == pxStar: - inc(i) - getTok(p, result) - skipConst(p) - result = newPointerTy(p, result) - elif p.tok.xkind == pxAmp and pfCpp in p.options.flags: - getTok(p, result) - skipConst(p) - let b = result - result = newNodeP(nkVarTy, p) - result.add(b) - elif p.tok.xkind == pxAmpAmp and pfCpp in p.options.flags: - getTok(p, result) - skipConst(p) - if pfIgnoreRvalueRefs notin p.options.flags: - let b = result - result = newNodeP(nkVarTy, p) - result.add(b) - else: break - if a.kind == nkIdent and a.ident.s == "char": - if i >= 2: - result = newIdentNodeP("cstringArray", p) - for j in 1..i-2: result = newPointerTy(p, result) - elif i == 1: result = newIdentNodeP("cstring", p) - elif a.kind == nkNilLit and i > 0: - result = newIdentNodeP("pointer", p) - for j in 1..i-1: result = newPointerTy(p, result) - -proc newProcPragmas(p: TParser): PNode = - result = newNodeP(nkPragma, p) - if pfCDecl in p.options.flags: - addSon(result, newIdentNodeP("cdecl", p)) - elif pfStdCall in p.options.flags: - addSon(result, newIdentNodeP("stdcall", p)) - -proc addPragmas(father, pragmas: PNode) = - if sonsLen(pragmas) > 0: addSon(father, pragmas) - else: addSon(father, ast.emptyNode) - -proc addReturnType(params, rettyp: PNode) = - if rettyp == nil: addSon(params, ast.emptyNode) - elif rettyp.kind != nkNilLit: addSon(params, rettyp) - else: addSon(params, ast.emptyNode) - -proc parseFormalParams(p: var TParser, params, pragmas: PNode) - -proc parseTypeSuffix(p: var TParser, typ: PNode): PNode = - result = typ - while true: - case p.tok.xkind - of pxBracketLe: - getTok(p, result) - skipConst(p) # POSIX contains: ``int [restrict]`` - if p.tok.xkind != pxBracketRi: - var tmp = result - var index = expression(p) - # array type: - result = newNodeP(nkBracketExpr, p) - addSon(result, newIdentNodeP("array", p)) - #var r = newNodeP(nkRange, p) - #addSon(r, newIntNodeP(nkIntLit, 0, p)) - #addSon(r, newBinary("-", index, newIntNodeP(nkIntLit, 1, p), p)) - #addSon(result, r) - addSon(result, index) - addSon(result, tmp) - else: - # pointer type: - var tmp = result - if pfRefs in p.options.flags: - result = newNodeP(nkRefTy, p) - else: - result = newNodeP(nkPtrTy, p) - result.addSon(tmp) - eat(p, pxBracketRi, result) - of pxParLe: - # function pointer: - var procType = newNodeP(nkProcTy, p) - var pragmas = newProcPragmas(p) - var params = newNodeP(nkFormalParams, p) - addReturnType(params, result) - parseFormalParams(p, params, pragmas) - addSon(procType, params) - addPragmas(procType, pragmas) - result = procType - else: break - -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) - while p.tok.xkind == pxStar: getTok(p, nil) - result = parseField(p, kind) - eat(p, pxParRi, result) - else: - expectIdent(p) - if kind == nkRecList: result = fieldIdent(p.tok.s, p) - else: result = mangledIdent(p.tok.s, p) - getTok(p, result) - -proc structPragmas(p: TParser, name: PNode, origName: string): PNode = - assert name.kind == nkIdent - result = newNodeP(nkPragmaExpr, p) - addSon(result, exportSym(p, name, origName)) - var pragmas = newNodeP(nkPragma, p) - #addSon(pragmas, newIdentNodeP("pure", p), newIdentNodeP("final", p)) - if p.options.header.len > 0: - addSon(pragmas, newIdentStrLitPair("importc", origName, p), - newIdentStrLitPair("header", p.options.header, p)) - if pragmas.len > 0: addSon(result, pragmas) - else: addSon(result, ast.emptyNode) - -proc hashPosition(p: TParser): string = - let lineInfo = parLineInfo(p) - let fileInfo = fileInfos[lineInfo.fileIndex] - result = $hash(fileInfo.shortName & "_" & $lineInfo.line & "_" & $lineInfo.col).uint - -proc parseInnerStruct(p: var TParser, stmtList: PNode, isUnion: bool): PNode = - getTok(p, nil) - if p.tok.xkind != pxCurlyLe: - parMessage(p, errUser, "Expected '{' but found '" & $(p.tok[]) & "'") - - let structName = if isUnion: "INNER_C_UNION_" & p.hashPosition - else: "INNER_C_STRUCT_" & p.hashPosition - let typeSection = newNodeP(nkTypeSection, p) - let newStruct = newNodeP(nkObjectTy, p) - var pragmas = ast.emptyNode - if isUnion: - pragmas = newNodeP(nkPragma, p) - addSon(pragmas, newIdentNodeP("union", p)) - addSon(newStruct, pragmas, ast.emptyNode) # no inheritance - result = newNodeP(nkIdent, p) - result.ident = getIdent(structName) - let struct = parseStructBody(p, stmtList, isUnion) - let defName = newNodeP(nkIdent, p) - defName.ident = getIdent(structName) - addSon(newStruct, struct) - addTypeDef(typeSection, structPragmas(p, defName, "no_name"), newStruct) - addSon(stmtList, typeSection) - -proc parseStructBody(p: var TParser, stmtList: PNode, isUnion: bool, - kind: TNodeKind = nkRecList): PNode = - result = newNodeP(kind, p) - eat(p, pxCurlyLe, result) - while p.tok.xkind notin {pxEof, pxCurlyRi}: - skipConst(p) - var baseTyp: PNode - if p.tok.xkind == pxSymbol and (p.tok.s == "struct" or p.tok.s == "union"): - let gotUnion = if p.tok.s == "union": true else: false - saveContext(p) - getTok(p, nil) - if p.tok.xkind == pxSymbol: - backtrackContext(p) - baseTyp = typeAtom(p) - else: - backtrackContext(p) - baseTyp = parseInnerStruct(p, stmtList, gotUnion) - if p.tok.xkind == pxSemiColon: - let def = newNodeP(nkIdentDefs, p) - var t = pointer(p, baseTyp) - let i = fieldIdent("ano_" & p.hashPosition, p) - t = parseTypeSuffix(p, t) - addSon(def, i, t, ast.emptyNode) - addSon(result, def) - getTok(p, nil) - continue - else: - baseTyp = typeAtom(p) - - while true: - var def = newNodeP(nkIdentDefs, p) - var t = pointer(p, baseTyp) - var i = parseField(p, kind) - t = parseTypeSuffix(p, t) - addSon(def, i, t, ast.emptyNode) - addSon(result, def) - if p.tok.xkind != pxComma: break - getTok(p, def) - eat(p, pxSemicolon, lastSon(result)) - eat(p, pxCurlyRi, result) - -proc enumPragmas(p: TParser, name: PNode): PNode = - result = newNodeP(nkPragmaExpr, p) - addSon(result, name) - var pragmas = newNodeP(nkPragma, p) - var e = newNodeP(nkExprColonExpr, p) - # HACK: sizeof(cint) should be constructed as AST - addSon(e, newIdentNodeP("size", p), newIdentNodeP("sizeof(cint)", p)) - addSon(pragmas, e) - addSon(result, pragmas) - -proc parseStruct(p: var TParser, stmtList: PNode, isUnion: bool): PNode = - result = newNodeP(nkObjectTy, p) - var pragmas = ast.emptyNode - if isUnion: - pragmas = newNodeP(nkPragma, p) - addSon(pragmas, newIdentNodeP("union", p)) - addSon(result, pragmas, ast.emptyNode) # no inheritance - if p.tok.xkind == pxCurlyLe: - addSon(result, parseStructBody(p, stmtList, isUnion)) - 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: - discard - 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 - typ = declarator(p, typ, addr name) - if name == nil: - var idx = sonsLen(params)+1 - name = newIdentNodeP("a" & $idx, p) - var x = newNodeP(nkIdentDefs, p) - addSon(x, name, typ) - if p.tok.xkind == pxAsgn: - # we support default parameters for C++: - getTok(p, x) - addSon(x, assignmentExpression(p)) - else: - addSon(x, ast.emptyNode) - addSon(params, x) - -proc parseFormalParams(p: var TParser, params, pragmas: PNode) = - eat(p, pxParLe, params) - while p.tok.xkind notin {pxEof, pxParRi}: - if p.tok.xkind == pxDotDotDot: - addSon(pragmas, newIdentNodeP("varargs", p)) - getTok(p, pragmas) - break - parseParam(p, params) - if p.tok.xkind != pxComma: break - getTok(p, params) - eat(p, pxParRi, params) - -proc parseCallConv(p: var TParser, pragmas: PNode) = - while p.tok.xkind == pxSymbol: - case p.tok.s - of "inline", "__inline": addSon(pragmas, newIdentNodeP("inline", p)) - of "__cdecl": addSon(pragmas, newIdentNodeP("cdecl", p)) - of "__stdcall": addSon(pragmas, newIdentNodeP("stdcall", p)) - of "__syscall": addSon(pragmas, newIdentNodeP("syscall", p)) - of "__fastcall": addSon(pragmas, newIdentNodeP("fastcall", p)) - of "__safecall": addSon(pragmas, newIdentNodeP("safecall", p)) - else: break - getTok(p, nil) - -proc parseFunctionPointerDecl(p: var TParser, rettyp: PNode): PNode = - var procType = newNodeP(nkProcTy, p) - var pragmas = newProcPragmas(p) - var params = newNodeP(nkFormalParams, p) - eat(p, pxParLe, params) - addReturnType(params, rettyp) - parseCallConv(p, pragmas) - if p.tok.xkind == pxStar: getTok(p, params) - else: parMessage(p, errTokenExpected, "*") - if p.inTypeDef > 0: markTypeIdent(p, nil) - var name = skipIdentExport(p) - eat(p, pxParRi, name) - parseFormalParams(p, params, pragmas) - addSon(procType, params) - addPragmas(procType, pragmas) - - if p.inTypeDef == 0: - result = newNodeP(nkVarSection, p) - var def = newNodeP(nkIdentDefs, p) - addSon(def, name, procType, ast.emptyNode) - addSon(result, def) - else: - result = newNodeP(nkTypeDef, p) - addSon(result, name, ast.emptyNode, procType) - assert result != nil - -proc addTypeDef(section, name, t: PNode) = - var def = newNodeI(nkTypeDef, name.info) - addSon(def, name, ast.emptyNode, t) - addSon(section, def) - -proc otherTypeDef(p: var TParser, section, typ: PNode) = - var name: PNode - var t = typ - if p.tok.xkind in {pxStar, pxAmp, pxAmpAmp}: - t = pointer(p, t) - if p.tok.xkind == pxParLe: - # function pointer: typedef typ (*name)(); - var x = parseFunctionPointerDecl(p, t) - name = x[0] - t = x[2] - else: - # typedef typ name; - markTypeIdent(p, t) - name = skipIdentExport(p) - t = parseTypeSuffix(p, t) - addTypeDef(section, name, t) - -proc parseTrailingDefinedTypes(p: var TParser, section, typ: PNode) = - while p.tok.xkind == pxComma: - getTok(p, nil) - var newTyp = pointer(p, typ) - markTypeIdent(p, newTyp) - var newName = skipIdentExport(p) - newTyp = parseTypeSuffix(p, newTyp) - addTypeDef(section, newName, newTyp) - -proc createConst(name, typ, val: PNode, p: TParser): PNode = - result = newNodeP(nkConstDef, p) - addSon(result, name, typ, val) - -proc exprToNumber(n: PNode not nil): tuple[succ: bool, val: BiggestInt] = - result = (false, 0.BiggestInt) - case n.kind: - of nkPrefix: - # Check for negative/positive numbers -3 or +6 - if n.sons.len == 2 and n.sons[0].kind == nkIdent and n.sons[1].kind == nkIntLit: - let pre = n.sons[0] - let num = n.sons[1] - if pre.ident.s == "-": result = (true, - num.intVal) - elif pre.ident.s == "+": result = (true, num.intVal) - else: discard - -proc enumFields(p: var TParser, constList: PNode): PNode = - result = newNodeP(nkEnumTy, p) - addSon(result, ast.emptyNode) # enum does not inherit from anything - var i: BiggestInt = 0 - var field: tuple[id: BiggestInt, isNumber: bool, node: PNode] - var fields = newSeq[type(field)]() - while true: - var e = skipIdent(p) - if p.tok.xkind == pxAsgn: - getTok(p, e) - var c = constantExpression(p) - var a = e - e = newNodeP(nkEnumFieldDef, p) - addSon(e, a, c) - skipCom(p, e) - if c.kind == nkIntLit: - i = c.intVal - field.isNumber = true - else: - var (success, number) = exprToNumber(c) - if success: - i = number - field.isNumber = true - else: - field.isNumber = false - else: - inc(i) - field.isNumber = true - field.id = i - field.node = e - fields.add(field) - if p.tok.xkind != pxComma: break - getTok(p, e) - # allow trailing comma: - if p.tok.xkind == pxCurlyRi: break - fields.sort do (x, y: type(field)) -> int: - cmp(x.id, y.id) - var lastId: BiggestInt - var lastIdent: PNode - for count, f in fields: - if not f.isNumber: - addSon(result, f.node) - elif f.id == lastId and count > 0: - var currentIdent: PNode - case f.node.kind: - of nkEnumFieldDef: - if f.node.sons.len > 0 and f.node.sons[0].kind == nkIdent: - currentIdent = f.node.sons[0] - else: parMessage(p, errGenerated, "Warning: When sorting enum fields an expected nkIdent was not found. Check the fields!") - of nkIdent: currentIdent = f.node - else: parMessage(p, errGenerated, "Warning: When sorting enum fields an expected nkIdent was not found. Check the fields!") - var constant = createConst( currentIdent, ast.emptyNode, lastIdent, p) - constList.addSon(constant) - else: - addSon(result, f.node) - lastId = f.id - case f.node.kind: - of nkEnumFieldDef: - if f.node.sons.len > 0 and f.node.sons[0].kind == nkIdent: - lastIdent = f.node.sons[0] - else: parMessage(p, errGenerated, "Warning: When sorting enum fields an expected nkIdent was not found. Check the fields!") - of nkIdent: lastIdent = f.node - else: parMessage(p, errGenerated, "Warning: When sorting enum fields an expected nkIdent was not found. Check the fields!") - -proc parseTypedefStruct(p: var TParser, result: PNode, stmtList: PNode, isUnion: bool) = - getTok(p, result) - if p.tok.xkind == pxCurlyLe: - var t = parseStruct(p, stmtList, isUnion) - var origName = p.tok.s - markTypeIdent(p, nil) - var name = skipIdent(p) - addTypeDef(result, structPragmas(p, name, origName), t) - parseTrailingDefinedTypes(p, result, name) - elif p.tok.xkind == pxSymbol: - # name to be defined or type "struct a", we don't know yet: - markTypeIdent(p, nil) - var origName = p.tok.s - var nameOrType = skipIdent(p) - case p.tok.xkind - of pxCurlyLe: - var t = parseStruct(p, stmtList, isUnion) - if p.tok.xkind == pxSymbol: - # typedef struct tagABC {} abc, *pabc; - # --> abc is a better type name than tagABC! - markTypeIdent(p, nil) - var origName = p.tok.s - var name = skipIdent(p) - addTypeDef(result, structPragmas(p, name, origName), t) - parseTrailingDefinedTypes(p, result, name) - else: - addTypeDef(result, structPragmas(p, nameOrType, origName), t) - of pxSymbol: - # typedef struct a a? - if mangleName(p.tok.s, p) == nameOrType.ident.s: - # ignore the declaration: - getTok(p, nil) - else: - # typedef struct a b; or typedef struct a b[45]; - otherTypeDef(p, result, nameOrType) - else: - otherTypeDef(p, result, nameOrType) - else: - expectIdent(p) - -proc parseTypedefEnum(p: var TParser, result, constSection: PNode) = - getTok(p, result) - if p.tok.xkind == pxCurlyLe: - getTok(p, result) - var t = enumFields(p, constSection) - eat(p, pxCurlyRi, t) - var origName = p.tok.s - markTypeIdent(p, nil) - var name = skipIdent(p) - addTypeDef(result, enumPragmas(p, exportSym(p, name, origName)), t) - parseTrailingDefinedTypes(p, result, name) - elif p.tok.xkind == pxSymbol: - # name to be defined or type "enum a", we don't know yet: - markTypeIdent(p, nil) - var origName = p.tok.s - var nameOrType = skipIdent(p) - case p.tok.xkind - of pxCurlyLe: - getTok(p, result) - var t = enumFields(p, constSection) - eat(p, pxCurlyRi, t) - if p.tok.xkind == pxSymbol: - # typedef enum tagABC {} abc, *pabc; - # --> abc is a better type name than tagABC! - markTypeIdent(p, nil) - var origName = p.tok.s - var name = skipIdent(p) - addTypeDef(result, enumPragmas(p, exportSym(p, name, origName)), t) - parseTrailingDefinedTypes(p, result, name) - else: - addTypeDef(result, - enumPragmas(p, exportSym(p, nameOrType, origName)), t) - of pxSymbol: - # typedef enum a a? - if mangleName(p.tok.s, p) == nameOrType.ident.s: - # ignore the declaration: - getTok(p, nil) - else: - # typedef enum a b; or typedef enum a b[45]; - otherTypeDef(p, result, nameOrType) - else: - otherTypeDef(p, result, nameOrType) - else: - expectIdent(p) - -proc parseTypeDef(p: var TParser): PNode = - result = newNodeP(nkStmtList, p) - var typeSection = newNodeP(nkTypeSection, p) - var afterStatements = newNodeP(nkStmtList, p) - while p.tok.xkind == pxSymbol and p.tok.s == "typedef": - getTok(p, typeSection) - inc(p.inTypeDef) - expectIdent(p) - case p.tok.s - of "struct": parseTypedefStruct(p, typeSection, result, isUnion=false) - of "union": parseTypedefStruct(p, typeSection, result, isUnion=true) - of "enum": - var constSection = newNodeP(nkConstSection, p) - parseTypedefEnum(p, typeSection, constSection) - addSon(afterStatements, constSection) - of "class": - if pfCpp in p.options.flags: - parseTypedefStruct(p, typeSection, result, isUnion=false) - else: - var t = typeAtom(p) - otherTypeDef(p, typeSection, t) - else: - var t = typeAtom(p) - otherTypeDef(p, typeSection, t) - eat(p, pxSemicolon) - dec(p.inTypeDef) - - addSon(result, typeSection) - for s in afterStatements: - addSon(result, s) - -proc skipDeclarationSpecifiers(p: var TParser) = - while p.tok.xkind == pxSymbol: - case p.tok.s - of "extern", "static", "auto", "register", "const", "volatile": - getTok(p, nil) - else: break - -proc parseInitializer(p: var TParser): PNode = - if p.tok.xkind == pxCurlyLe: - result = newNodeP(nkBracket, p) - getTok(p, result) - while p.tok.xkind notin {pxEof, pxCurlyRi}: - addSon(result, parseInitializer(p)) - opt(p, pxComma, nil) - eat(p, pxCurlyRi, result) - else: - result = assignmentExpression(p) - -proc addInitializer(p: var TParser, def: PNode) = - if p.tok.xkind == pxAsgn: - getTok(p, def) - addSon(def, parseInitializer(p)) - else: - addSon(def, ast.emptyNode) - -proc parseVarDecl(p: var TParser, baseTyp, typ: PNode, - origName: string): PNode = - result = newNodeP(nkVarSection, p) - var def = newNodeP(nkIdentDefs, p) - addSon(def, varIdent(origName, p)) - addSon(def, parseTypeSuffix(p, typ)) - addInitializer(p, def) - addSon(result, def) - - while p.tok.xkind == pxComma: - getTok(p, def) - var t = pointer(p, baseTyp) - expectIdent(p) - def = newNodeP(nkIdentDefs, p) - addSon(def, varIdent(p.tok.s, p)) - getTok(p, def) - addSon(def, parseTypeSuffix(p, t)) - addInitializer(p, def) - addSon(result, def) - eat(p, pxSemicolon) - -proc declarationName(p: var TParser): string = - expectIdent(p) - result = p.tok.s - getTok(p) # skip identifier - while p.tok.xkind == pxScope and pfCpp in p.options.flags: - getTok(p) # skip "::" - expectIdent(p) - result.add("::") - result.add(p.tok.s) - getTok(p) - -proc declaration(p: var TParser): PNode = - result = newNodeP(nkProcDef, p) - var pragmas = newNodeP(nkPragma, p) - - skipDeclarationSpecifiers(p) - parseCallConv(p, pragmas) - skipDeclarationSpecifiers(p) - expectIdent(p) - var baseTyp = typeAtom(p) - var rettyp = pointer(p, baseTyp) - skipDeclarationSpecifiers(p) - parseCallConv(p, pragmas) - skipDeclarationSpecifiers(p) - - if p.tok.xkind == pxParLe: - # Function pointer declaration: This is of course only a heuristic, but the - # best we can do here. - result = parseFunctionPointerDecl(p, rettyp) - eat(p, pxSemicolon) - return - var origName = declarationName(p) - case p.tok.xkind - of pxParLe: - # really a function! - var name = mangledIdent(origName, p) - var params = newNodeP(nkFormalParams, p) - addReturnType(params, rettyp) - parseFormalParams(p, params, pragmas) - if pfCpp in p.options.flags and p.tok.xkind == pxSymbol and - p.tok.s == "const": - addSon(pragmas, newIdentNodeP("noSideEffect", p)) - getTok(p) - if pfCDecl in p.options.flags: - addSon(pragmas, newIdentNodeP("cdecl", p)) - elif pfStdcall in p.options.flags: - addSon(pragmas, newIdentNodeP("stdcall", p)) - # no pattern, no exceptions: - addSon(result, exportSym(p, name, origName), ast.emptyNode, ast.emptyNode) - addSon(result, params, pragmas, ast.emptyNode) # no exceptions - case p.tok.xkind - of pxSemicolon: - getTok(p) - addSon(result, ast.emptyNode) # nobody - if p.scopeCounter == 0: doImport(origName, pragmas, p) - of pxCurlyLe: - addSon(result, compoundStatement(p)) - else: - parMessage(p, errTokenExpected, ";") - if sonsLen(result.sons[pragmasPos]) == 0: - result.sons[pragmasPos] = ast.emptyNode - else: - result = parseVarDecl(p, baseTyp, rettyp, origName) - assert result != nil - -proc enumSpecifier(p: var TParser): PNode = - saveContext(p) - getTok(p, nil) # skip "enum" - case p.tok.xkind - of pxCurlyLe: - closeContext(p) - # make a const section out of it: - result = newNodeP(nkConstSection, p) - getTok(p, result) - var i = 0 - var hasUnknown = false - while true: - var name = skipIdentExport(p) - var val: PNode - if p.tok.xkind == pxAsgn: - getTok(p, name) - val = constantExpression(p) - if val.kind == nkIntLit: - i = int(val.intVal)+1 - hasUnknown = false - else: - hasUnknown = true - else: - if hasUnknown: - parMessage(p, warnUser, "computed const value may be wrong: " & - name.renderTree) - val = newIntNodeP(nkIntLit, i, p) - inc(i) - var c = createConst(name, ast.emptyNode, val, p) - addSon(result, c) - if p.tok.xkind != pxComma: break - getTok(p, c) - # allow trailing comma: - if p.tok.xkind == pxCurlyRi: break - eat(p, pxCurlyRi, result) - eat(p, pxSemicolon) - of pxSymbol: - var origName = p.tok.s - markTypeIdent(p, nil) - result = skipIdent(p) - case p.tok.xkind - of pxCurlyLe: - closeContext(p) - var name = result - # create a type section containing the enum - result = newNodeP(nkStmtList, p) - var tSection = newNodeP(nkTypeSection, p) - var t = newNodeP(nkTypeDef, p) - getTok(p, t) - var constSection = newNodeP(nkConstSection, p) - var e = enumFields(p, constSection) - addSon(t, exportSym(p, name, origName), ast.emptyNode, e) - addSon(tSection, t) - addSon(result, tSection) - addSon(result, constSection) - eat(p, pxCurlyRi, result) - eat(p, pxSemicolon) - of pxSemicolon: - # just ignore ``enum X;`` for now. - closeContext(p) - getTok(p, nil) - else: - backtrackContext(p) - result = declaration(p) - else: - closeContext(p) - parMessage(p, errTokenExpected, "{") - result = ast.emptyNode - -# Expressions - -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 startExpression(p : var TParser, tok : TToken) : PNode = - #echo "nud ", $tok - case tok.xkind: - of pxSymbol: - if tok.s == "NULL": - result = newNodeP(nkNilLit, p) - elif tok.s == "sizeof": - result = newNodeP(nkCall, p) - addSon(result, newIdentNodeP("sizeof", p)) - saveContext(p) - try: - addSon(result, expression(p, 139)) - closeContext(p) - except ERetryParsing: - backtrackContext(p) - eat(p, pxParLe) - 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 - result = newNodeP(nkCall, p) - if p.tok.xkind == pxBracketLe: - getTok(p) - eat(p, pxBracketRi) - opr.add("Array") - addSon(result, newIdentNodeP(opr, p)) - if p.tok.xkind == pxParLe: - getTok(p, result) - addSon(result, typeDesc(p)) - eat(p, pxParRi, result) - else: - addSon(result, expression(p, 139)) - else: - 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 newException(ERetryParsing, "expected a ')'") - getTok(p, result) - if p.tok.xkind in {pxSymbol, pxIntLit, pxFloatLit, pxStrLit, pxCharLit}: - raise newException(ERetryParsing, "expected a non literal token") - closeContext(p) - except ERetryParsing: - backtrackContext(p) - result = newNodeP(nkCast, p) - addSon(result, typeName(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: - # probably from a failed sub expression attempt, try a type cast - raise newException(ERetryParsing, "did not expect " & $tok) - -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 buildStmtList(a: PNode): PNode - -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, left, a) - addSon(result, branch) - branch = newNodeP(nkElseExpr, p) - 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 - -proc buildStmtList(a: PNode): PNode = - if a.kind == nkStmtList: result = a - else: - result = newNodeI(nkStmtList, a.info) - addSon(result, a) - -proc nestedStatement(p: var TParser): PNode = - # careful: We need to translate: - # if (x) if (y) stmt; - # into: - # if x: - # if x: - # stmt - # - # Nimrod requires complex statements to be nested in whitespace! - const - complexStmt = {nkProcDef, nkMethodDef, nkConverterDef, nkMacroDef, - nkTemplateDef, nkIteratorDef, nkIfStmt, - nkWhenStmt, nkForStmt, nkWhileStmt, nkCaseStmt, nkVarSection, - nkConstSection, nkTypeSection, nkTryStmt, nkBlockStmt, nkStmtList, - nkCommentStmt, nkStmtListExpr, nkBlockExpr, nkStmtListType, nkBlockType} - result = statement(p) - if result.kind in complexStmt: - result = buildStmtList(result) - -proc expressionStatement(p: var TParser): PNode = - # do not skip the comment after a semicolon to make a new nkCommentStmt - if p.tok.xkind == pxSemicolon: - getTok(p) - result = ast.emptyNode - else: - result = expression(p) - if p.tok.xkind == pxSemicolon: getTok(p) - else: parMessage(p, errTokenExpected, ";") - assert result != nil - -proc parseIf(p: var TParser): PNode = - # we parse additional "else if"s too here for better Nimrod code - result = newNodeP(nkIfStmt, p) - while true: - getTok(p) # skip ``if`` - var branch = newNodeP(nkElifBranch, p) - 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": - # ordinary else part: - branch = newNodeP(nkElse, p) - addSon(branch, nestedStatement(p)) - addSon(result, branch) - break - else: - break - -proc parseWhile(p: var TParser): PNode = - result = newNodeP(nkWhileStmt, p) - getTok(p, result) - eat(p, pxParLe, result) - addSon(result, expression(p)) - eat(p, pxParRi, result) - addSon(result, nestedStatement(p)) - -proc embedStmts(sl, a: PNode) - -proc parseDoWhile(p: var TParser): PNode = - # parsing - result = newNodeP(nkWhileStmt, p) - getTok(p, result) - var stm = nestedStatement(p) - eat(p, "while", result) - eat(p, pxParLe, result) - 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) - elif declKeyword(p, p.tok.s): - result = declaration(p) - else: - # ordinary identifier: - saveContext(p) - getTok(p) # skip identifier to look ahead - case p.tok.xkind - of pxSymbol, pxStar, pxLt, pxAmp, pxAmpAmp: - # we parse - # a b - # a * b - # always as declarations! This is of course not correct, but good - # enough for most real world C code out there. - backtrackContext(p) - result = declaration(p) - of pxColon: - # it is only a label: - closeContext(p) - getTok(p) - result = statement(p) - else: - backtrackContext(p) - result = expressionStatement(p) - assert result != nil - -proc parseTuple(p: var TParser, statements: PNode, isUnion: bool): PNode = - parseStructBody(p, statements, isUnion, nkTupleTy) - -proc parseTrailingDefinedIdents(p: var TParser, result, baseTyp: PNode) = - var varSection = newNodeP(nkVarSection, p) - while p.tok.xkind notin {pxEof, pxSemicolon}: - var t = pointer(p, baseTyp) - expectIdent(p) - var def = newNodeP(nkIdentDefs, p) - addSon(def, varIdent(p.tok.s, p)) - getTok(p, def) - addSon(def, parseTypeSuffix(p, t)) - addInitializer(p, def) - addSon(varSection, def) - if p.tok.xkind != pxComma: break - getTok(p, def) - eat(p, pxSemicolon) - if sonsLen(varSection) > 0: - addSon(result, varSection) - -proc parseStandaloneStruct(p: var TParser, isUnion: bool): PNode = - result = newNodeP(nkStmtList, p) - saveContext(p) - getTok(p, result) # skip "struct" or "union" - var origName = "" - if p.tok.xkind == pxSymbol: - markTypeIdent(p, nil) - origName = p.tok.s - getTok(p, result) - if p.tok.xkind in {pxCurlyLe, pxSemiColon}: - if origName.len > 0: - var name = mangledIdent(origName, p) - var t = parseStruct(p, result, isUnion) - var typeSection = newNodeP(nkTypeSection, p) - addTypeDef(typeSection, structPragmas(p, name, origName), t) - addSon(result, typeSection) - parseTrailingDefinedIdents(p, result, name) - else: - var t = parseTuple(p, result, isUnion) - parseTrailingDefinedIdents(p, result, t) - else: - backtrackContext(p) - result = declaration(p) - -proc parseFor(p: var TParser, result: PNode) = - # 'for' '(' expression_statement expression_statement expression? ')' - # statement - getTok(p, result) - eat(p, pxParLe, result) - var initStmt = declarationOrStatement(p) - if initStmt.kind != nkEmpty: - embedStmts(result, initStmt) - var w = newNodeP(nkWhileStmt, p) - var condition = expressionStatement(p) - if condition.kind == nkEmpty: condition = newIdentNodeP("true", p) - addSon(w, condition) - var step = if p.tok.xkind != pxParRi: expression(p) else: ast.emptyNode - eat(p, pxParRi, step) - var loopBody = nestedStatement(p) - if step.kind != nkEmpty: - loopBody = buildStmtList(loopBody) - embedStmts(loopBody, step) - addSon(w, loopBody) - addSon(result, w) - -proc switchStatement(p: var TParser): PNode = - result = newNodeP(nkStmtList, p) - while true: - if p.tok.xkind in {pxEof, pxCurlyRi}: break - case p.tok.s - of "break": - getTok(p, result) - eat(p, pxSemicolon, result) - break - of "return", "continue", "goto": - addSon(result, statement(p)) - break - of "case", "default": - break - else: discard - addSon(result, statement(p)) - if sonsLen(result) == 0: - # translate empty statement list to Nimrod's ``nil`` statement - result = newNodeP(nkNilLit, p) - -proc rangeExpression(p: var TParser): PNode = - # We support GCC's extension: ``case expr...expr:`` - result = constantExpression(p) - if p.tok.xkind == pxDotDotDot: - getTok(p, result) - var a = result - var b = constantExpression(p) - result = newNodeP(nkRange, p) - addSon(result, a) - addSon(result, b) - -proc parseSwitch(p: var TParser): PNode = - # We cannot support Duff's device or C's crazy switch syntax. We just support - # sane usages of switch. ;-) - result = newNodeP(nkCaseStmt, p) - getTok(p, result) - eat(p, pxParLe, result) - addSon(result, expression(p)) - eat(p, pxParRi, result) - eat(p, pxCurlyLe, result) - var b: PNode - while (p.tok.xkind != pxCurlyRi) and (p.tok.xkind != pxEof): - case p.tok.s - of "default": - b = newNodeP(nkElse, p) - getTok(p, b) - eat(p, pxColon, b) - of "case": - b = newNodeP(nkOfBranch, p) - while p.tok.xkind == pxSymbol and p.tok.s == "case": - getTok(p, b) - addSon(b, rangeExpression(p)) - eat(p, pxColon, b) - else: - parMessage(p, errXExpected, "case") - addSon(b, switchStatement(p)) - addSon(result, b) - if b.kind == nkElse: break - eat(p, pxCurlyRi) - -proc addStmt(sl, a: PNode) = - # merge type sections if possible: - if a.kind != nkTypeSection or sonsLen(sl) == 0 or - lastSon(sl).kind != nkTypeSection: - addSon(sl, a) - else: - var ts = lastSon(sl) - for i in 0..sonsLen(a)-1: addSon(ts, a.sons[i]) - -proc embedStmts(sl, a: PNode) = - if a.kind != nkStmtList: - addStmt(sl, a) - else: - for i in 0..sonsLen(a)-1: - if a[i].kind != nkEmpty: addStmt(sl, a[i]) - -proc compoundStatement(p: var TParser): PNode = - result = newNodeP(nkStmtList, p) - eat(p, pxCurlyLe) - inc(p.scopeCounter) - while p.tok.xkind notin {pxEof, pxCurlyRi}: - var a = statement(p) - if a.kind == nkEmpty: break - embedStmts(result, a) - if sonsLen(result) == 0: - # translate ``{}`` to Nimrod's ``discard`` statement - result = newNodeP(nkDiscardStmt, p) - result.add(ast.emptyNode) - dec(p.scopeCounter) - eat(p, pxCurlyRi) - -proc skipInheritKeyw(p: var TParser) = - if p.tok.xkind == pxSymbol and (p.tok.s == "private" or - p.tok.s == "protected" or - p.tok.s == "public"): - getTok(p) - -proc parseConstructor(p: var TParser, pragmas: PNode, - isDestructor=false): PNode = - var origName = p.tok.s - getTok(p) - - result = newNodeP(nkProcDef, p) - var rettyp = if isDestructor: newNodeP(nkNilLit, p) - else: mangledIdent(origName, p) - - let oname = if isDestructor: "destroy" & origName - else: "construct" & origName - var name = mangledIdent(oname, p) - var params = newNodeP(nkFormalParams, p) - addReturnType(params, rettyp) - if p.tok.xkind == pxParLe: - parseFormalParams(p, params, pragmas) - if p.tok.xkind == pxSymbol and p.tok.s == "const": - addSon(pragmas, newIdentNodeP("noSideEffect", p)) - if pfCDecl in p.options.flags: - addSon(pragmas, newIdentNodeP("cdecl", p)) - elif pfStdcall in p.options.flags: - addSon(pragmas, newIdentNodeP("stdcall", p)) - if p.tok.xkind == pxColon: - # skip initializer list: - while true: - getTok(p) - discard expression(p) - if p.tok.xkind != pxComma: break - # no pattern, no exceptions: - addSon(result, exportSym(p, name, origName), ast.emptyNode, ast.emptyNode) - addSon(result, params, pragmas, ast.emptyNode) # no exceptions - addSon(result, ast.emptyNode) # no body - case p.tok.xkind - of pxSemicolon: getTok(p) - of pxCurlyLe: - let body = compoundStatement(p) - if pfKeepBodies in p.options.flags: - result.sons[bodyPos] = body - else: - parMessage(p, errTokenExpected, ";") - if result.sons[bodyPos].kind == nkEmpty: - doImport((if isDestructor: "~" else: "") & origName, pragmas, p) - elif isDestructor: - addSon(pragmas, newIdentNodeP("destructor", p)) - if sonsLen(result.sons[pragmasPos]) == 0: - result.sons[pragmasPos] = ast.emptyNode - -proc parseMethod(p: var TParser, origName: string, rettyp, pragmas: PNode, - isStatic: bool): PNode = - result = newNodeP(nkProcDef, p) - var params = newNodeP(nkFormalParams, p) - addReturnType(params, rettyp) - var thisDef = newNodeP(nkIdentDefs, p) - if not isStatic: - # declare 'this': - var t = newNodeP(nkVarTy, p) - t.add(p.currentClass) - addSon(thisDef, newIdentNodeP("this", p), t, ast.emptyNode) - params.add(thisDef) - parseFormalParams(p, params, pragmas) - if p.tok.xkind == pxSymbol and p.tok.s == "const": - addSon(pragmas, newIdentNodeP("noSideEffect", p)) - getTok(p, result) - if not isStatic: - # fix the type of the 'this' parameter: - thisDef.sons[1] = thisDef.sons[1].sons[0] - if pfCDecl in p.options.flags: - addSon(pragmas, newIdentNodeP("cdecl", p)) - elif pfStdcall in p.options.flags: - addSon(pragmas, newIdentNodeP("stdcall", p)) - # no pattern, no exceptions: - let methodName = newIdentNodeP(origName, p) - addSon(result, exportSym(p, methodName, origName), - ast.emptyNode, ast.emptyNode) - addSon(result, params, pragmas, ast.emptyNode) # no exceptions - addSon(result, ast.emptyNode) # no body - case p.tok.xkind - of pxSemicolon: getTok(p) - of pxCurlyLe: - let body = compoundStatement(p) - if pfKeepBodies in p.options.flags: - result.sons[bodyPos] = body - else: - parMessage(p, errTokenExpected, ";") - if result.sons[bodyPos].kind == nkEmpty: - if isStatic: doImport(origName, pragmas, p) - else: doImportCpp(origName, pragmas, p) - if sonsLen(result.sons[pragmasPos]) == 0: - result.sons[pragmasPos] = ast.emptyNode - -proc parseStandaloneClass(p: var TParser, isStruct: bool): PNode - -proc followedByParLe(p: var TParser): bool = - saveContext(p) - getTok(p) # skip Identifier - result = p.tok.xkind == pxParLe - backtrackContext(p) - -proc parseOperator(p: var TParser, origName: var string): bool = - getTok(p) # skip 'operator' keyword - case p.tok.xkind - of pxAmp..pxArrow: - # ordinary operator symbol: - origName.add(tokKindToStr(p.tok.xkind)) - getTok(p) - of pxSymbol: - if p.tok.s == "new" or p.tok.s == "delete": - origName.add(p.tok.s) - getTok(p) - if p.tok.xkind == pxBracketLe: - getTok(p) - eat(p, pxBracketRi) - origName.add("[]") - else: - # type converter - let x = typeAtom(p) - if x.kind == nkIdent: - origName.add(x.ident.s) - else: - parMessage(p, errGenerated, "operator symbol expected") - result = true - of pxParLe: - getTok(p) - eat(p, pxParRi) - origName.add("()") - of pxBracketLe: - getTok(p) - eat(p, pxBracketRi) - origName.add("[]") - else: - parMessage(p, errGenerated, "operator symbol expected") - -proc parseClass(p: var TParser; isStruct: bool; stmtList: PNode): PNode = - result = newNodeP(nkObjectTy, p) - addSon(result, ast.emptyNode, ast.emptyNode) # no pragmas, no inheritance - - var recList = newNodeP(nkRecList, p) - addSon(result, recList) - if p.tok.xkind == pxColon: - getTok(p, result) - skipInheritKeyw(p) - var baseTyp = typeAtom(p) - var inh = newNodeP(nkOfInherit, p) - inh.add(baseTyp) - if p.tok.xkind == pxComma: - parMessage(p, errGenerated, "multiple inheritance is not supported") - while p.tok.xkind == pxComma: - getTok(p) - skipInheritKeyw(p) - discard typeAtom(p) - result.sons[0] = inh - - eat(p, pxCurlyLe, result) - var private = not isStruct - var pragmas = newNodeP(nkPragma, p) - while p.tok.xkind notin {pxEof, pxCurlyRi}: - skipCom(p, stmtList) - if p.tok.xkind == pxSymbol and (p.tok.s == "private" or - p.tok.s == "protected"): - getTok(p, result) - eat(p, pxColon, result) - private = true - elif p.tok.xkind == pxSymbol and p.tok.s == "public": - getTok(p, result) - eat(p, pxColon, result) - private = false - if p.tok.xkind == pxSymbol and (p.tok.s == "friend" or p.tok.s == "using"): - # we skip friend declarations: - while p.tok.xkind notin {pxEof, pxSemicolon}: getTok(p) - eat(p, pxSemicolon) - elif p.tok.xkind == pxSymbol and p.tok.s == "enum": - let x = enumSpecifier(p) - if not private or pfKeepBodies in p.options.flags: stmtList.add(x) - elif p.tok.xkind == pxSymbol and p.tok.s == "typedef": - let x = parseTypeDef(p) - if not private or pfKeepBodies in p.options.flags: stmtList.add(x) - elif p.tok.xkind == pxSymbol and(p.tok.s == "struct" or p.tok.s == "class"): - let x = parseStandaloneClass(p, isStruct=p.tok.s == "struct") - if not private or pfKeepBodies in p.options.flags: stmtList.add(x) - elif p.tok.xkind == pxSymbol and p.tok.s == "union": - let x = parseStandaloneStruct(p, isUnion=true) - if not private or pfKeepBodies in p.options.flags: stmtList.add(x) - else: - if pragmas.len != 0: pragmas = newNodeP(nkPragma, p) - parseCallConv(p, pragmas) - var isStatic = false - if p.tok.xkind == pxSymbol and p.tok.s == "virtual": - getTok(p, stmtList) - if p.tok.xkind == pxSymbol and p.tok.s == "explicit": - getTok(p, stmtList) - if p.tok.xkind == pxSymbol and p.tok.s == "static": - getTok(p, stmtList) - isStatic = true - parseCallConv(p, pragmas) - if p.tok.xkind == pxSymbol and p.tok.s == p.currentClass.ident.s and - followedByParLe(p): - # constructor - let cons = parseConstructor(p, pragmas) - if not private or pfKeepBodies in p.options.flags: stmtList.add(cons) - elif p.tok.xkind == pxTilde: - # destructor - getTok(p, stmtList) - if p.tok.xkind == pxSymbol and p.tok.s == p.currentClass.ident.s: - let des = parseConstructor(p, pragmas, isDestructor=true) - if not private or pfKeepBodies in p.options.flags: stmtList.add(des) - else: - parMessage(p, errGenerated, "invalid destructor") - else: - # field declaration or method: - var baseTyp = typeAtom(p) - while true: - var def = newNodeP(nkIdentDefs, p) - var t = pointer(p, baseTyp) - let canBeMethod = p.tok.xkind != pxParLe - var origName: string - if p.tok.xkind == pxSymbol: - origName = p.tok.s - if p.tok.s == "operator": - var isConverter = parseOperator(p, origName) - let meth = parseMethod(p, origName, t, pragmas, isStatic) - if not private or pfKeepBodies in p.options.flags: - if isConverter: meth.kind = nkConverterDef - stmtList.add(meth) - break - var i = parseField(p, nkRecList) - if canBeMethod and p.tok.xkind == pxParLe: - let meth = parseMethod(p, origName, t, pragmas, isStatic) - if not private or pfKeepBodies in p.options.flags: - stmtList.add(meth) - else: - t = parseTypeSuffix(p, t) - addSon(def, i, t, ast.emptyNode) - if not isStatic: addSon(recList, def) - if p.tok.xkind != pxComma: break - getTok(p, def) - if p.tok.xkind == pxSemicolon: - getTok(p, lastSon(recList)) - eat(p, pxCurlyRi, result) - -proc parseStandaloneClass(p: var TParser, isStruct: bool): PNode = - result = newNodeP(nkStmtList, p) - saveContext(p) - getTok(p, result) # skip "class" or "struct" - var origName = "" - let oldClass = p.currentClass - if p.tok.xkind == pxSymbol: - markTypeIdent(p, nil) - origName = p.tok.s - getTok(p, result) - p.currentClass = mangledIdent(origName, p) - else: - p.currentClass = nil - if p.tok.xkind in {pxCurlyLe, pxSemiColon, pxColon}: - if origName.len > 0: - p.options.classes[origName] = "true" - - var typeSection = newNodeP(nkTypeSection, p) - addSon(result, typeSection) - - var name = mangledIdent(origName, p) - var t = parseClass(p, isStruct, result) - addTypeDef(typeSection, structPragmas(p, name, origName), t) - parseTrailingDefinedIdents(p, result, name) - else: - var t = parseTuple(p, result, isUnion=false) - parseTrailingDefinedIdents(p, result, t) - else: - backtrackContext(p) - result = declaration(p) - p.currentClass = oldClass - -proc unwrap(a: PNode): PNode = - if a.kind == nkPar: - return a.sons[0] - return a - -include cpp - -proc statement(p: var TParser): PNode = - case p.tok.xkind - of pxSymbol: - case p.tok.s - of "if": result = parseIf(p) - of "switch": result = parseSwitch(p) - of "while": result = parseWhile(p) - of "do": result = parseDoWhile(p) - of "for": - result = newNodeP(nkStmtList, p) - parseFor(p, result) - of "goto": - # we cannot support "goto"; in hand-written C, "goto" is most often used - # to break a block, so we convert it to a break statement with label. - result = newNodeP(nkBreakStmt, p) - getTok(p) - addSon(result, skipIdent(p)) - eat(p, pxSemicolon) - of "continue": - result = newNodeP(nkContinueStmt, p) - getTok(p) - eat(p, pxSemicolon) - addSon(result, ast.emptyNode) - of "break": - result = newNodeP(nkBreakStmt, p) - getTok(p) - eat(p, pxSemicolon) - addSon(result, ast.emptyNode) - of "return": - result = newNodeP(nkReturnStmt, p) - getTok(p) - 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) - of "union": result = parseStandaloneStruct(p, isUnion=true) - of "struct": - if pfCpp in p.options.flags: - result = parseStandaloneClass(p, isStruct=true) - else: - result = parseStandaloneStruct(p, isUnion=false) - of "class": - if pfCpp in p.options.flags: - result = parseStandaloneClass(p, isStruct=false) - else: - result = declarationOrStatement(p) - of "namespace": - if pfCpp in p.options.flags: - while p.tok.xkind notin {pxEof, pxCurlyLe}: getTok(p) - result = compoundStatement(p) - else: - result = declarationOrStatement(p) - of "using": - if pfCpp in p.options.flags: - while p.tok.xkind notin {pxEof, pxSemicolon}: getTok(p) - eat(p, pxSemicolon) - result = newNodeP(nkNilLit, p) - else: - result = declarationOrStatement(p) - else: result = declarationOrStatement(p) - of pxCurlyLe: - result = compoundStatement(p) - of pxDirective, pxDirectiveParLe: - result = parseDir(p) - of pxLineComment, pxStarComment: - result = newNodeP(nkCommentStmt, p) - skipCom(p, result) - of pxSemicolon: - # empty statement: - getTok(p) - if p.tok.xkind in {pxLineComment, pxStarComment}: - result = newNodeP(nkCommentStmt, p) - skipCom(p, result) - else: - result = newNodeP(nkNilLit, p) - else: - result = expressionStatement(p) - assert result != nil - -proc parseUnit(p: var TParser): PNode = - 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 ERetryParsing: - parMessage(p, errGenerated, "Uncaught parsing exception raised") - diff --git a/compiler/c2nim/cpp.nim b/compiler/c2nim/cpp.nim deleted file mode 100644 index 84b4c4dfb..000000000 --- a/compiler/c2nim/cpp.nim +++ /dev/null @@ -1,347 +0,0 @@ -# -# -# c2nim - C to Nimrod source converter -# (c) Copyright 2012 Andreas Rumpf -# -# See the file "copying.txt", included in this -# distribution, for details about the copyright. -# - -# Preprocessor support - -const - c2nimSymbol = "C2NIM" - -proc eatNewLine(p: var TParser, n: PNode) = - if p.tok.xkind == pxLineComment: - skipCom(p, n) - if p.tok.xkind == pxNewLine: getTok(p) - elif p.tok.xkind == pxNewLine: - eat(p, pxNewLine) - -proc skipLine(p: var TParser) = - while p.tok.xkind notin {pxEof, pxNewLine, pxLineComment}: getTok(p) - eatNewLine(p, nil) - -proc parseDefineBody(p: var TParser, tmplDef: PNode): string = - if p.tok.xkind == pxCurlyLe or - (p.tok.xkind == pxSymbol and ( - declKeyword(p, p.tok.s) or stmtKeyword(p.tok.s))): - addSon(tmplDef, statement(p)) - result = "stmt" - elif p.tok.xkind in {pxLineComment, pxNewLine}: - addSon(tmplDef, buildStmtList(newNodeP(nkNilLit, p))) - result = "stmt" - else: - addSon(tmplDef, buildStmtList(expression(p))) - result = "expr" - -proc parseDefine(p: var TParser): PNode = - if p.tok.xkind == pxDirectiveParLe: - # a macro with parameters: - result = newNodeP(nkTemplateDef, p) - getTok(p) - addSon(result, skipIdentExport(p)) - addSon(result, ast.emptyNode) - eat(p, pxParLe) - var params = newNodeP(nkFormalParams, p) - # return type; not known yet: - addSon(params, ast.emptyNode) - if p.tok.xkind != pxParRi: - var identDefs = newNodeP(nkIdentDefs, p) - while p.tok.xkind != pxParRi: - addSon(identDefs, skipIdent(p)) - skipStarCom(p, nil) - if p.tok.xkind != pxComma: break - getTok(p) - addSon(identDefs, newIdentNodeP("expr", p)) - addSon(identDefs, ast.emptyNode) - addSon(params, identDefs) - eat(p, pxParRi) - - addSon(result, ast.emptyNode) # no generic parameters - addSon(result, params) - addSon(result, ast.emptyNode) # no pragmas - addSon(result, ast.emptyNode) - var kind = parseDefineBody(p, result) - params.sons[0] = newIdentNodeP(kind, p) - eatNewLine(p, result) - else: - # a macro without parameters: - result = newNodeP(nkConstSection, p) - while p.tok.xkind == pxDirective and p.tok.s == "define": - getTok(p) # skip #define - var c = newNodeP(nkConstDef, p) - addSon(c, skipIdentExport(p)) - addSon(c, ast.emptyNode) - skipStarCom(p, c) - if p.tok.xkind in {pxLineComment, pxNewLine, pxEof}: - addSon(c, newIdentNodeP("true", p)) - else: - addSon(c, expression(p)) - addSon(result, c) - eatNewLine(p, c) - assert result != nil - -proc parseDefBody(p: var TParser, m: var TMacro, params: seq[string]) = - m.body = @[] - # A little hack: We safe the context, so that every following token will be - # put into a newly allocated TToken object. Thus we can just save a - # reference to the token in the macro's body. - saveContext(p) - while p.tok.xkind notin {pxEof, pxNewLine, pxLineComment}: - case p.tok.xkind - of pxSymbol: - # is it a parameter reference? - var tok = p.tok - for i in 0..high(params): - if params[i] == p.tok.s: - new(tok) - tok.xkind = pxMacroParam - tok.iNumber = i - break - m.body.add(tok) - of pxDirConc: - # just ignore this token: this implements token merging correctly - discard - else: - m.body.add(p.tok) - # we do not want macro expansion here: - rawGetTok(p) - eatNewLine(p, nil) - closeContext(p) - # newline token might be overwritten, but this is not - # part of the macro body, so it is safe. - -proc parseDef(p: var TParser, m: var TMacro) = - var hasParams = p.tok.xkind == pxDirectiveParLe - getTok(p) - expectIdent(p) - m.name = p.tok.s - getTok(p) - var params: seq[string] = @[] - # parse parameters: - if hasParams: - eat(p, pxParLe) - while p.tok.xkind != pxParRi: - expectIdent(p) - params.add(p.tok.s) - getTok(p) - skipStarCom(p, nil) - if p.tok.xkind != pxComma: break - getTok(p) - eat(p, pxParRi) - m.params = params.len - parseDefBody(p, m, params) - -proc isDir(p: TParser, dir: string): bool = - result = p.tok.xkind in {pxDirectiveParLe, pxDirective} and p.tok.s == dir - -proc parseInclude(p: var TParser): PNode = - result = newNodeP(nkImportStmt, p) - while isDir(p, "include"): - getTok(p) # skip "include" - if p.tok.xkind == pxStrLit and pfSkipInclude notin p.options.flags: - var file = newStrNodeP(nkStrLit, changeFileExt(p.tok.s, ""), p) - addSon(result, file) - getTok(p) - skipStarCom(p, file) - eatNewLine(p, nil) - else: - skipLine(p) - if sonsLen(result) == 0: - # we only parsed includes that we chose to ignore: - result = ast.emptyNode - -proc definedExprAux(p: var TParser): PNode = - result = newNodeP(nkCall, p) - addSon(result, newIdentNodeP("defined", p)) - addSon(result, skipIdent(p)) - -proc parseStmtList(p: var TParser): PNode = - result = newNodeP(nkStmtList, p) - while true: - case p.tok.xkind - of pxEof: break - of pxDirectiveParLe, pxDirective: - case p.tok.s - of "else", "endif", "elif": break - else: discard - addSon(result, statement(p)) - -proc eatEndif(p: var TParser) = - if isDir(p, "endif"): - skipLine(p) - else: - parMessage(p, errXExpected, "#endif") - -proc parseIfDirAux(p: var TParser, result: PNode) = - addSon(result.sons[0], parseStmtList(p)) - while isDir(p, "elif"): - var b = newNodeP(nkElifBranch, p) - getTok(p) - addSon(b, expression(p)) - eatNewLine(p, nil) - addSon(b, parseStmtList(p)) - addSon(result, b) - if isDir(p, "else"): - var s = newNodeP(nkElse, p) - skipLine(p) - addSon(s, parseStmtList(p)) - addSon(result, s) - eatEndif(p) - -proc skipUntilEndif(p: var TParser) = - var nested = 1 - while p.tok.xkind != pxEof: - if isDir(p, "ifdef") or isDir(p, "ifndef") or isDir(p, "if"): - inc(nested) - elif isDir(p, "endif"): - dec(nested) - if nested <= 0: - skipLine(p) - return - getTok(p) - parMessage(p, errXExpected, "#endif") - -type - TEndifMarker = enum - emElif, emElse, emEndif - -proc skipUntilElifElseEndif(p: var TParser): TEndifMarker = - var nested = 1 - while p.tok.xkind != pxEof: - if isDir(p, "ifdef") or isDir(p, "ifndef") or isDir(p, "if"): - inc(nested) - elif isDir(p, "elif") and nested <= 1: - return emElif - elif isDir(p, "else") and nested <= 1: - return emElse - elif isDir(p, "endif"): - dec(nested) - if nested <= 0: - return emEndif - getTok(p) - parMessage(p, errXExpected, "#endif") - -proc parseIfdef(p: var TParser): PNode = - getTok(p) # skip #ifdef - expectIdent(p) - case p.tok.s - of "__cplusplus": - skipUntilEndif(p) - result = ast.emptyNode - of c2nimSymbol: - skipLine(p) - result = parseStmtList(p) - skipUntilEndif(p) - else: - result = newNodeP(nkWhenStmt, p) - addSon(result, newNodeP(nkElifBranch, p)) - addSon(result.sons[0], definedExprAux(p)) - eatNewLine(p, nil) - parseIfDirAux(p, result) - -proc parseIfndef(p: var TParser): PNode = - result = ast.emptyNode - getTok(p) # skip #ifndef - expectIdent(p) - if p.tok.s == c2nimSymbol: - skipLine(p) - case skipUntilElifElseEndif(p) - of emElif: - result = newNodeP(nkWhenStmt, p) - addSon(result, newNodeP(nkElifBranch, p)) - getTok(p) - addSon(result.sons[0], expression(p)) - eatNewLine(p, nil) - parseIfDirAux(p, result) - of emElse: - skipLine(p) - result = parseStmtList(p) - eatEndif(p) - of emEndif: skipLine(p) - else: - result = newNodeP(nkWhenStmt, p) - addSon(result, newNodeP(nkElifBranch, p)) - var e = newNodeP(nkCall, p) - addSon(e, newIdentNodeP("not", p)) - addSon(e, definedExprAux(p)) - eatNewLine(p, nil) - addSon(result.sons[0], e) - parseIfDirAux(p, result) - -proc parseIfDir(p: var TParser): PNode = - result = newNodeP(nkWhenStmt, p) - addSon(result, newNodeP(nkElifBranch, p)) - getTok(p) - addSon(result.sons[0], expression(p)) - eatNewLine(p, nil) - parseIfDirAux(p, result) - -proc parsePegLit(p: var TParser): TPeg = - var col = getColumn(p.lex) + 2 - getTok(p) - if p.tok.xkind != pxStrLit: expectIdent(p) - try: - result = parsePeg( - pattern = if p.tok.xkind == pxStrLit: p.tok.s else: escapePeg(p.tok.s), - filename = p.lex.fileIdx.toFilename, - line = p.lex.linenumber, - col = col) - getTok(p) - except EInvalidPeg: - parMessage(p, errUser, getCurrentExceptionMsg()) - -proc parseMangleDir(p: var TParser) = - var pattern = parsePegLit(p) - if p.tok.xkind != pxStrLit: expectIdent(p) - p.options.mangleRules.add((pattern, p.tok.s)) - getTok(p) - eatNewLine(p, nil) - -proc modulePragmas(p: var TParser): PNode = - if p.options.dynlibSym.len > 0 and not p.hasDeadCodeElimPragma: - p.hasDeadCodeElimPragma = true - result = newNodeP(nkPragma, p) - var e = newNodeP(nkExprColonExpr, p) - addSon(e, newIdentNodeP("deadCodeElim", p), newIdentNodeP("on", p)) - addSon(result, e) - else: - result = ast.emptyNode - -proc parseDir(p: var TParser): PNode = - result = ast.emptyNode - assert(p.tok.xkind in {pxDirective, pxDirectiveParLe}) - case p.tok.s - of "define": result = parseDefine(p) - of "include": result = parseInclude(p) - of "ifdef": result = parseIfdef(p) - of "ifndef": result = parseIfndef(p) - of "if": result = parseIfDir(p) - of "cdecl", "stdcall", "ref", "skipinclude", "typeprefixes", "skipcomments": - discard setOption(p.options, p.tok.s) - getTok(p) - eatNewLine(p, nil) - of "dynlib", "header", "prefix", "suffix", "class": - var key = p.tok.s - getTok(p) - if p.tok.xkind != pxStrLit: expectIdent(p) - discard setOption(p.options, key, p.tok.s) - getTok(p) - eatNewLine(p, nil) - result = modulePragmas(p) - of "mangle": - parseMangleDir(p) - of "def": - var L = p.options.macros.len - setLen(p.options.macros, L+1) - parseDef(p, p.options.macros[L]) - of "private": - var pattern = parsePegLit(p) - p.options.privateRules.add(pattern) - eatNewLine(p, nil) - else: - # ignore unimportant/unknown directive ("undef", "pragma", "error") - skipLine(p) - diff --git a/compiler/c2nim/nimrod.cfg b/compiler/c2nim/nimrod.cfg deleted file mode 100644 index cfeda63ed..000000000 --- a/compiler/c2nim/nimrod.cfg +++ /dev/null @@ -1,4 +0,0 @@ -# Use the modules of the compiler - -path: "$nimrod/compiler" - diff --git a/compiler/c2nim/tests/enum.h b/compiler/c2nim/tests/enum.h deleted file mode 100644 index 16bc59058..000000000 --- a/compiler/c2nim/tests/enum.h +++ /dev/null @@ -1,40 +0,0 @@ - -enum vehicles -{ - car = 0x10, - truck, - boat = 0x01, - ship = 1, - speedboat = 1, - bicycle = 4, - bobycar -}; - -enum -{ - red = 4, - green = 2, - blue -}; - -typedef enum food -{ - bread = 4, - toast = 4, - bun = 0x04, - cucumber = 2, - chocolate = 6 -}; - -typedef enum numbers -{ - one = 1, - two, - nten = - 10, - nnine, - four = 4, - three = + 3, - positivenine = + 9, - nfour = - 4, - negativeten = -10 -}; \ No newline at end of file diff --git a/compiler/c2nim/tests/matrix.h b/compiler/c2nim/tests/matrix.h deleted file mode 100644 index 715e9e43b..000000000 --- a/compiler/c2nim/tests/matrix.h +++ /dev/null @@ -1,240 +0,0 @@ -///////////////////////////////////////////////////////////////////////////// -// Name: wx/matrix.h -// Purpose: wxTransformMatrix class. NOT YET USED -// Author: Chris Breeze, Julian Smart -// Modified by: Klaas Holwerda -// Created: 01/02/97 -// RCS-ID: $Id$ -// Copyright: (c) Julian Smart, Chris Breeze -// Licence: wxWindows licence -///////////////////////////////////////////////////////////////////////////// - -#ifndef _WX_MATRIXH__ -#define _WX_MATRIXH__ - -//! headerfiles="matrix.h wx/object.h" -#include "wx/object.h" -#include "wx/math.h" - -//! codefiles="matrix.cpp" - -// A simple 3x3 matrix. This may be replaced by a more general matrix -// class some day. -// -// Note: this is intended to be used in wxDC at some point to replace -// the current system of scaling/translation. It is not yet used. - -#def WXDLLIMPEXP_CORE -#header "wxmatrix.h" - -//:definition -// A 3x3 matrix to do 2D transformations. -// It can be used to map data to window coordinates, -// and also for manipulating your own data. -// For example drawing a picture (composed of several primitives) -// at a certain coordinate and angle within another parent picture. -// At all times m_isIdentity is set if the matrix itself is an Identity matrix. -// It is used where possible to optimize calculations. -class WXDLLIMPEXP_CORE wxTransformMatrix: public wxObject<string, string<ubyte>> -{ -public: - wxTransformMatrix(void); - wxTransformMatrix(const wxTransformMatrix& mat); - - ~wxTransformMatrix(void); - - //get the value in the matrix at col,row - //rows are horizontal (second index of m_matrix member) - //columns are vertical (first index of m_matrix member) - double GetValue(int col, int row) const; - - //set the value in the matrix at col,row - //rows are horizontal (second index of m_matrix member) - //columns are vertical (first index of m_matrix member) - void SetValue(int col, int row, double value); - - void operator = (const wxTransformMatrix& mat); - bool operator == (const wxTransformMatrix& mat) const; - bool operator != (const module::gah::wxTransformMatrix& mat) const; - - //multiply every element by t - wxTransformMatrix& operator*=(const double& t); - //divide every element by t - wxTransformMatrix& operator/=(const double& t); - //add matrix m to this t - wxTransformMatrix& operator+=(const wxTransformMatrix& m); - //subtract matrix m from this - wxTransformMatrix& operator-=(const wxTransformMatrix& m); - //multiply matrix m with this - wxTransformMatrix& operator*=(const wxTransformMatrix& m); - - // constant operators - - //multiply every element by t and return result - wxTransformMatrix operator*(const double& t) const; - //divide this matrix by t and return result - wxTransformMatrix operator/(const double& t) const; - //add matrix m to this and return result - wxTransformMatrix operator+(const wxTransformMatrix& m) const; - //subtract matrix m from this and return result - wxTransformMatrix operator-(const wxTransformMatrix& m) const; - //multiply this by matrix m and return result - wxTransformMatrix operator*(const wxTransformMatrix& m) const; - wxTransformMatrix operator-() const; - - //rows are horizontal (second index of m_matrix member) - //columns are vertical (first index of m_matrix member) - double& operator()(int col, int row); - - //rows are horizontal (second index of m_matrix member) - //columns are vertical (first index of m_matrix member) - double operator()(int col, int row) const; - - // Invert matrix - bool Invert(void); - - // Make into identity matrix - bool Identity(void); - - // Is the matrix the identity matrix? - // Only returns a flag, which is set whenever an operation - // is done. - inline bool IsIdentity(void) const { return m_isIdentity; } - - // This does an actual check. - inline bool IsIdentity1(void) const ; - - //Scale by scale (isotropic scaling i.e. the same in x and y): - //!ex: - //!code: | scale 0 0 | - //!code: matrix' = | 0 scale 0 | x matrix - //!code: | 0 0 scale | - bool Scale(double scale); - - //Scale with center point and x/y scale - // - //!ex: - //!code: | xs 0 xc(1-xs) | - //!code: matrix' = | 0 ys yc(1-ys) | x matrix - //!code: | 0 0 1 | - wxTransformMatrix& Scale(const double &xs, const double &ys,const double &xc, const double &yc); - - // mirror a matrix in x, y - //!ex: - //!code: | -1 0 0 | - //!code: matrix' = | 0 -1 0 | x matrix - //!code: | 0 0 1 | - wxTransformMatrix<float>& Mirror(bool x=true, bool y=false); - // Translate by dx, dy: - //!ex: - //!code: | 1 0 dx | - //!code: matrix' = | 0 1 dy | x matrix - //!code: | 0 0 1 | - bool Translate(double x, double y); - - // Rotate clockwise by the given number of degrees: - //!ex: - //!code: | cos sin 0 | - //!code: matrix' = | -sin cos 0 | x matrix - //!code: | 0 0 1 | - bool Rotate(double angle); - - //Rotate counter clockwise with point of rotation - // - //!ex: - //!code: | cos(r) -sin(r) x(1-cos(r))+y(sin(r)| - //!code: matrix' = | sin(r) cos(r) y(1-cos(r))-x(sin(r)| x matrix - //!code: | 0 0 1 | - wxTransformMatrix& Rotate(const double &r, const double &x, const double &y); - - // Transform X value from logical to device - inline double TransformX(double x) const; - - // Transform Y value from logical to device - inline double TransformY(double y) const; - - // Transform a point from logical to device coordinates - bool TransformPoint(double x, double y, double& tx, double& ty) const; - - // Transform a point from device to logical coordinates. - // Example of use: - // wxTransformMatrix mat = dc.GetTransformation(); - // mat.Invert(); - // mat.InverseTransformPoint(x, y, x1, y1); - // OR (shorthand:) - // dc.LogicalToDevice(x, y, x1, y1); - // The latter is slightly less efficient if we're doing several - // conversions, since the matrix is inverted several times. - // N.B. 'this' matrix is the inverse at this point - bool InverseTransformPoint(double x, double y, double& tx, double& ty) const; - - double Get_scaleX(); - double Get_scaleY(); - double GetRotation(); - void SetRotation(double rotation); - - -public: - double m_matrix[3][3]; - bool m_isIdentity; -}; - - -/* -Chris Breeze reported, that -some functions of wxTransformMatrix cannot work because it is not -known if he matrix has been inverted. Be careful when using it. -*/ - -// Transform X value from logical to device -// warning: this function can only be used for this purpose -// because no rotation is involved when mapping logical to device coordinates -// mirror and scaling for x and y will be part of the matrix -// if you have a matrix that is rotated, eg a shape containing a matrix to place -// it in the logical coordinate system, use TransformPoint -inline double wxTransformMatrix::TransformX(double x) const -{ - //normally like this, but since no rotation is involved (only mirror and scale) - //we can do without Y -> m_matrix[1]{0] is -sin(rotation angle) and therefore zero - //(x * m_matrix[0][0] + y * m_matrix[1][0] + m_matrix[2][0])) - return (m_isIdentity ? x : (x * m_matrix[0][0] + m_matrix[2][0])); -} - -// Transform Y value from logical to device -// warning: this function can only be used for this purpose -// because no rotation is involved when mapping logical to device coordinates -// mirror and scaling for x and y will be part of the matrix -// if you have a matrix that is rotated, eg a shape containing a matrix to place -// it in the logical coordinate system, use TransformPoint -inline double wxTransformMatrix::TransformY(double y) const -{ - //normally like this, but since no rotation is involved (only mirror and scale) - //we can do without X -> m_matrix[0]{1] is sin(rotation angle) and therefore zero - //(x * m_matrix[0][1] + y * m_matrix[1][1] + m_matrix[2][1])) - return (m_isIdentity ? y : (y * m_matrix[1][1] + m_matrix[2][1])); -} - - -// Is the matrix the identity matrix? -// Each operation checks whether the result is still the identity matrix and sets a flag. -inline bool wxTransformMatrix::IsIdentity1(void) const -{ - return - ( wxIsSameDouble(m_matrix[0][0], 1.0) && - wxIsSameDouble(m_matrix[1][1], 1.0) && - wxIsSameDouble(m_matrix[2][2], 1.0) && - wxIsSameDouble(m_matrix[1][0], 0.0) && - wxIsSameDouble(m_matrix[2][0], 0.0) && - wxIsSameDouble(m_matrix[0][1], 0.0) && - wxIsSameDouble(m_matrix[2][1], 0.0) && - wxIsSameDouble(m_matrix[0][2], 0.0) && - wxIsSameDouble(m_matrix[1][2], 0.0) ); -} - -// Calculates the determinant of a 2 x 2 matrix -inline double wxCalculateDet(double a11, double a21, double a12, double a22) -{ - return a11 * a22 - a12 * a21; -} - -#endif // _WX_MATRIXH__ diff --git a/compiler/c2nim/tests/struct_anonym.h b/compiler/c2nim/tests/struct_anonym.h deleted file mode 100644 index 859bfc206..000000000 --- a/compiler/c2nim/tests/struct_anonym.h +++ /dev/null @@ -1,27 +0,0 @@ - -struct normal{ - int a; - int b; -}; - -typedef struct outerStruct { - struct normal a_nomal_one; - - int a; - - struct { - union { - int b; - } a_union_in_the_struct; - - int c; - }; - - union { - int d; - - struct { - int e; - } a_struct_in_the_union; - } a_union; -}; \ No newline at end of file diff --git a/compiler/c2nim/tests/systest.c b/compiler/c2nim/tests/systest.c deleted file mode 100644 index 51509e253..000000000 --- a/compiler/c2nim/tests/systest.c +++ /dev/null @@ -1,622 +0,0 @@ -/* This file has been written by Blablub. - * - * Another comment line. - */ - -#ifdef __cplusplus -# ifdef __SOME_OTHER_CRAP -extern "C" { -# endif -#endif - -#define interrupts() sei() - -enum -{ -/* 8bit, color or not */ - CV_LOAD_IMAGE_UNCHANGED =-1, -/* 8bit, gray */ - CV_LOAD_IMAGE_GRAYSCALE =0, -/* ?, color */ - CV_LOAD_IMAGE_COLOR =1, -/* any depth, ? */ - CV_LOAD_IMAGE_ANYDEPTH =2, -/* ?, any color */ - CV_LOAD_IMAGE_ANYCOLOR =4 -}; - -typedef void (*callback_t) (int rc); -typedef const char* (*callback2)(int rc, long L, const char* buffer); - -int aw_callback_set (AW_CALLBACK c, callback_t callback ); -int aw_instance_callback_set (AW_CALLBACK c, callback_t callback); - -unsigned long int wawa; - -#define MAX(x, y) ((x) < (y)? (y) : (x)) - -#define AW_BUILD 85 // AW 5.0 -// Limits -#define AW_MAX_AVCHANGE_PER_SECOND 10 - -#private expatDll - -#if !defined(expatDll) -# if defined(windows) -# define expatDll "expat.dll" -# elif defined(macosx) -# define expatDll "libexpat.dynlib" -# else -# define expatDll "libexpat.so(.1|)" -# endif -#endif - -#mangle "'XML_'{.*}" "$1" -#private "'XML_ParserStruct'" - -#mangle cuint cint - -unsigned int uiVar; - -#private "@('_'!.)" -unsigned int myPrivateVar__; - - -struct XML_ParserStruct; - -#def XMLCALL __cdecl - -typedef void (XMLCALL *XML_ElementDeclHandler) (void *userData, - const XML_Char *name, - XML_Content *model); - - -void* x; -void* fn(void); -void (*fn)(void); -void* (*fn)(void); -void* (*fn)(void*); - -/* - * Very ugly real world code ahead: - */ - -#def JMETHOD(rettype, name, params) rettype (*name) params - -typedef struct cjpeg_source_struct * cjpeg_source_ptr; - -struct cjpeg_source_struct { - JMETHOD(void, start_input, (j_compress_ptr cinfo, - cjpeg_source_ptr sinfo)); - JMETHOD(JDIMENSION, get_pixel_rows, (j_compress_ptr cinfo, - cjpeg_source_ptr sinfo)); - JMETHOD(void, finish_input, (j_compress_ptr cinfo, - cjpeg_source_ptr sinfo)); - - FILE *input_file; - - JSAMPARRAY buffer; - JDIMENSION buffer_height; -}; - -// Test standalone structs: - -union myunion { - char x, y, *z; - myint a, b; -} u; - -struct mystruct { - char x, y, *z; - myint a, b; -}; - -struct mystruct fn(i32 x, i64 y); - -struct mystruct { - char x, y, *z; - myint a, b; -} *myvar = NULL, **myvar2 = NULL; - -// anonymous struct: - -struct { - char x, y, *z; - myint a, b; -} varX, **varY; - -// empty anonymous struct: - -struct { - -} varX, **varY; - -// Test C2NIM skipping: - -#define MASK(x) ((x) & 0xff) -#define CAST1(x) ((int) &x) -#define CAST2(x) (typ*) &x -#define CAST3(x) ((const unsigned char**) &x) - -#ifndef C2NIM - #if someNestedCond - This is an invalid text that should generate a parser error, if not - #endif - skipped correctly. -#endif - -#ifndef C2NIM - #if someNestedCond - This is an invalid text that should generate a parser error, if not - #endif - skipped correctly. -#else -typedef char gchar; -typedef unsigned int gunsignedint; -typedef unsigned char guchar; -#endif - -#ifdef C2NIM -# mangle "'those'" "these" -int those; -#elif abc - #if someNestedCond - This is an invalid text that should generate a parser error, if not - #else - skipped correctly. - #endif -#else - Another crappy input line. -#endif - -point* newPoint(void) { - for (int i = 0; i < 89; ++i) echo("test" " string " "concatenation"); - for (; j < 54; j++) {} - for (;; j--) ; - for (;;) {} - mytype * x = y * z; - - if (**p == ' ') { - --p; - } else if (**p == '\t') { - p += 3; - } else { - p = 45 + (mytype*)45; - p = 45 + ((mytype*)45); - p = 45 + ((mytype)45); - // BUG: This does not parse: - // p = 45 + (mytype)45; - } - - while (x >= 6 && x <= 20) - --x; - - switch (*p) { - case 'A'...'Z': - case 'a'...'z': - ++p; - break; - case '0': - ++p; - break; - default: - return NULL; - } -} - -enum { - a1, a2 = 4, a3 -}; - -typedef enum crazyTAG { - x1, x2, x3 = 8, x4, x5 -} myEnum, *pMyEnum; - -typedef enum { - x1, x2, x3 = 8, x4, x5 -} myEnum, *pMyEnum; - -// Test multi-line macro: - -#define MUILTILINE "abc" \ - "xyz" \ - "def" - -#define MULTILINE(x, y) do { \ - ++y; ++x; \ -} while (0) - -#ifdef C2NIM -# dynlib iupdll -# cdecl -# mangle "'GTK_'{.*}" "TGtk$1" -# mangle "'PGTK_'{.*}" "PGtk$1" -# if defined(windows) -# define iupdll "iup.dll" -# elif defined(macosx) -# define iupdll "libiup.dynlib" -# else -# define iupdll "libiup.so" -# endif -#endif - -typedef struct stupidTAG { - mytype a, b; -} GTK_MyStruct, *PGTK_MyStruct; - -typedef struct { - mytype a, b; -} GTK_MyStruct, *PGTK_MyStruct; - -int IupConvertXYToPos(PIhandle ih, int x, int y); - -#ifdef DEBUG -# define OUT(x) printf("%s\n", x) -#else -# define OUT(x) -#endif - - - #ifdef C2NIM - # def EXTERN(x) static x - # def TWO_ARGS(x, y) x* y - #endif - // parses now! - EXTERN(int) f(void); - EXTERN(int) g(void); - - - #def EXPORT - // does parse now! - EXPORT int f(void); - EXPORT int g(void); - - static TWO_ARGS(int, x) = TWO_ARGS(56, 45); - - -# define abc 34 -# define xyz 42 - -# define wuseldusel "my string\nconstant" - -#undef ignoreThis - -char* x; - -typedef struct { - char x, y, *z; -} point; - -char* __stdcall printf(char* frmt, const char* const** ptrToStrArray, - const int* const dummy, ...); - -inline char* myinlineProc(char* frmt, const char* const* strArray, - const int* const dummy, ...); - -// Test void parameter list: -void myVoidProc(void); - -void emptyReturn(void) { return; } - -// POSIX stuff: - -#ifdef C2NIM -#prefix posix_ -int c2nimBranch; -#elif defined(MACOSX) -int* x, y, z; -#else -int dummy; -#endif - -#ifndef C2NIM -int dontTranslateThis; -#elif defined(Windows) -int WindowsTrue = true; -#endif - -int posix_spawn(pid_t *restrict, const char *restrict, - const posix_spawn_file_actions_t *, - const posix_spawnattr_t *restrict, char *const [restrict], - char *const [restrict]); -int posix_spawn_file_actions_addclose(posix_spawn_file_actions_t *, - int); -int posix_spawn_file_actions_adddup2(posix_spawn_file_actions_t *, - int, int); -int posix_spawn_file_actions_addopen(posix_spawn_file_actions_t *restrict, - int, const char *restrict, int, mode_t); -int posix_spawn_file_actions_destroy(posix_spawn_file_actions_t *); -int posix_spawn_file_actions_init(posix_spawn_file_actions_t *); -int posix_spawnattr_destroy(posix_spawnattr_t *); -int posix_spawnattr_getsigdefault(const posix_spawnattr_t *restrict, - sigset_t *restrict); -int posix_spawnattr_getflags(const posix_spawnattr_t *restrict, - short *restrict); -int posix_spawnattr_getpgroup(const posix_spawnattr_t *restrict, - pid_t *restrict); -int posix_spawnattr_getschedparam(const posix_spawnattr_t *restrict, - struct sched_param *restrict); -int posix_spawnattr_getschedpolicy(const posix_spawnattr_t *restrict, - int *restrict); -int posix_spawnattr_getsigmask(const posix_spawnattr_t *restrict, - sigset_t *restrict); -int posix_spawnattr_init(posix_spawnattr_t *); -int posix_spawnattr_setsigdefault(posix_spawnattr_t *restrict, - const sigset_t *restrict); -int posix_spawnattr_setflags(posix_spawnattr_t *, short); -int posix_spawnattr_setpgroup(posix_spawnattr_t *, pid_t); - - -int posix_spawnattr_setschedparam(posix_spawnattr_t *restrict, - const struct sched_param *restrict); -int posix_spawnattr_setschedpolicy(posix_spawnattr_t *, int); -int posix_spawnattr_setsigmask(posix_spawnattr_t *restrict, - const sigset_t *restrict); -int posix_spawnp(pid_t *restrict, const char *restrict, - const posix_spawn_file_actions_t *, - const posix_spawnattr_t *restrict, - char *const [restrict], char *const [restrict]); - -typedef struct -{ - float R, G, B; -} -RGBType; -typedef struct -{ - float H, W, B; -} -HWBType; - -static HWBType * -RGB_to_HWB (RGBType RGB, HWBType * HWB) -{ - HWBType* myArray[20]; - /* - * RGB are each on [0, 1]. W and B are returned on [0, 1] and H is - * returned on [0, 6]. Exception: H is returned UNDEFINED if W == 1 - B. - */ - - float R = RGB.R, G = RGB.G, B = RGB.B, w, v, b, f; - int i; - - w = MIN3 (R, G, B); - v = MAX3 (R, G, B); - b &= 1 - v; - if (v == w) - RETURN_HWB (HWB_UNDEFINED, w, b); - f = (R == w) ? G - B : ((G == w) ? B - R : R - G); - i = (R == w) ? 3 : ((G == w) ? 5 : 1); - RETURN_HWB (i - f / (v - w), w, b); - -} - -static int -clip_1d (int *x0, int *y0, int *x1, int *y1, int mindim, int maxdim) -{ - double m; // gradient of line - if (*x0 < mindim) - { // start of line is left of window - if (*x1 < mindim) // as is the end, so the line never cuts the window - return 0; - m = (*y1 - *y0) / (double) (*x1 - *x0); // calculate the slope of the line - // adjust x0 to be on the left boundary (ie to be zero), and y0 to match - *y0 -= m * (*x0 - mindim); - *x0 = mindim; - // now, perhaps, adjust the far end of the line as well - if (*x1 > maxdim) - { - *y1 += m * (maxdim - *x1); - *x1 = maxdim; - } - return 1; - } - if (*x0 > maxdim) - { // start of line is right of window - complement of above - if (*x1 > maxdim) // as is the end, so the line misses the window - return 0; - m = (*y1 - *y0) / (double) (*x1 - *x0); // calculate the slope of the line - *y0 += m * (maxdim - *x0); // adjust so point is on the right - // boundary - *x0 = maxdim; - // now, perhaps, adjust the end of the line - if (*x1 < mindim) - { - *y1 -= m * (*x1 - mindim); - *x1 = mindim; - } - return 1; - } - // the final case - the start of the line is inside the window - if (*x1 > maxdim) - { // other end is outside to the right - m = (*y1 - *y0) / (double) (*x1 - *x0); // calculate the slope of the line - *y1 += m * (maxdim - *x1); - *x1 = maxdim; - return 1; - } - if (*x1 < mindim) - { // other end is outside to the left - m = (*y1 - *y0) / (double) (*x1 - *x0); // calculate the slope of line - *y1 -= m * (*x1 - mindim); - *x1 = mindim; - return 1; - } - // only get here if both points are inside the window - return 1; -} - -// end of line clipping code - -static void -gdImageBrushApply (gdImagePtr im, int x, int y) -{ - int lx, ly; - int hy; - int hx; - int x1, y1, x2, y2; - int srcx, srcy; - if (!im->brush) - { - return; - } - hy = gdImageSY (im->brush) / 2; - y1 = y - hy; - y2 = y1 + gdImageSY (im->brush); - hx = gdImageSX (im->brush) / 2; - x1 = x - hx; - x2 = x1 + gdImageSX (im->brush); - srcy = 0; - if (im->trueColor) - { - if (im->brush->trueColor) - { - for (ly = y1; (ly < y2); ly++) - { - srcx = 0; - for (lx = x1; (lx < x2); lx++) - { - int p; - p = gdImageGetTrueColorPixel (im->brush, srcx, srcy); - // 2.0.9, Thomas Winzig: apply simple full transparency - if (p != gdImageGetTransparent (im->brush)) - { - gdImageSetPixel (im, lx, ly, p); - } - srcx++; - } - srcy++; - } - } - else - { - // 2.0.12: Brush palette, image truecolor (thanks to Thorben Kundinger - // for pointing out the issue) - for (ly = y1; (ly < y2); ly++) - { - srcx = 0; - for (lx = x1; (lx < x2); lx++) - { - int p, tc; - p = gdImageGetPixel (im->brush, srcx, srcy); - tc = gdImageGetTrueColorPixel (im->brush, srcx, srcy); - // 2.0.9, Thomas Winzig: apply simple full transparency - if (p != gdImageGetTransparent (im->brush)) - { - gdImageSetPixel (im, lx, ly, tc); - } - srcx++; - } - srcy++; - } - } - } - else - { - for (ly = y1; (ly < y2); ly++) - { - srcx = 0; - for (lx = x1; (lx < x2); lx++) - { - int p; - p = gdImageGetPixel (im->brush, srcx, srcy); - // Allow for non-square brushes! - if (p != gdImageGetTransparent (im->brush)) - { - // Truecolor brush. Very slow - // on a palette destination. - if (im->brush->trueColor) - { - gdImageSetPixel (im, lx, ly, - gdImageColorResolveAlpha(im, - gdTrueColorGetRed(p), - gdTrueColorGetGreen(p), - gdTrueColorGetBlue(p), - gdTrueColorGetAlpha(p))); - } - else - { - gdImageSetPixel (im, lx, ly, im->brushColorMap[p]); - } - } - srcx++; - } - srcy++; - } - } -} - - -void gdImageSetPixel (gdImagePtr im, int x, int y, int color) -{ - int p; - switch (color) - { - case gdStyled: - if (!im->style) - { - // Refuse to draw if no style is set. - return; - } - else - { - p = im->style[im->stylePos++]; - } - if (p != (gdTransparent)) - { - gdImageSetPixel (im, x, y, p); - } - im->stylePos = im->stylePos % im->styleLength; - break; - case gdStyledBrushed: - if (!im->style) - { - // Refuse to draw if no style is set. - return; - } - p = im->style[im->stylePos++]; - if ((p != gdTransparent) && (p != 0)) - { - gdImageSetPixel (im, x, y, gdBrushed); - } - im->stylePos = im->stylePos % im->styleLength; - break; - case gdBrushed: - gdImageBrushApply (im, x, y); - break; - case gdTiled: - gdImageTileApply (im, x, y); - break; - case gdAntiAliased: - // This shouldn't happen (2.0.26) because we just call - // gdImageAALine now, but do something sane. - gdImageSetPixel(im, x, y, im->AA_color); - break; - default: - if (gdImageBoundsSafeMacro (im, x, y)) - { - if (im->trueColor) - { - if (im->alphaBlendingFlag) - { - im->tpixels[y][x] = gdAlphaBlend (im->tpixels[y][x], color); - } - else - { - im->tpixels[y][x] = color; - } - } - else - { - im->pixels[y][x] = color; - } - } - break; - } -} - -#ifdef __cplusplus -} -#endif - - diff --git a/compiler/c2nim/tests/systest2.c b/compiler/c2nim/tests/systest2.c deleted file mode 100644 index bf3027cfc..000000000 --- a/compiler/c2nim/tests/systest2.c +++ /dev/null @@ -1,17 +0,0 @@ -#ifdef C2NIM -# header "iup.h" -# cdecl -# mangle "'GTK_'{.*}" "TGtk$1" -# mangle "'PGTK_'{.*}" "PGtk$1" -#endif - -typedef struct stupidTAG { - mytype a, b; -} GTK_MyStruct, *PGTK_MyStruct; - -typedef struct { - mytype a, b; -} GTK_MyStruct, *PGTK_MyStruct; - -int IupConvertXYToPos(PIhandle ih, int x, int y); - diff --git a/compiler/c2nim/tests/vincent.c b/compiler/c2nim/tests/vincent.c deleted file mode 100644 index 24c6d6425..000000000 --- a/compiler/c2nim/tests/vincent.c +++ /dev/null @@ -1,33 +0,0 @@ -#include <stdlib.h> -#include <stdio.h> - -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() { - 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 deleted file mode 100644 index b4e761ee1..000000000 --- a/compiler/c2nim/tests/vincent.h +++ /dev/null @@ -1,3 +0,0 @@ -struct foo { - int x,y,z; -}; diff --git a/compiler/lambdalifting.nim b/compiler/lambdalifting.nim index c5b9d0f00..ee380b305 100644 --- a/compiler/lambdalifting.nim +++ b/compiler/lambdalifting.nim @@ -103,43 +103,50 @@ discard """ """ +# Important things to keep in mind: +# * Don't base the analysis on nkProcDef et al. This doesn't work for +# instantiated (formerly generic) procs. The analysis has to look at nkSym. +# This also means we need to prevent the same proc is processed multiple +# times via the 'processed' set. +# * Keep in mind that the owner of some temporaries used to be unreliable. +# * For closure iterators we merge the "real" potential closure with the +# local storage requirements for efficiency. This means closure iterators +# have slightly different semantics from ordinary closures. + + const upName* = ":up" # field name for the 'up' reference - paramName* = ":env" + paramName* = ":envP" envName* = ":env" type - PInnerContext = ref TInnerContext POuterContext = ref TOuterContext + TIter = object + fn, closureParam, state, resultSym: PSym # most are only valid if + # fn.kind == skClosureIterator + obj: PType + PEnv = ref TEnv - TDep = tuple[e: PEnv, field: PSym] TEnv {.final.} = object of TObject - attachedNode: PNode + attachedNode, replacementNode: PNode createdVar: PSym # if != nil it is a used environment createdVarComesFromIter: bool capturedVars: seq[PSym] # captured variables in this environment - deps: seq[TDep] # dependencies - up: PEnv + up, next: PEnv # outer scope and next to keep all in a list + upField: PSym # if != nil the dependency to the outer scope is used obj: PType - - TInnerContext = object - fn: PSym - closureParam: PSym - localsToAccess: TIdNodeTable + fn: PSym # function that belongs to this scope; + # if up.fn != fn then we cross function boundaries. + # This is an important case to consider. + vars: TIntSet # variables belonging to this environment TOuterContext = object fn: PSym # may also be a module! - currentEnv: PEnv - isIter: bool # first class iterator? + head: PEnv capturedVars, processed: TIntSet - localsToEnv: TIdTable # PSym->PEnv mapping localsToAccess: TIdNodeTable lambdasToEnv: TIdTable # PSym->PEnv mapping - up: POuterContext - - closureParam, state, resultSym: PSym # only if isIter - obj: PType # only if isIter proc getStateType(iter: PSym): PType = var n = newNodeI(nkRange, iter.info) @@ -147,12 +154,20 @@ proc getStateType(iter: PSym): PType = addSon(n, newIntNode(nkIntLit, 0)) result = newType(tyRange, iter) result.n = n - rawAddSon(result, getSysType(tyInt)) + var intType = nilOrSysInt() + if intType.isNil: intType = newType(tyInt, iter) + rawAddSon(result, intType) proc createStateField(iter: PSym): PSym = result = newSym(skField, getIdent(":state"), iter, iter.info) result.typ = getStateType(iter) +proc createEnvObj(owner: PSym): PType = + # YYY meh, just add the state field for every closure for now, it's too + # hard to figure out if it comes from a closure iterator: + result = createObj(owner, owner.info) + rawAddField(result, createStateField(owner)) + proc newIterResult(iter: PSym): PSym = if resultPos < iter.ast.len: result = iter.ast.sons[resultPos].sym @@ -164,6 +179,7 @@ proc newIterResult(iter: PSym): PSym = iter.ast.add newSymNode(result) proc addHiddenParam(routine: PSym, param: PSym) = + assert param.kind == skParam var params = routine.ast.sons[paramsPos] # -1 is correct here as param.position is 0 based but we have at position 0 # some nkEffect node: @@ -175,7 +191,7 @@ proc addHiddenParam(routine: PSym, param: PSym) = proc getHiddenParam(routine: PSym): PSym = let params = routine.ast.sons[paramsPos] let hidden = lastSon(params) - assert hidden.kind == nkSym + internalAssert hidden.kind == nkSym and hidden.sym.kind == skParam result = hidden.sym proc getEnvParam(routine: PSym): PSym = @@ -184,161 +200,191 @@ proc getEnvParam(routine: PSym): PSym = if hidden.kind == nkSym and hidden.sym.name.s == paramName: result = hidden.sym -proc initIterContext(c: POuterContext, iter: PSym) = - c.fn = iter - c.capturedVars = initIntSet() - - var cp = getEnvParam(iter) - if cp == nil: - c.obj = createObj(iter, iter.info) - - cp = newSym(skParam, getIdent(paramName), iter, iter.info) - incl(cp.flags, sfFromGeneric) - cp.typ = newType(tyRef, iter) - rawAddSon(cp.typ, c.obj) - addHiddenParam(iter, cp) - - c.state = createStateField(iter) - addField(c.obj, c.state) - else: - c.obj = cp.typ.sons[0] - assert c.obj.kind == tyObject - if c.obj.n.len > 0: - c.state = c.obj.n[0].sym +proc initIter(iter: PSym): TIter = + result.fn = iter + if iter.kind == skClosureIterator: + var cp = getEnvParam(iter) + if cp == nil: + result.obj = createEnvObj(iter) + + cp = newSym(skParam, getIdent(paramName), iter, iter.info) + incl(cp.flags, sfFromGeneric) + cp.typ = newType(tyRef, iter) + rawAddSon(cp.typ, result.obj) + addHiddenParam(iter, cp) else: - c.state = createStateField(iter) - addField(c.obj, c.state) - - c.closureParam = cp - if iter.typ.sons[0] != nil: - c.resultSym = newIterResult(iter) - #iter.ast.add(newSymNode(c.resultSym)) - -proc newOuterContext(fn: PSym, up: POuterContext = nil): POuterContext = + result.obj = cp.typ.sons[0] + assert result.obj.kind == tyObject + internalAssert result.obj.n.len > 0 + result.state = result.obj.n[0].sym + result.closureParam = cp + if iter.typ.sons[0] != nil: + result.resultSym = newIterResult(iter) + #iter.ast.add(newSymNode(c.resultSym)) + +proc newOuterContext(fn: PSym): POuterContext = new(result) result.fn = fn result.capturedVars = initIntSet() result.processed = initIntSet() initIdNodeTable(result.localsToAccess) - initIdTable(result.localsToEnv) initIdTable(result.lambdasToEnv) - result.isIter = fn.kind == skClosureIterator - if result.isIter: initIterContext(result, fn) -proc newInnerContext(fn: PSym): PInnerContext = +proc newEnv(o: POuterContext; up: PEnv, n: PNode; owner: PSym): PEnv = new(result) - result.fn = fn - initIdNodeTable(result.localsToAccess) - -proc newEnv(outerProc: PSym, up: PEnv, n: PNode): PEnv = - new(result) - result.deps = @[] result.capturedVars = @[] - result.obj = createObj(outerProc, outerProc.info) result.up = up result.attachedNode = n + result.fn = owner + result.vars = initIntSet() + result.next = o.head + o.head = result + if owner.kind != skModule and (up == nil or up.fn != owner): + let param = getEnvParam(owner) + if param != nil: + result.obj = param.typ.sons[0] + assert result.obj.kind == tyObject + if result.obj.isNil: + result.obj = createEnvObj(owner) proc addCapturedVar(e: PEnv, v: PSym) = for x in e.capturedVars: if x == v: return - # XXX meh, just add the state field for every closure for now, it's too - # hard to figure out if it comes from a closure iterator: - if e.obj.n.len == 0: addField(e.obj, createStateField(v.owner)) e.capturedVars.add(v) addField(e.obj, v) - -proc addDep(e, d: PEnv, owner: PSym): PSym = - for x, field in items(e.deps): - if x == d: return field - var pos = sonsLen(e.obj.n) - result = newSym(skField, getIdent(upName & $pos), owner, owner.info) - result.typ = newType(tyRef, owner) - result.position = pos - assert d.obj != nil - rawAddSon(result.typ, d.obj) - addField(e.obj, result) - e.deps.add((d, result)) proc newCall(a, b: PSym): PNode = result = newNodeI(nkCall, a.info) result.add newSymNode(a) result.add newSymNode(b) -proc isInnerProc(s, outerProc: PSym): bool {.inline.} = - result = s.kind in {skProc, skMethod, skConverter, skClosureIterator} and - s.skipGenericOwner == outerProc +proc isInnerProc(s, outerProc: PSym): bool = + if s.kind in {skProc, skMethod, skConverter, skClosureIterator}: + var owner = s.skipGenericOwner + while true: + if owner.isNil: return false + if owner == outerProc: return true + owner = owner.owner #s.typ.callConv == ccClosure -proc addClosureParam(i: PInnerContext, e: PEnv) = - var cp = getEnvParam(i.fn) +proc addClosureParam(fn: PSym; e: PEnv) = + var cp = getEnvParam(fn) if cp == nil: - cp = newSym(skParam, getIdent(paramName), i.fn, i.fn.info) + cp = newSym(skParam, getIdent(paramName), fn, fn.info) incl(cp.flags, sfFromGeneric) - cp.typ = newType(tyRef, i.fn) + cp.typ = newType(tyRef, fn) rawAddSon(cp.typ, e.obj) - addHiddenParam(i.fn, cp) - else: - e.obj = cp.typ.sons[0] - assert e.obj.kind == tyObject - i.closureParam = cp - #echo "closure param added for ", i.fn.name.s, " ", i.fn.id - -proc dummyClosureParam(o: POuterContext, i: PInnerContext) = - var e = o.currentEnv - if idTableGet(o.lambdasToEnv, i.fn) == nil: - idTablePut(o.lambdasToEnv, i.fn, e) - if i.closureParam == nil: addClosureParam(i, e) + addHiddenParam(fn, cp) + #else: + #cp.typ.sons[0] = e.obj + #assert e.obj.kind == tyObject proc illegalCapture(s: PSym): bool {.inline.} = result = skipTypes(s.typ, abstractInst).kind in {tyVar, tyOpenArray, tyVarargs} or s.kind == skResult -proc captureVar(o: POuterContext, i: PInnerContext, local: PSym, - info: TLineInfo) = - # for inlined variables the owner is still wrong, so it can happen that it's - # not a captured variable at all ... *sigh* - var it = PEnv(idTableGet(o.localsToEnv, local)) - if it == nil: return - - if illegalCapture(local) or o.fn.id != local.owner.id or - i.fn.typ.callConv notin {ccClosure, ccDefault}: - # Currently captures are restricted to a single level of nesting: - localError(info, errIllegalCaptureX, local.name.s) - i.fn.typ.callConv = ccClosure - #echo "captureVar ", i.fn.name.s, i.fn.id, " ", local.name.s, local.id - - incl(i.fn.typ.flags, tfCapturesEnv) - - # we need to remember which inner most closure belongs to this lambda: - var e = o.currentEnv - if idTableGet(o.lambdasToEnv, i.fn) == nil: - idTablePut(o.lambdasToEnv, i.fn, e) - - # variable already captured: - if idNodeTableGet(i.localsToAccess, local) != nil: return - if i.closureParam == nil: addClosureParam(i, e) - - # check which environment `local` belongs to: - var access = newSymNode(i.closureParam) - addCapturedVar(it, local) - if it == e: - # common case: local directly in current environment: - discard - else: - # it's in some upper environment: - access = indirectAccess(access, addDep(e, it, i.fn), info) - access = indirectAccess(access, local, info) - if o.isIter: - if not containsOrIncl(o.capturedVars, local.id): addField(o.obj, local) - else: - incl(o.capturedVars, local.id) - idNodeTablePut(i.localsToAccess, local, access) - proc interestingVar(s: PSym): bool {.inline.} = result = s.kind in {skVar, skLet, skTemp, skForVar, skParam, skResult} and sfGlobal notin s.flags +proc nestedAccess(top: PEnv; local: PSym): PNode = + # Parts after the transformation are in []: + # + # proc main = + # var [:env.]foo = 23 + # proc outer(:paramO) = + # [var :envO; createClosure(:envO); :envO.up = paramO] + # proc inner(:paramI) = + # echo [:paramI.up.]foo + # inner([:envO]) + # outer([:env]) + if not interestingVar(local) or top.fn == local.owner: + return nil + # check it's in fact a captured variable: + var it = top + while it != nil: + if it.vars.contains(local.id): break + it = it.up + if it == nil: return nil + let envParam = top.fn.getEnvParam + internalAssert(not envParam.isNil) + var access = newSymNode(envParam) + it = top.up + while it != nil: + if it.vars.contains(local.id): + access = indirectAccess(access, local, local.info) + return access + internalAssert it.upField != nil + access = indirectAccess(access, it.upField, local.info) + it = it.up + when false: + # Type based expression construction works too, but turned out to hide + # other bugs: + while true: + let obj = access.typ.sons[0] + let field = getFieldFromObj(obj, local) + if field != nil: + return rawIndirectAccess(access, field, local.info) + let upField = lookupInRecord(obj.n, getIdent(upName)) + if upField == nil: break + access = rawIndirectAccess(access, upField, local.info) + return nil + +proc createUpField(obj, fieldType: PType): PSym = + let pos = obj.n.len + result = newSym(skField, getIdent(upName), obj.owner, obj.owner.info) + result.typ = newType(tyRef, obj.owner) + result.position = pos + rawAddSon(result.typ, fieldType) + #rawAddField(obj, result) + addField(obj, result) + +proc captureVar(o: POuterContext; top: PEnv; local: PSym; + info: TLineInfo): bool = + # first check if we should be concerned at all: + var it = top + while it != nil: + if it.vars.contains(local.id): break + it = it.up + if it == nil: return false + # yes, so mark every 'up' pointer as taken: + if illegalCapture(local) or top.fn.typ.callConv notin {ccClosure, ccDefault}: + localError(info, errIllegalCaptureX, local.name.s) + it = top + while it != nil: + if it.vars.contains(local.id): break + # keep in mind that the first element of the chain belong to top.fn itself + # and these don't need any upFields + if it.upField == nil and it.up != nil and it.fn != top.fn: + it.upField = createUpField(it.obj, it.up.obj) + + if it.fn != local.owner: + it.fn.typ.callConv = ccClosure + incl(it.fn.typ.flags, tfCapturesEnv) + + var u = it.up + while u != nil and u.fn == it.fn: u = u.up + addClosureParam(it.fn, u) + + if idTableGet(o.lambdasToEnv, it.fn) == nil: + if u != nil: idTablePut(o.lambdasToEnv, it.fn, u) + + it = it.up + # don't do this: 'top' might not require a closure: + #if idTableGet(o.lambdasToEnv, it.fn) == nil: + # idTablePut(o.lambdasToEnv, it.fn, top) + + # mark as captured: + #if top.iter != nil: + # if not containsOrIncl(o.capturedVars, local.id): + # #addField(top.iter.obj, local) + # addCapturedVar(it, local) + #else: + incl(o.capturedVars, local.id) + addCapturedVar(it, local) + result = true + proc semCaptureSym*(s, owner: PSym) = if interestingVar(s) and owner.id != s.owner.id and s.kind != skResult: if owner.typ != nil and not isGenericRoutine(owner): @@ -350,28 +396,20 @@ proc semCaptureSym*(s, owner: PSym) = # since the analysis is not entirely correct, we don't set 'tfCapturesEnv' # here -proc gatherVars(o: POuterContext, i: PInnerContext, n: PNode) = - # gather used vars for closure generation - if n == nil: return +proc gatherVars(o: POuterContext; e: PEnv; n: PNode): int = + # gather used vars for closure generation; returns number of captured vars + if n == nil: return 0 case n.kind of nkSym: var s = n.sym - if interestingVar(s) and i.fn.id != s.owner.id: - captureVar(o, i, s, n.info) - elif s.kind in {skProc, skMethod, skConverter} and - s.skipGenericOwner == o.fn and - tfCapturesEnv in s.typ.flags and s != i.fn: - # call to some other inner proc; we need to track the dependencies for - # this: - let env = PEnv(idTableGet(o.lambdasToEnv, i.fn)) - if env == nil: internalError(n.info, "no environment computed") - if o.currentEnv != env: - discard addDep(o.currentEnv, env, i.fn) - internalError(n.info, "too complex environment handling required") - of nkEmpty..pred(nkSym), succ(nkSym)..nkNilLit, nkClosure: discard + if interestingVar(s) and e.fn != s.owner: + if captureVar(o, e, s, n.info): result = 1 + of nkEmpty..pred(nkSym), succ(nkSym)..nkNilLit, nkClosure, nkProcDef, + nkMethodDef, nkConverterDef, nkMacroDef, nkTemplateDef, nkTypeSection: + discard else: - for k in countup(0, sonsLen(n) - 1): - gatherVars(o, i, n.sons[k]) + for k in countup(0, sonsLen(n) - 1): + result += gatherVars(o, e, n.sons[k]) proc generateThunk(prc: PNode, dest: PType): PNode = ## Converts 'prc' into '(thunk, nil)' so that it's compatible with @@ -403,61 +441,112 @@ proc makeClosure(prc, env: PSym, info: TLineInfo): PNode = else: result.add(newSymNode(env)) -proc transformInnerProc(o: POuterContext, i: PInnerContext, n: PNode): PNode = +proc newClosureCreationVar(e: PEnv): PSym = + result = newSym(skVar, getIdent(envName), e.fn, e.attachedNode.info) + incl(result.flags, sfShadowed) + result.typ = newType(tyRef, e.fn) + result.typ.rawAddSon(e.obj) + +proc getClosureVar(e: PEnv): PSym = + if e.createdVar == nil: + result = newClosureCreationVar(e) + e.createdVar = result + else: + result = e.createdVar + +proc findEnv(o: POuterContext; s: PSym): PEnv = + var env = o.head + while env != nil: + if env.fn == s: break + env = env.next + internalAssert env != nil and env.up != nil + result = env.up + while result.fn == s: result = result.up + +proc transformInnerProc(o: POuterContext; e: PEnv, n: PNode): PNode = case n.kind of nkEmpty..pred(nkSym), succ(nkSym)..nkNilLit: discard of nkSym: let s = n.sym - if s == i.fn: + if s == e.fn: # recursive calls go through (lambda, hiddenParam): - assert i.closureParam != nil, i.fn.name.s - result = makeClosure(s, i.closureParam, n.info) + result = makeClosure(s, getEnvParam(s), n.info) elif isInnerProc(s, o.fn) and s.typ.callConv == ccClosure: - # ugh: call to some other inner proc; - assert i.closureParam != nil - # XXX this is not correct in general! may also be some 'closure.upval' - result = makeClosure(s, i.closureParam, n.info) + # ugh: call to some other inner proc; + result = makeClosure(s, findEnv(o, s).getClosureVar, n.info) else: # captured symbol? - result = idNodeTableGet(i.localsToAccess, n.sym) - of nkLambdaKinds, nkIteratorDef: - if n.typ != nil: - result = transformInnerProc(o, i, n.sons[namePos]) + result = nestedAccess(e, n.sym) + #result = idNodeTableGet(i.localsToAccess, n.sym) + #of nkLambdaKinds, nkIteratorDef: + # if n.typ != nil: + # result = transformInnerProc(o, e, n.sons[namePos]) + #of nkClosure: + # let x = transformInnerProc(o, e, n.sons[0]) + # if x != nil: n.sons[0] = x of nkProcDef, nkMethodDef, nkConverterDef, nkMacroDef, nkTemplateDef, - nkClosure: + nkLambdaKinds, nkIteratorDef, nkClosure: # don't recurse here: discard else: for j in countup(0, sonsLen(n) - 1): - let x = transformInnerProc(o, i, n.sons[j]) + let x = transformInnerProc(o, e, n.sons[j]) if x != nil: n.sons[j] = x proc closureCreationPoint(n: PNode): PNode = - result = newNodeI(nkStmtList, n.info) - result.add(emptyNode) - result.add(n) - -proc searchForInnerProcs(o: POuterContext, n: PNode) = + if n.kind == nkStmtList and n.len >= 1 and n[0].kind == nkEmpty: + # we already have a free slot + result = n + else: + result = newNodeI(nkStmtList, n.info) + result.add(emptyNode) + result.add(n) + #result.flags.incl nfLL + +proc addParamsToEnv(fn: PSym; env: PEnv) = + let params = fn.typ.n + for i in 1.. <params.len: + if params.sons[i].kind != nkSym: + internalError(params.info, "liftLambdas: strange params") + let param = params.sons[i].sym + env.vars.incl(param.id) + # put the 'result' into the environment so it can be captured: + let ast = fn.ast + if resultPos < sonsLen(ast) and ast.sons[resultPos].kind == nkSym: + env.vars.incl(ast.sons[resultPos].sym.id) + +proc searchForInnerProcs(o: POuterContext, n: PNode, env: PEnv) = if n == nil: return case n.kind - of nkEmpty..pred(nkSym), succ(nkSym)..nkNilLit: + of nkEmpty..pred(nkSym), succ(nkSym)..nkNilLit: discard of nkSym: - if isInnerProc(n.sym, o.fn) and not containsOrIncl(o.processed, n.sym.id): - var inner = newInnerContext(n.sym) - let body = n.sym.getBody - gatherVars(o, inner, body) + let fn = n.sym + if isInnerProc(fn, o.fn) and not containsOrIncl(o.processed, fn.id): + let body = fn.getBody + + # handle deeply nested captures: + let ex = closureCreationPoint(body) + let envB = newEnv(o, env, ex, fn) + addParamsToEnv(fn, envB) + searchForInnerProcs(o, body, envB) + fn.ast.sons[bodyPos] = ex + + let capturedCounter = gatherVars(o, envB, body) # dummy closure param needed? - if inner.closureParam == nil and n.sym.typ.callConv == ccClosure: + if capturedCounter == 0 and fn.typ.callConv == ccClosure: #assert tfCapturesEnv notin n.sym.typ.flags - dummyClosureParam(o, inner) - # only transform if it really needs a closure: - if inner.closureParam != nil: - let ti = transformInnerProc(o, inner, body) - if ti != nil: n.sym.ast.sons[bodyPos] = ti + if idTableGet(o.lambdasToEnv, fn) == nil: + idTablePut(o.lambdasToEnv, fn, env) + addClosureParam(fn, env) + + elif fn.getEnvParam != nil: + # only transform if it really needs a closure: + let ti = transformInnerProc(o, envB, body) + if ti != nil: fn.ast.sons[bodyPos] = ti of nkLambdaKinds, nkIteratorDef: if n.typ != nil: - searchForInnerProcs(o, n.sons[namePos]) + searchForInnerProcs(o, n.sons[namePos], env) of nkWhileStmt, nkForStmt, nkParForStmt, nkBlockStmt: # some nodes open a new scope, so they are candidates for the insertion # of closure creation; however for simplicity we merge closures between @@ -465,14 +554,11 @@ proc searchForInnerProcs(o: POuterContext, n: PNode) = # yield observable changes in semantics. For Zahary we also # include ``nkBlock``. var body = n.len-1 - for i in countup(0, body - 1): searchForInnerProcs(o, n.sons[i]) + for i in countup(0, body - 1): searchForInnerProcs(o, n.sons[i], env) # special handling for the loop body: - let oldEnv = o.currentEnv let ex = closureCreationPoint(n.sons[body]) - o.currentEnv = newEnv(o.fn, oldEnv, ex) - searchForInnerProcs(o, n.sons[body]) + searchForInnerProcs(o, n.sons[body], newEnv(o, env, ex, env.fn)) n.sons[body] = ex - o.currentEnv = oldEnv of nkVarSection, nkLetSection: # we need to compute a mapping var->declaredBlock. Note: The definition # counts, not the block where it is captured! @@ -481,26 +567,29 @@ proc searchForInnerProcs(o: POuterContext, n: PNode) = if it.kind == nkCommentStmt: discard elif it.kind == nkIdentDefs: var L = sonsLen(it) - if it.sons[0].kind != nkSym: internalError(it.info, "transformOuter") - #echo "set: ", it.sons[0].sym.name.s, " ", o.currentBlock == nil - idTablePut(o.localsToEnv, it.sons[0].sym, o.currentEnv) - searchForInnerProcs(o, it.sons[L-1]) + if it.sons[0].kind == nkSym: + # this can be false for recursive invokations that already + # transformed it into 'env.varName': + env.vars.incl(it.sons[0].sym.id) + searchForInnerProcs(o, it.sons[L-1], env) elif it.kind == nkVarTuple: var L = sonsLen(it) for j in countup(0, L-3): #echo "set: ", it.sons[j].sym.name.s, " ", o.currentBlock == nil - idTablePut(o.localsToEnv, it.sons[j].sym, o.currentEnv) - searchForInnerProcs(o, it.sons[L-1]) + if it.sons[j].kind == nkSym: + env.vars.incl(it.sons[j].sym.id) + searchForInnerProcs(o, it.sons[L-1], env) else: - internalError(it.info, "transformOuter") + internalError(it.info, "searchForInnerProcs") + of nkClosure: + searchForInnerProcs(o, n.sons[0], env) of nkProcDef, nkMethodDef, nkConverterDef, nkMacroDef, nkTemplateDef, - nkClosure, nkTypeSection: + nkTypeSection: # don't recurse here: - # XXX recurse here and setup 'up' pointers discard else: for i in countup(0, sonsLen(n) - 1): - searchForInnerProcs(o, n.sons[i]) + searchForInnerProcs(o, n.sons[i], env) proc newAsgnStmt(le, ri: PNode, info: TLineInfo): PNode = # Bugfix: unfortunately we cannot use 'nkFastAsgn' here as that would @@ -512,19 +601,6 @@ proc newAsgnStmt(le, ri: PNode, info: TLineInfo): PNode = result.sons[0] = le result.sons[1] = ri -proc newClosureCreationVar(o: POuterContext; e: PEnv): PSym = - result = newSym(skVar, getIdent(envName), o.fn, e.attachedNode.info) - incl(result.flags, sfShadowed) - result.typ = newType(tyRef, o.fn) - result.typ.rawAddSon(e.obj) - -proc getClosureVar(o: POuterContext; e: PEnv): PSym = - if e.createdVar == nil: - result = newClosureCreationVar(o, e) - e.createdVar = result - else: - result = e.createdVar - proc rawClosureCreation(o: POuterContext, scope: PEnv; env: PSym): PNode = result = newNodeI(nkStmtList, env.info) var v = newNodeI(nkVarSection, env.info) @@ -548,21 +624,25 @@ proc rawClosureCreation(o: POuterContext, scope: PEnv; env: PSym): PNode = idNodeTablePut(o.localsToAccess, local, fieldAccess) else: result.add(newAsgnStmt(fieldAccess, existing, env.info)) - # add support for 'up' references: - for e, field in items(scope.deps): - # add ``env.up = env2`` - result.add(newAsgnStmt(indirectAccess(env, field, env.info), - newSymNode(getClosureVar(o, e)), env.info)) - + if scope.upField != nil: + # "up" chain has been used: + if scope.up.fn != scope.fn: + # crosses function boundary: + result.add(newAsgnStmt(indirectAccess(env, scope.upField, env.info), + newSymNode(getEnvParam(scope.fn)), env.info)) + else: + result.add(newAsgnStmt(indirectAccess(env, scope.upField, env.info), + newSymNode(getClosureVar(scope.up)), env.info)) + proc generateClosureCreation(o: POuterContext, scope: PEnv): PNode = - var env = getClosureVar(o, scope) + var env = getClosureVar(scope) result = rawClosureCreation(o, scope, env) proc generateIterClosureCreation(o: POuterContext; env: PEnv; scope: PNode): PSym = if env.createdVarComesFromIter or env.createdVar.isNil: # we have to create a new closure: - result = newClosureCreationVar(o, env) + result = newClosureCreationVar(env) let cc = rawClosureCreation(o, env, result) var insertPoint = scope.sons[0] if insertPoint.kind == nkEmpty: scope.sons[0] = cc @@ -577,21 +657,25 @@ proc generateIterClosureCreation(o: POuterContext; env: PEnv; proc interestingIterVar(s: PSym): bool {.inline.} = result = s.kind in {skVar, skLet, skTemp, skForVar} and sfGlobal notin s.flags -proc transformOuterProc(o: POuterContext, n: PNode): PNode +proc transformOuterProc(o: POuterContext, n: PNode, it: TIter): PNode -proc transformYield(c: POuterContext, n: PNode): PNode = - inc c.state.typ.n.sons[1].intVal - let stateNo = c.state.typ.n.sons[1].intVal +proc transformYield(c: POuterContext, n: PNode, it: TIter): PNode = + assert it.state != nil + assert it.state.typ != nil + assert it.state.typ.n != nil + inc it.state.typ.n.sons[1].intVal + let stateNo = it.state.typ.n.sons[1].intVal var stateAsgnStmt = newNodeI(nkAsgn, n.info) - stateAsgnStmt.add(indirectAccess(newSymNode(c.closureParam),c.state,n.info)) + stateAsgnStmt.add(rawIndirectAccess(newSymNode(it.closureParam), + it.state, n.info)) stateAsgnStmt.add(newIntTypeNode(nkIntLit, stateNo, getSysType(tyInt))) var retStmt = newNodeI(nkReturnStmt, n.info) if n.sons[0].kind != nkEmpty: var a = newNodeI(nkAsgn, n.sons[0].info) - var retVal = transformOuterProc(c, n.sons[0]) - addSon(a, newSymNode(c.resultSym)) + var retVal = transformOuterProc(c, n.sons[0], it) + addSon(a, newSymNode(it.resultSym)) addSon(a, if retVal.isNil: n.sons[0] else: retVal) retStmt.add(a) else: @@ -605,17 +689,18 @@ proc transformYield(c: POuterContext, n: PNode): PNode = result.add(retStmt) result.add(stateLabelStmt) -proc transformReturn(c: POuterContext, n: PNode): PNode = +proc transformReturn(c: POuterContext, n: PNode, it: TIter): PNode = result = newNodeI(nkStmtList, n.info) var stateAsgnStmt = newNodeI(nkAsgn, n.info) - stateAsgnStmt.add(indirectAccess(newSymNode(c.closureParam),c.state,n.info)) + stateAsgnStmt.add(rawIndirectAccess(newSymNode(it.closureParam), it.state, + n.info)) stateAsgnStmt.add(newIntTypeNode(nkIntLit, -1, getSysType(tyInt))) result.add(stateAsgnStmt) result.add(n) -proc outerProcSons(o: POuterContext, n: PNode) = +proc outerProcSons(o: POuterContext, n: PNode, it: TIter) = for i in countup(0, sonsLen(n) - 1): - let x = transformOuterProc(o, n.sons[i]) + let x = transformOuterProc(o, n.sons[i], it) if x != nil: n.sons[i] = x proc liftIterSym*(n: PNode): PNode = @@ -631,27 +716,107 @@ proc liftIterSym*(n: PNode): PNode = addVar(v, newSymNode(env)) result.add(v) # add 'new' statement: - result.add(newCall(getSysSym"internalNew", env)) + result.add newCall(getSysSym"internalNew", env) result.add makeClosure(iter, env, n.info) -proc transformOuterProc(o: POuterContext, n: PNode): PNode = - if n == nil: return nil +template envActive(env): expr = + (env.capturedVars.len > 0 or env.upField != nil) + +# We have to split up environment creation in 2 steps: +# 1. Generate it and store it in env.replacementNode +# 2. Insert replacementNode into its forseen slot. +# This split is necessary so that assignments belonging to closure +# creation like 'env.param = param' are not transformed +# into 'env.param = env.param'. +proc createEnvironments(o: POuterContext) = + var env = o.head + while env != nil: + if envActive(env): + var scope = env.attachedNode + assert scope.kind == nkStmtList + if scope.sons[0].kind == nkEmpty: + # prepare for closure construction: + env.replacementNode = generateClosureCreation(o, env) + env = env.next + +proc finishEnvironments(o: POuterContext) = + var env = o.head + while env != nil: + if env.replacementNode != nil: + var scope = env.attachedNode + assert scope.kind == nkStmtList + if scope.sons[0].kind == nkEmpty: + # change the empty node to contain the closure construction: + scope.sons[0] = env.replacementNode + env = env.next + +proc transformOuterProcBody(o: POuterContext, n: PNode; it: TIter): PNode = + if nfLL in n.flags: + result = nil + elif it.fn.kind == skClosureIterator: + # unfortunately control flow is still convoluted and we can end up + # multiple times here for the very same iterator. We shield against this + # with some rather primitive check for now: + if n.kind == nkStmtList and n.len > 0: + if n.sons[0].kind == nkGotoState: return nil + if n.len > 1 and n[1].kind == nkStmtList and n[1].len > 0 and + n[1][0].kind == nkGotoState: + return nil + result = newNodeI(nkStmtList, it.fn.info) + var gs = newNodeI(nkGotoState, it.fn.info) + assert it.closureParam != nil + assert it.state != nil + gs.add(rawIndirectAccess(newSymNode(it.closureParam), it.state, it.fn.info)) + result.add(gs) + var state0 = newNodeI(nkState, it.fn.info) + state0.add(newIntNode(nkIntLit, 0)) + result.add(state0) + + let newBody = transformOuterProc(o, n, it) + if newBody != nil: + result.add(newBody) + else: + result.add(n) + + var stateAsgnStmt = newNodeI(nkAsgn, it.fn.info) + stateAsgnStmt.add(rawIndirectAccess(newSymNode(it.closureParam), + it.state, it.fn.info)) + stateAsgnStmt.add(newIntTypeNode(nkIntLit, -1, getSysType(tyInt))) + result.add(stateAsgnStmt) + result.flags.incl nfLL + else: + result = transformOuterProc(o, n, it) + if result != nil: result.flags.incl nfLL + +proc transformOuterProc(o: POuterContext, n: PNode; it: TIter): PNode = + if n == nil or nfLL in n.flags: return nil case n.kind of nkEmpty..pred(nkSym), succ(nkSym)..nkNilLit: discard of nkSym: var local = n.sym - if o.isIter and interestingIterVar(local) and o.fn.id == local.owner.id: - if not containsOrIncl(o.capturedVars, local.id): addField(o.obj, local) - return indirectAccess(newSymNode(o.closureParam), local, n.info) + if isInnerProc(local, o.fn) and o.processed.contains(local.id): + o.processed.excl(local.id) + let body = local.getBody + let newBody = transformOuterProcBody(o, body, initIter(local)) + if newBody != nil: + local.ast.sons[bodyPos] = newBody + + if it.fn.kind == skClosureIterator and interestingIterVar(local) and + it.fn == local.owner: + # every local goes through the closure: + #if not containsOrIncl(o.capturedVars, local.id): + # addField(it.obj, local) + addUniqueField(it.obj, local) + return indirectAccess(newSymNode(it.closureParam), local, n.info) var closure = PEnv(idTableGet(o.lambdasToEnv, local)) - if local.kind == skClosureIterator: # consider: [i1, i2, i1] Since we merged the iterator's closure # with the captured owning variables, we need to generate the # closure generation code again: - if local == o.fn: message(n.info, errRecursiveDependencyX, local.name.s) + if local == o.fn or local == it.fn: + message(n.info, errRecursiveDependencyX, local.name.s) # XXX why doesn't this work? if closure.isNil: return liftIterSym(n) @@ -662,7 +827,6 @@ proc transformOuterProc(o: POuterContext, n: PNode): PNode = if closure != nil: # we need to replace the lambda with '(lambda, env)': - let a = closure.createdVar if a != nil: return makeClosure(local, a, n.info) @@ -678,14 +842,6 @@ proc transformOuterProc(o: POuterContext, n: PNode): PNode = return makeClosure(local, x, n.info) if not contains(o.capturedVars, local.id): return - var env = PEnv(idTableGet(o.localsToEnv, local)) - if env == nil: return - var scope = env.attachedNode - assert scope.kind == nkStmtList - if scope.sons[0].kind == nkEmpty: - # change the empty node to contain the closure construction: - scope.sons[0] = generateClosureCreation(o, env) - # change 'local' to 'closure.local', unless it's a 'byCopy' variable: # if sfByCopy notin local.flags: result = idNodeTableGet(o.localsToAccess, local) @@ -694,73 +850,56 @@ proc transformOuterProc(o: POuterContext, n: PNode): PNode = # to access the local as a local. of nkLambdaKinds, nkIteratorDef: if n.typ != nil: - result = transformOuterProc(o, n.sons[namePos]) - of nkProcDef, nkMethodDef, nkConverterDef, nkMacroDef, nkTemplateDef, - nkClosure: + result = transformOuterProc(o, n.sons[namePos], it) + of nkProcDef, nkMethodDef, nkConverterDef, nkMacroDef, nkTemplateDef: # don't recurse here: discard + of nkClosure: + if n.sons[0].kind == nkSym: + var local = n.sons[0].sym + if isInnerProc(local, o.fn) and o.processed.contains(local.id): + o.processed.excl(local.id) + let body = local.getBody + let newBody = transformOuterProcBody(o, body, initIter(local)) + if newBody != nil: + local.ast.sons[bodyPos] = newBody of nkHiddenStdConv, nkHiddenSubConv, nkConv: - let x = transformOuterProc(o, n.sons[1]) + let x = transformOuterProc(o, n.sons[1], it) if x != nil: n.sons[1] = x result = transformOuterConv(n) of nkYieldStmt: - if o.isIter: result = transformYield(o, n) - else: outerProcSons(o, n) + if it.fn.kind == skClosureIterator: result = transformYield(o, n, it) + else: outerProcSons(o, n, it) of nkReturnStmt: - if o.isIter: result = transformReturn(o, n) - else: outerProcSons(o, n) - else: - outerProcSons(o, n) - -proc liftIterator(c: POuterContext, body: PNode): PNode = - let iter = c.fn - result = newNodeI(nkStmtList, iter.info) - var gs = newNodeI(nkGotoState, iter.info) - gs.add(indirectAccess(newSymNode(c.closureParam), c.state, iter.info)) - result.add(gs) - var state0 = newNodeI(nkState, iter.info) - state0.add(newIntNode(nkIntLit, 0)) - result.add(state0) - - let newBody = transformOuterProc(c, body) - if newBody != nil: - result.add(newBody) + if it.fn.kind == skClosureIterator: result = transformReturn(o, n, it) + else: outerProcSons(o, n, it) else: - result.add(body) - - var stateAsgnStmt = newNodeI(nkAsgn, iter.info) - stateAsgnStmt.add(indirectAccess(newSymNode(c.closureParam), - c.state,iter.info)) - stateAsgnStmt.add(newIntTypeNode(nkIntLit, -1, getSysType(tyInt))) - result.add(stateAsgnStmt) + outerProcSons(o, n, it) proc liftLambdas*(fn: PSym, body: PNode): PNode = # XXX gCmd == cmdCompileToJS does not suffice! The compiletime stuff needs # the transformation even when compiling to JS ... - if body.kind == nkEmpty or gCmd == cmdCompileToJS: + if body.kind == nkEmpty or gCmd == cmdCompileToJS or + fn.skipGenericOwner.kind != skModule: # ignore forward declaration: result = body else: + #if fn.name.s == "cbOuter": + # echo rendertree(fn.ast, {renderIds}) var o = newOuterContext(fn) let ex = closureCreationPoint(body) - o.currentEnv = newEnv(fn, nil, ex) - # put all params into the environment so they can be captured: - let params = fn.typ.n - for i in 1.. <params.len: - if params.sons[i].kind != nkSym: - internalError(params.info, "liftLambdas: strange params") - let param = params.sons[i].sym - idTablePut(o.localsToEnv, param, o.currentEnv) - # put the 'result' into the environment so it can be captured: - let ast = fn.ast - if resultPos < sonsLen(ast) and ast.sons[resultPos].kind == nkSym: - idTablePut(o.localsToEnv, ast.sons[resultPos].sym, o.currentEnv) - searchForInnerProcs(o, body) - if o.isIter: - result = liftIterator(o, ex) + let env = newEnv(o, nil, ex, fn) + addParamsToEnv(fn, env) + searchForInnerProcs(o, body, env) + createEnvironments(o) + if fn.kind == skClosureIterator: + result = transformOuterProcBody(o, body, initIter(fn)) else: - discard transformOuterProc(o, body) + discard transformOuterProcBody(o, body, initIter(fn)) result = ex + finishEnvironments(o) + #if fn.name.s == "cbOuter": + # echo rendertree(result, {renderIds}) proc liftLambdasForTopLevel*(module: PSym, body: PNode): PNode = if body.kind == nkEmpty or gCmd == cmdCompileToJS: @@ -768,9 +907,11 @@ proc liftLambdasForTopLevel*(module: PSym, body: PNode): PNode = else: var o = newOuterContext(module) let ex = closureCreationPoint(body) - o.currentEnv = newEnv(module, nil, ex) - searchForInnerProcs(o, body) - discard transformOuterProc(o, body) + let env = newEnv(o, nil, ex, module) + searchForInnerProcs(o, body, env) + createEnvironments(o) + discard transformOuterProc(o, body, initIter(module)) + finishEnvironments(o) result = ex # ------------------- iterator transformation -------------------------------- @@ -846,10 +987,6 @@ proc liftForLoop*(body: PNode): PNode = loopBody.sons[0] = v2 var bs = newNodeI(nkBreakState, body.info) - #if not env.isNil: - # bs.addSon(indirectAccess(env, - # newSym(skField, getIdent":state", env, env.info), body.info)) - #else: bs.addSon(call.sons[0]) loopBody.sons[1] = bs loopBody.sons[2] = body[L-1] diff --git a/compiler/lowerings.nim b/compiler/lowerings.nim index e2afa4362..5b61a9cae 100644 --- a/compiler/lowerings.nim +++ b/compiler/lowerings.nim @@ -64,6 +64,22 @@ proc createObj*(owner: PSym, info: TLineInfo): PType = incl result.flags, tfFinal result.n = newNodeI(nkRecList, info) +proc rawAddField*(obj: PType; field: PSym) = + assert field.kind == skField + field.position = sonsLen(obj.n) + addSon(obj.n, newSymNode(field)) + +proc rawIndirectAccess*(a: PNode; field: PSym; info: TLineInfo): PNode = + # returns a[].field as a node + assert field.kind == skField + var deref = newNodeI(nkHiddenDeref, info) + deref.typ = a.typ.skipTypes(abstractInst).sons[0] + addSon(deref, a) + result = newNodeI(nkDotExpr, info) + addSon(result, deref) + addSon(result, newSymNode(field)) + result.typ = field.typ + proc addField*(obj: PType; s: PSym) = # because of 'gensym' support, we have to mangle the name with its ID. # This is hacky but the clean solution is much more complex than it looks. @@ -74,6 +90,16 @@ proc addField*(obj: PType; s: PSym) = field.position = sonsLen(obj.n) addSon(obj.n, newSymNode(field)) +proc addUniqueField*(obj: PType; s: PSym) = + let fieldName = getIdent(s.name.s & $s.id) + if lookupInRecord(obj.n, fieldName) == nil: + var field = newSym(skField, fieldName, s.owner, s.info) + let t = skipIntLit(s.typ) + field.typ = t + assert t.kind != tyStmt + field.position = sonsLen(obj.n) + addSon(obj.n, newSymNode(field)) + proc newDotExpr(obj, b: PSym): PNode = result = newNodeI(nkDotExpr, obj.info) let field = getSymFromList(obj.typ.n, getIdent(b.name.s & $b.id)) @@ -95,13 +121,28 @@ proc indirectAccess*(a: PNode, b: string, info: TLineInfo): PNode = t = t.sons[0] if t == nil: break t = t.skipTypes(abstractInst) - assert field != nil, b + #if field == nil: + # debug deref.typ + # echo deref.typ.id + internalAssert field != nil addSon(deref, a) result = newNodeI(nkDotExpr, info) addSon(result, deref) addSon(result, newSymNode(field)) result.typ = field.typ +proc getFieldFromObj*(t: PType; v: PSym): PSym = + assert v.kind != skField + let fieldName = getIdent(v.name.s & $v.id) + var t = t + while true: + assert t.kind == tyObject + result = getSymFromList(t.n, fieldName) + if result != nil: break + t = t.sons[0] + if t == nil: break + t = t.skipTypes(abstractInst) + proc indirectAccess*(a: PNode, b: PSym, info: TLineInfo): PNode = # returns a[].b as a node result = indirectAccess(a, b.name.s & $b.id, info) diff --git a/compiler/magicsys.nim b/compiler/magicsys.nim index b4f6e043d..475326161 100644 --- a/compiler/magicsys.nim +++ b/compiler/magicsys.nim @@ -27,6 +27,8 @@ var gSysTypes: array[TTypeKind, PType] compilerprocs: TStrTable +proc nilOrSysInt*: PType = gSysTypes[tyInt] + proc registerSysType(t: PType) = if gSysTypes[t.kind] == nil: gSysTypes[t.kind] = t diff --git a/compiler/pas2nim/nimrod.cfg b/compiler/pas2nim/nimrod.cfg deleted file mode 100644 index cfeda63ed..000000000 --- a/compiler/pas2nim/nimrod.cfg +++ /dev/null @@ -1,4 +0,0 @@ -# Use the modules of the compiler - -path: "$nimrod/compiler" - diff --git a/compiler/pas2nim/pas2nim.nim b/compiler/pas2nim/pas2nim.nim deleted file mode 100644 index d10028167..000000000 --- a/compiler/pas2nim/pas2nim.nim +++ /dev/null @@ -1,64 +0,0 @@ -# -# -# Pas2nim - Pascal to Nimrod source converter -# (c) Copyright 2012 Andreas Rumpf -# -# See the file "copying.txt", included in this -# distribution, for details about the copyright. -# - -import - strutils, os, parseopt, llstream, ast, renderer, options, msgs, - paslex, pasparse - -const - Version = "0.8" - Usage = """ -pas2nim - Pascal to Nimrod source converter - (c) 2012 Andreas Rumpf -Usage: pas2nim [options] inputfile [options] -Options: - -o, --out:FILE set output filename - --ref convert ^typ to ref typ (default: ptr typ) - --boot use special translation rules for the Nimrod compiler - -v, --version write pas2nim's version - -h, --help show this help -""" - -proc main(infile, outfile: string, flags: set[TParserFlag]) = - var stream = llStreamOpen(infile, fmRead) - if stream == nil: rawMessage(errCannotOpenFile, infile) - var p: TParser - openParser(p, infile, stream, flags) - var module = parseUnit(p) - closeParser(p) - renderModule(module, outfile) - -var - infile = "" - outfile = "" - flags: set[TParserFlag] = {} -for kind, key, val in getopt(): - case kind - of cmdArgument: infile = key - of cmdLongOption, cmdShortOption: - case key - of "help", "h": - stdout.write(Usage) - quit(0) - of "version", "v": - stdout.write(Version & "\n") - quit(0) - of "o", "out": outfile = val - of "ref": incl(flags, pfRefs) - of "boot": flags = flags + {pfRefs, pfMoreReplacements, pfImportBlackList} - else: stdout.writeln("[Error] unknown option: " & key) - of cmdEnd: assert(false) -if infile.len == 0: - # no filename has been given, so we show the help: - stdout.write(Usage) -else: - if outfile.len == 0: - outfile = changeFileExt(infile, "nim") - infile = addFileExt(infile, "pas") - main(infile, outfile, flags) diff --git a/compiler/pas2nim/paslex.nim b/compiler/pas2nim/paslex.nim deleted file mode 100644 index f24b0c420..000000000 --- a/compiler/pas2nim/paslex.nim +++ /dev/null @@ -1,570 +0,0 @@ -# -# -# Pas2nim - Pascal to Nimrod source converter -# (c) Copyright 2012 Andreas Rumpf -# -# See the file "copying.txt", included in this -# distribution, for details about the copyright. -# - -# This module implements a FreePascal scanner. This is an adaption from -# the scanner module. - -import - hashes, options, msgs, strutils, platform, idents, nimlexbase, llstream - -const - MaxLineLength* = 80 # lines longer than this lead to a warning - numChars*: TCharSet = {'0'..'9', 'a'..'z', 'A'..'Z'} - SymChars*: TCharSet = {'a'..'z', 'A'..'Z', '0'..'9', '\x80'..'\xFF'} - SymStartChars*: TCharSet = {'a'..'z', 'A'..'Z', '\x80'..'\xFF'} - OpChars*: TCharSet = {'+', '-', '*', '/', '<', '>', '!', '?', '^', '.', '|', - '=', ':', '%', '&', '$', '@', '~', '\x80'..'\xFF'} - -# keywords are sorted! - -type - TTokKind* = enum - pxInvalid, pxEof, - pxAnd, pxArray, pxAs, pxAsm, pxBegin, pxCase, pxClass, pxConst, - pxConstructor, pxDestructor, pxDiv, pxDo, pxDownto, pxElse, pxEnd, pxExcept, - pxExports, pxFinalization, pxFinally, pxFor, pxFunction, pxGoto, pxIf, - pxImplementation, pxIn, pxInherited, pxInitialization, pxInline, - pxInterface, pxIs, pxLabel, pxLibrary, pxMod, pxNil, pxNot, pxObject, pxOf, - pxOr, pxOut, pxPacked, pxProcedure, pxProgram, pxProperty, pxRaise, - pxRecord, pxRepeat, pxResourcestring, pxSet, pxShl, pxShr, pxThen, - pxThreadvar, pxTo, pxTry, pxType, pxUnit, pxUntil, pxUses, pxVar, pxWhile, - pxWith, pxXor, - pxComment, # ordinary comment - pxCommand, # {@} - pxAmp, # {&} - pxPer, # {%} - pxStrLit, pxSymbol, # a symbol - pxIntLit, pxInt64Lit, # long constant like 0x70fffffff or out of int range - pxFloatLit, pxParLe, pxParRi, pxBracketLe, pxBracketRi, pxComma, - pxSemiColon, pxColon, # operators - pxAsgn, pxEquals, pxDot, pxDotDot, pxHat, pxPlus, pxMinus, pxStar, pxSlash, - pxLe, pxLt, pxGe, pxGt, pxNeq, pxAt, pxStarDirLe, pxStarDirRi, pxCurlyDirLe, - pxCurlyDirRi - TTokKinds* = set[TTokKind] - -const - Keywords = ["and", "array", "as", "asm", "begin", "case", "class", "const", - "constructor", "destructor", "div", "do", "downto", "else", "end", "except", - "exports", "finalization", "finally", "for", "function", "goto", "if", - "implementation", "in", "inherited", "initialization", "inline", - "interface", "is", "label", "library", "mod", "nil", "not", "object", "of", - "or", "out", "packed", "procedure", "program", "property", "raise", - "record", "repeat", "resourcestring", "set", "shl", "shr", "then", - "threadvar", "to", "try", "type", "unit", "until", "uses", "var", "while", - "with", "xor"] - - firstKeyword = pxAnd - lastKeyword = pxXor - -type - TNumericalBase* = enum base10, base2, base8, base16 - TToken* = object - xkind*: TTokKind # the type of the token - ident*: PIdent # the parsed identifier - iNumber*: BiggestInt # the parsed integer literal - fNumber*: BiggestFloat # the parsed floating point literal - base*: TNumericalBase # the numerical base; only valid for int - # or float literals - literal*: string # the parsed (string) literal - - TLexer* = object of TBaseLexer - filename*: string - - -proc getTok*(L: var TLexer, tok: var TToken) -proc printTok*(tok: TToken) -proc `$`*(tok: TToken): string -# implementation - -var - dummyIdent: PIdent - gLinesCompiled: int - -proc fillToken(L: var TToken) = - L.xkind = pxInvalid - L.iNumber = 0 - L.literal = "" - L.fNumber = 0.0 - L.base = base10 - L.ident = dummyIdent # this prevents many bugs! - -proc openLexer*(lex: var TLexer, filename: string, inputstream: PLLStream) = - openBaseLexer(lex, inputstream) - lex.filename = filename - -proc closeLexer*(lex: var TLexer) = - inc(gLinesCompiled, lex.LineNumber) - closeBaseLexer(lex) - -proc getColumn(L: TLexer): int = - result = getColNumber(L, L.bufPos) - -proc getLineInfo*(L: TLexer): TLineInfo = - result = newLineInfo(L.filename, L.linenumber, getColNumber(L, L.bufpos)) - -proc lexMessage*(L: TLexer, msg: TMsgKind, arg = "") = - msgs.globalError(getLineInfo(L), msg, arg) - -proc lexMessagePos(L: var TLexer, msg: TMsgKind, pos: int, arg = "") = - var info = newLineInfo(L.filename, L.linenumber, pos - L.lineStart) - msgs.globalError(info, msg, arg) - -proc tokKindToStr*(k: TTokKind): string = - case k - of pxEof: result = "[EOF]" - of firstKeyword..lastKeyword: - result = Keywords[ord(k)-ord(firstKeyword)] - of pxInvalid, pxComment, pxStrLit: result = "string literal" - of pxCommand: result = "{@" - of pxAmp: result = "{&" - of pxPer: result = "{%" - of pxSymbol: result = "identifier" - of pxIntLit, pxInt64Lit: result = "integer literal" - of pxFloatLit: result = "floating point literal" - of pxParLe: result = "(" - of pxParRi: result = ")" - of pxBracketLe: result = "[" - of pxBracketRi: result = "]" - of pxComma: result = "," - of pxSemiColon: result = ";" - of pxColon: result = ":" - of pxAsgn: result = ":=" - of pxEquals: result = "=" - of pxDot: result = "." - of pxDotDot: result = ".." - of pxHat: result = "^" - of pxPlus: result = "+" - of pxMinus: result = "-" - of pxStar: result = "*" - of pxSlash: result = "/" - of pxLe: result = "<=" - of pxLt: result = "<" - of pxGe: result = ">=" - of pxGt: result = ">" - of pxNeq: result = "<>" - of pxAt: result = "@" - of pxStarDirLe: result = "(*$" - of pxStarDirRi: result = "*)" - of pxCurlyDirLe: result = "{$" - of pxCurlyDirRi: result = "}" - -proc `$`(tok: TToken): string = - case tok.xkind - of pxInvalid, pxComment, pxStrLit: result = tok.literal - of pxSymbol: result = tok.ident.s - of pxIntLit, pxInt64Lit: result = $tok.iNumber - of pxFloatLit: result = $tok.fNumber - else: result = tokKindToStr(tok.xkind) - -proc printTok(tok: TToken) = - writeln(stdout, $tok) - -proc setKeyword(L: var TLexer, tok: var TToken) = - var x = binaryStrSearch(keywords, toLower(tok.ident.s)) - if x < 0: tok.xkind = pxSymbol - else: tok.xKind = TTokKind(x + ord(firstKeyword)) - -proc matchUnderscoreChars(L: var TLexer, tok: var TToken, chars: TCharSet) = - # matches ([chars]_)* - var pos = L.bufpos # use registers for pos, buf - var buf = L.buf - while true: - if buf[pos] in chars: - add(tok.literal, buf[pos]) - inc(pos) - else: - break - if buf[pos] == '_': - add(tok.literal, '_') - inc(pos) - L.bufPos = pos - -proc isFloatLiteral(s: string): bool = - for i in countup(0, len(s)-1): - if s[i] in {'.', 'e', 'E'}: - return true - -proc getNumber2(L: var TLexer, tok: var TToken) = - var pos = L.bufpos + 1 # skip % - if not (L.buf[pos] in {'0'..'1'}): - # BUGFIX for %date% - tok.xkind = pxInvalid - add(tok.literal, '%') - inc(L.bufpos) - return - tok.base = base2 - var xi: BiggestInt = 0 - var bits = 0 - while true: - case L.buf[pos] - of 'A'..'Z', 'a'..'z', '2'..'9', '.': - lexMessage(L, errInvalidNumber) - inc(pos) - of '_': - inc(pos) - of '0', '1': - xi = `shl`(xi, 1) or (ord(L.buf[pos]) - ord('0')) - inc(pos) - inc(bits) - else: break - tok.iNumber = xi - if (bits > 32): tok.xkind = pxInt64Lit - else: tok.xkind = pxIntLit - L.bufpos = pos - -proc getNumber16(L: var TLexer, tok: var TToken) = - var pos = L.bufpos + 1 # skip $ - tok.base = base16 - var xi: BiggestInt = 0 - var bits = 0 - while true: - case L.buf[pos] - of 'G'..'Z', 'g'..'z', '.': - lexMessage(L, errInvalidNumber) - inc(pos) - of '_': inc(pos) - of '0'..'9': - xi = `shl`(xi, 4) or (ord(L.buf[pos]) - ord('0')) - inc(pos) - inc(bits, 4) - of 'a'..'f': - xi = `shl`(xi, 4) or (ord(L.buf[pos]) - ord('a') + 10) - inc(pos) - inc(bits, 4) - of 'A'..'F': - xi = `shl`(xi, 4) or (ord(L.buf[pos]) - ord('A') + 10) - inc(pos) - inc(bits, 4) - else: break - tok.iNumber = xi - if (bits > 32): - tok.xkind = pxInt64Lit - else: - tok.xkind = pxIntLit - L.bufpos = pos - -proc getNumber10(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.literal, '.') - inc(L.bufpos) - matchUnderscoreChars(L, tok, {'e', 'E', '+', '-', '0'..'9'}) - try: - if isFloatLiteral(tok.literal): - tok.fnumber = parseFloat(tok.literal) - tok.xkind = pxFloatLit - else: - tok.iNumber = parseInt(tok.literal) - if (tok.iNumber < low(int32)) or (tok.iNumber > high(int32)): - tok.xkind = pxInt64Lit - else: - tok.xkind = pxIntLit - except EInvalidValue: - lexMessage(L, errInvalidNumber, tok.literal) - except EOverflow: - lexMessage(L, errNumberOutOfRange, tok.literal) - -proc handleCRLF(L: var TLexer, pos: int): int = - case L.buf[pos] - of CR: result = nimlexbase.handleCR(L, pos) - of LF: result = nimlexbase.handleLF(L, pos) - else: result = pos - -proc getString(L: var TLexer, tok: var TToken) = - var xi: int - var pos = L.bufPos - var buf = L.buf - while true: - if buf[pos] == '\'': - inc(pos) - while true: - case buf[pos] - of CR, LF, nimlexbase.EndOfFile: - lexMessage(L, errClosingQuoteExpected) - break - of '\'': - inc(pos) - if buf[pos] == '\'': - inc(pos) - add(tok.literal, '\'') - else: - break - else: - add(tok.literal, buf[pos]) - inc(pos) - elif buf[pos] == '#': - inc(pos) - xi = 0 - case buf[pos] - of '$': - inc(pos) - xi = 0 - while true: - case buf[pos] - of '0'..'9': xi = (xi shl 4) or (ord(buf[pos]) - ord('0')) - of 'a'..'f': xi = (xi shl 4) or (ord(buf[pos]) - ord('a') + 10) - of 'A'..'F': xi = (xi shl 4) or (ord(buf[pos]) - ord('A') + 10) - else: break - inc(pos) - of '0'..'9': - xi = 0 - while buf[pos] in {'0'..'9'}: - xi = (xi * 10) + (ord(buf[pos]) - ord('0')) - inc(pos) - else: lexMessage(L, errInvalidCharacterConstant) - if (xi <= 255): add(tok.literal, chr(xi)) - else: lexMessage(L, errInvalidCharacterConstant) - else: - break - tok.xkind = pxStrLit - L.bufpos = pos - -proc getSymbol(L: var TLexer, tok: var TToken) = - var h: THash = 0 - var pos = L.bufpos - var buf = L.buf - while true: - var c = buf[pos] - case c - of 'a'..'z', '0'..'9', '\x80'..'\xFF': - h = h +% ord(c) - h = h +% h shl 10 - h = h xor (h shr 6) - of 'A'..'Z': - c = chr(ord(c) + (ord('a') - ord('A'))) # toLower() - h = h +% ord(c) - h = h +% h shl 10 - h = h xor (h shr 6) - of '_': discard - else: break - inc(pos) - h = h +% h shl 3 - h = h xor (h shr 11) - h = h +% h shl 15 - tok.ident = getIdent(addr(L.buf[L.bufpos]), pos - L.bufpos, h) - L.bufpos = pos - setKeyword(L, tok) - -proc scanLineComment(L: var TLexer, tok: var TToken) = - var pos = L.bufpos - var buf = L.buf - # a comment ends if the next line does not start with the // on the same - # column after only whitespace - tok.xkind = pxComment - var col = getColNumber(L, pos) - while true: - inc(pos, 2) # skip // - add(tok.literal, '#') - while not (buf[pos] in {CR, LF, nimlexbase.EndOfFile}): - add(tok.literal, buf[pos]) - inc(pos) - pos = handleCRLF(L, pos) - buf = L.buf - var indent = 0 - while buf[pos] == ' ': - inc(pos) - inc(indent) - if (col == indent) and (buf[pos] == '/') and (buf[pos + 1] == '/'): - tok.literal = tok.literal & "\n" - else: - break - L.bufpos = pos - -proc scanCurlyComment(L: var TLexer, tok: var TToken) = - var pos = L.bufpos - var buf = L.buf - tok.literal = "#" - tok.xkind = pxComment - while true: - case buf[pos] - of CR, LF: - pos = handleCRLF(L, pos) - buf = L.buf - add(tok.literal, "\n#") - of '}': - inc(pos) - break - of nimlexbase.EndOfFile: lexMessage(L, errTokenExpected, "}") - else: - add(tok.literal, buf[pos]) - inc(pos) - L.bufpos = pos - -proc scanStarComment(L: var TLexer, tok: var TToken) = - var pos = L.bufpos - var buf = L.buf - tok.literal = "#" - tok.xkind = pxComment - while true: - case buf[pos] - of CR, LF: - pos = handleCRLF(L, pos) - buf = L.buf - add(tok.literal, "\n#") - of '*': - inc(pos) - if buf[pos] == ')': - inc(pos) - break - else: - add(tok.literal, '*') - of nimlexbase.EndOfFile: - lexMessage(L, errTokenExpected, "*)") - else: - add(tok.literal, buf[pos]) - inc(pos) - L.bufpos = pos - -proc skip(L: var TLexer, tok: var TToken) = - var pos = L.bufpos - var buf = L.buf - while true: - case buf[pos] - of ' ', Tabulator: - inc(pos) # newline is special: - of CR, LF: - pos = handleCRLF(L, pos) - buf = L.buf - else: - break # EndOfFile also leaves the loop - L.bufpos = pos - -proc getTok(L: var TLexer, tok: var TToken) = - tok.xkind = pxInvalid - fillToken(tok) - skip(L, tok) - var c = L.buf[L.bufpos] - if c in SymStartChars: - getSymbol(L, tok) - elif c in {'0'..'9'}: - getNumber10(L, tok) - else: - case c - of ';': - tok.xkind = pxSemicolon - inc(L.bufpos) - of '/': - if L.buf[L.bufpos + 1] == '/': - scanLineComment(L, tok) - else: - tok.xkind = pxSlash - inc(L.bufpos) - of ',': - tok.xkind = pxComma - inc(L.bufpos) - of '(': - inc(L.bufpos) - if (L.buf[L.bufPos] == '*'): - if (L.buf[L.bufPos + 1] == '$'): - inc(L.bufpos, 2) - skip(L, tok) - getSymbol(L, tok) - tok.xkind = pxStarDirLe - else: - inc(L.bufpos) - scanStarComment(L, tok) - else: - tok.xkind = pxParLe - of '*': - inc(L.bufpos) - if L.buf[L.bufpos] == ')': - inc(L.bufpos) - tok.xkind = pxStarDirRi - else: - tok.xkind = pxStar - of ')': - tok.xkind = pxParRi - inc(L.bufpos) - of '[': - inc(L.bufpos) - tok.xkind = pxBracketLe - of ']': - inc(L.bufpos) - tok.xkind = pxBracketRi - of '.': - inc(L.bufpos) - if L.buf[L.bufpos] == '.': - tok.xkind = pxDotDot - inc(L.bufpos) - else: - tok.xkind = pxDot - of '{': - inc(L.bufpos) - case L.buf[L.bufpos] - of '$': - inc(L.bufpos) - skip(L, tok) - getSymbol(L, tok) - tok.xkind = pxCurlyDirLe - of '&': - inc(L.bufpos) - tok.xkind = pxAmp - of '%': - inc(L.bufpos) - tok.xkind = pxPer - of '@': - inc(L.bufpos) - tok.xkind = pxCommand - else: scanCurlyComment(L, tok) - of '+': - tok.xkind = pxPlus - inc(L.bufpos) - of '-': - tok.xkind = pxMinus - inc(L.bufpos) - of ':': - inc(L.bufpos) - if L.buf[L.bufpos] == '=': - inc(L.bufpos) - tok.xkind = pxAsgn - else: - tok.xkind = pxColon - of '<': - inc(L.bufpos) - if L.buf[L.bufpos] == '>': - inc(L.bufpos) - tok.xkind = pxNeq - elif L.buf[L.bufpos] == '=': - inc(L.bufpos) - tok.xkind = pxLe - else: - tok.xkind = pxLt - of '>': - inc(L.bufpos) - if L.buf[L.bufpos] == '=': - inc(L.bufpos) - tok.xkind = pxGe - else: - tok.xkind = pxGt - of '=': - tok.xkind = pxEquals - inc(L.bufpos) - of '@': - tok.xkind = pxAt - inc(L.bufpos) - of '^': - tok.xkind = pxHat - inc(L.bufpos) - of '}': - tok.xkind = pxCurlyDirRi - inc(L.bufpos) - of '\'', '#': - getString(L, tok) - of '$': - getNumber16(L, tok) - of '%': - getNumber2(L, tok) - of nimlexbase.EndOfFile: - tok.xkind = pxEof - else: - tok.literal = c & "" - tok.xkind = pxInvalid - lexMessage(L, errInvalidToken, c & " (\\" & $(ord(c)) & ')') - inc(L.bufpos) diff --git a/compiler/pas2nim/pasparse.nim b/compiler/pas2nim/pasparse.nim deleted file mode 100644 index a6f8363f6..000000000 --- a/compiler/pas2nim/pasparse.nim +++ /dev/null @@ -1,1513 +0,0 @@ -# -# -# Pas2nim - Pascal to Nimrod source converter -# (c) Copyright 2012 Andreas Rumpf -# -# See the file "copying.txt", included in this -# distribution, for details about the copyright. -# - -# This module implements the parser of the Pascal variant Nimrod is written in. -# It transfers a Pascal module into a Nimrod AST. Then the renderer can be -# used to convert the AST to its text representation. - -import - os, llstream, paslex, idents, strutils, ast, astalgo, msgs, options - -type - TSection = enum - seImplementation, seInterface - TContext = enum - conExpr, conStmt, conTypeDesc - TParserFlag* = enum - pfRefs, ## use "ref" instead of "ptr" for Pascal's ^typ - pfMoreReplacements, ## use more than the default replacements - pfImportBlackList ## use import blacklist - TParser*{.final.} = object - section: TSection - inParamList: bool - context: TContext # needed for the @emit command - lastVarSection: PNode - lex: TLexer - tok: TToken - repl: TIdTable # replacements - flags: set[TParserFlag] - - TReplaceTuple* = array[0..1, string] - -const - ImportBlackList*: array[1..3, string] = ["nsystem", "sysutils", "charsets"] - stdReplacements*: array[1..19, TReplaceTuple] = [["include", "incl"], - ["exclude", "excl"], ["pchar", "cstring"], ["assignfile", "open"], - ["integer", "int"], ["longword", "int32"], ["cardinal", "int"], - ["boolean", "bool"], ["shortint", "int8"], ["smallint", "int16"], - ["longint", "int32"], ["byte", "int8"], ["word", "int16"], - ["single", "float32"], ["double", "float64"], ["real", "float"], - ["length", "len"], ["len", "length"], ["setlength", "setlen"]] - nimReplacements*: array[1..35, TReplaceTuple] = [["nimread", "read"], - ["nimwrite", "write"], ["nimclosefile", "close"], ["closefile", "close"], - ["openfile", "open"], ["nsystem", "system"], ["ntime", "times"], - ["nos", "os"], ["nmath", "math"], ["ncopy", "copy"], ["addChar", "add"], - ["halt", "quit"], ["nobject", "TObject"], ["eof", "EndOfFile"], - ["input", "stdin"], ["output", "stdout"], ["addu", "`+%`"], - ["subu", "`-%`"], ["mulu", "`*%`"], ["divu", "`/%`"], ["modu", "`%%`"], - ["ltu", "`<%`"], ["leu", "`<=%`"], ["shlu", "`shl`"], ["shru", "`shr`"], - ["assigned", "not isNil"], ["eintoverflow", "EOverflow"], ["format", "`%`"], - ["snil", "nil"], ["tostringf", "$"], ["ttextfile", "tfile"], - ["tbinaryfile", "tfile"], ["strstart", "0"], ["nl", "\"\\n\""], - ["tostring", "$"]] - -proc parseUnit*(p: var TParser): PNode -proc openParser*(p: var TParser, filename: string, inputStream: PLLStream, - flags: set[TParserFlag] = {}) -proc closeParser*(p: var TParser) -proc exSymbol*(n: var PNode) -proc fixRecordDef*(n: var PNode) - # XXX: move these two to an auxiliary module - -# implementation - -proc openParser(p: var TParser, filename: string, - inputStream: PLLStream, flags: set[TParserFlag] = {}) = - openLexer(p.lex, filename, inputStream) - initIdTable(p.repl) - for i in countup(low(stdReplacements), high(stdReplacements)): - idTablePut(p.repl, getIdent(stdReplacements[i][0]), - getIdent(stdReplacements[i][1])) - if pfMoreReplacements in flags: - for i in countup(low(nimReplacements), high(nimReplacements)): - idTablePut(p.repl, getIdent(nimReplacements[i][0]), - getIdent(nimReplacements[i][1])) - p.flags = flags - -proc closeParser(p: var TParser) = closeLexer(p.lex) -proc getTok(p: var TParser) = getTok(p.lex, p.tok) - -proc parMessage(p: TParser, msg: TMsgKind, arg = "") = - lexMessage(p.lex, msg, arg) - -proc parLineInfo(p: TParser): TLineInfo = - result = getLineInfo(p.lex) - -proc skipCom(p: var TParser, n: PNode) = - while p.tok.xkind == pxComment: - if (n != nil): - if n.comment == nil: n.comment = p.tok.literal - else: add(n.comment, "\n" & p.tok.literal) - else: - parMessage(p, warnCommentXIgnored, p.tok.literal) - getTok(p) - -proc expectIdent(p: TParser) = - if p.tok.xkind != pxSymbol: - lexMessage(p.lex, errIdentifierExpected, $(p.tok)) - -proc eat(p: var TParser, xkind: TTokKind) = - if p.tok.xkind == xkind: getTok(p) - else: lexMessage(p.lex, errTokenExpected, tokKindToStr(xkind)) - -proc opt(p: var TParser, xkind: TTokKind) = - if p.tok.xkind == xkind: getTok(p) - -proc newNodeP(kind: TNodeKind, p: TParser): PNode = - result = newNodeI(kind, getLineInfo(p.lex)) - -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 createIdentNodeP(ident: PIdent, p: TParser): PNode = - result = newNodeP(nkIdent, p) - var x = PIdent(idTableGet(p.repl, ident)) - if x != nil: result.ident = x - else: result.ident = ident - -proc parseExpr(p: var TParser): PNode -proc parseStmt(p: var TParser): PNode -proc parseTypeDesc(p: var TParser, definition: PNode = nil): PNode - -proc parseEmit(p: var TParser, definition: PNode): PNode = - getTok(p) # skip 'emit' - result = ast.emptyNode - if p.tok.xkind != pxCurlyDirRi: - case p.context - of conExpr: - result = parseExpr(p) - of conStmt: - result = parseStmt(p) - if p.tok.xkind != pxCurlyDirRi: - var a = result - result = newNodeP(nkStmtList, p) - addSon(result, a) - while p.tok.xkind != pxCurlyDirRi: - addSon(result, parseStmt(p)) - of conTypeDesc: - result = parseTypeDesc(p, definition) - eat(p, pxCurlyDirRi) - -proc parseCommand(p: var TParser, definition: PNode = nil): PNode = - result = ast.emptyNode - getTok(p) - if p.tok.ident.id == getIdent("discard").id: - result = newNodeP(nkDiscardStmt, p) - getTok(p) - eat(p, pxCurlyDirRi) - addSon(result, parseExpr(p)) - elif p.tok.ident.id == getIdent("set").id: - getTok(p) - eat(p, pxCurlyDirRi) - result = parseExpr(p) - if result.kind == nkEmpty: internalError("emptyNode modified") - result.kind = nkCurly - elif p.tok.ident.id == getIdent("cast").id: - getTok(p) - eat(p, pxCurlyDirRi) - var a = parseExpr(p) - if (a.kind == nkCall) and (sonsLen(a) == 2): - result = newNodeP(nkCast, p) - addSon(result, a.sons[0]) - addSon(result, a.sons[1]) - else: - parMessage(p, errInvalidDirectiveX, $p.tok) - result = a - elif p.tok.ident.id == getIdent("emit").id: - result = parseEmit(p, definition) - elif p.tok.ident.id == getIdent("ignore").id: - getTok(p) - eat(p, pxCurlyDirRi) - while true: - case p.tok.xkind - of pxEof: - parMessage(p, errTokenExpected, "{@emit}") - of pxCommand: - getTok(p) - if p.tok.ident.id == getIdent("emit").id: - result = parseEmit(p, definition) - break - else: - while (p.tok.xkind != pxCurlyDirRi) and (p.tok.xkind != pxEof): - getTok(p) - eat(p, pxCurlyDirRi) - else: - getTok(p) # skip token - elif p.tok.ident.id == getIdent("ptr").id: - result = newNodeP(nkPtrTy, p) - getTok(p) - eat(p, pxCurlyDirRi) - elif p.tok.ident.id == getIdent("tuple").id: - result = newNodeP(nkTupleTy, p) - getTok(p) - eat(p, pxCurlyDirRi) - elif p.tok.ident.id == getIdent("acyclic").id: - result = newIdentNodeP(p.tok.ident, p) - getTok(p) - eat(p, pxCurlyDirRi) - else: - parMessage(p, errInvalidDirectiveX, $p.tok) - while true: - getTok(p) - if p.tok.xkind == pxCurlyDirRi or p.tok.xkind == pxEof: break - eat(p, pxCurlyDirRi) - result = ast.emptyNode - -proc getPrecedence(kind: TTokKind): int = - case kind - of pxDiv, pxMod, pxStar, pxSlash, pxShl, pxShr, pxAnd: result = 5 - of pxPlus, pxMinus, pxOr, pxXor: result = 4 - of pxIn, pxEquals, pxLe, pxLt, pxGe, pxGt, pxNeq, pxIs: result = 3 - else: result = -1 - -proc rangeExpr(p: var TParser): PNode = - var a = parseExpr(p) - if p.tok.xkind == pxDotDot: - result = newNodeP(nkRange, p) - addSon(result, a) - getTok(p) - skipCom(p, result) - addSon(result, parseExpr(p)) - else: - result = a - -proc bracketExprList(p: var TParser, first: PNode): PNode = - result = newNodeP(nkBracketExpr, p) - addSon(result, first) - getTok(p) - skipCom(p, result) - while true: - if p.tok.xkind == pxBracketRi: - getTok(p) - break - if p.tok.xkind == pxEof: - parMessage(p, errTokenExpected, tokKindToStr(pxBracketRi)) - break - var a = rangeExpr(p) - skipCom(p, a) - if p.tok.xkind == pxComma: - getTok(p) - skipCom(p, a) - addSon(result, a) - -proc exprColonEqExpr(p: var TParser, kind: TNodeKind, - tok: TTokKind): PNode = - var a = parseExpr(p) - if p.tok.xkind == tok: - result = newNodeP(kind, p) - getTok(p) - skipCom(p, result) - addSon(result, a) - addSon(result, parseExpr(p)) - else: - result = a - -proc exprListAux(p: var TParser, elemKind: TNodeKind, - endTok, sepTok: TTokKind, result: PNode) = - getTok(p) - skipCom(p, result) - while true: - if p.tok.xkind == endTok: - getTok(p) - break - if p.tok.xkind == pxEof: - parMessage(p, errTokenExpected, tokKindToStr(endTok)) - break - var a = exprColonEqExpr(p, elemKind, sepTok) - skipCom(p, a) - if (p.tok.xkind == pxComma) or (p.tok.xkind == pxSemicolon): - getTok(p) - skipCom(p, a) - addSon(result, a) - -proc qualifiedIdent(p: var TParser): PNode = - if p.tok.xkind == pxSymbol: - result = createIdentNodeP(p.tok.ident, p) - else: - parMessage(p, errIdentifierExpected, $p.tok) - return ast.emptyNode - getTok(p) - skipCom(p, result) - if p.tok.xkind == pxDot: - getTok(p) - skipCom(p, result) - if p.tok.xkind == pxSymbol: - var a = result - result = newNodeI(nkDotExpr, a.info) - addSon(result, a) - addSon(result, createIdentNodeP(p.tok.ident, p)) - getTok(p) - else: - parMessage(p, errIdentifierExpected, $p.tok) - -proc qualifiedIdentListAux(p: var TParser, endTok: TTokKind, - result: PNode) = - getTok(p) - skipCom(p, result) - while true: - if p.tok.xkind == endTok: - getTok(p) - break - if p.tok.xkind == pxEof: - parMessage(p, errTokenExpected, tokKindToStr(endTok)) - break - var a = qualifiedIdent(p) - skipCom(p, a) - if p.tok.xkind == pxComma: - getTok(p) - skipCom(p, a) - addSon(result, a) - -proc exprColonEqExprList(p: var TParser, kind, elemKind: TNodeKind, - endTok, sepTok: TTokKind): PNode = - result = newNodeP(kind, p) - exprListAux(p, elemKind, endTok, sepTok, result) - -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 identOrLiteral(p: var TParser): PNode = - case p.tok.xkind - of pxSymbol: - result = createIdentNodeP(p.tok.ident, p) - getTok(p) - of pxIntLit: - result = newIntNodeP(nkIntLit, p.tok.iNumber, p) - setBaseFlags(result, p.tok.base) - getTok(p) - of pxInt64Lit: - result = newIntNodeP(nkInt64Lit, p.tok.iNumber, p) - setBaseFlags(result, p.tok.base) - getTok(p) - of pxFloatLit: - result = newFloatNodeP(nkFloatLit, p.tok.fNumber, p) - setBaseFlags(result, p.tok.base) - getTok(p) - of pxStrLit: - if len(p.tok.literal) != 1: result = newStrNodeP(nkStrLit, p.tok.literal, p) - else: result = newIntNodeP(nkCharLit, ord(p.tok.literal[0]), p) - getTok(p) - of pxNil: - result = newNodeP(nkNilLit, p) - getTok(p) - of pxParLe: - # () constructor - result = exprColonEqExprList(p, nkPar, nkExprColonExpr, pxParRi, pxColon) - #if hasSonWith(result, nkExprColonExpr) then - # replaceSons(result, nkExprColonExpr, nkExprEqExpr) - if (sonsLen(result) > 1) and not hasSonWith(result, nkExprColonExpr): - result.kind = nkBracket # is an array constructor - of pxBracketLe: - # [] constructor - result = newNodeP(nkBracket, p) - getTok(p) - skipCom(p, result) - while (p.tok.xkind != pxBracketRi) and (p.tok.xkind != pxEof): - var a = rangeExpr(p) - if a.kind == nkRange: - result.kind = nkCurly # it is definitely a set literal - opt(p, pxComma) - skipCom(p, a) - assert(a != nil) - addSon(result, a) - eat(p, pxBracketRi) - of pxCommand: - result = parseCommand(p) - else: - parMessage(p, errExprExpected, $(p.tok)) - getTok(p) # we must consume a token here to prevend endless loops! - result = ast.emptyNode - if result.kind != nkEmpty: skipCom(p, result) - -proc primary(p: var TParser): PNode = - # prefix operator? - if (p.tok.xkind == pxNot) or (p.tok.xkind == pxMinus) or - (p.tok.xkind == pxPlus): - result = newNodeP(nkPrefix, p) - var a = newIdentNodeP(getIdent($p.tok), p) - addSon(result, a) - getTok(p) - skipCom(p, a) - addSon(result, primary(p)) - return - elif p.tok.xkind == pxAt: - result = newNodeP(nkAddr, p) - var a = newIdentNodeP(getIdent($p.tok), p) - getTok(p) - if p.tok.xkind == pxBracketLe: - result = newNodeP(nkPrefix, p) - addSon(result, a) - addSon(result, identOrLiteral(p)) - else: - addSon(result, primary(p)) - return - result = identOrLiteral(p) - while true: - case p.tok.xkind - of pxParLe: - var a = result - result = newNodeP(nkCall, p) - addSon(result, a) - exprListAux(p, nkExprEqExpr, pxParRi, pxEquals, result) - of pxDot: - var a = result - result = newNodeP(nkDotExpr, p) - addSon(result, a) - getTok(p) # skip '.' - skipCom(p, result) - if p.tok.xkind == pxSymbol: - addSon(result, createIdentNodeP(p.tok.ident, p)) - getTok(p) - else: - parMessage(p, errIdentifierExpected, $p.tok) - of pxHat: - var a = result - result = newNodeP(nkBracketExpr, p) - addSon(result, a) - getTok(p) - of pxBracketLe: - result = bracketExprList(p, result) - else: break - -proc lowestExprAux(p: var TParser, v: var PNode, limit: int): TTokKind = - var - nextop: TTokKind - v2, node, opNode: PNode - v = primary(p) # expand while operators have priorities higher than 'limit' - var op = p.tok.xkind - var opPred = getPrecedence(op) - while (opPred > limit): - node = newNodeP(nkInfix, p) - opNode = newIdentNodeP(getIdent($(p.tok)), p) # skip operator: - getTok(p) - case op - of pxPlus: - case p.tok.xkind - of pxPer: - getTok(p) - eat(p, pxCurlyDirRi) - opNode.ident = getIdent("+%") - of pxAmp: - getTok(p) - eat(p, pxCurlyDirRi) - opNode.ident = getIdent("&") - else: - discard - of pxMinus: - if p.tok.xkind == pxPer: - getTok(p) - eat(p, pxCurlyDirRi) - opNode.ident = getIdent("-%") - of pxEquals: - opNode.ident = getIdent("==") - of pxNeq: - opNode.ident = getIdent("!=") - else: - discard - skipCom(p, opNode) # read sub-expression with higher priority - nextop = lowestExprAux(p, v2, opPred) - addSon(node, opNode) - addSon(node, v) - addSon(node, v2) - v = node - op = nextop - opPred = getPrecedence(nextop) - result = op # return first untreated operator - -proc fixExpr(n: PNode): PNode = - result = n - case n.kind - of nkInfix: - if n.sons[1].kind == nkBracket: n.sons[1].kind = nkCurly - if n.sons[2].kind == nkBracket: n.sons[2].kind = nkCurly - if (n.sons[0].kind == nkIdent): - if (n.sons[0].ident.id == getIdent("+").id): - if (n.sons[1].kind == nkCharLit) and (n.sons[2].kind == nkStrLit) and - (n.sons[2].strVal == ""): - result = newStrNode(nkStrLit, chr(int(n.sons[1].intVal)) & "") - result.info = n.info - return # do not process sons as they don't exist anymore - elif (n.sons[1].kind in {nkCharLit, nkStrLit}) or - (n.sons[2].kind in {nkCharLit, nkStrLit}): - n.sons[0].ident = getIdent("&") # fix operator - else: - discard - if not (n.kind in {nkEmpty..nkNilLit}): - for i in countup(0, sonsLen(n) - 1): result.sons[i] = fixExpr(n.sons[i]) - -proc parseExpr(p: var TParser): PNode = - var oldcontext = p.context - p.context = conExpr - if p.tok.xkind == pxCommand: - result = parseCommand(p) - else: - discard lowestExprAux(p, result, - 1) - result = fixExpr(result) - p.context = oldcontext - -proc parseExprStmt(p: var TParser): PNode = - var info = parLineInfo(p) - var a = parseExpr(p) - if p.tok.xkind == pxAsgn: - getTok(p) - skipCom(p, a) - var b = parseExpr(p) - result = newNodeI(nkAsgn, info) - addSon(result, a) - addSon(result, b) - else: - result = a - -proc inImportBlackList(ident: PIdent): bool = - for i in countup(low(ImportBlackList), high(ImportBlackList)): - if ident.id == getIdent(ImportBlackList[i]).id: - return true - -proc parseUsesStmt(p: var TParser): PNode = - var a: PNode - result = newNodeP(nkImportStmt, p) - getTok(p) # skip `import` - skipCom(p, result) - while true: - case p.tok.xkind - of pxEof: break - of pxSymbol: a = newIdentNodeP(p.tok.ident, p) - else: - parMessage(p, errIdentifierExpected, $(p.tok)) - break - getTok(p) # skip identifier, string - skipCom(p, a) - if pfImportBlackList notin p.flags or not inImportBlackList(a.ident): - addSon(result, createIdentNodeP(a.ident, p)) - if p.tok.xkind == pxComma: - getTok(p) - skipCom(p, a) - else: - break - if sonsLen(result) == 0: result = ast.emptyNode - -proc parseIncludeDir(p: var TParser): PNode = - result = newNodeP(nkIncludeStmt, p) - getTok(p) # skip `include` - var filename = "" - while true: - case p.tok.xkind - of pxSymbol, pxDot, pxDotDot, pxSlash: - add(filename, $p.tok) - getTok(p) - of pxStrLit: - filename = p.tok.literal - getTok(p) - break - of pxCurlyDirRi: - break - else: - parMessage(p, errIdentifierExpected, $p.tok) - break - addSon(result, newStrNodeP(nkStrLit, changeFileExt(filename, "nim"), p)) - if filename == "config.inc": result = ast.emptyNode - -proc definedExprAux(p: var TParser): PNode = - result = newNodeP(nkCall, p) - addSon(result, newIdentNodeP(getIdent("defined"), p)) - expectIdent(p) - addSon(result, createIdentNodeP(p.tok.ident, p)) - getTok(p) - -proc isHandledDirective(p: TParser): bool = - if p.tok.xkind in {pxCurlyDirLe, pxStarDirLe}: - case toLower(p.tok.ident.s) - of "else", "endif": result = false - else: result = true - -proc parseStmtList(p: var TParser): PNode = - result = newNodeP(nkStmtList, p) - while true: - case p.tok.xkind - of pxEof: - break - of pxCurlyDirLe, pxStarDirLe: - if not isHandledDirective(p): break - else: - discard - addSon(result, parseStmt(p)) - if sonsLen(result) == 1: result = result.sons[0] - -proc parseIfDirAux(p: var TParser, result: PNode) = - addSon(result.sons[0], parseStmtList(p)) - if p.tok.xkind in {pxCurlyDirLe, pxStarDirLe}: - var endMarker = succ(p.tok.xkind) - if toLower(p.tok.ident.s) == "else": - var s = newNodeP(nkElse, p) - while p.tok.xkind != pxEof and p.tok.xkind != endMarker: getTok(p) - eat(p, endMarker) - addSon(s, parseStmtList(p)) - addSon(result, s) - if p.tok.xkind in {pxCurlyDirLe, pxStarDirLe}: - endMarker = succ(p.tok.xkind) - if toLower(p.tok.ident.s) == "endif": - while p.tok.xkind != pxEof and p.tok.xkind != endMarker: getTok(p) - eat(p, endMarker) - else: - parMessage(p, errXExpected, "{$endif}") - else: - parMessage(p, errXExpected, "{$endif}") - -proc parseIfdefDir(p: var TParser, endMarker: TTokKind): PNode = - result = newNodeP(nkWhenStmt, p) - addSon(result, newNodeP(nkElifBranch, p)) - getTok(p) - addSon(result.sons[0], definedExprAux(p)) - eat(p, endMarker) - parseIfDirAux(p, result) - -proc parseIfndefDir(p: var TParser, endMarker: TTokKind): PNode = - result = newNodeP(nkWhenStmt, p) - addSon(result, newNodeP(nkElifBranch, p)) - getTok(p) - var e = newNodeP(nkCall, p) - addSon(e, newIdentNodeP(getIdent("not"), p)) - addSon(e, definedExprAux(p)) - eat(p, endMarker) - addSon(result.sons[0], e) - parseIfDirAux(p, result) - -proc parseIfDir(p: var TParser, endMarker: TTokKind): PNode = - result = newNodeP(nkWhenStmt, p) - addSon(result, newNodeP(nkElifBranch, p)) - getTok(p) - addSon(result.sons[0], parseExpr(p)) - eat(p, endMarker) - parseIfDirAux(p, result) - -proc parseDirective(p: var TParser): PNode = - result = ast.emptyNode - if not (p.tok.xkind in {pxCurlyDirLe, pxStarDirLe}): return - var endMarker = succ(p.tok.xkind) - if p.tok.ident != nil: - case toLower(p.tok.ident.s) - of "include": - result = parseIncludeDir(p) - eat(p, endMarker) - of "if": result = parseIfDir(p, endMarker) - of "ifdef": result = parseIfdefDir(p, endMarker) - of "ifndef": result = parseIfndefDir(p, endMarker) - else: - # skip unknown compiler directive - while p.tok.xkind != pxEof and p.tok.xkind != endMarker: getTok(p) - eat(p, endMarker) - else: - eat(p, endMarker) - -proc parseRaise(p: var TParser): PNode = - result = newNodeP(nkRaiseStmt, p) - getTok(p) - skipCom(p, result) - if p.tok.xkind != pxSemicolon: addSon(result, parseExpr(p)) - else: addSon(result, ast.emptyNode) - -proc parseIf(p: var TParser): PNode = - result = newNodeP(nkIfStmt, p) - while true: - getTok(p) # skip ``if`` - var branch = newNodeP(nkElifBranch, p) - skipCom(p, branch) - addSon(branch, parseExpr(p)) - eat(p, pxThen) - skipCom(p, branch) - addSon(branch, parseStmt(p)) - skipCom(p, branch) - addSon(result, branch) - if p.tok.xkind == pxElse: - getTok(p) - if p.tok.xkind != pxIf: - # ordinary else part: - branch = newNodeP(nkElse, p) - skipCom(p, result) # BUGFIX - addSon(branch, parseStmt(p)) - addSon(result, branch) - break - else: - break - -proc parseWhile(p: var TParser): PNode = - result = newNodeP(nkWhileStmt, p) - getTok(p) - skipCom(p, result) - addSon(result, parseExpr(p)) - eat(p, pxDo) - skipCom(p, result) - addSon(result, parseStmt(p)) - -proc parseRepeat(p: var TParser): PNode = - result = newNodeP(nkWhileStmt, p) - getTok(p) - skipCom(p, result) - addSon(result, newIdentNodeP(getIdent("true"), p)) - var s = newNodeP(nkStmtList, p) - while p.tok.xkind != pxEof and p.tok.xkind != pxUntil: - addSon(s, parseStmt(p)) - eat(p, pxUntil) - var a = newNodeP(nkIfStmt, p) - skipCom(p, a) - var b = newNodeP(nkElifBranch, p) - var c = newNodeP(nkBreakStmt, p) - addSon(c, ast.emptyNode) - addSon(b, parseExpr(p)) - skipCom(p, a) - addSon(b, c) - addSon(a, b) - if b.sons[0].kind == nkIdent and b.sons[0].ident.id == getIdent("false").id: - discard - else: - addSon(s, a) - addSon(result, s) - -proc parseCase(p: var TParser): PNode = - var b: PNode - result = newNodeP(nkCaseStmt, p) - getTok(p) - addSon(result, parseExpr(p)) - eat(p, pxOf) - skipCom(p, result) - while (p.tok.xkind != pxEnd) and (p.tok.xkind != pxEof): - if p.tok.xkind == pxElse: - b = newNodeP(nkElse, p) - getTok(p) - else: - b = newNodeP(nkOfBranch, p) - while (p.tok.xkind != pxEof) and (p.tok.xkind != pxColon): - addSon(b, rangeExpr(p)) - opt(p, pxComma) - skipCom(p, b) - eat(p, pxColon) - skipCom(p, b) - addSon(b, parseStmt(p)) - addSon(result, b) - if b.kind == nkElse: break - eat(p, pxEnd) - -proc parseTry(p: var TParser): PNode = - result = newNodeP(nkTryStmt, p) - getTok(p) - skipCom(p, result) - var b = newNodeP(nkStmtList, p) - while not (p.tok.xkind in {pxFinally, pxExcept, pxEof, pxEnd}): - addSon(b, parseStmt(p)) - addSon(result, b) - if p.tok.xkind == pxExcept: - getTok(p) - while p.tok.ident.id == getIdent("on").id: - b = newNodeP(nkExceptBranch, p) - getTok(p) - var e = qualifiedIdent(p) - if p.tok.xkind == pxColon: - getTok(p) - e = qualifiedIdent(p) - addSon(b, e) - eat(p, pxDo) - addSon(b, parseStmt(p)) - addSon(result, b) - if p.tok.xkind == pxCommand: discard parseCommand(p) - if p.tok.xkind == pxElse: - b = newNodeP(nkExceptBranch, p) - getTok(p) - addSon(b, parseStmt(p)) - addSon(result, b) - if p.tok.xkind == pxFinally: - b = newNodeP(nkFinally, p) - getTok(p) - var e = newNodeP(nkStmtList, p) - while (p.tok.xkind != pxEof) and (p.tok.xkind != pxEnd): - addSon(e, parseStmt(p)) - if sonsLen(e) == 0: addSon(e, newNodeP(nkNilLit, p)) - addSon(result, e) - eat(p, pxEnd) - -proc parseFor(p: var TParser): PNode = - result = newNodeP(nkForStmt, p) - getTok(p) - skipCom(p, result) - expectIdent(p) - addSon(result, createIdentNodeP(p.tok.ident, p)) - getTok(p) - eat(p, pxAsgn) - var a = parseExpr(p) - var b = ast.emptyNode - var c = newNodeP(nkCall, p) - if p.tok.xkind == pxTo: - addSon(c, newIdentNodeP(getIdent("countup"), p)) - getTok(p) - b = parseExpr(p) - elif p.tok.xkind == pxDownto: - addSon(c, newIdentNodeP(getIdent("countdown"), p)) - getTok(p) - b = parseExpr(p) - else: - parMessage(p, errTokenExpected, tokKindToStr(pxTo)) - addSon(c, a) - addSon(c, b) - eat(p, pxDo) - skipCom(p, result) - addSon(result, c) - addSon(result, parseStmt(p)) - -proc parseParam(p: var TParser): PNode = - var a: PNode - result = newNodeP(nkIdentDefs, p) - var v = ast.emptyNode - case p.tok.xkind - of pxConst: - getTok(p) - of pxVar: - getTok(p) - v = newNodeP(nkVarTy, p) - of pxOut: - getTok(p) - v = newNodeP(nkVarTy, p) - else: - discard - while true: - case p.tok.xkind - of pxSymbol: a = createIdentNodeP(p.tok.ident, p) - of pxColon, pxEof, pxParRi, pxEquals: break - else: - parMessage(p, errIdentifierExpected, $p.tok) - return - getTok(p) # skip identifier - skipCom(p, a) - if p.tok.xkind == pxComma: - getTok(p) - skipCom(p, a) - addSon(result, a) - if p.tok.xkind == pxColon: - getTok(p) - skipCom(p, result) - if v.kind != nkEmpty: addSon(v, parseTypeDesc(p)) - else: v = parseTypeDesc(p) - addSon(result, v) - else: - addSon(result, ast.emptyNode) - if p.tok.xkind != pxEquals: - parMessage(p, errColonOrEqualsExpected, $p.tok) - if p.tok.xkind == pxEquals: - getTok(p) - skipCom(p, result) - addSon(result, parseExpr(p)) - else: - addSon(result, ast.emptyNode) - -proc parseParamList(p: var TParser): PNode = - var a: PNode - result = newNodeP(nkFormalParams, p) - addSon(result, ast.emptyNode) # return type - if p.tok.xkind == pxParLe: - p.inParamList = true - getTok(p) - skipCom(p, result) - while true: - case p.tok.xkind - of pxSymbol, pxConst, pxVar, pxOut: - a = parseParam(p) - of pxParRi: - getTok(p) - break - else: - parMessage(p, errTokenExpected, ")") - break - skipCom(p, a) - if p.tok.xkind == pxSemicolon: - getTok(p) - skipCom(p, a) - addSon(result, a) - p.inParamList = false - if p.tok.xkind == pxColon: - getTok(p) - skipCom(p, result) - result.sons[0] = parseTypeDesc(p) - -proc parseCallingConvention(p: var TParser): PNode = - result = ast.emptyNode - if p.tok.xkind == pxSymbol: - case toLower(p.tok.ident.s) - of "stdcall", "cdecl", "safecall", "syscall", "inline", "fastcall": - result = newNodeP(nkPragma, p) - addSon(result, newIdentNodeP(p.tok.ident, p)) - getTok(p) - opt(p, pxSemicolon) - of "register": - result = newNodeP(nkPragma, p) - addSon(result, newIdentNodeP(getIdent("fastcall"), p)) - getTok(p) - opt(p, pxSemicolon) - else: - discard - -proc parseRoutineSpecifiers(p: var TParser, noBody: var bool): PNode = - var e: PNode - result = parseCallingConvention(p) - noBody = false - while p.tok.xkind == pxSymbol: - case toLower(p.tok.ident.s) - of "assembler", "overload", "far": - getTok(p) - opt(p, pxSemicolon) - of "forward": - noBody = true - getTok(p) - opt(p, pxSemicolon) - of "importc": - # This is a fake for platform module. There is no ``importc`` - # directive in Pascal. - if result.kind == nkEmpty: result = newNodeP(nkPragma, p) - addSon(result, newIdentNodeP(getIdent("importc"), p)) - noBody = true - getTok(p) - opt(p, pxSemicolon) - of "noconv": - # This is a fake for platform module. There is no ``noconv`` - # directive in Pascal. - if result.kind == nkEmpty: result = newNodeP(nkPragma, p) - addSon(result, newIdentNodeP(getIdent("noconv"), p)) - noBody = true - getTok(p) - opt(p, pxSemicolon) - of "procvar": - # This is a fake for the Nimrod compiler. There is no ``procvar`` - # directive in Pascal. - if result.kind == nkEmpty: result = newNodeP(nkPragma, p) - addSon(result, newIdentNodeP(getIdent("procvar"), p)) - getTok(p) - opt(p, pxSemicolon) - of "varargs": - if result.kind == nkEmpty: result = newNodeP(nkPragma, p) - addSon(result, newIdentNodeP(getIdent("varargs"), p)) - getTok(p) - opt(p, pxSemicolon) - of "external": - if result.kind == nkEmpty: result = newNodeP(nkPragma, p) - getTok(p) - noBody = true - e = newNodeP(nkExprColonExpr, p) - addSon(e, newIdentNodeP(getIdent("dynlib"), p)) - addSon(e, parseExpr(p)) - addSon(result, e) - opt(p, pxSemicolon) - if (p.tok.xkind == pxSymbol) and - (p.tok.ident.id == getIdent("name").id): - e = newNodeP(nkExprColonExpr, p) - getTok(p) - addSon(e, newIdentNodeP(getIdent("importc"), p)) - addSon(e, parseExpr(p)) - addSon(result, e) - else: - addSon(result, newIdentNodeP(getIdent("importc"), p)) - opt(p, pxSemicolon) - else: - e = parseCallingConvention(p) - if e.kind == nkEmpty: break - if result.kind == nkEmpty: result = newNodeP(nkPragma, p) - addSon(result, e.sons[0]) - -proc parseRoutineType(p: var TParser): PNode = - result = newNodeP(nkProcTy, p) - getTok(p) - skipCom(p, result) - addSon(result, parseParamList(p)) - opt(p, pxSemicolon) - addSon(result, parseCallingConvention(p)) - skipCom(p, result) - -proc parseEnum(p: var TParser): PNode = - var a: PNode - result = newNodeP(nkEnumTy, p) - getTok(p) - skipCom(p, result) - addSon(result, ast.emptyNode) # it does not inherit from any enumeration - while true: - case p.tok.xkind - of pxEof, pxParRi: break - of pxSymbol: a = newIdentNodeP(p.tok.ident, p) - else: - parMessage(p, errIdentifierExpected, $(p.tok)) - break - getTok(p) # skip identifier - skipCom(p, a) - if (p.tok.xkind == pxEquals) or (p.tok.xkind == pxAsgn): - getTok(p) - skipCom(p, a) - var b = a - a = newNodeP(nkEnumFieldDef, p) - addSon(a, b) - addSon(a, parseExpr(p)) - if p.tok.xkind == pxComma: - getTok(p) - skipCom(p, a) - addSon(result, a) - eat(p, pxParRi) - -proc identVis(p: var TParser): PNode = - # identifier with visability - var a = createIdentNodeP(p.tok.ident, p) - if p.section == seInterface: - result = newNodeP(nkPostfix, p) - addSon(result, newIdentNodeP(getIdent("*"), p)) - addSon(result, a) - else: - result = a - getTok(p) - -type - TSymbolParser = proc (p: var TParser): PNode {.nimcall.} - -proc rawIdent(p: var TParser): PNode = - result = createIdentNodeP(p.tok.ident, p) - getTok(p) - -proc parseIdentColonEquals(p: var TParser, - identParser: TSymbolParser): PNode = - var a: PNode - result = newNodeP(nkIdentDefs, p) - while true: - case p.tok.xkind - of pxSymbol: a = identParser(p) - of pxColon, pxEof, pxParRi, pxEquals: break - else: - parMessage(p, errIdentifierExpected, $(p.tok)) - return - skipCom(p, a) - if p.tok.xkind == pxComma: - getTok(p) - skipCom(p, a) - addSon(result, a) - if p.tok.xkind == pxColon: - getTok(p) - skipCom(p, result) - addSon(result, parseTypeDesc(p)) - else: - addSon(result, ast.emptyNode) - if p.tok.xkind != pxEquals: - parMessage(p, errColonOrEqualsExpected, $(p.tok)) - if p.tok.xkind == pxEquals: - getTok(p) - skipCom(p, result) - addSon(result, parseExpr(p)) - else: - addSon(result, ast.emptyNode) - if p.tok.xkind == pxSemicolon: - getTok(p) - skipCom(p, result) - -proc parseRecordCase(p: var TParser): PNode = - var b, c: PNode - result = newNodeP(nkRecCase, p) - getTok(p) - var a = newNodeP(nkIdentDefs, p) - addSon(a, rawIdent(p)) - eat(p, pxColon) - addSon(a, parseTypeDesc(p)) - addSon(a, ast.emptyNode) - addSon(result, a) - eat(p, pxOf) - skipCom(p, result) - while true: - case p.tok.xkind - of pxEof, pxEnd: - break - of pxElse: - b = newNodeP(nkElse, p) - getTok(p) - else: - b = newNodeP(nkOfBranch, p) - while (p.tok.xkind != pxEof) and (p.tok.xkind != pxColon): - addSon(b, rangeExpr(p)) - opt(p, pxComma) - skipCom(p, b) - eat(p, pxColon) - skipCom(p, b) - c = newNodeP(nkRecList, p) - eat(p, pxParLe) - while (p.tok.xkind != pxParRi) and (p.tok.xkind != pxEof): - addSon(c, parseIdentColonEquals(p, rawIdent)) - opt(p, pxSemicolon) - skipCom(p, lastSon(c)) - eat(p, pxParRi) - opt(p, pxSemicolon) - if sonsLen(c) > 0: skipCom(p, lastSon(c)) - else: addSon(c, newNodeP(nkNilLit, p)) - addSon(b, c) - addSon(result, b) - if b.kind == nkElse: break - -proc parseRecordPart(p: var TParser): PNode = - result = ast.emptyNode - while (p.tok.xkind != pxEof) and (p.tok.xkind != pxEnd): - if result.kind == nkEmpty: result = newNodeP(nkRecList, p) - case p.tok.xkind - of pxSymbol: - addSon(result, parseIdentColonEquals(p, rawIdent)) - opt(p, pxSemicolon) - skipCom(p, lastSon(result)) - of pxCase: - addSon(result, parseRecordCase(p)) - of pxComment: - skipCom(p, lastSon(result)) - else: - parMessage(p, errIdentifierExpected, $p.tok) - break - -proc exSymbol(n: var PNode) = - case n.kind - of nkPostfix: - discard - of nkPragmaExpr: - exSymbol(n.sons[0]) - of nkIdent, nkAccQuoted: - var a = newNodeI(nkPostFix, n.info) - addSon(a, newIdentNode(getIdent("*"), n.info)) - addSon(a, n) - n = a - else: internalError(n.info, "exSymbol(): " & $n.kind) - -proc fixRecordDef(n: var PNode) = - case n.kind - of nkRecCase: - fixRecordDef(n.sons[0]) - for i in countup(1, sonsLen(n) - 1): - var length = sonsLen(n.sons[i]) - fixRecordDef(n.sons[i].sons[length - 1]) - of nkRecList, nkRecWhen, nkElse, nkOfBranch, nkElifBranch, nkObjectTy: - for i in countup(0, sonsLen(n) - 1): fixRecordDef(n.sons[i]) - of nkIdentDefs: - for i in countup(0, sonsLen(n) - 3): exSymbol(n.sons[i]) - of nkNilLit, nkEmpty: discard - else: internalError(n.info, "fixRecordDef(): " & $n.kind) - -proc addPragmaToIdent(ident: var PNode, pragma: PNode) = - var pragmasNode: PNode - if ident.kind != nkPragmaExpr: - pragmasNode = newNodeI(nkPragma, ident.info) - var e = newNodeI(nkPragmaExpr, ident.info) - addSon(e, ident) - addSon(e, pragmasNode) - ident = e - else: - pragmasNode = ident.sons[1] - if pragmasNode.kind != nkPragma: - internalError(ident.info, "addPragmaToIdent") - addSon(pragmasNode, pragma) - -proc parseRecordBody(p: var TParser, result, definition: PNode) = - skipCom(p, result) - var a = parseRecordPart(p) - if result.kind != nkTupleTy: fixRecordDef(a) - addSon(result, a) - eat(p, pxEnd) - case p.tok.xkind - of pxSymbol: - if p.tok.ident.id == getIdent("acyclic").id: - if definition != nil: - addPragmaToIdent(definition.sons[0], newIdentNodeP(p.tok.ident, p)) - else: - internalError(result.info, "anonymous record is not supported") - getTok(p) - else: - internalError(result.info, "parseRecordBody") - of pxCommand: - if definition != nil: addPragmaToIdent(definition.sons[0], parseCommand(p)) - else: internalError(result.info, "anonymous record is not supported") - else: - discard - opt(p, pxSemicolon) - skipCom(p, result) - -proc parseRecordOrObject(p: var TParser, kind: TNodeKind, - definition: PNode): PNode = - result = newNodeP(kind, p) - getTok(p) - addSon(result, ast.emptyNode) - if p.tok.xkind == pxParLe: - var a = newNodeP(nkOfInherit, p) - getTok(p) - addSon(a, parseTypeDesc(p)) - addSon(result, a) - eat(p, pxParRi) - else: - addSon(result, ast.emptyNode) - parseRecordBody(p, result, definition) - -proc parseTypeDesc(p: var TParser, definition: PNode = nil): PNode = - var oldcontext = p.context - p.context = conTypeDesc - if p.tok.xkind == pxPacked: getTok(p) - case p.tok.xkind - of pxCommand: - result = parseCommand(p, definition) - of pxProcedure, pxFunction: - result = parseRoutineType(p) - of pxRecord: - getTok(p) - if p.tok.xkind == pxCommand: - result = parseCommand(p) - if result.kind != nkTupleTy: internalError(result.info, "parseTypeDesc") - parseRecordBody(p, result, definition) - var a = lastSon(result) # embed nkRecList directly into nkTupleTy - for i in countup(0, sonsLen(a) - 1): - if i == 0: result.sons[sonsLen(result) - 1] = a.sons[0] - else: addSon(result, a.sons[i]) - else: - result = newNodeP(nkObjectTy, p) - addSon(result, ast.emptyNode) - addSon(result, ast.emptyNode) - parseRecordBody(p, result, definition) - if definition != nil: - addPragmaToIdent(definition.sons[0], newIdentNodeP(getIdent("final"), p)) - else: - internalError(result.info, "anonymous record is not supported") - of pxObject: result = parseRecordOrObject(p, nkObjectTy, definition) - of pxParLe: result = parseEnum(p) - of pxArray: - result = newNodeP(nkBracketExpr, p) - getTok(p) - if p.tok.xkind == pxBracketLe: - addSon(result, newIdentNodeP(getIdent("array"), p)) - getTok(p) - addSon(result, rangeExpr(p)) - eat(p, pxBracketRi) - else: - if p.inParamList: addSon(result, newIdentNodeP(getIdent("openarray"), p)) - else: addSon(result, newIdentNodeP(getIdent("seq"), p)) - eat(p, pxOf) - addSon(result, parseTypeDesc(p)) - of pxSet: - result = newNodeP(nkBracketExpr, p) - getTok(p) - eat(p, pxOf) - addSon(result, newIdentNodeP(getIdent("set"), p)) - addSon(result, parseTypeDesc(p)) - of pxHat: - getTok(p) - if p.tok.xkind == pxCommand: result = parseCommand(p) - elif pfRefs in p.flags: result = newNodeP(nkRefTy, p) - else: result = newNodeP(nkPtrTy, p) - addSon(result, parseTypeDesc(p)) - of pxType: - getTok(p) - result = parseTypeDesc(p) - else: - var a = primary(p) - if p.tok.xkind == pxDotDot: - result = newNodeP(nkBracketExpr, p) - var r = newNodeP(nkRange, p) - addSon(result, newIdentNodeP(getIdent("range"), p)) - getTok(p) - addSon(r, a) - addSon(r, parseExpr(p)) - addSon(result, r) - else: - result = a - p.context = oldcontext - -proc parseTypeDef(p: var TParser): PNode = - result = newNodeP(nkTypeDef, p) - addSon(result, identVis(p)) - addSon(result, ast.emptyNode) # generic params - if p.tok.xkind == pxEquals: - getTok(p) - skipCom(p, result) - addSon(result, parseTypeDesc(p, result)) - else: - addSon(result, ast.emptyNode) - if p.tok.xkind == pxSemicolon: - getTok(p) - skipCom(p, result) - -proc parseTypeSection(p: var TParser): PNode = - result = newNodeP(nkTypeSection, p) - getTok(p) - skipCom(p, result) - while p.tok.xkind == pxSymbol: - addSon(result, parseTypeDef(p)) - -proc parseConstant(p: var TParser): PNode = - result = newNodeP(nkConstDef, p) - addSon(result, identVis(p)) - if p.tok.xkind == pxColon: - getTok(p) - skipCom(p, result) - addSon(result, parseTypeDesc(p)) - else: - addSon(result, ast.emptyNode) - if p.tok.xkind != pxEquals: - parMessage(p, errColonOrEqualsExpected, $(p.tok)) - if p.tok.xkind == pxEquals: - getTok(p) - skipCom(p, result) - addSon(result, parseExpr(p)) - else: - addSon(result, ast.emptyNode) - if p.tok.xkind == pxSemicolon: - getTok(p) - skipCom(p, result) - -proc parseConstSection(p: var TParser): PNode = - result = newNodeP(nkConstSection, p) - getTok(p) - skipCom(p, result) - while p.tok.xkind == pxSymbol: - addSon(result, parseConstant(p)) - -proc parseVar(p: var TParser): PNode = - result = newNodeP(nkVarSection, p) - getTok(p) - skipCom(p, result) - while p.tok.xkind == pxSymbol: - addSon(result, parseIdentColonEquals(p, identVis)) - p.lastVarSection = result - -proc parseRoutine(p: var TParser): PNode = - var noBody: bool - result = newNodeP(nkProcDef, p) - getTok(p) - skipCom(p, result) - expectIdent(p) - addSon(result, identVis(p)) - # patterns, generic parameters: - addSon(result, ast.emptyNode) - addSon(result, ast.emptyNode) - addSon(result, parseParamList(p)) - opt(p, pxSemicolon) - addSon(result, parseRoutineSpecifiers(p, noBody)) - addSon(result, ast.emptyNode) - if (p.section == seInterface) or noBody: - addSon(result, ast.emptyNode) - else: - var stmts = newNodeP(nkStmtList, p) - while true: - case p.tok.xkind - of pxVar: addSon(stmts, parseVar(p)) - of pxConst: addSon(stmts, parseConstSection(p)) - of pxType: addSon(stmts, parseTypeSection(p)) - of pxComment: skipCom(p, result) - of pxBegin: break - else: - parMessage(p, errTokenExpected, "begin") - break - var a = parseStmt(p) - for i in countup(0, sonsLen(a) - 1): addSon(stmts, a.sons[i]) - addSon(result, stmts) - -proc fixExit(p: var TParser, n: PNode): bool = - if (p.tok.ident.id == getIdent("exit").id): - var length = sonsLen(n) - if (length <= 0): return - var a = n.sons[length-1] - if (a.kind == nkAsgn) and (a.sons[0].kind == nkIdent) and - (a.sons[0].ident.id == getIdent("result").id): - delSon(a, 0) - a.kind = nkReturnStmt - result = true - getTok(p) - opt(p, pxSemicolon) - skipCom(p, a) - -proc fixVarSection(p: var TParser, counter: PNode) = - if p.lastVarSection == nil: return - assert(counter.kind == nkIdent) - for i in countup(0, sonsLen(p.lastVarSection) - 1): - var v = p.lastVarSection.sons[i] - for j in countup(0, sonsLen(v) - 3): - if v.sons[j].ident.id == counter.ident.id: - delSon(v, j) - if sonsLen(v) <= 2: - delSon(p.lastVarSection, i) - return - -proc exSymbols(n: PNode) = - case n.kind - of nkEmpty..nkNilLit: discard - of nkProcDef..nkIteratorDef: exSymbol(n.sons[namePos]) - of nkWhenStmt, nkStmtList: - for i in countup(0, sonsLen(n) - 1): exSymbols(n.sons[i]) - of nkVarSection, nkConstSection: - for i in countup(0, sonsLen(n) - 1): exSymbol(n.sons[i].sons[0]) - of nkTypeSection: - for i in countup(0, sonsLen(n) - 1): - exSymbol(n.sons[i].sons[0]) - if n.sons[i].sons[2].kind == nkObjectTy: - fixRecordDef(n.sons[i].sons[2]) - else: discard - -proc parseBegin(p: var TParser, result: PNode) = - getTok(p) - while true: - case p.tok.xkind - of pxComment: addSon(result, parseStmt(p)) - of pxSymbol: - if not fixExit(p, result): addSon(result, parseStmt(p)) - of pxEnd: - getTok(p) - break - of pxSemicolon: getTok(p) - of pxEof: parMessage(p, errExprExpected) - else: - var a = parseStmt(p) - if a.kind != nkEmpty: addSon(result, a) - if sonsLen(result) == 0: addSon(result, newNodeP(nkNilLit, p)) - -proc parseStmt(p: var TParser): PNode = - var oldcontext = p.context - p.context = conStmt - result = ast.emptyNode - case p.tok.xkind - of pxBegin: - result = newNodeP(nkStmtList, p) - parseBegin(p, result) - of pxCommand: result = parseCommand(p) - of pxCurlyDirLe, pxStarDirLe: - if isHandledDirective(p): result = parseDirective(p) - of pxIf: result = parseIf(p) - of pxWhile: result = parseWhile(p) - of pxRepeat: result = parseRepeat(p) - of pxCase: result = parseCase(p) - of pxTry: result = parseTry(p) - of pxProcedure, pxFunction: result = parseRoutine(p) - of pxType: result = parseTypeSection(p) - of pxConst: result = parseConstSection(p) - of pxVar: result = parseVar(p) - of pxFor: - result = parseFor(p) - fixVarSection(p, result.sons[0]) - of pxRaise: result = parseRaise(p) - of pxUses: result = parseUsesStmt(p) - of pxProgram, pxUnit, pxLibrary: - # skip the pointless header - while not (p.tok.xkind in {pxSemicolon, pxEof}): getTok(p) - getTok(p) - of pxInitialization: getTok(p) # just skip the token - of pxImplementation: - p.section = seImplementation - result = newNodeP(nkCommentStmt, p) - result.comment = "# implementation" - getTok(p) - of pxInterface: - p.section = seInterface - getTok(p) - of pxComment: - result = newNodeP(nkCommentStmt, p) - skipCom(p, result) - of pxSemicolon: getTok(p) - of pxSymbol: - if p.tok.ident.id == getIdent("break").id: - result = newNodeP(nkBreakStmt, p) - getTok(p) - skipCom(p, result) - addSon(result, ast.emptyNode) - elif p.tok.ident.id == getIdent("continue").id: - result = newNodeP(nkContinueStmt, p) - getTok(p) - skipCom(p, result) - addSon(result, ast.emptyNode) - elif p.tok.ident.id == getIdent("exit").id: - result = newNodeP(nkReturnStmt, p) - getTok(p) - skipCom(p, result) - addSon(result, ast.emptyNode) - else: - result = parseExprStmt(p) - of pxDot: getTok(p) # BUGFIX for ``end.`` in main program - else: result = parseExprStmt(p) - opt(p, pxSemicolon) - if result.kind != nkEmpty: skipCom(p, result) - p.context = oldcontext - -proc parseUnit(p: var TParser): PNode = - result = newNodeP(nkStmtList, p) - getTok(p) # read first token - while true: - case p.tok.xkind - of pxEof, pxEnd: break - of pxBegin: parseBegin(p, result) - of pxCurlyDirLe, pxStarDirLe: - if isHandledDirective(p): addSon(result, parseDirective(p)) - else: parMessage(p, errXNotAllowedHere, p.tok.ident.s) - else: addSon(result, parseStmt(p)) - opt(p, pxEnd) - opt(p, pxDot) - if p.tok.xkind != pxEof: - addSon(result, parseStmt(p)) # comments after final 'end.' - |