Skip to content

Commit

Permalink
Allow invoking primitives
Browse files Browse the repository at this point in the history
  • Loading branch information
kevinmehall committed Jul 6, 2017
1 parent 564de6a commit 098302d
Show file tree
Hide file tree
Showing 6 changed files with 132 additions and 91 deletions.
28 changes: 14 additions & 14 deletions src/language/expr.rs
Original file line number Diff line number Diff line change
Expand Up @@ -141,7 +141,7 @@ fn resolve_call<'s>(ctxt: &'s Ctxt<'s>, scope: &Scope, func: &'s ast::Expr, arg:

/// Resolve an expression as used in the argument of an `on` block, defining variables
pub fn on_expr_message<'s>(ctx: &'s Ctxt<'s>, scope: &mut Scope,
shape: &mut Shape, expr: &'s ast::Expr) -> Vec<Option<Expr>> {
shape: &Shape, expr: &'s ast::Expr) -> Vec<Option<Expr>> {

// First test if this shape matches the expression, in order to avoid binding variables
// for the wrong protocol variant.
Expand All @@ -161,21 +161,21 @@ pub fn on_expr_message<'s>(ctx: &'s Ctxt<'s>, scope: &mut Scope,
}

match (shape, expr) {
(&mut Shape::Const(ref c), &ast::Expr::Value(ref val)) if c == val => vec![],
(&Shape::Const(ref c), &ast::Expr::Value(ref val)) if c == val => vec![],

(&mut Shape::Val(ref ty), &ast::Expr::Var(ref name)) => { // A variable binding
(&Shape::Val(ref ty), &ast::Expr::Var(ref name)) => { // A variable binding
let id = scope.new_variable(ctx.session, &name[..], ty.clone());
vec![Some((Expr::Variable(id, ty.clone())))]
}

(&mut Shape::Tup(ref mut ss), &ast::Expr::Var(ref name)) => { // A variable binding for a tuple
(&Shape::Tup(ref ss), &ast::Expr::Var(ref name)) => { // A variable binding for a tuple
// Capture a tuple by recursively building a tuple Item containing each of the
// captured variables
fn build_tuple<'s>(ctx: &'s Ctxt<'s>, msg: &mut Vec<Option<Expr>>, ss: &mut [Shape]) -> Item {
Item::Tuple(ss.iter_mut().map(|i| {
fn build_tuple<'s>(ctx: &'s Ctxt<'s>, msg: &mut Vec<Option<Expr>>, ss: &[Shape]) -> Item {
Item::Tuple(ss.iter().map(|i| {
match *i {
Shape::Const(ref c) => Item::Value(Expr::Const(c.clone())),
Shape::Tup(ref mut t) => build_tuple(ctx, msg, &mut t[..]),
Shape::Tup(ref t) => build_tuple(ctx, msg, &t[..]),
Shape::Val(ref ty) => {
let id = ctx.session.make_id();
msg.push(Some((Expr::Variable(id, ty.clone()))));
Expand All @@ -187,28 +187,28 @@ pub fn on_expr_message<'s>(ctx: &'s Ctxt<'s>, scope: &mut Scope,
}

let mut msg = Vec::new();
scope.bind(name, build_tuple(ctx, &mut msg, &mut ss[..]));
scope.bind(name, build_tuple(ctx, &mut msg, &ss[..]));
msg
}

(&mut Shape::Val(_), expr) => { // A match against a refutable pattern
(&Shape::Val(_), expr) => { // A match against a refutable pattern
let e = resolve(ctx, None, &mut |_| { panic!("Variable binding not allowed here") }, expr);
vec![Some(e)]
}

(&mut Shape::Tup(ref mut ss), &ast::Expr::Tup(ref se)) => {
ss.iter_mut().zip(se.iter()).flat_map(|(s, i)| {
(&Shape::Tup(ref ss), &ast::Expr::Tup(ref se)) => {
ss.iter().zip(se.iter()).flat_map(|(s, i)| {
on_expr_message(ctx, scope, s, i)
}).collect()
}

(&mut Shape::Protocol { ref mut messages, ..}, expr) => {
(&Shape::Protocol { ref messages, ..}, expr) => {
if messages.len() == 1 {
on_expr_message(ctx, scope, &mut messages[0], expr)
on_expr_message(ctx, scope, &messages[0], expr)
} else {
let mut fields = vec![None];
let mut matching_variants = 0;
for (i, shape) in messages.iter_mut().enumerate() {
for (i, shape) in messages.iter().enumerate() {
if try_match(shape, expr) {
fields[0] = Some(Expr::Const(Value::Integer(i as i64)));
fields.extend(on_expr_message(ctx, scope, shape, expr));
Expand Down
116 changes: 77 additions & 39 deletions src/language/program.rs
Original file line number Diff line number Diff line change
@@ -1,8 +1,9 @@
use super::{ ast, nfa };
use super::{ ast, nfa, PrimitiveDef, PrimitiveDefFields };
use super::dfa::{ self, Dfa };
use super::scope::Scope;
use protocol::{ Shape, Fields };
use super::protocol::{ ProtocolScope, resolve_protocol_invoke };
use super::protocol::{ ProtocolScope, DefImpl, resolve_protocol_invoke };
use super::step::resolve_seq;
use super::Ctxt;
use data::{DataMode};
use process::{ Process, ProcessStack, ProcessInfo };
Expand All @@ -29,56 +30,93 @@ impl Process for ProgramFlip {
}
}

pub fn resolve_process<'s>(ctx: &'s Ctxt<'s>,
scope: &Scope,
protocol_scope: &ProtocolScope<'s>,
shape: &Shape,
fields_down: &Fields,
p: &'s ast::Process) -> ProcessInfo {
match *p {
ast::Process::Call(ref name, ref arg) => {
let arg = super::expr::rexpr(ctx, scope, arg);
let (shape_up, mut step) = protocol_scope.call(ctx, shape, name, arg);
fn compile_block<'s>(ctx: &'s Ctxt<'s>,
scope: &Scope,
protocol_scope: &ProtocolScope<'s>,
shape_down: &Shape,
fields_down: &Fields,
shape_up: Shape,
seq: &'s ast::Block,
name: &str) -> ProcessInfo {
let mut step = resolve_seq(ctx, &scope, protocol_scope, shape_down, &shape_up, seq);

let mut fields_up = shape_up.fields(DataMode { down: false, up: true });
super::step::infer_direction(&mut step, &fields_down, &mut fields_up);

if let Some(mut f) = ctx.session.debug_file(|| format!("{}.steps", name)) {
step.write_tree(&mut f, 0).unwrap_or_else(|e| error!("{}", e));
}

let mut fields_up = shape_up.fields(DataMode { down: false, up: true });
super::step::infer_direction(&mut step, &fields_down, &mut fields_up);
let mut nfa = nfa::from_step_tree(&step);

if let Some(mut f) = ctx.session.debug_file(|| format!("{}.steps", name)) {
step.write_tree(&mut f, 0).unwrap_or_else(|e| error!("{}", e));
}
if let Some(mut f) = ctx.session.debug_file(|| format!("{}.nfa.dot", name)) {
nfa.to_graphviz(&mut f).unwrap_or_else(|e| error!("{}", e));
}

let mut nfa = nfa::from_step_tree(&step);
nfa.remove_useless_epsilons();

if let Some(mut f) = ctx.session.debug_file(|| format!("{}.nfa.dot", name)) {
nfa.to_graphviz(&mut f).unwrap_or_else(|e| error!("{}", e));
}
if let Some(mut f) = ctx.session.debug_file(|| format!("{}.cleaned.nfa.dot", name)) {
nfa.to_graphviz(&mut f).unwrap_or_else(|e| error!("{}", e));
}

nfa.remove_useless_epsilons();
let dfa = dfa::make_dfa(&nfa, &fields_down, &fields_up);

if let Some(mut f) = ctx.session.debug_file(|| format!("{}.cleaned.nfa.dot", name)) {
nfa.to_graphviz(&mut f).unwrap_or_else(|e| error!("{}", e));
}
if let Some(mut f) = ctx.session.debug_file(|| format!("{}.dfa.dot", name)) {
dfa.to_graphviz(&mut f).unwrap_or_else(|e| error!("{}", e));
}

let dfa = dfa::make_dfa(&nfa, &fields_down, &fields_up);
ProcessInfo { fields_up, shape_up, implementation: Box::new(Program{dfa}) }
}

if let Some(mut f) = ctx.session.debug_file(|| format!("{}.dfa.dot", name)) {
dfa.to_graphviz(&mut f).unwrap_or_else(|e| error!("{}", e));
}
fn call_primitive<'s>(_ctx: &'s Ctxt<'s>,
scope: &Scope,
fields_down: &Fields,
shape_up: Shape,
primitive_impls: &[PrimitiveDef],
name: &str) -> ProcessInfo {
for def in primitive_impls {
if fields_down == &def.fields_down {
info!("Using {} for primitive at {}", def.id, name);
let fields_up = match def.fields_up {
PrimitiveDefFields::Explicit(ref fields) => fields.clone(),
PrimitiveDefFields::Auto(dir) => shape_up.fields(dir),
};

let implementation = (def.instantiate)(scope).expect("Failed to instantiate primitive");

ProcessInfo { fields_up, shape_up, implementation: Box::new(Program{dfa}) }
return ProcessInfo { fields_up, shape_up, implementation };
}
ast::Process::Block(ref block) => {
let mut shape_up = Shape::null();
let mut step = super::step::resolve_seq(ctx, scope, protocol_scope, shape, &mut shape_up, block);
}

let mut fields_up = shape_up.fields(DataMode { up: false, down: false });
super::step::infer_direction(&mut step, &fields_down, &mut fields_up);
panic!("No matching call for primitive {} for {:?}", name, fields_down);
}

pub fn resolve_process<'s>(ctx: &'s Ctxt<'s>,
scope: &Scope,
protocol_scope: &ProtocolScope<'s>,
shape_down: &Shape,
fields_down: &Fields,
p: &'s ast::Process) -> ProcessInfo {
match *p {
ast::Process::Call(ref name, ref arg) => {
let arg = super::expr::rexpr(ctx, scope, arg);
let (scope, imp, shape_up) = protocol_scope.find(ctx, shape_down, name, arg);

match *imp {
DefImpl::Code(ref seq) => {
compile_block(ctx, &scope, protocol_scope, shape_down, fields_down, shape_up, seq, &name)
}
DefImpl::Primitive(ref primitive) => {
call_primitive(ctx, &scope, fields_down, shape_up, primitive, &name)
},
}
}

let mut nfa = nfa::from_step_tree(&step);
nfa.remove_useless_epsilons();
let dfa = dfa::make_dfa(&nfa, &fields_down, &fields_up);
ProcessInfo { shape_up, fields_up, implementation: Box::new(Program { dfa }) }
ast::Process::Block(ref block) => {
let shape_up = Shape::null();
compile_block(ctx, scope, protocol_scope, shape_down, fields_down, shape_up, block, "anon_block")
}

ast::Process::Literal(dir, ref shape_up_expr, ref block) => {
let is_up = match dir {
ast::ProcessLiteralDirection::Up => true,
Expand Down
57 changes: 26 additions & 31 deletions src/language/protocol.rs
Original file line number Diff line number Diff line change
Expand Up @@ -95,18 +95,15 @@ fn resolve_protocol_match<'a>(ctx: &'a Ctxt<'a>, scope: &Scope, ast: &'a ast::Pr
struct WithBlock<'a> {
protocol: ProtocolMatch<'a>,
name: String,
implementation: WithDef<'a>
param: &'a ast::Expr,
scope: Scope,
shape_up: &'a Option<ast::ProtocolRef>,
implementation: DefImpl<'a>
}

enum WithDef <'a> {
Code {
def: &'a ast::Def,
scope: Scope,
},
Primitive {
shape_up: &'a Option<ast::ProtocolRef>,
defs: Vec<PrimitiveDef>,
}
pub enum DefImpl <'a> {
Code(&'a ast::Block),
Primitive(Vec<PrimitiveDef>)
}


Expand All @@ -123,22 +120,25 @@ impl<'a> ProtocolScope<'a> {
self.entries.push(WithBlock {
protocol: resolve_protocol_match(ctx, &scope, &def.bottom),
name: def.name.clone(),
implementation: WithDef::Code {
def: def,
scope: scope,
}
scope: scope,
param: &def.param,
shape_up: &def.top,
implementation: DefImpl::Code(&def.block)
});
}

pub fn add_primitive(&mut self, ctx: &'a Ctxt<'a>, scope: &Scope, header: &'a ast::PrimitiveHeader, defs: Vec<PrimitiveDef>) {
self.entries.push(WithBlock {
protocol: resolve_protocol_match(ctx, scope, &header.bottom),
name: header.name.clone(),
implementation: WithDef::Primitive { shape_up: &header.top, defs }
scope: scope.child(),
param: &header.param,
shape_up: &header.top,
implementation: DefImpl::Primitive(defs),
});
}

fn find<'m>(&'m self, _shape: &Shape, name: &str) -> Option<&'m WithBlock<'a>> {
fn find_by_name<'m>(&'m self, _shape: &Shape, name: &str) -> Option<&'m WithBlock<'a>> {
let mut found = None;
for entry in &self.entries {
if entry.name != name { continue }
Expand All @@ -152,22 +152,17 @@ impl<'a> ProtocolScope<'a> {
found
}

pub fn call(&self, ctx: &'a Ctxt<'a>, shape: &Shape, name: &str, param: Item) -> (Shape, Step) {
match self.find(shape, name).unwrap_or_else(|| panic!("No definition found for `{}`", name)).implementation {
WithDef::Code { ref def, ref scope } => {
let mut scope = scope.child();
let mut shape_up = if let Some(ref x) = def.top {
resolve_protocol_invoke(ctx, &scope, x)
} else {
Shape::null()
};
pub fn find(&self, ctx: &'a Ctxt<'a>, shape: &Shape, name: &str, param: Item) -> (Scope, &DefImpl<'a>, Shape) {
let block = self.find_by_name(shape, name).unwrap_or_else(|| panic!("No definition found for `{}`", name));
let mut scope = block.scope.child();
expr::assign(ctx.session, &mut scope, &block.param, param);

expr::assign(ctx.session, &mut scope, &def.param, param);
let step = resolve_seq(ctx, &scope, self, shape, &mut shape_up, &def.block);
let shape_up = if let &Some(ref x) = block.shape_up {
resolve_protocol_invoke(ctx, &scope, x)
} else {
Shape::null()
};

(shape_up, step)
}
WithDef::Primitive { .. } => panic!("Primitive not allowed here"),
}
(scope, &block.implementation, shape_up)
}
}
15 changes: 11 additions & 4 deletions src/language/step.rs
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@ use session::ValueID;
use super::{ ast, expr };
use super::scope::{ Scope, Item };
use super::eval::Expr;
use super::protocol::ProtocolScope;
use super::protocol::{ ProtocolScope, DefImpl };
use super::module_loader::Ctxt;
use protocol::{ Shape, Fields };

Expand Down Expand Up @@ -112,13 +112,20 @@ fn resolve_action<'s>(ctx: &'s Ctxt<'s>,
scope: &Scope,
protocol_scope: &ProtocolScope<'s>,
shape_down: &Shape,
shape_up: &mut Shape,
shape_up: &Shape,
action: &'s ast::Action) -> Step {
match *action {
ast::Action::Call(ref name, ref param_ast, ref body) => {
let param = expr::rexpr(ctx, scope, param_ast);

let (child_shape, step) = protocol_scope.call(ctx, shape_down, name, param);
let (scope, imp, mut shape_up) = protocol_scope.find(ctx, shape_down, name, param);

let step = match *imp {
DefImpl::Code(ref seq) => {
resolve_seq(ctx, &scope, protocol_scope, shape_down, &mut shape_up, seq)
}
DefImpl::Primitive(..) => panic!("Primitive not allowed here"),
};

if let &Some(ref _body) = body {
unimplemented!();
Expand Down Expand Up @@ -197,7 +204,7 @@ pub fn resolve_seq<'s>(ctx: &'s Ctxt<'s>,
pscope: &Scope,
protocol_scope: &ProtocolScope<'s>,
shape_down: &Shape,
shape_up: &mut Shape,
shape_up: &Shape,
block: &'s ast::Block) -> Step {
let mut scope = pscope.child();
resolve_letdef(ctx, &mut scope, &block.lets);
Expand Down
4 changes: 2 additions & 2 deletions src/primitives/file_io.rs
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@ use language::{ Ctxt, PrimitiveDef, PrimitiveDefFields };
use process::Process;

pub fn add_primitives<'a>(loader: &'a Ctxt<'a>) {
loader.define_primitive("with Base() def file(name, #r): Bytes", vec![
loader.define_primitive("with Base() def file_r(name): Bytes", vec![
PrimitiveDef {
id: "file_read",
fields_down: Fields::null(),
Expand All @@ -20,7 +20,7 @@ pub fn add_primitives<'a>(loader: &'a Ctxt<'a>) {
}
]);

loader.define_primitive("with Base() def file(name, #w): Bytes", vec![
loader.define_primitive("with Base() def file_w(name): Bytes", vec![
PrimitiveDef {
id: "file_write",
fields_down: Fields::null(),
Expand Down
3 changes: 2 additions & 1 deletion src/primitives/mod.rs
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
macro_rules! bind {
(|$($i:ident: $t:ty),*| $body:block) => {
Box::new(|s: &::language::Scope| {
$(let $i:$t = s.get_as(stringify!(s))?;)*
$(let $i:$t = s.get_as(stringify!($i))?;)*
$body
})
};
Expand All @@ -17,6 +17,7 @@ pub fn add_primitives<'a>(loader: &'a Ctxt<'a>) {
loader.define_prelude(r#"
protocol Base() {}
let Bytes = #0..#255
let Float32 = -1.0..1.0
"#);

add_primitive_fns(loader);
Expand Down

0 comments on commit 098302d

Please sign in to comment.