Small problem with SourceUnit.failedWithUnexpectedEOF()

classic Classic list List threaded Threaded
3 messages Options
Reply | Threaded
Open this post in threaded view
|

Small problem with SourceUnit.failedWithUnexpectedEOF()

Jason Dillon
Seems like this does not return true for things like:

<snip>
class a {
def b() {
</snip>

Which is causing some problems for a go-less groovysh.

Anyone know how to fix this?

--jason

---------------------------------------------------------------------
To unsubscribe from this list please visit:

    http://xircles.codehaus.org/manage_email

Reply | Threaded
Open this post in threaded view
|

Re: Small problem with SourceUnit.failedWithUnexpectedEOF()

Jochen Theodorou
Jason Dillon schrieb:

> Seems like this does not return true for things like:
>
> <snip>
> class a {
> def b() {
> </snip>
>
> Which is causing some problems for a go-less groovysh.
>
> Anyone know how to fix this?
I remember there was a recognition of this case in the old shell. I mean
it recognized the exception and did then something different... The
toher way would surely be to count those enclosing symbols... This was
the approach of my last try of a more interactive console.

I wrote the attached class. It may help, but be warned, this was
bleeding edge devolpment. Besides the attached testcases I tested
nothing, it is well possible the code contains big bugs.

bye blackdrag

--
Jochen "blackdrag" Theodorou
Groovy Tech Lead (http://groovy.codehaus.org)
http://blackdragsview.blogspot.com/

/** License ASL 2 **/

/**
* class to determine if a groovy source contains missing block elements
* @author Jochen Theodorou
*/
class InputParser {
    // parse errors (if a block is not closed for example)
        def errors = []
    // stack of open block identifiers
        def stack = new LinkedList(["x"])
        // the current line we parser (older lines are not stored)              
        private currentLine
         
        /**
        *  gives a new line to the parser and automatically clears the
        *  errors of the last parsed line. If an parse error occurd
        *  the stack will not contain the ifnormations for the new line.
        */
        def updateState(line) {
          errors.clear()
          currentLine = line.trim()
          def oldStack = stack.clone()
          parseExpression(0)
          if (errors) stack = oldStack
        }
       
    /**
    *  parse any expression.
    */
        private parseExpression(offset) {
          def lineOffset = offset
          while (lineOffset<currentLine.size() && !errors) {
                  switch(stack.getLast()) {
                  case "/*"  : lineOffset = skipMultiComment(lineOffset); break
                  case "{"   : lineOffset = matchBracket(lineOffset,"}"); break
                  case "("   : lineOffset = matchBracket(lineOffset,")"); break
                  case "["   : lineOffset = matchBracket(lineOffset,"]"); break
                  case "x"   : lineOffset = matchBracket(lineOffset,null); break
                  case "'''" : lineOffset = matchString(lineOffset,"'''",true); break
                  case '"""' : lineOffset = matchString(lineOffset,'"""',true); break
                  case "'"   : lineOffset = matchString(lineOffset,"'",false); break
                  case '"'   : lineOffset = matchString(lineOffset,'"',false); break
                  case "op"  :
                  case '//'  : lineOffset = currentLine.size(); stack.removeLast(); break
                  case "\$"  : lineOffset = matchGString(lineOffset); break
                  default: errors << "unexpected token at "+lineOffset; lineOffSet = currentLine.size(); break
                  }
          }
          if (stack.getLast()=="'" || stack.getLast()=='"') errors << "string is not terminated with "+stack.getLast()
        }
       
    /**
    * skips the multiline comment
    */
        def skipMultiComment(offset) {
                def index = currentLine.indexOf("*/",offset)
                if (index == -1) return currentLine.size()
                stack.removeLast();
                return index+2
        }
       
    /**
    * match a bracket, which is started by one of /{(['". Strings
    * with handled as brackets with one delimeter, "/" stands for
    * a comment. This method is used if the current position is at
    * a braket.
    */
        def matchBracket(offset,end) {
                if (currentLine.size()-offset==0) return currentLine.size()
                for (pos in offset..<currentLine.size()) {
                        def c = currentLine[pos]
                        def tokens = "/{(['\""
                def falseTokens = new StringBuffer("})]")
                if (end) {
                tokens += end
                int findex = falseTokens.indexOf(end)
                if (findex != -1) {
                falseTokens.deleteCharAt(findex)
                }
                }
                        if (falseTokens.indexOf(c)!=-1) {
                                errors << "unexpected closing bracket $c for $end"
                                return
                        }
                        if (tokens.indexOf(c)==-1) continue
                        if (c==end) {stack.removeLast(); return pos+1}
                        return matchSignificantToken(pos,c)
                }
            if ("+-/%*\\".indexOf(currentLine[-1])!=-1) stack << "op"
            return currentLine.size()
        }
               
    /**
    *  match a certain token (c contains the value of the token)
    */
        def matchSignificantToken(offset,c) {
                def hasOneMore = currentLine.size()>offset+1
                def hasTwoMore = currentLine.size()>offset+2
                switch (c) {
                        case "(": case "[": case "{": stack << c; return offset+1
                        case "/":
                                if (hasOneMore) {
                                        if (currentLine[offset+1]=='*') {stack << "/*"; return offset+2}
                                        if (currentLine[offset+1]=='/') {stack << "//"; return offset+2}
                                } else {
                                        stack << "x"; return offset
                                }
                                return offset+1
                        case "'":
                                if (hasTwoMore && currentLine[offset..<offset+3]=="'''") {
                                        stack << "'''"; return offset+3
                                } else {
                                        stack << "'"; return offset+1
                                }
                        case '"':
                                if (hasTwoMore && currentLine[offset..<offset+3]=='"""') {
                                        stack << '"""'; return offset+3
                                } else {
                                        stack << '"'; return offset+1
                                }
                }
            errors << "BUG: unexpected end of matchSignificantToken"
            return offset
        }
               
        /**
        *  match the contents of a String, including GString specialities
        */
        def matchString(offset,stringDelimeter,mString) {
                def stringPart = currentLine.substring(offset)
                def isEscaping = false
                def allowGString = stringDelimeter.startsWith('"')
                def stringDelimeterMatch=0
                for (it in stringPart) {
                        offset++
                        if (isEscaping) {
                                isEscaping = false
                        } else if (it=='\\') {
                                isEscaping = true
                        } else if (allowGString && it=='$' && offset<currentLine.size()){
                                stack << "\$"
                                return offset
                        } else if (stringDelimeter[stringDelimeterMatch]==it) {
                                stringDelimeterMatch++
                                if (stringDelimeterMatch==stringDelimeter.size()) {
                                        stack.removeLast()
                                        return offset
                                }
                        } else if (stringDelimeterMatch>0) {
                                stringDelimeterMatch=0
                        }
                }
                if (!mString) {
                        errors << "end of string is missing"
                }
                return offset
        }
       
        /**
        *  match a GString
        */
        def matchGString(offset) {
                stack.removeLast()
                if (currentLine[offset] == '{') {
                        stack << '{'
                        return offset+1
                } else {
                        return matchBracket(offset,null)
                }
        }
}
class InputParserTest extends GroovyTestCase {
        static{ def a =1 }
  void testMultiComment() {
          def ip = new InputParser()
          ip.updateState("/*")
          assert ip.errors == []
          assert ip.stack == ["x","/*"]
          ip.updateState("*/")
          assert ip.errors == []
     assert ip.stack == ["x"]  
  }
 
  void testMultiLineWrapping() {
          def start = ["{","(","["]
          def end   = ["}",")","]"]
          def items = [0,1,2,1,1,2,1,0,0,0,2,2]
          items.size().times { lengthn ->
              def length = 2
                  def list = items[0..<length]
              def ip = new InputParser()
              def txt = new StringBuffer()
                  list.each {
             ip.updateState(start[it])
             txt << start[it]
              }
                  assert ip.errors == []
                  list.eachWithIndex { item, index ->
                    assert ip.stack[index+1] == start[item]
                  }
              list.reverse().each {
             ip.updateState(end[it])
             txt << end[it]
             }
              assert ip.errors == []
              assert ip.stack == ["x"]  
             
              ip.updateState(txt.toString())
              assert ip.errors == []
         assert ip.stack == ["x"]  
          }
  }
 
  void testLineComment() {
          def ip = new InputParser()
          ip.updateState("//(")
      assert ip.errors == []
      assert ip.stack == ["x"]    
  }
 
  void testStringError() {
          def ip = new InputParser()
          ip.updateState("'")
      assert ip.errors.size() == 1
      assert ip.stack == ["x"]
     
      ip.updateState("def a == null")
      assert ip.errors == []
      assert ip.stack == ["x"]
                         
      ip.updateState('"')
      assert ip.errors.size() == 1
      assert ip.stack == ["x"]
  }
 
  void testGString() {
          def ip = new InputParser()
          ip.updateState('def b=null; def a ="""${b')
      assert ip.errors == []
      assert ip.stack == ["x",'"""',"{"]
      ip.updateState('}"""')
      assert ip.errors == []
      assert ip.stack == ["x"]  
  }
         
  void testWrongClose() {
          def ip = new InputParser()
          ip.updateState('((')
      assert ip.errors == []
      assert ip.stack == ["x",'(',"("]
      ip.updateState('}')
      assert ip.errors.size() > 0
      assert ip.stack == ["x",'(',"("]
  }
 
 
}
---------------------------------------------------------------------
To unsubscribe from this list please visit:

    http://xircles.codehaus.org/manage_email
Reply | Threaded
Open this post in threaded view
|

Re: Small problem with SourceUnit.failedWithUnexpectedEOF()

Guillaume Laforge
Administrator
I haven't catch up on email yet after a long week-end offline, but I
played a bit with the idea of a new interactive shell. I've attached a
version to this email (shout if the attachment is stripped by the list
server).

No need to type go anymore.
Imports are remembered and can be discarded.
No more bug in groovysh when you create a class.
Methods are also remembered as method closures in the binding.


On 8/20/07, Jochen Theodorou <[hidden email]> wrote:

> Jason Dillon schrieb:
> > Seems like this does not return true for things like:
> >
> > <snip>
> > class a {
> > def b() {
> > </snip>
> >
> > Which is causing some problems for a go-less groovysh.
> >
> > Anyone know how to fix this?
>
> I remember there was a recognition of this case in the old shell. I mean
> it recognized the exception and did then something different... The
> toher way would surely be to count those enclosing symbols... This was
> the approach of my last try of a more interactive console.
>
> I wrote the attached class. It may help, but be warned, this was
> bleeding edge devolpment. Besides the attached testcases I tested
> nothing, it is well possible the code contains big bugs.
>
> bye blackdrag
>
> --
> Jochen "blackdrag" Theodorou
> Groovy Tech Lead (http://groovy.codehaus.org)
> http://blackdragsview.blogspot.com/
>
> ---------------------------------------------------------------------
> To unsubscribe from this list please visit:
>
>     http://xircles.codehaus.org/manage_email
>
>

--
Guillaume Laforge
Groovy Project Manager
http://glaforge.free.fr/blog/groovy

---------------------------------------------------------------------
To unsubscribe from this list please visit:

    http://xircles.codehaus.org/manage_email

newsh.groovy (7K) Download Attachment