syntaxes.nim 4.3 KB

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