Permalink
Browse files

ConsumingMatchParser, parse results as final classes

  • Loading branch information...
1 parent b9e659e commit 07d400fb8d06c0c04708bb5b1e68d9262d02f678 @freels committed Mar 1, 2012
@@ -4,6 +4,23 @@ import org.jboss.netty.buffer.ChannelBuffer
import com.twitter.finagle.parser.util.Matcher
+class ConsumingMatchParser(matcher: Matcher) extends Parser[Int] {
+ def decodeRaw(buffer: ChannelBuffer) = {
+ val size = matcher.bytesMatching(buffer, buffer.readerIndex)
+
+ if (size < 0) {
+ if (size == -1) {
+ throw Continue(this)
+ } else {
+ throw Fail(() => "Matcher %s failed." format matcher)
+ }
+ }
+
+ buffer.skipBytes(size)
+ size
+ }
+}
+
class MatchParser(matcher: Matcher) extends Parser[Int] {
def decodeRaw(buffer: ChannelBuffer) = {
val size = matcher.bytesMatching(buffer, buffer.readerIndex)
@@ -19,3 +36,46 @@ class MatchParser(matcher: Matcher) extends Parser[Int] {
size
}
}
+
+class Match1ByteParser(byte: Byte) extends Parser[Int] {
+ def decodeRaw(buffer: ChannelBuffer) = {
+ try {
+ val b = buffer.readByte
+ if (byte != b) {
+ buffer.readerIndex(buffer.readerIndex - 1)
+ throw Fail(() => "Match for '%s' failed." format byte)
+ }
+
+ 1
+ } catch {
+ case e: IndexOutOfBoundsException => throw Continue(this)
+ }
+ }
+}
+
+
+class Match2ByteParser(byte1: Byte, byte2: Byte) extends Parser[Int] {
+ def decodeRaw(buffer: ChannelBuffer) = {
+ val start = buffer.readerIndex
+
+ try {
+ val b1 = buffer.readByte
+ if (byte1 != b1) {
+ buffer.readerIndex(start)
+ throw Fail(() => "Match for '%s%s' failed." format (byte1, byte2))
+ }
+
+ val b2 = buffer.readByte
+ if (byte2 != b2) {
+ buffer.readerIndex(start)
+ throw Fail(() => "Match for '%s%s' failed." format (byte1, byte2))
+ }
+
+ 2
+ } catch {
+ case e: IndexOutOfBoundsException =>
+ buffer.readerIndex(start)
+ throw Continue(this)
+ }
+ }
+}
@@ -4,7 +4,7 @@ package com.twitter.finagle.parser.incremental
sealed trait ParseResult[+Out]
-abstract class ParseException extends Throwable {
+sealed abstract class ParseException extends Throwable {
def message: String
override def fillInStackTrace(): Throwable = null
@@ -14,16 +14,16 @@ abstract class ParseException extends Throwable {
override def getMessage() = message
}
-case class Return[@specialized +Out](ret: Out) extends ParseResult[Out]
+final case class Return[@specialized +Out](ret: Out) extends ParseResult[Out]
-case class Continue[+Out](next: Parser[Out]) extends ParseException with ParseResult[Out] {
+final case class Continue[+Out](next: Parser[Out]) extends ParseException with ParseResult[Out] {
def message = "Insufficient data."
}
-case class Fail(messageString: () => String) extends ParseException with ParseResult[Nothing] {
+final case class Fail(messageString: () => String) extends ParseException with ParseResult[Nothing] {
def message = messageString()
}
-case class Error(messageString: () => String) extends ParseException with ParseResult[Nothing] {
+final case class Error(messageString: () => String) extends ParseException with ParseResult[Nothing] {
def message = messageString()
}
@@ -164,12 +164,12 @@ abstract class Parser[+Out] {
/**
* Returns a parser that discards the result of this and returns `value`.
*/
- def then[T](value: T): Parser[T] = new ConstParser(this, value)
+ def thenReturn[T](value: T): Parser[T] = new ConstParser(this, value)
/**
* Returns a parser that discards the result of `skipped` and returns the result of parsing this.
*/
- def thenSkip(skipped: Parser[_]): Parser[Out] = this flatMap { rv => skipped then rv }
+ def thenSkip(skipped: Parser[_]): Parser[Out] = this flatMap { rv => skipped thenReturn rv }
def and[T, C <: ChainableTuple](rhs: Parser[T])(implicit chn: Out => C): Parser[C#Next[T]] = {
for (tup <- this; next <- rhs) yield chn(tup).append(next)
@@ -214,9 +214,9 @@ abstract class Parser[+Out] {
def >>=[T](f: Out => Parser[T]) = this flatMap f
/**
- * Equivalent to `this then value`
+ * Equivalent to `this thenReturn value`
*/
- def ^[T](value: T) = this then value
+ def ^[T](value: T) = this thenReturn value
/**
* Equivalent to `this map f
@@ -622,30 +622,31 @@ object Parser {
*
* Returns a ChannelBuffer of the bytes matched by `m`.
*/
- def accept(m: Matcher) = new MatchParser(m) flatMap { readBytes(_) }
-
- implicit def acceptString(choice: String) = {
- val bytes = choice.getBytes("US-ASCII")
- new MatchParser(new BytesMatcher(bytes)) then skipBytes(bytes.size)
- }
+ def accept(m: Matcher) = new ConsumingMatchParser(m)
/**
* Succeed parsing if the input buffer starts with `choice`.
*
- * Returns a ChannelBuffer of the bytes matched by `choice`.
- */
- def accept(choice: String): Parser[ChannelBuffer] = {
- accept(new BytesMatcher(choice))
- // val m = new MatchParser(new BytesMatcher(choice))
- // m then skipBytes(choice.size) then success(choice)
+ * Returns the length of the match.
+ */
+ implicit def accept(choice: String): Parser[Int] = {
+ if (choice.length == 1) {
+ val bytes = choice.getBytes("US-ASCII")
+ new Match1ByteParser(bytes(0))
+ } else if (choice.length == 2) {
+ val bytes = choice.getBytes("US-ASCII")
+ new Match2ByteParser(bytes(0), bytes(1))
+ } else {
+ accept(new BytesMatcher(choice))
+ }
}
/**
* Succeed parsing if the input buffer starts with any of the alternatives.
*
- * Returns a ChannelBuffer of the bytes matched.
+ * Returns the length of the match.
*/
- def accept(first: String, second: String, rest: String*): Parser[ChannelBuffer] = {
+ def accept(first: String, second: String, rest: String*): Parser[Int] = {
accept(AlternateMatcher(first +: second +: rest))
}
@@ -1,9 +1,7 @@
package com.twitter.finagle.parser.util
-import com.twitter.finagle.parser.ParseException
import org.jboss.netty.buffer.{ChannelBuffers, ChannelBufferIndexFinder, ChannelBuffer}
-
object DecimalIntCodec {
val AsciiZero = 48.toByte
val MinIntBytes = Int.MinValue.toString.getBytes("US-ASCII")
@@ -15,8 +15,8 @@ object Redis {
case class Status(msg: ChannelBuffer) extends Reply
case class Error(msg: ChannelBuffer) extends Reply
case class Integer(i: Int) extends Reply
- case class Bulk(bytes: Option[ChannelBuffer]) extends Reply
- case class MultiBulk(bulks: Option[Seq[Bulk]]) extends Reply
+ case class Bulk(bytes: ChannelBuffer) extends Reply
+ case class MultiBulk(bulks: Seq[Bulk]) extends Reply
}
def decodeInt(buf: ChannelBuffer): Int = {
@@ -46,27 +46,31 @@ object Redis {
0
}
- val CRLF = acceptString("\r\n")
- val readInt = withRawBuffer { decodeInt(_) }
- val readLine = readTo("\r\n")
- val returnNull = success(null)
+ val CRLF = skipBytes(2)//accept("\r\n")
+ val readInt = withRawBuffer { decodeInt(_) }
+ val readLine = readTo("\r\n")
- val readStatusReply = "+" then readLine map { Reply.Status(_) }
-
- val readErrorReply = "-" then readLine map { Reply.Error(_) }
-
- val readIntegerReply = ":" then readInt map { Reply.Integer(_) }
-
- val readBulkReply = "$" then readInt flatMap { count =>
- if (count == -1) returnNull else readBytes(count)
- } thenSkip CRLF map { bulk =>
- Reply.Bulk(Option(bulk))
+ val readStatusReply = "+" then readLine map { Reply.Status(_) }
+ val readErrorReply = "-" then readLine map { Reply.Error(_) }
+ val readIntegerReply = ":" then readInt map { Reply.Integer(_) }
+ val readBulkReply = "$" then readInt flatMap { count =>
+ if (count == -1) {
+ success(null)
+ } else {
+ readBytes(count) thenSkip CRLF
+ }
+ } map { b =>
+ Reply.Bulk(b)
}
val readMultiBulkReply = "*" then readInt flatMap { count =>
- if (count == -1) returnNull else repN(count, readBulkReply)
+ if (count == -1) {
+ success(null)
+ } else {
+ repN(count, readBulkReply)
+ }
} map { bulks =>
- Reply.MultiBulk(Option(bulks))
+ Reply.MultiBulk(bulks)
}
val readReply: Parser[Reply] = {
@@ -118,15 +118,15 @@ object ParserSpec extends ParserSpecification {
}
"accept" in {
- val parser = Parser.accept("foo") map asString
-
- parser mustParse "f" andContinue() readingBytes(0)
- parser mustParse "fo" andContinue() readingBytes(0)
- parser mustParse "foo" andReturn("foo") readingBytes(3)
- parser mustParse "foox" andReturn("foo") readingBytes(3)
- parser mustParse "x" andFail() readingBytes(0)
- parser mustParse "fx" andFail() readingBytes(0)
- parser mustParse "fox" andFail() readingBytes(0)
+ val parser = Parser.accept("foo")
+
+ parser mustParse "f" andContinue() readingBytes(0)
+ parser mustParse "fo" andContinue() readingBytes(0)
+ parser mustParse "foo" andReturn(3) readingBytes(3)
+ parser mustParse "foox" andReturn(3) readingBytes(3)
+ parser mustParse "x" andFail() readingBytes(0)
+ parser mustParse "fx" andFail() readingBytes(0)
+ parser mustParse "fox" andFail() readingBytes(0)
}
"choice" in {
@@ -149,19 +149,19 @@ object ParserSpec extends ParserSpecification {
}
"rep" in {
- val parser = rep(Parser.accept("foo") map asString)
+ val parser = rep(Parser.accept("foo"))
parser mustParse "xx" andReturn List()
- parser mustParse "foox" andReturn List("foo")
- parser mustParse "foofoox" andReturn List("foo", "foo")
+ parser mustParse "foox" andReturn List(3)
+ parser mustParse "foofoox" andReturn List(3, 3)
}
"repsep" in {
- val parser = repsep(Parser.accept("foo") map asString, " ")
+ val parser = repsep(Parser.accept("foo"), " ")
parser mustParse " dddddd" andReturn List()
- parser mustParse "fooddddd" andReturn List("foo")
- parser mustParse "foo foodddddd" andReturn List("foo", "foo")
+ parser mustParse "fooddddd" andReturn List(3)
+ parser mustParse "foo foodddddd" andReturn List(3, 3)
}
"readByte" in {

0 comments on commit 07d400f

Please sign in to comment.