// // // The Nimrod Compiler // (c) Copyright 2009 Andreas Rumpf // // See the file "copying.txt", included in this // distribution, for details about the copyright. // unit ptmplsyn; // This module implements Nimrod's standard template filter. {$include config.inc} interface uses nsystem, llstream, nos, charsets, wordrecg, idents, strutils, ast, astalgo, msgs, options, rnimsyn, filters; function filterTmpl(input: PLLStream; const filename: string; call: PNode): PLLStream; // #! template(subsChar='$', metaChar='#') | standard(version="0.7.2") implementation type TParseState = (psDirective, psTempl); TTmplParser = record inp: PLLStream; state: TParseState; info: TLineInfo; indent, par: int; x: string; // the current input line outp: PLLStream; // the ouput will be parsed by pnimsyn subsChar, NimDirective: Char; emit, conc, toStr: string; end; const PatternChars = ['a'..'z', 'A'..'Z', '0'..'9', #128..#255, '.', '_']; procedure newLine(var p: TTmplParser); begin LLStreamWrite(p.outp, repeatChar(p.par, ')')); p.par := 0; if p.info.line > int16(1) then LLStreamWrite(p.outp, nl); end; procedure parseLine(var p: TTmplParser); var d, j, curly: int; keyw: string; begin j := strStart; while p.x[j] = ' ' do inc(j); if (p.x[strStart] = p.NimDirective) and (p.x[strStart+1] = '!') then newLine(p) else if (p.x[j] = p.NimDirective) then begin newLine(p); inc(j); while p.x[j] = ' ' do inc(j); d := j; keyw := ''; while p.x[j] in PatternChars do begin addChar(keyw, p.x[j]); inc(j); end; case whichKeyword(keyw) of wEnd: begin if p.indent >= 2 then dec(p.indent, 2) else begin p.info.col := int16(j); liMessage(p.info, errXNotAllowedHere, 'end'); end; LLStreamWrite(p.outp, repeatChar(p.indent)); LLStreamWrite(p.outp, '#end'); end; wIf, wWhen, wTry, wWhile, wFor, wBlock, wCase, wProc, wIterator, wConverter, wMacro, wTemplate, wMethod: begin LLStreamWrite(p.outp, repeatChar(p.indent)); LLStreamWrite(p.outp, ncopy(p.x, d)); inc(p.indent, 2); end; wElif, wOf, wElse, wExcept, wFinally: begin LLStreamWrite(p.outp, repeatChar(p.indent-2)); LLStreamWrite(p.outp, ncopy(p.x, d)); end else begin LLStreamWrite(p.outp, repeatChar(p.indent)); LLStreamWrite(p.outp, ncopy(p.x, d)); end end; p.state := psDirective end else begin // data line j := strStart; case p.state of psTempl: begin // next line of string literal: LLStreamWrite(p.outp, p.conc); LLStreamWrite(p.outp, nl); LLStreamWrite(p.outp, repeatChar(p.indent + 2)); LLStreamWrite(p.outp, '"'+''); end; psDirective: begin newLine(p); LLStreamWrite(p.outp, repeatChar(p.indent)); LLStreamWrite(p.outp, p.emit); LLStreamWrite(p.outp, '("'); inc(p.par); end end; p.state := psTempl; while true do begin case p.x[j] of #0: break; #1..#31, #128..#255: begin LLStreamWrite(p.outp, '\x'); LLStreamWrite(p.outp, toHex(ord(p.x[j]), 2)); inc(j); end; '\': begin LLStreamWrite(p.outp, '\\'); inc(j); end; '''': begin LLStreamWrite(p.outp, '\'''); inc(j); end; '"': begin LLStreamWrite(p.outp, '\"'); inc(j); end; else if p.x[j] = p.subsChar then begin // parse Nimrod expression: inc(j); case p.x[j] of '{': begin p.info.col := int16(j); LLStreamWrite(p.outp, '"'); LLStreamWrite(p.outp, p.conc); LLStreamWrite(p.outp, p.toStr); LLStreamWrite(p.outp, '('); inc(j); curly := 0; while true do begin case p.x[j] of #0: liMessage(p.info, errXExpected, '}'+''); '{': begin inc(j); inc(curly); LLStreamWrite(p.outp, '{'); end; '}': begin inc(j); if curly = 0 then break; if curly > 0 then dec(curly); LLStreamWrite(p.outp, '}'); end; else begin LLStreamWrite(p.outp, p.x[j]); inc(j) end end end; LLStreamWrite(p.outp, ')'); LLStreamWrite(p.outp, p.conc); LLStreamWrite(p.outp, '"'); end; 'a'..'z', 'A'..'Z', #128..#255: begin LLStreamWrite(p.outp, '"'); LLStreamWrite(p.outp, p.conc); LLStreamWrite(p.outp, p.toStr); LLStreamWrite(p.outp, '('); while p.x[j] in PatternChars do begin LLStreamWrite(p.outp, p.x[j]); inc(j) end; LLStreamWrite(p.outp, ')'); LLStreamWrite(p.outp, p.conc); LLStreamWrite(p.outp, '"') end; else if p.x[j] = p.subsChar then begin LLStreamWrite(p.outp, p.subsChar); inc(j); end else begin p.info.col := int16(j); liMessage(p.info, errInvalidExpression, '$'+''); end end end else begin LLStreamWrite(p.outp, p.x[j]); inc(j); end end end; LLStreamWrite(p.outp, '\n"'); end end; function filterTmpl(input: PLLStream; const filename: string; call: PNode): PLLStream; var p: TTmplParser; begin {@ignore} FillChar(p, sizeof(p), 0); {@emit} p.info := newLineInfo(filename, 0, 0); p.outp := LLStreamOpen(''); p.inp := input; 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, '$'+''); while not LLStreamAtEnd(p.inp) do begin p.x := LLStreamReadLine(p.inp) {@ignore} + #0 {@emit}; p.info.line := p.info.line + int16(1); parseLine(p); end; newLine(p); result := p.outp; LLStreamClose(p.inp); end; end.