summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorLi Haoyi <haoyi@dropbox.com>2014-11-03 22:13:10 -0800
committerLi Haoyi <haoyi@dropbox.com>2014-11-03 22:13:10 -0800
commited923d3445e1f304949eee2ee414987d98756a86 (patch)
tree42f81cf36afc3ce34b31ca5a4d15432ac56f4445
parent2ca177afb55232ae762051d9a2cc205629000e70 (diff)
downloadhands-on-scala-js-ed923d3445e1f304949eee2ee414987d98756a86.tar.gz
hands-on-scala-js-ed923d3445e1f304949eee2ee414987d98756a86.tar.bz2
hands-on-scala-js-ed923d3445e1f304949eee2ee414987d98756a86.zip
Tests pass!
-rw-r--r--scalatexApi/src/main/scala/scalatex/package.scala15
-rw-r--r--scalatexApi/src/main/scala/scalatex/stages/Compiler.scala150
-rw-r--r--scalatexApi/src/main/scala/scalatex/stages/Parser.scala (renamed from scalatexApi/src/main/scala/scalatex/ScalatexParser.scala)34
-rw-r--r--scalatexApi/src/main/scala/torimatomeru/ScalaSyntax.scala2
-rw-r--r--scalatexApi/src/test/scala/scalatex/BasicTests.scala34
-rw-r--r--scalatexApi/src/test/scala/scalatex/ParserTests.scala78
6 files changed, 108 insertions, 205 deletions
diff --git a/scalatexApi/src/main/scala/scalatex/package.scala b/scalatexApi/src/main/scala/scalatex/package.scala
index a55f3e3..eb3ba6e 100644
--- a/scalatexApi/src/main/scala/scalatex/package.scala
+++ b/scalatexApi/src/main/scala/scalatex/package.scala
@@ -2,7 +2,7 @@ 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 scalatex.stages.Compiler
+import scalatex.stages.{Parser, Compiler}
import scala.language.experimental.macros
import acyclic.file
@@ -69,7 +69,7 @@ package object scalatex {
}
def compileThing(c: Context)
- (s: String,
+ (scalatexSource: String,
source: SourceFile,
point: Int,
runtimeErrors: Boolean,
@@ -78,12 +78,17 @@ package object scalatex {
def compile(s: String): c.Tree = {
val realPos = new OffsetPosition(source, point).asInstanceOf[c.universe.Position]
- Compiler(c)(realPos, new ScalatexParser(s).Body.run().get)
+ Compiler(c)(realPos, new Parser(s).Body.run().get)
}
-
+ def normalize(str: String) = {
+ val lines = str.split("\n")
+ val offset = lines.iterator.map(_.takeWhile(_ == ' ').length).min
+ lines.iterator.map(_.drop(offset)).mkString("\n")
+ }
+
import c.Position
try {
- val compiled = compile(s)
+ val compiled = compile(scalatexSource)
if (debug) println(compiled)
c.Expr[Frag](c.typeCheck(compiled))
} catch {
diff --git a/scalatexApi/src/main/scala/scalatex/stages/Compiler.scala b/scalatexApi/src/main/scala/scalatex/stages/Compiler.scala
index 5127945..a305199 100644
--- a/scalatexApi/src/main/scala/scalatex/stages/Compiler.scala
+++ b/scalatexApi/src/main/scala/scalatex/stages/Compiler.scala
@@ -2,7 +2,7 @@ package scalatex
package stages
import acyclic.file
-import scala.reflect.macros.Context
+import scala.reflect.macros.whitebox.Context
import scala.reflect.internal.util.{Position, OffsetPosition}
/**
@@ -27,150 +27,20 @@ object Compiler{
case (curr, Ast.Chain.TypeArgs(str, offset2)) =>
val TypeApply(fun, args) = c.parse(s"omg$str")
TypeApply(curr, args)
+ case (curr, Ast.Block(parts, offset)) =>
+ q"$curr(..${compileBlock(parts, offset)})"
}
-
}
- def compileBlock(parts: Seq[Ast.Block.Sub], offset: Int): c.Tree = {
- val compiledParts = parts.map{
+ def compileBlock(parts: Seq[Ast.Block.Sub], offset: Int): Seq[c.Tree] = {
+ parts.map{
case Ast.Block.Text(str, offset2) => q"$str"
case Ast.Chain(code, parts, offset) => compileChain(code, parts, offset)
}
- q"Seq[$fragType](..$compiledParts)"
}
- ???
-// def compileTree(frag: Ast): Tree = frag match{
-// case Ast.Block(parts, offset)
-//
-//// println(s"${frag.offset}\n${literalPos.point}\n${pos.point}\n$frag\n")
-//
-// val f: Tree = frag match {
-// case Ast.Block.Text(text, offset) => fragPos.at(q"$text", 0)
-// 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) fragPos.set(x, x.pos.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))
-// atPos(a.pos)(a2)
-// atPos(f.pos)(f2)
-// a2
-// case Ident(x: TermName) if x.decoded == fresh =>
-// q"Seq[$fragType](..$b)"
-// }
-// rec(skeleton)
-//
-// 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 =>
-// fragPos.set(x, x.pos.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 =>
-// fragPos.set(x, x.pos.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 res = t match {
-// case Apply(fun, args) =>
-// for(arg <- args; tree <- arg if tree.pos != NoPosition){
-// fragPos.set(tree, tree.pos.point + first.length - fresh.length)
-// }
-//
-// Apply(rec(fun), args)
-// case Select(qualifier, name) => Select(rec(qualifier), name)
-// case Ident(x: TermName) if x.decoded == fresh => l
-// }
-// fragPos.at(res, t.pos.point + first.length - fresh.length)
-//// println(Position.formatMessage(newPos.asInstanceOf[scala.reflect.internal.util.Position], "", true))
-// res
-// }
-// rec(skeleton)
-//
-// case (l, WN.Block(ws, None, content, offset)) =>
-// val contentTrees = content.map(compileTree(_))
-// fragPos.at(q"$l(..$contentTrees)", offset)
-//
-// case (l, WN.Block(ws, Some(args), content, offset)) =>
-//
-// val snippet = s"{$args ()}"
-// val skeleton = c.parse(snippet)
-//
-// val Function(vparams, body) = skeleton
-//
-// vparams.map(_.foreach { t =>
-// if (t.pos != NoPosition)
-// fragPos.set(t, t.pos.point)
-// })
-// val contentTrees = content.map{compileTree(_)}
-//
-// val func = Function(vparams, q"Seq[$fragType](..$contentTrees)")
-// fragPos.at(func, skeleton.pos.point)
-// val res = q"$l($func)"
-// fragPos.at(res, offset)
-// res
-// }
-//
-// s
-// }
-// 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"""{
-// ..${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)}")
+ val res = q"Seq[$fragType](..${compileBlock(template.parts, template.offset)})"
+ println("::::::::::::::::::::::::::::::::::::::::::::::::")
+ println(res)
+ println("::::::::::::::::::::::::::::::::::::::::::::::::")
+ res
}
} \ No newline at end of file
diff --git a/scalatexApi/src/main/scala/scalatex/ScalatexParser.scala b/scalatexApi/src/main/scala/scalatex/stages/Parser.scala
index a36d2d8..d8878de 100644
--- a/scalatexApi/src/main/scala/scalatex/ScalatexParser.scala
+++ b/scalatexApi/src/main/scala/scalatex/stages/Parser.scala
@@ -1,8 +1,9 @@
package scalatex
+package stages
import org.parboiled2._
import torimatomeru.ScalaSyntax
-
+import Util._
trait Ast{
def offset: Int
}
@@ -21,7 +22,12 @@ object Ast{
case class Args(str: String, offset: Int = 0) extends Sub
}
}
-class ScalatexParser(input: ParserInput, indent: Int = 0) extends ScalaSyntax(input) {
+object Parser{
+ def parse(input: String): Ast.Block = {
+ new Parser(input).Body.run().get
+ }
+}
+class Parser(input: ParserInput, indent: Int = 0) extends ScalaSyntax(input) {
val txt = input.sliceString(0, input.length)
val indentTable = txt.split('\n').map{ s =>
if (s.trim == "") -1
@@ -35,7 +41,7 @@ class ScalatexParser(input: ParserInput, indent: Int = 0) extends ScalaSyntax(in
def cursorNextIndent() = {
nextIndentTable(txt.take(cursor).count(_ == '\n'))
}
- println("INDENT " + indent)
+
def TextNot(chars: String) = rule {
capture(oneOrMore(noneOf(chars + "\n") | "@@")) ~> {
x => Ast.Block.Text(x.replace("@@", "@"))
@@ -47,13 +53,13 @@ class ScalatexParser(input: ParserInput, indent: Int = 0) extends ScalaSyntax(in
}
def BlankLine = rule{ '\n' ~ zeroOrMore(' ') ~ &('\n') }
def Indent = rule{ '\n' ~ indent.times(' ') ~ zeroOrMore(' ') }
- def LoneScalaChain: Rule1[Ast.Chain] = rule {
- Indent ~
+ def LoneScalaChain: Rule2[Ast.Block.Text, Ast.Chain] = rule {
+ (capture(Indent) ~> (Ast.Block.Text(_))) ~
ScalaChain ~
zeroOrMore(BlankLine) ~
test(cursorNextIndent() > indent) ~
runSubParser {
- new ScalatexParser(_, cursorNextIndent()).Body
+ new Parser(_, cursorNextIndent()).Body
} ~> { (chain: Ast.Chain, body: Ast.Block) =>
chain.copy(parts = chain.parts :+ body)
}
@@ -63,7 +69,7 @@ class ScalatexParser(input: ParserInput, indent: Int = 0) extends ScalaSyntax(in
Code ~ zeroOrMore(Extension) ~> {Ast.Chain(_, _)}
}
def Extension: Rule1[Ast.Chain.Sub] = rule {
- (capture('.' ~ Id) ~> (Ast.Chain.Prop(_))) |
+ ('.' ~ capture(Id) ~> (Ast.Chain.Prop(_))) |
(capture(TypeArgs2) ~> (Ast.Chain.TypeArgs(_))) |
(capture(ArgumentExprs2) ~> (Ast.Chain.Args(_))) |
TBlock
@@ -78,14 +84,14 @@ class ScalatexParser(input: ParserInput, indent: Int = 0) extends ScalaSyntax(in
def TBlock = rule{ '{' ~ Body ~ '}' }
def Body = rule{
- zeroOrMore(
- LoneScalaChain |
- TextNot("@}") |
- (capture(Indent) ~> (Ast.Block.Text(_))) |
- (capture(BlankLine) ~> (Ast.Block.Text(_))) |
- ScalaChain
+ oneOrMore(
+ LoneScalaChain ~> (Seq(_, _)) |
+ TextNot("@}") ~> (Seq(_)) |
+ (capture(Indent) ~> (x => Seq(Ast.Block.Text(x)))) |
+ (capture(BlankLine) ~> (x => Seq(Ast.Block.Text(x)))) |
+ ScalaChain ~> (Seq(_))
) ~> {x =>
- Ast.Block(x)
+ Ast.Block(x.flatten)
}
}
}
diff --git a/scalatexApi/src/main/scala/torimatomeru/ScalaSyntax.scala b/scalatexApi/src/main/scala/torimatomeru/ScalaSyntax.scala
index 4b94033..5bbd0af 100644
--- a/scalatexApi/src/main/scala/torimatomeru/ScalaSyntax.scala
+++ b/scalatexApi/src/main/scala/torimatomeru/ScalaSyntax.scala
@@ -6,7 +6,7 @@ import org.parboiled2._
class ScalaSyntax(val input: ParserInput) extends Parser with Basic with Identifiers with Literals {
- def Whitespace = rule { zeroOrMore(WhitespaceChar | Comment | Newline) }
+ def Whitespace = rule { zeroOrMore(WhitespaceChar | Comment) }
/**
* Every token handles space at the end.
diff --git a/scalatexApi/src/test/scala/scalatex/BasicTests.scala b/scalatexApi/src/test/scala/scalatex/BasicTests.scala
index 2ecf9c4..91291dd 100644
--- a/scalatexApi/src/test/scala/scalatex/BasicTests.scala
+++ b/scalatexApi/src/test/scala/scalatex/BasicTests.scala
@@ -1,16 +1,16 @@
-//package scalatex
-//import utest._
-//import scalatex.stages._
-//import scalatags.Text.all._
-//
-//
-///**
-//* Created by haoyi on 7/14/14.
-//*/
-//object BasicTests extends TestSuite{
-// import TestUtil._
-//
-// val tests = TestSuite{
+package scalatex
+import utest._
+import scalatex.stages._
+import scalatags.Text.all._
+
+
+/**
+* Created by haoyi on 7/14/14.
+*/
+object BasicTests extends TestSuite{
+ import TestUtil._
+
+ val tests = TestSuite{
//
// 'helloWorld{
// object omg {
@@ -25,7 +25,7 @@
// "|i<b>am</b>cowhearmemoo|"
// )
// }
-// 'interpolation{
+ 'interpolation{
// 'chained-check(
// tw("omg @scala.math.pow(0.5, 3) wtf"),
// "omg 0.125 wtf"
@@ -47,7 +47,7 @@
// omgomg
// """
// )
-// }
+ }
// 'imports{
// object Whee{
// def func(x: Int) = x * 2
@@ -421,5 +421,5 @@
// )
// }
// }
-// }
-//}
+ }
+}
diff --git a/scalatexApi/src/test/scala/scalatex/ParserTests.scala b/scalatexApi/src/test/scala/scalatex/ParserTests.scala
index c28cdbd..b6e64ec 100644
--- a/scalatexApi/src/test/scala/scalatex/ParserTests.scala
+++ b/scalatexApi/src/test/scala/scalatex/ParserTests.scala
@@ -4,16 +4,15 @@ package scalatex
import org.parboiled2._
import torimatomeru.ScalaSyntax
-import scalatex.Ast.Block.Text
-import scalatex.Ast.Chain.Args
+import scalatex.stages.{Parser, Ast}
+import Ast.Block.Text
+import Ast.Chain.Args
object ParserTests extends utest.TestSuite{
import Ast._
import utest._
- def check[T](input: String, parse: ScalatexParser => scala.util.Try[T], expected: T) = {
- val parsed = parse(new ScalatexParser(input))
- println(parsed.get)
- println(expected)
+ def check[T](input: String, parse: Parser => scala.util.Try[T], expected: T) = {
+ val parsed = parse(new Parser(input))
assert(parsed.get == expected)
}
def tests = TestSuite{
@@ -47,9 +46,9 @@ object ParserTests extends utest.TestSuite{
'Chain{
* - check("@omg.bbq[omg].fff[fff](123) ", _.ScalaChain.run(),
Chain("omg",Seq(
- Chain.Prop(".bbq"),
+ Chain.Prop("bbq"),
Chain.TypeArgs("[omg]"),
- Chain.Prop(".fff"),
+ Chain.Prop("fff"),
Chain.TypeArgs("[fff]"),
Chain.Args("(123)")
))
@@ -57,7 +56,7 @@ object ParserTests extends utest.TestSuite{
* - check("@omg{bbq}.cow(moo){a @b}\n", _.ScalaChain.run(),
Chain("omg",Seq(
Block(Seq(Block.Text("bbq"))),
- Chain.Prop(".cow"),
+ Chain.Prop("cow"),
Chain.Args("(moo)"),
Block(Seq(Block.Text("a "), Chain("b", Nil)))
))
@@ -72,11 +71,14 @@ object ParserTests extends utest.TestSuite{
| @lol""".stripMargin,
_.Body.run(),
Block(Seq(
+ Text("\n"),
Chain("omg",Seq(Block(Seq(
+ Text("\n "),
Chain("wtf",Seq(Block(Seq(
+ Text("\n "),
Chain("bbq",Seq(Block(Seq(
- Chain("lol",Seq(Block(Seq(
- ))))
+ Text("\n "),
+ Chain("lol",Seq())
))))
))))
))))
@@ -89,14 +91,14 @@ object ParserTests extends utest.TestSuite{
|@bbq""".stripMargin,
_.Body.run(),
Block(Seq(
+ Text("\n"),
Chain("omg",Seq(Block(
Seq(
Text("\n "),
Chain("wtf",Seq()))
))),
- Chain("bbq",
- Seq(Block(Seq()))
- )
+ Text("\n"),
+ Chain("bbq", Seq())
))
)
* - check(
@@ -106,6 +108,7 @@ object ParserTests extends utest.TestSuite{
|bbq""".stripMargin,
_.Body.run(),
Block(Seq(
+ Text("\n"),
Chain("omg",Seq(
Args("""("lol", 1, 2)"""),
Block(Seq(
@@ -116,27 +119,46 @@ object ParserTests extends utest.TestSuite{
Text("bbq")
))
)
+// * - check(
+// """
+// |@omg("lol",
+// |1,
+// | 2
+// | )
+// | wtf
+// |bbq""".stripMargin,
+// _.Body.run(),
+// Block(Seq(
+// Chain("omg",Seq(
+// Args("(\"lol\",\n1,\n 2\n )"),
+// Block(Seq(
+// Text("\n "), Text("wtf")
+// ))
+// )),
+// Text("\n"),
+// Text("bbq")
+// ))
+// )
* - check(
"""
- |@omg("lol",
- |1,
- | 2
- | )
- | wtf
- |bbq""".stripMargin,
+ |@{"lol" * 3}
+ |@{
+ | val omg = "omg"
+ | omg * 2
+ |}""".stripMargin,
_.Body.run(),
Block(Seq(
- Chain("omg",Seq(
- Args("(\"lol\",\n1,\n 2\n )"),
- Block(Seq(
- Text("\n "), Text("wtf")
- ))
- )),
Text("\n"),
- Text("bbq")
+ Chain("{\"lol\" * 3}", Seq()),
+ Text("\n"),
+ Chain("""{
+ | val omg = "omg"
+ | omg * 2
+ |}""".stripMargin,
+ Seq()
+ )
))
)
-
}
}