summaryrefslogtreecommitdiff
path: root/api
diff options
context:
space:
mode:
authorLi Haoyi <haoyi@dropbox.com>2014-11-01 00:56:12 -0700
committerLi Haoyi <haoyi@dropbox.com>2014-11-01 00:56:12 -0700
commit3d73267c4b3ecf3cdca54ded8dfd8a2caeeb3ca9 (patch)
tree9f1846359c784a6e4bfc4682fddc8b6c884bf811 /api
parent058e9bac2dd8166051bafa16d51f27ee5856929a (diff)
downloadhands-on-scala-js-3d73267c4b3ecf3cdca54ded8dfd8a2caeeb3ca9.tar.gz
hands-on-scala-js-3d73267c4b3ecf3cdca54ded8dfd8a2caeeb3ca9.tar.bz2
hands-on-scala-js-3d73267c4b3ecf3cdca54ded8dfd8a2caeeb3ca9.zip
works, with a compiler-plugin based architecture supporting incremental compilation
Diffstat (limited to 'api')
-rw-r--r--api/src/main/scala/twist/Util.scala10
-rw-r--r--api/src/main/scala/twist/package.scala88
-rw-r--r--api/src/main/scala/twist/stages/Compiler.scala161
-rw-r--r--api/src/main/scala/twist/stages/IndentHandler.scala105
-rw-r--r--api/src/main/scala/twist/stages/Parser.scala790
-rw-r--r--api/src/test/scala/twist/AdvancedTests.scala120
-rw-r--r--api/src/test/scala/twist/BasicTests.scala418
-rw-r--r--api/src/test/scala/twist/ErrorTests.scala321
-rw-r--r--api/src/test/scala/twist/ParserTests.scala50
-rw-r--r--api/src/test/scala/twist/TestUtil.scala16
10 files changed, 0 insertions, 2079 deletions
diff --git a/api/src/main/scala/twist/Util.scala b/api/src/main/scala/twist/Util.scala
deleted file mode 100644
index c7b3598..0000000
--- a/api/src/main/scala/twist/Util.scala
+++ /dev/null
@@ -1,10 +0,0 @@
-package twist
-import acyclic.file
-
-object Util {
-
- implicit class Pipeable[T](t: T){
- def |>[V](f: T => V): V = f(t)
- }
-}
-
diff --git a/api/src/main/scala/twist/package.scala b/api/src/main/scala/twist/package.scala
deleted file mode 100644
index a70212a..0000000
--- a/api/src/main/scala/twist/package.scala
+++ /dev/null
@@ -1,88 +0,0 @@
-import scala.reflect.internal.util.{BatchSourceFile, SourceFile, OffsetPosition}
-import scala.reflect.io.{PlainFile, AbstractFile}
-import scala.reflect.macros.{TypecheckException, Context}
-import scalatags.Text.all._
-import twist.stages.Compiler
-import scala.language.experimental.macros
-import acyclic.file
-
-package object twist {
- import Util._
- /**
- * Wraps the given string as a twist fragment.
- */
- def tw(expr: String): Frag = macro Internals.applyMacro
- def twf(filename: String): Frag = macro Internals.applyMacroFile
- object Internals {
-
- def twDebug(expr: String): Frag = macro applyMacroDebug
-
- def applyMacro(c: Context)(expr: c.Expr[String]): c.Expr[Frag] = applyMacroFull(c)(expr, false)
-
- def applyMacroDebug(c: Context)(expr: c.Expr[String]): c.Expr[Frag] = applyMacroFull(c)(expr, true)
-
- def applyMacroFile(c: Context)(filename: c.Expr[String]): c.Expr[Frag] = {
- import c.universe._
- val s = filename.tree
- .asInstanceOf[Literal]
- .value
- .value
- .asInstanceOf[String]
- val txt = io.Source.fromFile(s).mkString |> stages.IndentHandler
- val sourceFile = new BatchSourceFile(
- new PlainFile(s),
- txt.toCharArray
- )
-
- compileThing(c)(txt, sourceFile, 0, false)
- }
-
- case class DebugFailure(msg: String, pos: String) extends Exception(msg)
-
- private[this] def applyMacroFull(c: Context)(expr: c.Expr[String], runtimeErrors: Boolean): c.Expr[Frag] = {
- import c.universe._
- val s = expr.tree
- .asInstanceOf[Literal]
- .value
- .value
- .asInstanceOf[String]
- val stringStart =
- expr.tree
- .pos
- .lineContent
- .drop(expr.tree.pos.column)
- .take(2)
- compileThing(c)(
- s |> stages.IndentHandler,
- expr.tree.pos.source,
- expr.tree.pos.point + (if (stringStart == "\"\"") 1 else -1),
- runtimeErrors
- )
- }
- }
-
- def compileThing(c: Context)(s: String, source: SourceFile, point: Int, runtimeErrors: Boolean) = {
- import c.universe._
- def compile(s: String): c.Tree = {
-
-
- val realPos = new OffsetPosition(source, point).asInstanceOf[c.universe.Position]
-
- Compiler(c)(realPos, s |> stages.Parser)
- }
-
- import c.Position
- try {
- c.Expr(c.typeCheck(compile(s)))
- } catch {
- case e@TypecheckException(pos: Position, msg) =>
- if (!runtimeErrors) c.abort(pos, msg)
- else {
- val posMsg = pos.lineContent + "\n" + (" " * pos.column) + "^"
- c.Expr( q"""throw twist.Internals.DebugFailure($msg, $posMsg)""")
- }
- }
-
- }
-
-}
diff --git a/api/src/main/scala/twist/stages/Compiler.scala b/api/src/main/scala/twist/stages/Compiler.scala
deleted file mode 100644
index e00c373..0000000
--- a/api/src/main/scala/twist/stages/Compiler.scala
+++ /dev/null
@@ -1,161 +0,0 @@
-package twist.stages
-import acyclic.file
-
-import scala.reflect.macros.Context
-import scala.reflect.internal.util.{Position, OffsetPosition}
-
-/**
- * Walks the parsed AST, converting it into an un-structured Scala source-blob
- * which when compiled results in a function that can be used to generate the
- * given Frag at runtime.
- */
-object Compiler{
- val WN = TwistNodes
- def apply(c: Context)(literalPos: c.Position, template: WN.Template): c.Tree = {
-
- import c.universe._
- def fragType = tq"scalatags.Text.all.Frag"
- def posFor(offset: Int) = {
- new OffsetPosition(
- literalPos.source,
- offset
- ).asInstanceOf[c.universe.Position]
- }
- def compileTree(frag: WN.TemplateTree): Tree = {
-
-// println(frag)
- val fragPos = posFor(literalPos.point + frag.offset)
-
-// println(s"${frag.offset}\n${literalPos.point}\n${pos.point}\n$frag\n")
-
- val f: Tree = frag match {
- case WN.Plain(text, offset) => q"$text"
- case WN.Display(exp, offset) => compileTree(exp)
- case WN.Comment(msg, offset) => q""
- case WN.ScalaExp(Seq(WN.Simple(first, _), WN.Block(ws, args, content, _)), offset)
- if first.startsWith("for(") =>
- val fresh = c.fresh()
- val skeleton: Tree = c.parse(first + s"{$fresh}").asInstanceOf[Apply]
-// println("FIRST " + first)
- skeleton.foreach{x =>
- x
- if (x.pos != NoPosition) x.pos = posFor(x.pos.point + fragPos.point + 1)
- }
- val b = content.map(compileTree(_))
- def rec(t: Tree): Tree = t match {
- case a @ Apply(fun, List(f @ Function(vparams, body))) =>
- val f2 = Function(vparams, rec(body))
- val a2 = Apply(fun, List(f2))
- a2.pos = a.pos
- f2.pos = f.pos
- a2
- case Ident(x: TermName) if x.decoded == fresh =>
- q"Seq[$fragType](..$b)"
- }
-
- val out = rec(skeleton)
-
-
- out
-
- case WN.ScalaExp(WN.Simple(first, _) +: WN.Block(_, None, content1, _) +: rest, offset)
- if first.startsWith("if(") =>
-
- val b1 = content1.map(compileTree(_))
- val tree = c.parse(first + "{}").asInstanceOf[If]
- tree.foreach{x =>
- x.pos = posFor(x.pos.point + fragPos.point + 1)
- }
- val If(cond, _, _) = tree
- val b2 = rest match{
- case Seq(WN.Simple(next, _), WN.Block(_, None, content2, _)) =>
- content2.map(compileTree(_))
- case Seq() => Nil
- }
- q"if($cond){ Seq[$fragType](..$b1): $fragType } else { Seq[$fragType](..$b2): $fragType }"
-
- case xx @ WN.ScalaExp(WN.Simple(first, _) +: rest, offset) =>
- val firstTree = c.parse(first)
-
- firstTree.foreach{x =>
- x.pos = posFor(x.pos.point + fragPos.point)
- }
-
- val s = rest.foldLeft[Tree](firstTree) {
- case (l, WN.Simple(code, _)) =>
- val fresh = c.fresh()
-
- val snippet = s"$fresh$code"
- val skeleton = c.parse(snippet)
-
- def rec(t: Tree): Tree = {
- val newPos = posFor(fragPos.point + t.pos.point + first.length - fresh.length)
- val res = t match {
- case Apply(fun, args) =>
- for(arg <- args; tree <- arg if tree.pos != NoPosition){
- tree.pos = posFor(tree.pos.point + newPos.point - t.pos.point)
- }
-
- Apply(rec(fun), args)
- case Select(qualifier, name) => Select(rec(qualifier), name)
- case Ident(x: TermName) if x.decoded == fresh => l
- }
- res.pos = newPos
-
-// println(Position.formatMessage(newPos.asInstanceOf[scala.reflect.internal.util.Position], "", true))
- res
- }
-
-
- rec(skeleton)
-
- case (l, WN.Block(ws, None, content, _)) =>
- q"$l(..${content.map(compileTree(_))})"
-
- case (l, WN.Block(ws, Some(args), content, _)) =>
- val snippet = s"{$args ()}"
- val skeleton = c.parse(snippet)
- val Function(vparamss, body) = skeleton
-
- val func = Function(vparamss, q"Seq[$fragType](..${content.map(compileTree(_))})")
- func.pos = posFor(fragPos.point + skeleton.pos.point)
- q"$l($func)"
- }
-
- s
- }
-
-// f.foreach(_.pos = pos)
-
-// println("XXX " + f.pos)
-// println("F " + f)
- f
- }
-
- def compileTemplate(tmpl: WN.Template): Tree = {
- val WN.Template(name, comment, params, topImports, imports, subs, content, offset) = tmpl
- val fullName = if (name.toString == "") c.fresh("outer") else name
-
- val DefDef(mods, realName, tparams, vparamss, tpt, rhs) = {
- val snippet = s"def $fullName$params = {}"
- val z = c.parse(snippet).asInstanceOf[DefDef]
- z
- }
-
- val innerDefs = subs.map(compileTemplate(_))
-
- val body = atPos(literalPos)(q"""{
- import scalatags.Text.all._
- ..${topImports.map(i => c.parse(i.code))}
- ..$innerDefs
-
- Seq[scalatags.Text.all.Frag](..${content.map(compileTree(_))})
- }""")
-
- if (name.toString == "") body
- else DefDef(mods, realName, tparams, vparamss, tpt, body)
- }
-
- atPos(literalPos)(q"${compileTemplate(template)}")
- }
-} \ No newline at end of file
diff --git a/api/src/main/scala/twist/stages/IndentHandler.scala b/api/src/main/scala/twist/stages/IndentHandler.scala
deleted file mode 100644
index 1a59046..0000000
--- a/api/src/main/scala/twist/stages/IndentHandler.scala
+++ /dev/null
@@ -1,105 +0,0 @@
-package twist.stages
-
-import acyclic.file
-/**
- * Implements the offside-rule for Twirlite fragments.
- *
- * This stage walks over the whitespace before each line of the Twirlite,
- * augmenting it with the corresponding open-{ and close-} such that an
- * un-modified Twirl parser can then parse it.
- *
- * The rule is simple: any line which starts with an expression potentially
- * opens a new block. Any text after the end of the expression is inside the
- * block, as well as any text on subsequent lines which are indented more
- * deeply.
- */
-object IndentHandler extends (String => String){
- def noBraceLine(remainder: String) = {
- !remainder.trim.headOption.exists("{(".toSeq.contains)
- }
-
- def successRemainder(r: Parser.ParseResult[_]) = r match {
- case Parser.Error(_, _) => None
- case Parser.Success(_, input) => Some(input.source().take(input.offset()))
- }
-
- def apply(input: String): String = {
-
- val lines = "" :: input.lines.toList ::: List("")
- val tails = lines.tails.takeWhile(_ != Nil)
-
- def getIndent(rest: List[String]): (Int, String) = rest match {
- case Nil => (0, "")
- case x if x.head.trim() != "" => (x.head.takeWhile(_ == ' ').length, x.head)
- case _ => getIndent(rest.tail)
- }
- val min = input.lines
- .map(_.indexWhere(_ != ' '))
- .filter(_ != -1)
- .min
-
- val linesOut = tails.foldLeft((min :: Nil, Seq[String]())){ (x, tail) =>
- val (stack, text) = x
- val spacedCurrent :: next = tail
- val (spaces, current) = spacedCurrent.splitAt(
- math.max(spacedCurrent.indexWhere(_ != ' '), 0)
- )
-
-// println("index " + math.max(spacedCurrent.indexWhere(_ != ' '), 0))
-// println(spaces, current)
-
- val declRemainder = successRemainder(Parser.parse(current.trim, _.templateDeclaration()))
-
- val exprRemainder = successRemainder(Parser.parse(current.trim, _.expression())).filter(_ == current.trim)
-
-
- /**
- * Whether or not the line starts with some sort of indent-start-marker, and
- * if it does what the remainder of the line contains
- */
- val headerSplit: Option[(String, String)] = {
- if (current.startsWith("@import ")) None
- else
- declRemainder.orElse(exprRemainder)
- .map(r => (r, current.trim.drop(r.length)))
- }
-
- val (nextIndent, nextLine) = getIndent(next)
- val indent = spaces.length
- val nextIsElse = nextLine.drop(nextIndent).take("@else ".length).trim.startsWith("@else")
-
- val elseCorrection = if (nextIsElse) 1 else 0
-
- val delta =
- if (nextIndent > indent && headerSplit.map(_._2).exists(noBraceLine)) 1
- else -stack.takeWhile(_ > nextIndent).length
-
- val baseLine: String = headerSplit match {
- case None => current
- case Some((before, after)) =>
-
- val newFirst =
- if (!before.startsWith("@else")) before
- else before.dropRight("@else".length)+ "} else"
-
- if (delta > 0 && noBraceLine(after)) newFirst + "{" + after
- else if (delta <= 0 && noBraceLine(after) && after.trim != "") newFirst + "{" + after + "}" * (1 - elseCorrection)
- else current
-
-
- }
-
- val closing = "}" * (-delta - elseCorrection)
-
- val newStack =
- if (delta > 0) nextIndent :: stack
- else stack.drop(-delta)
-// println(stack.toString.padTo(15, ' ') + current.padTo(15, ' ') + indent + "\t" + nextIndent + "\t" + delta + "\t" + headerSplit.toString.padTo(15, ' ') + (baseLine + closing))
- (newStack, text :+ (spaces + baseLine + closing))
- }
-
- val res = linesOut._2.mkString("\n")
-// println(res)
- res
- }
-}
diff --git a/api/src/main/scala/twist/stages/Parser.scala b/api/src/main/scala/twist/stages/Parser.scala
deleted file mode 100644
index 925cbcc..0000000
--- a/api/src/main/scala/twist/stages/Parser.scala
+++ /dev/null
@@ -1,790 +0,0 @@
-package twist.stages
-
-import acyclic.file
-import scala.annotation.elidable
-import scala.annotation.elidable._
-import scala.Some
-
-import scala.collection.mutable.{ArrayBuffer, Buffer, ListBuffer}
-
-object TwistNodes{
- trait Positioned{
- def offset: Int
- }
- abstract class TemplateTree extends Positioned
- abstract class ScalaExpPart extends Positioned
-
- case class Params(code: String, offset: Int) extends Positioned
- case class Template(name: PosString, comment: Option[Comment], params: PosString, topImports: Seq[Simple], imports: Seq[Simple], sub: Seq[Template], content: Seq[TemplateTree], offset: Int) extends Positioned
- case class PosString(str: String, offset: Int) extends Positioned {
- override def toString = str
- }
- case class Plain(text: String, offset: Int) extends TemplateTree with Positioned
- case class Display(exp: ScalaExp, offset: Int) extends TemplateTree with Positioned
- case class Comment(msg: String, offset: Int) extends TemplateTree with Positioned
- case class ScalaExp(parts: Seq[ScalaExpPart], offset: Int) extends TemplateTree with Positioned
- case class Simple(code: String, offset: Int) extends ScalaExpPart with Positioned
- case class Block(whitespace: String, args: Option[PosString], content: Seq[TemplateTree], offset: Int) extends ScalaExpPart with Positioned
-}
-
-import TwistNodes._
-
-object Parser extends (String => Template){
- def apply(source: String) = {
- Parser.parse(source, _.template()) match{
- case Parser.Success(tmpl: Template, input) => tmpl
- case Parser.Error(input, errors) => throw new Exception("Parsing Failed " + errors.mkString("\n"))
- }
- }
-
- def parse[T](source: String, f: Parser => T): ParseResult[T] = {
- val p = new Parser
-
- // Initialize mutable state
-
- p.input.reset(source)
- p.errorStack.clear()
- val res = f(p)
-
- if (p.errorStack.length == 0 && res != null) Success(res, p.input)
- else Error(p.input, p.errorStack.toList)
- }
-
- sealed abstract class ParseResult[+T]{
- def toOption: Option[T]
- }
- case class Success[+T](t: T, input: Input) extends ParseResult[T]{
- def toOption = Some(t)
- }
- case class Error(input: Input, errors: List[PosString]) extends ParseResult[Nothing]{
- def toOption = None
- }
-
- case class Input() {
- private var offset_ = 0
- private var source_ = ""
- private var length_ = 1
- val regressionStatistics = new collection.mutable.HashMap[String, (Int, Int)]
-
- /** Peek at the current input. Does not check for EOF. */
- def apply(): Char = source_.charAt(offset_)
-
- /**
- * Peek `length` characters ahead. Does not check for EOF.
- * @return string from current offset upto current offset + `length`
- */
- def apply(length: Int): String = source_.substring(offset_, (offset_ + length))
-
- /** Equivalent to `input(str.length) == str`. Does not check for EOF. */
- def matches(str: String): Boolean = {
- var i = 0;
- val l = str.length
- while (i < l) {
- if (source_.charAt(offset_ + i) != str.charAt(i))
- return false
- i += 1
- }
- true
- }
-
- /** Advance input by one character */
- def advance(): Unit = offset_ += 1
-
- /** Advance input by `increment` number of characters */
- def advance(increment: Int): Unit = offset_ += increment
-
- /** Backtrack by `decrement` numner of characters */
- def regress(decrement: Int): Unit = offset_ -= decrement
-
- /** Backtrack to a known offset */
- def regressTo(offset: Int): Unit = {
- @noinline @elidable(INFO)
- def updateRegressionStatistics() = {
- val distance = offset_ - offset
- val methodName = Thread.currentThread().getStackTrace()(2).getMethodName()
- val (count, charAccum) = regressionStatistics.get(methodName) getOrElse ((0, 0))
- regressionStatistics(methodName) = (count + 1, charAccum + distance)
- }
-
- offset_ = offset
- }
-
- def isPastEOF(len: Int): Boolean = (offset_ + len-1) >= length_
-
- def isEOF() = isPastEOF(1)
-
- def atEnd() = isEOF()
-
- def offset() = offset_
-
- def source() = source_
-
- /** Reset the input to have the given contents */
- def reset(source: String) {
- offset_ = 0
- source_ = source
- length_ = source.length()
- regressionStatistics.clear()
- }
- }
-}
-class Parser{
- import Parser._
-
- val input: Input = new Input
- val errorStack: ListBuffer[PosString] = ListBuffer()
-
- /**
- * Try to match `str` and advance `str.length` characters.
- *
- * Reports an error if the input does not match `str` or if `str.length` goes past the EOF.
- */
- def accept(str: String): Unit = {
- val len = str.length
- if (!input.isPastEOF(len) && input.matches(str))
- input.advance(len)
- else
- error("Expected '" + str + "' but found '" + (if (input.isPastEOF(len)) "EOF" else input(len)) + "'")
- }
-
- /**
- * Does `f` applied to the current peek return true or false? If true, advance one character.
- *
- * Will not advance if at EOF.
- *
- * @return true if advancing, false otherwise.
- */
- def check(f: Char => Boolean): Boolean = {
- if (!input.isEOF() && f(input())) {
- input.advance()
- true
- } else false
- }
-
- /**
- * Does the current input match `str`? If so, advance `str.length`.
- *
- * Will not advance if `str.length` surpasses EOF
- *
- * @return true if advancing, false otherwise.
- */
- def check(str: String): Boolean = {
- val len = str.length
- if (!input.isPastEOF(len) && input.matches(str)){
- input.advance(len)
- true
- } else false
- }
-
- def error(message: String, offset: Int = input.offset): Unit = {
- errorStack += PosString(message, offset)
- }
-
- /** Consume/Advance `length` characters, and return the consumed characters. Returns "" if at EOF. */
- def any(length: Int = 1): String = {
- if (input.isEOF()) {
- error("Expected more input but found 'EOF'")
- ""
- } else {
- val s = input(length)
- input.advance(length)
- s
- }
- }
-
- /**
- * Consume characters until input matches `stop`
- *
- * @param inclusive - should stop be included in the consumed characters?
- * @return the consumed characters
- */
- def anyUntil(stop: String, inclusive: Boolean): String = {
- var sb = new StringBuilder
- while (!input.isPastEOF(stop.length) && !input.matches(stop))
- sb.append(any())
- if (inclusive && !input.isPastEOF(stop.length))
- sb.append(any(stop.length))
- sb.toString()
- }
-
- /**
- * Consume characters until `f` returns false on the peek of input.
- *
- * @param inclusive - should the stopped character be included in the consumed characters?
- * @return the consumed characters
- */
- def anyUntil(f: Char => Boolean, inclusive: Boolean): String = {
- var sb = new StringBuilder
- while (!input.isEOF() && f(input()) == false)
- sb.append(any())
- if (inclusive && !input.isEOF())
- sb.append(any())
- sb.toString
- }
-
- /** Recursively match pairs of prefixes and suffixes and return the consumed characters
- *
- * Terminates at EOF.
- */
- def recursiveTag(prefix: String, suffix: String, allowStringLiterals: Boolean = false): String = {
- if (check(prefix)) {
- var stack = 1
- val sb = new StringBuffer
- sb.append(prefix)
- while (stack > 0) {
- if (check(prefix)) {
- stack += 1
- sb.append(prefix)
- } else if (check(suffix)) {
- stack -= 1
- sb.append(suffix)
- } else if (input.isEOF()) {
- error("Expected '" + suffix + "' but found 'EOF'")
- stack = 0
- } else if (allowStringLiterals) {
- stringLiteral("\"", "\\") match {
- case null => sb.append(any())
- case s => sb.append(s)
- }
- } else {
- sb.append(any())
- }
- }
- sb.toString()
- } else null
- }
-
- /**
- * Match a string literal, allowing for escaped quotes.
- * Terminates at EOF.
- */
- def stringLiteral(quote: String, escape: String): String = {
- if (check(quote)) {
- var within = true
- val sb = new StringBuffer
- sb.append(quote)
- while (within) {
- if (check(quote)) { // end of string literal
- sb.append(quote)
- within = false
- } else if (check(escape)) {
- sb.append(escape)
- if (check(quote)) { // escaped quote
- sb.append(quote)
- } else if (check(escape)) { // escaped escape
- sb.append(escape)
- }
- } else if (input.isEOF()) {
- error("Expected '" + quote + "' but found 'EOF'")
- within = false
- } else {
- sb.append(any())
- }
- }
- sb.toString()
- } else null
- }
-
- /** Match zero or more `parser` */
- def several[T, BufferType <: Buffer[T]](parser: () => T, provided: BufferType = null)(implicit manifest: Manifest[BufferType]): BufferType = {
-
- val ab = if (provided != null) provided else manifest.runtimeClass.newInstance().asInstanceOf[BufferType]
- var parsed = parser()
- while (parsed != null) {
- ab += parsed
- parsed = parser()
- }
- ab
- }
-
- def parentheses(): String = recursiveTag("(", ")", allowStringLiterals = true)
-
- def squareBrackets(): String = recursiveTag("[", "]")
-
- def whitespace(): String = anyUntil(_ > '\u0020', inclusive = false)
-
- // not completely faithful to original because it allows for zero whitespace
- def whitespaceNoBreak(): String = anyUntil(c => c != ' ' && c != '\t', inclusive = false)
-
- def identifier(): String = {
- var result: String = null
- // TODO: should I be checking for EOF here?
- if (!input.isEOF() && Character.isJavaIdentifierStart(input())) {
- result = anyUntil(Character.isJavaIdentifierPart(_) == false, false)
- }
- result
- }
-
- def comment(): Comment = {
- val pos = input.offset
- if (check("@*")) {
- val text = anyUntil("*@", inclusive = false)
- accept("*@")
- Comment(text, pos)
- } else null
- }
-
- def startArgs(): String = {
- val result = several[String, ArrayBuffer[String]](parentheses)
- if (result.length > 0)
- result.mkString
- else
- null
- }
-
- def importExpression(): Simple = {
- val p = input.offset
- if (check("@import "))
- Simple("import " + anyUntil("\n", inclusive = true).trim, p+1) // don't include position of @
- else null
- }
-
- def scalaBlock(): Simple = {
- if (check("@{")) {
- input.regress(1); // we need to parse the '{' via 'brackets()'
- val p = input.offset
- brackets() match {
- case null => null
- case b => Simple(b, p)
- }
- } else null
- }
-
- def brackets(): String = {
- var result = recursiveTag("{", "}")
- // mimicking how the original parser handled EOF for this rule
- if (result != null && result.last != '}')
- result = null
- result
- }
-
- def mixed(): ListBuffer[TemplateTree] = {
- // parses: comment | scalaBlockDisplayed | forExpression | matchExpOrSafeExprOrExpr | caseExpression | plain
- def opt1(): ListBuffer[TemplateTree] = {
- val t =
- comment() match {
- case null => scalaBlockDisplayed() match {
- case null => forExpression match {
- case null => matchExpOrSafeExpOrExpr() match {
- case null => caseExpression() match {
- case null => plain()
- case x => x
- }
- case x => x
- }
- case x => x
- }
- case x => x
- }
- case x => x
- }
- if (t != null) ListBuffer(t)
- else null
- }
-
- // parses: '{' mixed* '}'
- def opt2(): ListBuffer[TemplateTree] = {
- val lbracepos = input.offset()
- if (check("{")) {
- var buffer = new ListBuffer[TemplateTree]
- buffer += Plain("{", lbracepos)
- for (m <- several[ListBuffer[TemplateTree], ListBuffer[ListBuffer[TemplateTree]]](mixed))
- buffer = buffer ++ m // creates a new object, but is constant in time, as opposed to buffer ++= m which is linear (proportional to size of m)
- val rbracepos = input.offset
- if (check("}"))
- buffer += Plain("}", rbracepos)
- else
- error("Expected ending '}'")
- buffer
- } else null
- }
-
- opt1() match {
- case null => opt2()
- case x => x
- }
- }
-
- def scalaBlockDisplayed(): Display = {
- val sb = scalaBlock()
-
- if (sb != null)
- Display(ScalaExp(sb :: Nil, sb.offset), sb.offset)
- else
- null
- }
-
- def blockArgs(): PosString = {
- val p = input.offset
- val result = anyUntil("=>", true)
- if (result.endsWith("=>") && !result.contains("\n"))
- PosString(result, p)
- else {
- input.regress(result.length())
- null
- }
- }
-
- def block(): Block = {
- var result: Block = null
- val p = input.offset
- val ws = whitespaceNoBreak()
- if (check("{")) {
- val blkArgs = Option(blockArgs())
- val mixeds = several[ListBuffer[TemplateTree], ListBuffer[ListBuffer[TemplateTree]]](mixed)
- accept("}")
- // TODO - not use flatten here (if it's a performance problem)
- result = Block(ws, blkArgs, mixeds.flatten, p)
- } else {
- input.regressTo(p)
- }
-
- result
- }
-
- def caseExpression(): TemplateTree = {
- var result: TemplateTree = null
-
- val wspos = input.offset
- val ws = whitespace()
- val p = input.offset()
- if (check("case ")) {
- val pattern = Simple("case " + anyUntil("=>", inclusive = true), p)
- val blk = block()
- if (blk != null) {
- result = ScalaExp(ListBuffer(pattern, blk), blk.offset)
- whitespace()
- } else {
- //error("Expected block after 'case'")
- input.regressTo(wspos)
- }
- } else if (ws.length > 0) {
- // We could regress here and not return something for the ws, because the plain production rule
- // would parse this, but this would actually be a hotspot for backtracking, so let's return it
- // here seeing as it's been parsed all ready.
- result = Plain(ws, wspos)
- }
-
- result
- }
-
- def matchExpOrSafeExpOrExpr(): Display = {
- val resetPosition = input.offset
- val result =
- expression() match {
- case null => safeExpression()
- case x => x
- }
-
- if (result != null) {
- val exprs = result.exp.parts.asInstanceOf[ListBuffer[ScalaExpPart]]
- val mpos = input.offset
- val ws = whitespaceNoBreak()
- if (check("match")) {
- val m = Simple(ws + "match", mpos)
- val blk = block()
- if (blk != null) {
- exprs.append(m)
- exprs.append(blk)
- } else {
- // error("expected block after match")
- input.regressTo(mpos)
- }
- } else {
- input.regressTo(mpos)
- }
- }
-
- result
- }
-
- def forExpression(): Display = {
- var result: Display = null
- val p = input.offset
- if (check("@for")) {
- val parens = parentheses()
- if (parens != null) {
- val blk = block()
- if (blk != null) {
- result = Display(ScalaExp(ListBuffer(Simple("for" + parens + " yield ", p+1), blk), p+1), p+1) // don't include pos of @
- }
- }
- }
-
- if (result == null)
- input.regressTo(p)
-
- result
- }
-
- def safeExpression(): Display = {
- if (check("@(")) {
- input.regress(1)
- val p = input.offset
- Display(ScalaExp(ListBuffer(Simple(parentheses(), p)), p), p)
- } else null
- }
-
- def plain(): Plain = {
- def single(): String = {
- if (check("@@")) "@"
- else if (!input.isEOF() && input() != '@' && input() != '}' && input() != '{') any()
- else null
- }
- val p = input.offset
- var result: Plain = null
- var part = single()
- if (part != null) {
- val sb = new StringBuffer
- while (part != null) {
- sb.append(part)
- part = single()
- }
- result = Plain(sb.toString(), p)
- }
-
- result
- }
-
- def expression(): Display = {
- var result: Display = null
- if (check("@")) {
- val pos = input.offset
- val code = methodCall()
- if (code != null) {
- val parts = several[ScalaExpPart, ListBuffer[ScalaExpPart]](expressionPart)
- parts.prepend(Simple(code, pos))
- result = Display(ScalaExp(parts, pos-1), pos-1)
- } else {
- input.regressTo(pos - 1) // don't consume the @ if we fail
- }
- }
-
- result
- }
-
- def methodCall(): String = {
- val name = identifier()
- if (name != null) {
- val sb = new StringBuffer(name)
- sb.append(Option(squareBrackets) getOrElse "")
- sb.append(Option(parentheses) getOrElse "")
- sb.toString()
- } else null
- }
-
- def expressionPart(): ScalaExpPart = {
- def simpleParens() = {
- val p = input.offset
- val parens = parentheses()
- if (parens != null) Simple(parens, p)
- else null
- }
-
- def wsThenScalaBlockChained() = {
- val reset = input.offset
- val ws = whitespaceNoBreak()
- val chained = scalaBlockChained()
- if (chained eq null) input.regressTo(reset)
- chained
- }
-
- chainedMethods() match {
- case null => block() match {
- case null => wsThenScalaBlockChained() match {
- case null => elseCall() match {
- case null => simpleParens()
- case x => x
- }
- case x => x
- }
- case x => x
- }
- case x => x
- }
- }
-
- def scalaBlockChained(): Block = {
- val blk = scalaBlock()
- if (blk != null)
- Block("", None, ListBuffer(ScalaExp(ListBuffer(blk), blk.offset)), blk.offset)
- else null
- }
-
- def chainedMethods(): Simple = {
- val p = input.offset
- var result: Simple = null
- if (check(".")) {
- val firstMethodCall = methodCall()
- if (firstMethodCall != null) {
- val sb = new StringBuffer("." + firstMethodCall)
- var done = false
- while (!done) {
- val reset = input.offset
- var nextLink: String = null
- if (check(".")) {
- methodCall() match {
- case m: String => nextLink = m
- case _ =>
- }
- }
-
- nextLink match {
- case null => {
- done = true
- input.regressTo(reset)
- }
- case _ => {
- sb.append(".")
- sb.append(nextLink)
- }
- }
- }
-
- result = Simple(sb.toString(), p)
- } else input.regressTo(p)
- }
-
- result
- }
-
- def elseCall(): Simple = {
- val reset = input.offset
- whitespaceNoBreak()
- val p = input.offset
- if (check("else")) {
- whitespaceNoBreak()
- Simple("else", p)
- } else {
- input.regressTo(reset)
- null
- }
- }
-
- def template(): Template = {
- val topImports = extraImports()
- whitespace()
- val commentpos = input.offset
- val cm = Option(comment()).map(_.copy(offset = commentpos))
- whitespace()
- val args =
- if (check("@(")) {
- input.regress(1)
- val p = input.offset
- val args = startArgs()
- if (args != null) Some(PosString(args, p))
- else None
- } else None
- val (imports, templates, mixeds) = templateContent()
-
- Template(PosString("", 0), cm, args.getOrElse(PosString("()", 0)), topImports, imports, templates, mixeds, 0)
- }
- def subTemplate(): Template = {
- var result: Template = null
- val resetPosition = input.offset
- val templDecl = templateDeclaration()
- if (templDecl != null) {
- anyUntil(c => c != ' ' && c != '\t', inclusive = false)
- if (check("{")) {
- val (imports, templates, mixeds) = templateContent()
- if (check("}"))
- result = Template(templDecl._1, None, templDecl._2, Nil, imports, templates, mixeds, templDecl._1.offset)
- }
- }
-
- if (result == null)
- input.regressTo(resetPosition)
- result
- }
-
- def templateDeclaration(): (PosString, PosString) = {
- if (check("@")) {
- val namepos = input.offset
- val name = identifier() match {
- case null => null
- case id => PosString(id, namepos)
- }
-
- if (name != null) {
- val paramspos = input.offset
- val types = Option(squareBrackets) getOrElse ""
- val args = several[String, ArrayBuffer[String]](parentheses)
- val params = PosString(types + args.mkString, paramspos)
- if (params != null)
-
- anyUntil(c => c != ' ' && c != '\t', inclusive = false)
- if (check("=")) {
- return (name, params)
- }
- } else input.regress(1) // don't consume @
- }
-
- null
- }
-
- def templateContent(): (Seq[Simple], Seq[Template], Seq[TemplateTree]) = {
- val imports = new ArrayBuffer[Simple]
-
- val templates = new ArrayBuffer[Template]
- val mixeds = new ArrayBuffer[TemplateTree]
-
- var done = false
- while (!done) {
- val impExp = importExpression()
- if (impExp != null) imports += impExp
- else {
-
- val templ = subTemplate()
- if (templ != null) templates += templ
- else {
- val mix = mixed()
- if (mix != null) mixeds ++= mix
- else {
- // check for an invalid '@' symbol, and just skip it so we can continue the parse
- val pos = input.offset
- if (check("@")) error("Invalid '@' symbol", pos)
- else done = true
- }
- }
- }
- }
-
- (imports, templates, mixeds)
- }
-
- def extraImports(): Seq[Simple] = {
- val resetPosition = input.offset
- val imports = new ArrayBuffer[Simple]
-
- while (whitespace().nonEmpty || (comment() ne null)) {} // ignore
-
- var done = false
- while (!done) {
- val importExp = importExpression()
- if (importExp ne null) {
- imports += importExp
- whitespace()
- } else {
- done = true
- }
- }
-
- if (imports.isEmpty) {
- input.regressTo(resetPosition)
- }
-
- imports
- }
-
-
-
- def mkRegressionStatisticsString() {
- val a = input.regressionStatistics.toArray.sortBy { case (m, (c, a)) => c }
- a.mkString("\n")
- }
-
- // TODO - only for debugging purposes, remove before release
- def setSource(source: String) {
- input.reset(source)
- }
-} \ No newline at end of file
diff --git a/api/src/test/scala/twist/AdvancedTests.scala b/api/src/test/scala/twist/AdvancedTests.scala
deleted file mode 100644
index eb9e69f..0000000
--- a/api/src/test/scala/twist/AdvancedTests.scala
+++ /dev/null
@@ -1,120 +0,0 @@
-package twist
-
-import utest._
-import twist.stages._
-import scalatags.Text.all._
-
-
-/**
-* Created by haoyi on 7/14/14.
-*/
-object AdvancedTests extends TestSuite{
- import TestUtil._
-
- val tests = TestSuite{
- 'localDef{
- check(
- tw("""
- @lol(n: Int) = @{
- "omg" * n
- }
-
- @lol(2)
- """),
- "omgomg"
- )
- }
- 'innerTemplate{
- check(
- tw("""
- @lol(f: Int) =
- omg @f
-
- @lol(1)
- @lol(2: Int)
- @lol(3 + 1)
- """),
- tw("""
- @lol(f: Int) ={
- omg @f
- }
- @lol(1)
- @lol(2: Int)
- @lol(3 + 1)
- """),
- tw("""
- @lol(f: Int) = {
- omg @f
- }
- @lol(1)
- @lol(2: Int)
- @lol(3 + 1)
- """),
- """
- omg1omg2omg4
- """
- )
- }
- 'innerInnerTemplate{
- check(
- tw("""
- @lol(f: Int) =
- @wtf(g: Int) =
- wtf @g
-
- @wtf(1 + 2 + 3)
- @wtf(f)
-
- @lol(1)
- @lol(2: Int)
- @lol(3 + 1)
- """),
- tw("""
- @lol(f: Int) = {
- @wtf(g: Int) = {
- wtf @g
- }
- @wtf(1 + 2 + 3)
- @wtf(f)
- }
- @lol(1)
- @lol(2: Int)
- @lol(3 + 1)
- """),
- tw("""
- @lol(f: Int) = {
- @wtf(g: Int) =
- wtf @g
-
- @wtf(1 + 2 + 3)
- @wtf(f)
- }
- @lol(1)
- @lol(2: Int)
- @lol(3 + 1)
- """),
- tw("""
- @lol(f: Int) =
- @wtf(g: Int) = {
- wtf @g
- }
- @wtf(1 + 2 + 3)
- @wtf(f)
-
- @lol(1)
- @lol(2: Int)
- @lol(3 + 1)
- """),
- """
- wtf6
- wtf1
- wtf6
- wtf2
- wtf6
- wtf4
- """
- )
- }
-
- }
-}
diff --git a/api/src/test/scala/twist/BasicTests.scala b/api/src/test/scala/twist/BasicTests.scala
deleted file mode 100644
index 0151ba3..0000000
--- a/api/src/test/scala/twist/BasicTests.scala
+++ /dev/null
@@ -1,418 +0,0 @@
-package twist
-import utest._
-import twist.stages._
-import scalatags.Text.all._
-
-
-/**
-* Created by haoyi on 7/14/14.
-*/
-object BasicTests extends TestSuite{
- import TestUtil._
-
- val tests = TestSuite{
-
- 'helloWorld{
- object omg {
- def wtf(s: Frag*): Frag = Seq[Frag]("|", s, "|")
- }
- def str = "hear me moo"
- check(
- tw("""
- @omg.wtf
- i @b{am} cow @str
- """),
- "|i<b>am</b>cowhearmemoo|"
- )
- }
- 'interpolation{
- 'chained-check(
- tw("omg @scala.math.pow(0.5, 3) wtf"),
- "omg 0.125 wtf"
- )
- 'parens-check(
- tw("omg @(1 + 2 + 3 + 4) wtf"),
- "omg 10 wtf"
- )
- 'block-check(
- tw("""
- @{"lol" * 3}
- @{
- val omg = "omg"
- omg * 2
- }
- """),
- """
- lollollol
- omgomg
- """
- )
- }
- 'imports{
- object Whee{
- def func(x: Int) = x * 2
- }
- check(
- tw("""
- @import math._
- @import Whee.func
- @abs(-10)
- @p
- @max(1, 2)
- @func(2)
- """),
- """
- 10
- <p>
- 2
- 4
- </p>
- """
- )
- }
- 'parenArgumentLists{
- 'attributes{
- check(
- tw("""
- @div(id:="my-id") omg
- @div(id:="my-id"){ omg }
- @div(id:="my-id")
- omg
- """),
- """
- <divid="my-id">omg</div>
- <divid="my-id">omg</div>
- <divid="my-id">omg</div>
- """
- )
- }
- 'multiline{
-
- check(
- tw("""
- @div(
- h1("Hello World"),
- p("I am a ", b{"cow"})
- )
- """),
- """
- <div>
- <h1>Hello World</h1>
- <p>I am a <b>cow</b></p>
- </div>
- """
- )
- }
- }
- 'grouping{
- 'negative{
- // The indentation for "normal" text is ignored; we only
- // create blocks from the indentation following a scala
- // @xxx expression
- check(
- tw("""
- I am cow hear me moo
- I weigh twice as much as you
- And I look good on the barbecue
- Yoghurt curds cream cheese and butter
- Comes from liquids from my udder
- I am cow I am cow hear me moooooo
- """),
- """
- I am cow hear me moo
- I weigh twice as much as you
- And I look good on the barbecue
- Yoghurt curds cream cheese and butter
- Comes from liquids from my udder
- I am cow I am cow hear me moooooo
- """
- )
- }
- 'indentation{
- 'simple{
- val world = "World2"
-
- check(
- tw("""
- @h1 Hello World
- @h2 hello
- @world
- @h3
- Cow
- """),
- """
- <h1>HelloWorld</h1>
- <h2>helloWorld2</h2>
- <h3>Cow</h3>
- """
- )
- }
- 'linearNested{
- check(
- tw("""
- @h1 @span @a Hello World
- @h2 @span @a hello
- @b world
- @h3 @i
- @div Cow
- """),
- """
- <h1><span></span><a></a>HelloWorld</h1>
- <h2><span></span><a></a>hello<b>world</b></h2>
- <h3><i></i><div>Cow</div></h3>
- """
- )
- }
- 'crasher{
- tw("""
-@html
- @head
- @meta
- @div
- @a
- @span
- """)
- }
- }
- 'curlies{
- 'simple{
- val world = "World2"
-
- check(
- tw("""@div{Hello World}"""),
- """<div>HelloWorld</div>"""
- )
- }
- 'multiline{
- check(
- tw("""
- @div{
- Hello
- }
- """),
- """
- <div>Hello</div>
- """
- )
- }
- }
- 'mixed{
- check(
- tw("""
- @div{
- Hello
- @div
- @h1 WORLD @b{!!!}
- lol
- @p{
- @h2{Header 2}
- }
- }
- """),
- """
- <div>
- Hello
- <div>
- <h1>WORLD<b>!!!</b>lol</h1>
- <p><h2>Header2</h2></p>
- </div>
- </div>
- """
- )
- }
-
- 'args{
- val things = Seq(1, 2, 3)
- check(
- tw("""
- @ul
- @things.map { x =>
- @li @x
- }
- """),
- tw("""
- @ul
- @things.map x =>
- @li @x
-
- """),
- """
- <ul>
- <li>1</li>
- <li>2</li>
- <li>3</li>
- </ul>
- """
- )
- }
- }
-
- 'loops {
-
- * - check(
- tw("""
- @for(x <- 0 until 3)
- lol
- """),
- tw("""
- @for(x <- 0 until 3){
- lol
- }
- """),
- tw(
- """
- @for(x <- 0 until 3) {
- lol
- }
- """),
- "lollollol"
- )
-
-
- * - check(
- tw("""
- @p
- @for(x <- 0 until 2)
- @for(y <- 0 until 2)
- lol@x@y
- """),
- tw( """
- @p
- @for(x <- 0 until 2) {
- @for(y <- 0 until 2)
- lol@x@y
- }
- """),
- tw("""
- @p
- @for(x <- 0 until 2)
- @for(y <- 0 until 2){
- lol@x@y
- }
- """),
- "<p>lol00lol01lol10lol11</p>"
- )
- check(
- tw("""
- @p
- @for(x <- 0 until 2)
- @for(y <- 0 until 2)
- lol@x@y
- """),
- "<p>lol00lol01lol10lol11</p>"
- )
-
- * - check(
- tw(
- """
- @for(x <- 0 until 2; y <- 0 until 2)
- @div{@x@y}
-
- """),
- """<div>00</div><div>01</div><div>10</div><div>11</div>"""
- )
- }
-
- 'ifElse{
- 'basicExamples{
- * - check(
- tw("""
- @if(false)
- Hello
- @else lols
- @p
- """),
- "lols<p></p>"
- )
-
- * - check(
- tw("""
- @div
- @if(true)
- Hello
- @else lols
- """),
- "<div>Hello</div>"
- )
-
- * - check(
- tw("""
- @div
- @if(true) Hello
- @else lols
- """),
- "<div>Hello</div>"
- )
- * - check(
- tw("""
- @if(false) Hello
- @else lols
- """),
- "lols"
- )
- * - check(
- tw("""
- @if(false)
- Hello
- @else
- lols
- @img
- """),
- "lols<img/>"
- )
- * - check(
- tw("""
- @p
- @if(true)
- Hello
- @else
- lols
- """),
- tw("""
- @p
- @if(true) {
- Hello
- } else {
- lols
- }
- """),
- "<p>Hello</p>"
- )
- }
- 'funkyExpressions{
- * - check(
- tw("""
- @p
- @if(true == false == (true.==(false)))
- @if(true == false == (true.==(false)))
- Hello1
- @else
- lols1
- @else
- @if(true == false == (true.==(false)))
- Hello2
- @else
- lols2
- """),
- "<p>Hello1</p>"
- )
- * - check(
- tw("""
- @p
- @if(true == false != (true.==(false)))
- @if(true == false != (true.==(false)))
- Hello1
- @else
- lols1
- @else
- @if(true == false != (true.==(false)))
- Hello2
- @else
- lols2
- """),
- "<p>lols2</p>"
- )
- }
- }
- }
-}
diff --git a/api/src/test/scala/twist/ErrorTests.scala b/api/src/test/scala/twist/ErrorTests.scala
deleted file mode 100644
index fec3fc3..0000000
--- a/api/src/test/scala/twist/ErrorTests.scala
+++ /dev/null
@@ -1,321 +0,0 @@
-package twist
-
-import utest._
-import twist.stages._
-import scalatags.Text.all._
-import twist.Internals.{DebugFailure, twDebug}
-
-/**
-* Created by haoyi on 7/14/14.
-*/
-object ErrorTests extends TestSuite{
- def check(x: => Unit, expectedMsg: String, expectedError: String) = {
- val DebugFailure(msg, pos) = intercept[DebugFailure](x)
- def format(str: String) = {
- val whitespace = " \t\n".toSet
- "\n" + str.dropWhile(_ == '\n')
- .reverse
- .dropWhile(whitespace.contains)
- .reverse
- }
- // Format these guys nicely to normalize them and make them
- // display nicely in the assert error message if it blows up
- val formattedPos = format(pos)
- val formattedExpectedPos = format(expectedError)
-
- assert(msg.contains(expectedMsg))
- assert(formattedPos == formattedExpectedPos)
-
- }
- val tests = TestSuite{
-
-
- 'simple - check(
- twDebug("omg @notInScope lol"),
- """not found: value notInScope""",
- """
- twDebug("omg @notInScope lol"),
- ^
- """
- )
-
- 'chained{
- 'properties {
- * - check(
- twDebug("omg @math.lol lol"),
- """object lol is not a member of package math""",
- """
- twDebug("omg @math.lol lol"),
- ^
- """
- )
-
- * - check(
- twDebug("omg @math.E.lol lol"),
- """value lol is not a member of Double""",
- """
- twDebug("omg @math.E.lol lol"),
- ^
- """
- )
- * - check(
- twDebug("omg @_root_.scala.math.lol lol"),
- """object lol is not a member of package math""",
- """
- twDebug("omg @_root_.scala.math.lol lol"),
- ^
- """
- )
- * - check(
- twDebug("omg @_root_.scala.gg.lol lol"),
- """object gg is not a member of package scala""",
- """
- twDebug("omg @_root_.scala.gg.lol lol"),
- ^
- """
- )
- * - check(
- twDebug("omg @_root_.ggnore.math.lol lol"),
- """object ggnore is not a member of package <root>""",
- """
- twDebug("omg @_root_.ggnore.math.lol lol"),
- ^
- """
- )
- }
- 'calls{
- * - check(
- twDebug("@scala.QQ.abs(-10).tdo(10).sum.z"),
- """object QQ is not a member of package scala""",
- """
- twDebug("@scala.QQ.abs(-10).tdo(10).sum.z"),
- ^
- """
- )
- * - check(
- twDebug("@scala.math.abs(-10).tdo(10).sum.z"),
- "value tdo is not a member of Int",
- """
- twDebug("@scala.math.abs(-10).tdo(10).sum.z"),
- ^
- """
- )
- * - check(
- twDebug("@scala.math.abs(-10).to(10).sum.z"),
- "value z is not a member of Int",
- """
- twDebug("@scala.math.abs(-10).to(10).sum.z"),
- ^
- """
- )
- * - check(
- twDebug("@scala.math.abs(-10).to(10).sum.z()"),
- "value z is not a member of Int",
- """
- twDebug("@scala.math.abs(-10).to(10).sum.z()"),
- ^
- """
- )
- * - check(
- twDebug("@scala.math.abs(-10).cow.sum.z"),
- "value cow is not a member of Int",
- """
- twDebug("@scala.math.abs(-10).cow.sum.z"),
- ^
- """
- )
- * - check(
- twDebug("@scala.smath.abs.cow.sum.z"),
- "object smath is not a member of package scala",
- """
- twDebug("@scala.smath.abs.cow.sum.z"),
- ^
- """
- )
- * - check(
- twDebug("""
- I am cow hear me moo
- @scala.math.abs(-10).tdo(10).sum.z
- I weigh twice as much as you
- """),
- "value tdo is not a member of Int",
- """
- @scala.math.abs(-10).tdo(10).sum.z
- ^
- """
- )
- }
- 'callContents{
- * - check(
- twDebug("@scala.math.abs((1, 2).wtf)"),
- "value wtf is not a member of (Int, Int)",
- """
- twDebug("@scala.math.abs((1, 2).wtf)"),
- ^
- """
- )
-
- * - check(
- twDebug("@scala.math.abs((1, 2).swap._1.toString().map(_.toString.wtf))"),
- "value wtf is not a member of String",
- """
- twDebug("@scala.math.abs((1, 2).swap._1.toString().map(_.toString.wtf))"),
- ^
- """
- )
- }
- }
- 'ifElse{
- 'oneLine {
- * - check(
- twDebug("@if(math > 10){ 1 }else{ 2 }"),
- "object > is not a member of package math",
- """
- twDebug("@if(math > 10){ 1 }else{ 2 }"),
- ^
- """
- )
- * - check(
- twDebug("@if(true){ (@math.pow(10)) * 10 }else{ 2 }"),
- "Unspecified value parameter y",
- """
- twDebug("@if(true){ (@math.pow(10)) * 10 }else{ 2 }"),
- ^
- """
- )
- * - check(
- twDebug("@if(true){ * 10 }else{ @math.sin(3, 4, 5) }"),
- "too many arguments for method sin: (x: Double)Double",
- """
- twDebug("@if(true){ * 10 }else{ @math.sin(3, 4, 5) }"),
- ^
- """
- )
- }
- 'multiLine{
- * - check(
- twDebug("""
- Ho Ho Ho
-
- @if(math != 10)
- I am a cow
- @else
- You are a cow
- GG
- """),
- "object != is not a member of package math",
- """
- @if(math != 10)
- ^
- """
- )
- * - check(
- twDebug("""
- Ho Ho Ho
-
- @if(4 != 10)
- I am a cow @math.lols
- @else
- You are a cow
- GG
- """),
- "object lols is not a member of package math",
- """
- I am a cow @math.lols
- ^
- """
- )
- * - check(
- twDebug("""
- Ho Ho Ho
-
- @if(12 != 10)
- I am a cow
- @else
- @math.E.toString.gog(1)
- GG
- """),
- "value gog is not a member of String",
- """
- @math.E.toString.gog(1)
- ^
- """
- )
- }
- }
- 'forLoop{
- 'oneLine{
- 'header - check(
- twDebug("omg @for(x <- (0 + 1 + 2) omglolol (10 + 11 + 2)){ hello }"),
- """value omglolol is not a member of Int""",
- """
- twDebug("omg @for(x <- (0 + 1 + 2) omglolol (10 + 11 + 2)){ hello }"),
- ^
- """
- )
-
- 'body - check(
- twDebug("omg @for(x <- 0 until 10){ @((x, 2) + (1, 2)) }"),
- """too many arguments for method +""",
- """
- twDebug("omg @for(x <- 0 until 10){ @((x, 2) + (1, 2)) }"),
- ^
- """
- )
- }
- 'multiLine{
- 'body - check(
- twDebug("""
- omg
- @for(x <- 0 until 10)
- I am cow hear me moo
- I weigh twice as much as @x.kkk
- """),
- """value kkk is not a member of Int""",
- """
- I weigh twice as much as @x.kkk
- ^
- """
- )
- }
- }
- 'multiLine{
- 'missingVar - check(
- twDebug("""
- omg @notInScope lol
- """),
- """not found: value notInScope""",
- """
- omg @notInScope lol
- ^
- """
- )
- 'wrongType - check(
- twDebug("""
- omg @{() => ()} lol
- """),
- """type mismatch""",
- """
- omg @{() => ()} lol
- ^
- """
- )
-
- 'bigExpression - check(
- twDebug("""
- @{
- val x = 1 + 2
- val y = new Object()
- val z = y * x
- x
- }
- """),
- "value * is not a member of Object",
- """
- val z = y * x
- ^
- """
- )
- }
- }
-}
diff --git a/api/src/test/scala/twist/ParserTests.scala b/api/src/test/scala/twist/ParserTests.scala
deleted file mode 100644
index 721fb29..0000000
--- a/api/src/test/scala/twist/ParserTests.scala
+++ /dev/null
@@ -1,50 +0,0 @@
-package twist
-import utest._
-import twist.stages.{TwistNodes, Parser}
-
-/**
- * Created by haoyi on 8/2/14.
- */
-object ParserTests extends TestSuite{
- val WN = TwistNodes
- val WP = Parser
- def check[T](s: String, f: Parser => T, expected: Option[T]){
- val parsed = WP.parse(s, f).toOption
- assert(parsed == expected)
- }
- val tests = TestSuite{
-// 'chainedExpressions {
-// check("", _.expression(), None)
-// check("asd", _.expression(), None)
-// check("@asd", _.expression(), Some(
-// WN.Display(WN.ScalaExp(Seq(WN.Simple("asd"))))
-// ))
-//
-// check("@asd{", _.expression(), None)
-// check("@asd(", _.expression(), None)
-// check("@asd()", _.expression(), Some(
-// WN.Display(WN.ScalaExp(Seq(WN.Simple("asd()"))))
-// ))
-// check("@asd(ggnore)", _.expression(), Some(
-// WN.Display(WN.ScalaExp(Seq(WN.Simple("asd(ggnore)"))))
-// ))
-// check("@asd.wtf(ggnore).bbq.lol", _.expression(), Some(
-// WN.Display(WN.ScalaExp(Seq(WN.Simple("asd"), WN.Simple(".wtf(ggnore).bbq.lol"))))
-// ))
-// check("@asd{}", _.expression(), Some(
-// WN.Display(WN.ScalaExp(Seq(WN.Simple("asd"), WN.Block("", None, Seq()))))
-// ))
-// check("@asd{lol}", _.expression(), Some(
-// WN.Display(WN.ScalaExp(Seq(WN.Simple("asd"), WN.Block("", None, Seq(WN.Plain("lol"))))))
-// ))
-// check("@asd{lol}.wtf('l'){gg}", _.expression(), Some(
-// WN.Display(WN.ScalaExp(Seq(
-// WN.Simple("asd"),
-// WN.Block("", None, Seq(WN.Plain("lol"))),
-// WN.Simple(".wtf('l')"),
-// WN.Block("", None, Seq(WN.Plain("gg")))
-// )))
-// ))
-// }
- }
-}
diff --git a/api/src/test/scala/twist/TestUtil.scala b/api/src/test/scala/twist/TestUtil.scala
deleted file mode 100644
index 33af527..0000000
--- a/api/src/test/scala/twist/TestUtil.scala
+++ /dev/null
@@ -1,16 +0,0 @@
-package twist
-
-import utest._
-
-
-object TestUtil {
- implicit def stringify(f: scalatags.Text.all.Frag) = f.render
- def check(rendered: String*) = {
- val collapsed = rendered.map(collapse)
- val first = collapsed(0)
- assert(collapsed.forall(_ == first))
- }
- def collapse(s: String): String = {
- s.replaceAll("[ \n]", "")
- }
-}