diff options
Diffstat (limited to 'scalatex/api/src/main/scala/scalatex/package.scala')
-rw-r--r-- | scalatex/api/src/main/scala/scalatex/package.scala | 98 |
1 files changed, 98 insertions, 0 deletions
diff --git a/scalatex/api/src/main/scala/scalatex/package.scala b/scalatex/api/src/main/scala/scalatex/package.scala new file mode 100644 index 0000000..1f13e63 --- /dev/null +++ b/scalatex/api/src/main/scala/scalatex/package.scala @@ -0,0 +1,98 @@ +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.{Parser, Compiler} +import scala.language.experimental.macros +import acyclic.file + +package object scalatex { + /** + * 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 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, false) + def applyMacroDebug(c: Context)(expr: c.Expr[String]): c.Expr[Frag] = applyMacroFull(c)(expr, false, 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._ + val fileName = filename.tree + .asInstanceOf[Literal] + .value + .value + .asInstanceOf[String] + val txt = io.Source.fromFile(fileName).mkString + val sourceFile = new BatchSourceFile( + new PlainFile(fileName), + txt.toCharArray + ) + + 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, + debug: Boolean) + : c.Expr[Frag] = { + import c.universe._ + val scalatexFragment = expr.tree + .asInstanceOf[Literal] + .value + .value + .asInstanceOf[String] + val stringStart = + expr.tree + .pos + .lineContent + .drop(expr.tree.pos.column) + .take(2) + compileThing(c)( + scalatexFragment, + expr.tree.pos.source, + expr.tree.pos.point + (if (stringStart == "\"\"") 1 else -1), + runtimeErrors, + debug + ) + } + } + + def compileThing(c: Context) + (scalatexSource: 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] + + Compiler(c)(realPos, Parser.tupled(stages.Trim(s))) + } + + + import c.Position + try { + val compiled = compile(scalatexSource) + 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 scalatex.Internals.DebugFailure($msg, $posMsg)""") + } + } + } +} |