Error checking for protocols. We'll need spans though.
This commit is contained in:
parent
156eceb24a
commit
c8739cb0bc
7 changed files with 274 additions and 141 deletions
|
@ -23,6 +23,29 @@ impl extensions <T:copy, U:copy> for (T, U) {
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
impl extensions<A: copy, B: copy> for (&[A], &[B]) {
|
||||||
|
fn zip() -> ~[(A, B)] {
|
||||||
|
let (a, b) = self;
|
||||||
|
vec::zip(a, b)
|
||||||
|
}
|
||||||
|
|
||||||
|
fn map<C>(f: fn(A, B) -> C) -> ~[C] {
|
||||||
|
let (a, b) = self;
|
||||||
|
vec::map2(a, b, f)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl extensions<A: copy, B: copy> for (~[A], ~[B]) {
|
||||||
|
fn zip() -> ~[(A, B)] {
|
||||||
|
let (a, b) = self;
|
||||||
|
vec::zip(a, b)
|
||||||
|
}
|
||||||
|
|
||||||
|
fn map<C>(f: fn(A, B) -> C) -> ~[C] {
|
||||||
|
let (a, b) = self;
|
||||||
|
vec::map2(a, b, f)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn test_tuple() {
|
fn test_tuple() {
|
||||||
|
|
|
@ -100,6 +100,7 @@ iface ext_ctxt {
|
||||||
fn bt_pop();
|
fn bt_pop();
|
||||||
fn span_fatal(sp: span, msg: ~str) -> !;
|
fn span_fatal(sp: span, msg: ~str) -> !;
|
||||||
fn span_err(sp: span, msg: ~str);
|
fn span_err(sp: span, msg: ~str);
|
||||||
|
fn span_warn(sp: span, msg: ~str);
|
||||||
fn span_unimpl(sp: span, msg: ~str) -> !;
|
fn span_unimpl(sp: span, msg: ~str) -> !;
|
||||||
fn span_bug(sp: span, msg: ~str) -> !;
|
fn span_bug(sp: span, msg: ~str) -> !;
|
||||||
fn bug(msg: ~str) -> !;
|
fn bug(msg: ~str) -> !;
|
||||||
|
@ -148,6 +149,10 @@ fn mk_ctxt(parse_sess: parse::parse_sess,
|
||||||
self.print_backtrace();
|
self.print_backtrace();
|
||||||
self.parse_sess.span_diagnostic.span_err(sp, msg);
|
self.parse_sess.span_diagnostic.span_err(sp, msg);
|
||||||
}
|
}
|
||||||
|
fn span_warn(sp: span, msg: ~str) {
|
||||||
|
self.print_backtrace();
|
||||||
|
self.parse_sess.span_diagnostic.span_warn(sp, msg);
|
||||||
|
}
|
||||||
fn span_unimpl(sp: span, msg: ~str) -> ! {
|
fn span_unimpl(sp: span, msg: ~str) -> ! {
|
||||||
self.print_backtrace();
|
self.print_backtrace();
|
||||||
self.parse_sess.span_diagnostic.span_unimpl(sp, msg);
|
self.parse_sess.span_diagnostic.span_unimpl(sp, msg);
|
||||||
|
|
|
@ -8,7 +8,9 @@ import parse::common::parser_common;
|
||||||
|
|
||||||
import pipes::parse_proto::proto_parser;
|
import pipes::parse_proto::proto_parser;
|
||||||
|
|
||||||
import pipes::pipec::methods;
|
import pipes::pipec::compile;
|
||||||
|
import pipes::proto::{visit, protocol};
|
||||||
|
import pipes::check::proto_check;
|
||||||
|
|
||||||
fn expand_proto(cx: ext_ctxt, _sp: span, id: ast::ident,
|
fn expand_proto(cx: ext_ctxt, _sp: span, id: ast::ident,
|
||||||
tt: ~[ast::token_tree]) -> base::mac_result
|
tt: ~[ast::token_tree]) -> base::mac_result
|
||||||
|
@ -22,5 +24,9 @@ fn expand_proto(cx: ext_ctxt, _sp: span, id: ast::ident,
|
||||||
|
|
||||||
let proto = rust_parser.parse_proto(id);
|
let proto = rust_parser.parse_proto(id);
|
||||||
|
|
||||||
|
// check for errors
|
||||||
|
visit(proto, cx);
|
||||||
|
|
||||||
|
// compile
|
||||||
base::mr_item(proto.compile(cx))
|
base::mr_item(proto.compile(cx))
|
||||||
}
|
}
|
||||||
|
|
74
src/libsyntax/ext/pipes/check.rs
Normal file
74
src/libsyntax/ext/pipes/check.rs
Normal file
|
@ -0,0 +1,74 @@
|
||||||
|
/// Correctness for protocols
|
||||||
|
|
||||||
|
/*
|
||||||
|
|
||||||
|
This section of code makes sure the protocol is likely to generate
|
||||||
|
correct code. The correctness criteria include:
|
||||||
|
|
||||||
|
* No protocols transition to states that don't exist.
|
||||||
|
* Messages step to states with the right number of type parameters.
|
||||||
|
|
||||||
|
In addition, this serves as a lint pass. Lint warns for the following
|
||||||
|
things.
|
||||||
|
|
||||||
|
* States with no messages, it's better to step to !.
|
||||||
|
|
||||||
|
It would also be nice to warn about unreachable states, but the
|
||||||
|
visitor infrastructure for protocols doesn't currently work well for
|
||||||
|
that.
|
||||||
|
|
||||||
|
*/
|
||||||
|
|
||||||
|
import dvec::extensions;
|
||||||
|
|
||||||
|
import ext::base::ext_ctxt;
|
||||||
|
|
||||||
|
import ast::{ident};
|
||||||
|
|
||||||
|
import proto::{state, protocol, next_state, methods};
|
||||||
|
import ast_builder::empty_span;
|
||||||
|
|
||||||
|
impl proto_check of proto::visitor<(), (), ()> for ext_ctxt {
|
||||||
|
fn visit_proto(_proto: protocol,
|
||||||
|
_states: &[()]) { }
|
||||||
|
|
||||||
|
fn visit_state(state: state, _m: &[()]) {
|
||||||
|
if state.messages.len() == 0 {
|
||||||
|
self.span_warn(
|
||||||
|
empty_span(), // use a real span!
|
||||||
|
#fmt("state %s contains no messages, \
|
||||||
|
consider stepping to a terminal state instead",
|
||||||
|
*state.name))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn visit_message(name: ident, _tys: &[@ast::ty],
|
||||||
|
this: state, next: next_state) {
|
||||||
|
alt next {
|
||||||
|
some({state: next, tys: next_tys}) {
|
||||||
|
let proto = this.proto;
|
||||||
|
if !proto.has_state(next) {
|
||||||
|
// This should be a span fatal, but then we need to
|
||||||
|
// track span information.
|
||||||
|
self.span_err(
|
||||||
|
empty_span(),
|
||||||
|
#fmt("message %s steps to undefined state, %s",
|
||||||
|
*name, *next));
|
||||||
|
}
|
||||||
|
|
||||||
|
let next = proto.get_state(next);
|
||||||
|
|
||||||
|
if next.ty_params.len() != next_tys.len() {
|
||||||
|
self.span_err(
|
||||||
|
empty_span(), // use a real span
|
||||||
|
#fmt("message %s target (%s) \
|
||||||
|
needs %u type parameters, but got %u",
|
||||||
|
*name, *next.name,
|
||||||
|
next.ty_params.len(),
|
||||||
|
next_tys.len()));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
none { }
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
|
@ -5,6 +5,8 @@ import to_str::to_str;
|
||||||
import dvec::dvec;
|
import dvec::dvec;
|
||||||
import dvec::extensions;
|
import dvec::extensions;
|
||||||
|
|
||||||
|
import tuple::extensions;
|
||||||
|
|
||||||
import ast::ident;
|
import ast::ident;
|
||||||
import util::interner;
|
import util::interner;
|
||||||
import interner::{intern, get};
|
import interner::{intern, get};
|
||||||
|
@ -14,57 +16,13 @@ import ext::base::{mk_ctxt, ext_ctxt};
|
||||||
import parse;
|
import parse;
|
||||||
import parse::*;
|
import parse::*;
|
||||||
|
|
||||||
|
import proto::*;
|
||||||
|
|
||||||
import ast_builder::ast_builder;
|
import ast_builder::ast_builder;
|
||||||
import ast_builder::methods;
|
import ast_builder::methods;
|
||||||
import ast_builder::path;
|
import ast_builder::path;
|
||||||
|
|
||||||
enum direction {
|
impl compile for message {
|
||||||
send, recv
|
|
||||||
}
|
|
||||||
|
|
||||||
impl of to_str for direction {
|
|
||||||
fn to_str() -> ~str {
|
|
||||||
alt self {
|
|
||||||
send { ~"send" }
|
|
||||||
recv { ~"recv" }
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl methods for direction {
|
|
||||||
fn reverse() -> direction {
|
|
||||||
alt self {
|
|
||||||
send { recv }
|
|
||||||
recv { send }
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
type next_state = option<{state: ident, tys: ~[@ast::ty]}>;
|
|
||||||
|
|
||||||
enum message {
|
|
||||||
// name, data, current state, next state
|
|
||||||
message(ident, ~[@ast::ty], state, next_state)
|
|
||||||
}
|
|
||||||
|
|
||||||
impl methods for message {
|
|
||||||
fn name() -> ident {
|
|
||||||
alt self {
|
|
||||||
message(id, _, _, _) {
|
|
||||||
id
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Return the type parameters actually used by this message
|
|
||||||
fn get_params() -> ~[ast::ty_param] {
|
|
||||||
alt self {
|
|
||||||
message(_, _, this, _) {
|
|
||||||
this.ty_params
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
fn gen_send(cx: ext_ctxt) -> @ast::item {
|
fn gen_send(cx: ext_ctxt) -> @ast::item {
|
||||||
#debug("pipec: gen_send");
|
#debug("pipec: gen_send");
|
||||||
alt self {
|
alt self {
|
||||||
|
@ -154,34 +112,7 @@ impl methods for message {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
enum state {
|
impl compile for state {
|
||||||
state_(@{
|
|
||||||
name: ident,
|
|
||||||
dir: direction,
|
|
||||||
ty_params: ~[ast::ty_param],
|
|
||||||
messages: dvec<message>,
|
|
||||||
proto: protocol,
|
|
||||||
}),
|
|
||||||
}
|
|
||||||
|
|
||||||
impl methods for state {
|
|
||||||
fn add_message(name: ident, +data: ~[@ast::ty], next: next_state) {
|
|
||||||
self.messages.push(message(name, data, self,
|
|
||||||
next));
|
|
||||||
}
|
|
||||||
|
|
||||||
fn filename() -> ~str {
|
|
||||||
(*self).proto.filename()
|
|
||||||
}
|
|
||||||
|
|
||||||
fn data_name() -> ident {
|
|
||||||
self.name
|
|
||||||
}
|
|
||||||
|
|
||||||
fn to_ty(cx: ext_ctxt) -> @ast::ty {
|
|
||||||
cx.ty_path(path(self.name).add_tys(cx.ty_vars(self.ty_params)))
|
|
||||||
}
|
|
||||||
|
|
||||||
fn to_type_decls(cx: ext_ctxt) -> ~[@ast::item] {
|
fn to_type_decls(cx: ext_ctxt) -> ~[@ast::item] {
|
||||||
#debug("pipec: to_type_decls");
|
#debug("pipec: to_type_decls");
|
||||||
// This compiles into two different type declarations. Say the
|
// This compiles into two different type declarations. Say the
|
||||||
|
@ -248,47 +179,7 @@ impl methods for state {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
enum protocol {
|
impl compile for protocol {
|
||||||
protocol_(@{
|
|
||||||
name: ident,
|
|
||||||
states: dvec<state>,
|
|
||||||
}),
|
|
||||||
}
|
|
||||||
|
|
||||||
fn protocol(name: ident) -> protocol {
|
|
||||||
protocol_(@{name: name, states: dvec()})
|
|
||||||
}
|
|
||||||
|
|
||||||
impl methods for protocol {
|
|
||||||
fn add_state(name: ident, dir: direction) -> state {
|
|
||||||
self.add_state_poly(name, dir, ~[])
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Get or create a state.
|
|
||||||
fn get_state(name: ident) -> state {
|
|
||||||
self.states.find(|i| i.name == name).get()
|
|
||||||
}
|
|
||||||
|
|
||||||
fn add_state_poly(name: ident, dir: direction,
|
|
||||||
+ty_params: ~[ast::ty_param]) -> state {
|
|
||||||
let messages = dvec();
|
|
||||||
|
|
||||||
let state = state_(@{
|
|
||||||
name: name,
|
|
||||||
dir: dir,
|
|
||||||
ty_params: ty_params,
|
|
||||||
messages: messages,
|
|
||||||
proto: self
|
|
||||||
});
|
|
||||||
|
|
||||||
self.states.push(state);
|
|
||||||
state
|
|
||||||
}
|
|
||||||
|
|
||||||
fn filename() -> ~str {
|
|
||||||
~"proto://" + *self.name
|
|
||||||
}
|
|
||||||
|
|
||||||
fn gen_init(cx: ext_ctxt) -> @ast::item {
|
fn gen_init(cx: ext_ctxt) -> @ast::item {
|
||||||
let start_state = self.states[0];
|
let start_state = self.states[0];
|
||||||
|
|
||||||
|
@ -302,18 +193,12 @@ impl methods for protocol {
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
parse_item_from_source_str(
|
cx.parse_item(#fmt("fn init%s() -> (client::%s, server::%s)\
|
||||||
self.filename(),
|
|
||||||
@#fmt("fn init%s() -> (client::%s, server::%s)\
|
|
||||||
{ %s }",
|
{ %s }",
|
||||||
start_state.ty_params.to_source(),
|
start_state.ty_params.to_source(),
|
||||||
start_state.to_ty(cx).to_source(),
|
start_state.to_ty(cx).to_source(),
|
||||||
start_state.to_ty(cx).to_source(),
|
start_state.to_ty(cx).to_source(),
|
||||||
body.to_source()),
|
body.to_source()))
|
||||||
cx.cfg(),
|
|
||||||
~[],
|
|
||||||
ast::public,
|
|
||||||
cx.parse_sess()).get()
|
|
||||||
}
|
}
|
||||||
|
|
||||||
fn compile(cx: ext_ctxt) -> @ast::item {
|
fn compile(cx: ext_ctxt) -> @ast::item {
|
||||||
|
@ -407,15 +292,3 @@ impl parse_utils for ext_ctxt {
|
||||||
self.parse_sess())
|
self.parse_sess())
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl methods<A: copy, B: copy> for (~[A], ~[B]) {
|
|
||||||
fn zip() -> ~[(A, B)] {
|
|
||||||
let (a, b) = self;
|
|
||||||
vec::zip(a, b)
|
|
||||||
}
|
|
||||||
|
|
||||||
fn map<C>(f: fn(A, B) -> C) -> ~[C] {
|
|
||||||
let (a, b) = self;
|
|
||||||
vec::map2(a, b, f)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
150
src/libsyntax/ext/pipes/proto.rs
Normal file
150
src/libsyntax/ext/pipes/proto.rs
Normal file
|
@ -0,0 +1,150 @@
|
||||||
|
import to_str::to_str;
|
||||||
|
import dvec::{dvec, extensions};
|
||||||
|
|
||||||
|
import ast::{ident};
|
||||||
|
|
||||||
|
import ast_builder::{path, methods, ast_builder};
|
||||||
|
|
||||||
|
enum direction {
|
||||||
|
send, recv
|
||||||
|
}
|
||||||
|
|
||||||
|
impl of to_str for direction {
|
||||||
|
fn to_str() -> ~str {
|
||||||
|
alt self {
|
||||||
|
send { ~"send" }
|
||||||
|
recv { ~"recv" }
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl methods for direction {
|
||||||
|
fn reverse() -> direction {
|
||||||
|
alt self {
|
||||||
|
send { recv }
|
||||||
|
recv { send }
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
type next_state = option<{state: ident, tys: ~[@ast::ty]}>;
|
||||||
|
|
||||||
|
enum message {
|
||||||
|
// name, data, current state, next state
|
||||||
|
message(ident, ~[@ast::ty], state, next_state)
|
||||||
|
}
|
||||||
|
|
||||||
|
impl methods for message {
|
||||||
|
fn name() -> ident {
|
||||||
|
alt self {
|
||||||
|
message(id, _, _, _) {
|
||||||
|
id
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Return the type parameters actually used by this message
|
||||||
|
fn get_params() -> ~[ast::ty_param] {
|
||||||
|
alt self {
|
||||||
|
message(_, _, this, _) {
|
||||||
|
this.ty_params
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
enum state {
|
||||||
|
state_(@{
|
||||||
|
name: ident,
|
||||||
|
dir: direction,
|
||||||
|
ty_params: ~[ast::ty_param],
|
||||||
|
messages: dvec<message>,
|
||||||
|
proto: protocol,
|
||||||
|
}),
|
||||||
|
}
|
||||||
|
|
||||||
|
impl methods for state {
|
||||||
|
fn add_message(name: ident, +data: ~[@ast::ty], next: next_state) {
|
||||||
|
self.messages.push(message(name, data, self,
|
||||||
|
next));
|
||||||
|
}
|
||||||
|
|
||||||
|
fn filename() -> ~str {
|
||||||
|
(*self).proto.filename()
|
||||||
|
}
|
||||||
|
|
||||||
|
fn data_name() -> ident {
|
||||||
|
self.name
|
||||||
|
}
|
||||||
|
|
||||||
|
fn to_ty(cx: ext_ctxt) -> @ast::ty {
|
||||||
|
cx.ty_path(path(self.name).add_tys(cx.ty_vars(self.ty_params)))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
enum protocol {
|
||||||
|
protocol_(@{
|
||||||
|
name: ident,
|
||||||
|
states: dvec<state>,
|
||||||
|
}),
|
||||||
|
}
|
||||||
|
|
||||||
|
fn protocol(name: ident) -> protocol {
|
||||||
|
protocol_(@{name: name, states: dvec()})
|
||||||
|
}
|
||||||
|
|
||||||
|
impl methods for protocol {
|
||||||
|
fn add_state(name: ident, dir: direction) -> state {
|
||||||
|
self.add_state_poly(name, dir, ~[])
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Get or create a state.
|
||||||
|
fn get_state(name: ident) -> state {
|
||||||
|
self.states.find(|i| i.name == name).get()
|
||||||
|
}
|
||||||
|
|
||||||
|
fn has_state(name: ident) -> bool {
|
||||||
|
self.states.find(|i| i.name == name) != none
|
||||||
|
}
|
||||||
|
|
||||||
|
fn add_state_poly(name: ident, dir: direction,
|
||||||
|
+ty_params: ~[ast::ty_param]) -> state {
|
||||||
|
let messages = dvec();
|
||||||
|
|
||||||
|
let state = state_(@{
|
||||||
|
name: name,
|
||||||
|
dir: dir,
|
||||||
|
ty_params: ty_params,
|
||||||
|
messages: messages,
|
||||||
|
proto: self
|
||||||
|
});
|
||||||
|
|
||||||
|
self.states.push(state);
|
||||||
|
state
|
||||||
|
}
|
||||||
|
|
||||||
|
fn filename() -> ~str {
|
||||||
|
~"proto://" + *self.name
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
trait visitor<Tproto, Tstate, Tmessage> {
|
||||||
|
fn visit_proto(proto: protocol, st: &[Tstate]) -> Tproto;
|
||||||
|
fn visit_state(state: state, m: &[Tmessage]) -> Tstate;
|
||||||
|
fn visit_message(name: ident, tys: &[@ast::ty],
|
||||||
|
this: state, next: next_state) -> Tmessage;
|
||||||
|
}
|
||||||
|
|
||||||
|
fn visit<Tproto, Tstate, Tmessage, V: visitor<Tproto, Tstate, Tmessage>>(
|
||||||
|
proto: protocol, visitor: V) -> Tproto {
|
||||||
|
|
||||||
|
// the copy keywords prevent recursive use of dvec
|
||||||
|
let states = do (copy proto.states).map_to_vec |s| {
|
||||||
|
let messages = do (copy s.messages).map_to_vec |m| {
|
||||||
|
let message(name, tys, this, next) = m;
|
||||||
|
visitor.visit_message(name, tys, this, next)
|
||||||
|
};
|
||||||
|
visitor.visit_state(s, messages)
|
||||||
|
};
|
||||||
|
visitor.visit_proto(proto, states)
|
||||||
|
}
|
|
@ -85,5 +85,7 @@ mod ext {
|
||||||
mod ast_builder;
|
mod ast_builder;
|
||||||
mod parse_proto;
|
mod parse_proto;
|
||||||
mod pipec;
|
mod pipec;
|
||||||
|
mod proto;
|
||||||
|
mod check;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue