├── .gitignore ├── buildfile └── src └── main └── scala └── edu └── uwm └── cs └── dc └── Parsers.scala /.gitignore: -------------------------------------------------------------------------------- 1 | target/ 2 | -------------------------------------------------------------------------------- /buildfile: -------------------------------------------------------------------------------- 1 | Buildr.settings.build['scala.version'] = '2.8.1' 2 | 3 | require 'buildr/scala' 4 | 5 | define 'derivative-combinators' do 6 | project.group = 'edu.uwm.cs' 7 | project.version = '0.1-SNAPSHOT' 8 | 9 | package 10 | end 11 | -------------------------------------------------------------------------------- /src/main/scala/edu/uwm/cs/dc/Parsers.scala: -------------------------------------------------------------------------------- 1 | package edu.uwm.cs.dc 2 | 3 | import scala.collection.mutable 4 | 5 | trait Parsers { 6 | val epsilon: Parser = new Parser { 7 | val isNullable = true 8 | 9 | def derive(c: Char) = empty 10 | 11 | def innerToString(visited: Set[Parser]) = ("<>", visited) 12 | } 13 | 14 | private val empty = new Parser { 15 | val isNullable = false 16 | 17 | def derive(c: Char) = error("Cannot derive the empty parser") 18 | 19 | override def ~(that: =>Parser) = this 20 | 21 | def innerToString(visited: Set[Parser]) = ("{}", visited) 22 | } 23 | 24 | implicit def literal(c: Char): Parser = LiteralParser(c) 25 | 26 | implicit def unionSyntax(c: Char): RichParser = unionSyntax(literal(c)) 27 | 28 | implicit def unionSyntax(left: =>Parser): RichParser = new RichParser(left) 29 | 30 | 31 | class RichParser(left: =>Parser) { 32 | def |(right: =>Parser): Parser = new UnionParser(left, right) 33 | } 34 | 35 | 36 | trait Parser extends (Stream[Char] => Boolean) { 37 | def isNullable: Boolean 38 | 39 | def derive(c: Char): Parser 40 | 41 | def apply(str: Stream[Char]) = str match { 42 | case hd #:: tail => derive(hd)(tail) 43 | 44 | case Stream() => isNullable 45 | } 46 | 47 | def ~(that: =>Parser): Parser = new ConcatParser(this, that) 48 | 49 | override def toString = innerToString(Set())._1 50 | 51 | def innerToString(visited: Set[Parser]): (String, Set[Parser]) 52 | } 53 | 54 | class UnionParser(_left: =>Parser, _right: =>Parser) extends Parser with MemoizedDerivation { 55 | lazy val left = _left 56 | lazy val right = _right 57 | 58 | private var _isNullable: Option[Boolean] = None 59 | 60 | def isNullable = _isNullable getOrElse { 61 | _isNullable = Some(false) 62 | var back = left.isNullable || right.isNullable 63 | _isNullable = Some(back) 64 | 65 | // try to achieve a fixpoint 66 | while ((left.isNullable || right.isNullable) != back) { 67 | back = left.isNullable || right.isNullable 68 | _isNullable = Some(back) 69 | } 70 | 71 | back 72 | } 73 | 74 | def innerDerive(c: Char) = { 75 | if (left == empty && right == empty) 76 | empty 77 | else if (left == empty) 78 | right.derive(c) 79 | else if (right == empty) 80 | left.derive(c) 81 | else 82 | left.derive(c) | right.derive(c) 83 | } 84 | 85 | def innerToString(visited: Set[Parser]) = { 86 | if (visited contains this) 87 | (hashCode.toString, visited) 88 | else { 89 | val (leftStr, leftVisit) = left.innerToString(visited + this) 90 | val (rightStr, rightVisit) = right.innerToString(leftVisit) 91 | ("(| " + leftStr + " " + rightStr + ")", rightVisit) 92 | } 93 | } 94 | } 95 | 96 | class ConcatParser(_left: =>Parser, _right: =>Parser) extends Parser { 97 | lazy val left = _left 98 | lazy val right = _right 99 | 100 | def isNullable = left.isNullable && right.isNullable 101 | 102 | def derive(c: Char) = { 103 | if (left.isNullable) 104 | left.derive(c) ~ right | right.derive(c) 105 | else 106 | left.derive(c) ~ right 107 | } 108 | 109 | def innerToString(visited: Set[Parser]) = { 110 | val (leftStr, leftVisited) = left.innerToString(visited) 111 | val (rightStr, rightVisited) = right.innerToString(visited) 112 | ("(~ " + leftStr + " " + rightStr + ")", rightVisited) 113 | } 114 | } 115 | 116 | case class LiteralParser(c: Char) extends Parser { 117 | val isNullable = false 118 | 119 | def derive(c: Char) = 120 | if (this.c == c) epsilon else empty 121 | 122 | def innerToString(visited: Set[Parser]) = (c.toString, visited) 123 | } 124 | 125 | trait MemoizedDerivation extends Parser { 126 | private val derivations = mutable.Map[Char, Parser]() 127 | 128 | final def derive(c: Char) = derivations get c getOrElse { 129 | val back = innerDerive(c) 130 | derivations(c) = back 131 | 132 | back 133 | } 134 | 135 | protected def innerDerive(c: Char): Parser 136 | } 137 | } 138 | --------------------------------------------------------------------------------