From c0b06c5e3f3caa60736794143f278c2af5cbe9fb Mon Sep 17 00:00:00 2001 From: Li Haoyi Date: Sat, 1 Nov 2014 10:15:13 -0700 Subject: scalatex tests pass --- book/src/main/scalatex/book/WebPage.scalatex | 8 +- scalatexApi/src/main/scala/scalatex/package.scala | 42 +++++-- .../src/main/scala/scalatex/stages/Compiler.scala | 1 + .../main/scala/scalatex/stages/IndentHandler.scala | 16 ++- .../src/test/scala/scalatex/BasicTests.scala | 47 ++++---- .../src/test/scala/scalatex/ErrorTests.scala | 132 ++++++++++----------- 6 files changed, 138 insertions(+), 108 deletions(-) diff --git a/book/src/main/scalatex/book/WebPage.scalatex b/book/src/main/scalatex/book/WebPage.scalatex index aa45699..c27981e 100644 --- a/book/src/main/scalatex/book/WebPage.scalatex +++ b/book/src/main/scalatex/book/WebPage.scalatex @@ -1,3 +1,5 @@ + + @p Most web applications aren't neat little games which live on a single canvas: they are large, structured HTML pages, which involve displaying data (whether from the user or from the web) in multiple ways, while allowing the user to make changes to the data that can be saved back to whatever remote web-service/database it came from. @@ -17,6 +19,7 @@ @div(id:="div1") @script("HelloWorld0().main(document.getElementById('div1'))") + @p This approach works, as the above example shows, but has a couple of disadvantages: @@ -125,8 +128,5 @@ @p Now that you've gotten a quick overview of the kinds of things you can do with Scalatags, let's move on to the next section of our hands-on tutorial... -@sect{Using Web Services} - - @p - One half of the web application faces forwards towards the user, managing and rendering HTML or Canvas for the user to view and interact with. Another half faces backwards, talking to various web-services or databases which turn the application from a standalone-widget into part of a greater whole. We've already seen how to make the front half, let's now talk about working with the back half. +@sect{Using Web Services} diff --git a/scalatexApi/src/main/scala/scalatex/package.scala b/scalatexApi/src/main/scala/scalatex/package.scala index 5732411..d86976e 100644 --- a/scalatexApi/src/main/scala/scalatex/package.scala +++ b/scalatexApi/src/main/scala/scalatex/package.scala @@ -15,11 +15,13 @@ package object scalatex { def twf(filename: String): Frag = macro Internals.applyMacroFile object Internals { + def twRuntimeErrors(expr: String): Frag = macro applyMacroRuntimeErrors def twDebug(expr: String): Frag = macro applyMacroDebug - def applyMacro(c: Context)(expr: c.Expr[String]): c.Expr[Frag] = applyMacroFull(c)(expr, false) + def applyMacro(c: Context)(expr: c.Expr[String]): c.Expr[Frag] = applyMacroFull(c)(expr, false, false) + def applyMacroDebug(c: Context)(expr: c.Expr[String]): c.Expr[Frag] = applyMacroFull(c)(expr, false, true) - def applyMacroDebug(c: Context)(expr: c.Expr[String]): c.Expr[Frag] = applyMacroFull(c)(expr, true) + def applyMacroRuntimeErrors(c: Context)(expr: c.Expr[String]): c.Expr[Frag] = applyMacroFull(c)(expr, true, false) def applyMacroFile(c: Context)(filename: c.Expr[String]): c.Expr[Frag] = { import c.universe._ @@ -34,34 +36,46 @@ package object scalatex { txt.toCharArray ) - compileThing(c)(txt, sourceFile, 0, false) + compileThing(c)(txt, sourceFile, 0, false, 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] = { + private[this] def applyMacroFull(c: Context) + (expr: c.Expr[String], + runtimeErrors: Boolean, + debug: Boolean) + : c.Expr[Frag] = { import c.universe._ val s = expr.tree - .asInstanceOf[Literal] - .value - .value - .asInstanceOf[String] + .asInstanceOf[Literal] + .value + .value + .asInstanceOf[String] val stringStart = expr.tree .pos .lineContent .drop(expr.tree.pos.column) .take(2) + val indented = s |> stages.IndentHandler + if (debug) println(indented) compileThing(c)( - s |> stages.IndentHandler, + indented, expr.tree.pos.source, expr.tree.pos.point + (if (stringStart == "\"\"") 1 else -1), - runtimeErrors + runtimeErrors, + debug ) } } - def compileThing(c: Context)(s: String, source: SourceFile, point: Int, runtimeErrors: Boolean) = { + def compileThing(c: Context) + (s: String, + source: SourceFile, + point: Int, + runtimeErrors: Boolean, + debug: Boolean) = { import c.universe._ def compile(s: String): c.Tree = { val realPos = new OffsetPosition(source, point).asInstanceOf[c.universe.Position] @@ -71,13 +85,15 @@ package object scalatex { import c.Position try { - c.Expr(c.typeCheck(compile(s))) + val compiled = compile(s) + if (debug) println(compiled) + c.Expr[Frag](c.typeCheck(compiled)) } 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)""") + c.Expr( q"""throw scalatex.Internals.DebugFailure($msg, $posMsg)""") } } diff --git a/scalatexApi/src/main/scala/scalatex/stages/Compiler.scala b/scalatexApi/src/main/scala/scalatex/stages/Compiler.scala index 85bb0f0..fcbe08b 100644 --- a/scalatexApi/src/main/scala/scalatex/stages/Compiler.scala +++ b/scalatexApi/src/main/scala/scalatex/stages/Compiler.scala @@ -75,6 +75,7 @@ object Compiler{ 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 => diff --git a/scalatexApi/src/main/scala/scalatex/stages/IndentHandler.scala b/scalatexApi/src/main/scala/scalatex/stages/IndentHandler.scala index 180d134..697ccc3 100644 --- a/scalatexApi/src/main/scala/scalatex/stages/IndentHandler.scala +++ b/scalatexApi/src/main/scala/scalatex/stages/IndentHandler.scala @@ -49,8 +49,15 @@ object IndentHandler extends (String => String){ // println(spaces, current) val declRemainder = successRemainder(Parser.parse(current.trim, _.templateDeclaration())) - - val exprRemainder = successRemainder(Parser.parse(current.trim, _.expression())).filter(_ == current.trim) +// println("::::::" + current.trim) +// println(successRemainder(Parser.parse(current.trim, _.expression()))) + val exprRemainder = successRemainder(Parser.parse(current.trim, _.expression())).filter { + // Either it takes up the entire line or the line ends with a =>, in which case + // we assume the guy wanted to pass a lambda + x => +// println(x) + x == current.trim || current.trim.endsWith("=>") + } /** @@ -81,14 +88,13 @@ object IndentHandler extends (String => String){ val newFirst = if (!before.startsWith("@else")) before else before.dropRight("@else".length)+ "} else" - + println(before, after, delta) 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 = diff --git a/scalatexApi/src/test/scala/scalatex/BasicTests.scala b/scalatexApi/src/test/scala/scalatex/BasicTests.scala index 0488ef6..f432e51 100644 --- a/scalatexApi/src/test/scala/scalatex/BasicTests.scala +++ b/scalatexApi/src/test/scala/scalatex/BasicTests.scala @@ -74,7 +74,6 @@ object BasicTests extends TestSuite{ 'attributes{ check( tw(""" - @div(id:="my-id") omg @div(id:="my-id"){ omg } @div(id:="my-id") omg @@ -82,7 +81,6 @@ object BasicTests extends TestSuite{ """ omg omg - omg """ ) } @@ -134,11 +132,12 @@ object BasicTests extends TestSuite{ check( tw(""" - @h1 Hello World - @h2 hello - @world + @h1 + Hello World + @h2 + hello @world @h3 - Cow + Cow """), """

HelloWorld

@@ -157,9 +156,9 @@ object BasicTests extends TestSuite{ @div Cow """), """ -

HelloWorld

-

helloworld

-

Cow

+

HelloWorld +

helloworld +

Cow """ ) } @@ -213,7 +212,7 @@ object BasicTests extends TestSuite{
Hello
-

WORLD!!!lol

+

WORLD!!!lol

Header2

@@ -227,13 +226,15 @@ object BasicTests extends TestSuite{ tw(""" @ul @things.map { x => - @li @x + @li + @x } """), - tw(""" + Internals.twDebug(""" @ul - @things.map x => - @li @x + @things.map x => + @li + @x """), """ @@ -319,7 +320,8 @@ object BasicTests extends TestSuite{ tw(""" @if(false) Hello - @else lols + @else + lols @p """), "lols

" @@ -330,7 +332,8 @@ object BasicTests extends TestSuite{ @div @if(true) Hello - @else lols + @else + lols """), "
Hello
" ) @@ -338,15 +341,19 @@ object BasicTests extends TestSuite{ * - check( tw(""" @div - @if(true) Hello - @else lols + @if(true) + Hello + @else + lols """), "
Hello
" ) * - check( tw(""" - @if(false) Hello - @else lols + @if(false) + Hello + @else + lols """), "lols" ) diff --git a/scalatexApi/src/test/scala/scalatex/ErrorTests.scala b/scalatexApi/src/test/scala/scalatex/ErrorTests.scala index a122dd2..f9e9308 100644 --- a/scalatexApi/src/test/scala/scalatex/ErrorTests.scala +++ b/scalatexApi/src/test/scala/scalatex/ErrorTests.scala @@ -3,7 +3,7 @@ package scalatex import utest._ import scalatex.stages._ import scalatags.Text.all._ -import scalatex.Internals.{DebugFailure, twDebug} +import scalatex.Internals.{DebugFailure, twRuntimeErrors} /** * Created by haoyi on 7/14/14. @@ -31,109 +31,109 @@ object ErrorTests extends TestSuite{ 'simple - check( - twDebug("omg @notInScope lol"), + twRuntimeErrors("omg @notInScope lol"), """not found: value notInScope""", """ - twDebug("omg @notInScope lol"), - ^ + twRuntimeErrors("omg @notInScope lol"), + ^ """ ) 'chained{ 'properties { * - check( - twDebug("omg @math.lol lol"), + twRuntimeErrors("omg @math.lol lol"), """object lol is not a member of package math""", """ - twDebug("omg @math.lol lol"), - ^ + twRuntimeErrors("omg @math.lol lol"), + ^ """ ) * - check( - twDebug("omg @math.E.lol lol"), + twRuntimeErrors("omg @math.E.lol lol"), """value lol is not a member of Double""", """ - twDebug("omg @math.E.lol lol"), - ^ + twRuntimeErrors("omg @math.E.lol lol"), + ^ """ ) * - check( - twDebug("omg @_root_.scala.math.lol lol"), + twRuntimeErrors("omg @_root_.scala.math.lol lol"), """object lol is not a member of package math""", """ - twDebug("omg @_root_.scala.math.lol lol"), - ^ + twRuntimeErrors("omg @_root_.scala.math.lol lol"), + ^ """ ) * - check( - twDebug("omg @_root_.scala.gg.lol lol"), + twRuntimeErrors("omg @_root_.scala.gg.lol lol"), """object gg is not a member of package scala""", """ - twDebug("omg @_root_.scala.gg.lol lol"), - ^ + twRuntimeErrors("omg @_root_.scala.gg.lol lol"), + ^ """ ) * - check( - twDebug("omg @_root_.ggnore.math.lol lol"), + twRuntimeErrors("omg @_root_.ggnore.math.lol lol"), """object ggnore is not a member of package """, """ - twDebug("omg @_root_.ggnore.math.lol lol"), - ^ + twRuntimeErrors("omg @_root_.ggnore.math.lol lol"), + ^ """ ) } 'calls{ * - check( - twDebug("@scala.QQ.abs(-10).tdo(10).sum.z"), + twRuntimeErrors("@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"), - ^ + twRuntimeErrors("@scala.QQ.abs(-10).tdo(10).sum.z"), + ^ """ ) * - check( - twDebug("@scala.math.abs(-10).tdo(10).sum.z"), + twRuntimeErrors("@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"), - ^ + twRuntimeErrors("@scala.math.abs(-10).tdo(10).sum.z"), + ^ """ ) * - check( - twDebug("@scala.math.abs(-10).to(10).sum.z"), + twRuntimeErrors("@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"), - ^ + twRuntimeErrors("@scala.math.abs(-10).to(10).sum.z"), + ^ """ ) * - check( - twDebug("@scala.math.abs(-10).to(10).sum.z()"), + twRuntimeErrors("@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()"), - ^ + twRuntimeErrors("@scala.math.abs(-10).to(10).sum.z()"), + ^ """ ) * - check( - twDebug("@scala.math.abs(-10).cow.sum.z"), + twRuntimeErrors("@scala.math.abs(-10).cow.sum.z"), "value cow is not a member of Int", """ - twDebug("@scala.math.abs(-10).cow.sum.z"), - ^ + twRuntimeErrors("@scala.math.abs(-10).cow.sum.z"), + ^ """ ) * - check( - twDebug("@scala.smath.abs.cow.sum.z"), + twRuntimeErrors("@scala.smath.abs.cow.sum.z"), "object smath is not a member of package scala", """ - twDebug("@scala.smath.abs.cow.sum.z"), - ^ + twRuntimeErrors("@scala.smath.abs.cow.sum.z"), + ^ """ ) * - check( - twDebug(""" + twRuntimeErrors(""" I am cow hear me moo @scala.math.abs(-10).tdo(10).sum.z I weigh twice as much as you @@ -147,20 +147,20 @@ object ErrorTests extends TestSuite{ } 'callContents{ * - check( - twDebug("@scala.math.abs((1, 2).wtf)"), + twRuntimeErrors("@scala.math.abs((1, 2).wtf)"), "value wtf is not a member of (Int, Int)", """ - twDebug("@scala.math.abs((1, 2).wtf)"), - ^ + twRuntimeErrors("@scala.math.abs((1, 2).wtf)"), + ^ """ ) * - check( - twDebug("@scala.math.abs((1, 2).swap._1.toString().map(_.toString.wtf))"), + twRuntimeErrors("@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))"), - ^ + twRuntimeErrors("@scala.math.abs((1, 2).swap._1.toString().map(_.toString.wtf))"), + ^ """ ) } @@ -168,33 +168,33 @@ object ErrorTests extends TestSuite{ 'ifElse{ 'oneLine { * - check( - twDebug("@if(math > 10){ 1 }else{ 2 }"), + twRuntimeErrors("@if(math > 10){ 1 }else{ 2 }"), "object > is not a member of package math", """ - twDebug("@if(math > 10){ 1 }else{ 2 }"), - ^ + twRuntimeErrors("@if(math > 10){ 1 }else{ 2 }"), + ^ """ ) * - check( - twDebug("@if(true){ (@math.pow(10)) * 10 }else{ 2 }"), + twRuntimeErrors("@if(true){ (@math.pow(10)) * 10 }else{ 2 }"), "Unspecified value parameter y", """ - twDebug("@if(true){ (@math.pow(10)) * 10 }else{ 2 }"), - ^ + twRuntimeErrors("@if(true){ (@math.pow(10)) * 10 }else{ 2 }"), + ^ """ ) * - check( - twDebug("@if(true){ * 10 }else{ @math.sin(3, 4, 5) }"), + twRuntimeErrors("@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) }"), - ^ + twRuntimeErrors("@if(true){ * 10 }else{ @math.sin(3, 4, 5) }"), + ^ """ ) } 'multiLine{ * - check( - twDebug(""" + twRuntimeErrors(""" Ho Ho Ho @if(math != 10) @@ -210,7 +210,7 @@ object ErrorTests extends TestSuite{ """ ) * - check( - twDebug(""" + twRuntimeErrors(""" Ho Ho Ho @if(4 != 10) @@ -226,7 +226,7 @@ object ErrorTests extends TestSuite{ """ ) * - check( - twDebug(""" + twRuntimeErrors(""" Ho Ho Ho @if(12 != 10) @@ -246,26 +246,26 @@ object ErrorTests extends TestSuite{ 'forLoop{ 'oneLine{ 'header - check( - twDebug("omg @for(x <- (0 + 1 + 2) omglolol (10 + 11 + 2)){ hello }"), + twRuntimeErrors("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 }"), - ^ + twRuntimeErrors("omg @for(x <- (0 + 1 + 2) omglolol (10 + 11 + 2)){ hello }"), + ^ """ ) 'body - check( - twDebug("omg @for(x <- 0 until 10){ @((x, 2) + (1, 2)) }"), + twRuntimeErrors("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)) }"), - ^ + twRuntimeErrors("omg @for(x <- 0 until 10){ @((x, 2) + (1, 2)) }"), + ^ """ ) } 'multiLine{ 'body - check( - twDebug(""" + twRuntimeErrors(""" omg @for(x <- 0 until 10) I am cow hear me moo @@ -281,7 +281,7 @@ object ErrorTests extends TestSuite{ } 'multiLine{ 'missingVar - check( - twDebug(""" + twRuntimeErrors(""" omg @notInScope lol """), """not found: value notInScope""", @@ -291,7 +291,7 @@ object ErrorTests extends TestSuite{ """ ) 'wrongType - check( - twDebug(""" + twRuntimeErrors(""" omg @{() => ()} lol """), """type mismatch""", @@ -302,7 +302,7 @@ object ErrorTests extends TestSuite{ ) 'bigExpression - check( - twDebug(""" + twRuntimeErrors(""" @{ val x = 1 + 2 val y = new Object() -- cgit v1.2.3