Skip to content

Instantly share code, notes, and snippets.

@natefaubion
Created June 22, 2014 01:01
Show Gist options
  • Save natefaubion/bd7addb60a888c1ec759 to your computer and use it in GitHub Desktop.
Save natefaubion/bd7addb60a888c1ec759 to your computer and use it in GitHub Desktop.
macro protocol {
case { _ $name:ident { $fn:protocol_fn ... } } => {
// Probably not a good idea to rely on `__fresh`. Sweet.js should provide
// some sort of blessed gensym capability.
letstx $id = [makeValue(__fresh(), #{ here })];
return #{
function $name(proto, impl) {
if (!impl) {
impl = proto;
proto = {};
}
$(protocol_define_impl $id proto impl $name
$fn$name ($fn$args (,) ...) { $fn$body ... }) ...
return proto;
}
$(function $fn$name($fn$args (,) ...) {
return $fn$first[to_str($fn$name '__' $id)].call(null, $fn$args (,) ...);
}) ...
}
}
}
macroclass protocol_fn {
pattern {
rule { $name:ident ($first) { $body ... } }
with $rest ... = #{},
$args ... = #{ $first };
}
pattern {
rule { $name:ident ($first, $rest (,) ...) { $body ... } }
with $args ... = #{ $first $rest ... };
}
pattern {
rule { $name:ident ($first) }
with $rest ... = #{},
$args ... = #{ $first },
$body ... = #{};
}
pattern {
rule { $name:ident ($first, $rest (,) ...) }
with $args ... = #{ $first $rest ... },
$body ... = #{};
}
}
macro protocol_define_impl {
rule { $id $proto $impl $name $fn$name $args {} } => {
if (!$impl.$fn$name) {
throw new Error(to_str('No implementation defined for ' $name '.' $fn$name));
}
Object.defineProperty($proto, to_str($fn$name '__' $id), {
value: $impl.$fn$name
});
}
rule { $id $proto $impl $name $fn$name $args { $body ... } } => {
Object.defineProperty($proto, to_str($fn$name '__' $id), {
value: $impl.$fn$name || function $args { $body ... }
});
}
}
macro extend {
rule { $name:ident { $proto:protocol_impl ... } } => {
$($proto$name($name.prototype, $proto$def);) ...
}
}
macroclass protocol_impl {
pattern {
rule { $name:ident $def:protocol_def }
}
pattern {
rule { $name:ident }
with $def = #{ {} };
}
}
macro protocol_def {
rule {
{
$($name:ident ($arg:ident (,) ...) {
$body ...
}) ...
}
} => {
{
$($name: function($arg (,) ...) {
$body ...
}) (,) ...
}
}
}
macro to_str {
case { _ ($arg ...) } => {
var str = #{ $arg ... }.map(unwrapSyntax).join('');
return [makeValue(str, #{ here })];
}
}
// Example
// -------
protocol Renderable {
asString(a)
asHtml(a) {
return '<b>' + asString(a) + '</b>';
}
}
function Foo(value) {
this.value = value;
}
extend Foo {
Renderable {
asString(foo) {
return 'Foo(' + foo.value + ')';
}
}
}
var f = new Foo('foo');
console.log(f, asString(f), asHtml(f));
// Protocols generate a function that can be used to reify one-off instances of
// the protocol.
var r = Renderable({
asString: function() {
return 'Reified';
},
asHtml: function() {
return '<i>Reified as HTML</i>';
}
});
console.log(r, asString(r), asHtml(r));
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment