/src/GCog.groovy
Groovy | 262 lines | 153 code | 23 blank | 86 comment | 34 complexity | 31da53adaaa9bfad50ff85dabb693c76 MD5 | raw file
- /**
- * gcog - generate code with inlined Groovy code.
- * http://code.google.com/p/gcog/
- *
- * Copyright 2008, Tobias Hientzsch.
- *
- * MIT license http://www.opensource.org/licenses/mit-license.php
- *
- * Original idea and code from Cog code generation tool.
- * http://nedbatchelder.com/code/cog
- *
- * Copyright 2004-2008, Ned Batchelder.
- *
- */
- public class GCog {
- def sBeginSpec
- def sEndSpec
- def sEndOutput
- def sEndFormat
- def sOutputMode
- def reEndOutput
- def options
- def GCog() {
- sBeginSpec = '[[[gcog'
- sEndSpec = ']]]'
- sEndOutput = '[[[end]]]'
- // reEndOutput = /\[\[\[end]]](?P<hashsect> *\(checksum: (?P<hash>[a-f0-9]+)\))/
- // sEndFormat = '[[[end]]] (checksum: %s)'
- //options = CogOptions()
- options = [:]
- // sOutputMode = 'w'
- //installCogModule()
- }
- def showWarning(msg) {
- println "Warning:", msg
- }
- def isBeginSpecLine(String s) {
- return s.indexOf(sBeginSpec) >= 0
- }
- def isEndSpecLine(String s) {
- return s.indexOf(sEndSpec) >= 0 && !isEndOutputLine(s)
- }
- def isEndOutputLine(String s) {
- return s.indexOf(sEndOutput) >= 0
- }
-
- def processString(fIn , fname=null) {
- def fOut = new StringWriter();
- process(new StringReader(fIn), fOut, fname);
- return fOut.toString()
- }
- void processFile(fIn, fOut, fname=null) {
- fOut = new FileWriter(new File(fOut));
- fIn = new FileReader(new File(fIn));
- process(fIn, fOut, fname);
- fOut.flush();
- fOut.close();
- }
- def processOneFile(fInOut , fname=null) {
- def fIn = new File(fInOut).getText();
- def fOut = processString(fIn, fInOut);
- if(fIn != fOut) {
- fOut = new File(fInOut).write(fOut)
- }
- }
- def process(fIn, Writer fOut, fname=null) {
- fIn = new NumberedFileReader(fIn)
- def sFileIn = fname || ''
-
- def bSawCog = false
-
- // loop over generator chunks
- def l = fIn.readLine()
- def firstLine = true
- def write = { s ->
- if(firstLine){
- firstLine = false
- } else {
- s = '\n' + s
- }
-
- fOut.write(s)
- }
- while(l!=null) {
- // Find the next spec begin
- while(l!=null && !isBeginSpecLine(l)) {
- if (isEndSpecLine(l)) {
- throw new Exception("Unexpected '$sEndSpec'" /*% self.sEndSpec,
- file=sFileIn, line=fIn.linenumber()*/)
- }
- if (isEndOutputLine(l)) {
- throw new Exception("Unexpected '$sEndOutput'" /*% self.sEndOutput,
- file=sFileIn, line=fIn.linenumber()*/)
- }
- write(l)
- l = fIn.readLine()
- }
- if(l==null)
- break
- if(!options.bDeleteCode) {
- write(l)
- }
- // l is the begin spec
- def gen = new GCogGenerator()
- //gen.setOutput(stdout=self.stdout)
- gen.parseMarker(l)
- def firstLineNum = fIn.linenumber()
-
- // If the spec begin is also a spec end, then process the single
- // line of code inside.
- if(isEndSpecLine(l)) {
- def beg = l.indexOf(sBeginSpec)
- def end = l.indexOf(sEndSpec)
- if(beg > end && end >=0) {
- throw new Exception("Cog code markers inverted");
- //file=sFileIn, line=firstLineNum)
- } else {
- def sCode = l[beg+sBeginSpec.length() .. end-1].trim()
- gen.parseLine(sCode)
- }
- } else {
- // Deal with an ordinary code block.
- l = fIn.readLine()
-
- // Get all the lines in the spec
- while(l!=null && !isEndSpecLine(l)) {
- if(isBeginSpecLine(l)){
- throw new Exception("Unexpected '$sBeginSpec'") /*% self.,
- file=sFileIn, line=fIn.linenumber())*/
- }
- if(isEndOutputLine(l)) {
- throw new Exception("Unexpected '$sEndOutput'") /* % self.sEndOutput,
- file=sFileIn, line=fIn.linenumber())*/
- }
- if(!options.bDeleteCode) {
- write(l)
- }
- gen.parseLine(l)
- l = fIn.readLine()
- }
- if (l==null) {
- new Exception(
- "Cog block begun but never ended.");/*,
- file=sFileIn, line=firstLineNum)*/
- }
- if(!options.bDeleteCode) {
- write(l)
- }
- gen.parseMarker(l)
- }
- l = fIn.readLine()
-
- // Eat all the lines in the output section. While reading past
- // them, compute the md5 hash of the old output.
- //hasher = md5.new()
- while(l!=null && !isEndOutputLine(l)) {
- if (isBeginSpecLine(l)){
- throw new Exception("Unexpected '$sBeginSpec'")/* % self.sBeginSpec,
- file=sFileIn, line=fIn.linenumber())*/
- }
- if(isEndSpecLine(l)) {
- throw new Exception("Unexpected '$sEndSpec'") /* % self.sEndSpec,
- file=sFileIn, line=fIn.linenumber())*/
- }
- //hasher.update(l)
- l = fIn.readLine()
- }
- //curHash = hasher.hexdigest()
- if(l==null && !options.bEofCanBeEnd) {
- // We reached end of file before we found the end output line.
- throw new Exception("Missing '$sEndOutput' before end of file.")/* % self.sEndOutput,
- file=sFileIn, line=fIn.linenumber())*/
- }
- // Write the output of the spec to be the new output if we're
- // supposed to generate code.
-
- //hasher = md5.new()
- if(!options.bNoGenerate) {
- def sFile = "$sFileIn+$firstLineNum"
- def globals = []
- def sGen = gen.evaluate(this, globals, fname=sFile)
- //sGen = suffixLines(sGen)
- //hasher.update(sGen)
- if(sGen != '') {
- write(sGen)
- firstLine = true //TODO: get rid of this workaround
- }
- }
- //newHash = hasher.hexdigest()
-
- bSawCog = true
- /*
- // Write the ending output line
- hashMatch = self.reEndOutput.search(l)
- if self.options.bHashOutput:
- if hashMatch:
- oldHash = hashMatch.groupdict()['hash']
- if oldHash != curHash:
- raise CogError("Output has been edited! Delete old checksum to unprotect.",
- file=sFileIn, line=fIn.linenumber())
- # Create a new end line with the correct hash.
- endpieces = l.split(hashMatch.group(0), 1)
- else:
- # There was no old hash, but we want a new hash.
- endpieces = l.split(self.sEndOutput, 1)
- l = (self.sEndFormat % newHash).join(endpieces)
- else:
- # We don't want hashes output, so if there was one, get rid of
- # it.
- if hashMatch:
- l = l.replace(hashMatch.groupdict()['hashsect'], '', 1)
- */
- if(!options.bDeleteCode) {
- write(l)
- }
- l = fIn.readLine()
- }
- if(!bSawCog && options.bWarnEmpty) {
- println "no cog code found in $sFileIn"
- }
- }
- /*
- // A regex for non-empty lines, used by suffixLines.
- def reNonEmptyLines = re.compile(/^\s*\S+.*$/, re.MULTILINE)
-
- def suffixLines(text) {
- """ Add suffixes to the lines in text, if our options desire it.
- text is many lines, as a single string.
- """
- if(options.sSuffix) {
- // Find all non-blank lines, and add the suffix to the end.
- repl = /\g<0>/ + options.sSuffix.replaceAll('\\', '\\\\')
- text = self.reNonEmptyLines.sub(repl, text)
- }
- return text
- }
- */
- /**
- * @param args
- */
- static def void main(String[] args){
- def c = new GCog()
- if(args.size()> 0) {
- def f = args[0]
- c.processOneFile(f,f)
- }
- }
-
- }