summary refs log tree commit diff stats
path: root/tests/modules/t8665.nim
Commit message (Expand)AuthorAgeFilesLines
* add a prepass for codeReordering (#21513)ringabout2023-03-171-1/+0
* fixes #19795; fixes #11852; fixes #19974; remove parsing pipeline, Nim now pa...ringabout2023-02-221-0/+1
* make run the default action of a test in testerArne Döring2018-11-231-0/+4
* sfForward is also sfReorder for skModule symbols (#8692)LemonBoy2018-08-211-0/+1
ef='#n64'>64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138
#
#
#           The Nim Compiler
#        (c) Copyright 2012 Andreas Rumpf
#
#    See the file "copying.txt", included in this
#    distribution, for details about the copyright.
#

## Implements the dispatcher for the different parsers.

import
  strutils, llstream, ast, idents, lexer, options, msgs, parser,
  filters, filter_tmpl, renderer, lineinfos, pathutils

when defined(nimPreviewSlimSystem):
  import std/[syncio, assertions]

export Parser, parseAll, parseTopLevelStmt, checkFirstLineIndentation, closeParser

type
  FilterKind = enum
    filtNone = "none"
    filtTemplate = "stdtmpl"
    filtReplace = "replace"
    filtStrip = "strip"

proc utf8Bom(s: string): int =
  if s.len >= 3 and s[0] == '\xEF' and s[1] == '\xBB' and s[2] == '\xBF':
    3
  else:
    0

proc containsShebang(s: string, i: int): bool =
  if i+1 < s.len and s[i] == '#' and s[i+1] == '!':
    var j = i + 2
    while j < s.len and s[j] in Whitespace: inc(j)
    result = s[j] == '/'

proc parsePipe(filename: AbsoluteFile, inputStream: PLLStream; cache: IdentCache;
               config: ConfigRef): PNode =
  result = newNode(nkEmpty)
  var s = llStreamOpen(filename, fmRead)
  if s != nil:
    var line = newStringOfCap(80)
    discard llStreamReadLine(s, line)
    var i = utf8Bom(line)
    var linenumber = 1
    if containsShebang(line, i):
      discard llStreamReadLine(s, line)
      i = 0
      inc linenumber
    if i+1 < line.len and line[i] == '#' and line[i+1] == '?':
      when defined(nimpretty):
        # XXX this is a bit hacky, but oh well...
        config.quitOrRaise "can't nimpretty a source code filter: " & $filename
      else:
        inc(i, 2)
        while i < line.len and line[i] in Whitespace: inc(i)
        var p: Parser
        openParser(p, filename, llStreamOpen(substr(line, i)), cache, config)
        result = parseAll(p)
        closeParser(p)
    llStreamClose(s)

proc getFilter(ident: PIdent): FilterKind =
  for i in FilterKind:
    if cmpIgnoreStyle(ident.s, $i) == 0:
      return i

proc getCallee(conf: ConfigRef; n: PNode): PIdent =
  if n.kind in nkCallKinds and n[0].kind == nkIdent:
    result = n[0].ident
  elif n.kind == nkIdent:
    result = n.ident
  else:
    localError(conf, n.info, "invalid filter: " & renderTree(n))

proc applyFilter(p: var Parser, n: PNode, filename: AbsoluteFile,
                 stdin: PLLStream): PLLStream =
  var f = getFilter(getCallee(p.lex.config, n))
  result = case f
           of filtNone:
             stdin
           of filtTemplate:
             filterTmpl(p.lex.config, stdin, filename, n)
           of filtStrip:
             filterStrip(p.lex.config, stdin, filename, n)
           of filtReplace:
             filterReplace(p.lex.config, stdin, filename, n)
  if f != filtNone:
    assert p.lex.config != nil
    if p.lex.config.hasHint(hintCodeBegin):
      rawMessage(p.lex.config, hintCodeBegin, "")
      msgWriteln(p.lex.config, result.s)
      rawMessage(p.lex.config, hintCodeEnd, "")

proc evalPipe(p: var Parser, n: PNode, filename: AbsoluteFile,
              start: PLLStream): PLLStream =
  assert p.lex.config != nil
  result = start
  if n.kind == nkEmpty: return
  if n.kind == nkInfix and n[0].kind == nkIdent and n[0].ident.s == "|":
    for i in 1..2:
      if n[i].kind == nkInfix:
        result = evalPipe(p, n[i], filename, result)
      else:
        result = applyFilter(p, n[i], filename, result)
  elif n.kind == nkStmtList:
    result = evalPipe(p, n[0], filename, result)
  else:
    result = applyFilter(p, n, filename, result)

proc openParser*(p: var Parser, fileIdx: FileIndex, inputstream: PLLStream;
                  cache: IdentCache; config: ConfigRef) =
  assert config != nil
  let filename = toFullPathConsiderDirty(config, fileIdx)
  var pipe = parsePipe(filename, inputstream, cache, config)
  p.lex.config = config
  let s = if pipe != nil: evalPipe(p, pipe, filename, inputstream)
          else: inputstream
  parser.openParser(p, fileIdx, s, cache, config)

proc setupParser*(p: var Parser; fileIdx: FileIndex; cache: IdentCache;
                   config: ConfigRef): bool =
  let filename = toFullPathConsiderDirty(config, fileIdx)
  var f: File
  if not open(f, filename.string):
    rawMessage(config, errGenerated, "cannot open file: " & filename.string)
    return false
  openParser(p, fileIdx, llStreamOpen(f), cache, config)
  result = true

proc parseFile*(fileIdx: FileIndex; cache: IdentCache; config: ConfigRef): PNode =
  var p: Parser
  if setupParser(p, fileIdx, cache, config):
    result = parseAll(p)
    closeParser(p)