syntaxes.nim 4.6 KB

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