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, os, wordrecg, idents, strutils, ast, astalgo, msgs, options,
- renderer, filters
- 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 ouput will be parsed by pnimsyn
- subsChar, nimDirective: char
- emit, conc, toStr: string
- curly, bracket, par: int
- pendingExprLine: bool
- 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 > int16(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 true:
- case p.x[i]
- of '\0': break
- 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
- while p.x[j] == ' ': inc(j)
- if p.x[0] == p.nimDirective and p.x[1] == '?':
- newLine(p)
- elif p.x[j] == p.nimDirective:
- newLine(p)
- inc(j)
- while p.x[j] == ' ': inc(j)
- let d = j
- var keyw = ""
- while p.x[j] in PatternChars:
- add(keyw, 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.info, errXNotAllowedHere, "end")
- 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 true:
- case p.x[j]
- of '\0':
- break
- 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 true:
- case p.x[j]
- of '\0':
- localError(p.info, errXExpected, "}")
- break
- 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)
- 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 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.info, errInvalidExpression, "$")
- else:
- llStreamWrite(p.outp, p.x[j])
- inc(j)
- llStreamWrite(p.outp, "\\n\"")
- proc filterTmpl*(stdin: PLLStream, filename: string, call: PNode): PLLStream =
- var p: TTmplParser
- p.info = newLineInfo(filename, 0, 0)
- p.outp = llStreamOpen("")
- p.inp = stdin
- p.subsChar = charArg(call, "subschar", 1, '$')
- p.nimDirective = charArg(call, "metachar", 2, '#')
- p.emit = strArg(call, "emit", 3, "result.add")
- p.conc = strArg(call, "conc", 4, " & ")
- p.toStr = strArg(call, "tostring", 5, "$")
- p.x = newStringOfCap(120)
- # do not process the first line which contains the directive:
- if llStreamReadLine(p.inp, p.x):
- p.info.line = p.info.line + int16(1)
- while llStreamReadLine(p.inp, p.x):
- p.info.line = p.info.line + int16(1)
- parseLine(p)
- newLine(p)
- result = p.outp
- llStreamClose(p.inp)
|