Open disnet opened 8 years ago
How about "destructuring" syntax templates:
syntax new = function (ctx) {
return match (ctx) {
case #`new ${ ident } ${ params }` => #`${ ident }.create ${ params }`
}
};
syntax let = function (ctx) {
return match (ctx) {
case #`let ${ ident } = ${ init : AssignmentExpression } ${ ...rest }` => #`
(function (${ ident }) {
${ ...rest }
})(${ init })
`
}
};
It's a little more verbose, but has nice symmetry with the current syntax template literals and es2015+ destructuring assignments.
Perhaps this could be an option:
syntax new = (#`new ${ ident } ${ params }`) => #`${ ident }.create ${ params }`;
I have a port of Clojure threading macros in mind:
syntaxrec threadFirst = ctx => {
const inner = ctx.next().value.inner();
let args, call;
match (inner) {
case #`(${ val }) { ${ ident } (${ args }) ${ ...rest } }` => {
args = #`${ val }, ${ ...args }`;
call = #`${ ident }(${ args })`;
}
case #`(${ val }) { ${ ident } ${ ...rest } }` => {
call = #`${ ident }(${ val })`;
}
}
return rest.isEmpty() ? call : #`threadFirst (${ call }) { ${ ...rest } }`;
}
const inc = x => x + 1;
const mult = (x, y) => x * y;
const div = (x, y) => x / y;
threadFirst (2) { inc mult(3) inc div(5) } // 2
I'm currently blocked on #519. In the meantime, I've got a question about semantics:
let #`1 + ${a} - ${b}` = #`1 + 2 - 3`; // let a = #`2`, b = #`3`
let #`1 + ${c} - 3 + ${d}` = #`1 + 2 - 4 + 5`; // let c = #`2`, d = undefined
let #`1 + ${e} - ${f} + 4` = #`1 + 2 - 3 + 5`; // let e = #`2`, f = ?
Should f
in the last case be 3
or undefined
? A case could be made for either one. My gut tells me to be consistent w/ Object and Array destructuring and let f = 3
.
OTOH I was hoping matching could be naively implemented by checking that all of the placeholder variables point to a value.
Humm...why doesn't the second and third cases just throw a "failed to match" error?
Yeah. I guess for destructuring values shouldn't be allowed on the LHS. It would be an Unexpected token error if I tried that w/ objects or arrays. So destructuring only makes sense if just placeholders are present in the LHS template:
let #`${a} ${b} ${c}` = #`(1 + 2) * 3`; // let a = #`(1 + 2)`, b = #`*`, c = #`3`
And I suppose nested destructuring takes the analogy too far?
let #`${ #`${a} ${b} ${c}` }` = #`(1 + 2)`; // let a = #`1`, b = #`+`, c = #`2`
I'm thinking about this again and I have some ideas:
const BindingElement = ctx => match(ctx) {
// isPunctuator(,) - consumes a ',' if it exists and is a Punctuator returning true or false
// comma? - comma is optional. if it isn't present ctx is reset to right before that point
// BindingTarget - call functions with those names w/ ctx as the argument
#`${binding: BindingTarget} = ${init: Expression} ${comma?: isPunctuator(,)}` => new T.BindingWithDefault({ binding, init })
#`${binding: BindingTarget} ${comma?: isPunctuator(,)}` => binding
};
const FormalParameters = ctx => match(ctx) {
#`()` => new T.FormalParameters({ items: List() })
#`(...${rest: BindingIdentifier})` => new T.FormalParameters({ items: List(), rest })
// ...items calls BindingIdentifier repeatedly and returns a List
#`(${...items: BindingElement} ...${rest: BindingIdentifier})` => new T.FormalParameters({ items, rest })
#`(${...items: BindingElement})` => new T.FormalParamters({ items })
};
function BindingTarget(ctx) {
return match(ctx) {
#`${name: Identifier}` => new T.BindingIdentifier({ name })
#`[]` => new T.ArrayBinding({ elements: List() })
#`[...${rest: BindingTarget}]` => T.ArrayBinding({ elements: List(), rest })
#`[${...elements: BindingElement} ...${rest: BindingTarget}]` => new T.ArrayBinding({ elements, rest })
#`[${...elements: BindingElement}]` => new T.ArrayBinding({ elements })
#`{}` => new T.ObjectBinding({ properties: List() })
#`{${...properties: BindingProperty}}` => new T.ObjectBinding({ properties })
};
}
syntax function = ctx => match(ctx) {
#`${star?: isPunctuator(*)} ${name?: BindingIdentifier} ${params: FormalParameters} ${body: Statement}` =>
new T.FunctionExpression({ name, isGenerator: star, params, body });
};
I don't intend to implement the enforester as a bunch of macros. This is just to show the features I have in mind. Though we could have theexpandMacro
not splice the result back into rest
if it's a term (and not a list).
Yeah, I think I really like this.
The declarative syntax was to me the biggest selling point of sweetjs. I understand there is obviously a lot of work that has to be done to the core in order to add this back.
I was just wondering if a declarative syntax still in the works? Is there a list somewhere of what needs to be accomplished before it can be added back in?
@jimmyhmiller I actually don't think there's anything holding this back. In fact I don't think we even need to do this in core since match
(or something like it) can just be a macro that expands to the appropriate low-level matching.
0.7.8 is still available and it works great.
Version 1.0 only comes with primitive syntax transformer forms which is obviously not great for everyday use. Some initial syntax thoughts for a more declarative form:
This should happen after #378 and
import ... for macros
lands so the matching can just be a library.