Is there a way to define a dsl, that would allow the following form?
variable identifier identifier variable
For example:
1 equals to 2
I know how to create a simpler form: 1 equals to (2)
, but I want to avoid parentheses. Is there a way to do it?
You can ask the parser:
$ scala -Xprint:parser
Welcome to Scala version 2.9.2 ... blah
scala> variable1 identifier1 identifier2 variable2
// lots of stuff and inside:
val res0 = variable1.identifier1(identifier2).variable2
// this is how the parser sees it.
// if you can make that work (dynamic classes…?), you’re good to go.
However, there is a problem: This only works as long as variable2
is an identifier (so that it can be used as a method name). With
scala> 1 equals to 2
already the parser fails:
<console>:1: error: ';' expected but integer literal found.
1 equals to 2
^
Parentheses are really your only way around(*):
scala> 1 equals to (2)
// ...
val res1 = 1.equals(to(2))
(*) unless you make 2
an identifier by using it with backticks
scala> 1 equals to `2`
// ...
val res2 = 1.equals(to).2
… nah, maybe not.
As far as I know it's impossible at the moment to implement it as you want (I would be happy to be proven wrong). I also faced this issue, when I was creating my small DSL for injection. But I have realized, that even if you can't have 2 variables between 2 identifiers, you still can have three identifiers between them. It looks like this:
variable1 method constant method variable2
which is the same as:
variable1.method(constant).method(variable2)
With this I was able to come up with very nice DSL that looks like this:
by default defaultDb and identified by 'remote
'remote is by default defaultDb
You can find more examples of it's usage in this spec:
https://github.com/OlegIlyenko/scaldi/blob/48f7e4186cf7eb441116087003d7f45f16e0ac6c/src/test/scala/scaldi/InjectableSpec.scala#L47
It's implementation can be found here:
https://github.com/OlegIlyenko/scaldi/blob/master/src/main/scala/scaldi/Injectable.scala
I used ByWord
and IdentifiedWord
class types as method arguments. For example:
def is(by: ByWord) = and(by)
Which leaves possibility, that users of the library will extend ByWorld
class and generally can provide their own ByWorld
implementations. Now when I think about this, I find it not very nice. Better solution would be to create singelton objects for the constant words and then use their type like this:
object by
def is(byWord: by.type) = and(by)
This generally restricts argument to only one possible by
word instance.
Well, you can have two successive identifiers if the final one is a unary operator:
a identifier1 - d
is parsed as
a.identifier1(d.unary_-)
Note that unary functions can be only ! ~ and -
If you don't mind to have your last variable name treated as a string (e.g. you have a way to later retrieve its value from a map or the interpreter itself), you can have the following using dynamics:
import scala.Dynamic
import scala.language.dynamics
class I2
class Res(i: I2) extends Dynamic {
def selectDynamic(obj: String): Unit = {
println(obj)
}
}
class V1 { def i1(i2: I2): Res= new Res(i2) }
val v1 = new V1
val i2 = new I2
v1 i1 i2 v2
results in the string "v2".
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With