syntaxes.nim 4.6 KB

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