Skip to content

Instantly share code, notes, and snippets.

@pr1001
Created June 18, 2011 00:13
Show Gist options
  • Save pr1001/1032634 to your computer and use it in GitHub Desktop.
Save pr1001/1032634 to your computer and use it in GitHub Desktop.
Proposed Javascript DSLs
JsFunc('myFunc, 'param1, 'param2) {
JsIf('param1 __< 30) {
Var('home) := Wrap(234 __- 3) __/ 2 `;`
Var('someArray) := JsArray(1, 2, 3, 4, 5) `;`
'myFunc(1, 2, "do it", 'home) `;`
$("#myID") >> 'attr("value", "123") `;`
} ~
JsForEach(Var('i) in 'someArray) {
'console >> 'log("Hi there " __+ 'i) `;`
} ~
JsAnonFunc('arg1, 'arg2) {
'alert("Anonymous function " __+ 'arg1 __+ 'arg2)
}(1, 2) `;`
}
/*
Marius describing his approach:
1. JsIf, JsForEach describe JavaScript if and for(each) statements
2. Functions like __<, __>, ... __+, __- are function that alows definition of boolean and/or algebraic expressions.
3. Wrap just wraps an expression into ()
4. Var defined a variable
5 := defines an assignment
6. JsFunc declares a JS function
7. JsAnonFunc declares an anonymous function
8. 'myFunc(1, 2, "do it", 'home) is simply a javascript function invocation by providing 4 parameter.
9. ~ is just a function that chains statements that don;t necessarily end in ;
*/
import net.liftweb.http.js.JsCmds._
import net.liftweb.http.js.JE._
JsFor(JsEq(JsVar("k"), Num(0)), JsLt(JsVar("k"), Num(10)), JsVar("k") === (JsVar("k") + Num(1)), Call("console.log", JsVar("k")))
/*
(Incidentally, shouldn't the first argument of JsFor be a JsExp, not a JsEq?) It would be very cool if you could write something like:
*/
For (var k = 0, k < 10, k++) {
console.log(k);
}
/*
We can start to get there:
*/
def For(start: JsEq, condition: JsExp, increment: JsExp)(body: JsExp) = JsFor(start, condition, increment, body)
class jsOps(exp: JsExp) {
def < (right: JsExp) = JsLt(exp, right)
}
implicit def toJsOps(exp: JsExp) = new jsOps(exp)
For(JsEq(JsVar("k"), Num(1)), JsVar("k") < Num(10), JsVar("k") ++) {
Call("console.log", JsVar("k"))
}
/*
I used an implicit def because I was just playing around in my REPL.
Also, I cheated on the ++. In theory you'd have something where JsVars
might know their values and so you could manipulate them.
*/
class jsVarWithValue(jsVar: JsExp, value: Box[JsExp]) {
def ++ = value.map(v =>
new jsVarWithValue(jsVar === jsVar + Num(1), Full(v + 1))
) openOr (jsVar === jsVar + Num(1))
}
implicit def jsVarValued(jsVar: JsVar) = new jsVarWithValue(jsVar, Empty)
/*
Actually, I guess that might work...
JsVarWithValue hints at a larger potential feature: not only being
about to generate a final JsExp that can be converted to a String
which will evaluate correctly in a Javascript interpreter but actually
being able to know something about the state of the Javascript script
from within Scala while you're assembling the final JsExp. The hope is
that you'd be able to have the super-simple syntax I showed at the
beginning of this message and actually run the loop as valid Scala and
have 0 through 9 printed to the console. Not being a Scala Jedi, I
don't know if this is do-able but I suspect that with some crazy type
and closure magic it might be possible.
*/
j_var ('test) := j_true
j_if (j_true) {
j_return (j_false)
} j_else {
j_return (j_true)
}
j_for (j_var (k) := 0, k j_< JsVar("arguments", "length"), k++) {
console_log(k)
}
/*
As you can see, for Javascript language features I've basically just put 'j_' before what are also reserved words in Scala. So, JsFalse becomes j_false. The same is true for break, continue, and return.
As you can see with j_if and j_for, those case classes are declared such that you get a more natural syntax, one approaching Javascript's. For instance, you can directly call a JsVar with 0 to n parameters, without writing Call("var_name", params0, ...). I guess that the experimental Dynamic trait could be added to the pimped JsVar (RichJsVar, of course) but that's probably going a bit far! ;-)
I've also added a bunch of implicits, both for my pimped classes but also to convert JsCmd to JsExp. Lift already has a conversion the other way around but there isn't one in this direction. I'm not sure if there is no implicit for a good reason. Perhaps this will break things?
*/
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment