Created
June 22, 2014 01:01
-
-
Save natefaubion/bd7addb60a888c1ec759 to your computer and use it in GitHub Desktop.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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