Skip to content

Instantly share code, notes, and snippets.

@fizzy33
Created October 27, 2012 16:09
Show Gist options
  • Save fizzy33/3965184 to your computer and use it in GitHub Desktop.
Save fizzy33/3965184 to your computer and use it in GitHub Desktop.
Chord's an ast for memoizing and reifying string ops
object Chord {
import Impl._
def ~(s: String): Chord = stringToChord(s)
implicit def stringToChord(s: String): Chord = ChordedString(s)
case class PimpedIterable[T](iter: Iterable[T]) {
def mkChord(seperator: String) = {
val seperatorChord: Chord = seperator
iter.foldLeft(emptyChord) { (acc, i) =>
(acc, i) match {
case (EmptyChord, ch: Chord) => ch
case (_, ch: Chord) => acc ~ seperatorChord ~ ch
case (EmptyChord, _) => ChordedString(String.valueOf(i))
case (_, _) => acc ~ seperatorChord ~ i.toString
}
}
}
}
implicit def pimpedIterable[T](iter: Iterable[T]) = PimpedIterable(iter)
val emptyChord: Chord = EmptyChord
object Impl {
case object EmptyChord extends Chord {
override def writeTo(csb: ChordStringBuilder) = {
}
}
case class CatStr(left: Chord, right: String) extends Chord {
override def writeTo(csb: ChordStringBuilder) = {
left.writeTo(csb)
csb.append(right)
}
}
case class TrunCatStr(left: Chord, right: String, separator: String = "") extends Chord {
override def writeTo(csb: ChordStringBuilder) = {
left.writeTo(csb)
csb.trimRight
csb.append(separator)
csb.trimIncomingWhitespace = true
csb.append(right)
csb.trimIncomingWhitespace = false
}
}
case class Cat(left: Chord, right: Chord) extends Chord {
override def writeTo(csb: ChordStringBuilder) = {
left.writeTo(csb)
right.writeTo(csb)
}
}
case class TrunCat(left: Chord, right: Chord, separator: String = "") extends Chord {
override def writeTo(csb: ChordStringBuilder) = {
left.writeTo(csb)
csb.trimRight
csb.append(separator)
csb.trimIncomingWhitespace = true
right.writeTo(csb)
csb.trimIncomingWhitespace = false
}
}
case class TrimRight(chord: Chord) extends Chord {
override def writeTo(csb: ChordStringBuilder) = {
chord.writeTo(csb)
csb.trimRight
}
}
case class TrimLeadingWhitespace(chord: Chord) extends Chord {
override def writeTo(csb: ChordStringBuilder) = {
csb.trimIncomingWhitespace = true
chord.writeTo(csb)
csb.trimIncomingWhitespace = false
}
}
case class ChordedString(value: String) extends Chord {
override def writeTo(csb: ChordStringBuilder) = {
csb.append(value)
}
}
class ChordStringBuilder {
val sb = new StringBuilder
var start = 0
var trimIncomingWhitespace = false
def charAt(i: Int) = sb.charAt(i)
def length = sb.length
def trimRight = {
val end = sb.length
var trimStart = end
while (trimStart > start && Character.isWhitespace(sb.charAt(trimStart - 1))) {
trimStart -= 1
}
if (trimStart != end)
sb.delete(trimStart, end)
}
def append(s: String) = {
if (trimIncomingWhitespace) {
var i = 0
while (i < s.length && Character.isWhitespace(s.charAt(i))) {
i += 1
}
while (i < s.length) {
trimIncomingWhitespace = false
sb.append(s.charAt(i))
i += 1
}
} else {
sb.append(s)
}
}
override def toString = sb.substring(start)
}
}
}
trait Chord {
import Chord.Impl._
/**
* concatenate with a string equivalent of s0 + s1
*/
def ~(s: String): Chord = CatStr(this, s)
/**
* concatenate with a chord equivalent of s0 + s1
*/
def ~(c: Chord): Chord = Cat(this, c)
/**
* concatenate with a string equivalent of trimRight(s0) + trimLeft(s1)
*/
def ~~(s: String): Chord = TrunCatStr(this, s)
/**
* concatenate with a chord equivalent of trimRight(s0) + trimLeft(s1)
*/
def ~~(c: Chord): Chord = TrunCat(this, c)
/**
* concatenate with a string equivalent of trimRight(s0) + " " + trimLeft(s1)
*/
def ~*~(s: String): Chord = TrunCatStr(this, s, " ")
/**
* concatenate with a chord equivalent of trimRight(s0) + " " + trimLeft(s1)
*/
def ~*~(c: Chord): Chord = TrunCat(this, c, " ")
def trimRight: Chord = TrimRight(this)
def trimLeft: Chord = TrimLeadingWhitespace(this)
def trim = trimLeft.trimRight
def asString = {
val csb = new ChordStringBuilder
writeTo(csb)
csb.toString
}
def originalToString =
super.toString
def writeTo(csb: ChordStringBuilder)
override def toString = asString
}
import Chord._
import org.scalatest.junit.JUnitSuite
import org.junit.Assert._
import org.junit.Test
class ChordTest extends JUnitSuite {
@Test def chordSquiglyStarSquigly {
val tests = List[((Chord, Chord), String)](
(("a", "b"), "a b")
, (("a ", "b"), "a b")
, (("a ", " b"), "a b")
, (("a", " b"), "a b")
, (("a ", "b"), "a b")
, (("a", " b"), "a b")
, (("a ", " b"), "a b")
)
runTests(tests) { input =>
(input._1 ~*~ input._2).asString
}
}
@Test def stringSquiglyStarSquigly {
val tests = List[((Chord, String), String)](
(("a", "b"), "a b")
, (("a ", "b"), "a b")
, (("a ", " b"), "a b")
, (("a", " b"), "a b")
, (("a ", " b"), "a b")
, (("a", " b"), "a b")
, (("a ", " b"), "a b")
)
runTests(tests) { input =>
(input._1 ~*~ input._2).asString
}
}
@Test def mkChord {
assertEquals("a 1 2 b", ("a" ~*~ List("1": Chord,"2": Chord).mkChord(" ") ~*~ "b").toString)
assertEquals("a b c", ("a" ~*~ "b" ~*~ List().mkChord(" ") ~*~ "c").toString)
}
@Test def chordSquiglySquigly {
val tests = List[((Chord, Chord), String)](
(("a", "b"), "ab")
, (("a ", "b"), "ab")
, (("a ", " b"), "ab")
, (("a", " b"), "ab")
, (("a", " b"), "ab")
, (("a ", "b"), "ab")
, (("a ", " b"), "ab")
)
runTests(tests) { input =>
(input._1 ~~ input._2).asString
}
}
@Test def stringSquiglySquigly {
val tests = List[((Chord, String), String)](
(("a", "b"), "ab")
, (("a ", "b"), "ab")
, (("a ", " b"), "ab")
, (("a", " b"), "ab")
, (("a", " b"), "ab")
, (("a ", "b"), "ab")
, (("a ", " b"), "ab")
)
runTests(tests) { input =>
(input._1 ~~ input._2).asString
}
}
@Test def chordSquigly {
val tests = List[(List[Chord], String)](
(List("a", "b"), "ab")
, (List("a ", "b"), "a b")
, (List("a ", " b"), "a b")
, (List("a", " b"), "a b")
, (List("a", " b"), "a b")
, (List("a ", "b"), "a b")
, (List("a ", " b"), "a b")
)
runTests(tests) { input =>
input.foldLeft("": Chord)((acc, s) => acc ~ s).asString
}
}
@Test def stringSquigly {
val tests = List[((Chord, String), String)](
(("a", "b"), "ab")
, (("a ", "b"), "a b")
, (("a ", " b"), "a b")
, (("a", " b"), "a b")
, (("a", " b"), "a b")
, (("a ", "b"), "a b")
, (("a ", " b"), "a b")
)
runTests(tests) { input =>
(input._1 ~ input._2).asString
}
}
def runTests[TInput,TOutput](tests: List[(TInput,TOutput)])( code: TInput => TOutput ) = {
tests.foreach { case (input, expectedOutput) =>
val actualOutput = code(input)
assertEquals(expectedOutput, actualOutput)
}
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment