Skip to content

HTTPS clone URL

Subversion checkout URL

You can clone with
or
.
Download ZIP
Browse files

Started implementing keyword arguments

  • Loading branch information...
commit 317f3fbec806e2fa6db776dcb66fee8b5c809892 1 parent f180c51
@davegurnell authored
View
20 src/main/scala/tipi/core/Ast.scala
@@ -19,12 +19,20 @@ object Range {
val Empty = Range(Nil)
}
-trait Argument[T] { val value: T }
-case class IdArgument(val value: Id) extends Argument[Id]
-case class StringArgument(val value: String) extends Argument[String]
-case class IntArgument(val value: Int) extends Argument[Int]
-case class DoubleArgument(val value: Double) extends Argument[Double]
-case class BooleanArgument(val value: Boolean) extends Argument[Boolean]
+trait Argument[T] {
+ val name: Id
+ val value: T
+ override def toString = "%s=%s".format(name.name, value)
+}
+case class IdArgument(val name: Id, val value: Id) extends Argument[Id]
+case class StringArgument(val name: Id, val value: String) extends Argument[String]
+case class IntArgument(val name: Id, val value: Int) extends Argument[Int]
+case class DoubleArgument(val name: Id, val value: Double) extends Argument[Double]
+case class BooleanArgument(val name: Id, val value: Boolean) extends Argument[Boolean]
+case class UnitArgument(val name: Id) extends Argument[Unit] {
+ val value = ()
+ override def toString = name.name
+}
object Argument {
def unapply[T](arg: Argument[T]): Option[T] = {
View
13 src/main/scala/tipi/core/Env.scala
@@ -49,13 +49,13 @@ object Env {
def apply(in: (Env, Doc)): (Env, Doc) = {
val (env, doc) = in
doc match {
- case Block(_, IdArgument(name) :: value :: Nil, Range.Empty) =>
- (env + (name -> Transform.argToTransform(value, env)), Range.Empty)
+ case Block(_, args, Range.Empty) =>
+ (Env.fromArgs(env, args), Range.Empty)
// case block @ Block(_, IdArgument(name) :: args, Range.Empty) =>
// sys.error("Empty 'def' block")
- case block @ Block(_, IdArgument(name) :: args, _) =>
+ case block @ Block(_, UnitArgument(name) :: _, _) =>
(env + (name -> Template(block, env)), Range.Empty)
case _ => sys.error("Invalid 'def' block")
@@ -65,4 +65,11 @@ object Env {
override def toString = "define"
}
))
+
+ def fromArgs(initial: Env, args: List[Argument[_]]): Env = {
+ args.foldLeft(initial) {
+ (accum, arg) =>
+ accum + (arg.name -> Transform.argToTransform(arg, accum))
+ }
+ }
}
View
19 src/main/scala/tipi/core/Parser.scala
@@ -64,20 +64,15 @@ case class Parser(
private def optWs = "[ \t]*".r
def arg: Parser[Argument[_]] =
- (double ^^ doubleArgument) |
- (int ^^ intArgument) |
- (boolean ^^ booleanArgument) |
- (string ^^ stringArgument) |
- (id ^^ idArgument)
-
- private def idArgument(value: Id): Argument[_] = IdArgument(value)
- private def stringArgument(value: String): Argument[_] = StringArgument(value)
- private def intArgument(value: Int): Argument[_] = IntArgument(value)
- private def doubleArgument(value: Double): Argument[_] = DoubleArgument(value)
- private def booleanArgument(value: Boolean): Argument[_] = BooleanArgument(value)
+ ( (id ~ (optWs ~ "=" ~ optWs) ~ double ) ^^ { case name ~ _ ~ value => DoubleArgument(name, value) : Argument[_] } ) |
+ ( (id ~ (optWs ~ "=" ~ optWs) ~ int ) ^^ { case name ~ _ ~ value => IntArgument(name, value) : Argument[_] } ) |
+ ( (id ~ (optWs ~ "=" ~ optWs) ~ boolean) ^^ { case name ~ _ ~ value => BooleanArgument(name, value) : Argument[_] } ) |
+ ( (id ~ (optWs ~ "=" ~ optWs) ~ string ) ^^ { case name ~ _ ~ value => StringArgument(name, value) : Argument[_] } ) |
+ ( (id ~ (optWs ~ "=" ~ optWs) ~ id ) ^^ { case name ~ _ ~ value => IdArgument(name, value) : Argument[_] } ) |
+ ( (id ) ^^ { case name => UnitArgument(name) : Argument[_] } )
def id: Parser[Id] =
- "[a-zA-Z0-9_$-]+".r ^^ Id
+ "[a-zA-Z_$][a-zA-Z0-9_$-]*".r ^^ Id
def string: Parser[String] =
(""" "([^\\"]|(\\\\)|(\\"))*" """.trim.r ^^ (str => str.substring(1, str.length - 1).replace("\\\\", "\\").replace("\\\"", "\"")))
View
50 src/main/scala/tipi/core/Template.scala
@@ -2,45 +2,29 @@ package tipi.core
import com.weiglewilczek.slf4s.Logging
-object Template {
- object IdArguments {
- def unapply(args: List[Argument[_]]): Option[List[Id]] = {
- args match {
- case Nil => None
- case args if args.forall(_.isInstanceOf[IdArgument]) =>
- Some(args.collect{ case IdArgument(id) => id })
- }
- }
- }
-}
-
case class Template(val defn: Block, val globalEnv: Env) extends Transform with TransformImplicits with Logging{
import Template._
- def defnArgs: List[Id] = {
- defn.args match {
- case IdArguments(ids) => ids
- case other => sys.error("Bad template header: " + other)
- }
- }
+ lazy val defnName: Id = defn.args.head.name
+ lazy val defnEnv: Env = Env.fromArgs(globalEnv, defn.args.tail)
- lazy val tagName: Id = defnArgs.head
- lazy val argNames: List[Id] = defnArgs.tail
+ logger.debug(
+ """
+ |Define %s
+ | %s
+ | %s
+ """.trim.stripMargin.format(defnName.name, defnEnv, defn)
+ )
def localEnv(callingEnv: Env, doc: Block): Env = {
- val thisKeywordEnv =
- Env.empty + (Id("this") -> doc.body)
+ val thisKwEnv = Env.empty + (Id("this") -> doc.body)
- val argsEnv =
- argNames.zip(doc.args).foldLeft(Env.empty) {
- case (accum, (name, value)) =>
- accum + (name -> Transform.argToTransform(value, globalEnv))
- }
+ val argsEnv = Env.fromArgs(defnEnv, doc.args)
val bindEnv = {
def loop(env: Env, doc: Doc): Env = {
doc match {
- case Block(Id("bind"), IdArgument(name) :: _, body) =>
+ case Block(Id("bind"), UnitArgument(name) :: _, body) =>
env + (name -> Expand((callingEnv, body))._2)
case Range(children) =>
children.foldLeft(env)(loop)
@@ -51,16 +35,16 @@ case class Template(val defn: Block, val globalEnv: Env) extends Transform with
loop(Env.empty, doc.body)
}
- val bindKeywordEnv =
+ val bindKwEnv =
Env.empty + (Id("bind") -> Transform.identity)
- globalEnv ++ thisKeywordEnv ++ argsEnv ++ bindEnv ++ bindKeywordEnv
+ argsEnv ++ bindEnv ++ thisKwEnv ++ bindKwEnv
}
def isDefinedAt(in: (Env, Doc)) = {
val (env, doc) = in
doc match {
- case Block(name, argValues, _) if name == tagName && argValues.length == argNames.length => true
+ case Block(name, argValues, _) if name == defnName => true
case _ => false
}
}
@@ -72,10 +56,10 @@ case class Template(val defn: Block, val globalEnv: Env) extends Transform with
logger.debug(
"""
- |Template %s
+ |Call %s
| %s
| %s
- """.trim.stripMargin.format(tagName, localEnv, inDoc)
+ """.trim.stripMargin.format(defnName.name, localEnv, inDoc)
)
val (_, outDoc) = Expand(localEnv, defn.body)
View
11 src/main/scala/tipi/core/Transform.scala
@@ -23,11 +23,12 @@ object Transform {
def argToTransform(arg: Argument[_], env: Env): Transform = {
arg match {
- case IdArgument(id) => env.bindings.get(id).getOrElse(Transform.constant(Text(id.name)))
- case StringArgument(value) => Transform.constant(Text(value))
- case IntArgument(value) => Transform.constant(Text(value.toString))
- case DoubleArgument(value) => Transform.constant(Text(value.toString))
- case BooleanArgument(value) => Transform.constant(Text(value.toString))
+ case IdArgument(name, value) => env.bindings.get(value).getOrElse(Transform.constant(Range.Empty))
+ case StringArgument(name, value) => Transform.constant(Text(value))
+ case IntArgument(name, value) => Transform.constant(Text(value.toString))
+ case DoubleArgument(name, value) => Transform.constant(Text(value.toString))
+ case BooleanArgument(name, value) => Transform.constant(Text(value.toString))
+ case UnitArgument(name) => Transform.constant(Range.Empty)
}
}
}
View
2  src/test/scala/tipi/core/ComplianceSuite.scala
@@ -24,7 +24,7 @@ class ComplianceSuite extends FunSuite {
(src, des) <- tipiTests
} {
test(src + " => " + des) {
- assert(tipi(resourceSource(src).mkString) === des)
+ assert(tipi(resourceSource(src).mkString) === resourceSource(des).mkString)
}
}
}
View
37 src/test/scala/tipi/core/ExpandSuite.scala
@@ -6,17 +6,24 @@ class ExpandSuite extends FunSuite {
val parser = Parser("{{", "}}")
+ def withLines[T](fn : => T): T = {
+ println("----------------")
+ val ans = fn
+ println("----------------")
+ ans
+ }
+
test("static binding") {
val source =
"""
- |{{def x "x1"}}
+ |{{def x="x1"}}
|{{#def a}}{{x}}{{/def}}
- |{{def x "x2"}}
+ |{{def x="x2"}}
|{{#def b}}{{x}}{{/def}}
|{{a}}{{b}}
""".trim.stripMargin
- assert(
+ assert {
Render(Expand((Env.basic, parser(source).get))) ===
"""
|
@@ -25,15 +32,15 @@ class ExpandSuite extends FunSuite {
|
|x1x2
""".trim.stripMargin
- )
+ }
}
test("function arguments") {
val source =
"""
- |{{def x "x1"}}
+ |{{def x="x1"}}
|{{#def a x}}{{x}}{{/def}}
- |{{a "x2"}}
+ |{{a x="x2"}}
""".trim.stripMargin
assert {
@@ -49,8 +56,8 @@ class ExpandSuite extends FunSuite {
test("function bindings") {
val source =
"""
- |{{def x "x1"}}
- |{{#def a}}{{x}}{{/def}}
+ |{{def x="x1"}}
+ |{{#def a x}}{{x}}{{/def}}
|{{#a}}{{#bind x}}x2{{/bind}}{{/a}}
""".trim.stripMargin
@@ -67,8 +74,8 @@ class ExpandSuite extends FunSuite {
test("variable bound to variable") {
val source =
"""
- |{{def x "x"}}
- |{{def y x}}
+ |{{def x="x"}}
+ |{{def y=x}}
|{{y}}
""".trim.stripMargin
@@ -85,10 +92,10 @@ class ExpandSuite extends FunSuite {
test("bind tags expanded at the call site") {
val source =
"""
- |{{def y "y1"}}
- |{{def z "z1"}}
- |{{#def x}}c{{y}}d{{/def}}
- |{{def z "z2"}}
+ |{{def y="y1"}}
+ |{{def z="z1"}}
+ |{{#def x y}}c{{y}}d{{/def}}
+ |{{def z="z2"}}
|{{#x}}{{#bind y}}{{z}}{{/bind}}{{/x}}
""".trim.stripMargin
@@ -107,7 +114,7 @@ class ExpandSuite extends FunSuite {
test("definition not found") {
val source =
"""
- |{{def x "x"}}
+ |{{def x="x"}}
|{{x}}{{y}}{{x}}
""".trim.stripMargin
View
46 src/test/scala/tipi/core/ParserSuite.scala
@@ -66,42 +66,54 @@ class ParserSuite extends FunSuite {
test("arg") {
import p1._
- assert(runRule(arg, "a") === Some(IdArgument(Id("a"))))
- assert(runRule(arg, "\"a\"") === Some(StringArgument("a")))
- assert(runRule(arg, "\"a\\\"b\"") === Some(StringArgument("a\"b")))
- assert(runRule(arg, "1") === Some(IntArgument(1)))
- assert(runRule(arg, "1.0") === Some(DoubleArgument(1.0)))
- assert(runRule(arg, "true") === Some(BooleanArgument(true)))
+ assert(runRule(arg, "x=a") === Some(IdArgument(Id("x"), Id("a"))))
+ assert(runRule(arg, "x = a") === Some(IdArgument(Id("x"), Id("a"))))
+ assert(runRule(arg, "x=\"a\"") === Some(StringArgument(Id("x"), "a")))
+ assert(runRule(arg, "x = \"a\"") === Some(StringArgument(Id("x"), "a")))
+ assert(runRule(arg, "x=\"a\\\"b\"") === Some(StringArgument(Id("x"), "a\"b")))
+ assert(runRule(arg, "x = \"a\\\"b\"") === Some(StringArgument(Id("x"), "a\"b")))
+ assert(runRule(arg, "x=1") === Some(IntArgument(Id("x"), 1)))
+ assert(runRule(arg, "x = 1") === Some(IntArgument(Id("x"), 1)))
+ assert(runRule(arg, "x=1.0") === Some(DoubleArgument(Id("x"), 1.0)))
+ assert(runRule(arg, "x = 1.0") === Some(DoubleArgument(Id("x"), 1.0)))
+ assert(runRule(arg, "x=true") === Some(BooleanArgument(Id("x"), true)))
+ assert(runRule(arg, "x = true") === Some(BooleanArgument(Id("x"), true)))
+ assert(runRule(arg, "x") === Some(UnitArgument(Id("x"))))
}
test("argList") {
import p1._
- assert(runRule(argList, "a 1 1.0 \"a\" true") === Some(List(
- IdArgument(Id("a")),
- IntArgument(1),
- DoubleArgument(1.0),
- StringArgument("a"),
- BooleanArgument(true)
+ assert(runRule(argList, "i=a j =1 k= 1.0 l m = \"a\" n = true") === Some(List(
+ IdArgument(Id("i"), Id("a")),
+ IntArgument(Id("j"), 1),
+ DoubleArgument(Id("k"), 1.0),
+ UnitArgument(Id("l")),
+ StringArgument(Id("m"), "a"),
+ BooleanArgument(Id("n"), true)
)))
}
test("tag - {{ }}") {
import p1._
assert(runRule(tag, "{{a}}") === Some(SimpleTag(Id("a"), Nil)))
- assert(runRule(tag, "{{ a 1 }}") === Some(SimpleTag(Id("a"), List(IntArgument(1)))))
+ assert(runRule(tag, "{{ a b=1 }}") === Some(SimpleTag(Id("a"), List(IntArgument(Id("b"),1)))))
+ assert(runRule(tag, "{{ a 1 }}") === None)
assert(runRule(tag, "{{#a}}") === Some(OpenTag(Id("a"), Nil)))
- assert(runRule(tag, "{{# a 1 }}") === Some(OpenTag(Id("a"), List(IntArgument(1)))))
+ assert(runRule(tag, "{{# a b=1 }}") === Some(OpenTag(Id("a"), List(IntArgument(Id("b"), 1)))))
+ assert(runRule(tag, "{{# a 1 }}") === None)
assert(runRule(tag, "{{/a}}") === Some(CloseTag(Id("a"))))
- assert(runRule(tag, "{{/ a 1 }}") === None)
+ assert(runRule(tag, "{{/ a b=1 }}") === None)
assert(runRule(tag, "[:a:]") === None)
}
test("tag - [: :]") {
import p2._
assert(runRule(tag, "[:a:]") === Some(SimpleTag(Id("a"), Nil)))
- assert(runRule(tag, "[: a 1 :]") === Some(SimpleTag(Id("a"), List(IntArgument(1)))))
+ assert(runRule(tag, "[: a b=1 :]") === Some(SimpleTag(Id("a"), List(IntArgument(Id("b"), 1)))))
+ assert(runRule(tag, "[: a 1 :]") === None)
assert(runRule(tag, "[:#a:]") === Some(OpenTag(Id("a"), Nil)))
- assert(runRule(tag, "[:# a 1 :]") === Some(OpenTag(Id("a"), List(IntArgument(1)))))
+ assert(runRule(tag, "[:# a b=1 :]") === Some(OpenTag(Id("a"), List(IntArgument(Id("b"), 1)))))
+ assert(runRule(tag, "[:# a 1 :]") === None)
assert(runRule(tag, "[:/a:]") === Some(CloseTag(Id("a"))))
assert(runRule(tag, "[:/ a 1 :]") === None)
assert(runRule(tag, "{{a}}") === None)
View
28 src/test/scala/tipi/core/TemplateSuite.scala
@@ -9,7 +9,7 @@ class TemplateSuite extends FunSuite {
val argsTemplate = Template(
parse(
"""
- |{{# def page title author }}
+ |{{# def page title="Default title" author="Default author" }}
|<html>
| <head>
| <title>{{ title }}</title>
@@ -20,14 +20,14 @@ class TemplateSuite extends FunSuite {
|{{/ def }}
""".trim.stripMargin
).get.asInstanceOf[Block],
- Env.empty
+ Env.basic
)
val argsIn = (
- Env.empty,
+ Env.basic,
parse(
"""
- |{{# page "Title" "Author" }}
+ |{{# page title="Title" author="Author" }}
| Foo
| Bar
| Baz
@@ -39,7 +39,7 @@ class TemplateSuite extends FunSuite {
val bindTemplate = Template(
parse(
"""
- |{{# def page }}
+ |{{# def page title author }}
|<html>
| <head>
| <title>{{ title }}</title>
@@ -50,11 +50,11 @@ class TemplateSuite extends FunSuite {
|{{/ def }}
""".trim.stripMargin
).get.asInstanceOf[Block],
- Env.empty
+ Env.basic
)
val bindIn = (
- Env.empty,
+ Env.basic,
parse(
"""
|{{# page }}
@@ -67,18 +67,24 @@ class TemplateSuite extends FunSuite {
)
val wrongNameIn = (
- Env.empty,
+ Env.basic,
parse(
"""
- {{# pages "Title" "Body" }}
+ {{# pages title="Title" body="Body" }}
{{/ pages }}
"""
).get
)
test("Template.argNames") {
- assert(argsTemplate.argNames === List(Id("title"), Id("author")))
- assert(bindTemplate.argNames === List())
+ assert(argsTemplate.defnEnv === Env.basic ++ Env(Map(
+ Id("title") -> Transform.constant(Text("Default title")),
+ Id("author") -> Transform.constant(Text("Default author"))
+ )))
+ assert(bindTemplate.defnEnv === Env.basic ++ Env(Map(
+ Id("title") -> Transform.constant(Range.Empty),
+ Id("author") -> Transform.constant(Range.Empty)
+ )))
}
test("Template.isDefinedAt - args") {
View
8 src/test/scala/tipi/core/TipiSuite.scala
@@ -9,7 +9,7 @@ class TipiSuite extends FunSuite {
assert(
tipi(
"""
- |{{def x "x1"}}
+ |{{def x="x1"}}
|{{x}}
""".trim.stripMargin
) === Right(
@@ -25,18 +25,18 @@ class TipiSuite extends FunSuite {
assert(
tipi(
"""
- |{{def x "x1"}}
+ |{{def x="x1"}}
|{{x}
""".trim.stripMargin
) === Left("[1,1]: `}}' expected but `}' found")
)
}
-
+
test("invalid def block") {
assert(
tipi(
"""
- |{{def x "x1"}}
+ |{{def x="x1"}}
|{{x}
""".trim.stripMargin
) === Left("[1,1]: `}}' expected but `}' found")
Please sign in to comment.
Something went wrong with that request. Please try again.