su4sml/src/codegen/tpl_parser.sml

221 lines
7.9 KiB
Standard ML

(*****************************************************************************
* su4sml GCG - Generic Code Generator
*
* tpl_parser.sml - template parser of a su4sml-gcg template
* Copyright (C) 2005 Raphael Eidenbenz <eraphael@student.ethz.ch>
*
* This file is part of su4sml-gcg.
*
* su4sml is free software; you can redistribute it and/or modify it under
* the terms of the GNU General Public License as published by the Free
* Software Foundation; either version 2 of the License, or (at your option)
* any later version.
*
* su4sml is distributed in the hope that it will be useful, but WITHOUT ANY
* WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
* FOR A PARTICULAR PURPOSE. See the GNU General Public License for more
* details.
*
* You should have received a copy of the GNU General Public License along
* with this program; if not, write to the Free Software Foundation, Inc.,
* 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
******************************************************************************)
signature TPL_PARSER =
sig
datatype TemplateTree
= ElseNode of TemplateTree list
| EvalLeaf of TemplateTree list
| ForEachNode of string * TemplateTree list
| IfNode of string * TemplateTree list
| OpenFileLeaf of string
| RootNode of TemplateTree list
| TextLeaf of string
val printTTree : TemplateTree -> unit
val parse : string -> TemplateTree
end
structure Tpl_Parser : TPL_PARSER =
struct
open Gcg_Helper
val tplStream = ref (TextIO.openString "@// dummy template\n");
fun opentFile file = (TextIO.closeIn (!tplStream) ;
print ("opening "^file^"...\n");
tplStream := (TextIO.openIn file))
fun cleanUp tplFile = (TextIO.closeIn (!tplStream);
OS.FileSys.remove tplFile)
fun readNextLine () = TextIO.inputLine (!tplStream)
datatype TemplateTree = RootNode of TemplateTree list
| OpenFileLeaf of string
| EvalLeaf of TemplateTree list
| TextLeaf of string
| IfNode of string * TemplateTree list
| ElseNode of TemplateTree list
| ForEachNode of string * TemplateTree list
(**
* replaceSafely (s,v,x) replaces every v in s with x or if v is escaped removes "\"
*)
fun replaceSafely ("",_,_) = ""
| replaceSafely (s,v,x) = let val v_size = size v and
s_size = size s
in
if String.isPrefix ((str #"\\")^v) s
then (v^(replaceSafely(String.extract(s,v_size +1,NONE),v,x)))
else if String.isPrefix v s
then x^(replaceSafely(String.extract(s,v_size,NONE),v,x))
else str(String.sub(s,0))^(replaceSafely(String.extract(s,1,NONE),v,x))
end
(**
* splits string into tokens and
* removes space- and tab-characters
*)
fun cleanLine s = let fun removeWspace s =
String.implode (List.filter (fn c => not (Char.isSpace c)) (String.explode s))
fun concatWith [] d = ""
| concatWith [s] d = s^" "
| concatWith (h::t) d = h^d^(concatWith t d)
val myToken = (String.tokens (fn c => c = #" "))
in
concatWith ( List.filter (fn s => s <>"")(((List.map removeWspace) o myToken) s )) " "
end
(* debugging function
* prints ParseTree to stdOut
*)
fun printTplTree prefix (RootNode(l)) = (print (prefix^"root"^"\n"); List.app (printTplTree (prefix))l)
| printTplTree prefix (OpenFileLeaf(s))= print (prefix^"openfile:"^s^"\n")
| printTplTree prefix (EvalLeaf(l)) = (print (prefix^"eval:\n"); List.app (printTplTree (prefix^"\t"))l)
| printTplTree prefix (TextLeaf(s)) = print (prefix^"text:"^s^"\n")
| printTplTree prefix (IfNode(s,l)) = (print (prefix^"if:"^s^"\n");List.app (printTplTree (prefix^"\t")) l)
| printTplTree prefix (ElseNode(l)) = (print (prefix^"else:"^"\n"); List.app (printTplTree (prefix^"\t")) l)
| printTplTree prefix (ForEachNode(s,l))=(print (prefix^"foreach:"^s^"\n");List.app (printTplTree (prefix^"\t")) l)
val printTTree = printTplTree ""
fun isComment s = (String.isPrefix "//" s)
(** returns the left part of l up to the element where f evaluates to true
*)
fun itemsUntil f [] = []
| itemsUntil f (h::t) = if (f h) then []
else h::(itemsUntil f t)
(** splits line into tokens considering handling escaped @ *)
fun tokenize line = let val l = joinEscapeSplitted "@" (fieldSplit line #"@");
in
(hd l)::(itemsUntil isComment (tl l))
end
(**
* extracts the type of line.
* line type must be first token in line!
* if no control tag in line -> "text" returned
*)
fun getType l = let val sl = tokenize l
in
if (length sl = 1)
then "text"
else hd(fieldSplit (String.concat(tl(sl))) #" ")
end
(**
* getContent line
* @return the content of a line
*)
fun getContent l = let val sl = tokenize l
in
if (length sl = 1)
then hd(sl)
else String.concat(tl(fieldSplit (String.concat(tl(sl))) #" "))
end
(**
* cleans line, replaces nl and tabs
* so that no space char is left out
*)
fun preprocess s = let val rl = replaceSafely(replaceSafely(cleanLine s,"@nl ","\n"),"@tab ","\t")
in
replaceSafely(replaceSafely(rl,"@nl","\n"),"@tab","\t")
end
(**
* builds the TemplateTree.
* @return a TemplateTree list
*)
fun buildTree (SOME line) = let fun getNode ("text",c) = (TextLeaf(c))::(buildTree (readNextLine()))
| getNode ("foreach",c) = ForEachNode(c,(buildTree (readNextLine())))::(buildTree (readNextLine()))
| getNode ("if",c) = IfNode(c,buildTree (readNextLine()))::(buildTree (readNextLine()))
| getNode ("else",_) = [ElseNode(buildTree (readNextLine()))]
| getNode ("elsif",c) = [ElseNode([IfNode(c,buildTree (readNextLine()))])]
| getNode ("openfile",c)= (OpenFileLeaf(c))::(buildTree (readNextLine()))
| getNode ("eval","") =
(EvalLeaf(buildTree(readNextLine())))::(buildTree (readNextLine()))
| getNode ("eval",expr) = (EvalLeaf([TextLeaf(expr)]))::(buildTree (readNextLine()))
| getNode ("end",_) = []
| getNode (t,c) = gcg_error ("Couldn't parse the node \""^t^"\" with content\""^c^"\" in tpl_parser.buildTree.")
val prLine = preprocess line
in
getNode ((getType prLine),(getContent prLine))
end
| buildTree NONE = []
fun codegen_env _ = getOpt(OS.Process.getEnv "CODEGEN_HOME",".")
(** calls the external cpp ( C PreProcessor).
* writes merged template to a file with extension .tmp instead of .tpl
* and returns this file
*)
fun call_cpp file =
let (*val targetFile = String.substring (file,0,size file -4) ^".tmp";*)
val targetFile = OS.FileSys.tmpName ()
val _ = OS.Process.system ("cpp "^codegen_env()^"/"^file^" "^targetFile^" -P -C")
in
targetFile
end
(** parse template-file
* @return the parsed template tree
*)
fun parse file = let val mergedTpl = call_cpp file;
val u = opentFile mergedTpl;
val pt = RootNode(buildTree (readNextLine()));
val u2 = cleanUp mergedTpl;
in
(print "...template parsed.\n"; pt)
end
(*
val testline = "@foreach \\@public @// commkejbk";
val textline1 = "\t\tpublic $class_name$ ";
val textline2 = "";
val endline = "\t@end";
*)
(*
val ParseTree = parse "examples/C#.tpl";
printTplTree ParseTree;
*)
end