syntaxes.nim 5.6 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174
  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, astalgo, idents, lexer, options, msgs, parser,
  12. filters, filter_tmpl, renderer, lineinfos, pathutils
  13. type
  14. TFilterKind* = enum
  15. filtNone, filtTemplate, filtReplace, filtStrip
  16. TParserKind* = enum
  17. skinStandard, skinStrongSpaces, skinEndX
  18. const
  19. parserNames*: array[TParserKind, string] = ["standard", "strongspaces",
  20. "endx"]
  21. filterNames*: array[TFilterKind, string] = ["none", "stdtmpl", "replace",
  22. "strip"]
  23. type
  24. TParsers* = object
  25. skin*: TParserKind
  26. parser*: TParser
  27. template config(p: TParsers): ConfigRef = p.parser.lex.config
  28. proc parseAll*(p: var TParsers): PNode =
  29. case p.skin
  30. of skinStandard, skinStrongSpaces:
  31. result = parser.parseAll(p.parser)
  32. of skinEndX:
  33. internalError(p.config, "parser to implement")
  34. proc parseTopLevelStmt*(p: var TParsers): PNode =
  35. case p.skin
  36. of skinStandard, skinStrongSpaces:
  37. result = parser.parseTopLevelStmt(p.parser)
  38. of skinEndX:
  39. internalError(p.config, "parser to implement")
  40. proc utf8Bom(s: string): int =
  41. if s.len >= 3 and s[0] == '\xEF' and s[1] == '\xBB' and s[2] == '\xBF':
  42. result = 3
  43. else:
  44. result = 0
  45. proc containsShebang(s: string, i: int): bool =
  46. if i+1 < s.len and s[i] == '#' and s[i+1] == '!':
  47. var j = i + 2
  48. while j < s.len and s[j] in Whitespace: inc(j)
  49. result = s[j] == '/'
  50. proc parsePipe(filename: AbsoluteFile, inputStream: PLLStream; cache: IdentCache;
  51. config: ConfigRef): PNode =
  52. result = newNode(nkEmpty)
  53. var s = llStreamOpen(filename, fmRead)
  54. if s != nil:
  55. var line = newStringOfCap(80)
  56. discard llStreamReadLine(s, line)
  57. var i = utf8Bom(line)
  58. var linenumber = 1
  59. if containsShebang(line, i):
  60. discard llStreamReadLine(s, line)
  61. i = 0
  62. inc linenumber
  63. if i+1 < line.len and line[i] == '#' and line[i+1] == '?':
  64. when defined(nimpretty2):
  65. # XXX this is a bit hacky, but oh well...
  66. quit "can't nimpretty a source code filter"
  67. else:
  68. inc(i, 2)
  69. while i < line.len and line[i] in Whitespace: inc(i)
  70. var q: TParser
  71. parser.openParser(q, filename, llStreamOpen(substr(line, i)), cache, config)
  72. result = parser.parseAll(q)
  73. parser.closeParser(q)
  74. llStreamClose(s)
  75. proc getFilter(ident: PIdent): TFilterKind =
  76. for i in countup(low(TFilterKind), high(TFilterKind)):
  77. if cmpIgnoreStyle(ident.s, filterNames[i]) == 0:
  78. return i
  79. result = filtNone
  80. proc getParser(conf: ConfigRef; n: PNode; ident: PIdent): TParserKind =
  81. for i in countup(low(TParserKind), high(TParserKind)):
  82. if cmpIgnoreStyle(ident.s, parserNames[i]) == 0:
  83. return i
  84. localError(conf, n.info, "unknown parser: " & ident.s)
  85. proc getCallee(conf: ConfigRef; n: PNode): PIdent =
  86. if n.kind in nkCallKinds and n.sons[0].kind == nkIdent:
  87. result = n.sons[0].ident
  88. elif n.kind == nkIdent:
  89. result = n.ident
  90. else:
  91. localError(conf, n.info, "invalid filter: " & renderTree(n))
  92. proc applyFilter(p: var TParsers, n: PNode, filename: AbsoluteFile,
  93. stdin: PLLStream): PLLStream =
  94. var ident = getCallee(p.config, n)
  95. var f = getFilter(ident)
  96. case f
  97. of filtNone:
  98. p.skin = getParser(p.config, n, ident)
  99. result = stdin
  100. of filtTemplate:
  101. result = filterTmpl(stdin, filename, n, p.config)
  102. of filtStrip:
  103. result = filterStrip(p.config, stdin, filename, n)
  104. of filtReplace:
  105. result = filterReplace(p.config, stdin, filename, n)
  106. if f != filtNone:
  107. assert p.config != nil
  108. if hintCodeBegin in p.config.notes:
  109. rawMessage(p.config, hintCodeBegin, [])
  110. msgWriteln(p.config, result.s)
  111. rawMessage(p.config, hintCodeEnd, [])
  112. proc evalPipe(p: var TParsers, n: PNode, filename: AbsoluteFile,
  113. start: PLLStream): PLLStream =
  114. assert p.config != nil
  115. result = start
  116. if n.kind == nkEmpty: return
  117. if n.kind == nkInfix and n[0].kind == nkIdent and n[0].ident.s == "|":
  118. for i in countup(1, 2):
  119. if n.sons[i].kind == nkInfix:
  120. result = evalPipe(p, n.sons[i], filename, result)
  121. else:
  122. result = applyFilter(p, n.sons[i], filename, result)
  123. elif n.kind == nkStmtList:
  124. result = evalPipe(p, n.sons[0], filename, result)
  125. else:
  126. result = applyFilter(p, n, filename, result)
  127. proc openParsers*(p: var TParsers, fileIdx: FileIndex, inputstream: PLLStream;
  128. cache: IdentCache; config: ConfigRef) =
  129. assert config != nil
  130. var s: PLLStream
  131. p.skin = skinStandard
  132. let filename = toFullPathConsiderDirty(config, fileIdx)
  133. var pipe = parsePipe(filename, inputstream, cache, config)
  134. p.config() = config
  135. if pipe != nil: s = evalPipe(p, pipe, filename, inputstream)
  136. else: s = inputstream
  137. case p.skin
  138. of skinStandard, skinEndX:
  139. parser.openParser(p.parser, fileIdx, s, cache, config, false)
  140. of skinStrongSpaces:
  141. parser.openParser(p.parser, fileIdx, s, cache, config, true)
  142. proc closeParsers*(p: var TParsers) =
  143. parser.closeParser(p.parser)
  144. proc parseFile*(fileIdx: FileIndex; cache: IdentCache; config: ConfigRef): PNode {.procvar.} =
  145. var
  146. p: TParsers
  147. f: File
  148. let filename = toFullPathConsiderDirty(config, fileIdx)
  149. if not open(f, filename.string):
  150. rawMessage(config, errGenerated, "cannot open file: " & filename.string)
  151. return
  152. openParsers(p, fileIdx, llStreamOpen(f), cache, config)
  153. result = parseAll(p)
  154. closeParsers(p)