syntaxes.nim 5.4 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170
  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. inc(i, 2)
  65. while i < line.len and line[i] in Whitespace: inc(i)
  66. var q: TParser
  67. parser.openParser(q, filename, llStreamOpen(substr(line, i)), cache, config)
  68. result = parser.parseAll(q)
  69. parser.closeParser(q)
  70. llStreamClose(s)
  71. proc getFilter(ident: PIdent): TFilterKind =
  72. for i in countup(low(TFilterKind), high(TFilterKind)):
  73. if cmpIgnoreStyle(ident.s, filterNames[i]) == 0:
  74. return i
  75. result = filtNone
  76. proc getParser(conf: ConfigRef; n: PNode; ident: PIdent): TParserKind =
  77. for i in countup(low(TParserKind), high(TParserKind)):
  78. if cmpIgnoreStyle(ident.s, parserNames[i]) == 0:
  79. return i
  80. localError(conf, n.info, "unknown parser: " & ident.s)
  81. proc getCallee(conf: ConfigRef; n: PNode): PIdent =
  82. if n.kind in nkCallKinds and n.sons[0].kind == nkIdent:
  83. result = n.sons[0].ident
  84. elif n.kind == nkIdent:
  85. result = n.ident
  86. else:
  87. localError(conf, n.info, "invalid filter: " & renderTree(n))
  88. proc applyFilter(p: var TParsers, n: PNode, filename: AbsoluteFile,
  89. stdin: PLLStream): PLLStream =
  90. var ident = getCallee(p.config, n)
  91. var f = getFilter(ident)
  92. case f
  93. of filtNone:
  94. p.skin = getParser(p.config, n, ident)
  95. result = stdin
  96. of filtTemplate:
  97. result = filterTmpl(stdin, filename, n, p.config)
  98. of filtStrip:
  99. result = filterStrip(p.config, stdin, filename, n)
  100. of filtReplace:
  101. result = filterReplace(p.config, stdin, filename, n)
  102. if f != filtNone:
  103. assert p.config != nil
  104. if hintCodeBegin in p.config.notes:
  105. rawMessage(p.config, hintCodeBegin, [])
  106. msgWriteln(p.config, result.s)
  107. rawMessage(p.config, hintCodeEnd, [])
  108. proc evalPipe(p: var TParsers, n: PNode, filename: AbsoluteFile,
  109. start: PLLStream): PLLStream =
  110. assert p.config != nil
  111. result = start
  112. if n.kind == nkEmpty: return
  113. if n.kind == nkInfix and n[0].kind == nkIdent and n[0].ident.s == "|":
  114. for i in countup(1, 2):
  115. if n.sons[i].kind == nkInfix:
  116. result = evalPipe(p, n.sons[i], filename, result)
  117. else:
  118. result = applyFilter(p, n.sons[i], filename, result)
  119. elif n.kind == nkStmtList:
  120. result = evalPipe(p, n.sons[0], filename, result)
  121. else:
  122. result = applyFilter(p, n, filename, result)
  123. proc openParsers*(p: var TParsers, fileIdx: FileIndex, inputstream: PLLStream;
  124. cache: IdentCache; config: ConfigRef) =
  125. assert config != nil
  126. var s: PLLStream
  127. p.skin = skinStandard
  128. let filename = toFullPathConsiderDirty(config, fileIdx)
  129. var pipe = parsePipe(filename, inputstream, cache, config)
  130. p.config() = config
  131. if pipe != nil: s = evalPipe(p, pipe, filename, inputstream)
  132. else: s = inputstream
  133. case p.skin
  134. of skinStandard, skinEndX:
  135. parser.openParser(p.parser, fileIdx, s, cache, config, false)
  136. of skinStrongSpaces:
  137. parser.openParser(p.parser, fileIdx, s, cache, config, true)
  138. proc closeParsers*(p: var TParsers) =
  139. parser.closeParser(p.parser)
  140. proc parseFile*(fileIdx: FileIndex; cache: IdentCache; config: ConfigRef): PNode {.procvar.} =
  141. var
  142. p: TParsers
  143. f: File
  144. let filename = toFullPathConsiderDirty(config, fileIdx)
  145. if not open(f, filename.string):
  146. rawMessage(config, errGenerated, "cannot open file: " & filename.string)
  147. return
  148. openParsers(p, fileIdx, llStreamOpen(f), cache, config)
  149. result = parseAll(p)
  150. closeParsers(p)