# # # 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, astalgo, idents, lexer, options, msgs, parser, filters, filter_tmpl, renderer, configuration type TFilterKind* = enum filtNone, filtTemplate, filtReplace, filtStrip TParserKind* = enum skinStandard, skinStrongSpaces, skinEndX const parserNames*: array[TParserKind, string] = ["standard", "strongspaces", "endx"] filterNames*: array[TFilterKind, string] = ["none", "stdtmpl", "replace", "strip"] type TParsers* = object skin*: TParserKind parser*: TParser template config(p: TParsers): ConfigRef = p.parser.lex.config proc parseAll*(p: var TParsers): PNode = case p.skin of skinStandard, skinStrongSpaces: result = parser.parseAll(p.parser) of skinEndX: internalError(p.config, "parser to implement") result = ast.emptyNode proc parseTopLevelStmt*(p: var TParsers): PNode = case p.skin of skinStandard, skinStrongSpaces: result = parser.parseTopLevelStmt(p.parser) of skinEndX: internalError(p.config, "parser to implement") result = ast.emptyNode proc utf8Bom(s: string): int = if s.len >= 3 and s[0] == '\xEF' and s[1] == '\xBB' and s[2] == '\xBF': result = 3 else: result = 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: string, inputStream: PLLStream; cache: IdentCache; config: ConfigRef): PNode = result = ast.emptyNode 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] == '?': inc(i, 2) while i < line.len and line[i] in Whitespace: inc(i) var q: TParser parser.openParser(q, filename, llStreamOpen(substr(line, i)), cache, config) result = parser.parseAll(q) parser.closeParser(q) llStreamClose(s) proc getFilter(ident: PIdent): TFilterKind = for i in countup(low(TFilterKind), high(TFilterKind)): if cmpIgnoreStyle(ident.s, filterNames[i]) == 0: return i result = filtNone proc getParser(conf: ConfigRef; n: PNode; ident: PIdent): TParserKind = for i in countup(low(TParserKind), high(TParserKind)): if cmpIgnoreStyle(ident.s, parserNames[i]) == 0: return i localError(conf, n.info, "unknown parser: " & ident.s) proc getCallee(conf: ConfigRef; n: PNode): PIdent = if n.kind in nkCallKinds and n.sons[0].kind == nkIdent: result = n.sons[0].ident elif n.kind == nkIdent: result = n.ident else: localError(conf, n.info, "invalid filter: " & renderTree(n)) proc applyFilter(p: var TParsers, n: PNode, filename: string, stdin: PLLStream): PLLStream = var ident = getCallee(p.config, n) var f = getFilter(ident) case f of filtNone: p.skin = getParser(p.config, n, ident) result = stdin of filtTemplate: result = filterTmpl(stdin, filename, n, p.config) of filtStrip: result = filterStrip(p.config, stdin, filename, n) of filtReplace: result = filterReplace(p.config, stdin, filename, n) if f != filtNone: if hintCodeBegin in p.config.notes: rawMessage(p.config, hintCodeBegin, []) msgWriteln(result.s) rawMessage(p.config, hintCodeEnd, []) proc evalPipe(p: var TParsers, n: PNode, filename: string, start: PLLStream): PLLStream = result = start if n.kind == nkEmpty: return if n.kind == nkInfix and n[0].kind == nkIdent and n[0].ident.s == "|": for i in countup(1, 2): if n.sons[i].kind == nkInfix: result = evalPipe(p, n.sons[i], filename, result) else: result = applyFilter(p, n.sons[i], filename, result) elif n.kind == nkStmtList: result = evalPipe(p, n.sons[0], filename, result) else: result = applyFilter(p, n, filename, result) proc openParsers*(p: var TParsers, fileIdx: FileIndex, inputstream: PLLStream; cache: IdentCache; config: ConfigRef) = var s: PLLStream p.skin = skinStandard let filename = fileIdx.toFullPathConsiderDirty var pipe = parsePipe(filename, inputstream, cache, config) if pipe != nil: s = evalPipe(p, pipe, filename, inputstream) else: s = inputstream case p.skin of skinStandard, skinEndX: parser.openParser(p.parser, fileIdx, s, cache, config, false) of skinStrongSpaces: parser.openParser(p.parser, fileIdx, s, cache, config, true) proc closeParsers*(p: var TParsers) = parser.closeParser(p.parser) proc parseFile*(fileIdx: FileIndex; cache: IdentCache; config: ConfigRef): PNode {.procvar.} = var p: TParsers f: File let filename = fileIdx.toFullPathConsiderDirty if not open(f, filename): rawMessage(config, errGenerated, "cannot open file: " & filename) return openParsers(p, fileIdx, llStreamOpen(f), cache, config) result = parseAll(p) closeParsers(p)