syntaxes.nim 4.5 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139
  1. #
  2. #
  3. # The Nim Compiler
  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. ## Implements the dispatcher for the different parsers.
  10. import
  11. strutils, llstream, ast, idents, lexer, options, msgs, parser,
  12. filters, filter_tmpl, renderer, lineinfos, pathutils
  13. when defined(nimPreviewSlimSystem):
  14. import std/[syncio, assertions]
  15. export Parser, parseAll, parseTopLevelStmt, checkFirstLineIndentation, closeParser
  16. type
  17. FilterKind = enum
  18. filtNone = "none"
  19. filtTemplate = "stdtmpl"
  20. filtReplace = "replace"
  21. filtStrip = "strip"
  22. proc utf8Bom(s: string): int =
  23. if s.len >= 3 and s[0] == '\xEF' and s[1] == '\xBB' and s[2] == '\xBF':
  24. 3
  25. else:
  26. 0
  27. proc containsShebang(s: string, i: int): bool =
  28. if i+1 < s.len and s[i] == '#' and s[i+1] == '!':
  29. var j = i + 2
  30. while j < s.len and s[j] in Whitespace: inc(j)
  31. result = s[j] == '/'
  32. proc parsePipe(filename: AbsoluteFile, inputStream: PLLStream; cache: IdentCache;
  33. config: ConfigRef): PNode =
  34. result = newNode(nkEmpty)
  35. var s = llStreamOpen(filename, fmRead)
  36. if s != nil:
  37. var line = newStringOfCap(80)
  38. discard llStreamReadLine(s, line)
  39. var i = utf8Bom(line)
  40. var linenumber = 1
  41. if containsShebang(line, i):
  42. discard llStreamReadLine(s, line)
  43. i = 0
  44. inc linenumber
  45. if i+1 < line.len and line[i] == '#' and line[i+1] == '?':
  46. when defined(nimpretty):
  47. # XXX this is a bit hacky, but oh well...
  48. config.quitOrRaise "can't nimpretty a source code filter: " & $filename
  49. else:
  50. inc(i, 2)
  51. while i < line.len and line[i] in Whitespace: inc(i)
  52. var p: Parser
  53. openParser(p, filename, llStreamOpen(substr(line, i)), cache, config)
  54. result = parseAll(p)
  55. closeParser(p)
  56. llStreamClose(s)
  57. proc getFilter(ident: PIdent): FilterKind =
  58. for i in FilterKind:
  59. if cmpIgnoreStyle(ident.s, $i) == 0:
  60. return i
  61. proc getCallee(conf: ConfigRef; n: PNode): PIdent =
  62. if n.kind in nkCallKinds and n[0].kind == nkIdent:
  63. result = n[0].ident
  64. elif n.kind == nkIdent:
  65. result = n.ident
  66. else:
  67. localError(conf, n.info, "invalid filter: " & renderTree(n))
  68. proc applyFilter(p: var Parser, n: PNode, filename: AbsoluteFile,
  69. stdin: PLLStream): PLLStream =
  70. var f = getFilter(getCallee(p.lex.config, n))
  71. result = case f
  72. of filtNone:
  73. stdin
  74. of filtTemplate:
  75. filterTmpl(p.lex.config, stdin, filename, n)
  76. of filtStrip:
  77. filterStrip(p.lex.config, stdin, filename, n)
  78. of filtReplace:
  79. filterReplace(p.lex.config, stdin, filename, n)
  80. if f != filtNone:
  81. assert p.lex.config != nil
  82. if p.lex.config.hasHint(hintCodeBegin):
  83. rawMessage(p.lex.config, hintCodeBegin, "")
  84. msgWriteln(p.lex.config, result.s)
  85. rawMessage(p.lex.config, hintCodeEnd, "")
  86. proc evalPipe(p: var Parser, n: PNode, filename: AbsoluteFile,
  87. start: PLLStream): PLLStream =
  88. assert p.lex.config != nil
  89. result = start
  90. if n.kind == nkEmpty: return
  91. if n.kind == nkInfix and n[0].kind == nkIdent and n[0].ident.s == "|":
  92. for i in 1..2:
  93. if n[i].kind == nkInfix:
  94. result = evalPipe(p, n[i], filename, result)
  95. else:
  96. result = applyFilter(p, n[i], filename, result)
  97. elif n.kind == nkStmtList:
  98. result = evalPipe(p, n[0], filename, result)
  99. else:
  100. result = applyFilter(p, n, filename, result)
  101. proc openParser*(p: var Parser, fileIdx: FileIndex, inputstream: PLLStream;
  102. cache: IdentCache; config: ConfigRef) =
  103. assert config != nil
  104. let filename = toFullPathConsiderDirty(config, fileIdx)
  105. var pipe = parsePipe(filename, inputstream, cache, config)
  106. p.lex.config = config
  107. let s = if pipe != nil: evalPipe(p, pipe, filename, inputstream)
  108. else: inputstream
  109. parser.openParser(p, fileIdx, s, cache, config)
  110. proc setupParser*(p: var Parser; fileIdx: FileIndex; cache: IdentCache;
  111. config: ConfigRef): bool =
  112. let filename = toFullPathConsiderDirty(config, fileIdx)
  113. var f: File
  114. if not open(f, filename.string):
  115. rawMessage(config, errGenerated, "cannot open file: " & filename.string)
  116. return false
  117. openParser(p, fileIdx, llStreamOpen(f), cache, config)
  118. result = true
  119. proc parseFile*(fileIdx: FileIndex; cache: IdentCache; config: ConfigRef): PNode =
  120. var p: Parser
  121. if setupParser(p, fileIdx, cache, config):
  122. result = parseAll(p)
  123. closeParser(p)