+ All Categories
Home > Engineering > Embedding a language into string interpolator

Embedding a language into string interpolator

Date post: 12-Aug-2015
Category:
Upload: michael-limansky
View: 102 times
Download: 1 times
Share this document with a friend
57
N Embedding a language into string interpolator Mikhail Limanskiy June 10, 2015
Transcript
Page 1: Embedding a language into string interpolator

N

Embedding a language into stringinterpolator

Mikhail LimanskiyJune 10, 2015

Page 2: Embedding a language into string interpolator

String interpolation is easy

val language = "English"

val embedded = s"Embedded $language"

Page 3: Embedding a language into string interpolator

String interpolation is easy

val language = "English"

val embedded = s"Embedded $language"

Page 4: Embedding a language into string interpolator

MongoDB

MongoDB is a document oriented database, storing BSON documents.db.people.insert({ name: "John Doe", age: 42 })

db.people.insert({name: "William Smith",age: 28,phone: [ "1234567", "7654321" ]})

db.people.insert({name: "Alice White",age: 29,address: {

country: "UK",city: "London"

}})db.people.insert({ name : "Ivan Petrov", age : 28 })

Page 5: Embedding a language into string interpolator

MongoDB

MongoDB is a document oriented database, storing BSON documents.db.people.insert({ name: "John Doe", age: 42 })db.people.insert({

name: "William Smith",age: 28,phone: [ "1234567", "7654321" ]})

db.people.insert({name: "Alice White",age: 29,address: {

country: "UK",city: "London"

}})db.people.insert({ name : "Ivan Petrov", age : 28 })

Page 6: Embedding a language into string interpolator

MongoDB

MongoDB is a document oriented database, storing BSON documents.db.people.insert({ name: "John Doe", age: 42 })db.people.insert({

name: "William Smith",age: 28,phone: [ "1234567", "7654321" ]})

db.people.insert({name: "Alice White",age: 29,address: {

country: "UK",city: "London"

}})

db.people.insert({ name : "Ivan Petrov", age : 28 })

Page 7: Embedding a language into string interpolator

MongoDB

MongoDB is a document oriented database, storing BSON documents.db.people.insert({ name: "John Doe", age: 42 })db.people.insert({

name: "William Smith",age: 28,phone: [ "1234567", "7654321" ]})

db.people.insert({name: "Alice White",age: 29,address: {

country: "UK",city: "London"

}})db.people.insert({ name : "Ivan Petrov", age : 28 })

Page 8: Embedding a language into string interpolator

Search and update

Quering:db.people.find({ name: "John Doe"})db.people.find({ age: { $lt : 30 }})db.people.find({ phone: { $not: { $size : 0 }}})

db.people.update({ age : 42},{ $set : { name : "Ford Prefect" } })

db.people.aggregate([ { $group : { _id : "$age", count : {$sum : 1} } },

{ $sort : { count : -1 } },{ $limit : 5 }

])

Page 9: Embedding a language into string interpolator

Search and update

Quering:db.people.find({ name: "John Doe"})db.people.find({ age: { $lt : 30 }})db.people.find({ phone: { $not: { $size : 0 }}})

db.people.update({ age : 42},{ $set : { name : "Ford Prefect" } })

db.people.aggregate([ { $group : { _id : "$age", count : {$sum : 1} } },

{ $sort : { count : -1 } },{ $limit : 5 }

])

Page 10: Embedding a language into string interpolator

Search and update

Quering:db.people.find({ name: "John Doe"})db.people.find({ age: { $lt : 30 }})db.people.find({ phone: { $not: { $size : 0 }}})

db.people.update({ age : 42},{ $set : { name : "Ford Prefect" } })

db.people.aggregate([ { $group : { _id : "$age", count : {$sum : 1} } },

{ $sort : { count : -1 } },{ $limit : 5 }

])

Page 11: Embedding a language into string interpolator

MongoDB in Scala

There are three main drivers for MongoDB:

I Casbah – synchronous, on top of Java driver.

I ReactiveMongo – asynchronous, built on Akka actors.I Tepkin – reactive, on top of Akka IO and Akka Streams.

Page 12: Embedding a language into string interpolator

MongoDB in Scala

There are three main drivers for MongoDB:

I Casbah – synchronous, on top of Java driver.I ReactiveMongo – asynchronous, built on Akka actors.

I Tepkin – reactive, on top of Akka IO and Akka Streams.

Page 13: Embedding a language into string interpolator

MongoDB in Scala

There are three main drivers for MongoDB:

I Casbah – synchronous, on top of Java driver.I ReactiveMongo – asynchronous, built on Akka actors.I Tepkin – reactive, on top of Akka IO and Akka Streams.

Page 14: Embedding a language into string interpolator

How Casbah API looks like

val name = "John Doe"

people.insert(MongoDBObject("name" -> "James Bond","age" -> 80,"phone" -> List("007007"),"address" -> MongoDBObject("country" -> "UK")))

val a = people.findOne(MongoDBObject("name" -> name))val b = people.find(MongoDBObject("age" ->

MongoDBObject("$lt" -> 30)))

// Using Casbah DSLval c = people.find("age" $lt 30)val d = people.find("phone" -> $not(_ $size 0))

people.update(MongoDBObject("age" -> 42),$set("name" -> "Ford Prefect"))

val e = people.aggregate(List(MongoDBObject("$group" ->

MongoDBObject("_id" -> "$age", "count" ->MongoDBObject("$sum" -> 1))),

MongoDBObject("$sort" -> MongoDBObject("count" -> -1)),MongoDBObject("$limit" -> 5)))

Page 15: Embedding a language into string interpolator

How Casbah API looks like

val name = "John Doe"

people.insert(MongoDBObject("name" -> "James Bond","age" -> 80,"phone" -> List("007007"),"address" -> MongoDBObject("country" -> "UK")))

val a = people.findOne(MongoDBObject("name" -> name))val b = people.find(MongoDBObject("age" ->

MongoDBObject("$lt" -> 30)))

// Using Casbah DSLval c = people.find("age" $lt 30)val d = people.find("phone" -> $not(_ $size 0))

people.update(MongoDBObject("age" -> 42),$set("name" -> "Ford Prefect"))

val e = people.aggregate(List(MongoDBObject("$group" ->

MongoDBObject("_id" -> "$age", "count" ->MongoDBObject("$sum" -> 1))),

MongoDBObject("$sort" -> MongoDBObject("count" -> -1)),MongoDBObject("$limit" -> 5)))

Page 16: Embedding a language into string interpolator

How Casbah API looks like

val name = "John Doe"

people.insert(MongoDBObject("name" -> "James Bond","age" -> 80,"phone" -> List("007007"),"address" -> MongoDBObject("country" -> "UK")))

val a = people.findOne(MongoDBObject("name" -> name))val b = people.find(MongoDBObject("age" ->

MongoDBObject("$lt" -> 30)))

// Using Casbah DSLval c = people.find("age" $lt 30)val d = people.find("phone" -> $not(_ $size 0))

people.update(MongoDBObject("age" -> 42),$set("name" -> "Ford Prefect"))

val e = people.aggregate(List(MongoDBObject("$group" ->

MongoDBObject("_id" -> "$age", "count" ->MongoDBObject("$sum" -> 1))),

MongoDBObject("$sort" -> MongoDBObject("count" -> -1)),MongoDBObject("$limit" -> 5)))

Page 17: Embedding a language into string interpolator

How Casbah API looks like

val name = "John Doe"

people.insert(MongoDBObject("name" -> "James Bond","age" -> 80,"phone" -> List("007007"),"address" -> MongoDBObject("country" -> "UK")))

val a = people.findOne(MongoDBObject("name" -> name))val b = people.find(MongoDBObject("age" ->

MongoDBObject("$lt" -> 30)))

// Using Casbah DSLval c = people.find("age" $lt 30)val d = people.find("phone" -> $not(_ $size 0))

people.update(MongoDBObject("age" -> 42),$set("name" -> "Ford Prefect"))

val e = people.aggregate(List(MongoDBObject("$group" ->

MongoDBObject("_id" -> "$age", "count" ->MongoDBObject("$sum" -> 1))),

MongoDBObject("$sort" -> MongoDBObject("count" -> -1)),MongoDBObject("$limit" -> 5)))

Page 18: Embedding a language into string interpolator

How Casbah API looks like

val name = "John Doe"

people.insert(MongoDBObject("name" -> "James Bond","age" -> 80,"phone" -> List("007007"),"address" -> MongoDBObject("country" -> "UK")))

val a = people.findOne(MongoDBObject("name" -> name))val b = people.find(MongoDBObject("age" ->

MongoDBObject("$lt" -> 30)))

// Using Casbah DSLval c = people.find("age" $lt 30)val d = people.find("phone" -> $not(_ $size 0))

people.update(MongoDBObject("age" -> 42),$set("name" -> "Ford Prefect"))

val e = people.aggregate(List(MongoDBObject("$group" ->

MongoDBObject("_id" -> "$age", "count" ->MongoDBObject("$sum" -> 1))),

MongoDBObject("$sort" -> MongoDBObject("count" -> -1)),MongoDBObject("$limit" -> 5)))

Page 19: Embedding a language into string interpolator

How Casbah API looks like

Page 20: Embedding a language into string interpolator

ReactiveMongo

// Future[BSONDocument]val a = people.find(BSONDocument("name" -> "John Doe"))

.one[BSONDocument]

// Future[List[Person]]val b = people.find(BSONDocument("age" ->

BSONDocument("$lt" -> 30))).cursor[Person].collect[List]()

val futureUpdate = people.update(BSONDocument("age" -> 42),BSONDocument("$set" -> BSONDocument("name" -> "Ford Prefect")))

// Futureval e = db.command(RawCommand(BSONDocument(

"aggregate" -> "people","pipeline" -> BSONArray(

BSONDocument("$group" ->BSONDocument("_id" -> "$age",

"count" -> BSONDocument("$sum" -> 1))),BSONDocument("$sort" -> BSONDocument("count" -> -1)),BSONDocument("$limit" -> 5)))))

Page 21: Embedding a language into string interpolator

Why?

Page 22: Embedding a language into string interpolator

Meet MongoQuery

Using MongoQuery with Casbah:import com.github.limansky.mongoquery.casbah._

val name = "John Doe"

val a = people.findOne(mq"{ name : $name }")

val b = people.find(mq"{age : { $$lt : 30 }}")

val d = people.find(mq"{ phone : { $$not : { $$size : 0 }}}")

people.update(mq"{ age : 42 }",mq"{ $$set { name : 'Ford Prefect' }}")

val e = people.aggregate(List(mq"""{ $$group :

{ _id : "$$age", count : { $$sum : 1 }}}""",mq"{ $$sort : { count : -1 }}",mq"{ $$limit : 5}"))

Page 23: Embedding a language into string interpolator

Meet MongoQuery

Using MongoQuery with Casbah:import com.github.limansky.mongoquery.casbah._

val name = "John Doe"

val a = people.findOne(mq"{ name : $name }")val b = people.find(mq"{age : { $$lt : 30 }}")

val d = people.find(mq"{ phone : { $$not : { $$size : 0 }}}")

people.update(mq"{ age : 42 }",mq"{ $$set { name : 'Ford Prefect' }}")

val e = people.aggregate(List(mq"""{ $$group :

{ _id : "$$age", count : { $$sum : 1 }}}""",mq"{ $$sort : { count : -1 }}",mq"{ $$limit : 5}"))

Page 24: Embedding a language into string interpolator

Meet MongoQuery

Using MongoQuery with Casbah:import com.github.limansky.mongoquery.casbah._

val name = "John Doe"

val a = people.findOne(mq"{ name : $name }")val b = people.find(mq"{age : { $$lt : 30 }}")

val d = people.find(mq"{ phone : { $$not : { $$size : 0 }}}")

people.update(mq"{ age : 42 }",mq"{ $$set { name : 'Ford Prefect' }}")

val e = people.aggregate(List(mq"""{ $$group :

{ _id : "$$age", count : { $$sum : 1 }}}""",mq"{ $$sort : { count : -1 }}",mq"{ $$limit : 5}"))

Page 25: Embedding a language into string interpolator

String interpolation

implicit class MongoHelper(val sc: StringContext)extends AnyVal {

def mq(args: Any*): DBObject = {Parser.parseQuery(sc.parts, args) match {

case Success(v, _) =>createObject(v)

case NoSuccess(msg, _) =>throw new MqException(s"Invalid object: $msg")

}}

}

mq"{ name : $name }"

sc.parts == List("{ name: ", " }")args = List(name)

Page 26: Embedding a language into string interpolator

String interpolation

implicit class MongoHelper(val sc: StringContext)extends AnyVal {

def mq(args: Any*): DBObject = {Parser.parseQuery(sc.parts, args) match {

case Success(v, _) =>createObject(v)

case NoSuccess(msg, _) =>throw new MqException(s"Invalid object: $msg")

}}

}

mq"{ name : $name }"

sc.parts == List("{ name: ", " }")args = List(name)

Page 27: Embedding a language into string interpolator

String interpolation

Page 28: Embedding a language into string interpolator

Wrapping it into macro

implicit class MongoHelper(val sc: StringContext) extends AnyVal {def mq(args: Any*): DBObject = macro MongoHelper.mq_impl

}

object MongoHelper {def mq_impl(c: Context)(args: c.Expr[Any]*):

c.Expr[DBObject] = ???}

Page 29: Embedding a language into string interpolator

Wrapping it into macro

implicit class MongoHelper(val sc: StringContext) extends AnyVal {def mq(args: Any*): DBObject = macro MongoHelper.mq_impl

}

object MongoHelper {def mq_impl(c: Context)(args: c.Expr[Any]*):

c.Expr[DBObject] = {import c.universe._

val q"$cn(scala.StringContext.apply(..$pTrees))"= c.prefix.tree

val parsed = parse(c)(pTrees)wrapObject(c)(parsed, args.map(_.tree).iterator)

}}

Page 30: Embedding a language into string interpolator

Wrapping it into macro

object MongoHelper {

def parse(c: Context)(pTrees: List[c.Tree]) = {import c.universe._

val parts = pTrees map {case Literal(Constant(s: String)) => s

}

parser.parse(parts) match {case Success(v, _) => vcase NoSuccess(msg, reader) =>

val partIndex = reader.asInstanceOf[PartReader].partval pos = pTrees(partIndex).posc.abort(pos.withPoint(pos.point + reader.offset)),

s"Invalid BSON object: $msg")}

}}

Page 31: Embedding a language into string interpolator

Parsing BSON

mq"{ name : $name, age : { $$gte : 18, $$lte : $max }}"

Lexical

List("{", Field("name"), ":", Placeholder , ",", Field("age"),":", "{", Keyword("$gte"), ":", NumericLit(18), ",",Keyword("$lte"), ":", Placeholder , ",", "}", "}")

Syntactical

Object(List((Member("name"), Placeholder),(Member("age"), Object(List(

(Keyword("$gte"), 18),(Keyword("$lte"), Placeholder))

))))

Page 32: Embedding a language into string interpolator

Parsing BSON

mq"{ name : $name, age : { $$gte : 18, $$lte : $max }}"

Lexical

List("{", Field("name"), ":", Placeholder , ",", Field("age"),":", "{", Keyword("$gte"), ":", NumericLit(18), ",",Keyword("$lte"), ":", Placeholder , ",", "}", "}")

Syntactical

Object(List((Member("name"), Placeholder),(Member("age"), Object(List(

(Keyword("$gte"), 18),(Keyword("$lte"), Placeholder))

))))

Page 33: Embedding a language into string interpolator

Parsing BSON

mq"{ name : $name, age : { $$gte : 18, $$lte : $max }}"

Lexical

List("{", Field("name"), ":", Placeholder , ",", Field("age"),":", "{", Keyword("$gte"), ":", NumericLit(18), ",",Keyword("$lte"), ":", Placeholder , ",", "}", "}")

Syntactical

Object(List((Member("name"), Placeholder),(Member("age"), Object(List(

(Keyword("$gte"), 18),(Keyword("$lte"), Placeholder))

))))

Page 34: Embedding a language into string interpolator

Create objects

protected def wrapObject(c: Context)(obj: Object,args: Iterator[c.Tree]): c.Expr[DBType] = {

val dbparts = obj.members.map {case (lv, v) => (lv.asString , wrapValue(c)(v, args))

}

c.Expr(q"com.mongodb.casbah.commons.MongoDBObject(..$dbparts)")}

Page 35: Embedding a language into string interpolator

Wrapping values

protected def wrapValue(c: Context) (value: Any,args: Iterator[c.Tree]): c.Expr[Any] = {

import c.universe._value match {

case BSON.Placeholder =>c.Expr(args.next())

case o: BSON.Object =>wrapObject(c)(o, args)

case BSON.Id(id) =>c.Expr(q"new org.bson.types.ObjectId($id)")

case a: List[_] =>val wrapped = a.map(i => wrapValue(c)(i, args))c.Expr[List[Any]](q"List(..$wrapped)")

case v =>c.Expr[Any](Literal(Constant(v)))

}}

Page 36: Embedding a language into string interpolator

Wrapping values

protected def wrapValue(c: Context) (value: Any,args: Iterator[c.Tree]): c.Expr[Any] = {

import c.universe._value match {

case BSON.Placeholder =>c.Expr(args.next())

case o: BSON.Object =>wrapObject(c)(o, args)

case BSON.Id(id) =>c.Expr(q"new org.bson.types.ObjectId($id)")

case a: List[_] =>val wrapped = a.map(i => wrapValue(c)(i, args))c.Expr[List[Any]](q"List(..$wrapped)")

case v =>c.Expr[Any](Literal(Constant(v)))

}}

Page 37: Embedding a language into string interpolator

Wrapping values

protected def wrapValue(c: Context) (value: Any,args: Iterator[c.Tree]): c.Expr[Any] = {

import c.universe._value match {

case BSON.Placeholder =>c.Expr(args.next())

case o: BSON.Object =>wrapObject(c)(o, args)

case BSON.Id(id) =>c.Expr(q"new org.bson.types.ObjectId($id)")

case a: List[_] =>val wrapped = a.map(i => wrapValue(c)(i, args))c.Expr[List[Any]](q"List(..$wrapped)")

case v =>c.Expr[Any](Literal(Constant(v)))

}}

Page 38: Embedding a language into string interpolator

Wrapping values

protected def wrapValue(c: Context) (value: Any,args: Iterator[c.Tree]): c.Expr[Any] = {

import c.universe._value match {

case BSON.Placeholder =>c.Expr(args.next())

case o: BSON.Object =>wrapObject(c)(o, args)

case BSON.Id(id) =>c.Expr(q"new org.bson.types.ObjectId($id)")

case a: List[_] =>val wrapped = a.map(i => wrapValue(c)(i, args))c.Expr[List[Any]](q"List(..$wrapped)")

case v =>c.Expr[Any](Literal(Constant(v)))

}}

Page 39: Embedding a language into string interpolator

Wrapping values

protected def wrapValue(c: Context) (value: Any,args: Iterator[c.Tree]): c.Expr[Any] = {

import c.universe._value match {

case BSON.Placeholder =>c.Expr(args.next())

case o: BSON.Object =>wrapObject(c)(o, args)

case BSON.Id(id) =>c.Expr(q"new org.bson.types.ObjectId($id)")

case a: List[_] =>val wrapped = a.map(i => wrapValue(c)(i, args))c.Expr[List[Any]](q"List(..$wrapped)")

case v =>c.Expr[Any](Literal(Constant(v)))

}}

Page 40: Embedding a language into string interpolator

Type safety

Page 41: Embedding a language into string interpolator

mqt – typechecking interpolator

case class Phone(kind: String, number: String)case class Person(name: String, age: Int, phone: List[Phone])

// OKpersons.update(mq"{}", mqt"{ $$inc : { age : 1 } }"[Person])persons.find(mqt"{ phone.number : '223322' }"[Person])

// COMPILE ERRORpersons.update(mq"{}", mqt"""{$$set : { nme : "Joe" }}"""[Person])persons.find(mqt"{ name.1 : 'Joe' }"[Person])persons.find(mqt"{ phone.num : '223322' }"[Person])

Page 42: Embedding a language into string interpolator

mqt – typechecking interpolator

case class Phone(kind: String, number: String)case class Person(name: String, age: Int, phone: List[Phone])

// OKpersons.update(mq"{}", mqt"{ $$inc : { age : 1 } }"[Person])persons.find(mqt"{ phone.number : '223322' }"[Person])

// COMPILE ERRORpersons.update(mq"{}", mqt"""{$$set : { nme : "Joe" }}"""[Person])persons.find(mqt"{ name.1 : 'Joe' }"[Person])persons.find(mqt"{ phone.num : '223322' }"[Person])

Page 43: Embedding a language into string interpolator

mqt – typechecking interpolator

case class Phone(kind: String, number: String)case class Person(name: String, age: Int, phone: List[Phone])

// OKpersons.update(mq"{}", mqt"{ $$inc : { age : 1 } }"[Person])persons.find(mqt"{ phone.number : '223322' }"[Person])

// COMPILE ERRORpersons.update(mq"{}", mqt"""{$$set : { nme : "Joe" }}"""[Person])persons.find(mqt"{ name.1 : 'Joe' }"[Person])persons.find(mqt"{ phone.num : '223322' }"[Person])

Page 44: Embedding a language into string interpolator

Passing type into intepolator

implicit class MongoHelper(val sc: StringContext) extends AnyVal {def mq(args: Any*): DBObject = macro MongoHelper.mq_impl

def mqt(args: Any*) = new QueryWrapper}

class QueryWrapper {def apply[T]: DBObject = macro MongoHelper.mqt_impl[T]

}

object MongoHelper {

def mqt_impl[T: c.WeakTypeTag](c: Context):c.Expr[DBObject] = ???

}

Page 45: Embedding a language into string interpolator

Inside mqt_impl

def mqt_impl[T: c.WeakTypeTag](c: Context): c.Expr[DBObject] = {val q"$cn(scala.StringContext.apply(..$pTrees)).mqt(..$aTrees)"

= c.prefix.treeval args = aTrees.map(c.Expr(_))val parsed = parse(c)(pTrees)

checkObject(c)(c.weakTypeOf[T], parsed)

wrapObject(c)(parsed, args.iterator)}

Page 46: Embedding a language into string interpolator

Verifing the type

def checkType(c: Context)(tpe: c.Type, obj: Object) = {import c.universe._

val ctor = tpe.decl(termNames.CONSTRUCTOR).asMethodval params = ctor.paramLists.headval className = t.typeSymbol.name.toString

val fields = params.map(s => s.name.toString -> s).toMap

obj.members.foreach { case (m, _) =>if (!fields.contains(m.name)) {

c.abort(c.enclosingPosition ,s"Class $className doesn't contain field '${m.name}'")

}}

}

Page 47: Embedding a language into string interpolator

Testing interpolator

it should "support nested objects" in {val q = mq"""{ user : "Joe", age : {$$gt : 25}}"""q should equal(MongoDBObject("user" -> "Joe",

"age" -> MongoDBObject("$gt" -> 25)))}

Page 48: Embedding a language into string interpolator

Testing error scenarios

import scala.reflect.runtime.{ universe => ru }

class CompileTest extends FlatSpec {

val cl = getClass.getClassLoader.asInstanceOf[URLClassLoader]val cp = cl.getURLs.map(_.getFile).mkString(File.pathSeparator)val mirror = ru.runtimeMirror(cl)val tb = mirror.mkToolBox(options = s"-cp $cp")

def getError(q: String): String = {val e = intercept[ToolBoxError] {

tb.eval(tb.parse(q))}e.message

}

it should "fail on malformed BSON objects" in {val e = getError("""mq"{ test 5 }" """)e should include("`:' expected, but 5 found")

}}

Page 49: Embedding a language into string interpolator

Testing error scenarios

import scala.reflect.runtime.{ universe => ru }

class CompileTest extends FlatSpec {

val cl = getClass.getClassLoader.asInstanceOf[URLClassLoader]val cp = cl.getURLs.map(_.getFile).mkString(File.pathSeparator)val mirror = ru.runtimeMirror(cl)val tb = mirror.mkToolBox(options = s"-cp $cp")

def getError(q: String): String = {val e = intercept[ToolBoxError] {

tb.eval(tb.parse(q))}e.message

}

it should "fail on malformed BSON objects" in {val e = getError("""mq"{ test 5 }" """)e should include("`:' expected, but 5 found")

}}

Page 50: Embedding a language into string interpolator

Testing error scenarios

import scala.reflect.runtime.{ universe => ru }

class CompileTest extends FlatSpec {

val cl = getClass.getClassLoader.asInstanceOf[URLClassLoader]val cp = cl.getURLs.map(_.getFile).mkString(File.pathSeparator)val mirror = ru.runtimeMirror(cl)val tb = mirror.mkToolBox(options = s"-cp $cp")

def getError(q: String): String = {val e = intercept[ToolBoxError] {

tb.eval(tb.parse(q))}e.message

}

it should "fail on malformed BSON objects" in {val e = getError("""mq"{ test 5 }" """)e should include("`:' expected, but 5 found")

}}

Page 51: Embedding a language into string interpolator

Summary

ConsI Not easy to implement

I Not highlighted in IDE

Pros

I Less limitations on language structureI Can preserve existing languageI Martin said that string interpolation is cool

Page 52: Embedding a language into string interpolator

Summary

ConsI Not easy to implementI Not highlighted in IDE

Pros

I Less limitations on language structureI Can preserve existing languageI Martin said that string interpolation is cool

Page 53: Embedding a language into string interpolator

Summary

ConsI Not easy to implementI Not highlighted in IDE

Pros

I Less limitations on language structureI Can preserve existing languageI Martin said that string interpolation is cool

Page 54: Embedding a language into string interpolator

Summary

ConsI Not easy to implementI Not highlighted in IDE

ProsI Less limitations on language structure

I Can preserve existing languageI Martin said that string interpolation is cool

Page 55: Embedding a language into string interpolator

Summary

ConsI Not easy to implementI Not highlighted in IDE

ProsI Less limitations on language structureI Can preserve existing language

I Martin said that string interpolation is cool

Page 56: Embedding a language into string interpolator

Summary

ConsI Not easy to implementI Not highlighted in IDE

ProsI Less limitations on language structureI Can preserve existing languageI Martin said that string interpolation is cool

Page 57: Embedding a language into string interpolator

k

Thanks. Questions?


Recommended