├── .gitignore ├── LICENSE.txt ├── README.md ├── nim.cfg ├── pas2nim.babel ├── pas2nim.nim ├── paslex.nim └── pasparse.nim /.gitignore: -------------------------------------------------------------------------------- 1 | nimcache 2 | -------------------------------------------------------------------------------- /LICENSE.txt: -------------------------------------------------------------------------------- 1 | The MIT License (MIT) 2 | 3 | Copyright (c) 2015 Andreas Rumpf 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | pas2nim 2 | ======= 3 | 4 | pas2nim is a tool to translate Delphi/Pascal wrappers to Nim code. 5 | 6 | It was used to translate the original Nim compiler that was written in Pascal. 7 | Only what maps easily to Nim is supported. Delphi's ``class`` or other "fancy" 8 | features are not supported. 9 | 10 | **Note**: This project is essentially not maintained anymore! Help if you are 11 | interested in keeping it alive! 12 | -------------------------------------------------------------------------------- /nim.cfg: -------------------------------------------------------------------------------- 1 | # Use the modules of the compiler 2 | 3 | path: "$nim/compiler" 4 | 5 | -------------------------------------------------------------------------------- /pas2nim.babel: -------------------------------------------------------------------------------- 1 | [Package] 2 | name = "pas2nim" 3 | version = "0.9.6" 4 | author = "Andreas Rumpf" 5 | description = "pas2nim is a tool to translate Pascal code to Nim." 6 | license = "MIT" 7 | 8 | bin = "pas2nim" 9 | 10 | [Deps] 11 | Requires: "nim >= 0.9.4" 12 | 13 | -------------------------------------------------------------------------------- /pas2nim.nim: -------------------------------------------------------------------------------- 1 | # 2 | # 3 | # Pas2nim - Pascal to Nim source converter 4 | # (c) Copyright 2012 Andreas Rumpf 5 | # 6 | # See the file "copying.txt", included in this 7 | # distribution, for details about the copyright. 8 | # 9 | 10 | import 11 | strutils, os, parseopt, llstream, ast, renderer, options, msgs, 12 | paslex, pasparse 13 | 14 | const 15 | Version = "0.8" 16 | Usage = """ 17 | pas2nim - Pascal to Nim source converter 18 | (c) 2012 Andreas Rumpf 19 | Usage: pas2nim [options] inputfile [options] 20 | Options: 21 | -o, --out:FILE set output filename 22 | --ref convert ^typ to ref typ (default: ptr typ) 23 | --boot use special translation rules for the Nim compiler 24 | -v, --version write pas2nim's version 25 | -h, --help show this help 26 | """ 27 | 28 | proc main(infile, outfile: string, flags: set[TParserFlag]) = 29 | var stream = llStreamOpen(infile, fmRead) 30 | if stream == nil: rawMessage(errCannotOpenFile, infile) 31 | var p: TParser 32 | openParser(p, infile, stream, flags) 33 | var module = parseUnit(p) 34 | closeParser(p) 35 | renderModule(module, outfile) 36 | 37 | var 38 | infile = "" 39 | outfile = "" 40 | flags: set[TParserFlag] = {} 41 | for kind, key, val in getopt(): 42 | case kind 43 | of cmdArgument: infile = key 44 | of cmdLongOption, cmdShortOption: 45 | case key 46 | of "help", "h": 47 | stdout.write(Usage) 48 | quit(0) 49 | of "version", "v": 50 | stdout.write(Version & "\n") 51 | quit(0) 52 | of "o", "out": outfile = val 53 | of "ref": incl(flags, pfRefs) 54 | of "boot": flags = flags + {pfRefs, pfMoreReplacements, pfImportBlackList} 55 | else: stdout.writeln("[Error] unknown option: " & key) 56 | of cmdEnd: assert(false) 57 | if infile.len == 0: 58 | # no filename has been given, so we show the help: 59 | stdout.write(Usage) 60 | else: 61 | if outfile.len == 0: 62 | outfile = changeFileExt(infile, "nim") 63 | infile = addFileExt(infile, "pas") 64 | main(infile, outfile, flags) 65 | -------------------------------------------------------------------------------- /paslex.nim: -------------------------------------------------------------------------------- 1 | # 2 | # 3 | # Pas2nim - Pascal to Nim source converter 4 | # (c) Copyright 2012 Andreas Rumpf 5 | # 6 | # See the file "copying.txt", included in this 7 | # distribution, for details about the copyright. 8 | # 9 | 10 | # This module implements a FreePascal scanner. This is an adaption from 11 | # the scanner module. 12 | 13 | import 14 | hashes, options, msgs, strutils, platform, idents, nimlexbase, llstream 15 | 16 | const 17 | MaxLineLength* = 80 # lines longer than this lead to a warning 18 | numChars*: set[char] = {'0'..'9', 'a'..'z', 'A'..'Z'} 19 | SymChars*: set[char] = {'a'..'z', 'A'..'Z', '0'..'9', '\x80'..'\xFF'} 20 | SymStartChars*: set[char] = {'a'..'z', 'A'..'Z', '\x80'..'\xFF'} 21 | OpChars*: set[char] = {'+', '-', '*', '/', '<', '>', '!', '?', '^', '.', '|', 22 | '=', ':', '%', '&', '$', '@', '~', '\x80'..'\xFF'} 23 | 24 | # keywords are sorted! 25 | 26 | type 27 | TTokKind* = enum 28 | pxInvalid, pxEof, 29 | pxAnd, pxArray, pxAs, pxAsm, pxBegin, pxCase, pxClass, pxConst, 30 | pxConstructor, pxDestructor, pxDiv, pxDo, pxDownto, pxElse, pxEnd, pxExcept, 31 | pxExports, pxFinalization, pxFinally, pxFor, pxFunction, pxGoto, pxIf, 32 | pxImplementation, pxIn, pxInherited, pxInitialization, pxInline, 33 | pxInterface, pxIs, pxLabel, pxLibrary, pxMod, pxNil, pxNot, pxObject, pxOf, 34 | pxOr, pxOut, pxPacked, pxProcedure, pxProgram, pxProperty, pxRaise, 35 | pxRecord, pxRepeat, pxResourcestring, pxSet, pxShl, pxShr, pxThen, 36 | pxThreadvar, pxTo, pxTry, pxType, pxUnit, pxUntil, pxUses, pxVar, pxWhile, 37 | pxWith, pxXor, 38 | pxComment, # ordinary comment 39 | pxCommand, # {@} 40 | pxAmp, # {&} 41 | pxPer, # {%} 42 | pxStrLit, pxSymbol, # a symbol 43 | pxIntLit, pxInt64Lit, # long constant like 0x70fffffff or out of int range 44 | pxFloatLit, pxParLe, pxParRi, pxBracketLe, pxBracketRi, pxComma, 45 | pxSemiColon, pxColon, # operators 46 | pxAsgn, pxEquals, pxDot, pxDotDot, pxHat, pxPlus, pxMinus, pxStar, pxSlash, 47 | pxLe, pxLt, pxGe, pxGt, pxNeq, pxAt, pxStarDirLe, pxStarDirRi, pxCurlyDirLe, 48 | pxCurlyDirRi 49 | TTokKinds* = set[TTokKind] 50 | 51 | const 52 | Keywords = ["and", "array", "as", "asm", "begin", "case", "class", "const", 53 | "constructor", "destructor", "div", "do", "downto", "else", "end", "except", 54 | "exports", "finalization", "finally", "for", "function", "goto", "if", 55 | "implementation", "in", "inherited", "initialization", "inline", 56 | "interface", "is", "label", "library", "mod", "nil", "not", "object", "of", 57 | "or", "out", "packed", "procedure", "program", "property", "raise", 58 | "record", "repeat", "resourcestring", "set", "shl", "shr", "then", 59 | "threadvar", "to", "try", "type", "unit", "until", "uses", "var", "while", 60 | "with", "xor"] 61 | 62 | firstKeyword = pxAnd 63 | lastKeyword = pxXor 64 | 65 | type 66 | TNumericalBase* = enum base10, base2, base8, base16 67 | TToken* = object 68 | xkind*: TTokKind # the type of the token 69 | ident*: PIdent # the parsed identifier 70 | iNumber*: BiggestInt # the parsed integer literal 71 | fNumber*: BiggestFloat # the parsed floating point literal 72 | base*: TNumericalBase # the numerical base; only valid for int 73 | # or float literals 74 | literal*: string # the parsed (string) literal 75 | 76 | TLexer* = object of TBaseLexer 77 | filename*: string 78 | 79 | 80 | proc getTok*(L: var TLexer, tok: var TToken) 81 | proc printTok*(tok: TToken) 82 | proc `$`*(tok: TToken): string 83 | # implementation 84 | 85 | var 86 | dummyIdent: PIdent 87 | gLinesCompiled: int 88 | 89 | proc fillToken(L: var TToken) = 90 | L.xkind = pxInvalid 91 | L.iNumber = 0 92 | L.literal = "" 93 | L.fNumber = 0.0 94 | L.base = base10 95 | L.ident = dummyIdent # this prevents many bugs! 96 | 97 | proc openLexer*(lex: var TLexer, filename: string, inputstream: PLLStream) = 98 | openBaseLexer(lex, inputstream) 99 | lex.filename = filename 100 | 101 | proc closeLexer*(lex: var TLexer) = 102 | inc(gLinesCompiled, lex.lineNumber) 103 | closeBaseLexer(lex) 104 | 105 | proc getColumn(L: TLexer): int = 106 | result = getColNumber(L, L.bufPos) 107 | 108 | proc getLineInfo*(L: TLexer): TLineInfo = 109 | result = newLineInfo(L.filename, L.lineNumber, getColNumber(L, L.bufpos)) 110 | 111 | proc lexMessage*(L: TLexer, msg: TMsgKind, arg = "") = 112 | msgs.globalError(getLineInfo(L), msg, arg) 113 | 114 | proc lexMessagePos(L: var TLexer, msg: TMsgKind, pos: int, arg = "") = 115 | var info = newLineInfo(L.filename, L.lineNumber, pos - L.lineStart) 116 | msgs.globalError(info, msg, arg) 117 | 118 | proc tokKindToStr*(k: TTokKind): string = 119 | case k 120 | of pxEof: result = "[EOF]" 121 | of firstKeyword..lastKeyword: 122 | result = Keywords[ord(k)-ord(firstKeyword)] 123 | of pxInvalid, pxComment, pxStrLit: result = "string literal" 124 | of pxCommand: result = "{@" 125 | of pxAmp: result = "{&" 126 | of pxPer: result = "{%" 127 | of pxSymbol: result = "identifier" 128 | of pxIntLit, pxInt64Lit: result = "integer literal" 129 | of pxFloatLit: result = "floating point literal" 130 | of pxParLe: result = "(" 131 | of pxParRi: result = ")" 132 | of pxBracketLe: result = "[" 133 | of pxBracketRi: result = "]" 134 | of pxComma: result = "," 135 | of pxSemiColon: result = ";" 136 | of pxColon: result = ":" 137 | of pxAsgn: result = ":=" 138 | of pxEquals: result = "=" 139 | of pxDot: result = "." 140 | of pxDotDot: result = ".." 141 | of pxHat: result = "^" 142 | of pxPlus: result = "+" 143 | of pxMinus: result = "-" 144 | of pxStar: result = "*" 145 | of pxSlash: result = "/" 146 | of pxLe: result = "<=" 147 | of pxLt: result = "<" 148 | of pxGe: result = ">=" 149 | of pxGt: result = ">" 150 | of pxNeq: result = "<>" 151 | of pxAt: result = "@" 152 | of pxStarDirLe: result = "(*$" 153 | of pxStarDirRi: result = "*)" 154 | of pxCurlyDirLe: result = "{$" 155 | of pxCurlyDirRi: result = "}" 156 | 157 | proc `$`(tok: TToken): string = 158 | case tok.xkind 159 | of pxInvalid, pxComment, pxStrLit: result = tok.literal 160 | of pxSymbol: result = tok.ident.s 161 | of pxIntLit, pxInt64Lit: result = $tok.iNumber 162 | of pxFloatLit: result = $tok.fNumber 163 | else: result = tokKindToStr(tok.xkind) 164 | 165 | proc printTok(tok: TToken) = 166 | writeln(stdout, $tok) 167 | 168 | proc setKeyword(L: var TLexer, tok: var TToken) = 169 | var x = binaryStrSearch(Keywords, toLower(tok.ident.s)) 170 | if x < 0: tok.xkind = pxSymbol 171 | else: tok.xKind = TTokKind(x + ord(firstKeyword)) 172 | 173 | proc matchUnderscoreChars(L: var TLexer, tok: var TToken, chars: set[char]) = 174 | # matches ([chars]_)* 175 | var pos = L.bufpos # use registers for pos, buf 176 | var buf = L.buf 177 | while true: 178 | if buf[pos] in chars: 179 | add(tok.literal, buf[pos]) 180 | inc(pos) 181 | else: 182 | break 183 | if buf[pos] == '_': 184 | add(tok.literal, '_') 185 | inc(pos) 186 | L.bufPos = pos 187 | 188 | proc isFloatLiteral(s: string): bool = 189 | for i in countup(0, len(s)-1): 190 | if s[i] in {'.', 'e', 'E'}: 191 | return true 192 | 193 | proc getNumber2(L: var TLexer, tok: var TToken) = 194 | var pos = L.bufpos + 1 # skip % 195 | if not (L.buf[pos] in {'0'..'1'}): 196 | # BUGFIX for %date% 197 | tok.xkind = pxInvalid 198 | add(tok.literal, '%') 199 | inc(L.bufpos) 200 | return 201 | tok.base = base2 202 | var xi: BiggestInt = 0 203 | var bits = 0 204 | while true: 205 | case L.buf[pos] 206 | of 'A'..'Z', 'a'..'z', '2'..'9', '.': 207 | lexMessage(L, errInvalidNumber) 208 | inc(pos) 209 | of '_': 210 | inc(pos) 211 | of '0', '1': 212 | xi = `shl`(xi, 1) or (ord(L.buf[pos]) - ord('0')) 213 | inc(pos) 214 | inc(bits) 215 | else: break 216 | tok.iNumber = xi 217 | if (bits > 32): tok.xkind = pxInt64Lit 218 | else: tok.xkind = pxIntLit 219 | L.bufpos = pos 220 | 221 | proc getNumber16(L: var TLexer, tok: var TToken) = 222 | var pos = L.bufpos + 1 # skip $ 223 | tok.base = base16 224 | var xi: BiggestInt = 0 225 | var bits = 0 226 | while true: 227 | case L.buf[pos] 228 | of 'G'..'Z', 'g'..'z', '.': 229 | lexMessage(L, errInvalidNumber) 230 | inc(pos) 231 | of '_': inc(pos) 232 | of '0'..'9': 233 | xi = `shl`(xi, 4) or (ord(L.buf[pos]) - ord('0')) 234 | inc(pos) 235 | inc(bits, 4) 236 | of 'a'..'f': 237 | xi = `shl`(xi, 4) or (ord(L.buf[pos]) - ord('a') + 10) 238 | inc(pos) 239 | inc(bits, 4) 240 | of 'A'..'F': 241 | xi = `shl`(xi, 4) or (ord(L.buf[pos]) - ord('A') + 10) 242 | inc(pos) 243 | inc(bits, 4) 244 | else: break 245 | tok.iNumber = xi 246 | if (bits > 32): 247 | tok.xkind = pxInt64Lit 248 | else: 249 | tok.xkind = pxIntLit 250 | L.bufpos = pos 251 | 252 | proc getNumber10(L: var TLexer, tok: var TToken) = 253 | tok.base = base10 254 | matchUnderscoreChars(L, tok, {'0'..'9'}) 255 | if (L.buf[L.bufpos] == '.') and (L.buf[L.bufpos + 1] in {'0'..'9'}): 256 | add(tok.literal, '.') 257 | inc(L.bufpos) 258 | matchUnderscoreChars(L, tok, {'e', 'E', '+', '-', '0'..'9'}) 259 | try: 260 | if isFloatLiteral(tok.literal): 261 | tok.fnumber = parseFloat(tok.literal) 262 | tok.xkind = pxFloatLit 263 | else: 264 | tok.iNumber = parseInt(tok.literal) 265 | if (tok.iNumber < low(int32)) or (tok.iNumber > high(int32)): 266 | tok.xkind = pxInt64Lit 267 | else: 268 | tok.xkind = pxIntLit 269 | except ValueError: 270 | lexMessage(L, errInvalidNumber, tok.literal) 271 | except OverflowError: 272 | lexMessage(L, errNumberOutOfRange, tok.literal) 273 | 274 | proc handleCRLF(L: var TLexer, pos: int): int = 275 | case L.buf[pos] 276 | of CR: result = nimlexbase.handleCR(L, pos) 277 | of LF: result = nimlexbase.handleLF(L, pos) 278 | else: result = pos 279 | 280 | proc getString(L: var TLexer, tok: var TToken) = 281 | var xi: int 282 | var pos = L.bufPos 283 | var buf = L.buf 284 | while true: 285 | if buf[pos] == '\'': 286 | inc(pos) 287 | while true: 288 | case buf[pos] 289 | of CR, LF, nimlexbase.EndOfFile: 290 | lexMessage(L, errClosingQuoteExpected) 291 | break 292 | of '\'': 293 | inc(pos) 294 | if buf[pos] == '\'': 295 | inc(pos) 296 | add(tok.literal, '\'') 297 | else: 298 | break 299 | else: 300 | add(tok.literal, buf[pos]) 301 | inc(pos) 302 | elif buf[pos] == '#': 303 | inc(pos) 304 | xi = 0 305 | case buf[pos] 306 | of '$': 307 | inc(pos) 308 | xi = 0 309 | while true: 310 | case buf[pos] 311 | of '0'..'9': xi = (xi shl 4) or (ord(buf[pos]) - ord('0')) 312 | of 'a'..'f': xi = (xi shl 4) or (ord(buf[pos]) - ord('a') + 10) 313 | of 'A'..'F': xi = (xi shl 4) or (ord(buf[pos]) - ord('A') + 10) 314 | else: break 315 | inc(pos) 316 | of '0'..'9': 317 | xi = 0 318 | while buf[pos] in {'0'..'9'}: 319 | xi = (xi * 10) + (ord(buf[pos]) - ord('0')) 320 | inc(pos) 321 | else: lexMessage(L, errInvalidCharacterConstant) 322 | if (xi <= 255): add(tok.literal, chr(xi)) 323 | else: lexMessage(L, errInvalidCharacterConstant) 324 | else: 325 | break 326 | tok.xkind = pxStrLit 327 | L.bufpos = pos 328 | 329 | proc getSymbol(L: var TLexer, tok: var TToken) = 330 | var h: THash = 0 331 | var pos = L.bufpos 332 | var buf = L.buf 333 | while true: 334 | var c = buf[pos] 335 | case c 336 | of 'a'..'z', '0'..'9', '\x80'..'\xFF': 337 | h = h +% ord(c) 338 | h = h +% h shl 10 339 | h = h xor (h shr 6) 340 | of 'A'..'Z': 341 | c = chr(ord(c) + (ord('a') - ord('A'))) # toLower() 342 | h = h +% ord(c) 343 | h = h +% h shl 10 344 | h = h xor (h shr 6) 345 | of '_': discard 346 | else: break 347 | inc(pos) 348 | h = h +% h shl 3 349 | h = h xor (h shr 11) 350 | h = h +% h shl 15 351 | tok.ident = getIdent(addr(L.buf[L.bufpos]), pos - L.bufpos, h) 352 | L.bufpos = pos 353 | setKeyword(L, tok) 354 | 355 | proc scanLineComment(L: var TLexer, tok: var TToken) = 356 | var pos = L.bufpos 357 | var buf = L.buf 358 | # a comment ends if the next line does not start with the // on the same 359 | # column after only whitespace 360 | tok.xkind = pxComment 361 | var col = getColNumber(L, pos) 362 | while true: 363 | inc(pos, 2) # skip // 364 | add(tok.literal, '#') 365 | while not (buf[pos] in {CR, LF, nimlexbase.EndOfFile}): 366 | add(tok.literal, buf[pos]) 367 | inc(pos) 368 | pos = handleCRLF(L, pos) 369 | buf = L.buf 370 | var indent = 0 371 | while buf[pos] == ' ': 372 | inc(pos) 373 | inc(indent) 374 | if (col == indent) and (buf[pos] == '/') and (buf[pos + 1] == '/'): 375 | tok.literal = tok.literal & "\n" 376 | else: 377 | break 378 | L.bufpos = pos 379 | 380 | proc scanCurlyComment(L: var TLexer, tok: var TToken) = 381 | var pos = L.bufpos 382 | var buf = L.buf 383 | tok.literal = "#" 384 | tok.xkind = pxComment 385 | while true: 386 | case buf[pos] 387 | of CR, LF: 388 | pos = handleCRLF(L, pos) 389 | buf = L.buf 390 | add(tok.literal, "\n#") 391 | of '}': 392 | inc(pos) 393 | break 394 | of nimlexbase.EndOfFile: lexMessage(L, errTokenExpected, "}") 395 | else: 396 | add(tok.literal, buf[pos]) 397 | inc(pos) 398 | L.bufpos = pos 399 | 400 | proc scanStarComment(L: var TLexer, tok: var TToken) = 401 | var pos = L.bufpos 402 | var buf = L.buf 403 | tok.literal = "#" 404 | tok.xkind = pxComment 405 | while true: 406 | case buf[pos] 407 | of CR, LF: 408 | pos = handleCRLF(L, pos) 409 | buf = L.buf 410 | add(tok.literal, "\n#") 411 | of '*': 412 | inc(pos) 413 | if buf[pos] == ')': 414 | inc(pos) 415 | break 416 | else: 417 | add(tok.literal, '*') 418 | of nimlexbase.EndOfFile: 419 | lexMessage(L, errTokenExpected, "*)") 420 | else: 421 | add(tok.literal, buf[pos]) 422 | inc(pos) 423 | L.bufpos = pos 424 | 425 | proc skip(L: var TLexer, tok: var TToken) = 426 | var pos = L.bufpos 427 | var buf = L.buf 428 | while true: 429 | case buf[pos] 430 | of ' ', Tabulator: 431 | inc(pos) # newline is special: 432 | of CR, LF: 433 | pos = handleCRLF(L, pos) 434 | buf = L.buf 435 | else: 436 | break # EndOfFile also leaves the loop 437 | L.bufpos = pos 438 | 439 | proc getTok(L: var TLexer, tok: var TToken) = 440 | tok.xkind = pxInvalid 441 | fillToken(tok) 442 | skip(L, tok) 443 | var c = L.buf[L.bufpos] 444 | if c in SymStartChars: 445 | getSymbol(L, tok) 446 | elif c in {'0'..'9'}: 447 | getNumber10(L, tok) 448 | else: 449 | case c 450 | of ';': 451 | tok.xkind = pxSemicolon 452 | inc(L.bufpos) 453 | of '/': 454 | if L.buf[L.bufpos + 1] == '/': 455 | scanLineComment(L, tok) 456 | else: 457 | tok.xkind = pxSlash 458 | inc(L.bufpos) 459 | of ',': 460 | tok.xkind = pxComma 461 | inc(L.bufpos) 462 | of '(': 463 | inc(L.bufpos) 464 | if (L.buf[L.bufPos] == '*'): 465 | if (L.buf[L.bufPos + 1] == '$'): 466 | inc(L.bufpos, 2) 467 | skip(L, tok) 468 | getSymbol(L, tok) 469 | tok.xkind = pxStarDirLe 470 | else: 471 | inc(L.bufpos) 472 | scanStarComment(L, tok) 473 | else: 474 | tok.xkind = pxParLe 475 | of '*': 476 | inc(L.bufpos) 477 | if L.buf[L.bufpos] == ')': 478 | inc(L.bufpos) 479 | tok.xkind = pxStarDirRi 480 | else: 481 | tok.xkind = pxStar 482 | of ')': 483 | tok.xkind = pxParRi 484 | inc(L.bufpos) 485 | of '[': 486 | inc(L.bufpos) 487 | tok.xkind = pxBracketLe 488 | of ']': 489 | inc(L.bufpos) 490 | tok.xkind = pxBracketRi 491 | of '.': 492 | inc(L.bufpos) 493 | if L.buf[L.bufpos] == '.': 494 | tok.xkind = pxDotDot 495 | inc(L.bufpos) 496 | else: 497 | tok.xkind = pxDot 498 | of '{': 499 | inc(L.bufpos) 500 | case L.buf[L.bufpos] 501 | of '$': 502 | inc(L.bufpos) 503 | skip(L, tok) 504 | getSymbol(L, tok) 505 | tok.xkind = pxCurlyDirLe 506 | of '&': 507 | inc(L.bufpos) 508 | tok.xkind = pxAmp 509 | of '%': 510 | inc(L.bufpos) 511 | tok.xkind = pxPer 512 | of '@': 513 | inc(L.bufpos) 514 | tok.xkind = pxCommand 515 | else: scanCurlyComment(L, tok) 516 | of '+': 517 | tok.xkind = pxPlus 518 | inc(L.bufpos) 519 | of '-': 520 | tok.xkind = pxMinus 521 | inc(L.bufpos) 522 | of ':': 523 | inc(L.bufpos) 524 | if L.buf[L.bufpos] == '=': 525 | inc(L.bufpos) 526 | tok.xkind = pxAsgn 527 | else: 528 | tok.xkind = pxColon 529 | of '<': 530 | inc(L.bufpos) 531 | if L.buf[L.bufpos] == '>': 532 | inc(L.bufpos) 533 | tok.xkind = pxNeq 534 | elif L.buf[L.bufpos] == '=': 535 | inc(L.bufpos) 536 | tok.xkind = pxLe 537 | else: 538 | tok.xkind = pxLt 539 | of '>': 540 | inc(L.bufpos) 541 | if L.buf[L.bufpos] == '=': 542 | inc(L.bufpos) 543 | tok.xkind = pxGe 544 | else: 545 | tok.xkind = pxGt 546 | of '=': 547 | tok.xkind = pxEquals 548 | inc(L.bufpos) 549 | of '@': 550 | tok.xkind = pxAt 551 | inc(L.bufpos) 552 | of '^': 553 | tok.xkind = pxHat 554 | inc(L.bufpos) 555 | of '}': 556 | tok.xkind = pxCurlyDirRi 557 | inc(L.bufpos) 558 | of '\'', '#': 559 | getString(L, tok) 560 | of '$': 561 | getNumber16(L, tok) 562 | of '%': 563 | getNumber2(L, tok) 564 | of nimlexbase.EndOfFile: 565 | tok.xkind = pxEof 566 | else: 567 | tok.literal = c & "" 568 | tok.xkind = pxInvalid 569 | lexMessage(L, errInvalidToken, c & " (\\" & $(ord(c)) & ')') 570 | inc(L.bufpos) 571 | -------------------------------------------------------------------------------- /pasparse.nim: -------------------------------------------------------------------------------- 1 | # 2 | # 3 | # Pas2nim - Pascal to Nim source converter 4 | # (c) Copyright 2012 Andreas Rumpf 5 | # 6 | # See the file "copying.txt", included in this 7 | # distribution, for details about the copyright. 8 | # 9 | 10 | # This module implements the parser of the Pascal variant Nim is written in. 11 | # It transfers a Pascal module into a Nimf AST. Then the renderer can be 12 | # used to convert the AST to its text representation. 13 | 14 | import 15 | os, llstream, paslex, idents, strutils, ast, astalgo, msgs, options 16 | 17 | type 18 | TSection = enum 19 | seImplementation, seInterface 20 | TContext = enum 21 | conExpr, conStmt, conTypeDesc 22 | TParserFlag* = enum 23 | pfRefs, ## use "ref" instead of "ptr" for Pascal's ^typ 24 | pfMoreReplacements, ## use more than the default replacements 25 | pfImportBlackList ## use import blacklist 26 | TParser*{.final.} = object 27 | section: TSection 28 | inParamList: bool 29 | context: TContext # needed for the @emit command 30 | lastVarSection: PNode 31 | lex: TLexer 32 | tok: TToken 33 | repl: TIdTable # replacements 34 | flags: set[TParserFlag] 35 | 36 | TReplaceTuple* = array[0..1, string] 37 | 38 | const 39 | ImportBlackList*: array[1..3, string] = ["nsystem", "sysutils", "charsets"] 40 | stdReplacements*: array[1..19, TReplaceTuple] = [["include", "incl"], 41 | ["exclude", "excl"], ["pchar", "cstring"], ["assignfile", "open"], 42 | ["integer", "int"], ["longword", "int32"], ["cardinal", "int"], 43 | ["boolean", "bool"], ["shortint", "int8"], ["smallint", "int16"], 44 | ["longint", "int32"], ["byte", "int8"], ["word", "int16"], 45 | ["single", "float32"], ["double", "float64"], ["real", "float"], 46 | ["length", "len"], ["len", "length"], ["setlength", "setlen"]] 47 | nimReplacements*: array[1..35, TReplaceTuple] = [["nimread", "read"], 48 | ["nimwrite", "write"], ["nimclosefile", "close"], ["closefile", "close"], 49 | ["openfile", "open"], ["nsystem", "system"], ["ntime", "times"], 50 | ["nos", "os"], ["nmath", "math"], ["ncopy", "copy"], ["addChar", "add"], 51 | ["halt", "quit"], ["nobject", "TObject"], ["eof", "EndOfFile"], 52 | ["input", "stdin"], ["output", "stdout"], ["addu", "`+%`"], 53 | ["subu", "`-%`"], ["mulu", "`*%`"], ["divu", "`/%`"], ["modu", "`%%`"], 54 | ["ltu", "`<%`"], ["leu", "`<=%`"], ["shlu", "`shl`"], ["shru", "`shr`"], 55 | ["assigned", "not isNil"], ["eintoverflow", "EOverflow"], ["format", "`%`"], 56 | ["snil", "nil"], ["tostringf", "$"], ["ttextfile", "tfile"], 57 | ["tbinaryfile", "tfile"], ["strstart", "0"], ["nl", "\"\\n\""], 58 | ["tostring", "$"]] 59 | 60 | proc parseUnit*(p: var TParser): PNode 61 | proc openParser*(p: var TParser, filename: string, inputStream: PLLStream, 62 | flags: set[TParserFlag] = {}) 63 | proc closeParser*(p: var TParser) 64 | proc exSymbol*(n: var PNode) 65 | proc fixRecordDef*(n: var PNode) 66 | # XXX: move these two to an auxiliary module 67 | 68 | # implementation 69 | 70 | proc openParser(p: var TParser, filename: string, 71 | inputStream: PLLStream, flags: set[TParserFlag] = {}) = 72 | openLexer(p.lex, filename, inputStream) 73 | initIdTable(p.repl) 74 | for i in countup(low(stdReplacements), high(stdReplacements)): 75 | idTablePut(p.repl, getIdent(stdReplacements[i][0]), 76 | getIdent(stdReplacements[i][1])) 77 | if pfMoreReplacements in flags: 78 | for i in countup(low(nimReplacements), high(nimReplacements)): 79 | idTablePut(p.repl, getIdent(nimReplacements[i][0]), 80 | getIdent(nimReplacements[i][1])) 81 | p.flags = flags 82 | 83 | proc closeParser(p: var TParser) = closeLexer(p.lex) 84 | proc getTok(p: var TParser) = getTok(p.lex, p.tok) 85 | 86 | proc parMessage(p: TParser, msg: TMsgKind, arg = "") = 87 | lexMessage(p.lex, msg, arg) 88 | 89 | proc parLineInfo(p: TParser): TLineInfo = 90 | result = getLineInfo(p.lex) 91 | 92 | proc skipCom(p: var TParser, n: PNode) = 93 | while p.tok.xkind == pxComment: 94 | if (n != nil): 95 | if n.comment == nil: n.comment = p.tok.literal 96 | else: add(n.comment, "\n" & p.tok.literal) 97 | else: 98 | parMessage(p, warnCommentXIgnored, p.tok.literal) 99 | getTok(p) 100 | 101 | proc expectIdent(p: TParser) = 102 | if p.tok.xkind != pxSymbol: 103 | lexMessage(p.lex, errIdentifierExpected, $(p.tok)) 104 | 105 | proc eat(p: var TParser, xkind: TTokKind) = 106 | if p.tok.xkind == xkind: getTok(p) 107 | else: lexMessage(p.lex, errTokenExpected, tokKindToStr(xkind)) 108 | 109 | proc opt(p: var TParser, xkind: TTokKind) = 110 | if p.tok.xkind == xkind: getTok(p) 111 | 112 | proc newNodeP(kind: TNodeKind, p: TParser): PNode = 113 | result = newNodeI(kind, getLineInfo(p.lex)) 114 | 115 | proc newIntNodeP(kind: TNodeKind, intVal: BiggestInt, p: TParser): PNode = 116 | result = newNodeP(kind, p) 117 | result.intVal = intVal 118 | 119 | proc newFloatNodeP(kind: TNodeKind, floatVal: BiggestFloat, 120 | p: TParser): PNode = 121 | result = newNodeP(kind, p) 122 | result.floatVal = floatVal 123 | 124 | proc newStrNodeP(kind: TNodeKind, strVal: string, p: TParser): PNode = 125 | result = newNodeP(kind, p) 126 | result.strVal = strVal 127 | 128 | proc newIdentNodeP(ident: PIdent, p: TParser): PNode = 129 | result = newNodeP(nkIdent, p) 130 | result.ident = ident 131 | 132 | proc createIdentNodeP(ident: PIdent, p: TParser): PNode = 133 | result = newNodeP(nkIdent, p) 134 | var x = PIdent(idTableGet(p.repl, ident)) 135 | if x != nil: result.ident = x 136 | else: result.ident = ident 137 | 138 | proc parseExpr(p: var TParser): PNode 139 | proc parseStmt(p: var TParser): PNode 140 | proc parseTypeDesc(p: var TParser, definition: PNode = nil): PNode 141 | 142 | proc parseEmit(p: var TParser, definition: PNode): PNode = 143 | getTok(p) # skip 'emit' 144 | result = ast.emptyNode 145 | if p.tok.xkind != pxCurlyDirRi: 146 | case p.context 147 | of conExpr: 148 | result = parseExpr(p) 149 | of conStmt: 150 | result = parseStmt(p) 151 | if p.tok.xkind != pxCurlyDirRi: 152 | var a = result 153 | result = newNodeP(nkStmtList, p) 154 | addSon(result, a) 155 | while p.tok.xkind != pxCurlyDirRi: 156 | addSon(result, parseStmt(p)) 157 | of conTypeDesc: 158 | result = parseTypeDesc(p, definition) 159 | eat(p, pxCurlyDirRi) 160 | 161 | proc parseCommand(p: var TParser, definition: PNode = nil): PNode = 162 | result = ast.emptyNode 163 | getTok(p) 164 | if p.tok.ident.id == getIdent("discard").id: 165 | result = newNodeP(nkDiscardStmt, p) 166 | getTok(p) 167 | eat(p, pxCurlyDirRi) 168 | addSon(result, parseExpr(p)) 169 | elif p.tok.ident.id == getIdent("set").id: 170 | getTok(p) 171 | eat(p, pxCurlyDirRi) 172 | result = parseExpr(p) 173 | if result.kind == nkEmpty: internalError("emptyNode modified") 174 | result.kind = nkCurly 175 | elif p.tok.ident.id == getIdent("cast").id: 176 | getTok(p) 177 | eat(p, pxCurlyDirRi) 178 | var a = parseExpr(p) 179 | if (a.kind == nkCall) and (sonsLen(a) == 2): 180 | result = newNodeP(nkCast, p) 181 | addSon(result, a.sons[0]) 182 | addSon(result, a.sons[1]) 183 | else: 184 | parMessage(p, errInvalidDirectiveX, $p.tok) 185 | result = a 186 | elif p.tok.ident.id == getIdent("emit").id: 187 | result = parseEmit(p, definition) 188 | elif p.tok.ident.id == getIdent("ignore").id: 189 | getTok(p) 190 | eat(p, pxCurlyDirRi) 191 | while true: 192 | case p.tok.xkind 193 | of pxEof: 194 | parMessage(p, errTokenExpected, "{@emit}") 195 | of pxCommand: 196 | getTok(p) 197 | if p.tok.ident.id == getIdent("emit").id: 198 | result = parseEmit(p, definition) 199 | break 200 | else: 201 | while (p.tok.xkind != pxCurlyDirRi) and (p.tok.xkind != pxEof): 202 | getTok(p) 203 | eat(p, pxCurlyDirRi) 204 | else: 205 | getTok(p) # skip token 206 | elif p.tok.ident.id == getIdent("ptr").id: 207 | result = newNodeP(nkPtrTy, p) 208 | getTok(p) 209 | eat(p, pxCurlyDirRi) 210 | elif p.tok.ident.id == getIdent("tuple").id: 211 | result = newNodeP(nkTupleTy, p) 212 | getTok(p) 213 | eat(p, pxCurlyDirRi) 214 | elif p.tok.ident.id == getIdent("acyclic").id: 215 | result = newIdentNodeP(p.tok.ident, p) 216 | getTok(p) 217 | eat(p, pxCurlyDirRi) 218 | else: 219 | parMessage(p, errInvalidDirectiveX, $p.tok) 220 | while true: 221 | getTok(p) 222 | if p.tok.xkind == pxCurlyDirRi or p.tok.xkind == pxEof: break 223 | eat(p, pxCurlyDirRi) 224 | result = ast.emptyNode 225 | 226 | proc getPrecedence(kind: TTokKind): int = 227 | case kind 228 | of pxDiv, pxMod, pxStar, pxSlash, pxShl, pxShr, pxAnd: result = 5 229 | of pxPlus, pxMinus, pxOr, pxXor: result = 4 230 | of pxIn, pxEquals, pxLe, pxLt, pxGe, pxGt, pxNeq, pxIs: result = 3 231 | else: result = -1 232 | 233 | proc rangeExpr(p: var TParser): PNode = 234 | var a = parseExpr(p) 235 | if p.tok.xkind == pxDotDot: 236 | result = newNodeP(nkRange, p) 237 | addSon(result, a) 238 | getTok(p) 239 | skipCom(p, result) 240 | addSon(result, parseExpr(p)) 241 | else: 242 | result = a 243 | 244 | proc bracketExprList(p: var TParser, first: PNode): PNode = 245 | result = newNodeP(nkBracketExpr, p) 246 | addSon(result, first) 247 | getTok(p) 248 | skipCom(p, result) 249 | while true: 250 | if p.tok.xkind == pxBracketRi: 251 | getTok(p) 252 | break 253 | if p.tok.xkind == pxEof: 254 | parMessage(p, errTokenExpected, tokKindToStr(pxBracketRi)) 255 | break 256 | var a = rangeExpr(p) 257 | skipCom(p, a) 258 | if p.tok.xkind == pxComma: 259 | getTok(p) 260 | skipCom(p, a) 261 | addSon(result, a) 262 | 263 | proc exprColonEqExpr(p: var TParser, kind: TNodeKind, 264 | tok: TTokKind): PNode = 265 | var a = parseExpr(p) 266 | if p.tok.xkind == tok: 267 | result = newNodeP(kind, p) 268 | getTok(p) 269 | skipCom(p, result) 270 | addSon(result, a) 271 | addSon(result, parseExpr(p)) 272 | else: 273 | result = a 274 | 275 | proc exprListAux(p: var TParser, elemKind: TNodeKind, 276 | endTok, sepTok: TTokKind, result: PNode) = 277 | getTok(p) 278 | skipCom(p, result) 279 | while true: 280 | if p.tok.xkind == endTok: 281 | getTok(p) 282 | break 283 | if p.tok.xkind == pxEof: 284 | parMessage(p, errTokenExpected, tokKindToStr(endTok)) 285 | break 286 | var a = exprColonEqExpr(p, elemKind, sepTok) 287 | skipCom(p, a) 288 | if (p.tok.xkind == pxComma) or (p.tok.xkind == pxSemicolon): 289 | getTok(p) 290 | skipCom(p, a) 291 | addSon(result, a) 292 | 293 | proc qualifiedIdent(p: var TParser): PNode = 294 | if p.tok.xkind == pxSymbol: 295 | result = createIdentNodeP(p.tok.ident, p) 296 | else: 297 | parMessage(p, errIdentifierExpected, $p.tok) 298 | return ast.emptyNode 299 | getTok(p) 300 | skipCom(p, result) 301 | if p.tok.xkind == pxDot: 302 | getTok(p) 303 | skipCom(p, result) 304 | if p.tok.xkind == pxSymbol: 305 | var a = result 306 | result = newNodeI(nkDotExpr, a.info) 307 | addSon(result, a) 308 | addSon(result, createIdentNodeP(p.tok.ident, p)) 309 | getTok(p) 310 | else: 311 | parMessage(p, errIdentifierExpected, $p.tok) 312 | 313 | proc qualifiedIdentListAux(p: var TParser, endTok: TTokKind, 314 | result: PNode) = 315 | getTok(p) 316 | skipCom(p, result) 317 | while true: 318 | if p.tok.xkind == endTok: 319 | getTok(p) 320 | break 321 | if p.tok.xkind == pxEof: 322 | parMessage(p, errTokenExpected, tokKindToStr(endTok)) 323 | break 324 | var a = qualifiedIdent(p) 325 | skipCom(p, a) 326 | if p.tok.xkind == pxComma: 327 | getTok(p) 328 | skipCom(p, a) 329 | addSon(result, a) 330 | 331 | proc exprColonEqExprList(p: var TParser, kind, elemKind: TNodeKind, 332 | endTok, sepTok: TTokKind): PNode = 333 | result = newNodeP(kind, p) 334 | exprListAux(p, elemKind, endTok, sepTok, result) 335 | 336 | proc setBaseFlags(n: PNode, base: TNumericalBase) = 337 | case base 338 | of base10: discard 339 | of base2: incl(n.flags, nfBase2) 340 | of base8: incl(n.flags, nfBase8) 341 | of base16: incl(n.flags, nfBase16) 342 | 343 | proc identOrLiteral(p: var TParser): PNode = 344 | case p.tok.xkind 345 | of pxSymbol: 346 | result = createIdentNodeP(p.tok.ident, p) 347 | getTok(p) 348 | of pxIntLit: 349 | result = newIntNodeP(nkIntLit, p.tok.iNumber, p) 350 | setBaseFlags(result, p.tok.base) 351 | getTok(p) 352 | of pxInt64Lit: 353 | result = newIntNodeP(nkInt64Lit, p.tok.iNumber, p) 354 | setBaseFlags(result, p.tok.base) 355 | getTok(p) 356 | of pxFloatLit: 357 | result = newFloatNodeP(nkFloatLit, p.tok.fNumber, p) 358 | setBaseFlags(result, p.tok.base) 359 | getTok(p) 360 | of pxStrLit: 361 | if len(p.tok.literal) != 1: result = newStrNodeP(nkStrLit, p.tok.literal, p) 362 | else: result = newIntNodeP(nkCharLit, ord(p.tok.literal[0]), p) 363 | getTok(p) 364 | of pxNil: 365 | result = newNodeP(nkNilLit, p) 366 | getTok(p) 367 | of pxParLe: 368 | # () constructor 369 | result = exprColonEqExprList(p, nkPar, nkExprColonExpr, pxParRi, pxColon) 370 | #if hasSonWith(result, nkExprColonExpr) then 371 | # replaceSons(result, nkExprColonExpr, nkExprEqExpr) 372 | if (sonsLen(result) > 1) and not hasSonWith(result, nkExprColonExpr): 373 | result.kind = nkBracket # is an array constructor 374 | of pxBracketLe: 375 | # [] constructor 376 | result = newNodeP(nkBracket, p) 377 | getTok(p) 378 | skipCom(p, result) 379 | while (p.tok.xkind != pxBracketRi) and (p.tok.xkind != pxEof): 380 | var a = rangeExpr(p) 381 | if a.kind == nkRange: 382 | result.kind = nkCurly # it is definitely a set literal 383 | opt(p, pxComma) 384 | skipCom(p, a) 385 | assert(a != nil) 386 | addSon(result, a) 387 | eat(p, pxBracketRi) 388 | of pxCommand: 389 | result = parseCommand(p) 390 | else: 391 | parMessage(p, errExprExpected, $(p.tok)) 392 | getTok(p) # we must consume a token here to prevend endless loops! 393 | result = ast.emptyNode 394 | if result.kind != nkEmpty: skipCom(p, result) 395 | 396 | proc primary(p: var TParser): PNode = 397 | # prefix operator? 398 | if (p.tok.xkind == pxNot) or (p.tok.xkind == pxMinus) or 399 | (p.tok.xkind == pxPlus): 400 | result = newNodeP(nkPrefix, p) 401 | var a = newIdentNodeP(getIdent($p.tok), p) 402 | addSon(result, a) 403 | getTok(p) 404 | skipCom(p, a) 405 | addSon(result, primary(p)) 406 | return 407 | elif p.tok.xkind == pxAt: 408 | result = newNodeP(nkAddr, p) 409 | var a = newIdentNodeP(getIdent($p.tok), p) 410 | getTok(p) 411 | if p.tok.xkind == pxBracketLe: 412 | result = newNodeP(nkPrefix, p) 413 | addSon(result, a) 414 | addSon(result, identOrLiteral(p)) 415 | else: 416 | addSon(result, primary(p)) 417 | return 418 | result = identOrLiteral(p) 419 | while true: 420 | case p.tok.xkind 421 | of pxParLe: 422 | var a = result 423 | result = newNodeP(nkCall, p) 424 | addSon(result, a) 425 | exprListAux(p, nkExprEqExpr, pxParRi, pxEquals, result) 426 | of pxDot: 427 | var a = result 428 | result = newNodeP(nkDotExpr, p) 429 | addSon(result, a) 430 | getTok(p) # skip '.' 431 | skipCom(p, result) 432 | if p.tok.xkind == pxSymbol: 433 | addSon(result, createIdentNodeP(p.tok.ident, p)) 434 | getTok(p) 435 | else: 436 | parMessage(p, errIdentifierExpected, $p.tok) 437 | of pxHat: 438 | var a = result 439 | result = newNodeP(nkBracketExpr, p) 440 | addSon(result, a) 441 | getTok(p) 442 | of pxBracketLe: 443 | result = bracketExprList(p, result) 444 | else: break 445 | 446 | proc lowestExprAux(p: var TParser, v: var PNode, limit: int): TTokKind = 447 | var 448 | nextop: TTokKind 449 | v2, node, opNode: PNode 450 | v = primary(p) # expand while operators have priorities higher than 'limit' 451 | var op = p.tok.xkind 452 | var opPred = getPrecedence(op) 453 | while (opPred > limit): 454 | node = newNodeP(nkInfix, p) 455 | opNode = newIdentNodeP(getIdent($(p.tok)), p) # skip operator: 456 | getTok(p) 457 | case op 458 | of pxPlus: 459 | case p.tok.xkind 460 | of pxPer: 461 | getTok(p) 462 | eat(p, pxCurlyDirRi) 463 | opNode.ident = getIdent("+%") 464 | of pxAmp: 465 | getTok(p) 466 | eat(p, pxCurlyDirRi) 467 | opNode.ident = getIdent("&") 468 | else: 469 | discard 470 | of pxMinus: 471 | if p.tok.xkind == pxPer: 472 | getTok(p) 473 | eat(p, pxCurlyDirRi) 474 | opNode.ident = getIdent("-%") 475 | of pxEquals: 476 | opNode.ident = getIdent("==") 477 | of pxNeq: 478 | opNode.ident = getIdent("!=") 479 | else: 480 | discard 481 | skipCom(p, opNode) # read sub-expression with higher priority 482 | nextop = lowestExprAux(p, v2, opPred) 483 | addSon(node, opNode) 484 | addSon(node, v) 485 | addSon(node, v2) 486 | v = node 487 | op = nextop 488 | opPred = getPrecedence(nextop) 489 | result = op # return first untreated operator 490 | 491 | proc fixExpr(n: PNode): PNode = 492 | result = n 493 | case n.kind 494 | of nkInfix: 495 | if n.sons[1].kind == nkBracket: n.sons[1].kind = nkCurly 496 | if n.sons[2].kind == nkBracket: n.sons[2].kind = nkCurly 497 | if (n.sons[0].kind == nkIdent): 498 | if (n.sons[0].ident.id == getIdent("+").id): 499 | if (n.sons[1].kind == nkCharLit) and (n.sons[2].kind == nkStrLit) and 500 | (n.sons[2].strVal == ""): 501 | result = newStrNode(nkStrLit, chr(int(n.sons[1].intVal)) & "") 502 | result.info = n.info 503 | return # do not process sons as they don't exist anymore 504 | elif (n.sons[1].kind in {nkCharLit, nkStrLit}) or 505 | (n.sons[2].kind in {nkCharLit, nkStrLit}): 506 | n.sons[0].ident = getIdent("&") # fix operator 507 | else: 508 | discard 509 | if not (n.kind in {nkEmpty..nkNilLit}): 510 | for i in countup(0, sonsLen(n) - 1): result.sons[i] = fixExpr(n.sons[i]) 511 | 512 | proc parseExpr(p: var TParser): PNode = 513 | var oldcontext = p.context 514 | p.context = conExpr 515 | if p.tok.xkind == pxCommand: 516 | result = parseCommand(p) 517 | else: 518 | discard lowestExprAux(p, result, - 1) 519 | result = fixExpr(result) 520 | p.context = oldcontext 521 | 522 | proc parseExprStmt(p: var TParser): PNode = 523 | var info = parLineInfo(p) 524 | var a = parseExpr(p) 525 | if p.tok.xkind == pxAsgn: 526 | getTok(p) 527 | skipCom(p, a) 528 | var b = parseExpr(p) 529 | result = newNodeI(nkAsgn, info) 530 | addSon(result, a) 531 | addSon(result, b) 532 | else: 533 | result = a 534 | 535 | proc inImportBlackList(ident: PIdent): bool = 536 | for i in countup(low(ImportBlackList), high(ImportBlackList)): 537 | if ident.id == getIdent(ImportBlackList[i]).id: 538 | return true 539 | 540 | proc parseUsesStmt(p: var TParser): PNode = 541 | var a: PNode 542 | result = newNodeP(nkImportStmt, p) 543 | getTok(p) # skip `import` 544 | skipCom(p, result) 545 | while true: 546 | case p.tok.xkind 547 | of pxEof: break 548 | of pxSymbol: a = newIdentNodeP(p.tok.ident, p) 549 | else: 550 | parMessage(p, errIdentifierExpected, $(p.tok)) 551 | break 552 | getTok(p) # skip identifier, string 553 | skipCom(p, a) 554 | if pfImportBlackList notin p.flags or not inImportBlackList(a.ident): 555 | addSon(result, createIdentNodeP(a.ident, p)) 556 | if p.tok.xkind == pxComma: 557 | getTok(p) 558 | skipCom(p, a) 559 | else: 560 | break 561 | if sonsLen(result) == 0: result = ast.emptyNode 562 | 563 | proc parseIncludeDir(p: var TParser): PNode = 564 | result = newNodeP(nkIncludeStmt, p) 565 | getTok(p) # skip `include` 566 | var filename = "" 567 | while true: 568 | case p.tok.xkind 569 | of pxSymbol, pxDot, pxDotDot, pxSlash: 570 | add(filename, $p.tok) 571 | getTok(p) 572 | of pxStrLit: 573 | filename = p.tok.literal 574 | getTok(p) 575 | break 576 | of pxCurlyDirRi: 577 | break 578 | else: 579 | parMessage(p, errIdentifierExpected, $p.tok) 580 | break 581 | addSon(result, newStrNodeP(nkStrLit, changeFileExt(filename, "nim"), p)) 582 | if filename == "config.inc": result = ast.emptyNode 583 | 584 | proc definedExprAux(p: var TParser): PNode = 585 | result = newNodeP(nkCall, p) 586 | addSon(result, newIdentNodeP(getIdent("defined"), p)) 587 | expectIdent(p) 588 | addSon(result, createIdentNodeP(p.tok.ident, p)) 589 | getTok(p) 590 | 591 | proc isHandledDirective(p: TParser): bool = 592 | if p.tok.xkind in {pxCurlyDirLe, pxStarDirLe}: 593 | case toLower(p.tok.ident.s) 594 | of "else", "endif": result = false 595 | else: result = true 596 | 597 | proc parseStmtList(p: var TParser): PNode = 598 | result = newNodeP(nkStmtList, p) 599 | while true: 600 | case p.tok.xkind 601 | of pxEof: 602 | break 603 | of pxCurlyDirLe, pxStarDirLe: 604 | if not isHandledDirective(p): break 605 | else: 606 | discard 607 | addSon(result, parseStmt(p)) 608 | if sonsLen(result) == 1: result = result.sons[0] 609 | 610 | proc parseIfDirAux(p: var TParser, result: PNode) = 611 | addSon(result.sons[0], parseStmtList(p)) 612 | if p.tok.xkind in {pxCurlyDirLe, pxStarDirLe}: 613 | var endMarker = succ(p.tok.xkind) 614 | if toLower(p.tok.ident.s) == "else": 615 | var s = newNodeP(nkElse, p) 616 | while p.tok.xkind != pxEof and p.tok.xkind != endMarker: getTok(p) 617 | eat(p, endMarker) 618 | addSon(s, parseStmtList(p)) 619 | addSon(result, s) 620 | if p.tok.xkind in {pxCurlyDirLe, pxStarDirLe}: 621 | endMarker = succ(p.tok.xkind) 622 | if toLower(p.tok.ident.s) == "endif": 623 | while p.tok.xkind != pxEof and p.tok.xkind != endMarker: getTok(p) 624 | eat(p, endMarker) 625 | else: 626 | parMessage(p, errXExpected, "{$endif}") 627 | else: 628 | parMessage(p, errXExpected, "{$endif}") 629 | 630 | proc parseIfdefDir(p: var TParser, endMarker: TTokKind): PNode = 631 | result = newNodeP(nkWhenStmt, p) 632 | addSon(result, newNodeP(nkElifBranch, p)) 633 | getTok(p) 634 | addSon(result.sons[0], definedExprAux(p)) 635 | eat(p, endMarker) 636 | parseIfDirAux(p, result) 637 | 638 | proc parseIfndefDir(p: var TParser, endMarker: TTokKind): PNode = 639 | result = newNodeP(nkWhenStmt, p) 640 | addSon(result, newNodeP(nkElifBranch, p)) 641 | getTok(p) 642 | var e = newNodeP(nkCall, p) 643 | addSon(e, newIdentNodeP(getIdent("not"), p)) 644 | addSon(e, definedExprAux(p)) 645 | eat(p, endMarker) 646 | addSon(result.sons[0], e) 647 | parseIfDirAux(p, result) 648 | 649 | proc parseIfDir(p: var TParser, endMarker: TTokKind): PNode = 650 | result = newNodeP(nkWhenStmt, p) 651 | addSon(result, newNodeP(nkElifBranch, p)) 652 | getTok(p) 653 | addSon(result.sons[0], parseExpr(p)) 654 | eat(p, endMarker) 655 | parseIfDirAux(p, result) 656 | 657 | proc parseDirective(p: var TParser): PNode = 658 | result = ast.emptyNode 659 | if not (p.tok.xkind in {pxCurlyDirLe, pxStarDirLe}): return 660 | var endMarker = succ(p.tok.xkind) 661 | if p.tok.ident != nil: 662 | case toLower(p.tok.ident.s) 663 | of "include": 664 | result = parseIncludeDir(p) 665 | eat(p, endMarker) 666 | of "if": result = parseIfDir(p, endMarker) 667 | of "ifdef": result = parseIfdefDir(p, endMarker) 668 | of "ifndef": result = parseIfndefDir(p, endMarker) 669 | else: 670 | # skip unknown compiler directive 671 | while p.tok.xkind != pxEof and p.tok.xkind != endMarker: getTok(p) 672 | eat(p, endMarker) 673 | else: 674 | eat(p, endMarker) 675 | 676 | proc parseRaise(p: var TParser): PNode = 677 | result = newNodeP(nkRaiseStmt, p) 678 | getTok(p) 679 | skipCom(p, result) 680 | if p.tok.xkind != pxSemicolon: addSon(result, parseExpr(p)) 681 | else: addSon(result, ast.emptyNode) 682 | 683 | proc parseIf(p: var TParser): PNode = 684 | result = newNodeP(nkIfStmt, p) 685 | while true: 686 | getTok(p) # skip ``if`` 687 | var branch = newNodeP(nkElifBranch, p) 688 | skipCom(p, branch) 689 | addSon(branch, parseExpr(p)) 690 | eat(p, pxThen) 691 | skipCom(p, branch) 692 | addSon(branch, parseStmt(p)) 693 | skipCom(p, branch) 694 | addSon(result, branch) 695 | if p.tok.xkind == pxElse: 696 | getTok(p) 697 | if p.tok.xkind != pxIf: 698 | # ordinary else part: 699 | branch = newNodeP(nkElse, p) 700 | skipCom(p, result) # BUGFIX 701 | addSon(branch, parseStmt(p)) 702 | addSon(result, branch) 703 | break 704 | else: 705 | break 706 | 707 | proc parseWhile(p: var TParser): PNode = 708 | result = newNodeP(nkWhileStmt, p) 709 | getTok(p) 710 | skipCom(p, result) 711 | addSon(result, parseExpr(p)) 712 | eat(p, pxDo) 713 | skipCom(p, result) 714 | addSon(result, parseStmt(p)) 715 | 716 | proc parseRepeat(p: var TParser): PNode = 717 | result = newNodeP(nkWhileStmt, p) 718 | getTok(p) 719 | skipCom(p, result) 720 | addSon(result, newIdentNodeP(getIdent("true"), p)) 721 | var s = newNodeP(nkStmtList, p) 722 | while p.tok.xkind != pxEof and p.tok.xkind != pxUntil: 723 | addSon(s, parseStmt(p)) 724 | eat(p, pxUntil) 725 | var a = newNodeP(nkIfStmt, p) 726 | skipCom(p, a) 727 | var b = newNodeP(nkElifBranch, p) 728 | var c = newNodeP(nkBreakStmt, p) 729 | addSon(c, ast.emptyNode) 730 | addSon(b, parseExpr(p)) 731 | skipCom(p, a) 732 | addSon(b, c) 733 | addSon(a, b) 734 | if b.sons[0].kind == nkIdent and b.sons[0].ident.id == getIdent("false").id: 735 | discard 736 | else: 737 | addSon(s, a) 738 | addSon(result, s) 739 | 740 | proc parseCase(p: var TParser): PNode = 741 | var b: PNode 742 | result = newNodeP(nkCaseStmt, p) 743 | getTok(p) 744 | addSon(result, parseExpr(p)) 745 | eat(p, pxOf) 746 | skipCom(p, result) 747 | while (p.tok.xkind != pxEnd) and (p.tok.xkind != pxEof): 748 | if p.tok.xkind == pxElse: 749 | b = newNodeP(nkElse, p) 750 | getTok(p) 751 | else: 752 | b = newNodeP(nkOfBranch, p) 753 | while (p.tok.xkind != pxEof) and (p.tok.xkind != pxColon): 754 | addSon(b, rangeExpr(p)) 755 | opt(p, pxComma) 756 | skipCom(p, b) 757 | eat(p, pxColon) 758 | skipCom(p, b) 759 | addSon(b, parseStmt(p)) 760 | addSon(result, b) 761 | if b.kind == nkElse: break 762 | eat(p, pxEnd) 763 | 764 | proc parseTry(p: var TParser): PNode = 765 | result = newNodeP(nkTryStmt, p) 766 | getTok(p) 767 | skipCom(p, result) 768 | var b = newNodeP(nkStmtList, p) 769 | while not (p.tok.xkind in {pxFinally, pxExcept, pxEof, pxEnd}): 770 | addSon(b, parseStmt(p)) 771 | addSon(result, b) 772 | if p.tok.xkind == pxExcept: 773 | getTok(p) 774 | while p.tok.ident.id == getIdent("on").id: 775 | b = newNodeP(nkExceptBranch, p) 776 | getTok(p) 777 | var e = qualifiedIdent(p) 778 | if p.tok.xkind == pxColon: 779 | getTok(p) 780 | e = qualifiedIdent(p) 781 | addSon(b, e) 782 | eat(p, pxDo) 783 | addSon(b, parseStmt(p)) 784 | addSon(result, b) 785 | if p.tok.xkind == pxCommand: discard parseCommand(p) 786 | if p.tok.xkind == pxElse: 787 | b = newNodeP(nkExceptBranch, p) 788 | getTok(p) 789 | addSon(b, parseStmt(p)) 790 | addSon(result, b) 791 | if p.tok.xkind == pxFinally: 792 | b = newNodeP(nkFinally, p) 793 | getTok(p) 794 | var e = newNodeP(nkStmtList, p) 795 | while (p.tok.xkind != pxEof) and (p.tok.xkind != pxEnd): 796 | addSon(e, parseStmt(p)) 797 | if sonsLen(e) == 0: addSon(e, newNodeP(nkNilLit, p)) 798 | addSon(result, e) 799 | eat(p, pxEnd) 800 | 801 | proc parseFor(p: var TParser): PNode = 802 | result = newNodeP(nkForStmt, p) 803 | getTok(p) 804 | skipCom(p, result) 805 | expectIdent(p) 806 | addSon(result, createIdentNodeP(p.tok.ident, p)) 807 | getTok(p) 808 | eat(p, pxAsgn) 809 | var a = parseExpr(p) 810 | var b = ast.emptyNode 811 | var c = newNodeP(nkCall, p) 812 | if p.tok.xkind == pxTo: 813 | addSon(c, newIdentNodeP(getIdent("countup"), p)) 814 | getTok(p) 815 | b = parseExpr(p) 816 | elif p.tok.xkind == pxDownto: 817 | addSon(c, newIdentNodeP(getIdent("countdown"), p)) 818 | getTok(p) 819 | b = parseExpr(p) 820 | else: 821 | parMessage(p, errTokenExpected, tokKindToStr(pxTo)) 822 | addSon(c, a) 823 | addSon(c, b) 824 | eat(p, pxDo) 825 | skipCom(p, result) 826 | addSon(result, c) 827 | addSon(result, parseStmt(p)) 828 | 829 | proc parseParam(p: var TParser): PNode = 830 | var a: PNode 831 | result = newNodeP(nkIdentDefs, p) 832 | var v = ast.emptyNode 833 | case p.tok.xkind 834 | of pxConst: 835 | getTok(p) 836 | of pxVar: 837 | getTok(p) 838 | v = newNodeP(nkVarTy, p) 839 | of pxOut: 840 | getTok(p) 841 | v = newNodeP(nkVarTy, p) 842 | else: 843 | discard 844 | while true: 845 | case p.tok.xkind 846 | of pxSymbol: a = createIdentNodeP(p.tok.ident, p) 847 | of pxColon, pxEof, pxParRi, pxEquals: break 848 | else: 849 | parMessage(p, errIdentifierExpected, $p.tok) 850 | return 851 | getTok(p) # skip identifier 852 | skipCom(p, a) 853 | if p.tok.xkind == pxComma: 854 | getTok(p) 855 | skipCom(p, a) 856 | addSon(result, a) 857 | if p.tok.xkind == pxColon: 858 | getTok(p) 859 | skipCom(p, result) 860 | if v.kind != nkEmpty: addSon(v, parseTypeDesc(p)) 861 | else: v = parseTypeDesc(p) 862 | addSon(result, v) 863 | else: 864 | addSon(result, ast.emptyNode) 865 | if p.tok.xkind != pxEquals: 866 | parMessage(p, errColonOrEqualsExpected, $p.tok) 867 | if p.tok.xkind == pxEquals: 868 | getTok(p) 869 | skipCom(p, result) 870 | addSon(result, parseExpr(p)) 871 | else: 872 | addSon(result, ast.emptyNode) 873 | 874 | proc parseParamList(p: var TParser): PNode = 875 | var a: PNode 876 | result = newNodeP(nkFormalParams, p) 877 | addSon(result, ast.emptyNode) # return type 878 | if p.tok.xkind == pxParLe: 879 | p.inParamList = true 880 | getTok(p) 881 | skipCom(p, result) 882 | while true: 883 | case p.tok.xkind 884 | of pxSymbol, pxConst, pxVar, pxOut: 885 | a = parseParam(p) 886 | of pxParRi: 887 | getTok(p) 888 | break 889 | else: 890 | parMessage(p, errTokenExpected, ")") 891 | break 892 | skipCom(p, a) 893 | if p.tok.xkind == pxSemicolon: 894 | getTok(p) 895 | skipCom(p, a) 896 | addSon(result, a) 897 | p.inParamList = false 898 | if p.tok.xkind == pxColon: 899 | getTok(p) 900 | skipCom(p, result) 901 | result.sons[0] = parseTypeDesc(p) 902 | 903 | proc parseCallingConvention(p: var TParser): PNode = 904 | result = ast.emptyNode 905 | if p.tok.xkind == pxSymbol: 906 | case toLower(p.tok.ident.s) 907 | of "stdcall", "cdecl", "safecall", "syscall", "inline", "fastcall": 908 | result = newNodeP(nkPragma, p) 909 | addSon(result, newIdentNodeP(p.tok.ident, p)) 910 | getTok(p) 911 | opt(p, pxSemicolon) 912 | of "register": 913 | result = newNodeP(nkPragma, p) 914 | addSon(result, newIdentNodeP(getIdent("fastcall"), p)) 915 | getTok(p) 916 | opt(p, pxSemicolon) 917 | else: 918 | discard 919 | 920 | proc parseRoutineSpecifiers(p: var TParser, noBody: var bool): PNode = 921 | var e: PNode 922 | result = parseCallingConvention(p) 923 | noBody = false 924 | while p.tok.xkind == pxSymbol: 925 | case toLower(p.tok.ident.s) 926 | of "assembler", "overload", "far": 927 | getTok(p) 928 | opt(p, pxSemicolon) 929 | of "forward": 930 | noBody = true 931 | getTok(p) 932 | opt(p, pxSemicolon) 933 | of "importc": 934 | # This is a fake for platform module. There is no ``importc`` 935 | # directive in Pascal. 936 | if result.kind == nkEmpty: result = newNodeP(nkPragma, p) 937 | addSon(result, newIdentNodeP(getIdent("importc"), p)) 938 | noBody = true 939 | getTok(p) 940 | opt(p, pxSemicolon) 941 | of "noconv": 942 | # This is a fake for platform module. There is no ``noconv`` 943 | # directive in Pascal. 944 | if result.kind == nkEmpty: result = newNodeP(nkPragma, p) 945 | addSon(result, newIdentNodeP(getIdent("noconv"), p)) 946 | noBody = true 947 | getTok(p) 948 | opt(p, pxSemicolon) 949 | of "procvar": 950 | # This is a fake for the Nim compiler. There is no ``procvar`` 951 | # directive in Pascal. 952 | if result.kind == nkEmpty: result = newNodeP(nkPragma, p) 953 | addSon(result, newIdentNodeP(getIdent("procvar"), p)) 954 | getTok(p) 955 | opt(p, pxSemicolon) 956 | of "varargs": 957 | if result.kind == nkEmpty: result = newNodeP(nkPragma, p) 958 | addSon(result, newIdentNodeP(getIdent("varargs"), p)) 959 | getTok(p) 960 | opt(p, pxSemicolon) 961 | of "external": 962 | if result.kind == nkEmpty: result = newNodeP(nkPragma, p) 963 | getTok(p) 964 | noBody = true 965 | e = newNodeP(nkExprColonExpr, p) 966 | addSon(e, newIdentNodeP(getIdent("dynlib"), p)) 967 | addSon(e, parseExpr(p)) 968 | addSon(result, e) 969 | opt(p, pxSemicolon) 970 | if (p.tok.xkind == pxSymbol) and 971 | (p.tok.ident.id == getIdent("name").id): 972 | e = newNodeP(nkExprColonExpr, p) 973 | getTok(p) 974 | addSon(e, newIdentNodeP(getIdent("importc"), p)) 975 | addSon(e, parseExpr(p)) 976 | addSon(result, e) 977 | else: 978 | addSon(result, newIdentNodeP(getIdent("importc"), p)) 979 | opt(p, pxSemicolon) 980 | else: 981 | e = parseCallingConvention(p) 982 | if e.kind == nkEmpty: break 983 | if result.kind == nkEmpty: result = newNodeP(nkPragma, p) 984 | addSon(result, e.sons[0]) 985 | 986 | proc parseRoutineType(p: var TParser): PNode = 987 | result = newNodeP(nkProcTy, p) 988 | getTok(p) 989 | skipCom(p, result) 990 | addSon(result, parseParamList(p)) 991 | opt(p, pxSemicolon) 992 | addSon(result, parseCallingConvention(p)) 993 | skipCom(p, result) 994 | 995 | proc parseEnum(p: var TParser): PNode = 996 | var a: PNode 997 | result = newNodeP(nkEnumTy, p) 998 | getTok(p) 999 | skipCom(p, result) 1000 | addSon(result, ast.emptyNode) # it does not inherit from any enumeration 1001 | while true: 1002 | case p.tok.xkind 1003 | of pxEof, pxParRi: break 1004 | of pxSymbol: a = newIdentNodeP(p.tok.ident, p) 1005 | else: 1006 | parMessage(p, errIdentifierExpected, $(p.tok)) 1007 | break 1008 | getTok(p) # skip identifier 1009 | skipCom(p, a) 1010 | if (p.tok.xkind == pxEquals) or (p.tok.xkind == pxAsgn): 1011 | getTok(p) 1012 | skipCom(p, a) 1013 | var b = a 1014 | a = newNodeP(nkEnumFieldDef, p) 1015 | addSon(a, b) 1016 | addSon(a, parseExpr(p)) 1017 | if p.tok.xkind == pxComma: 1018 | getTok(p) 1019 | skipCom(p, a) 1020 | addSon(result, a) 1021 | eat(p, pxParRi) 1022 | 1023 | proc identVis(p: var TParser): PNode = 1024 | # identifier with visability 1025 | var a = createIdentNodeP(p.tok.ident, p) 1026 | if p.section == seInterface: 1027 | result = newNodeP(nkPostfix, p) 1028 | addSon(result, newIdentNodeP(getIdent("*"), p)) 1029 | addSon(result, a) 1030 | else: 1031 | result = a 1032 | getTok(p) 1033 | 1034 | type 1035 | TSymbolParser = proc (p: var TParser): PNode {.nimcall.} 1036 | 1037 | proc rawIdent(p: var TParser): PNode = 1038 | result = createIdentNodeP(p.tok.ident, p) 1039 | getTok(p) 1040 | 1041 | proc parseIdentColonEquals(p: var TParser, 1042 | identParser: TSymbolParser): PNode = 1043 | var a: PNode 1044 | result = newNodeP(nkIdentDefs, p) 1045 | while true: 1046 | case p.tok.xkind 1047 | of pxSymbol: a = identParser(p) 1048 | of pxColon, pxEof, pxParRi, pxEquals: break 1049 | else: 1050 | parMessage(p, errIdentifierExpected, $(p.tok)) 1051 | return 1052 | skipCom(p, a) 1053 | if p.tok.xkind == pxComma: 1054 | getTok(p) 1055 | skipCom(p, a) 1056 | addSon(result, a) 1057 | if p.tok.xkind == pxColon: 1058 | getTok(p) 1059 | skipCom(p, result) 1060 | addSon(result, parseTypeDesc(p)) 1061 | else: 1062 | addSon(result, ast.emptyNode) 1063 | if p.tok.xkind != pxEquals: 1064 | parMessage(p, errColonOrEqualsExpected, $(p.tok)) 1065 | if p.tok.xkind == pxEquals: 1066 | getTok(p) 1067 | skipCom(p, result) 1068 | addSon(result, parseExpr(p)) 1069 | else: 1070 | addSon(result, ast.emptyNode) 1071 | if p.tok.xkind == pxSemicolon: 1072 | getTok(p) 1073 | skipCom(p, result) 1074 | 1075 | proc parseRecordCase(p: var TParser): PNode = 1076 | var b, c: PNode 1077 | result = newNodeP(nkRecCase, p) 1078 | getTok(p) 1079 | var a = newNodeP(nkIdentDefs, p) 1080 | addSon(a, rawIdent(p)) 1081 | eat(p, pxColon) 1082 | addSon(a, parseTypeDesc(p)) 1083 | addSon(a, ast.emptyNode) 1084 | addSon(result, a) 1085 | eat(p, pxOf) 1086 | skipCom(p, result) 1087 | while true: 1088 | case p.tok.xkind 1089 | of pxEof, pxEnd: 1090 | break 1091 | of pxElse: 1092 | b = newNodeP(nkElse, p) 1093 | getTok(p) 1094 | else: 1095 | b = newNodeP(nkOfBranch, p) 1096 | while (p.tok.xkind != pxEof) and (p.tok.xkind != pxColon): 1097 | addSon(b, rangeExpr(p)) 1098 | opt(p, pxComma) 1099 | skipCom(p, b) 1100 | eat(p, pxColon) 1101 | skipCom(p, b) 1102 | c = newNodeP(nkRecList, p) 1103 | eat(p, pxParLe) 1104 | while (p.tok.xkind != pxParRi) and (p.tok.xkind != pxEof): 1105 | addSon(c, parseIdentColonEquals(p, rawIdent)) 1106 | opt(p, pxSemicolon) 1107 | skipCom(p, lastSon(c)) 1108 | eat(p, pxParRi) 1109 | opt(p, pxSemicolon) 1110 | if sonsLen(c) > 0: skipCom(p, lastSon(c)) 1111 | else: addSon(c, newNodeP(nkNilLit, p)) 1112 | addSon(b, c) 1113 | addSon(result, b) 1114 | if b.kind == nkElse: break 1115 | 1116 | proc parseRecordPart(p: var TParser): PNode = 1117 | result = ast.emptyNode 1118 | while (p.tok.xkind != pxEof) and (p.tok.xkind != pxEnd): 1119 | if result.kind == nkEmpty: result = newNodeP(nkRecList, p) 1120 | case p.tok.xkind 1121 | of pxSymbol: 1122 | addSon(result, parseIdentColonEquals(p, rawIdent)) 1123 | opt(p, pxSemicolon) 1124 | skipCom(p, lastSon(result)) 1125 | of pxCase: 1126 | addSon(result, parseRecordCase(p)) 1127 | of pxComment: 1128 | skipCom(p, lastSon(result)) 1129 | else: 1130 | parMessage(p, errIdentifierExpected, $p.tok) 1131 | break 1132 | 1133 | proc exSymbol(n: var PNode) = 1134 | case n.kind 1135 | of nkPostfix: 1136 | discard 1137 | of nkPragmaExpr: 1138 | exSymbol(n.sons[0]) 1139 | of nkIdent, nkAccQuoted: 1140 | var a = newNodeI(nkPostFix, n.info) 1141 | addSon(a, newIdentNode(getIdent("*"), n.info)) 1142 | addSon(a, n) 1143 | n = a 1144 | else: internalError(n.info, "exSymbol(): " & $n.kind) 1145 | 1146 | proc fixRecordDef(n: var PNode) = 1147 | case n.kind 1148 | of nkRecCase: 1149 | fixRecordDef(n.sons[0]) 1150 | for i in countup(1, sonsLen(n) - 1): 1151 | var length = sonsLen(n.sons[i]) 1152 | fixRecordDef(n.sons[i].sons[length - 1]) 1153 | of nkRecList, nkRecWhen, nkElse, nkOfBranch, nkElifBranch, nkObjectTy: 1154 | for i in countup(0, sonsLen(n) - 1): fixRecordDef(n.sons[i]) 1155 | of nkIdentDefs: 1156 | for i in countup(0, sonsLen(n) - 3): exSymbol(n.sons[i]) 1157 | of nkNilLit, nkEmpty: discard 1158 | else: internalError(n.info, "fixRecordDef(): " & $n.kind) 1159 | 1160 | proc addPragmaToIdent(ident: var PNode, pragma: PNode) = 1161 | var pragmasNode: PNode 1162 | if ident.kind != nkPragmaExpr: 1163 | pragmasNode = newNodeI(nkPragma, ident.info) 1164 | var e = newNodeI(nkPragmaExpr, ident.info) 1165 | addSon(e, ident) 1166 | addSon(e, pragmasNode) 1167 | ident = e 1168 | else: 1169 | pragmasNode = ident.sons[1] 1170 | if pragmasNode.kind != nkPragma: 1171 | internalError(ident.info, "addPragmaToIdent") 1172 | addSon(pragmasNode, pragma) 1173 | 1174 | proc parseRecordBody(p: var TParser, result, definition: PNode) = 1175 | skipCom(p, result) 1176 | var a = parseRecordPart(p) 1177 | if result.kind != nkTupleTy: fixRecordDef(a) 1178 | addSon(result, a) 1179 | eat(p, pxEnd) 1180 | case p.tok.xkind 1181 | of pxSymbol: 1182 | if p.tok.ident.id == getIdent("acyclic").id: 1183 | if definition != nil: 1184 | addPragmaToIdent(definition.sons[0], newIdentNodeP(p.tok.ident, p)) 1185 | else: 1186 | internalError(result.info, "anonymous record is not supported") 1187 | getTok(p) 1188 | else: 1189 | internalError(result.info, "parseRecordBody") 1190 | of pxCommand: 1191 | if definition != nil: addPragmaToIdent(definition.sons[0], parseCommand(p)) 1192 | else: internalError(result.info, "anonymous record is not supported") 1193 | else: 1194 | discard 1195 | opt(p, pxSemicolon) 1196 | skipCom(p, result) 1197 | 1198 | proc parseRecordOrObject(p: var TParser, kind: TNodeKind, 1199 | definition: PNode): PNode = 1200 | result = newNodeP(kind, p) 1201 | getTok(p) 1202 | addSon(result, ast.emptyNode) 1203 | if p.tok.xkind == pxParLe: 1204 | var a = newNodeP(nkOfInherit, p) 1205 | getTok(p) 1206 | addSon(a, parseTypeDesc(p)) 1207 | addSon(result, a) 1208 | eat(p, pxParRi) 1209 | else: 1210 | addSon(result, ast.emptyNode) 1211 | parseRecordBody(p, result, definition) 1212 | 1213 | proc parseTypeDesc(p: var TParser, definition: PNode = nil): PNode = 1214 | var oldcontext = p.context 1215 | p.context = conTypeDesc 1216 | if p.tok.xkind == pxPacked: getTok(p) 1217 | case p.tok.xkind 1218 | of pxCommand: 1219 | result = parseCommand(p, definition) 1220 | of pxProcedure, pxFunction: 1221 | result = parseRoutineType(p) 1222 | of pxRecord: 1223 | getTok(p) 1224 | if p.tok.xkind == pxCommand: 1225 | result = parseCommand(p) 1226 | if result.kind != nkTupleTy: internalError(result.info, "parseTypeDesc") 1227 | parseRecordBody(p, result, definition) 1228 | var a = lastSon(result) # embed nkRecList directly into nkTupleTy 1229 | for i in countup(0, sonsLen(a) - 1): 1230 | if i == 0: result.sons[sonsLen(result) - 1] = a.sons[0] 1231 | else: addSon(result, a.sons[i]) 1232 | else: 1233 | result = newNodeP(nkObjectTy, p) 1234 | addSon(result, ast.emptyNode) 1235 | addSon(result, ast.emptyNode) 1236 | parseRecordBody(p, result, definition) 1237 | if definition != nil: 1238 | addPragmaToIdent(definition.sons[0], newIdentNodeP(getIdent("final"), p)) 1239 | else: 1240 | internalError(result.info, "anonymous record is not supported") 1241 | of pxObject: result = parseRecordOrObject(p, nkObjectTy, definition) 1242 | of pxParLe: result = parseEnum(p) 1243 | of pxArray: 1244 | result = newNodeP(nkBracketExpr, p) 1245 | getTok(p) 1246 | if p.tok.xkind == pxBracketLe: 1247 | addSon(result, newIdentNodeP(getIdent("array"), p)) 1248 | getTok(p) 1249 | addSon(result, rangeExpr(p)) 1250 | eat(p, pxBracketRi) 1251 | else: 1252 | if p.inParamList: addSon(result, newIdentNodeP(getIdent("openarray"), p)) 1253 | else: addSon(result, newIdentNodeP(getIdent("seq"), p)) 1254 | eat(p, pxOf) 1255 | addSon(result, parseTypeDesc(p)) 1256 | of pxSet: 1257 | result = newNodeP(nkBracketExpr, p) 1258 | getTok(p) 1259 | eat(p, pxOf) 1260 | addSon(result, newIdentNodeP(getIdent("set"), p)) 1261 | addSon(result, parseTypeDesc(p)) 1262 | of pxHat: 1263 | getTok(p) 1264 | if p.tok.xkind == pxCommand: result = parseCommand(p) 1265 | elif pfRefs in p.flags: result = newNodeP(nkRefTy, p) 1266 | else: result = newNodeP(nkPtrTy, p) 1267 | addSon(result, parseTypeDesc(p)) 1268 | of pxType: 1269 | getTok(p) 1270 | result = parseTypeDesc(p) 1271 | else: 1272 | var a = primary(p) 1273 | if p.tok.xkind == pxDotDot: 1274 | result = newNodeP(nkBracketExpr, p) 1275 | var r = newNodeP(nkRange, p) 1276 | addSon(result, newIdentNodeP(getIdent("range"), p)) 1277 | getTok(p) 1278 | addSon(r, a) 1279 | addSon(r, parseExpr(p)) 1280 | addSon(result, r) 1281 | else: 1282 | result = a 1283 | p.context = oldcontext 1284 | 1285 | proc parseTypeDef(p: var TParser): PNode = 1286 | result = newNodeP(nkTypeDef, p) 1287 | addSon(result, identVis(p)) 1288 | addSon(result, ast.emptyNode) # generic params 1289 | if p.tok.xkind == pxEquals: 1290 | getTok(p) 1291 | skipCom(p, result) 1292 | addSon(result, parseTypeDesc(p, result)) 1293 | else: 1294 | addSon(result, ast.emptyNode) 1295 | if p.tok.xkind == pxSemicolon: 1296 | getTok(p) 1297 | skipCom(p, result) 1298 | 1299 | proc parseTypeSection(p: var TParser): PNode = 1300 | result = newNodeP(nkTypeSection, p) 1301 | getTok(p) 1302 | skipCom(p, result) 1303 | while p.tok.xkind == pxSymbol: 1304 | addSon(result, parseTypeDef(p)) 1305 | 1306 | proc parseConstant(p: var TParser): PNode = 1307 | result = newNodeP(nkConstDef, p) 1308 | addSon(result, identVis(p)) 1309 | if p.tok.xkind == pxColon: 1310 | getTok(p) 1311 | skipCom(p, result) 1312 | addSon(result, parseTypeDesc(p)) 1313 | else: 1314 | addSon(result, ast.emptyNode) 1315 | if p.tok.xkind != pxEquals: 1316 | parMessage(p, errColonOrEqualsExpected, $(p.tok)) 1317 | if p.tok.xkind == pxEquals: 1318 | getTok(p) 1319 | skipCom(p, result) 1320 | addSon(result, parseExpr(p)) 1321 | else: 1322 | addSon(result, ast.emptyNode) 1323 | if p.tok.xkind == pxSemicolon: 1324 | getTok(p) 1325 | skipCom(p, result) 1326 | 1327 | proc parseConstSection(p: var TParser): PNode = 1328 | result = newNodeP(nkConstSection, p) 1329 | getTok(p) 1330 | skipCom(p, result) 1331 | while p.tok.xkind == pxSymbol: 1332 | addSon(result, parseConstant(p)) 1333 | 1334 | proc parseVar(p: var TParser): PNode = 1335 | result = newNodeP(nkVarSection, p) 1336 | getTok(p) 1337 | skipCom(p, result) 1338 | while p.tok.xkind == pxSymbol: 1339 | addSon(result, parseIdentColonEquals(p, identVis)) 1340 | p.lastVarSection = result 1341 | 1342 | proc parseRoutine(p: var TParser): PNode = 1343 | var noBody: bool 1344 | result = newNodeP(nkProcDef, p) 1345 | getTok(p) 1346 | skipCom(p, result) 1347 | expectIdent(p) 1348 | addSon(result, identVis(p)) 1349 | # patterns, generic parameters: 1350 | addSon(result, ast.emptyNode) 1351 | addSon(result, ast.emptyNode) 1352 | addSon(result, parseParamList(p)) 1353 | opt(p, pxSemicolon) 1354 | addSon(result, parseRoutineSpecifiers(p, noBody)) 1355 | addSon(result, ast.emptyNode) 1356 | if (p.section == seInterface) or noBody: 1357 | addSon(result, ast.emptyNode) 1358 | else: 1359 | var stmts = newNodeP(nkStmtList, p) 1360 | while true: 1361 | case p.tok.xkind 1362 | of pxVar: addSon(stmts, parseVar(p)) 1363 | of pxConst: addSon(stmts, parseConstSection(p)) 1364 | of pxType: addSon(stmts, parseTypeSection(p)) 1365 | of pxComment: skipCom(p, result) 1366 | of pxBegin: break 1367 | else: 1368 | parMessage(p, errTokenExpected, "begin") 1369 | break 1370 | var a = parseStmt(p) 1371 | for i in countup(0, sonsLen(a) - 1): addSon(stmts, a.sons[i]) 1372 | addSon(result, stmts) 1373 | 1374 | proc fixExit(p: var TParser, n: PNode): bool = 1375 | if (p.tok.ident.id == getIdent("exit").id): 1376 | var length = sonsLen(n) 1377 | if (length <= 0): return 1378 | var a = n.sons[length-1] 1379 | if (a.kind == nkAsgn) and (a.sons[0].kind == nkIdent) and 1380 | (a.sons[0].ident.id == getIdent("result").id): 1381 | delSon(a, 0) 1382 | a.kind = nkReturnStmt 1383 | result = true 1384 | getTok(p) 1385 | opt(p, pxSemicolon) 1386 | skipCom(p, a) 1387 | 1388 | proc fixVarSection(p: var TParser, counter: PNode) = 1389 | if p.lastVarSection == nil: return 1390 | assert(counter.kind == nkIdent) 1391 | for i in countup(0, sonsLen(p.lastVarSection) - 1): 1392 | var v = p.lastVarSection.sons[i] 1393 | for j in countup(0, sonsLen(v) - 3): 1394 | if v.sons[j].ident.id == counter.ident.id: 1395 | delSon(v, j) 1396 | if sonsLen(v) <= 2: 1397 | delSon(p.lastVarSection, i) 1398 | return 1399 | 1400 | proc exSymbols(n: PNode) = 1401 | case n.kind 1402 | of nkEmpty..nkNilLit: discard 1403 | of nkProcDef..nkIteratorDef: exSymbol(n.sons[namePos]) 1404 | of nkWhenStmt, nkStmtList: 1405 | for i in countup(0, sonsLen(n) - 1): exSymbols(n.sons[i]) 1406 | of nkVarSection, nkConstSection: 1407 | for i in countup(0, sonsLen(n) - 1): exSymbol(n.sons[i].sons[0]) 1408 | of nkTypeSection: 1409 | for i in countup(0, sonsLen(n) - 1): 1410 | exSymbol(n.sons[i].sons[0]) 1411 | if n.sons[i].sons[2].kind == nkObjectTy: 1412 | fixRecordDef(n.sons[i].sons[2]) 1413 | else: discard 1414 | 1415 | proc parseBegin(p: var TParser, result: PNode) = 1416 | getTok(p) 1417 | while true: 1418 | case p.tok.xkind 1419 | of pxComment: addSon(result, parseStmt(p)) 1420 | of pxSymbol: 1421 | if not fixExit(p, result): addSon(result, parseStmt(p)) 1422 | of pxEnd: 1423 | getTok(p) 1424 | break 1425 | of pxSemicolon: getTok(p) 1426 | of pxEof: parMessage(p, errExprExpected) 1427 | else: 1428 | var a = parseStmt(p) 1429 | if a.kind != nkEmpty: addSon(result, a) 1430 | if sonsLen(result) == 0: addSon(result, newNodeP(nkNilLit, p)) 1431 | 1432 | proc parseStmt(p: var TParser): PNode = 1433 | var oldcontext = p.context 1434 | p.context = conStmt 1435 | result = ast.emptyNode 1436 | case p.tok.xkind 1437 | of pxBegin: 1438 | result = newNodeP(nkStmtList, p) 1439 | parseBegin(p, result) 1440 | of pxCommand: result = parseCommand(p) 1441 | of pxCurlyDirLe, pxStarDirLe: 1442 | if isHandledDirective(p): result = parseDirective(p) 1443 | of pxIf: result = parseIf(p) 1444 | of pxWhile: result = parseWhile(p) 1445 | of pxRepeat: result = parseRepeat(p) 1446 | of pxCase: result = parseCase(p) 1447 | of pxTry: result = parseTry(p) 1448 | of pxProcedure, pxFunction: result = parseRoutine(p) 1449 | of pxType: result = parseTypeSection(p) 1450 | of pxConst: result = parseConstSection(p) 1451 | of pxVar: result = parseVar(p) 1452 | of pxFor: 1453 | result = parseFor(p) 1454 | fixVarSection(p, result.sons[0]) 1455 | of pxRaise: result = parseRaise(p) 1456 | of pxUses: result = parseUsesStmt(p) 1457 | of pxProgram, pxUnit, pxLibrary: 1458 | # skip the pointless header 1459 | while not (p.tok.xkind in {pxSemicolon, pxEof}): getTok(p) 1460 | getTok(p) 1461 | of pxInitialization: getTok(p) # just skip the token 1462 | of pxImplementation: 1463 | p.section = seImplementation 1464 | result = newNodeP(nkCommentStmt, p) 1465 | result.comment = "# implementation" 1466 | getTok(p) 1467 | of pxInterface: 1468 | p.section = seInterface 1469 | getTok(p) 1470 | of pxComment: 1471 | result = newNodeP(nkCommentStmt, p) 1472 | skipCom(p, result) 1473 | of pxSemicolon: getTok(p) 1474 | of pxSymbol: 1475 | if p.tok.ident.id == getIdent("break").id: 1476 | result = newNodeP(nkBreakStmt, p) 1477 | getTok(p) 1478 | skipCom(p, result) 1479 | addSon(result, ast.emptyNode) 1480 | elif p.tok.ident.id == getIdent("continue").id: 1481 | result = newNodeP(nkContinueStmt, p) 1482 | getTok(p) 1483 | skipCom(p, result) 1484 | addSon(result, ast.emptyNode) 1485 | elif p.tok.ident.id == getIdent("exit").id: 1486 | result = newNodeP(nkReturnStmt, p) 1487 | getTok(p) 1488 | skipCom(p, result) 1489 | addSon(result, ast.emptyNode) 1490 | else: 1491 | result = parseExprStmt(p) 1492 | of pxDot: getTok(p) # BUGFIX for ``end.`` in main program 1493 | else: result = parseExprStmt(p) 1494 | opt(p, pxSemicolon) 1495 | if result.kind != nkEmpty: skipCom(p, result) 1496 | p.context = oldcontext 1497 | 1498 | proc parseUnit(p: var TParser): PNode = 1499 | result = newNodeP(nkStmtList, p) 1500 | getTok(p) # read first token 1501 | while true: 1502 | case p.tok.xkind 1503 | of pxEof, pxEnd: break 1504 | of pxBegin: parseBegin(p, result) 1505 | of pxCurlyDirLe, pxStarDirLe: 1506 | if isHandledDirective(p): addSon(result, parseDirective(p)) 1507 | else: parMessage(p, errXNotAllowedHere, p.tok.ident.s) 1508 | else: addSon(result, parseStmt(p)) 1509 | opt(p, pxEnd) 1510 | opt(p, pxDot) 1511 | if p.tok.xkind != pxEof: 1512 | addSon(result, parseStmt(p)) # comments after final 'end.' 1513 | 1514 | --------------------------------------------------------------------------------