1# 2# 3# Nim's Runtime Library 4# (c) Copyright 2009 Andreas Rumpf 5# 6# See the file "copying.txt", included in this 7# distribution, for details about the copyright. 8# 9 10## The `parsesql` module implements a high performance SQL file 11## parser. It parses PostgreSQL syntax and the SQL ANSI standard. 12## 13## Unstable API. 14 15import strutils, lexbase 16import std/private/decode_helpers 17 18# ------------------- scanner ------------------------------------------------- 19 20type 21 TokKind = enum ## enumeration of all SQL tokens 22 tkInvalid, ## invalid token 23 tkEof, ## end of file reached 24 tkIdentifier, ## abc 25 tkQuotedIdentifier, ## "abc" 26 tkStringConstant, ## 'abc' 27 tkEscapeConstant, ## e'abc' 28 tkDollarQuotedConstant, ## $tag$abc$tag$ 29 tkBitStringConstant, ## B'00011' 30 tkHexStringConstant, ## x'00011' 31 tkInteger, 32 tkNumeric, 33 tkOperator, ## + - * / < > = ~ ! @ # % ^ & | ` ? 34 tkSemicolon, ## ';' 35 tkColon, ## ':' 36 tkComma, ## ',' 37 tkParLe, ## '(' 38 tkParRi, ## ')' 39 tkBracketLe, ## '[' 40 tkBracketRi, ## ']' 41 tkDot ## '.' 42 43 Token = object # a token 44 kind: TokKind # the type of the token 45 literal: string # the parsed (string) literal 46 47 SqlLexer* = object of BaseLexer ## the parser object. 48 filename: string 49 50const 51 tokKindToStr: array[TokKind, string] = [ 52 "invalid", "[EOF]", "identifier", "quoted identifier", "string constant", 53 "escape string constant", "dollar quoted constant", "bit string constant", 54 "hex string constant", "integer constant", "numeric constant", "operator", 55 ";", ":", ",", "(", ")", "[", "]", "." 56 ] 57 58 reservedKeywords = @[ 59 # statements 60 "select", "from", "where", "group", "limit", "having", 61 # functions 62 "count", 63 ] 64 65proc close(L: var SqlLexer) = 66 lexbase.close(L) 67 68proc getColumn(L: SqlLexer): int = 69 ## get the current column the parser has arrived at. 70 result = getColNumber(L, L.bufpos) 71 72proc getLine(L: SqlLexer): int = 73 result = L.lineNumber 74 75proc handleOctChar(c: var SqlLexer, xi: var int) = 76 if c.buf[c.bufpos] in {'0'..'7'}: 77 xi = (xi shl 3) or (ord(c.buf[c.bufpos]) - ord('0')) 78 inc(c.bufpos) 79 80proc getEscapedChar(c: var SqlLexer, tok: var Token) = 81 inc(c.bufpos) 82 case c.buf[c.bufpos] 83 of 'n', 'N': 84 add(tok.literal, '\L') 85 inc(c.bufpos) 86 of 'r', 'R', 'c', 'C': 87 add(tok.literal, '\c') 88 inc(c.bufpos) 89 of 'l', 'L': 90 add(tok.literal, '\L') 91 inc(c.bufpos) 92 of 'f', 'F': 93 add(tok.literal, '\f') 94 inc(c.bufpos) 95 of 'e', 'E': 96 add(tok.literal, '\e') 97 inc(c.bufpos) 98 of 'a', 'A': 99 add(tok.literal, '\a') 100 inc(c.bufpos) 101 of 'b', 'B': 102 add(tok.literal, '\b') 103 inc(c.bufpos) 104 of 'v', 'V': 105 add(tok.literal, '\v') 106 inc(c.bufpos) 107 of 't', 'T': 108 add(tok.literal, '\t') 109 inc(c.bufpos) 110 of '\'', '\"': 111 add(tok.literal, c.buf[c.bufpos]) 112 inc(c.bufpos) 113 of '\\': 114 add(tok.literal, '\\') 115 inc(c.bufpos) 116 of 'x', 'X': 117 inc(c.bufpos) 118 var xi = 0 119 if handleHexChar(c.buf[c.bufpos], xi): 120 inc(c.bufpos) 121 if handleHexChar(c.buf[c.bufpos], xi): 122 inc(c.bufpos) 123 add(tok.literal, chr(xi)) 124 of '0'..'7': 125 var xi = 0 126 handleOctChar(c, xi) 127 handleOctChar(c, xi) 128 handleOctChar(c, xi) 129 if (xi <= 255): add(tok.literal, chr(xi)) 130 else: tok.kind = tkInvalid 131 else: tok.kind = tkInvalid 132 133proc handleCRLF(c: var SqlLexer, pos: int): int = 134 case c.buf[pos] 135 of '\c': result = lexbase.handleCR(c, pos) 136 of '\L': result = lexbase.handleLF(c, pos) 137 else: result = pos 138 139proc skip(c: var SqlLexer) = 140 var pos = c.bufpos 141 var nested = 0 142 while true: 143 case c.buf[pos] 144 of ' ', '\t': 145 inc(pos) 146 of '-': 147 if c.buf[pos+1] == '-': 148 while not (c.buf[pos] in {'\c', '\L', lexbase.EndOfFile}): inc(pos) 149 else: 150 break 151 of '/': 152 if c.buf[pos+1] == '*': 153 inc(pos, 2) 154 while true: 155 case c.buf[pos] 156 of '\0': break 157 of '\c', '\L': 158 pos = handleCRLF(c, pos) 159 of '*': 160 if c.buf[pos+1] == '/': 161 inc(pos, 2) 162 if nested <= 0: break 163 dec(nested) 164 else: 165 inc(pos) 166 of '/': 167 if c.buf[pos+1] == '*': 168 inc(pos, 2) 169 inc(nested) 170 else: 171 inc(pos) 172 else: inc(pos) 173 else: break 174 of '\c', '\L': 175 pos = handleCRLF(c, pos) 176 else: 177 break # EndOfFile also leaves the loop 178 c.bufpos = pos 179 180proc getString(c: var SqlLexer, tok: var Token, kind: TokKind) = 181 var pos = c.bufpos + 1 182 tok.kind = kind 183 block parseLoop: 184 while true: 185 while true: 186 var ch = c.buf[pos] 187 if ch == '\'': 188 if c.buf[pos+1] == '\'': 189 inc(pos, 2) 190 add(tok.literal, '\'') 191 else: 192 inc(pos) 193 break 194 elif ch in {'\c', '\L', lexbase.EndOfFile}: 195 tok.kind = tkInvalid 196 break parseLoop 197 elif (ch == '\\') and kind == tkEscapeConstant: 198 c.bufpos = pos 199 getEscapedChar(c, tok) 200 pos = c.bufpos 201 else: 202 add(tok.literal, ch) 203 inc(pos) 204 c.bufpos = pos 205 var line = c.lineNumber 206 skip(c) 207 if c.lineNumber > line: 208 # a new line whitespace has been parsed, so we check if the string 209 # continues after the whitespace: 210 pos = c.bufpos 211 if c.buf[pos] == '\'': inc(pos) 212 else: break parseLoop 213 else: break parseLoop 214 c.bufpos = pos 215 216proc getDollarString(c: var SqlLexer, tok: var Token) = 217 var pos = c.bufpos + 1 218 tok.kind = tkDollarQuotedConstant 219 var tag = "$" 220 while c.buf[pos] in IdentChars: 221 add(tag, c.buf[pos]) 222 inc(pos) 223 if c.buf[pos] == '$': inc(pos) 224 else: 225 tok.kind = tkInvalid 226 return 227 while true: 228 case c.buf[pos] 229 of '\c', '\L': 230 pos = handleCRLF(c, pos) 231 add(tok.literal, "\L") 232 of '\0': 233 tok.kind = tkInvalid 234 break 235 of '$': 236 inc(pos) 237 var tag2 = "$" 238 while c.buf[pos] in IdentChars: 239 add(tag2, c.buf[pos]) 240 inc(pos) 241 if c.buf[pos] == '$': inc(pos) 242 if tag2 == tag: break 243 add(tok.literal, tag2) 244 add(tok.literal, '$') 245 else: 246 add(tok.literal, c.buf[pos]) 247 inc(pos) 248 c.bufpos = pos 249 250proc getSymbol(c: var SqlLexer, tok: var Token) = 251 var pos = c.bufpos 252 while true: 253 add(tok.literal, c.buf[pos]) 254 inc(pos) 255 if c.buf[pos] notin {'a'..'z', 'A'..'Z', '0'..'9', '_', '$', 256 '\128'..'\255'}: 257 break 258 c.bufpos = pos 259 tok.kind = tkIdentifier 260 261proc getQuotedIdentifier(c: var SqlLexer, tok: var Token, quote = '\"') = 262 var pos = c.bufpos + 1 263 tok.kind = tkQuotedIdentifier 264 while true: 265 var ch = c.buf[pos] 266 if ch == quote: 267 if c.buf[pos+1] == quote: 268 inc(pos, 2) 269 add(tok.literal, quote) 270 else: 271 inc(pos) 272 break 273 elif ch in {'\c', '\L', lexbase.EndOfFile}: 274 tok.kind = tkInvalid 275 break 276 else: 277 add(tok.literal, ch) 278 inc(pos) 279 c.bufpos = pos 280 281proc getBitHexString(c: var SqlLexer, tok: var Token, validChars: set[char]) = 282 var pos = c.bufpos + 1 283 block parseLoop: 284 while true: 285 while true: 286 var ch = c.buf[pos] 287 if ch in validChars: 288 add(tok.literal, ch) 289 inc(pos) 290 elif ch == '\'': 291 inc(pos) 292 break 293 else: 294 tok.kind = tkInvalid 295 break parseLoop 296 c.bufpos = pos 297 var line = c.lineNumber 298 skip(c) 299 if c.lineNumber > line: 300 # a new line whitespace has been parsed, so we check if the string 301 # continues after the whitespace: 302 pos = c.bufpos 303 if c.buf[pos] == '\'': inc(pos) 304 else: break parseLoop 305 else: break parseLoop 306 c.bufpos = pos 307 308proc getNumeric(c: var SqlLexer, tok: var Token) = 309 tok.kind = tkInteger 310 var pos = c.bufpos 311 while c.buf[pos] in Digits: 312 add(tok.literal, c.buf[pos]) 313 inc(pos) 314 if c.buf[pos] == '.': 315 tok.kind = tkNumeric 316 add(tok.literal, c.buf[pos]) 317 inc(pos) 318 while c.buf[pos] in Digits: 319 add(tok.literal, c.buf[pos]) 320 inc(pos) 321 if c.buf[pos] in {'E', 'e'}: 322 tok.kind = tkNumeric 323 add(tok.literal, c.buf[pos]) 324 inc(pos) 325 if c.buf[pos] == '+': 326 inc(pos) 327 elif c.buf[pos] == '-': 328 add(tok.literal, c.buf[pos]) 329 inc(pos) 330 if c.buf[pos] in Digits: 331 while c.buf[pos] in Digits: 332 add(tok.literal, c.buf[pos]) 333 inc(pos) 334 else: 335 tok.kind = tkInvalid 336 c.bufpos = pos 337 338proc getOperator(c: var SqlLexer, tok: var Token) = 339 const operators = {'+', '-', '*', '/', '<', '>', '=', '~', '!', '@', '#', '%', 340 '^', '&', '|', '`', '?'} 341 tok.kind = tkOperator 342 var pos = c.bufpos 343 var trailingPlusMinus = false 344 while true: 345 case c.buf[pos] 346 of '-': 347 if c.buf[pos] == '-': break 348 if not trailingPlusMinus and c.buf[pos+1] notin operators and 349 tok.literal.len > 0: break 350 of '/': 351 if c.buf[pos] == '*': break 352 of '~', '!', '@', '#', '%', '^', '&', '|', '`', '?': 353 trailingPlusMinus = true 354 of '+': 355 if not trailingPlusMinus and c.buf[pos+1] notin operators and 356 tok.literal.len > 0: break 357 of '*', '<', '>', '=': discard 358 else: break 359 add(tok.literal, c.buf[pos]) 360 inc(pos) 361 c.bufpos = pos 362 363proc getTok(c: var SqlLexer, tok: var Token) = 364 tok.kind = tkInvalid 365 setLen(tok.literal, 0) 366 skip(c) 367 case c.buf[c.bufpos] 368 of ';': 369 tok.kind = tkSemicolon 370 inc(c.bufpos) 371 add(tok.literal, ';') 372 of ',': 373 tok.kind = tkComma 374 inc(c.bufpos) 375 add(tok.literal, ',') 376 of ':': 377 tok.kind = tkColon 378 inc(c.bufpos) 379 add(tok.literal, ':') 380 of 'e', 'E': 381 if c.buf[c.bufpos + 1] == '\'': 382 inc(c.bufpos) 383 getString(c, tok, tkEscapeConstant) 384 else: 385 getSymbol(c, tok) 386 of 'b', 'B': 387 if c.buf[c.bufpos + 1] == '\'': 388 tok.kind = tkBitStringConstant 389 getBitHexString(c, tok, {'0'..'1'}) 390 else: 391 getSymbol(c, tok) 392 of 'x', 'X': 393 if c.buf[c.bufpos + 1] == '\'': 394 tok.kind = tkHexStringConstant 395 getBitHexString(c, tok, {'a'..'f', 'A'..'F', '0'..'9'}) 396 else: 397 getSymbol(c, tok) 398 of '$': getDollarString(c, tok) 399 of '[': 400 tok.kind = tkBracketLe 401 inc(c.bufpos) 402 add(tok.literal, '[') 403 of ']': 404 tok.kind = tkBracketRi 405 inc(c.bufpos) 406 add(tok.literal, ']') 407 of '(': 408 tok.kind = tkParLe 409 inc(c.bufpos) 410 add(tok.literal, '(') 411 of ')': 412 tok.kind = tkParRi 413 inc(c.bufpos) 414 add(tok.literal, ')') 415 of '.': 416 if c.buf[c.bufpos + 1] in Digits: 417 getNumeric(c, tok) 418 else: 419 tok.kind = tkDot 420 inc(c.bufpos) 421 add(tok.literal, '.') 422 of '0'..'9': getNumeric(c, tok) 423 of '\'': getString(c, tok, tkStringConstant) 424 of '"': getQuotedIdentifier(c, tok, '"') 425 of '`': getQuotedIdentifier(c, tok, '`') 426 of lexbase.EndOfFile: 427 tok.kind = tkEof 428 tok.literal = "[EOF]" 429 of 'a', 'c', 'd', 'f'..'w', 'y', 'z', 'A', 'C', 'D', 'F'..'W', 'Y', 'Z', '_', 430 '\128'..'\255': 431 getSymbol(c, tok) 432 of '+', '-', '*', '/', '<', '>', '=', '~', '!', '@', '#', '%', 433 '^', '&', '|', '?': 434 getOperator(c, tok) 435 else: 436 add(tok.literal, c.buf[c.bufpos]) 437 inc(c.bufpos) 438 439proc errorStr(L: SqlLexer, msg: string): string = 440 result = "$1($2, $3) Error: $4" % [L.filename, $getLine(L), $getColumn(L), msg] 441 442 443# ----------------------------- parser ---------------------------------------- 444 445# Operator/Element Associativity Description 446# . left table/column name separator 447# :: left PostgreSQL-style typecast 448# [ ] left array element selection 449# - right unary minus 450# ^ left exponentiation 451# * / % left multiplication, division, modulo 452# + - left addition, subtraction 453# IS IS TRUE, IS FALSE, IS UNKNOWN, IS NULL 454# ISNULL test for null 455# NOTNULL test for not null 456# (any other) left all other native and user-defined oprs 457# IN set membership 458# BETWEEN range containment 459# OVERLAPS time interval overlap 460# LIKE ILIKE SIMILAR string pattern matching 461# < > less than, greater than 462# = right equality, assignment 463# NOT right logical negation 464# AND left logical conjunction 465# OR left logical disjunction 466 467type 468 SqlNodeKind* = enum ## kind of SQL abstract syntax tree 469 nkNone, 470 nkIdent, 471 nkQuotedIdent, 472 nkStringLit, 473 nkBitStringLit, 474 nkHexStringLit, 475 nkIntegerLit, 476 nkNumericLit, 477 nkPrimaryKey, 478 nkForeignKey, 479 nkNotNull, 480 nkNull, 481 482 nkStmtList, 483 nkDot, 484 nkDotDot, 485 nkPrefix, 486 nkInfix, 487 nkCall, 488 nkPrGroup, 489 nkColumnReference, 490 nkReferences, 491 nkDefault, 492 nkCheck, 493 nkConstraint, 494 nkUnique, 495 nkIdentity, 496 nkColumnDef, ## name, datatype, constraints 497 nkInsert, 498 nkUpdate, 499 nkDelete, 500 nkSelect, 501 nkSelectDistinct, 502 nkSelectColumns, 503 nkSelectPair, 504 nkAsgn, 505 nkFrom, 506 nkFromItemPair, 507 nkGroup, 508 nkLimit, 509 nkHaving, 510 nkOrder, 511 nkJoin, 512 nkDesc, 513 nkUnion, 514 nkIntersect, 515 nkExcept, 516 nkColumnList, 517 nkValueList, 518 nkWhere, 519 nkCreateTable, 520 nkCreateTableIfNotExists, 521 nkCreateType, 522 nkCreateTypeIfNotExists, 523 nkCreateIndex, 524 nkCreateIndexIfNotExists, 525 nkEnumDef 526 527const 528 LiteralNodes = { 529 nkIdent, nkQuotedIdent, nkStringLit, nkBitStringLit, nkHexStringLit, 530 nkIntegerLit, nkNumericLit 531 } 532 533type 534 SqlParseError* = object of ValueError ## Invalid SQL encountered 535 SqlNode* = ref SqlNodeObj ## an SQL abstract syntax tree node 536 SqlNodeObj* = object ## an SQL abstract syntax tree node 537 case kind*: SqlNodeKind ## kind of syntax tree 538 of LiteralNodes: 539 strVal*: string ## AST leaf: the identifier, numeric literal 540 ## string literal, etc. 541 else: 542 sons*: seq[SqlNode] ## the node's children 543 544 SqlParser* = object of SqlLexer ## SQL parser object 545 tok: Token 546 547proc newNode*(k: SqlNodeKind): SqlNode = 548 when defined(js): # bug #14117 549 case k 550 of LiteralNodes: 551 result = SqlNode(kind: k, strVal: "") 552 else: 553 result = SqlNode(kind: k, sons: @[]) 554 else: 555 result = SqlNode(kind: k) 556 557proc newNode*(k: SqlNodeKind, s: string): SqlNode = 558 result = SqlNode(kind: k) 559 result.strVal = s 560 561proc newNode*(k: SqlNodeKind, sons: seq[SqlNode]): SqlNode = 562 result = SqlNode(kind: k) 563 result.sons = sons 564 565proc len*(n: SqlNode): int = 566 if n.kind in LiteralNodes: 567 result = 0 568 else: 569 result = n.sons.len 570 571proc `[]`*(n: SqlNode; i: int): SqlNode = n.sons[i] 572proc `[]`*(n: SqlNode; i: BackwardsIndex): SqlNode = n.sons[n.len - int(i)] 573 574proc add*(father, n: SqlNode) = 575 add(father.sons, n) 576 577proc getTok(p: var SqlParser) = 578 getTok(p, p.tok) 579 580proc sqlError(p: SqlParser, msg: string) = 581 var e: ref SqlParseError 582 new(e) 583 e.msg = errorStr(p, msg) 584 raise e 585 586proc isKeyw(p: SqlParser, keyw: string): bool = 587 result = p.tok.kind == tkIdentifier and 588 cmpIgnoreCase(p.tok.literal, keyw) == 0 589 590proc isOpr(p: SqlParser, opr: string): bool = 591 result = p.tok.kind == tkOperator and 592 cmpIgnoreCase(p.tok.literal, opr) == 0 593 594proc optKeyw(p: var SqlParser, keyw: string) = 595 if p.tok.kind == tkIdentifier and cmpIgnoreCase(p.tok.literal, keyw) == 0: 596 getTok(p) 597 598proc expectIdent(p: SqlParser) = 599 if p.tok.kind != tkIdentifier and p.tok.kind != tkQuotedIdentifier: 600 sqlError(p, "identifier expected") 601 602proc expect(p: SqlParser, kind: TokKind) = 603 if p.tok.kind != kind: 604 sqlError(p, tokKindToStr[kind] & " expected") 605 606proc eat(p: var SqlParser, kind: TokKind) = 607 if p.tok.kind == kind: 608 getTok(p) 609 else: 610 sqlError(p, tokKindToStr[kind] & " expected") 611 612proc eat(p: var SqlParser, keyw: string) = 613 if isKeyw(p, keyw): 614 getTok(p) 615 else: 616 sqlError(p, keyw.toUpperAscii() & " expected") 617 618proc opt(p: var SqlParser, kind: TokKind) = 619 if p.tok.kind == kind: getTok(p) 620 621proc parseDataType(p: var SqlParser): SqlNode = 622 if isKeyw(p, "enum"): 623 result = newNode(nkEnumDef) 624 getTok(p) 625 if p.tok.kind == tkParLe: 626 getTok(p) 627 result.add(newNode(nkStringLit, p.tok.literal)) 628 getTok(p) 629 while p.tok.kind == tkComma: 630 getTok(p) 631 result.add(newNode(nkStringLit, p.tok.literal)) 632 getTok(p) 633 eat(p, tkParRi) 634 else: 635 expectIdent(p) 636 result = newNode(nkIdent, p.tok.literal) 637 getTok(p) 638 # ignore (12, 13) part: 639 if p.tok.kind == tkParLe: 640 getTok(p) 641 expect(p, tkInteger) 642 getTok(p) 643 while p.tok.kind == tkComma: 644 getTok(p) 645 expect(p, tkInteger) 646 getTok(p) 647 eat(p, tkParRi) 648 649proc getPrecedence(p: SqlParser): int = 650 if isOpr(p, "*") or isOpr(p, "/") or isOpr(p, "%"): 651 result = 6 652 elif isOpr(p, "+") or isOpr(p, "-"): 653 result = 5 654 elif isOpr(p, "=") or isOpr(p, "<") or isOpr(p, ">") or isOpr(p, ">=") or 655 isOpr(p, "<=") or isOpr(p, "<>") or isOpr(p, "!=") or isKeyw(p, "is") or 656 isKeyw(p, "like") or isKeyw(p, "in"): 657 result = 4 658 elif isKeyw(p, "and"): 659 result = 3 660 elif isKeyw(p, "or"): 661 result = 2 662 elif isKeyw(p, "between"): 663 result = 1 664 elif p.tok.kind == tkOperator: 665 # user-defined operator: 666 result = 0 667 else: 668 result = - 1 669 670proc parseExpr(p: var SqlParser): SqlNode {.gcsafe.} 671proc parseSelect(p: var SqlParser): SqlNode {.gcsafe.} 672 673proc identOrLiteral(p: var SqlParser): SqlNode = 674 case p.tok.kind 675 of tkQuotedIdentifier: 676 result = newNode(nkQuotedIdent, p.tok.literal) 677 getTok(p) 678 of tkIdentifier: 679 result = newNode(nkIdent, p.tok.literal) 680 getTok(p) 681 of tkStringConstant, tkEscapeConstant, tkDollarQuotedConstant: 682 result = newNode(nkStringLit, p.tok.literal) 683 getTok(p) 684 of tkBitStringConstant: 685 result = newNode(nkBitStringLit, p.tok.literal) 686 getTok(p) 687 of tkHexStringConstant: 688 result = newNode(nkHexStringLit, p.tok.literal) 689 getTok(p) 690 of tkInteger: 691 result = newNode(nkIntegerLit, p.tok.literal) 692 getTok(p) 693 of tkNumeric: 694 result = newNode(nkNumericLit, p.tok.literal) 695 getTok(p) 696 of tkParLe: 697 getTok(p) 698 result = newNode(nkPrGroup) 699 while true: 700 result.add(parseExpr(p)) 701 if p.tok.kind != tkComma: break 702 getTok(p) 703 eat(p, tkParRi) 704 else: 705 if p.tok.literal == "*": 706 result = newNode(nkIdent, p.tok.literal) 707 getTok(p) 708 else: 709 sqlError(p, "expression expected") 710 getTok(p) # we must consume a token here to prevent endless loops! 711 712proc primary(p: var SqlParser): SqlNode = 713 if (p.tok.kind == tkOperator and (p.tok.literal == "+" or p.tok.literal == 714 "-")) or isKeyw(p, "not"): 715 result = newNode(nkPrefix) 716 result.add(newNode(nkIdent, p.tok.literal)) 717 getTok(p) 718 result.add(primary(p)) 719 return 720 result = identOrLiteral(p) 721 while true: 722 case p.tok.kind 723 of tkParLe: 724 var a = result 725 result = newNode(nkCall) 726 result.add(a) 727 getTok(p) 728 while p.tok.kind != tkParRi: 729 result.add(parseExpr(p)) 730 if p.tok.kind == tkComma: getTok(p) 731 else: break 732 eat(p, tkParRi) 733 of tkDot: 734 getTok(p) 735 var a = result 736 if p.tok.kind == tkDot: 737 getTok(p) 738 result = newNode(nkDotDot) 739 else: 740 result = newNode(nkDot) 741 result.add(a) 742 if isOpr(p, "*"): 743 result.add(newNode(nkIdent, "*")) 744 elif p.tok.kind in {tkIdentifier, tkQuotedIdentifier}: 745 result.add(newNode(nkIdent, p.tok.literal)) 746 else: 747 sqlError(p, "identifier expected") 748 getTok(p) 749 else: break 750 751proc lowestExprAux(p: var SqlParser, v: var SqlNode, limit: int): int = 752 var 753 v2, node, opNode: SqlNode 754 v = primary(p) # expand while operators have priorities higher than 'limit' 755 var opPred = getPrecedence(p) 756 result = opPred 757 while opPred > limit: 758 node = newNode(nkInfix) 759 opNode = newNode(nkIdent, p.tok.literal.toLowerAscii()) 760 getTok(p) 761 result = lowestExprAux(p, v2, opPred) 762 node.add(opNode) 763 node.add(v) 764 node.add(v2) 765 v = node 766 opPred = getPrecedence(p) 767 768proc parseExpr(p: var SqlParser): SqlNode = 769 discard lowestExprAux(p, result, - 1) 770 771proc parseTableName(p: var SqlParser): SqlNode = 772 expectIdent(p) 773 result = primary(p) 774 775proc parseColumnReference(p: var SqlParser): SqlNode = 776 result = parseTableName(p) 777 if p.tok.kind == tkParLe: 778 getTok(p) 779 var a = result 780 result = newNode(nkColumnReference) 781 result.add(a) 782 result.add(parseTableName(p)) 783 while p.tok.kind == tkComma: 784 getTok(p) 785 result.add(parseTableName(p)) 786 eat(p, tkParRi) 787 788proc parseCheck(p: var SqlParser): SqlNode = 789 getTok(p) 790 result = newNode(nkCheck) 791 result.add(parseExpr(p)) 792 793proc parseConstraint(p: var SqlParser): SqlNode = 794 getTok(p) 795 result = newNode(nkConstraint) 796 expectIdent(p) 797 result.add(newNode(nkIdent, p.tok.literal)) 798 getTok(p) 799 optKeyw(p, "check") 800 result.add(parseExpr(p)) 801 802proc parseParIdentList(p: var SqlParser, father: SqlNode) = 803 eat(p, tkParLe) 804 while true: 805 expectIdent(p) 806 father.add(newNode(nkIdent, p.tok.literal)) 807 getTok(p) 808 if p.tok.kind != tkComma: break 809 getTok(p) 810 eat(p, tkParRi) 811 812proc parseColumnConstraints(p: var SqlParser, result: SqlNode) = 813 while true: 814 if isKeyw(p, "default"): 815 getTok(p) 816 var n = newNode(nkDefault) 817 n.add(parseExpr(p)) 818 result.add(n) 819 elif isKeyw(p, "references"): 820 getTok(p) 821 var n = newNode(nkReferences) 822 n.add(parseColumnReference(p)) 823 result.add(n) 824 elif isKeyw(p, "not"): 825 getTok(p) 826 eat(p, "null") 827 result.add(newNode(nkNotNull)) 828 elif isKeyw(p, "null"): 829 getTok(p) 830 result.add(newNode(nkNull)) 831 elif isKeyw(p, "identity"): 832 getTok(p) 833 result.add(newNode(nkIdentity)) 834 elif isKeyw(p, "primary"): 835 getTok(p) 836 eat(p, "key") 837 result.add(newNode(nkPrimaryKey)) 838 elif isKeyw(p, "check"): 839 result.add(parseCheck(p)) 840 elif isKeyw(p, "constraint"): 841 result.add(parseConstraint(p)) 842 elif isKeyw(p, "unique"): 843 getTok(p) 844 result.add(newNode(nkUnique)) 845 else: 846 break 847 848proc parseColumnDef(p: var SqlParser): SqlNode = 849 expectIdent(p) 850 result = newNode(nkColumnDef) 851 result.add(newNode(nkIdent, p.tok.literal)) 852 getTok(p) 853 result.add(parseDataType(p)) 854 parseColumnConstraints(p, result) 855 856proc parseIfNotExists(p: var SqlParser, k: SqlNodeKind): SqlNode = 857 getTok(p) 858 if isKeyw(p, "if"): 859 getTok(p) 860 eat(p, "not") 861 eat(p, "exists") 862 result = newNode(succ(k)) 863 else: 864 result = newNode(k) 865 866proc parseTableConstraint(p: var SqlParser): SqlNode = 867 if isKeyw(p, "primary"): 868 getTok(p) 869 eat(p, "key") 870 result = newNode(nkPrimaryKey) 871 parseParIdentList(p, result) 872 elif isKeyw(p, "foreign"): 873 getTok(p) 874 eat(p, "key") 875 result = newNode(nkForeignKey) 876 parseParIdentList(p, result) 877 eat(p, "references") 878 var m = newNode(nkReferences) 879 m.add(parseColumnReference(p)) 880 result.add(m) 881 elif isKeyw(p, "unique"): 882 getTok(p) 883 eat(p, "key") 884 result = newNode(nkUnique) 885 parseParIdentList(p, result) 886 elif isKeyw(p, "check"): 887 result = parseCheck(p) 888 elif isKeyw(p, "constraint"): 889 result = parseConstraint(p) 890 else: 891 sqlError(p, "column definition expected") 892 893proc parseUnique(p: var SqlParser): SqlNode = 894 result = parseExpr(p) 895 if result.kind == nkCall: result.kind = nkUnique 896 897proc parseTableDef(p: var SqlParser): SqlNode = 898 result = parseIfNotExists(p, nkCreateTable) 899 expectIdent(p) 900 result.add(newNode(nkIdent, p.tok.literal)) 901 getTok(p) 902 if p.tok.kind == tkParLe: 903 getTok(p) 904 while p.tok.kind != tkParRi: 905 if isKeyw(p, "constraint"): 906 result.add parseConstraint(p) 907 elif isKeyw(p, "primary") or isKeyw(p, "foreign"): 908 result.add parseTableConstraint(p) 909 elif isKeyw(p, "unique"): 910 result.add parseUnique(p) 911 elif p.tok.kind == tkIdentifier or p.tok.kind == tkQuotedIdentifier: 912 result.add(parseColumnDef(p)) 913 else: 914 result.add(parseTableConstraint(p)) 915 if p.tok.kind != tkComma: break 916 getTok(p) 917 eat(p, tkParRi) 918 # skip additional crap after 'create table (...) crap;' 919 while p.tok.kind notin {tkSemicolon, tkEof}: 920 getTok(p) 921 922proc parseTypeDef(p: var SqlParser): SqlNode = 923 result = parseIfNotExists(p, nkCreateType) 924 expectIdent(p) 925 result.add(newNode(nkIdent, p.tok.literal)) 926 getTok(p) 927 eat(p, "as") 928 result.add(parseDataType(p)) 929 930proc parseWhere(p: var SqlParser): SqlNode = 931 getTok(p) 932 result = newNode(nkWhere) 933 result.add(parseExpr(p)) 934 935proc parseFromItem(p: var SqlParser): SqlNode = 936 result = newNode(nkFromItemPair) 937 if p.tok.kind == tkParLe: 938 getTok(p) 939 var select = parseSelect(p) 940 result.add(select) 941 eat(p, tkParRi) 942 else: 943 result.add(parseExpr(p)) 944 if isKeyw(p, "as"): 945 getTok(p) 946 result.add(parseExpr(p)) 947 948proc parseIndexDef(p: var SqlParser): SqlNode = 949 result = parseIfNotExists(p, nkCreateIndex) 950 if isKeyw(p, "primary"): 951 getTok(p) 952 eat(p, "key") 953 result.add(newNode(nkPrimaryKey)) 954 else: 955 expectIdent(p) 956 result.add(newNode(nkIdent, p.tok.literal)) 957 getTok(p) 958 eat(p, "on") 959 expectIdent(p) 960 result.add(newNode(nkIdent, p.tok.literal)) 961 getTok(p) 962 eat(p, tkParLe) 963 expectIdent(p) 964 result.add(newNode(nkIdent, p.tok.literal)) 965 getTok(p) 966 while p.tok.kind == tkComma: 967 getTok(p) 968 expectIdent(p) 969 result.add(newNode(nkIdent, p.tok.literal)) 970 getTok(p) 971 eat(p, tkParRi) 972 973proc parseInsert(p: var SqlParser): SqlNode = 974 getTok(p) 975 eat(p, "into") 976 expectIdent(p) 977 result = newNode(nkInsert) 978 result.add(newNode(nkIdent, p.tok.literal)) 979 getTok(p) 980 if p.tok.kind == tkParLe: 981 var n = newNode(nkColumnList) 982 parseParIdentList(p, n) 983 result.add n 984 else: 985 result.add(newNode(nkNone)) 986 if isKeyw(p, "default"): 987 getTok(p) 988 eat(p, "values") 989 result.add(newNode(nkDefault)) 990 else: 991 eat(p, "values") 992 eat(p, tkParLe) 993 var n = newNode(nkValueList) 994 while true: 995 n.add(parseExpr(p)) 996 if p.tok.kind != tkComma: break 997 getTok(p) 998 result.add(n) 999 eat(p, tkParRi) 1000 1001proc parseUpdate(p: var SqlParser): SqlNode = 1002 getTok(p) 1003 result = newNode(nkUpdate) 1004 result.add(primary(p)) 1005 eat(p, "set") 1006 while true: 1007 var a = newNode(nkAsgn) 1008 expectIdent(p) 1009 a.add(newNode(nkIdent, p.tok.literal)) 1010 getTok(p) 1011 if isOpr(p, "="): getTok(p) 1012 else: sqlError(p, "= expected") 1013 a.add(parseExpr(p)) 1014 result.add(a) 1015 if p.tok.kind != tkComma: break 1016 getTok(p) 1017 if isKeyw(p, "where"): 1018 result.add(parseWhere(p)) 1019 else: 1020 result.add(newNode(nkNone)) 1021 1022proc parseDelete(p: var SqlParser): SqlNode = 1023 getTok(p) 1024 if isOpr(p, "*"): 1025 getTok(p) 1026 result = newNode(nkDelete) 1027 eat(p, "from") 1028 result.add(primary(p)) 1029 if isKeyw(p, "where"): 1030 result.add(parseWhere(p)) 1031 else: 1032 result.add(newNode(nkNone)) 1033 1034proc parseSelect(p: var SqlParser): SqlNode = 1035 getTok(p) 1036 if isKeyw(p, "distinct"): 1037 getTok(p) 1038 result = newNode(nkSelectDistinct) 1039 elif isKeyw(p, "all"): 1040 getTok(p) 1041 result = newNode(nkSelect) 1042 var a = newNode(nkSelectColumns) 1043 while true: 1044 if isOpr(p, "*"): 1045 a.add(newNode(nkIdent, "*")) 1046 getTok(p) 1047 else: 1048 var pair = newNode(nkSelectPair) 1049 pair.add(parseExpr(p)) 1050 a.add(pair) 1051 if isKeyw(p, "as"): 1052 getTok(p) 1053 pair.add(parseExpr(p)) 1054 if p.tok.kind != tkComma: break 1055 getTok(p) 1056 result.add(a) 1057 if isKeyw(p, "from"): 1058 var f = newNode(nkFrom) 1059 while true: 1060 getTok(p) 1061 f.add(parseFromItem(p)) 1062 if p.tok.kind != tkComma: break 1063 result.add(f) 1064 if isKeyw(p, "where"): 1065 result.add(parseWhere(p)) 1066 if isKeyw(p, "group"): 1067 getTok(p) 1068 eat(p, "by") 1069 var g = newNode(nkGroup) 1070 while true: 1071 g.add(parseExpr(p)) 1072 if p.tok.kind != tkComma: break 1073 getTok(p) 1074 result.add(g) 1075 if isKeyw(p, "order"): 1076 getTok(p) 1077 eat(p, "by") 1078 var n = newNode(nkOrder) 1079 while true: 1080 var e = parseExpr(p) 1081 if isKeyw(p, "asc"): 1082 getTok(p) # is default 1083 elif isKeyw(p, "desc"): 1084 getTok(p) 1085 var x = newNode(nkDesc) 1086 x.add(e) 1087 e = x 1088 n.add(e) 1089 if p.tok.kind != tkComma: break 1090 getTok(p) 1091 result.add(n) 1092 if isKeyw(p, "having"): 1093 var h = newNode(nkHaving) 1094 while true: 1095 getTok(p) 1096 h.add(parseExpr(p)) 1097 if p.tok.kind != tkComma: break 1098 result.add(h) 1099 if isKeyw(p, "union"): 1100 result.add(newNode(nkUnion)) 1101 getTok(p) 1102 elif isKeyw(p, "intersect"): 1103 result.add(newNode(nkIntersect)) 1104 getTok(p) 1105 elif isKeyw(p, "except"): 1106 result.add(newNode(nkExcept)) 1107 getTok(p) 1108 if isKeyw(p, "join") or isKeyw(p, "inner") or isKeyw(p, "outer") or isKeyw(p, "cross"): 1109 var join = newNode(nkJoin) 1110 result.add(join) 1111 if isKeyw(p, "join"): 1112 join.add(newNode(nkIdent, "")) 1113 getTok(p) 1114 else: 1115 join.add(newNode(nkIdent, p.tok.literal.toLowerAscii())) 1116 getTok(p) 1117 eat(p, "join") 1118 join.add(parseFromItem(p)) 1119 eat(p, "on") 1120 join.add(parseExpr(p)) 1121 if isKeyw(p, "limit"): 1122 getTok(p) 1123 var l = newNode(nkLimit) 1124 l.add(parseExpr(p)) 1125 result.add(l) 1126 1127proc parseStmt(p: var SqlParser; parent: SqlNode) = 1128 if isKeyw(p, "create"): 1129 getTok(p) 1130 optKeyw(p, "cached") 1131 optKeyw(p, "memory") 1132 optKeyw(p, "temp") 1133 optKeyw(p, "global") 1134 optKeyw(p, "local") 1135 optKeyw(p, "temporary") 1136 optKeyw(p, "unique") 1137 optKeyw(p, "hash") 1138 if isKeyw(p, "table"): 1139 parent.add parseTableDef(p) 1140 elif isKeyw(p, "type"): 1141 parent.add parseTypeDef(p) 1142 elif isKeyw(p, "index"): 1143 parent.add parseIndexDef(p) 1144 else: 1145 sqlError(p, "TABLE expected") 1146 elif isKeyw(p, "insert"): 1147 parent.add parseInsert(p) 1148 elif isKeyw(p, "update"): 1149 parent.add parseUpdate(p) 1150 elif isKeyw(p, "delete"): 1151 parent.add parseDelete(p) 1152 elif isKeyw(p, "select"): 1153 parent.add parseSelect(p) 1154 elif isKeyw(p, "begin"): 1155 getTok(p) 1156 else: 1157 sqlError(p, "SELECT, CREATE, UPDATE or DELETE expected") 1158 1159proc parse(p: var SqlParser): SqlNode = 1160 ## parses the content of `p`'s input stream and returns the SQL AST. 1161 ## Syntax errors raise an `SqlParseError` exception. 1162 result = newNode(nkStmtList) 1163 while p.tok.kind != tkEof: 1164 parseStmt(p, result) 1165 if p.tok.kind == tkEof: 1166 break 1167 eat(p, tkSemicolon) 1168 1169proc close(p: var SqlParser) = 1170 ## closes the parser `p`. The associated input stream is closed too. 1171 close(SqlLexer(p)) 1172 1173type 1174 SqlWriter = object 1175 indent: int 1176 upperCase: bool 1177 buffer: string 1178 1179proc add(s: var SqlWriter, thing: char) = 1180 s.buffer.add(thing) 1181 1182proc prepareAdd(s: var SqlWriter) {.inline.} = 1183 if s.buffer.len > 0 and s.buffer[^1] notin {' ', '\L', '(', '.'}: 1184 s.buffer.add(" ") 1185 1186proc add(s: var SqlWriter, thing: string) = 1187 s.prepareAdd 1188 s.buffer.add(thing) 1189 1190proc addKeyw(s: var SqlWriter, thing: string) = 1191 var keyw = thing 1192 if s.upperCase: 1193 keyw = keyw.toUpperAscii() 1194 s.add(keyw) 1195 1196proc addIden(s: var SqlWriter, thing: string) = 1197 var iden = thing 1198 if iden.toLowerAscii() in reservedKeywords: 1199 iden = '"' & iden & '"' 1200 s.add(iden) 1201 1202proc ra(n: SqlNode, s: var SqlWriter) {.gcsafe.} 1203 1204proc rs(n: SqlNode, s: var SqlWriter, prefix = "(", suffix = ")", sep = ", ") = 1205 if n.len > 0: 1206 s.add(prefix) 1207 for i in 0 .. n.len-1: 1208 if i > 0: s.add(sep) 1209 ra(n.sons[i], s) 1210 s.add(suffix) 1211 1212proc addMulti(s: var SqlWriter, n: SqlNode, sep = ',') = 1213 if n.len > 0: 1214 for i in 0 .. n.len-1: 1215 if i > 0: s.add(sep) 1216 ra(n.sons[i], s) 1217 1218proc addMulti(s: var SqlWriter, n: SqlNode, sep = ',', prefix, suffix: char) = 1219 if n.len > 0: 1220 s.add(prefix) 1221 for i in 0 .. n.len-1: 1222 if i > 0: s.add(sep) 1223 ra(n.sons[i], s) 1224 s.add(suffix) 1225 1226proc quoted(s: string): string = 1227 "\"" & replace(s, "\"", "\"\"") & "\"" 1228 1229func escape(result: var string; s: string) = 1230 result.add('\'') 1231 for c in items(s): 1232 case c 1233 of '\0'..'\31': 1234 result.add("\\x") 1235 result.add(toHex(ord(c), 2)) 1236 of '\'': result.add("''") 1237 else: result.add(c) 1238 result.add('\'') 1239 1240proc ra(n: SqlNode, s: var SqlWriter) = 1241 if n == nil: return 1242 case n.kind 1243 of nkNone: discard 1244 of nkIdent: 1245 if allCharsInSet(n.strVal, {'\33'..'\127'}): 1246 s.add(n.strVal) 1247 else: 1248 s.add(quoted(n.strVal)) 1249 of nkQuotedIdent: 1250 s.add(quoted(n.strVal)) 1251 of nkStringLit: 1252 s.prepareAdd 1253 s.buffer.escape(n.strVal) 1254 of nkBitStringLit: 1255 s.add("b'" & n.strVal & "'") 1256 of nkHexStringLit: 1257 s.add("x'" & n.strVal & "'") 1258 of nkIntegerLit, nkNumericLit: 1259 s.add(n.strVal) 1260 of nkPrimaryKey: 1261 s.addKeyw("primary key") 1262 rs(n, s) 1263 of nkForeignKey: 1264 s.addKeyw("foreign key") 1265 rs(n, s) 1266 of nkNotNull: 1267 s.addKeyw("not null") 1268 of nkNull: 1269 s.addKeyw("null") 1270 of nkDot: 1271 ra(n.sons[0], s) 1272 s.add('.') 1273 ra(n.sons[1], s) 1274 of nkDotDot: 1275 ra(n.sons[0], s) 1276 s.add(". .") 1277 ra(n.sons[1], s) 1278 of nkPrefix: 1279 ra(n.sons[0], s) 1280 s.add(' ') 1281 ra(n.sons[1], s) 1282 of nkInfix: 1283 ra(n.sons[1], s) 1284 s.add(' ') 1285 ra(n.sons[0], s) 1286 s.add(' ') 1287 ra(n.sons[2], s) 1288 of nkCall, nkColumnReference: 1289 ra(n.sons[0], s) 1290 s.add('(') 1291 for i in 1..n.len-1: 1292 if i > 1: s.add(',') 1293 ra(n.sons[i], s) 1294 s.add(')') 1295 of nkPrGroup: 1296 s.add('(') 1297 s.addMulti(n) 1298 s.add(')') 1299 of nkReferences: 1300 s.addKeyw("references") 1301 ra(n.sons[0], s) 1302 of nkDefault: 1303 s.addKeyw("default") 1304 ra(n.sons[0], s) 1305 of nkCheck: 1306 s.addKeyw("check") 1307 ra(n.sons[0], s) 1308 of nkConstraint: 1309 s.addKeyw("constraint") 1310 ra(n.sons[0], s) 1311 s.addKeyw("check") 1312 ra(n.sons[1], s) 1313 of nkUnique: 1314 s.addKeyw("unique") 1315 rs(n, s) 1316 of nkIdentity: 1317 s.addKeyw("identity") 1318 of nkColumnDef: 1319 rs(n, s, "", "", " ") 1320 of nkStmtList: 1321 for i in 0..n.len-1: 1322 ra(n.sons[i], s) 1323 s.add(';') 1324 of nkInsert: 1325 assert n.len == 3 1326 s.addKeyw("insert into") 1327 ra(n.sons[0], s) 1328 s.add(' ') 1329 ra(n.sons[1], s) 1330 if n.sons[2].kind == nkDefault: 1331 s.addKeyw("default values") 1332 else: 1333 ra(n.sons[2], s) 1334 of nkUpdate: 1335 s.addKeyw("update") 1336 ra(n.sons[0], s) 1337 s.addKeyw("set") 1338 var L = n.len 1339 for i in 1 .. L-2: 1340 if i > 1: s.add(", ") 1341 var it = n.sons[i] 1342 assert it.kind == nkAsgn 1343 ra(it, s) 1344 ra(n.sons[L-1], s) 1345 of nkDelete: 1346 s.addKeyw("delete from") 1347 ra(n.sons[0], s) 1348 ra(n.sons[1], s) 1349 of nkSelect, nkSelectDistinct: 1350 s.addKeyw("select") 1351 if n.kind == nkSelectDistinct: 1352 s.addKeyw("distinct") 1353 for i in 0 ..< n.len: 1354 ra(n.sons[i], s) 1355 of nkSelectColumns: 1356 for i, column in n.sons: 1357 if i > 0: s.add(',') 1358 ra(column, s) 1359 of nkSelectPair: 1360 ra(n.sons[0], s) 1361 if n.sons.len == 2: 1362 s.addKeyw("as") 1363 ra(n.sons[1], s) 1364 of nkFromItemPair: 1365 if n.sons[0].kind in {nkIdent, nkQuotedIdent}: 1366 ra(n.sons[0], s) 1367 else: 1368 assert n.sons[0].kind == nkSelect 1369 s.add('(') 1370 ra(n.sons[0], s) 1371 s.add(')') 1372 if n.sons.len == 2: 1373 s.addKeyw("as") 1374 ra(n.sons[1], s) 1375 of nkAsgn: 1376 ra(n.sons[0], s) 1377 s.add(" = ") 1378 ra(n.sons[1], s) 1379 of nkFrom: 1380 s.addKeyw("from") 1381 s.addMulti(n) 1382 of nkGroup: 1383 s.addKeyw("group by") 1384 s.addMulti(n) 1385 of nkLimit: 1386 s.addKeyw("limit") 1387 s.addMulti(n) 1388 of nkHaving: 1389 s.addKeyw("having") 1390 s.addMulti(n) 1391 of nkOrder: 1392 s.addKeyw("order by") 1393 s.addMulti(n) 1394 of nkJoin: 1395 var joinType = n.sons[0].strVal 1396 if joinType == "": 1397 joinType = "join" 1398 else: 1399 joinType &= " " & "join" 1400 s.addKeyw(joinType) 1401 ra(n.sons[1], s) 1402 s.addKeyw("on") 1403 ra(n.sons[2], s) 1404 of nkDesc: 1405 ra(n.sons[0], s) 1406 s.addKeyw("desc") 1407 of nkUnion: 1408 s.addKeyw("union") 1409 of nkIntersect: 1410 s.addKeyw("intersect") 1411 of nkExcept: 1412 s.addKeyw("except") 1413 of nkColumnList: 1414 rs(n, s) 1415 of nkValueList: 1416 s.addKeyw("values") 1417 rs(n, s) 1418 of nkWhere: 1419 s.addKeyw("where") 1420 ra(n.sons[0], s) 1421 of nkCreateTable, nkCreateTableIfNotExists: 1422 s.addKeyw("create table") 1423 if n.kind == nkCreateTableIfNotExists: 1424 s.addKeyw("if not exists") 1425 ra(n.sons[0], s) 1426 s.add('(') 1427 for i in 1..n.len-1: 1428 if i > 1: s.add(',') 1429 ra(n.sons[i], s) 1430 s.add(");") 1431 of nkCreateType, nkCreateTypeIfNotExists: 1432 s.addKeyw("create type") 1433 if n.kind == nkCreateTypeIfNotExists: 1434 s.addKeyw("if not exists") 1435 ra(n.sons[0], s) 1436 s.addKeyw("as") 1437 ra(n.sons[1], s) 1438 of nkCreateIndex, nkCreateIndexIfNotExists: 1439 s.addKeyw("create index") 1440 if n.kind == nkCreateIndexIfNotExists: 1441 s.addKeyw("if not exists") 1442 ra(n.sons[0], s) 1443 s.addKeyw("on") 1444 ra(n.sons[1], s) 1445 s.add('(') 1446 for i in 2..n.len-1: 1447 if i > 2: s.add(", ") 1448 ra(n.sons[i], s) 1449 s.add(");") 1450 of nkEnumDef: 1451 s.addKeyw("enum") 1452 rs(n, s) 1453 1454proc renderSQL*(n: SqlNode, upperCase = false): string = 1455 ## Converts an SQL abstract syntax tree to its string representation. 1456 var s: SqlWriter 1457 s.buffer = "" 1458 s.upperCase = upperCase 1459 ra(n, s) 1460 return s.buffer 1461 1462proc `$`*(n: SqlNode): string = 1463 ## an alias for `renderSQL`. 1464 renderSQL(n) 1465 1466proc treeReprAux(s: SqlNode, level: int, result: var string) = 1467 result.add('\n') 1468 for i in 0 ..< level: result.add(" ") 1469 1470 result.add($s.kind) 1471 if s.kind in LiteralNodes: 1472 result.add(' ') 1473 result.add(s.strVal) 1474 else: 1475 for son in s.sons: 1476 treeReprAux(son, level + 1, result) 1477 1478proc treeRepr*(s: SqlNode): string = 1479 result = newStringOfCap(128) 1480 treeReprAux(s, 0, result) 1481 1482import streams 1483 1484proc open(L: var SqlLexer, input: Stream, filename: string) = 1485 lexbase.open(L, input) 1486 L.filename = filename 1487 1488proc open(p: var SqlParser, input: Stream, filename: string) = 1489 ## opens the parser `p` and assigns the input stream `input` to it. 1490 ## `filename` is only used for error messages. 1491 open(SqlLexer(p), input, filename) 1492 p.tok.kind = tkInvalid 1493 p.tok.literal = "" 1494 getTok(p) 1495 1496proc parseSQL*(input: Stream, filename: string): SqlNode = 1497 ## parses the SQL from `input` into an AST and returns the AST. 1498 ## `filename` is only used for error messages. 1499 ## Syntax errors raise an `SqlParseError` exception. 1500 var p: SqlParser 1501 open(p, input, filename) 1502 try: 1503 result = parse(p) 1504 finally: 1505 close(p) 1506 1507proc parseSQL*(input: string, filename = ""): SqlNode = 1508 ## parses the SQL from `input` into an AST and returns the AST. 1509 ## `filename` is only used for error messages. 1510 ## Syntax errors raise an `SqlParseError` exception. 1511 parseSQL(newStringStream(input), "") 1512