diff options
author | Li Haoyi <haoyi@dropbox.com> | 2014-11-03 22:13:10 -0800 |
---|---|---|
committer | Li Haoyi <haoyi@dropbox.com> | 2014-11-03 22:13:10 -0800 |
commit | ed923d3445e1f304949eee2ee414987d98756a86 (patch) | |
tree | 42f81cf36afc3ce34b31ca5a4d15432ac56f4445 /scalatexApi/src | |
parent | 2ca177afb55232ae762051d9a2cc205629000e70 (diff) | |
download | hands-on-scala-js-ed923d3445e1f304949eee2ee414987d98756a86.tar.gz hands-on-scala-js-ed923d3445e1f304949eee2ee414987d98756a86.tar.bz2 hands-on-scala-js-ed923d3445e1f304949eee2ee414987d98756a86.zip |
Tests pass!
Diffstat (limited to 'scalatexApi/src')
-rw-r--r-- | scalatexApi/src/main/scala/scalatex/package.scala | 15 | ||||
-rw-r--r-- | scalatexApi/src/main/scala/scalatex/stages/Compiler.scala | 150 | ||||
-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.scala | 2 | ||||
-rw-r--r-- | scalatexApi/src/test/scala/scalatex/BasicTests.scala | 34 | ||||
-rw-r--r-- | scalatexApi/src/test/scala/scalatex/ParserTests.scala | 78 |
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() + ) )) ) - } } |