Skip to content

Instantly share code, notes, and snippets.

@durka
Last active October 1, 2017 15:04
Show Gist options
  • Save durka/23c0680d5581a14ca8c05436e1ed4b36 to your computer and use it in GitHub Desktop.
Save durka/23c0680d5581a14ca8c05436e1ed4b36 to your computer and use it in GitHub Desktop.
RustFest lightning talk
#![no_std]
pub mod prelude;
#[macro_export] macro_rules! lisp {
// empty
() => (());
(()) => (());
// special forms
((ns $($ns_form:tt)*) $($body:tt)*) => {
$(lisp!($ns_form);)*
$(lisp!($body);)*
};
((extern [$($krate:tt)*])) => {
$(lisp!(@krate $krate);)*
};
((use [$($uze:tt)*])) => {
$(lisp!(@uze $uze);)*
};
((lambda ($($argn:ident)*) $($body:tt)*)) => {
// regular lambda
|$($argn),*| { $(lisp!($body));* }
};
((lambda (($(($argn:ident $argt:ty))*) $ret:ty) $($body:tt)*)) => {
// regular lambda
|$($argn:$argt),*| -> $ret { $(lisp!($body));* }
};
((lambda $s:ident (($(($argn:ident $argt:ty))*) $ret:ty) $($body:tt)*)) => {{
// recursive lambda
// $s MUST NOT be "self"
// recurse by calling ($s ...)
// FIXME recursive lambdas can't capture variables
lisp!((rust { fn $s($($argn: $argt),*) -> $ret { $(lisp!($body));* } $s }))
}};
((defn $name:ident () $($body:tt)*)) => {
fn $name() {
$(lisp!($body));*
}
};
((defn $name:ident ([($selph:ident) $(($argn:ident $argt:ty))*] $ret:ty) $($body:tt)*)) => {
fn $name($selph $(, $argn:$argt)*) -> $ret {
$(lisp!($body));*
}
};
((defn $name:ident ([(&$selph:ident) $(($argn:ident $argt:ty))*] $ret:ty) $($body:tt)*)) => {
fn $name(&$selph $(, $argn:$argt)*) -> $ret {
$(lisp!($body));*
}
};
((defn $name:ident ([(&mut $selph:ident) $(($argn:ident $argt:ty))*] $ret:ty) $($body:tt)*)) => {
fn $name(&mut $selph $(, $argn:$argt)*) -> $ret {
$(lisp!($body));*
}
};
((defn $name:ident ([($selph:ident: Box<Self>) $(($argn:ident $argt:ty))*] $ret:ty) $($body:tt)*)) => {
fn $name($selph: Box<Self> $(, $argn:$argt)*) -> $ret {
$(lisp!($body));*
}
};
((defn $name:ident ([$(($argn:ident $argt:ty))*] $ret:ty) $($body:tt)*)) => {
fn $name($($argn:$argt),*) -> $ret {
$(lisp!($body));*
}
};
((defstruct $name:ident $(($typ:ty))*)) => {
struct $name($($typ),*);
};
((defstruct $name:ident $(($field:ident $typ:ty))*)) => {
struct $name { $($field: $typ),* }
};
((defstruct $name:ident <$($gen:ident)*>)) => {
struct $name<$($gen),*>;
};
((defstruct $name:ident <$($gen:ident)*> (where $(($wty:ident $($wtr:tt)*))*) $(($typ:ty))*)) => {
struct $name<$($gen),*>($($typ),*) where $($wty: $($wtr)*),*;
};
((defstruct $name:ident <$($gen:ident)*> (where $(($wty:ident $($wtr:tt)*))*) $(($field:ident $typ:ty))*)) => {
struct $name<$($gen),*> where $($wty: $($wtr)*),* { $($field: $typ),* }
};
((deftype $name:ident $typ:ty)) => { type $name = $typ; };
((deftype $name:ident <$($gen:ident)*> $typ:ty)) => { type $name<$($gen),*> = $typ; };
((defimpl ($trate:ty) (for $typ:ty) $($body:tt)*)) => {
impl $trate for $typ {
$(lisp!($body);)*
}
};
((defimpl <$($gen:ident)*> ($trate:ty) (for $typ:ty) (where $(($wty:ident $($wtr:tt)*))*) $($body:tt)*)) => {
impl<$($gen),*> $trate for $typ where $($wty: $($wtr)*),* {
$(lisp!($body);)*
}
};
((defimpl <$($gen:ident)*> ($trate:ty) (for $typ:ty) $($body:tt)*)) => {
impl<$($gen),*> $trate for $typ where $($wty: $($wtr)*),* {
$(lisp!($body);)*
}
};
((if $cond:tt $yes:tt $no:tt)) => {
if lisp!($cond) { lisp!($yes) } else { lisp!($no) }
};
((while $cond:tt $($body:tt)*)) => {
while lisp!($cond) { $(lisp!($body));* }
};
// TODO for loops
((match $var:tt $(($cond:pat) $arm:tt)*)) => {
match lisp!($var) {
$($cond => lisp!($arm)),*
}
};
((do $($stmts:tt)*)) => {{
$(lisp!($stmts));*
}};
// variables
((let [] $($body:tt)*)) => {{
$(lisp!($body));*
}};
((let [mut $var:ident $val:tt $($bindings:tt)*] $($body:tt)*)) => {{
let mut $var = lisp!($val);
lisp!((let [$($bindings)*] $($body)*))
}};
((let [$var:ident $val:tt $($bindings:tt)*] $($body:tt)*)) => {{
let $var = lisp!($val);
lisp!((let [$($bindings)*] $($body)*))
}};
((:= $var:ident $val:tt)) => {
$var = lisp!($val);
};
// escape hatch
((rust $body:block)) => {
{ $body }
};
// list parsing
(($($elem:tt)*)) => {
lisp!(@list $($elem),*)
};
// parsers for unary and binary operators
(@list -, $arg:tt ) => { lisp!(@unary _neg, $arg ) };
(@list !, $arg:tt ) => { lisp!(@unary _not, $arg ) };
(@list +, $($arg:tt),*) => { lisp!(@binary _add, $($arg),*) };
(@list &, $arg:tt) => { &lisp!($arg) };
(@list &, $($arg:tt),*) => { lisp!(@binary _and, $($arg),*) };
(@list |, $($arg:tt),*) => { lisp!(@binary _or, $($arg),*) };
(@list ^, $($arg:tt),*) => { lisp!(@binary _xor, $($arg),*) };
(@list /, $($arg:tt),*) => { lisp!(@binary _div, $($arg),*) };
(@list *, $arg:tt) => { *lisp!($arg) };
(@list *, $($arg:tt),*) => { lisp!(@binary _mul, $($arg),*) };
(@list %, $($arg:tt),*) => { lisp!(@binary _rem, $($arg),*) };
(@list <<, $($arg:tt),*) => { lisp!(@binary _shl, $($arg),*) };
(@list >>, $($arg:tt),*) => { lisp!(@binary _shr, $($arg),*) };
(@list -, $($arg:tt),*) => { lisp!(@binary _sub, $($arg),*) };
(@list ==, $($arg:tt),*) => { lisp!(@binary _eq, $($arg),*) };
(@list !=, $($arg:tt),*) => { lisp!(@binary _ne, $($arg),*) };
(@list >, $($arg:tt),*) => { lisp!(@binary _gt, $($arg),*) };
(@list <, $($arg:tt),*) => { lisp!(@binary _lt, $($arg),*) };
(@list >=, $($arg:tt),*) => { lisp!(@binary _ge, $($arg),*) };
(@list <=, $($arg:tt),*) => { lisp!(@binary _le, $($arg),*) };
// generically turn unary/binary operators into function calls
// binary operators can be used as n-ary operators through @reduce
(@unary $op:ident, $a:tt) => { lisp!(@list $op, $a) };
(@binary $op:ident, $a:tt, $b:tt) => { lisp!(@list $op, $a, $b) };
(@binary $op:ident, $a:tt, $b:tt, $($rest:tt),+) =>
{ lisp!(@reduce $op,
($op $a $b),
$($rest),+) };
// reduce implementation
// TODO external entry point for @reduce
(@reduce $op:ident, $acc:tt) => { lisp!($acc) };
(@reduce $op:ident, $acc:tt, $a:tt) => { lisp!(@reduce $op,
($op $acc $a)) };
(@reduce $op:ident, $acc:tt, $a:tt, $($rest:tt),+) => { lisp!(@reduce $op,
($op $acc $a),
$($rest),+) };
// ns form helpers
(@krate $(^$attr:meta)* $krate:ident) => {
$(#[$attr:meta])*
extern crate $krate;
};
(@krate $(^$attr:meta)* ($krate:ident $alias:ident)) => {
$(#[$attr:meta])*
extern crate $krate as $alias;
};
(@uze ($($head:ident)*)) => { use $($head)::*; };
(@uze ($($head:ident)* { $($multiple:ident)* })) => {
lisp!(@uze mult ($($head)*) { $($multiple)* });
};
(@uze mult $head:tt { $($multiple:ident)* }) => {
$(lisp!(@uze mult out $head $multiple);)*
};
(@uze mult out ($($head:ident)*) $multiple:ident) => {
use $($head)::*::$multiple;
};
// macro calls
(@list $mac:ident, !) => {
$mac!()
};
(@list $mac:ident, !, $($arg:tt),*) => {
$mac!($(lisp!($arg)),*)
};
// struct constructors
(@list (:: $($name:tt)*), . $(, ($member:ident $val:tt))*) => {
$($name)* { $($member: lisp!($val))* }
};
(@list $name:ident, . $(, ($member:ident $val:tt))*) => {
$name { $($member: lisp!($val))* }
};
// function calls
(@list (:: $name:path) $(, $arg:tt),*) => {
$name($(lisp!($arg)),*)
};
(@list $name:expr $(, $arg:tt)*) => {
lisp!($name)($(lisp!($arg)),*)
};
// method calls
(@list ., $name:ident, $subj:tt $(, $arg:tt)*) => {
lisp!($subj).$name($(lisp!($arg)),*)
};
// one expression
($e:expr) => ($e);
}
#[macro_use] extern crate macrolisp;
use macrolisp::prelude::*;
lisp! {
(ns
(extern [bytes futures
tokio_io
tokio_proto
tokio_service])
(use [(std {io str})
(bytes BytesMut)
(futures {future Future BoxFuture})
(tokio_io {AsyncRead AsyncWrite})
(tokio_io codec {Encoder Decoder Framed})
(tokio_proto TcpServer)
(tokio_proto pipeline ServerProto)
(tokio_service Service)]))
(defstruct LineCodec)
(defstruct LineProto)
(defstruct Echo)
(defimpl (LineCodec: Decoder)
(deftype Item String)
(deftype Error io::Error)
(defn decode (((self &mut Self) (buf &mut BytesMut))
io::Result<Option<String>>)
(match (.position (.iter buf)
(lambda (b)
(== (* b)
b'\n')))
((Some(i)) (let ((line (.split_to buf i))) // remove the serialized frame from the buffer.
(.split_to buf 1) // Also remove the '\n'
(match ((:: str::from_utf8) (& line))
((Ok(s)) (Ok (Some (.to_string s))))
((Err(_)) (Err ((:: io::Error::new) ((:: io::ErrorKind::Other) .) "invalid UTF-8"))))))
((None) (Ok None)))))
(defimpl (LineCodec: Encoder)
(deftype Item String)
(deftype Error io::Error)
(defn encode (((self &mut Self) (msg String) (buf &mut BytesMut))
io::Result<()>)
(.extend buf (.as_bytes msg))
(.extend buf b"\n")
(Ok ())))
(defimpl <T> (LineProto: ServerProto<T>)
(where (T AsyncRead)
(T AsyncWrite)
(T 'static))
// For this protocal style, `Request` matches the `Item` type of the codec's `Encoder`
(deftype Request String)
// `Response` matches the `Item` type of the codec's `Decoder`
(deftype Response String)
// boilerplate to hook in the codec
(deftype Transport Framed<T, LineCodec>)
(deftype BindTransport Result<Self::Transport, io::Error>)
(defn bind_transport (((self &Self) (io T))
Self::BindTransport)
(Ok (.framed io LineCodec))))
(defimpl (Echo: Service)
// These types must match the corresponding protocol types;
(deftype Request String)
(deftype Response String)
// For non-streaming protocols, service errors are always io::Error
(deftype Error io::Error)
// The future for computing the response; box it for simplicity.
(deftype Future BoxFuture<Self::Response, Self::Error>)
// Produce a future for computing a response from a request.
(defn call (((self &Self) (req Self::Request))
Self::Future)
// In this case, the response is immediate.
(.boxed ((:: future::ok) req))))
(defn main (() ())
(let ((addr (.unwrap (.parse "0.0.0.0:12345"))) // Specify the localhost address
(server ((:: TcpServer::new) LineProto addr))) // The builder requires a protocol and an address
// We provide a way to *instantiate* the service for each new
// connection; here, we just immediately return a new instance.
(.serve server (lambda ()
(Ok Echo)))))
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment