123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222 |
- #
- #
- # The Nim Compiler
- # (c) Copyright 2012 Andreas Rumpf
- #
- # See the file "copying.txt", included in this
- # distribution, for details about the copyright.
- #
- # This module implements Nim's standard template filter.
- import
- llstream, strutils, ast, msgs, options,
- filters, lineinfos, pathutils
- type
- TParseState = enum
- psDirective, psTempl
- TTmplParser = object
- inp: PLLStream
- state: TParseState
- info: TLineInfo
- indent, emitPar: int
- x: string # the current input line
- outp: PLLStream # the output will be parsed by parser
- subsChar, nimDirective: char
- emit, conc, toStr: string
- curly, bracket, par: int
- pendingExprLine: bool
- config: ConfigRef
- const
- PatternChars = {'a'..'z', 'A'..'Z', '0'..'9', '\x80'..'\xFF', '.', '_'}
- proc newLine(p: var TTmplParser) =
- llStreamWrite(p.outp, repeat(')', p.emitPar))
- p.emitPar = 0
- if p.info.line > uint16(1): llStreamWrite(p.outp, "\n")
- if p.pendingExprLine:
- llStreamWrite(p.outp, spaces(2))
- p.pendingExprLine = false
- proc scanPar(p: var TTmplParser, d: int) =
- var i = d
- while i < p.x.len:
- case p.x[i]
- of '(': inc(p.par)
- of ')': dec(p.par)
- of '[': inc(p.bracket)
- of ']': dec(p.bracket)
- of '{': inc(p.curly)
- of '}': dec(p.curly)
- else: discard
- inc(i)
- proc withInExpr(p: TTmplParser): bool {.inline.} =
- result = p.par > 0 or p.bracket > 0 or p.curly > 0
- const
- LineContinuationOprs = {'+', '-', '*', '/', '\\', '<', '>', '^',
- '|', '%', '&', '$', '@', '~', ','}
- proc parseLine(p: var TTmplParser) =
- var j = 0
- let len = p.x.len
- while j < len and p.x[j] == ' ': inc(j)
- if len >= 2 and p.x[0] == p.nimDirective and p.x[1] == '?':
- newLine(p)
- elif j < len and p.x[j] == p.nimDirective:
- newLine(p)
- inc(j)
- while j < len and p.x[j] == ' ': inc(j)
- let d = j
- var keyw = ""
- while j < len and p.x[j] in PatternChars:
- keyw.add(p.x[j])
- inc(j)
- scanPar(p, j)
- p.pendingExprLine = withInExpr(p) or p.x.endsWith(LineContinuationOprs)
- case keyw
- of "end":
- if p.indent >= 2:
- dec(p.indent, 2)
- else:
- p.info.col = int16(j)
- localError(p.config, p.info, "'end' does not close a control flow construct")
- llStreamWrite(p.outp, spaces(p.indent))
- llStreamWrite(p.outp, "#end")
- of "if", "when", "try", "while", "for", "block", "case", "proc", "iterator",
- "converter", "macro", "template", "method", "func":
- llStreamWrite(p.outp, spaces(p.indent))
- llStreamWrite(p.outp, substr(p.x, d))
- inc(p.indent, 2)
- of "elif", "of", "else", "except", "finally":
- llStreamWrite(p.outp, spaces(p.indent - 2))
- llStreamWrite(p.outp, substr(p.x, d))
- of "let", "var", "const", "type":
- llStreamWrite(p.outp, spaces(p.indent))
- llStreamWrite(p.outp, substr(p.x, d))
- if not p.x.contains({':', '='}):
- # no inline element --> treat as block:
- inc(p.indent, 2)
- else:
- llStreamWrite(p.outp, spaces(p.indent))
- llStreamWrite(p.outp, substr(p.x, d))
- p.state = psDirective
- else:
- # data line
- # reset counters
- p.par = 0
- p.curly = 0
- p.bracket = 0
- j = 0
- case p.state
- of psTempl:
- # next line of string literal:
- llStreamWrite(p.outp, p.conc)
- llStreamWrite(p.outp, "\n")
- llStreamWrite(p.outp, spaces(p.indent + 2))
- llStreamWrite(p.outp, "\"")
- of psDirective:
- newLine(p)
- llStreamWrite(p.outp, spaces(p.indent))
- llStreamWrite(p.outp, p.emit)
- llStreamWrite(p.outp, "(\"")
- inc(p.emitPar)
- p.state = psTempl
- while j < len:
- case p.x[j]
- of '\x01'..'\x1F', '\x80'..'\xFF':
- llStreamWrite(p.outp, "\\x")
- llStreamWrite(p.outp, toHex(ord(p.x[j]), 2))
- inc(j)
- of '\\':
- llStreamWrite(p.outp, "\\\\")
- inc(j)
- of '\'':
- llStreamWrite(p.outp, "\\\'")
- inc(j)
- of '\"':
- llStreamWrite(p.outp, "\\\"")
- inc(j)
- else:
- if p.x[j] == p.subsChar:
- # parse Nim expression:
- inc(j)
- case p.x[j]
- of '{':
- p.info.col = int16(j)
- llStreamWrite(p.outp, '\"')
- llStreamWrite(p.outp, p.conc)
- llStreamWrite(p.outp, p.toStr)
- llStreamWrite(p.outp, '(')
- inc(j)
- var curly = 0
- while j < len:
- case p.x[j]
- of '{':
- inc(j)
- inc(curly)
- llStreamWrite(p.outp, '{')
- of '}':
- inc(j)
- if curly == 0: break
- if curly > 0: dec(curly)
- llStreamWrite(p.outp, '}')
- else:
- llStreamWrite(p.outp, p.x[j])
- inc(j)
- if curly > 0:
- localError(p.config, p.info, "expected closing '}'")
- break
- llStreamWrite(p.outp, ')')
- llStreamWrite(p.outp, p.conc)
- llStreamWrite(p.outp, '\"')
- of 'a'..'z', 'A'..'Z', '\x80'..'\xFF':
- llStreamWrite(p.outp, '\"')
- llStreamWrite(p.outp, p.conc)
- llStreamWrite(p.outp, p.toStr)
- llStreamWrite(p.outp, '(')
- while j < len and p.x[j] in PatternChars:
- llStreamWrite(p.outp, p.x[j])
- inc(j)
- llStreamWrite(p.outp, ')')
- llStreamWrite(p.outp, p.conc)
- llStreamWrite(p.outp, '\"')
- else:
- if p.x[j] == p.subsChar:
- llStreamWrite(p.outp, p.subsChar)
- inc(j)
- else:
- p.info.col = int16(j)
- localError(p.config, p.info, "invalid expression")
- else:
- llStreamWrite(p.outp, p.x[j])
- inc(j)
- llStreamWrite(p.outp, "\\n\"")
- proc filterTmpl*(conf: ConfigRef, stdin: PLLStream, filename: AbsoluteFile,
- call: PNode): PLLStream =
- var p = TTmplParser(config: conf, info: newLineInfo(conf, filename, 0, 0),
- outp: llStreamOpen(""), inp: stdin,
- subsChar: charArg(conf, call, "subschar", 1, '$'),
- nimDirective: charArg(conf, call, "metachar", 2, '#'),
- emit: strArg(conf, call, "emit", 3, "result.add"),
- conc: strArg(conf, call, "conc", 4, " & "),
- toStr: strArg(conf, call, "tostring", 5, "$"),
- x: newStringOfCap(120)
- )
- # do not process the first line which contains the directive:
- if llStreamReadLine(p.inp, p.x):
- inc p.info.line
- while llStreamReadLine(p.inp, p.x):
- inc p.info.line
- parseLine(p)
- newLine(p)
- result = p.outp
- llStreamClose(p.inp)
|