diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000000000000000000000000000000000000..e15e2182882a86eb6a6cf25d1c3d0f2654559fd4 --- /dev/null +++ b/.gitignore @@ -0,0 +1,5 @@ +target +/.idea +**/*.rs.bk +Cargo.lock +main \ No newline at end of file diff --git a/Cargo.toml b/Cargo.toml new file mode 100644 index 0000000000000000000000000000000000000000..d0e004afbc8ddd1f0254255ceffc108d4978bf6a --- /dev/null +++ b/Cargo.toml @@ -0,0 +1,32 @@ +[package] +name = "reowolf_rs" +version = "0.1.0" +authors = ["Christopher Esterhuyse ", "Hans-Dieter Hiep "] +edition = "2018" + +[dependencies] + +getrandom = "0.1.14" # tiny crate. used to guess controller-id +take_mut = "0.2.2" +maplit = "1.0.2" # convenience macros +indexmap = "1.3.0" # hashsets with efficient arbitrary element removal + +# network stuff +integer-encoding = "1.0.7" +byteorder = "1.3.2" +mio = "0.6.21" # migrate to mio 0.7.0 when it stabilizes. It's much better. +mio-extras = "2.0.6" + +# protocol stuff +id-arena = "2.2.1" +backtrace = "0.3" + +[dev-dependencies] +test-generator = "0.3.0" + +[lib] +crate-type = ["cdylib"] + +[features] +default = ["ffi"] +ffi = [] \ No newline at end of file diff --git a/README.md b/README.md index 1f22b35d4fe227079ed93f60373eb51e823273c9..ad2feda1fcafed0544f4f332746666dbe81ae051 100644 --- a/README.md +++ b/README.md @@ -1 +1,3 @@ -# Reowolf +# Reowolf Implementation + +(Readme todo) \ No newline at end of file diff --git a/cbindgen.toml b/cbindgen.toml new file mode 100644 index 0000000000000000000000000000000000000000..f7e4686ef41006bd371f378a3ac2d6116d18d459 --- /dev/null +++ b/cbindgen.toml @@ -0,0 +1,4 @@ +language = "C" + +header = "/* CBindgen generated */" +include_guard = "REOWOLF_HEADER_DEFINED" \ No newline at end of file diff --git a/main.c b/main.c new file mode 100644 index 0000000000000000000000000000000000000000..4fc378f7f9f1b415915ef1c91cf7163f75da2d8b --- /dev/null +++ b/main.c @@ -0,0 +1,22 @@ +#include +#include "reowolf.h" + +int main() { + Connector* c = connector_new(); + + if (connector_configure(c, "primitive main(){}")) { + printf("CONFIG FAILED\n"); + } + if (port_bind_native(c, 0)) { + printf("BIND0 FAILED\n"); + } + if (port_bind_passive(c, 1, "0.0.0.0:8888")) { + printf("BIND1 FAILED\n"); + } + if (port_bind_passive(c, 2, "0.0.0.0:8888")) { + printf("BIND1 FAILED\n"); + } + printf("OK\n"); + connector_destroy(c); + return 0; +} \ No newline at end of file diff --git a/reowolf.h b/reowolf.h new file mode 100644 index 0000000000000000000000000000000000000000..e57118b313d79158b18039f8591994cb5f88e780 --- /dev/null +++ b/reowolf.h @@ -0,0 +1,109 @@ +/* CBindgen generated */ + +#ifndef REOWOLF_HEADER_DEFINED +#define REOWOLF_HEADER_DEFINED + +#include +#include +#include +#include + +typedef struct Connector Connector; + +typedef uint32_t ControllerId; + +/** + * Configures the given Reowolf connector with a protocol description in PDL. + * Returns: + */ +int connector_configure(Connector *connector, char *pdl); + +/** + * Provides a binding annotation for the port with the given index with "active": + * (The port will conenct to a "passive" port at the given address during connect()) + * Returns: + * - 0 SUCCESS: connected successfully + * - TODO error codes + */ +int connector_connect(Connector *connector, uint64_t timeout_millis); + +/** + * Destroys the given connector, freeing its underlying resources. + */ +void connector_destroy(Connector *connector); + +/** + * Resets the error message buffer. + * Returns: + * - 0 if an error was cleared + * - 1 if there was no error to clear + */ +int connector_error_clear(void); + +/** + * Returns a pointer into the error buffer for reading as a null-terminated string + * Returns null if there is no error in the buffer. + */ +const char *connector_error_peek(void); + +/** + * Creates and returns Reowolf Connector structure allocated on the heap. + */ +Connector *connector_new(void); + +int connector_next_batch(Connector *connector); + +int connector_sync(Connector *connector, uint64_t timeout_millis); + +/** + * Creates and returns Reowolf Connector structure allocated on the heap. + */ +Connector *connector_with_controller_id(ControllerId controller_id); + +/** + * Provides a binding annotation for the port with the given index with "active": + * (The port will conenct to a "passive" port at the given address during connect()) + * Returns: + * - 0 for success + * - 1 if the port was already bound and was left unchanged + */ +int port_bind_active(Connector *connector, unsigned int proto_port_index, const char *address); + +/** + * Provides a binding annotation for the port with the given index with "native": + * (The port is exposed for reading and writing from the application) + * Returns: + */ +int port_bind_native(Connector *connector, uintptr_t proto_port_index); + +/** + * Provides a binding annotation for the port with the given index with "native": + * (The port is exposed for reading and writing from the application) + * Returns: + */ +int port_bind_passive(Connector *connector, unsigned int proto_port_index, const char *address); + +int port_close(Connector *connector, unsigned int _proto_port_index); + +/** + * Prepares to synchronously put a message at the given port, writing it to the given buffer. + * - 0 SUCCESS + * - 1 this port has the wrong direction + * - 2 this port is already marked to get + */ +int port_get(Connector *connector, unsigned int proto_port_index); + +/** + * Prepares to synchronously put a message at the given port, reading it from the given buffer. + */ +int port_put(Connector *connector, + unsigned int proto_port_index, + unsigned char *buf_ptr, + unsigned int msg_len); + +int read_gotten(Connector *connector, + unsigned int proto_port_index, + const unsigned char **buf_ptr_outptr, + unsigned int *len_outptr); + +#endif /* REOWOLF_HEADER_DEFINED */ diff --git a/rustfmt.toml b/rustfmt.toml new file mode 100644 index 0000000000000000000000000000000000000000..7f3e0d410db11c55440296ab27faf6e2f1c59b7b --- /dev/null +++ b/rustfmt.toml @@ -0,0 +1,3 @@ +reorder_imports = true +use_field_init_shorthand = true +use_small_heuristics = "Max" \ No newline at end of file diff --git a/src/common.rs b/src/common.rs new file mode 100644 index 0000000000000000000000000000000000000000..09c7b444324f49055bac3e5a20295aa631e94054 --- /dev/null +++ b/src/common.rs @@ -0,0 +1,115 @@ +///////////////////// PRELUDE ///////////////////// + +pub use core::{ + cmp::Ordering, + fmt::Debug, + hash::{Hash, Hasher}, + ops::{Range, RangeFrom}, + time::Duration, +}; +pub use indexmap::{IndexMap, IndexSet}; +pub use maplit::{hashmap, hashset}; +pub use mio::{ + net::{TcpListener, TcpStream}, + Event, Evented, Events, Poll, PollOpt, Ready, Token, +}; +pub use std::{ + collections::{hash_map::Entry, BTreeMap, HashMap, HashSet}, + convert::TryInto, + net::SocketAddr, + sync::Arc, + time::Instant, +}; +pub use Polarity::*; + +///////////////////// DEFS ///////////////////// + +pub type Payload = Vec; +pub type ControllerId = u32; +pub type ChannelIndex = u32; + +/// This is a unique identifier for a channel (i.e., port). +#[derive(Debug, Eq, PartialEq, Clone, Hash, Copy, Ord, PartialOrd)] +pub struct ChannelId { + pub(crate) controller_id: ControllerId, + pub(crate) channel_index: ChannelIndex, +} + +#[derive(Debug, Eq, PartialEq, Clone, Hash, Copy, Ord, PartialOrd)] +pub enum Polarity { + Putter, // output port (from the perspective of the component) + Getter, // input port (from the perspective of the component) +} + +#[derive(Eq, PartialEq, Ord, PartialOrd, Hash, Copy, Clone, Debug)] +pub struct Key(u64); + +pub trait ProtocolDescription: Sized { + type S: ComponentState; + + fn parse(pdl: &[u8]) -> Result; + fn main_interface_polarities(&self) -> Vec; + fn new_main_component(&self, interface: &[Key]) -> Self::S; +} + +pub trait ComponentState: Sized + Clone { + type D: ProtocolDescription; + fn pre_sync_run>( + &mut self, + runtime_ctx: &mut C, + protocol_description: &Self::D, + ) -> MonoBlocker; + + fn sync_run>( + &mut self, + runtime_ctx: &mut C, + protocol_description: &Self::D, + ) -> PolyBlocker; +} + +#[derive(Debug, Clone)] +pub enum MonoBlocker { + Inconsistent, + ComponentExit, + SyncBlockStart, +} + +#[derive(Debug, Clone)] +pub enum PolyBlocker { + Inconsistent, + SyncBlockEnd, + CouldntReadMsg(Key), + CouldntCheckFiring(Key), + PutMsg(Key, Payload), +} + +pub trait MonoContext { + type D: ProtocolDescription; + type S: ComponentState; + + fn new_component(&mut self, moved_keys: HashSet, init_state: Self::S); + fn new_channel(&mut self) -> [Key; 2]; + fn new_random(&self) -> u64; +} +pub trait PolyContext { + type D: ProtocolDescription; + + fn is_firing(&self, ekey: Key) -> Option; + fn read_msg(&self, ekey: Key) -> Option<&Payload>; +} + +///////////////////// IMPL ///////////////////// +impl Key { + pub fn from_raw(raw: u64) -> Self { + Self(raw) + } + pub fn to_raw(self) -> u64 { + self.0 + } + pub fn to_token(self) -> mio::Token { + mio::Token(self.0.try_into().unwrap()) + } + pub fn from_token(t: mio::Token) -> Self { + Self(t.0.try_into().unwrap()) + } +} diff --git a/src/lib.rs b/src/lib.rs new file mode 100644 index 0000000000000000000000000000000000000000..9fd8ad6a559a2a554ffa7fb2b5af208c720b6b21 --- /dev/null +++ b/src/lib.rs @@ -0,0 +1,14 @@ +#[macro_use] +mod macros; + +mod common; // common to both +mod protocol; // hans' stuff +mod runtime; // chris' stuff + +#[cfg(test)] +mod test; + +pub use runtime::{errors, Connector, PortBinding}; + +#[cfg(feature = "ffi")] +pub use runtime::ffi; diff --git a/src/macros.rs b/src/macros.rs new file mode 100644 index 0000000000000000000000000000000000000000..7935e81f341f844fe743ee3467a37ddc51b97033 --- /dev/null +++ b/src/macros.rs @@ -0,0 +1,28 @@ +macro_rules! assert_let { + ($pat:pat = $expr:expr => $work:expr) => { + if let $pat = $expr { + $work + } else { + panic!("assert_let failed"); + } + }; +} + +#[test] +fn assert_let() { + let x = Some(5); + let z = assert_let![Some(y) = x => { + println!("{:?}", y); + 3 + }]; + println!("{:?}", z); +} + +#[test] +#[should_panic] +fn must_let_panic() { + let x: Option = None; + assert_let![Some(y) = x => { + println!("{:?}", y); + }]; +} diff --git a/src/protocol/ast.rs b/src/protocol/ast.rs new file mode 100644 index 0000000000000000000000000000000000000000..9df653dede77fb9515afcc835ff18a68c61548f4 --- /dev/null +++ b/src/protocol/ast.rs @@ -0,0 +1,2876 @@ +use std::fmt; +use std::fmt::{Debug, Display, Formatter}; +use std::ops::{Index, IndexMut}; + +use id_arena::{Arena, Id}; + +use crate::protocol::inputsource::*; + +#[derive(Debug, Clone, Copy, PartialEq)] +pub struct RootId(Id); + +#[derive(Debug, Clone, Copy, PartialEq)] +pub struct PragmaId(Id); + +#[derive(Debug, Clone, Copy, PartialEq)] +pub struct ImportId(Id); + +#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)] +pub struct IdentifierId(Id); + +#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)] +pub struct SourceIdentifierId(IdentifierId); + +impl SourceIdentifierId { + pub fn upcast(self) -> IdentifierId { + self.0 + } +} + +#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)] +pub struct ExternalIdentifierId(IdentifierId); + +impl ExternalIdentifierId { + pub fn upcast(self) -> IdentifierId { + self.0 + } +} + +#[derive(Debug, Clone, Copy, PartialEq)] +pub struct TypeAnnotationId(Id); + +#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)] +pub struct VariableId(Id); + +#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)] +pub struct ParameterId(VariableId); + +impl ParameterId { + pub fn upcast(self) -> VariableId { + self.0 + } +} + +#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)] +pub struct LocalId(VariableId); + +impl LocalId { + pub fn upcast(self) -> VariableId { + self.0 + } +} + +#[derive(Debug, Clone, Copy, PartialEq)] +pub struct DefinitionId(Id); + +#[derive(Debug, Clone, Copy, PartialEq)] +pub struct ComponentId(DefinitionId); + +impl ComponentId { + pub fn upcast(self) -> DefinitionId { + self.0 + } +} + +#[derive(Debug, Clone, Copy, PartialEq)] +pub struct FunctionId(DefinitionId); + +impl FunctionId { + pub fn upcast(self) -> DefinitionId { + self.0 + } +} + +#[derive(Debug, Clone, Copy, PartialEq)] +pub struct CompositeId(ComponentId); + +impl CompositeId { + pub fn upcast(self) -> ComponentId { + self.0 + } +} + +#[derive(Debug, Clone, Copy, PartialEq)] +pub struct PrimitiveId(ComponentId); + +impl PrimitiveId { + pub fn upcast(self) -> ComponentId { + self.0 + } +} + +#[derive(Debug, Clone, Copy, PartialEq)] +pub struct StatementId(Id); + +#[derive(Debug, Clone, Copy, PartialEq)] +pub struct BlockStatementId(StatementId); + +impl BlockStatementId { + pub fn upcast(self) -> StatementId { + self.0 + } +} + +#[derive(Debug, Clone, Copy, PartialEq)] +pub struct LocalStatementId(StatementId); + +impl LocalStatementId { + pub fn upcast(self) -> StatementId { + self.0 + } +} + +#[derive(Debug, Clone, Copy, PartialEq)] +pub struct MemoryStatementId(LocalStatementId); + +impl MemoryStatementId { + pub fn upcast(self) -> LocalStatementId { + self.0 + } +} + +#[derive(Debug, Clone, Copy, PartialEq)] +pub struct ChannelStatementId(LocalStatementId); + +impl ChannelStatementId { + pub fn upcast(self) -> LocalStatementId { + self.0 + } +} + +#[derive(Debug, Clone, Copy, PartialEq)] +pub struct SkipStatementId(StatementId); + +impl SkipStatementId { + pub fn upcast(self) -> StatementId { + self.0 + } +} + +#[derive(Debug, Clone, Copy, PartialEq)] +pub struct LabeledStatementId(StatementId); + +impl LabeledStatementId { + pub fn upcast(self) -> StatementId { + self.0 + } +} + +#[derive(Debug, Clone, Copy, PartialEq)] +pub struct IfStatementId(StatementId); + +impl IfStatementId { + pub fn upcast(self) -> StatementId { + self.0 + } +} + +#[derive(Debug, Clone, Copy, PartialEq)] +pub struct EndIfStatementId(StatementId); + +impl EndIfStatementId { + pub fn upcast(self) -> StatementId { + self.0 + } +} + +#[derive(Debug, Clone, Copy, PartialEq)] +pub struct WhileStatementId(StatementId); + +impl WhileStatementId { + pub fn upcast(self) -> StatementId { + self.0 + } +} + +#[derive(Debug, Clone, Copy, PartialEq)] +pub struct EndWhileStatementId(StatementId); + +impl EndWhileStatementId { + pub fn upcast(self) -> StatementId { + self.0 + } +} + +#[derive(Debug, Clone, Copy, PartialEq)] +pub struct BreakStatementId(StatementId); + +impl BreakStatementId { + pub fn upcast(self) -> StatementId { + self.0 + } +} + +#[derive(Debug, Clone, Copy, PartialEq)] +pub struct ContinueStatementId(StatementId); + +impl ContinueStatementId { + pub fn upcast(self) -> StatementId { + self.0 + } +} + +#[derive(Debug, Clone, Copy, PartialEq)] +pub struct SynchronousStatementId(StatementId); + +impl SynchronousStatementId { + pub fn upcast(self) -> StatementId { + self.0 + } +} + +#[derive(Debug, Clone, Copy, PartialEq)] +pub struct EndSynchronousStatementId(StatementId); + +impl EndSynchronousStatementId { + pub fn upcast(self) -> StatementId { + self.0 + } +} + +#[derive(Debug, Clone, Copy, PartialEq)] +pub struct ReturnStatementId(StatementId); + +impl ReturnStatementId { + pub fn upcast(self) -> StatementId { + self.0 + } +} + +#[derive(Debug, Clone, Copy, PartialEq)] +pub struct AssertStatementId(StatementId); + +impl AssertStatementId { + pub fn upcast(self) -> StatementId { + self.0 + } +} + +#[derive(Debug, Clone, Copy, PartialEq)] +pub struct GotoStatementId(StatementId); + +impl GotoStatementId { + pub fn upcast(self) -> StatementId { + self.0 + } +} + +#[derive(Debug, Clone, Copy, PartialEq)] +pub struct NewStatementId(StatementId); + +impl NewStatementId { + pub fn upcast(self) -> StatementId { + self.0 + } +} + +#[derive(Debug, Clone, Copy, PartialEq)] +pub struct PutStatementId(StatementId); + +impl PutStatementId { + pub fn upcast(self) -> StatementId { + self.0 + } +} + +#[derive(Debug, Clone, Copy, PartialEq)] +pub struct ExpressionStatementId(StatementId); + +impl ExpressionStatementId { + pub fn upcast(self) -> StatementId { + self.0 + } +} + +#[derive(Debug, Clone, Copy, PartialEq)] +pub struct ExpressionId(Id); + +#[derive(Debug, Clone, Copy)] +pub struct AssignmentExpressionId(ExpressionId); + +impl AssignmentExpressionId { + pub fn upcast(self) -> ExpressionId { + self.0 + } +} + +#[derive(Debug, Clone, Copy, PartialEq)] +pub struct ConditionalExpressionId(ExpressionId); + +impl ConditionalExpressionId { + pub fn upcast(self) -> ExpressionId { + self.0 + } +} + +#[derive(Debug, Clone, Copy, PartialEq)] +pub struct BinaryExpressionId(ExpressionId); + +impl BinaryExpressionId { + pub fn upcast(self) -> ExpressionId { + self.0 + } +} + +#[derive(Debug, Clone, Copy, PartialEq)] +pub struct UnaryExpressionId(ExpressionId); + +impl UnaryExpressionId { + pub fn upcast(self) -> ExpressionId { + self.0 + } +} + +#[derive(Debug, Clone, Copy, PartialEq)] +pub struct IndexingExpressionId(ExpressionId); + +impl IndexingExpressionId { + pub fn upcast(self) -> ExpressionId { + self.0 + } +} + +#[derive(Debug, Clone, Copy, PartialEq)] +pub struct SlicingExpressionId(ExpressionId); + +impl SlicingExpressionId { + pub fn upcast(self) -> ExpressionId { + self.0 + } +} + +#[derive(Debug, Clone, Copy, PartialEq)] +pub struct SelectExpressionId(ExpressionId); + +impl SelectExpressionId { + pub fn upcast(self) -> ExpressionId { + self.0 + } +} + +#[derive(Debug, Clone, Copy, PartialEq)] +pub struct ArrayExpressionId(ExpressionId); + +impl ArrayExpressionId { + pub fn upcast(self) -> ExpressionId { + self.0 + } +} + +#[derive(Debug, Clone, Copy, PartialEq)] +pub struct ConstantExpressionId(ExpressionId); + +impl ConstantExpressionId { + pub fn upcast(self) -> ExpressionId { + self.0 + } +} + +#[derive(Debug, Clone, Copy, PartialEq)] +pub struct CallExpressionId(ExpressionId); + +impl CallExpressionId { + pub fn upcast(self) -> ExpressionId { + self.0 + } +} + +#[derive(Debug, Clone, Copy, PartialEq)] +pub struct VariableExpressionId(ExpressionId); + +impl VariableExpressionId { + pub fn upcast(self) -> ExpressionId { + self.0 + } +} + +#[derive(Debug, Clone, Copy, PartialEq)] +pub struct DeclarationId(Id); + +#[derive(Debug, Clone, Copy, PartialEq)] +pub struct DefinedDeclarationId(DeclarationId); + +impl DefinedDeclarationId { + pub fn upcast(self) -> DeclarationId { + self.0 + } +} + +#[derive(Debug, Clone, Copy, PartialEq)] +pub struct ImportedDeclarationId(DeclarationId); + +impl ImportedDeclarationId { + pub fn upcast(self) -> DeclarationId { + self.0 + } +} + +pub struct Heap { + // Phase 0: allocation + protocol_descriptions: Arena, + pragmas: Arena, + imports: Arena, + identifiers: Arena, + type_annotations: Arena, + variables: Arena, + definitions: Arena, + statements: Arena, + expressions: Arena, + declarations: Arena, +} + +impl Heap { + pub fn new() -> Heap { + Heap { + protocol_descriptions: Arena::new(), + pragmas: Arena::new(), + imports: Arena::new(), + identifiers: Arena::new(), + type_annotations: Arena::new(), + variables: Arena::new(), + definitions: Arena::new(), + statements: Arena::new(), + expressions: Arena::new(), + declarations: Arena::new(), + } + } + pub fn alloc_source_identifier( + &mut self, + f: impl FnOnce(SourceIdentifierId) -> SourceIdentifier, + ) -> SourceIdentifierId { + SourceIdentifierId(IdentifierId( + self.identifiers + .alloc_with_id(|id| Identifier::Source(f(SourceIdentifierId(IdentifierId(id))))), + )) + } + pub fn alloc_external_identifier( + &mut self, + f: impl FnOnce(ExternalIdentifierId) -> ExternalIdentifier, + ) -> ExternalIdentifierId { + ExternalIdentifierId(IdentifierId( + self.identifiers.alloc_with_id(|id| { + Identifier::External(f(ExternalIdentifierId(IdentifierId(id)))) + }), + )) + } + pub fn alloc_type_annotation( + &mut self, + f: impl FnOnce(TypeAnnotationId) -> TypeAnnotation, + ) -> TypeAnnotationId { + TypeAnnotationId(self.type_annotations.alloc_with_id(|id| f(TypeAnnotationId(id)))) + } + pub fn alloc_parameter(&mut self, f: impl FnOnce(ParameterId) -> Parameter) -> ParameterId { + ParameterId(VariableId( + self.variables.alloc_with_id(|id| Variable::Parameter(f(ParameterId(VariableId(id))))), + )) + } + pub fn alloc_local(&mut self, f: impl FnOnce(LocalId) -> Local) -> LocalId { + LocalId(VariableId( + self.variables.alloc_with_id(|id| Variable::Local(f(LocalId(VariableId(id))))), + )) + } + pub fn alloc_assignment_expression( + &mut self, + f: impl FnOnce(AssignmentExpressionId) -> AssignmentExpression, + ) -> AssignmentExpressionId { + AssignmentExpressionId(ExpressionId(self.expressions.alloc_with_id(|id| { + Expression::Assignment(f(AssignmentExpressionId(ExpressionId(id)))) + }))) + } + pub fn alloc_conditional_expression( + &mut self, + f: impl FnOnce(ConditionalExpressionId) -> ConditionalExpression, + ) -> ConditionalExpressionId { + ConditionalExpressionId(ExpressionId(self.expressions.alloc_with_id(|id| { + Expression::Conditional(f(ConditionalExpressionId(ExpressionId(id)))) + }))) + } + pub fn alloc_binary_expression( + &mut self, + f: impl FnOnce(BinaryExpressionId) -> BinaryExpression, + ) -> BinaryExpressionId { + BinaryExpressionId(ExpressionId( + self.expressions + .alloc_with_id(|id| Expression::Binary(f(BinaryExpressionId(ExpressionId(id))))), + )) + } + pub fn alloc_unary_expression( + &mut self, + f: impl FnOnce(UnaryExpressionId) -> UnaryExpression, + ) -> UnaryExpressionId { + UnaryExpressionId(ExpressionId( + self.expressions + .alloc_with_id(|id| Expression::Unary(f(UnaryExpressionId(ExpressionId(id))))), + )) + } + pub fn alloc_slicing_expression( + &mut self, + f: impl FnOnce(SlicingExpressionId) -> SlicingExpression, + ) -> SlicingExpressionId { + SlicingExpressionId(ExpressionId( + self.expressions + .alloc_with_id(|id| Expression::Slicing(f(SlicingExpressionId(ExpressionId(id))))), + )) + } + pub fn alloc_indexing_expression( + &mut self, + f: impl FnOnce(IndexingExpressionId) -> IndexingExpression, + ) -> IndexingExpressionId { + IndexingExpressionId(ExpressionId( + self.expressions.alloc_with_id(|id| { + Expression::Indexing(f(IndexingExpressionId(ExpressionId(id)))) + }), + )) + } + pub fn alloc_select_expression( + &mut self, + f: impl FnOnce(SelectExpressionId) -> SelectExpression, + ) -> SelectExpressionId { + SelectExpressionId(ExpressionId( + self.expressions + .alloc_with_id(|id| Expression::Select(f(SelectExpressionId(ExpressionId(id))))), + )) + } + pub fn alloc_array_expression( + &mut self, + f: impl FnOnce(ArrayExpressionId) -> ArrayExpression, + ) -> ArrayExpressionId { + ArrayExpressionId(ExpressionId( + self.expressions + .alloc_with_id(|id| Expression::Array(f(ArrayExpressionId(ExpressionId(id))))), + )) + } + pub fn alloc_constant_expression( + &mut self, + f: impl FnOnce(ConstantExpressionId) -> ConstantExpression, + ) -> ConstantExpressionId { + ConstantExpressionId(ExpressionId( + self.expressions.alloc_with_id(|id| { + Expression::Constant(f(ConstantExpressionId(ExpressionId(id)))) + }), + )) + } + pub fn alloc_call_expression( + &mut self, + f: impl FnOnce(CallExpressionId) -> CallExpression, + ) -> CallExpressionId { + CallExpressionId(ExpressionId( + self.expressions + .alloc_with_id(|id| Expression::Call(f(CallExpressionId(ExpressionId(id))))), + )) + } + pub fn alloc_variable_expression( + &mut self, + f: impl FnOnce(VariableExpressionId) -> VariableExpression, + ) -> VariableExpressionId { + VariableExpressionId(ExpressionId( + self.expressions.alloc_with_id(|id| { + Expression::Variable(f(VariableExpressionId(ExpressionId(id)))) + }), + )) + } + pub fn alloc_block_statement( + &mut self, + f: impl FnOnce(BlockStatementId) -> BlockStatement, + ) -> BlockStatementId { + BlockStatementId(StatementId( + self.statements + .alloc_with_id(|id| Statement::Block(f(BlockStatementId(StatementId(id))))), + )) + } + pub fn alloc_memory_statement( + &mut self, + f: impl FnOnce(MemoryStatementId) -> MemoryStatement, + ) -> MemoryStatementId { + MemoryStatementId(LocalStatementId(StatementId(self.statements.alloc_with_id(|id| { + Statement::Local(LocalStatement::Memory(f(MemoryStatementId(LocalStatementId( + StatementId(id), + ))))) + })))) + } + pub fn alloc_channel_statement( + &mut self, + f: impl FnOnce(ChannelStatementId) -> ChannelStatement, + ) -> ChannelStatementId { + ChannelStatementId(LocalStatementId(StatementId(self.statements.alloc_with_id(|id| { + Statement::Local(LocalStatement::Channel(f(ChannelStatementId(LocalStatementId( + StatementId(id), + ))))) + })))) + } + pub fn alloc_skip_statement( + &mut self, + f: impl FnOnce(SkipStatementId) -> SkipStatement, + ) -> SkipStatementId { + SkipStatementId(StatementId( + self.statements + .alloc_with_id(|id| Statement::Skip(f(SkipStatementId(StatementId(id))))), + )) + } + pub fn alloc_if_statement( + &mut self, + f: impl FnOnce(IfStatementId) -> IfStatement, + ) -> IfStatementId { + IfStatementId(StatementId( + self.statements.alloc_with_id(|id| Statement::If(f(IfStatementId(StatementId(id))))), + )) + } + pub fn alloc_end_if_statement( + &mut self, + f: impl FnOnce(EndIfStatementId) -> EndIfStatement, + ) -> EndIfStatementId { + EndIfStatementId(StatementId( + self.statements + .alloc_with_id(|id| Statement::EndIf(f(EndIfStatementId(StatementId(id))))), + )) + } + pub fn alloc_while_statement( + &mut self, + f: impl FnOnce(WhileStatementId) -> WhileStatement, + ) -> WhileStatementId { + WhileStatementId(StatementId( + self.statements + .alloc_with_id(|id| Statement::While(f(WhileStatementId(StatementId(id))))), + )) + } + pub fn alloc_end_while_statement( + &mut self, + f: impl FnOnce(EndWhileStatementId) -> EndWhileStatement, + ) -> EndWhileStatementId { + EndWhileStatementId(StatementId( + self.statements + .alloc_with_id(|id| Statement::EndWhile(f(EndWhileStatementId(StatementId(id))))), + )) + } + pub fn alloc_break_statement( + &mut self, + f: impl FnOnce(BreakStatementId) -> BreakStatement, + ) -> BreakStatementId { + BreakStatementId(StatementId( + self.statements + .alloc_with_id(|id| Statement::Break(f(BreakStatementId(StatementId(id))))), + )) + } + pub fn alloc_continue_statement( + &mut self, + f: impl FnOnce(ContinueStatementId) -> ContinueStatement, + ) -> ContinueStatementId { + ContinueStatementId(StatementId( + self.statements + .alloc_with_id(|id| Statement::Continue(f(ContinueStatementId(StatementId(id))))), + )) + } + pub fn alloc_synchronous_statement( + &mut self, + f: impl FnOnce(SynchronousStatementId) -> SynchronousStatement, + ) -> SynchronousStatementId { + SynchronousStatementId(StatementId(self.statements.alloc_with_id(|id| { + Statement::Synchronous(f(SynchronousStatementId(StatementId(id)))) + }))) + } + pub fn alloc_end_synchronous_statement( + &mut self, + f: impl FnOnce(EndSynchronousStatementId) -> EndSynchronousStatement, + ) -> EndSynchronousStatementId { + EndSynchronousStatementId(StatementId(self.statements.alloc_with_id(|id| { + Statement::EndSynchronous(f(EndSynchronousStatementId(StatementId(id)))) + }))) + } + pub fn alloc_return_statement( + &mut self, + f: impl FnOnce(ReturnStatementId) -> ReturnStatement, + ) -> ReturnStatementId { + ReturnStatementId(StatementId( + self.statements + .alloc_with_id(|id| Statement::Return(f(ReturnStatementId(StatementId(id))))), + )) + } + pub fn alloc_assert_statement( + &mut self, + f: impl FnOnce(AssertStatementId) -> AssertStatement, + ) -> AssertStatementId { + AssertStatementId(StatementId( + self.statements + .alloc_with_id(|id| Statement::Assert(f(AssertStatementId(StatementId(id))))), + )) + } + pub fn alloc_goto_statement( + &mut self, + f: impl FnOnce(GotoStatementId) -> GotoStatement, + ) -> GotoStatementId { + GotoStatementId(StatementId( + self.statements + .alloc_with_id(|id| Statement::Goto(f(GotoStatementId(StatementId(id))))), + )) + } + pub fn alloc_new_statement( + &mut self, + f: impl FnOnce(NewStatementId) -> NewStatement, + ) -> NewStatementId { + NewStatementId(StatementId( + self.statements.alloc_with_id(|id| Statement::New(f(NewStatementId(StatementId(id))))), + )) + } + pub fn alloc_put_statement( + &mut self, + f: impl FnOnce(PutStatementId) -> PutStatement, + ) -> PutStatementId { + PutStatementId(StatementId( + self.statements.alloc_with_id(|id| Statement::Put(f(PutStatementId(StatementId(id))))), + )) + } + pub fn alloc_labeled_statement( + &mut self, + f: impl FnOnce(LabeledStatementId) -> LabeledStatement, + ) -> LabeledStatementId { + LabeledStatementId(StatementId( + self.statements + .alloc_with_id(|id| Statement::Labeled(f(LabeledStatementId(StatementId(id))))), + )) + } + pub fn alloc_expression_statement( + &mut self, + f: impl FnOnce(ExpressionStatementId) -> ExpressionStatement, + ) -> ExpressionStatementId { + ExpressionStatementId(StatementId( + self.statements.alloc_with_id(|id| { + Statement::Expression(f(ExpressionStatementId(StatementId(id)))) + }), + )) + } + pub fn alloc_composite(&mut self, f: impl FnOnce(CompositeId) -> Composite) -> CompositeId { + CompositeId(ComponentId(DefinitionId(self.definitions.alloc_with_id(|id| { + Definition::Component(Component::Composite(f(CompositeId(ComponentId(DefinitionId( + id, + )))))) + })))) + } + pub fn alloc_primitive(&mut self, f: impl FnOnce(PrimitiveId) -> Primitive) -> PrimitiveId { + PrimitiveId(ComponentId(DefinitionId(self.definitions.alloc_with_id(|id| { + Definition::Component(Component::Primitive(f(PrimitiveId(ComponentId(DefinitionId( + id, + )))))) + })))) + } + pub fn alloc_function(&mut self, f: impl FnOnce(FunctionId) -> Function) -> FunctionId { + FunctionId(DefinitionId( + self.definitions + .alloc_with_id(|id| Definition::Function(f(FunctionId(DefinitionId(id))))), + )) + } + pub fn alloc_pragma(&mut self, f: impl FnOnce(PragmaId) -> Pragma) -> PragmaId { + PragmaId(self.pragmas.alloc_with_id(|id| f(PragmaId(id)))) + } + pub fn alloc_import(&mut self, f: impl FnOnce(ImportId) -> Import) -> ImportId { + ImportId(self.imports.alloc_with_id(|id| f(ImportId(id)))) + } + pub fn alloc_protocol_description( + &mut self, + f: impl FnOnce(RootId) -> Root, + ) -> RootId { + RootId( + self.protocol_descriptions.alloc_with_id(|id| f(RootId(id))), + ) + } + pub fn alloc_imported_declaration( + &mut self, + f: impl FnOnce(ImportedDeclarationId) -> ImportedDeclaration, + ) -> ImportedDeclarationId { + ImportedDeclarationId(DeclarationId(self.declarations.alloc_with_id(|id| { + Declaration::Imported(f(ImportedDeclarationId(DeclarationId(id)))) + }))) + } + pub fn alloc_defined_declaration( + &mut self, + f: impl FnOnce(DefinedDeclarationId) -> DefinedDeclaration, + ) -> DefinedDeclarationId { + DefinedDeclarationId(DeclarationId( + self.declarations.alloc_with_id(|id| { + Declaration::Defined(f(DefinedDeclarationId(DeclarationId(id)))) + }), + )) + } + + pub fn get_external_identifier(&mut self, ident: &[u8]) -> ExternalIdentifierId { + for (_, id) in self.identifiers.iter() { + if id.is_external() && id.ident() == ident { + return id.as_external().this; + } + } + // Not found + self.alloc_external_identifier(|this| ExternalIdentifier { this, value: ident.to_vec() }) + } +} + +impl Index for Heap { + type Output = Root; + fn index(&self, index: RootId) -> &Self::Output { + &self.protocol_descriptions[index.0] + } +} + +impl IndexMut for Heap { + fn index_mut(&mut self, index: RootId) -> &mut Self::Output { + &mut self.protocol_descriptions[index.0] + } +} + +impl Index for Heap { + type Output = Pragma; + fn index(&self, index: PragmaId) -> &Self::Output { + &self.pragmas[index.0] + } +} + +impl Index for Heap { + type Output = Import; + fn index(&self, index: ImportId) -> &Self::Output { + &self.imports[index.0] + } +} + +impl Index for Heap { + type Output = Identifier; + fn index(&self, index: IdentifierId) -> &Self::Output { + &self.identifiers[index.0] + } +} + +impl Index for Heap { + type Output = SourceIdentifier; + fn index(&self, index: SourceIdentifierId) -> &Self::Output { + &self.identifiers[(index.0).0].as_source() + } +} + +impl Index for Heap { + type Output = ExternalIdentifier; + fn index(&self, index: ExternalIdentifierId) -> &Self::Output { + &self.identifiers[(index.0).0].as_external() + } +} + +impl Index for Heap { + type Output = TypeAnnotation; + fn index(&self, index: TypeAnnotationId) -> &Self::Output { + &self.type_annotations[index.0] + } +} + +impl Index for Heap { + type Output = Variable; + fn index(&self, index: VariableId) -> &Self::Output { + &self.variables[index.0] + } +} + +impl Index for Heap { + type Output = Parameter; + fn index(&self, index: ParameterId) -> &Self::Output { + &self.variables[(index.0).0].as_parameter() + } +} + +impl Index for Heap { + type Output = Local; + fn index(&self, index: LocalId) -> &Self::Output { + &self.variables[(index.0).0].as_local() + } +} + +impl Index for Heap { + type Output = Definition; + fn index(&self, index: DefinitionId) -> &Self::Output { + &self.definitions[index.0] + } +} + +impl Index for Heap { + type Output = Component; + fn index(&self, index: ComponentId) -> &Self::Output { + &self.definitions[(index.0).0].as_component() + } +} + +impl Index for Heap { + type Output = Function; + fn index(&self, index: FunctionId) -> &Self::Output { + &self.definitions[(index.0).0].as_function() + } +} + +impl Index for Heap { + type Output = Composite; + fn index(&self, index: CompositeId) -> &Self::Output { + &self.definitions[((index.0).0).0].as_composite() + } +} + +impl Index for Heap { + type Output = Primitive; + fn index(&self, index: PrimitiveId) -> &Self::Output { + &self.definitions[((index.0).0).0].as_primitive() + } +} + +impl Index for Heap { + type Output = Statement; + fn index(&self, index: StatementId) -> &Self::Output { + &self.statements[index.0] + } +} + +impl IndexMut for Heap { + fn index_mut(&mut self, index: StatementId) -> &mut Self::Output { + &mut self.statements[index.0] + } +} + +impl Index for Heap { + type Output = BlockStatement; + fn index(&self, index: BlockStatementId) -> &Self::Output { + &self.statements[(index.0).0].as_block() + } +} + +impl IndexMut for Heap { + fn index_mut(&mut self, index: BlockStatementId) -> &mut Self::Output { + (&mut self.statements[(index.0).0]).as_block_mut() + } +} + +impl Index for Heap { + type Output = LocalStatement; + fn index(&self, index: LocalStatementId) -> &Self::Output { + &self.statements[(index.0).0].as_local() + } +} + +impl Index for Heap { + type Output = MemoryStatement; + fn index(&self, index: MemoryStatementId) -> &Self::Output { + &self.statements[((index.0).0).0].as_memory() + } +} + +impl Index for Heap { + type Output = ChannelStatement; + fn index(&self, index: ChannelStatementId) -> &Self::Output { + &self.statements[((index.0).0).0].as_channel() + } +} + +impl Index for Heap { + type Output = SkipStatement; + fn index(&self, index: SkipStatementId) -> &Self::Output { + &self.statements[(index.0).0].as_skip() + } +} + +impl Index for Heap { + type Output = LabeledStatement; + fn index(&self, index: LabeledStatementId) -> &Self::Output { + &self.statements[(index.0).0].as_labeled() + } +} + +impl IndexMut for Heap { + fn index_mut(&mut self, index: LabeledStatementId) -> &mut Self::Output { + (&mut self.statements[(index.0).0]).as_labeled_mut() + } +} + +impl Index for Heap { + type Output = IfStatement; + fn index(&self, index: IfStatementId) -> &Self::Output { + &self.statements[(index.0).0].as_if() + } +} + +impl Index for Heap { + type Output = EndIfStatement; + fn index(&self, index: EndIfStatementId) -> &Self::Output { + &self.statements[(index.0).0].as_end_if() + } +} + +impl Index for Heap { + type Output = WhileStatement; + fn index(&self, index: WhileStatementId) -> &Self::Output { + &self.statements[(index.0).0].as_while() + } +} + +impl IndexMut for Heap { + fn index_mut(&mut self, index: WhileStatementId) -> &mut Self::Output { + (&mut self.statements[(index.0).0]).as_while_mut() + } +} + +impl Index for Heap { + type Output = BreakStatement; + fn index(&self, index: BreakStatementId) -> &Self::Output { + &self.statements[(index.0).0].as_break() + } +} + +impl IndexMut for Heap { + fn index_mut(&mut self, index: BreakStatementId) -> &mut Self::Output { + (&mut self.statements[(index.0).0]).as_break_mut() + } +} + +impl Index for Heap { + type Output = ContinueStatement; + fn index(&self, index: ContinueStatementId) -> &Self::Output { + &self.statements[(index.0).0].as_continue() + } +} + +impl IndexMut for Heap { + fn index_mut(&mut self, index: ContinueStatementId) -> &mut Self::Output { + (&mut self.statements[(index.0).0]).as_continue_mut() + } +} + +impl Index for Heap { + type Output = SynchronousStatement; + fn index(&self, index: SynchronousStatementId) -> &Self::Output { + &self.statements[(index.0).0].as_synchronous() + } +} + +impl IndexMut for Heap { + fn index_mut(&mut self, index: SynchronousStatementId) -> &mut Self::Output { + (&mut self.statements[(index.0).0]).as_synchronous_mut() + } +} + +impl Index for Heap { + type Output = EndSynchronousStatement; + fn index(&self, index: EndSynchronousStatementId) -> &Self::Output { + &self.statements[(index.0).0].as_end_synchronous() + } +} + +impl Index for Heap { + type Output = ReturnStatement; + fn index(&self, index: ReturnStatementId) -> &Self::Output { + &self.statements[(index.0).0].as_return() + } +} + +impl Index for Heap { + type Output = AssertStatement; + fn index(&self, index: AssertStatementId) -> &Self::Output { + &self.statements[(index.0).0].as_assert() + } +} + +impl Index for Heap { + type Output = GotoStatement; + fn index(&self, index: GotoStatementId) -> &Self::Output { + &self.statements[(index.0).0].as_goto() + } +} + +impl IndexMut for Heap { + fn index_mut(&mut self, index: GotoStatementId) -> &mut Self::Output { + (&mut self.statements[(index.0).0]).as_goto_mut() + } +} + +impl Index for Heap { + type Output = NewStatement; + fn index(&self, index: NewStatementId) -> &Self::Output { + &self.statements[(index.0).0].as_new() + } +} + +impl Index for Heap { + type Output = PutStatement; + fn index(&self, index: PutStatementId) -> &Self::Output { + &self.statements[(index.0).0].as_put() + } +} + +impl Index for Heap { + type Output = ExpressionStatement; + fn index(&self, index: ExpressionStatementId) -> &Self::Output { + &self.statements[(index.0).0].as_expression() + } +} + +impl Index for Heap { + type Output = Expression; + fn index(&self, index: ExpressionId) -> &Self::Output { + &self.expressions[index.0] + } +} + +impl Index for Heap { + type Output = AssignmentExpression; + fn index(&self, index: AssignmentExpressionId) -> &Self::Output { + &self.expressions[(index.0).0].as_assignment() + } +} + +impl Index for Heap { + type Output = ConditionalExpression; + fn index(&self, index: ConditionalExpressionId) -> &Self::Output { + &self.expressions[(index.0).0].as_conditional() + } +} + +impl Index for Heap { + type Output = BinaryExpression; + fn index(&self, index: BinaryExpressionId) -> &Self::Output { + &self.expressions[(index.0).0].as_binary() + } +} + +impl Index for Heap { + type Output = UnaryExpression; + fn index(&self, index: UnaryExpressionId) -> &Self::Output { + &self.expressions[(index.0).0].as_unary() + } +} + +impl Index for Heap { + type Output = IndexingExpression; + fn index(&self, index: IndexingExpressionId) -> &Self::Output { + &self.expressions[(index.0).0].as_indexing() + } +} + +impl Index for Heap { + type Output = SlicingExpression; + fn index(&self, index: SlicingExpressionId) -> &Self::Output { + &self.expressions[(index.0).0].as_slicing() + } +} + +impl Index for Heap { + type Output = SelectExpression; + fn index(&self, index: SelectExpressionId) -> &Self::Output { + &self.expressions[(index.0).0].as_select() + } +} + +impl Index for Heap { + type Output = ArrayExpression; + fn index(&self, index: ArrayExpressionId) -> &Self::Output { + &self.expressions[(index.0).0].as_array() + } +} + +impl Index for Heap { + type Output = ConstantExpression; + fn index(&self, index: ConstantExpressionId) -> &Self::Output { + &self.expressions[(index.0).0].as_constant() + } +} + +impl Index for Heap { + type Output = CallExpression; + fn index(&self, index: CallExpressionId) -> &Self::Output { + &self.expressions[(index.0).0].as_call() + } +} + +impl IndexMut for Heap { + fn index_mut(&mut self, index: CallExpressionId) -> &mut Self::Output { + (&mut self.expressions[(index.0).0]).as_call_mut() + } +} + +impl Index for Heap { + type Output = VariableExpression; + fn index(&self, index: VariableExpressionId) -> &Self::Output { + &self.expressions[(index.0).0].as_variable() + } +} + +impl IndexMut for Heap { + fn index_mut(&mut self, index: VariableExpressionId) -> &mut Self::Output { + (&mut self.expressions[(index.0).0]).as_variable_mut() + } +} + +impl Index for Heap { + type Output = Declaration; + fn index(&self, index: DeclarationId) -> &Self::Output { + &self.declarations[index.0] + } +} + +#[derive(Debug, Clone)] +pub struct Root { + pub this: RootId, + // Phase 1: parser + pub position: InputPosition, + pub pragmas: Vec, + pub imports: Vec, + pub definitions: Vec, + // Pase 2: linker + pub declarations: Vec, +} + +impl Root { + pub fn get_definition(&self, h: &Heap, id: IdentifierId) -> Option { + for &def in self.definitions.iter() { + if h[h[def].identifier()] == h[id] { + return Some(def); + } + } + None + } + pub fn get_declaration(&self, h: &Heap, id: IdentifierId) -> Option { + for &decl in self.declarations.iter() { + if h[h[decl].identifier()] == h[id] { + return Some(decl); + } + } + None + } +} + +impl SyntaxElement for Root { + fn position(&self) -> InputPosition { + self.position + } +} + +#[derive(Debug, Clone)] +pub struct Pragma { + pub this: PragmaId, + // Phase 1: parser + pub position: InputPosition, + pub value: Vec, +} + +impl SyntaxElement for Pragma { + fn position(&self) -> InputPosition { + self.position + } +} + +#[derive(Debug, Clone)] +pub struct Import { + pub this: ImportId, + // Phase 1: parser + pub position: InputPosition, + pub value: Vec, +} + +impl SyntaxElement for Import { + fn position(&self) -> InputPosition { + self.position + } +} + +#[derive(Debug, Clone)] +pub enum Identifier { + External(ExternalIdentifier), + Source(SourceIdentifier), +} + +impl Identifier { + pub fn as_source(&self) -> &SourceIdentifier { + match self { + Identifier::Source(result) => result, + _ => panic!("Unable to cast `Identifier` to `SourceIdentifier`"), + } + } + pub fn is_external(&self) -> bool { + match self { + Identifier::External(_) => true, + _ => false, + } + } + pub fn as_external(&self) -> &ExternalIdentifier { + match self { + Identifier::External(result) => result, + _ => panic!("Unable to cast `Identifier` to `ExternalIdentifier`"), + } + } + fn ident(&self) -> &[u8] { + match self { + Identifier::External(eid) => eid.ident(), + Identifier::Source(sid) => sid.ident(), + } + } +} + +impl Display for Identifier { + fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result { + // A source identifier is in ASCII range. + write!(f, "{}", String::from_utf8_lossy(self.ident())) + } +} + +impl PartialEq for Identifier { + fn eq(&self, rhs: &Identifier) -> bool { + self.ident() == rhs.ident() + } +} + +impl PartialEq for Identifier { + fn eq(&self, rhs: &SourceIdentifier) -> bool { + self.ident() == rhs.ident() + } +} + +#[derive(Debug, Clone)] +pub struct ExternalIdentifier { + pub this: ExternalIdentifierId, + // Phase 1: parser + pub value: Vec, +} + +impl ExternalIdentifier { + fn ident(&self) -> &[u8] { + &self.value + } +} + +impl Display for ExternalIdentifier { + fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result { + // A source identifier is in ASCII range. + write!(f, "{}", String::from_utf8_lossy(&self.value)) + } +} + +#[derive(Debug, Clone)] +pub struct SourceIdentifier { + pub this: SourceIdentifierId, + // Phase 1: parser + pub position: InputPosition, + pub value: Vec, +} + +impl SourceIdentifier { + fn ident(&self) -> &[u8] { + &self.value + } +} + +impl SyntaxElement for SourceIdentifier { + fn position(&self) -> InputPosition { + self.position + } +} + +impl Display for SourceIdentifier { + fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result { + // A source identifier is in ASCII range. + write!(f, "{}", String::from_utf8_lossy(&self.value)) + } +} + +impl PartialEq for SourceIdentifier { + fn eq(&self, rhs: &Identifier) -> bool { + self.ident() == rhs.ident() + } +} + +impl PartialEq for SourceIdentifier { + fn eq(&self, rhs: &SourceIdentifier) -> bool { + self.ident() == rhs.ident() + } +} + +type TypeData = Vec; + +#[derive(Debug, Clone, PartialEq, Eq)] +pub enum PrimitiveType { + Input, + Output, + Message, + Boolean, + Byte, + Short, + Int, + Long, + Symbolic(TypeData), +} + +#[derive(Debug, Clone, PartialEq, Eq)] +pub struct Type { + pub primitive: PrimitiveType, + pub array: bool, +} + +#[allow(dead_code)] +impl Type { + pub const INPUT: Type = Type { primitive: PrimitiveType::Input, array: false }; + pub const OUTPUT: Type = Type { primitive: PrimitiveType::Output, array: false }; + pub const MESSAGE: Type = Type { primitive: PrimitiveType::Message, array: false }; + pub const BOOLEAN: Type = Type { primitive: PrimitiveType::Boolean, array: false }; + pub const BYTE: Type = Type { primitive: PrimitiveType::Byte, array: false }; + pub const SHORT: Type = Type { primitive: PrimitiveType::Short, array: false }; + pub const INT: Type = Type { primitive: PrimitiveType::Int, array: false }; + pub const LONG: Type = Type { primitive: PrimitiveType::Long, array: false }; + + pub const INPUT_ARRAY: Type = Type { primitive: PrimitiveType::Input, array: true }; + pub const OUTPUT_ARRAY: Type = Type { primitive: PrimitiveType::Output, array: true }; + pub const MESSAGE_ARRAY: Type = Type { primitive: PrimitiveType::Message, array: true }; + pub const BOOLEAN_ARRAY: Type = Type { primitive: PrimitiveType::Boolean, array: true }; + pub const BYTE_ARRAY: Type = Type { primitive: PrimitiveType::Byte, array: true }; + pub const SHORT_ARRAY: Type = Type { primitive: PrimitiveType::Short, array: true }; + pub const INT_ARRAY: Type = Type { primitive: PrimitiveType::Int, array: true }; + pub const LONG_ARRAY: Type = Type { primitive: PrimitiveType::Long, array: true }; +} + +impl Display for Type { + fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result { + match &self.primitive { + PrimitiveType::Input => { + write!(f, "in")?; + } + PrimitiveType::Output => { + write!(f, "out")?; + } + PrimitiveType::Message => { + write!(f, "msg")?; + } + PrimitiveType::Boolean => { + write!(f, "boolean")?; + } + PrimitiveType::Byte => { + write!(f, "byte")?; + } + PrimitiveType::Short => { + write!(f, "short")?; + } + PrimitiveType::Int => { + write!(f, "int")?; + } + PrimitiveType::Long => { + write!(f, "long")?; + } + PrimitiveType::Symbolic(data) => { + // Type data is in ASCII range. + write!(f, "{}", String::from_utf8_lossy(&data))?; + } + } + if self.array { + write!(f, "[]") + } else { + Ok(()) + } + } +} + +#[derive(Debug, Clone)] +pub struct TypeAnnotation { + pub this: TypeAnnotationId, + // Phase 1: parser + pub position: InputPosition, + pub the_type: Type, +} + +impl SyntaxElement for TypeAnnotation { + fn position(&self) -> InputPosition { + self.position + } +} + +type CharacterData = Vec; +type IntegerData = Vec; + +#[derive(Debug, Clone)] +pub enum Constant { + Null, // message + True, + False, + Character(CharacterData), + Integer(IntegerData), +} + +#[derive(Debug, Clone)] +pub enum Method { + Get, + Fires, + Create, + Symbolic(SourceIdentifierId), +} + +#[derive(Debug, Clone)] +pub enum Field { + Length, + Symbolic(SourceIdentifierId), +} +impl Field { + pub fn is_length(&self) -> bool { + match self { + Field::Length => true, + _ => false, + } + } +} + +#[derive(Debug, Clone, Copy)] +pub enum Scope { + Definition(DefinitionId), + Block(BlockStatementId), + Synchronous(SynchronousStatementId), +} + +impl Scope { + pub fn to_block(&self) -> BlockStatementId { + match &self { + Scope::Block(id) => *id, + _ => panic!("Unable to cast `Scope` to `BlockStatement`"), + } + } +} + +pub trait VariableScope { + fn parent_scope(&self, h: &Heap) -> Option; + fn get_variable(&self, h: &Heap, id: SourceIdentifierId) -> Option; +} + +impl VariableScope for Scope { + fn parent_scope(&self, h: &Heap) -> Option { + match self { + Scope::Definition(def) => h[*def].parent_scope(h), + Scope::Block(stmt) => h[*stmt].parent_scope(h), + Scope::Synchronous(stmt) => h[*stmt].parent_scope(h), + } + } + fn get_variable(&self, h: &Heap, id: SourceIdentifierId) -> Option { + match self { + Scope::Definition(def) => h[*def].get_variable(h, id), + Scope::Block(stmt) => h[*stmt].get_variable(h, id), + Scope::Synchronous(stmt) => h[*stmt].get_variable(h, id), + } + } +} + +#[derive(Debug, Clone)] +pub enum Variable { + Parameter(Parameter), + Local(Local), +} + +impl Variable { + pub fn identifier(&self) -> SourceIdentifierId { + match self { + Variable::Parameter(var) => var.identifier, + Variable::Local(var) => var.identifier, + } + } + pub fn is_parameter(&self) -> bool { + match self { + Variable::Parameter(_) => true, + _ => false, + } + } + pub fn as_parameter(&self) -> &Parameter { + match self { + Variable::Parameter(result) => result, + _ => panic!("Unable to cast `Variable` to `Parameter`"), + } + } + pub fn as_local(&self) -> &Local { + match self { + Variable::Local(result) => result, + _ => panic!("Unable to cast `Variable` to `Local`"), + } + } + pub fn the_type<'b>(&self, h: &'b Heap) -> &'b Type { + match self { + Variable::Parameter(param) => &h[param.type_annotation].the_type, + Variable::Local(local) => &h[local.type_annotation].the_type, + } + } +} + +impl SyntaxElement for Variable { + fn position(&self) -> InputPosition { + match self { + Variable::Parameter(decl) => decl.position(), + Variable::Local(decl) => decl.position(), + } + } +} + +#[derive(Debug, Clone)] +pub struct Parameter { + pub this: ParameterId, + // Phase 1: parser + pub position: InputPosition, + pub type_annotation: TypeAnnotationId, + pub identifier: SourceIdentifierId, +} + +impl SyntaxElement for Parameter { + fn position(&self) -> InputPosition { + self.position + } +} + +#[derive(Debug, Clone)] +pub struct Local { + pub this: LocalId, + // Phase 1: parser + pub position: InputPosition, + pub type_annotation: TypeAnnotationId, + pub identifier: SourceIdentifierId, +} +impl SyntaxElement for Local { + fn position(&self) -> InputPosition { + self.position + } +} + +#[derive(Debug, Clone)] +pub enum Definition { + Component(Component), + Function(Function), +} + +impl Definition { + pub fn is_component(&self) -> bool { + match self { + Definition::Component(_) => true, + _ => false, + } + } + pub fn as_component(&self) -> &Component { + match self { + Definition::Component(result) => result, + _ => panic!("Unable to cast `Definition` to `Component`"), + } + } + pub fn as_function(&self) -> &Function { + match self { + Definition::Function(result) => result, + _ => panic!("Unable to cast `Definition` to `Function`"), + } + } + pub fn as_composite(&self) -> &Composite { + self.as_component().as_composite() + } + pub fn as_primitive(&self) -> &Primitive { + self.as_component().as_primitive() + } + pub fn identifier(&self) -> SourceIdentifierId { + match self { + Definition::Component(com) => com.identifier(), + Definition::Function(fun) => fun.identifier, + } + } + pub fn parameters(&self) -> &Vec { + match self { + Definition::Component(com) => com.parameters(), + Definition::Function(fun) => &fun.parameters, + } + } + pub fn body(&self) -> StatementId { + match self { + Definition::Component(com) => com.body(), + Definition::Function(fun) => fun.body, + } + } +} + +impl SyntaxElement for Definition { + fn position(&self) -> InputPosition { + match self { + Definition::Component(def) => def.position(), + Definition::Function(def) => def.position(), + } + } +} + +impl VariableScope for Definition { + fn parent_scope(&self, _h: &Heap) -> Option { + None + } + fn get_variable(&self, h: &Heap, id: SourceIdentifierId) -> Option { + for ¶m in self.parameters().iter() { + if h[h[param].identifier] == h[id] { + return Some(param.0); + } + } + None + } +} + +#[derive(Debug, Clone)] +pub enum Component { + Composite(Composite), + Primitive(Primitive), +} + +impl Component { + pub fn this(&self) -> ComponentId { + match self { + Component::Composite(com) => com.this.upcast(), + Component::Primitive(prim) => prim.this.upcast(), + } + } + pub fn as_composite(&self) -> &Composite { + match self { + Component::Composite(result) => result, + _ => panic!("Unable to cast `Component` to `Composite`"), + } + } + pub fn as_primitive(&self) -> &Primitive { + match self { + Component::Primitive(result) => result, + _ => panic!("Unable to cast `Component` to `Primitive`"), + } + } + fn identifier(&self) -> SourceIdentifierId { + match self { + Component::Composite(com) => com.identifier, + Component::Primitive(prim) => prim.identifier, + } + } + pub fn parameters(&self) -> &Vec { + match self { + Component::Composite(com) => &com.parameters, + Component::Primitive(prim) => &prim.parameters, + } + } + pub fn body(&self) -> StatementId { + match self { + Component::Composite(com) => com.body, + Component::Primitive(prim) => prim.body, + } + } +} + +impl SyntaxElement for Component { + fn position(&self) -> InputPosition { + match self { + Component::Composite(def) => def.position(), + Component::Primitive(def) => def.position(), + } + } +} + +#[derive(Debug, Clone)] +pub struct Composite { + pub this: CompositeId, + // Phase 1: parser + pub position: InputPosition, + pub identifier: SourceIdentifierId, + pub parameters: Vec, + pub body: StatementId, +} + +impl SyntaxElement for Composite { + fn position(&self) -> InputPosition { + self.position + } +} + +#[derive(Debug, Clone)] +pub struct Primitive { + pub this: PrimitiveId, + // Phase 1: parser + pub position: InputPosition, + pub identifier: SourceIdentifierId, + pub parameters: Vec, + pub body: StatementId, +} + +impl SyntaxElement for Primitive { + fn position(&self) -> InputPosition { + self.position + } +} + +#[derive(Debug, Clone)] +pub struct Function { + pub this: FunctionId, + // Phase 1: parser + pub position: InputPosition, + pub return_type: TypeAnnotationId, + pub identifier: SourceIdentifierId, + pub parameters: Vec, + pub body: StatementId, +} + +impl SyntaxElement for Function { + fn position(&self) -> InputPosition { + self.position + } +} + +#[derive(Debug, Clone)] +pub enum Declaration { + Defined(DefinedDeclaration), + Imported(ImportedDeclaration), +} + +impl Declaration { + pub fn signature(&self) -> &Signature { + match self { + Declaration::Defined(decl) => &decl.signature, + Declaration::Imported(decl) => &decl.signature, + } + } + pub fn identifier(&self) -> IdentifierId { + self.signature().identifier() + } + pub fn is_component(&self) -> bool { + self.signature().is_component() + } + pub fn is_function(&self) -> bool { + self.signature().is_function() + } +} + +#[derive(Debug, Clone)] +pub struct DefinedDeclaration { + pub this: DefinedDeclarationId, + // Phase 2: linker + pub definition: DefinitionId, + pub signature: Signature, +} + +#[derive(Debug, Clone)] +pub struct ImportedDeclaration { + pub this: ImportedDeclarationId, + // Phase 2: linker + pub import: ImportId, + pub signature: Signature, +} + +#[derive(Debug, Clone)] +pub enum Signature { + Component(ComponentSignature), + Function(FunctionSignature), +} + +impl Signature { + pub fn from_definition(h: &Heap, def: DefinitionId) -> Signature { + match &h[def] { + Definition::Component(com) => Signature::Component(ComponentSignature { + identifier: com.identifier().0, + arity: Signature::convert_parameters(h, com.parameters()), + }), + Definition::Function(fun) => Signature::Function(FunctionSignature { + return_type: h[fun.return_type].the_type.clone(), + identifier: fun.identifier.0, + arity: Signature::convert_parameters(h, &fun.parameters), + }), + } + } + fn convert_parameters(h: &Heap, params: &Vec) -> Vec { + let mut result = Vec::new(); + for ¶m in params.iter() { + result.push(h[h[param].type_annotation].the_type.clone()); + } + result + } + fn identifier(&self) -> IdentifierId { + match self { + Signature::Component(com) => com.identifier, + Signature::Function(fun) => fun.identifier, + } + } + pub fn is_component(&self) -> bool { + match self { + Signature::Component(_) => true, + Signature::Function(_) => false, + } + } + pub fn is_function(&self) -> bool { + match self { + Signature::Component(_) => false, + Signature::Function(_) => true, + } + } +} + +#[derive(Debug, Clone)] +pub struct ComponentSignature { + pub identifier: IdentifierId, + pub arity: Vec, +} + +#[derive(Debug, Clone)] +pub struct FunctionSignature { + pub return_type: Type, + pub identifier: IdentifierId, + pub arity: Vec, +} + +#[derive(Debug, Clone)] +pub enum Statement { + Block(BlockStatement), + Local(LocalStatement), + Skip(SkipStatement), + Labeled(LabeledStatement), + If(IfStatement), + EndIf(EndIfStatement), + While(WhileStatement), + EndWhile(EndWhileStatement), + Break(BreakStatement), + Continue(ContinueStatement), + Synchronous(SynchronousStatement), + EndSynchronous(EndSynchronousStatement), + Return(ReturnStatement), + Assert(AssertStatement), + Goto(GotoStatement), + New(NewStatement), + Put(PutStatement), + Expression(ExpressionStatement), +} + +impl Statement { + pub fn as_block(&self) -> &BlockStatement { + match self { + Statement::Block(result) => result, + _ => panic!("Unable to cast `Statement` to `BlockStatement`"), + } + } + pub fn as_block_mut(&mut self) -> &mut BlockStatement { + match self { + Statement::Block(result) => result, + _ => panic!("Unable to cast `Statement` to `BlockStatement`"), + } + } + pub fn as_local(&self) -> &LocalStatement { + match self { + Statement::Local(result) => result, + _ => panic!("Unable to cast `Statement` to `LocalStatement`"), + } + } + pub fn as_memory(&self) -> &MemoryStatement { + self.as_local().as_memory() + } + pub fn as_channel(&self) -> &ChannelStatement { + self.as_local().as_channel() + } + pub fn as_skip(&self) -> &SkipStatement { + match self { + Statement::Skip(result) => result, + _ => panic!("Unable to cast `Statement` to `SkipStatement`"), + } + } + pub fn as_labeled(&self) -> &LabeledStatement { + match self { + Statement::Labeled(result) => result, + _ => panic!("Unable to cast `Statement` to `LabeledStatement`"), + } + } + pub fn as_labeled_mut(&mut self) -> &mut LabeledStatement { + match self { + Statement::Labeled(result) => result, + _ => panic!("Unable to cast `Statement` to `LabeledStatement`"), + } + } + pub fn as_if(&self) -> &IfStatement { + match self { + Statement::If(result) => result, + _ => panic!("Unable to cast `Statement` to `IfStatement`"), + } + } + pub fn as_end_if(&self) -> &EndIfStatement { + match self { + Statement::EndIf(result) => result, + _ => panic!("Unable to cast `Statement` to `EndIfStatement`"), + } + } + pub fn is_while(&self) -> bool { + match self { + Statement::While(_) => true, + _ => false, + } + } + pub fn as_while(&self) -> &WhileStatement { + match self { + Statement::While(result) => result, + _ => panic!("Unable to cast `Statement` to `WhileStatement`"), + } + } + pub fn as_while_mut(&mut self) -> &mut WhileStatement { + match self { + Statement::While(result) => result, + _ => panic!("Unable to cast `Statement` to `WhileStatement`"), + } + } + pub fn as_end_while(&self) -> &EndWhileStatement { + match self { + Statement::EndWhile(result) => result, + _ => panic!("Unable to cast `Statement` to `EndWhileStatement`"), + } + } + pub fn as_break(&self) -> &BreakStatement { + match self { + Statement::Break(result) => result, + _ => panic!("Unable to cast `Statement` to `BreakStatement`"), + } + } + pub fn as_break_mut(&mut self) -> &mut BreakStatement { + match self { + Statement::Break(result) => result, + _ => panic!("Unable to cast `Statement` to `BreakStatement`"), + } + } + pub fn as_continue(&self) -> &ContinueStatement { + match self { + Statement::Continue(result) => result, + _ => panic!("Unable to cast `Statement` to `ContinueStatement`"), + } + } + pub fn as_continue_mut(&mut self) -> &mut ContinueStatement { + match self { + Statement::Continue(result) => result, + _ => panic!("Unable to cast `Statement` to `ContinueStatement`"), + } + } + pub fn as_synchronous(&self) -> &SynchronousStatement { + match self { + Statement::Synchronous(result) => result, + _ => panic!("Unable to cast `Statement` to `SynchronousStatement`"), + } + } + pub fn as_synchronous_mut(&mut self) -> &mut SynchronousStatement { + match self { + Statement::Synchronous(result) => result, + _ => panic!("Unable to cast `Statement` to `SynchronousStatement`"), + } + } + pub fn as_end_synchronous(&self) -> &EndSynchronousStatement { + match self { + Statement::EndSynchronous(result) => result, + _ => panic!("Unable to cast `Statement` to `EndSynchronousStatement`"), + } + } + pub fn as_return(&self) -> &ReturnStatement { + match self { + Statement::Return(result) => result, + _ => panic!("Unable to cast `Statement` to `ReturnStatement`"), + } + } + pub fn as_assert(&self) -> &AssertStatement { + match self { + Statement::Assert(result) => result, + _ => panic!("Unable to cast `Statement` to `AssertStatement`"), + } + } + pub fn as_goto(&self) -> &GotoStatement { + match self { + Statement::Goto(result) => result, + _ => panic!("Unable to cast `Statement` to `GotoStatement`"), + } + } + pub fn as_goto_mut(&mut self) -> &mut GotoStatement { + match self { + Statement::Goto(result) => result, + _ => panic!("Unable to cast `Statement` to `GotoStatement`"), + } + } + pub fn as_new(&self) -> &NewStatement { + match self { + Statement::New(result) => result, + _ => panic!("Unable to cast `Statement` to `NewStatement`"), + } + } + pub fn as_put(&self) -> &PutStatement { + match self { + Statement::Put(result) => result, + _ => panic!("Unable to cast `Statement` to `PutStatement`"), + } + } + pub fn as_expression(&self) -> &ExpressionStatement { + match self { + Statement::Expression(result) => result, + _ => panic!("Unable to cast `Statement` to `ExpressionStatement`"), + } + } + pub fn link_next(&mut self, next: StatementId) { + match self { + Statement::Block(stmt) => panic!(), + Statement::Local(stmt) => match stmt { + LocalStatement::Channel(stmt) => stmt.next = Some(next), + LocalStatement::Memory(stmt) => stmt.next = Some(next), + }, + Statement::Skip(stmt) => stmt.next = Some(next), + Statement::Labeled(stmt) => panic!(), + Statement::If(stmt) => panic!(), + Statement::EndIf(stmt) => stmt.next = Some(next), + Statement::While(stmt) => panic!(), // although while has a next field, it is linked manually + Statement::EndWhile(stmt) => stmt.next = Some(next), + Statement::Break(stmt) => panic!(), + Statement::Continue(stmt) => panic!(), + Statement::Synchronous(stmt) => panic!(), + Statement::EndSynchronous(stmt) => stmt.next = Some(next), + Statement::Return(stmt) => panic!(), + Statement::Assert(stmt) => stmt.next = Some(next), + Statement::Goto(stmt) => panic!(), + Statement::New(stmt) => stmt.next = Some(next), + Statement::Put(stmt) => stmt.next = Some(next), + Statement::Expression(stmt) => stmt.next = Some(next), + } + } +} + +impl SyntaxElement for Statement { + fn position(&self) -> InputPosition { + match self { + Statement::Block(stmt) => stmt.position(), + Statement::Local(stmt) => stmt.position(), + Statement::Skip(stmt) => stmt.position(), + Statement::Labeled(stmt) => stmt.position(), + Statement::If(stmt) => stmt.position(), + Statement::EndIf(stmt) => stmt.position(), + Statement::While(stmt) => stmt.position(), + Statement::EndWhile(stmt) => stmt.position(), + Statement::Break(stmt) => stmt.position(), + Statement::Continue(stmt) => stmt.position(), + Statement::Synchronous(stmt) => stmt.position(), + Statement::EndSynchronous(stmt) => stmt.position(), + Statement::Return(stmt) => stmt.position(), + Statement::Assert(stmt) => stmt.position(), + Statement::Goto(stmt) => stmt.position(), + Statement::New(stmt) => stmt.position(), + Statement::Put(stmt) => stmt.position(), + Statement::Expression(stmt) => stmt.position(), + } + } +} + +#[derive(Debug, Clone)] +pub struct BlockStatement { + pub this: BlockStatementId, + // Phase 1: parser + pub position: InputPosition, + pub statements: Vec, + // Phase 2: linker + pub parent_scope: Option, + pub locals: Vec, + pub labels: Vec, +} + +impl BlockStatement { + pub fn parent_block(&self, h: &Heap) -> Option { + let parent = self.parent_scope.unwrap(); + match parent { + Scope::Definition(_) => { + // If the parent scope is a definition, then there is no + // parent block. + None + } + Scope::Synchronous(parent) => { + // It is always the case that when this function is called, + // the parent of a synchronous statement is a block statement: + // nested synchronous statements are flagged illegal, + // and that happens before resolving variables that + // creates the parent_scope references in the first place. + Some(h[parent].parent_scope(h).unwrap().to_block()) + } + Scope::Block(parent) => { + // A variable scope is either a definition, sync, or block. + Some(parent) + } + } + } + pub fn first(&self) -> StatementId { + *self.statements.first().unwrap() + } +} + +impl SyntaxElement for BlockStatement { + fn position(&self) -> InputPosition { + self.position + } +} + +impl VariableScope for BlockStatement { + fn parent_scope(&self, _h: &Heap) -> Option { + self.parent_scope + } + fn get_variable(&self, h: &Heap, id: SourceIdentifierId) -> Option { + for &local in self.locals.iter() { + if h[h[local].identifier] == h[id] { + return Some(local.0); + } + } + None + } +} + +#[derive(Debug, Clone)] +pub enum LocalStatement { + Memory(MemoryStatement), + Channel(ChannelStatement), +} + +impl LocalStatement { + pub fn this(&self) -> LocalStatementId { + match self { + LocalStatement::Memory(stmt) => stmt.this.upcast(), + LocalStatement::Channel(stmt) => stmt.this.upcast(), + } + } + pub fn as_memory(&self) -> &MemoryStatement { + match self { + LocalStatement::Memory(result) => result, + _ => panic!("Unable to cast `LocalStatement` to `MemoryStatement`"), + } + } + pub fn as_channel(&self) -> &ChannelStatement { + match self { + LocalStatement::Channel(result) => result, + _ => panic!("Unable to cast `LocalStatement` to `ChannelStatement`"), + } + } + pub fn next(&self) -> Option { + match self { + LocalStatement::Memory(stmt) => stmt.next, + LocalStatement::Channel(stmt) => stmt.next, + } + } +} + +impl SyntaxElement for LocalStatement { + fn position(&self) -> InputPosition { + match self { + LocalStatement::Memory(stmt) => stmt.position(), + LocalStatement::Channel(stmt) => stmt.position(), + } + } +} + +#[derive(Debug, Clone)] +pub struct MemoryStatement { + pub this: MemoryStatementId, + // Phase 1: parser + pub position: InputPosition, + pub variable: LocalId, + pub initial: ExpressionId, + // Phase 2: linker + pub next: Option, +} + +impl SyntaxElement for MemoryStatement { + fn position(&self) -> InputPosition { + self.position + } +} + +#[derive(Debug, Clone)] +pub struct ChannelStatement { + pub this: ChannelStatementId, + // Phase 1: parser + pub position: InputPosition, + pub from: LocalId, // output + pub to: LocalId, // input + // Phase 2: linker + pub next: Option, +} + +impl SyntaxElement for ChannelStatement { + fn position(&self) -> InputPosition { + self.position + } +} + +#[derive(Debug, Clone)] +pub struct SkipStatement { + pub this: SkipStatementId, + // Phase 1: parser + pub position: InputPosition, + // Phase 2: linker + pub next: Option, +} + +impl SyntaxElement for SkipStatement { + fn position(&self) -> InputPosition { + self.position + } +} + +#[derive(Debug, Clone)] +pub struct LabeledStatement { + pub this: LabeledStatementId, + // Phase 1: parser + pub position: InputPosition, + pub label: SourceIdentifierId, + pub body: StatementId, + // Phase 2: linker + pub in_sync: Option, +} + +impl SyntaxElement for LabeledStatement { + fn position(&self) -> InputPosition { + self.position + } +} + +#[derive(Debug, Clone)] +pub struct IfStatement { + pub this: IfStatementId, + // Phase 1: parser + pub position: InputPosition, + pub test: ExpressionId, + pub true_body: StatementId, + pub false_body: StatementId, +} + +impl SyntaxElement for IfStatement { + fn position(&self) -> InputPosition { + self.position + } +} + +#[derive(Debug, Clone)] +pub struct EndIfStatement { + pub this: EndIfStatementId, + // Phase 2: linker + pub position: InputPosition, // of corresponding if statement + pub next: Option, +} + +impl SyntaxElement for EndIfStatement { + fn position(&self) -> InputPosition { + self.position + } +} + +#[derive(Debug, Clone)] +pub struct WhileStatement { + pub this: WhileStatementId, + // Phase 1: parser + pub position: InputPosition, + pub test: ExpressionId, + pub body: StatementId, + // Phase 2: linker + pub next: Option, + pub in_sync: Option, +} + +impl SyntaxElement for WhileStatement { + fn position(&self) -> InputPosition { + self.position + } +} + +#[derive(Debug, Clone)] +pub struct EndWhileStatement { + pub this: EndWhileStatementId, + // Phase 2: linker + pub position: InputPosition, // of corresponding while + pub next: Option, +} + +impl SyntaxElement for EndWhileStatement { + fn position(&self) -> InputPosition { + self.position + } +} + +#[derive(Debug, Clone)] +pub struct BreakStatement { + pub this: BreakStatementId, + // Phase 1: parser + pub position: InputPosition, + pub label: Option, + // Phase 2: linker + pub target: Option, +} + +impl SyntaxElement for BreakStatement { + fn position(&self) -> InputPosition { + self.position + } +} + +#[derive(Debug, Clone)] +pub struct ContinueStatement { + pub this: ContinueStatementId, + // Phase 1: parser + pub position: InputPosition, + pub label: Option, + // Phase 2: linker + pub target: Option, +} + +impl SyntaxElement for ContinueStatement { + fn position(&self) -> InputPosition { + self.position + } +} + +#[derive(Debug, Clone)] +pub struct SynchronousStatement { + pub this: SynchronousStatementId, + // Phase 1: parser + pub position: InputPosition, + pub parameters: Vec, + pub body: StatementId, + // Phase 2: linker + pub parent_scope: Option, +} + +impl SyntaxElement for SynchronousStatement { + fn position(&self) -> InputPosition { + self.position + } +} + +impl VariableScope for SynchronousStatement { + fn parent_scope(&self, _h: &Heap) -> Option { + self.parent_scope + } + fn get_variable(&self, h: &Heap, id: SourceIdentifierId) -> Option { + for ¶m in self.parameters.iter() { + if h[h[param].identifier] == h[id] { + return Some(param.0); + } + } + None + } +} + +#[derive(Debug, Clone)] +pub struct EndSynchronousStatement { + pub this: EndSynchronousStatementId, + // Phase 2: linker + pub position: InputPosition, // of corresponding sync statement + pub next: Option, +} + +impl SyntaxElement for EndSynchronousStatement { + fn position(&self) -> InputPosition { + self.position + } +} + +#[derive(Debug, Clone)] +pub struct ReturnStatement { + pub this: ReturnStatementId, + // Phase 1: parser + pub position: InputPosition, + pub expression: ExpressionId, +} + +impl SyntaxElement for ReturnStatement { + fn position(&self) -> InputPosition { + self.position + } +} + +#[derive(Debug, Clone)] +pub struct AssertStatement { + pub this: AssertStatementId, + // Phase 1: parser + pub position: InputPosition, + pub expression: ExpressionId, + // Phase 2: linker + pub next: Option, +} + +impl SyntaxElement for AssertStatement { + fn position(&self) -> InputPosition { + self.position + } +} + +#[derive(Debug, Clone)] +pub struct GotoStatement { + pub this: GotoStatementId, + // Phase 1: parser + pub position: InputPosition, + pub label: SourceIdentifierId, + // Phase 2: linker + pub target: Option, +} + +impl SyntaxElement for GotoStatement { + fn position(&self) -> InputPosition { + self.position + } +} + +#[derive(Debug, Clone)] +pub struct NewStatement { + pub this: NewStatementId, + // Phase 1: parser + pub position: InputPosition, + pub expression: CallExpressionId, + // Phase 2: linker + pub next: Option, +} + +impl SyntaxElement for NewStatement { + fn position(&self) -> InputPosition { + self.position + } +} + +#[derive(Debug, Clone)] +pub struct PutStatement { + pub this: PutStatementId, + // Phase 1: parser + pub position: InputPosition, + pub port: ExpressionId, + pub message: ExpressionId, + // Phase 2: linker + pub next: Option, +} + +impl SyntaxElement for PutStatement { + fn position(&self) -> InputPosition { + self.position + } +} + +#[derive(Debug, Clone)] +pub struct ExpressionStatement { + pub this: ExpressionStatementId, + // Phase 1: parser + pub position: InputPosition, + pub expression: ExpressionId, + // Phase 2: linker + pub next: Option, +} + +impl SyntaxElement for ExpressionStatement { + fn position(&self) -> InputPosition { + self.position + } +} + +#[derive(Debug, Clone)] +pub enum Expression { + Assignment(AssignmentExpression), + Conditional(ConditionalExpression), + Binary(BinaryExpression), + Unary(UnaryExpression), + Indexing(IndexingExpression), + Slicing(SlicingExpression), + Select(SelectExpression), + Array(ArrayExpression), + Constant(ConstantExpression), + Call(CallExpression), + Variable(VariableExpression), +} + +impl Expression { + pub fn as_assignment(&self) -> &AssignmentExpression { + match self { + Expression::Assignment(result) => result, + _ => panic!("Unable to cast `Expression` to `AssignmentExpression`"), + } + } + pub fn as_conditional(&self) -> &ConditionalExpression { + match self { + Expression::Conditional(result) => result, + _ => panic!("Unable to cast `Expression` to `ConditionalExpression`"), + } + } + pub fn as_binary(&self) -> &BinaryExpression { + match self { + Expression::Binary(result) => result, + _ => panic!("Unable to cast `Expression` to `BinaryExpression`"), + } + } + pub fn as_unary(&self) -> &UnaryExpression { + match self { + Expression::Unary(result) => result, + _ => panic!("Unable to cast `Expression` to `UnaryExpression`"), + } + } + pub fn as_indexing(&self) -> &IndexingExpression { + match self { + Expression::Indexing(result) => result, + _ => panic!("Unable to cast `Expression` to `IndexingExpression`"), + } + } + pub fn as_slicing(&self) -> &SlicingExpression { + match self { + Expression::Slicing(result) => result, + _ => panic!("Unable to cast `Expression` to `SlicingExpression`"), + } + } + pub fn as_select(&self) -> &SelectExpression { + match self { + Expression::Select(result) => result, + _ => panic!("Unable to cast `Expression` to `SelectExpression`"), + } + } + pub fn as_array(&self) -> &ArrayExpression { + match self { + Expression::Array(result) => result, + _ => panic!("Unable to cast `Expression` to `ArrayExpression`"), + } + } + pub fn as_constant(&self) -> &ConstantExpression { + match self { + Expression::Constant(result) => result, + _ => panic!("Unable to cast `Expression` to `ConstantExpression`"), + } + } + pub fn as_call(&self) -> &CallExpression { + match self { + Expression::Call(result) => result, + _ => panic!("Unable to cast `Expression` to `CallExpression`"), + } + } + pub fn as_call_mut(&mut self) -> &mut CallExpression { + match self { + Expression::Call(result) => result, + _ => panic!("Unable to cast `Expression` to `CallExpression`"), + } + } + pub fn as_variable(&self) -> &VariableExpression { + match self { + Expression::Variable(result) => result, + _ => panic!("Unable to cast `Expression` to `VariableExpression`"), + } + } + pub fn as_variable_mut(&mut self) -> &mut VariableExpression { + match self { + Expression::Variable(result) => result, + _ => panic!("Unable to cast `Expression` to `VariableExpression`"), + } + } +} + +impl SyntaxElement for Expression { + fn position(&self) -> InputPosition { + match self { + Expression::Assignment(expr) => expr.position(), + Expression::Conditional(expr) => expr.position(), + Expression::Binary(expr) => expr.position(), + Expression::Unary(expr) => expr.position(), + Expression::Indexing(expr) => expr.position(), + Expression::Slicing(expr) => expr.position(), + Expression::Select(expr) => expr.position(), + Expression::Array(expr) => expr.position(), + Expression::Constant(expr) => expr.position(), + Expression::Call(expr) => expr.position(), + Expression::Variable(expr) => expr.position(), + } + } +} + +#[derive(Debug, Clone)] +pub enum AssignmentOperator { + Set, + Multiplied, + Divided, + Remained, + Added, + Subtracted, + ShiftedLeft, + ShiftedRight, + BitwiseAnded, + BitwiseXored, + BitwiseOred, +} + +#[derive(Debug, Clone)] +pub struct AssignmentExpression { + pub this: AssignmentExpressionId, + // Phase 1: parser + pub position: InputPosition, + pub left: ExpressionId, + pub operation: AssignmentOperator, + pub right: ExpressionId, +} + +impl SyntaxElement for AssignmentExpression { + fn position(&self) -> InputPosition { + self.position + } +} + +#[derive(Debug, Clone)] +pub struct ConditionalExpression { + pub this: ConditionalExpressionId, + // Phase 1: parser + pub position: InputPosition, + pub test: ExpressionId, + pub true_expression: ExpressionId, + pub false_expression: ExpressionId, +} + +impl SyntaxElement for ConditionalExpression { + fn position(&self) -> InputPosition { + self.position + } +} + +#[derive(Debug, Clone, PartialEq, Eq)] +pub enum BinaryOperator { + Concatenate, + LogicalOr, + LogicalAnd, + BitwiseOr, + BitwiseXor, + BitwiseAnd, + Equality, + Inequality, + LessThan, + GreaterThan, + LessThanEqual, + GreaterThanEqual, + ShiftLeft, + ShiftRight, + Add, + Subtract, + Multiply, + Divide, + Remainder, +} + +#[derive(Debug, Clone)] +pub struct BinaryExpression { + pub this: BinaryExpressionId, + // Phase 1: parser + pub position: InputPosition, + pub left: ExpressionId, + pub operation: BinaryOperator, + pub right: ExpressionId, +} + +impl SyntaxElement for BinaryExpression { + fn position(&self) -> InputPosition { + self.position + } +} + +#[derive(Debug, Clone, PartialEq, Eq)] +pub enum UnaryOperation { + Positive, + Negative, + BitwiseNot, + LogicalNot, + PreIncrement, + PreDecrement, + PostIncrement, + PostDecrement, +} + +#[derive(Debug, Clone)] +pub struct UnaryExpression { + pub this: UnaryExpressionId, + // Phase 1: parser + pub position: InputPosition, + pub operation: UnaryOperation, + pub expression: ExpressionId, +} + +impl SyntaxElement for UnaryExpression { + fn position(&self) -> InputPosition { + self.position + } +} + +#[derive(Debug, Clone)] +pub struct IndexingExpression { + pub this: IndexingExpressionId, + // Phase 1: parser + pub position: InputPosition, + pub subject: ExpressionId, + pub index: ExpressionId, +} + +impl SyntaxElement for IndexingExpression { + fn position(&self) -> InputPosition { + self.position + } +} + +#[derive(Debug, Clone)] +pub struct SlicingExpression { + pub this: SlicingExpressionId, + // Phase 1: parser + pub position: InputPosition, + pub subject: ExpressionId, + pub from_index: ExpressionId, + pub to_index: ExpressionId, +} + +impl SyntaxElement for SlicingExpression { + fn position(&self) -> InputPosition { + self.position + } +} + +#[derive(Debug, Clone)] +pub struct SelectExpression { + pub this: SelectExpressionId, + // Phase 1: parser + pub position: InputPosition, + pub subject: ExpressionId, + pub field: Field, +} + +impl SyntaxElement for SelectExpression { + fn position(&self) -> InputPosition { + self.position + } +} + +#[derive(Debug, Clone)] +pub struct ArrayExpression { + pub this: ArrayExpressionId, + // Phase 1: parser + pub position: InputPosition, + pub elements: Vec, +} + +impl SyntaxElement for ArrayExpression { + fn position(&self) -> InputPosition { + self.position + } +} + +#[derive(Debug, Clone)] +pub struct CallExpression { + pub this: CallExpressionId, + // Phase 1: parser + pub position: InputPosition, + pub method: Method, + pub arguments: Vec, + // Phase 2: linker + pub declaration: Option, +} + +impl SyntaxElement for CallExpression { + fn position(&self) -> InputPosition { + self.position + } +} + +#[derive(Debug, Clone)] +pub struct ConstantExpression { + pub this: ConstantExpressionId, + // Phase 1: parser + pub position: InputPosition, + pub value: Constant, +} + +impl SyntaxElement for ConstantExpression { + fn position(&self) -> InputPosition { + self.position + } +} + +#[derive(Debug, Clone)] +pub struct VariableExpression { + pub this: VariableExpressionId, + // Phase 1: parser + pub position: InputPosition, + pub identifier: SourceIdentifierId, + // Phase 2: linker + pub declaration: Option, +} + +impl SyntaxElement for VariableExpression { + fn position(&self) -> InputPosition { + self.position + } +} diff --git a/src/protocol/eval.rs b/src/protocol/eval.rs new file mode 100644 index 0000000000000000000000000000000000000000..5f1166344a0335178806023514058faeefee7e5a --- /dev/null +++ b/src/protocol/eval.rs @@ -0,0 +1,1687 @@ +use std::collections::HashMap; +use std::fmt; +use std::fmt::{Debug, Display, Formatter}; +use std::{i16, i32, i64, i8}; + +use crate::common::*; + +use crate::protocol::ast::*; +use crate::protocol::inputsource::*; +use crate::protocol::parser::*; +use crate::protocol::EvalContext; + +const MAX_RECURSION: usize = 1024; + +const BYTE_MIN: i64 = i8::MIN as i64; +const BYTE_MAX: i64 = i8::MAX as i64; +const SHORT_MIN: i64 = i16::MIN as i64; +const SHORT_MAX: i64 = i16::MAX as i64; +const INT_MIN: i64 = i32::MIN as i64; +const INT_MAX: i64 = i32::MAX as i64; + +const MESSAGE_MAX_LENGTH: i64 = SHORT_MAX; + +const ONE: Value = Value::Byte(ByteValue(1)); + +trait ValueImpl { + fn exact_type(&self) -> Type; + fn is_type_compatible(&self, t: &Type) -> bool; +} + +#[derive(Debug, Clone)] +pub enum Value { + Input(InputValue), + Output(OutputValue), + Message(MessageValue), + Boolean(BooleanValue), + Byte(ByteValue), + Short(ShortValue), + Int(IntValue), + Long(LongValue), + InputArray(InputArrayValue), + OutputArray(OutputArrayValue), + MessageArray(MessageArrayValue), + BooleanArray(BooleanArrayValue), + ByteArray(ByteArrayValue), + ShortArray(ShortArrayValue), + IntArray(IntArrayValue), + LongArray(LongArrayValue), +} +impl Value { + pub fn receive_message(buffer: &Vec) -> Value { + Value::Message(MessageValue(Some(buffer.clone()))) + } + fn create_message(length: Value) -> Value { + match length { + Value::Byte(_) | Value::Short(_) | Value::Int(_) | Value::Long(_) => { + let length : i64 = i64::from(length); + if length < 0 || length > MESSAGE_MAX_LENGTH { + // Only messages within the expected length are allowed + Value::Message(MessageValue(None)) + } else { + Value::Message(MessageValue(Some(vec![0; length.try_into().unwrap()]))) + } + } + _ => unimplemented!() + } + } + fn from_constant(constant: &Constant) -> Value { + match constant { + Constant::Null => Value::Message(MessageValue(None)), + Constant::True => Value::Boolean(BooleanValue(true)), + Constant::False => Value::Boolean(BooleanValue(false)), + Constant::Integer(data) => { + // Convert raw ASCII data to UTF-8 string + let raw = String::from_utf8_lossy(data); + let val = raw.parse::().unwrap(); + if val >= BYTE_MIN && val <= BYTE_MAX { + Value::Byte(ByteValue(val as i8)) + } else if val >= SHORT_MIN && val <= SHORT_MAX { + Value::Short(ShortValue(val as i16)) + } else if val >= INT_MIN && val <= INT_MAX { + Value::Int(IntValue(val as i32)) + } else { + Value::Long(LongValue(val)) + } + } + Constant::Character(data) => unimplemented!(), + } + } + fn set(&mut self, index: &Value, value: &Value) -> Option { + // The index must be of integer type, and non-negative + let the_index : usize; + match index { + Value::Byte(_) | Value::Short(_) | Value::Int(_) | Value::Long(_) => { + let index = i64::from(index); + if index < 0 || index > MESSAGE_MAX_LENGTH { + // It is inconsistent to update out of bounds + return None; + } + the_index = index.try_into().unwrap(); + } + _ => unreachable!() + } + // The subject must be either a message or an array + // And the value and the subject must be compatible + match (self, value) { + (Value::Message(MessageValue(None)), _) => { + // It is inconsistent to update the null message + None + } + (Value::Message(MessageValue(Some(buffer))), Value::Byte(ByteValue(b))) => { + if *b < 0 { + // It is inconsistent to update with a negative value + return None; + } + if let Some(slot) = buffer.get_mut(the_index) { + *slot = (*b).try_into().unwrap(); + Some(value.clone()) + } else { + // It is inconsistent to update out of bounds + None + } + } + (Value::Message(MessageValue(Some(buffer))), Value::Short(ShortValue(b))) => { + if *b < 0 || *b > BYTE_MAX as i16 { + // It is inconsistent to update with a negative value or a too large value + return None; + } + if let Some(slot) = buffer.get_mut(the_index) { + *slot = (*b).try_into().unwrap(); + Some(value.clone()) + } else { + // It is inconsistent to update out of bounds + None + } + } + (Value::InputArray(_), Value::Input(_)) => todo!(), + (Value::OutputArray(_), Value::Output(_)) => todo!(), + (Value::MessageArray(_), Value::Message(_)) => todo!(), + (Value::BooleanArray(_), Value::Boolean(_)) => todo!(), + (Value::ByteArray(_), Value::Byte(_)) => todo!(), + (Value::ShortArray(_), Value::Short(_)) => todo!(), + (Value::IntArray(_), Value::Int(_)) => todo!(), + (Value::LongArray(_), Value::Long(_)) => todo!(), + _ => unreachable!() + } + } + fn plus(&self, other: &Value) -> Value { + // TODO: do a match on the value directly + assert!(!self.exact_type().array); + assert!(!other.exact_type().array); + match (self.exact_type().primitive, other.exact_type().primitive) { + (PrimitiveType::Byte, PrimitiveType::Byte) => { + Value::Byte(ByteValue(i8::from(self) + i8::from(other))) + } + (PrimitiveType::Byte, PrimitiveType::Short) => { + Value::Short(ShortValue(i16::from(self) + i16::from(other))) + } + (PrimitiveType::Byte, PrimitiveType::Int) => { + Value::Int(IntValue(i32::from(self) + i32::from(other))) + } + (PrimitiveType::Byte, PrimitiveType::Long) => { + Value::Long(LongValue(i64::from(self) + i64::from(other))) + } + (PrimitiveType::Short, PrimitiveType::Byte) => { + Value::Short(ShortValue(i16::from(self) + i16::from(other))) + } + (PrimitiveType::Short, PrimitiveType::Short) => { + Value::Short(ShortValue(i16::from(self) + i16::from(other))) + } + (PrimitiveType::Short, PrimitiveType::Int) => { + Value::Int(IntValue(i32::from(self) + i32::from(other))) + } + (PrimitiveType::Short, PrimitiveType::Long) => { + Value::Long(LongValue(i64::from(self) + i64::from(other))) + } + (PrimitiveType::Int, PrimitiveType::Byte) => { + Value::Int(IntValue(i32::from(self) + i32::from(other))) + } + (PrimitiveType::Int, PrimitiveType::Short) => { + Value::Int(IntValue(i32::from(self) + i32::from(other))) + } + (PrimitiveType::Int, PrimitiveType::Int) => { + Value::Int(IntValue(i32::from(self) + i32::from(other))) + } + (PrimitiveType::Int, PrimitiveType::Long) => { + Value::Long(LongValue(i64::from(self) + i64::from(other))) + } + (PrimitiveType::Long, PrimitiveType::Byte) => { + Value::Long(LongValue(i64::from(self) + i64::from(other))) + } + (PrimitiveType::Long, PrimitiveType::Short) => { + Value::Long(LongValue(i64::from(self) + i64::from(other))) + } + (PrimitiveType::Long, PrimitiveType::Int) => { + Value::Long(LongValue(i64::from(self) + i64::from(other))) + } + (PrimitiveType::Long, PrimitiveType::Long) => { + Value::Long(LongValue(i64::from(self) + i64::from(other))) + } + _ => unimplemented!(), + } + } + fn minus(&self, other: &Value) -> Value { + assert!(!self.exact_type().array); + assert!(!other.exact_type().array); + match (self.exact_type().primitive, other.exact_type().primitive) { + (PrimitiveType::Byte, PrimitiveType::Byte) => { + Value::Byte(ByteValue(i8::from(self) - i8::from(other))) + } + (PrimitiveType::Byte, PrimitiveType::Short) => { + Value::Short(ShortValue(i16::from(self) - i16::from(other))) + } + (PrimitiveType::Byte, PrimitiveType::Int) => { + Value::Int(IntValue(i32::from(self) - i32::from(other))) + } + (PrimitiveType::Byte, PrimitiveType::Long) => { + Value::Long(LongValue(i64::from(self) - i64::from(other))) + } + (PrimitiveType::Short, PrimitiveType::Byte) => { + Value::Short(ShortValue(i16::from(self) - i16::from(other))) + } + (PrimitiveType::Short, PrimitiveType::Short) => { + Value::Short(ShortValue(i16::from(self) - i16::from(other))) + } + (PrimitiveType::Short, PrimitiveType::Int) => { + Value::Int(IntValue(i32::from(self) - i32::from(other))) + } + (PrimitiveType::Short, PrimitiveType::Long) => { + Value::Long(LongValue(i64::from(self) - i64::from(other))) + } + (PrimitiveType::Int, PrimitiveType::Byte) => { + Value::Int(IntValue(i32::from(self) - i32::from(other))) + } + (PrimitiveType::Int, PrimitiveType::Short) => { + Value::Int(IntValue(i32::from(self) - i32::from(other))) + } + (PrimitiveType::Int, PrimitiveType::Int) => { + Value::Int(IntValue(i32::from(self) - i32::from(other))) + } + (PrimitiveType::Int, PrimitiveType::Long) => { + Value::Long(LongValue(i64::from(self) - i64::from(other))) + } + (PrimitiveType::Long, PrimitiveType::Byte) => { + Value::Long(LongValue(i64::from(self) - i64::from(other))) + } + (PrimitiveType::Long, PrimitiveType::Short) => { + Value::Long(LongValue(i64::from(self) - i64::from(other))) + } + (PrimitiveType::Long, PrimitiveType::Int) => { + Value::Long(LongValue(i64::from(self) - i64::from(other))) + } + (PrimitiveType::Long, PrimitiveType::Long) => { + Value::Long(LongValue(i64::from(self) - i64::from(other))) + } + _ => unimplemented!(), + } + } + fn modulus(&self, other: &Value) -> Value { + assert!(!self.exact_type().array); + assert!(!other.exact_type().array); + match (self.exact_type().primitive, other.exact_type().primitive) { + (PrimitiveType::Byte, PrimitiveType::Byte) => { + Value::Byte(ByteValue(i8::from(self) % i8::from(other))) + } + (PrimitiveType::Byte, PrimitiveType::Short) => { + Value::Short(ShortValue(i16::from(self) % i16::from(other))) + } + (PrimitiveType::Byte, PrimitiveType::Int) => { + Value::Int(IntValue(i32::from(self) % i32::from(other))) + } + (PrimitiveType::Byte, PrimitiveType::Long) => { + Value::Long(LongValue(i64::from(self) % i64::from(other))) + } + (PrimitiveType::Short, PrimitiveType::Byte) => { + Value::Short(ShortValue(i16::from(self) % i16::from(other))) + } + (PrimitiveType::Short, PrimitiveType::Short) => { + Value::Short(ShortValue(i16::from(self) % i16::from(other))) + } + (PrimitiveType::Short, PrimitiveType::Int) => { + Value::Int(IntValue(i32::from(self) % i32::from(other))) + } + (PrimitiveType::Short, PrimitiveType::Long) => { + Value::Long(LongValue(i64::from(self) % i64::from(other))) + } + (PrimitiveType::Int, PrimitiveType::Byte) => { + Value::Int(IntValue(i32::from(self) % i32::from(other))) + } + (PrimitiveType::Int, PrimitiveType::Short) => { + Value::Int(IntValue(i32::from(self) % i32::from(other))) + } + (PrimitiveType::Int, PrimitiveType::Int) => { + Value::Int(IntValue(i32::from(self) % i32::from(other))) + } + (PrimitiveType::Int, PrimitiveType::Long) => { + Value::Long(LongValue(i64::from(self) % i64::from(other))) + } + (PrimitiveType::Long, PrimitiveType::Byte) => { + Value::Long(LongValue(i64::from(self) % i64::from(other))) + } + (PrimitiveType::Long, PrimitiveType::Short) => { + Value::Long(LongValue(i64::from(self) % i64::from(other))) + } + (PrimitiveType::Long, PrimitiveType::Int) => { + Value::Long(LongValue(i64::from(self) % i64::from(other))) + } + (PrimitiveType::Long, PrimitiveType::Long) => { + Value::Long(LongValue(i64::from(self) % i64::from(other))) + } + _ => unimplemented!(), + } + } + fn eq(&self, other: &Value) -> Value { + assert!(!self.exact_type().array); + assert!(!other.exact_type().array); + match (self.exact_type().primitive, other.exact_type().primitive) { + (PrimitiveType::Byte, PrimitiveType::Byte) => { + Value::Boolean(BooleanValue(i8::from(self) == i8::from(other))) + } + (PrimitiveType::Byte, PrimitiveType::Short) => { + Value::Boolean(BooleanValue(i16::from(self) == i16::from(other))) + } + (PrimitiveType::Byte, PrimitiveType::Int) => { + Value::Boolean(BooleanValue(i32::from(self) == i32::from(other))) + } + (PrimitiveType::Byte, PrimitiveType::Long) => { + Value::Boolean(BooleanValue(i64::from(self) == i64::from(other))) + } + (PrimitiveType::Short, PrimitiveType::Byte) => { + Value::Boolean(BooleanValue(i16::from(self) == i16::from(other))) + } + (PrimitiveType::Short, PrimitiveType::Short) => { + Value::Boolean(BooleanValue(i16::from(self) == i16::from(other))) + } + (PrimitiveType::Short, PrimitiveType::Int) => { + Value::Boolean(BooleanValue(i32::from(self) == i32::from(other))) + } + (PrimitiveType::Short, PrimitiveType::Long) => { + Value::Boolean(BooleanValue(i64::from(self) == i64::from(other))) + } + (PrimitiveType::Int, PrimitiveType::Byte) => { + Value::Boolean(BooleanValue(i32::from(self) == i32::from(other))) + } + (PrimitiveType::Int, PrimitiveType::Short) => { + Value::Boolean(BooleanValue(i32::from(self) == i32::from(other))) + } + (PrimitiveType::Int, PrimitiveType::Int) => { + Value::Boolean(BooleanValue(i32::from(self) == i32::from(other))) + } + (PrimitiveType::Int, PrimitiveType::Long) => { + Value::Boolean(BooleanValue(i64::from(self) == i64::from(other))) + } + (PrimitiveType::Long, PrimitiveType::Byte) => { + Value::Boolean(BooleanValue(i64::from(self) == i64::from(other))) + } + (PrimitiveType::Long, PrimitiveType::Short) => { + Value::Boolean(BooleanValue(i64::from(self) == i64::from(other))) + } + (PrimitiveType::Long, PrimitiveType::Int) => { + Value::Boolean(BooleanValue(i64::from(self) == i64::from(other))) + } + (PrimitiveType::Long, PrimitiveType::Long) => { + Value::Boolean(BooleanValue(i64::from(self) == i64::from(other))) + } + _ => unimplemented!(), + } + } + fn neq(&self, other: &Value) -> Value { + assert!(!self.exact_type().array); + assert!(!other.exact_type().array); + match (self.exact_type().primitive, other.exact_type().primitive) { + (PrimitiveType::Byte, PrimitiveType::Byte) => { + Value::Boolean(BooleanValue(i8::from(self) != i8::from(other))) + } + (PrimitiveType::Byte, PrimitiveType::Short) => { + Value::Boolean(BooleanValue(i16::from(self) != i16::from(other))) + } + (PrimitiveType::Byte, PrimitiveType::Int) => { + Value::Boolean(BooleanValue(i32::from(self) != i32::from(other))) + } + (PrimitiveType::Byte, PrimitiveType::Long) => { + Value::Boolean(BooleanValue(i64::from(self) != i64::from(other))) + } + (PrimitiveType::Short, PrimitiveType::Byte) => { + Value::Boolean(BooleanValue(i16::from(self) != i16::from(other))) + } + (PrimitiveType::Short, PrimitiveType::Short) => { + Value::Boolean(BooleanValue(i16::from(self) != i16::from(other))) + } + (PrimitiveType::Short, PrimitiveType::Int) => { + Value::Boolean(BooleanValue(i32::from(self) != i32::from(other))) + } + (PrimitiveType::Short, PrimitiveType::Long) => { + Value::Boolean(BooleanValue(i64::from(self) != i64::from(other))) + } + (PrimitiveType::Int, PrimitiveType::Byte) => { + Value::Boolean(BooleanValue(i32::from(self) != i32::from(other))) + } + (PrimitiveType::Int, PrimitiveType::Short) => { + Value::Boolean(BooleanValue(i32::from(self) != i32::from(other))) + } + (PrimitiveType::Int, PrimitiveType::Int) => { + Value::Boolean(BooleanValue(i32::from(self) != i32::from(other))) + } + (PrimitiveType::Int, PrimitiveType::Long) => { + Value::Boolean(BooleanValue(i64::from(self) != i64::from(other))) + } + (PrimitiveType::Long, PrimitiveType::Byte) => { + Value::Boolean(BooleanValue(i64::from(self) != i64::from(other))) + } + (PrimitiveType::Long, PrimitiveType::Short) => { + Value::Boolean(BooleanValue(i64::from(self) != i64::from(other))) + } + (PrimitiveType::Long, PrimitiveType::Int) => { + Value::Boolean(BooleanValue(i64::from(self) != i64::from(other))) + } + (PrimitiveType::Long, PrimitiveType::Long) => { + Value::Boolean(BooleanValue(i64::from(self) != i64::from(other))) + } + _ => unimplemented!(), + } + } + fn lt(&self, other: &Value) -> Value { + assert!(!self.exact_type().array); + assert!(!other.exact_type().array); + match (self.exact_type().primitive, other.exact_type().primitive) { + (PrimitiveType::Byte, PrimitiveType::Byte) => { + Value::Boolean(BooleanValue(i8::from(self) < i8::from(other))) + } + (PrimitiveType::Byte, PrimitiveType::Short) => { + Value::Boolean(BooleanValue(i16::from(self) < i16::from(other))) + } + (PrimitiveType::Byte, PrimitiveType::Int) => { + Value::Boolean(BooleanValue(i32::from(self) < i32::from(other))) + } + (PrimitiveType::Byte, PrimitiveType::Long) => { + Value::Boolean(BooleanValue(i64::from(self) < i64::from(other))) + } + (PrimitiveType::Short, PrimitiveType::Byte) => { + Value::Boolean(BooleanValue(i16::from(self) < i16::from(other))) + } + (PrimitiveType::Short, PrimitiveType::Short) => { + Value::Boolean(BooleanValue(i16::from(self) < i16::from(other))) + } + (PrimitiveType::Short, PrimitiveType::Int) => { + Value::Boolean(BooleanValue(i32::from(self) < i32::from(other))) + } + (PrimitiveType::Short, PrimitiveType::Long) => { + Value::Boolean(BooleanValue(i64::from(self) < i64::from(other))) + } + (PrimitiveType::Int, PrimitiveType::Byte) => { + Value::Boolean(BooleanValue(i32::from(self) < i32::from(other))) + } + (PrimitiveType::Int, PrimitiveType::Short) => { + Value::Boolean(BooleanValue(i32::from(self) < i32::from(other))) + } + (PrimitiveType::Int, PrimitiveType::Int) => { + Value::Boolean(BooleanValue(i32::from(self) < i32::from(other))) + } + (PrimitiveType::Int, PrimitiveType::Long) => { + Value::Boolean(BooleanValue(i64::from(self) < i64::from(other))) + } + (PrimitiveType::Long, PrimitiveType::Byte) => { + Value::Boolean(BooleanValue(i64::from(self) < i64::from(other))) + } + (PrimitiveType::Long, PrimitiveType::Short) => { + Value::Boolean(BooleanValue(i64::from(self) < i64::from(other))) + } + (PrimitiveType::Long, PrimitiveType::Int) => { + Value::Boolean(BooleanValue(i64::from(self) < i64::from(other))) + } + (PrimitiveType::Long, PrimitiveType::Long) => { + Value::Boolean(BooleanValue(i64::from(self) < i64::from(other))) + } + _ => unimplemented!(), + } + } + fn lte(&self, other: &Value) -> Value { + assert!(!self.exact_type().array); + assert!(!other.exact_type().array); + match (self.exact_type().primitive, other.exact_type().primitive) { + (PrimitiveType::Byte, PrimitiveType::Byte) => { + Value::Boolean(BooleanValue(i8::from(self) <= i8::from(other))) + } + (PrimitiveType::Byte, PrimitiveType::Short) => { + Value::Boolean(BooleanValue(i16::from(self) <= i16::from(other))) + } + (PrimitiveType::Byte, PrimitiveType::Int) => { + Value::Boolean(BooleanValue(i32::from(self) <= i32::from(other))) + } + (PrimitiveType::Byte, PrimitiveType::Long) => { + Value::Boolean(BooleanValue(i64::from(self) <= i64::from(other))) + } + (PrimitiveType::Short, PrimitiveType::Byte) => { + Value::Boolean(BooleanValue(i16::from(self) <= i16::from(other))) + } + (PrimitiveType::Short, PrimitiveType::Short) => { + Value::Boolean(BooleanValue(i16::from(self) <= i16::from(other))) + } + (PrimitiveType::Short, PrimitiveType::Int) => { + Value::Boolean(BooleanValue(i32::from(self) <= i32::from(other))) + } + (PrimitiveType::Short, PrimitiveType::Long) => { + Value::Boolean(BooleanValue(i64::from(self) <= i64::from(other))) + } + (PrimitiveType::Int, PrimitiveType::Byte) => { + Value::Boolean(BooleanValue(i32::from(self) <= i32::from(other))) + } + (PrimitiveType::Int, PrimitiveType::Short) => { + Value::Boolean(BooleanValue(i32::from(self) <= i32::from(other))) + } + (PrimitiveType::Int, PrimitiveType::Int) => { + Value::Boolean(BooleanValue(i32::from(self) <= i32::from(other))) + } + (PrimitiveType::Int, PrimitiveType::Long) => { + Value::Boolean(BooleanValue(i64::from(self) <= i64::from(other))) + } + (PrimitiveType::Long, PrimitiveType::Byte) => { + Value::Boolean(BooleanValue(i64::from(self) <= i64::from(other))) + } + (PrimitiveType::Long, PrimitiveType::Short) => { + Value::Boolean(BooleanValue(i64::from(self) <= i64::from(other))) + } + (PrimitiveType::Long, PrimitiveType::Int) => { + Value::Boolean(BooleanValue(i64::from(self) <= i64::from(other))) + } + (PrimitiveType::Long, PrimitiveType::Long) => { + Value::Boolean(BooleanValue(i64::from(self) <= i64::from(other))) + } + _ => unimplemented!(), + } + } + fn gt(&self, other: &Value) -> Value { + assert!(!self.exact_type().array); + assert!(!other.exact_type().array); + match (self.exact_type().primitive, other.exact_type().primitive) { + (PrimitiveType::Byte, PrimitiveType::Byte) => { + Value::Boolean(BooleanValue(i8::from(self) > i8::from(other))) + } + (PrimitiveType::Byte, PrimitiveType::Short) => { + Value::Boolean(BooleanValue(i16::from(self) > i16::from(other))) + } + (PrimitiveType::Byte, PrimitiveType::Int) => { + Value::Boolean(BooleanValue(i32::from(self) > i32::from(other))) + } + (PrimitiveType::Byte, PrimitiveType::Long) => { + Value::Boolean(BooleanValue(i64::from(self) > i64::from(other))) + } + (PrimitiveType::Short, PrimitiveType::Byte) => { + Value::Boolean(BooleanValue(i16::from(self) > i16::from(other))) + } + (PrimitiveType::Short, PrimitiveType::Short) => { + Value::Boolean(BooleanValue(i16::from(self) > i16::from(other))) + } + (PrimitiveType::Short, PrimitiveType::Int) => { + Value::Boolean(BooleanValue(i32::from(self) > i32::from(other))) + } + (PrimitiveType::Short, PrimitiveType::Long) => { + Value::Boolean(BooleanValue(i64::from(self) > i64::from(other))) + } + (PrimitiveType::Int, PrimitiveType::Byte) => { + Value::Boolean(BooleanValue(i32::from(self) > i32::from(other))) + } + (PrimitiveType::Int, PrimitiveType::Short) => { + Value::Boolean(BooleanValue(i32::from(self) > i32::from(other))) + } + (PrimitiveType::Int, PrimitiveType::Int) => { + Value::Boolean(BooleanValue(i32::from(self) > i32::from(other))) + } + (PrimitiveType::Int, PrimitiveType::Long) => { + Value::Boolean(BooleanValue(i64::from(self) > i64::from(other))) + } + (PrimitiveType::Long, PrimitiveType::Byte) => { + Value::Boolean(BooleanValue(i64::from(self) > i64::from(other))) + } + (PrimitiveType::Long, PrimitiveType::Short) => { + Value::Boolean(BooleanValue(i64::from(self) > i64::from(other))) + } + (PrimitiveType::Long, PrimitiveType::Int) => { + Value::Boolean(BooleanValue(i64::from(self) > i64::from(other))) + } + (PrimitiveType::Long, PrimitiveType::Long) => { + Value::Boolean(BooleanValue(i64::from(self) > i64::from(other))) + } + _ => unimplemented!(), + } + } + fn gte(&self, other: &Value) -> Value { + assert!(!self.exact_type().array); + assert!(!other.exact_type().array); + match (self.exact_type().primitive, other.exact_type().primitive) { + (PrimitiveType::Byte, PrimitiveType::Byte) => { + Value::Boolean(BooleanValue(i8::from(self) >= i8::from(other))) + } + (PrimitiveType::Byte, PrimitiveType::Short) => { + Value::Boolean(BooleanValue(i16::from(self) >= i16::from(other))) + } + (PrimitiveType::Byte, PrimitiveType::Int) => { + Value::Boolean(BooleanValue(i32::from(self) >= i32::from(other))) + } + (PrimitiveType::Byte, PrimitiveType::Long) => { + Value::Boolean(BooleanValue(i64::from(self) >= i64::from(other))) + } + (PrimitiveType::Short, PrimitiveType::Byte) => { + Value::Boolean(BooleanValue(i16::from(self) >= i16::from(other))) + } + (PrimitiveType::Short, PrimitiveType::Short) => { + Value::Boolean(BooleanValue(i16::from(self) >= i16::from(other))) + } + (PrimitiveType::Short, PrimitiveType::Int) => { + Value::Boolean(BooleanValue(i32::from(self) >= i32::from(other))) + } + (PrimitiveType::Short, PrimitiveType::Long) => { + Value::Boolean(BooleanValue(i64::from(self) >= i64::from(other))) + } + (PrimitiveType::Int, PrimitiveType::Byte) => { + Value::Boolean(BooleanValue(i32::from(self) >= i32::from(other))) + } + (PrimitiveType::Int, PrimitiveType::Short) => { + Value::Boolean(BooleanValue(i32::from(self) >= i32::from(other))) + } + (PrimitiveType::Int, PrimitiveType::Int) => { + Value::Boolean(BooleanValue(i32::from(self) >= i32::from(other))) + } + (PrimitiveType::Int, PrimitiveType::Long) => { + Value::Boolean(BooleanValue(i64::from(self) >= i64::from(other))) + } + (PrimitiveType::Long, PrimitiveType::Byte) => { + Value::Boolean(BooleanValue(i64::from(self) >= i64::from(other))) + } + (PrimitiveType::Long, PrimitiveType::Short) => { + Value::Boolean(BooleanValue(i64::from(self) >= i64::from(other))) + } + (PrimitiveType::Long, PrimitiveType::Int) => { + Value::Boolean(BooleanValue(i64::from(self) >= i64::from(other))) + } + (PrimitiveType::Long, PrimitiveType::Long) => { + Value::Boolean(BooleanValue(i64::from(self) >= i64::from(other))) + } + _ => unimplemented!(), + } + } + fn as_boolean(&self) -> &BooleanValue { + match self { + Value::Boolean(result) => result, + _ => panic!("Unable to cast `Value` to `BooleanValue`"), + } + } +} + +impl From for Value { + fn from(b: bool) -> Self { + Value::Boolean(BooleanValue(b)) + } +} +impl From for bool { + fn from(val: Value) -> Self { + match val { + Value::Boolean(BooleanValue(b)) => b, + _ => unimplemented!(), + } + } +} +impl From<&Value> for bool { + fn from(val: &Value) -> Self { + match val { + Value::Boolean(BooleanValue(b)) => *b, + _ => unimplemented!(), + } + } +} + +impl From for i8 { + fn from(val: Value) -> Self { + match val { + Value::Byte(ByteValue(b)) => b, + _ => unimplemented!(), + } + } +} +impl From<&Value> for i8 { + fn from(val: &Value) -> Self { + match val { + Value::Byte(ByteValue(b)) => *b, + _ => unimplemented!(), + } + } +} + +impl From for i16 { + fn from(val: Value) -> Self { + match val { + Value::Byte(ByteValue(b)) => i16::from(b), + Value::Short(ShortValue(s)) => s, + _ => unimplemented!(), + } + } +} +impl From<&Value> for i16 { + fn from(val: &Value) -> Self { + match val { + Value::Byte(ByteValue(b)) => i16::from(*b), + Value::Short(ShortValue(s)) => *s, + _ => unimplemented!(), + } + } +} + +impl From for i32 { + fn from(val: Value) -> Self { + match val { + Value::Byte(ByteValue(b)) => i32::from(b), + Value::Short(ShortValue(s)) => i32::from(s), + Value::Int(IntValue(i)) => i, + _ => unimplemented!(), + } + } +} +impl From<&Value> for i32 { + fn from(val: &Value) -> Self { + match val { + Value::Byte(ByteValue(b)) => i32::from(*b), + Value::Short(ShortValue(s)) => i32::from(*s), + Value::Int(IntValue(i)) => *i, + _ => unimplemented!(), + } + } +} + +impl From for i64 { + fn from(val: Value) -> Self { + match val { + Value::Byte(ByteValue(b)) => i64::from(b), + Value::Short(ShortValue(s)) => i64::from(s), + Value::Int(IntValue(i)) => i64::from(i), + Value::Long(LongValue(l)) => l, + _ => unimplemented!(), + } + } +} +impl From<&Value> for i64 { + fn from(val: &Value) -> Self { + match val { + Value::Byte(ByteValue(b)) => i64::from(*b), + Value::Short(ShortValue(s)) => i64::from(*s), + Value::Int(IntValue(i)) => i64::from(*i), + Value::Long(LongValue(l)) => *l, + _ => unimplemented!(), + } + } +} + +impl ValueImpl for Value { + fn exact_type(&self) -> Type { + match self { + Value::Input(val) => val.exact_type(), + Value::Output(val) => val.exact_type(), + Value::Message(val) => val.exact_type(), + Value::Boolean(val) => val.exact_type(), + Value::Byte(val) => val.exact_type(), + Value::Short(val) => val.exact_type(), + Value::Int(val) => val.exact_type(), + Value::Long(val) => val.exact_type(), + Value::InputArray(val) => val.exact_type(), + Value::OutputArray(val) => val.exact_type(), + Value::MessageArray(val) => val.exact_type(), + Value::BooleanArray(val) => val.exact_type(), + Value::ByteArray(val) => val.exact_type(), + Value::ShortArray(val) => val.exact_type(), + Value::IntArray(val) => val.exact_type(), + Value::LongArray(val) => val.exact_type(), + } + } + fn is_type_compatible(&self, t: &Type) -> bool { + match self { + Value::Input(val) => val.is_type_compatible(t), + Value::Output(val) => val.is_type_compatible(t), + Value::Message(val) => val.is_type_compatible(t), + Value::Boolean(val) => val.is_type_compatible(t), + Value::Byte(val) => val.is_type_compatible(t), + Value::Short(val) => val.is_type_compatible(t), + Value::Int(val) => val.is_type_compatible(t), + Value::Long(val) => val.is_type_compatible(t), + Value::InputArray(val) => val.is_type_compatible(t), + Value::OutputArray(val) => val.is_type_compatible(t), + Value::MessageArray(val) => val.is_type_compatible(t), + Value::BooleanArray(val) => val.is_type_compatible(t), + Value::ByteArray(val) => val.is_type_compatible(t), + Value::ShortArray(val) => val.is_type_compatible(t), + Value::IntArray(val) => val.is_type_compatible(t), + Value::LongArray(val) => val.is_type_compatible(t), + } + } +} + +impl Display for Value { + fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result { + let disp: &dyn Display; + match self { + Value::Input(val) => disp = val, + Value::Output(val) => disp = val, + Value::Message(val) => disp = val, + Value::Boolean(val) => disp = val, + Value::Byte(val) => disp = val, + Value::Short(val) => disp = val, + Value::Int(val) => disp = val, + Value::Long(val) => disp = val, + Value::InputArray(val) => disp = val, + Value::OutputArray(val) => disp = val, + Value::MessageArray(val) => disp = val, + Value::BooleanArray(val) => disp = val, + Value::ByteArray(val) => disp = val, + Value::ShortArray(val) => disp = val, + Value::IntArray(val) => disp = val, + Value::LongArray(val) => disp = val, + } + disp.fmt(f) + } +} + +#[derive(Debug, Clone)] +pub struct InputValue(pub Key); + +impl Display for InputValue { + fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result { + write!(f, "#in") + } +} + +impl ValueImpl for InputValue { + fn exact_type(&self) -> Type { + Type::INPUT + } + fn is_type_compatible(&self, t: &Type) -> bool { + let Type { primitive, array } = t; + if *array { + return false; + } + match primitive { + PrimitiveType::Input => true, + _ => false, + } + } +} + +#[derive(Debug, Clone)] +pub struct OutputValue(pub Key); + +impl Display for OutputValue { + fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result { + write!(f, "#out") + } +} + +impl ValueImpl for OutputValue { + fn exact_type(&self) -> Type { + Type::OUTPUT + } + fn is_type_compatible(&self, t: &Type) -> bool { + let Type { primitive, array } = t; + if *array { + return false; + } + match primitive { + PrimitiveType::Output => true, + _ => false, + } + } +} + +#[derive(Debug, Clone)] +pub struct MessageValue(pub Option>); + +impl Display for MessageValue { + fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result { + match &self.0 { + None => write!(f, "null"), + Some(vec) => { + write!(f, "#msg({};", vec.len())?; + let mut i = 0; + for v in vec.iter() { + if i > 0 { + write!(f, ",")?; + } + write!(f, "{}", v)?; + i += 1; + if i >= 10 { + write!(f, ",...")?; + break; + } + } + write!(f, ")") + }, + } + } +} + +impl ValueImpl for MessageValue { + fn exact_type(&self) -> Type { + Type::MESSAGE + } + fn is_type_compatible(&self, t: &Type) -> bool { + let Type { primitive, array } = t; + if *array { + return false; + } + match primitive { + PrimitiveType::Message => true, + _ => false, + } + } +} + +#[derive(Debug, Clone)] +pub struct BooleanValue(bool); + +impl Display for BooleanValue { + fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result { + write!(f, "{}", self.0) + } +} + +impl ValueImpl for BooleanValue { + fn exact_type(&self) -> Type { + Type::BOOLEAN + } + fn is_type_compatible(&self, t: &Type) -> bool { + let Type { primitive, array } = t; + if *array { + return false; + } + match primitive { + PrimitiveType::Boolean => true, + PrimitiveType::Byte => true, + PrimitiveType::Short => true, + PrimitiveType::Int => true, + PrimitiveType::Long => true, + _ => false, + } + } +} + +#[derive(Debug, Clone)] +pub struct ByteValue(i8); + +impl Display for ByteValue { + fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result { + write!(f, "{}", self.0) + } +} + +impl ValueImpl for ByteValue { + fn exact_type(&self) -> Type { + Type::BYTE + } + fn is_type_compatible(&self, t: &Type) -> bool { + let Type { primitive, array } = t; + if *array { + return false; + } + match primitive { + PrimitiveType::Byte => true, + PrimitiveType::Short => true, + PrimitiveType::Int => true, + PrimitiveType::Long => true, + _ => false, + } + } +} + +#[derive(Debug, Clone)] +pub struct ShortValue(i16); + +impl Display for ShortValue { + fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result { + write!(f, "{}", self.0) + } +} + +impl ValueImpl for ShortValue { + fn exact_type(&self) -> Type { + Type::SHORT + } + fn is_type_compatible(&self, t: &Type) -> bool { + let Type { primitive, array } = t; + if *array { + return false; + } + match primitive { + PrimitiveType::Short => true, + PrimitiveType::Int => true, + PrimitiveType::Long => true, + _ => false, + } + } +} + +#[derive(Debug, Clone)] +pub struct IntValue(i32); + +impl Display for IntValue { + fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result { + write!(f, "{}", self.0) + } +} + +impl ValueImpl for IntValue { + fn exact_type(&self) -> Type { + Type::INT + } + fn is_type_compatible(&self, t: &Type) -> bool { + let Type { primitive, array } = t; + if *array { + return false; + } + match primitive { + PrimitiveType::Int => true, + PrimitiveType::Long => true, + _ => false, + } + } +} + +#[derive(Debug, Clone)] +pub struct LongValue(i64); + +impl Display for LongValue { + fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result { + write!(f, "{}", self.0) + } +} + +impl ValueImpl for LongValue { + fn exact_type(&self) -> Type { + Type::LONG + } + fn is_type_compatible(&self, t: &Type) -> bool { + let Type { primitive, array } = t; + if *array { + return false; + } + match primitive { + PrimitiveType::Long => true, + _ => false, + } + } +} + +#[derive(Debug, Clone)] +pub struct InputArrayValue(Vec); + +impl Display for InputArrayValue { + fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result { + write!(f, "{{")?; + let mut first = true; + for v in self.0.iter() { + if !first { + write!(f, ",")?; + } + write!(f, "{}", v)?; + first = false; + } + write!(f, "}}") + } +} + +impl ValueImpl for InputArrayValue { + fn exact_type(&self) -> Type { + Type::INPUT_ARRAY + } + fn is_type_compatible(&self, t: &Type) -> bool { + let Type { primitive, array } = t; + if !*array { + return false; + } + match primitive { + PrimitiveType::Input => true, + _ => false, + } + } +} + +#[derive(Debug, Clone)] +pub struct OutputArrayValue(Vec); + +impl Display for OutputArrayValue { + fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result { + write!(f, "{{")?; + let mut first = true; + for v in self.0.iter() { + if !first { + write!(f, ",")?; + } + write!(f, "{}", v)?; + first = false; + } + write!(f, "}}") + } +} + +impl ValueImpl for OutputArrayValue { + fn exact_type(&self) -> Type { + Type::OUTPUT_ARRAY + } + fn is_type_compatible(&self, t: &Type) -> bool { + let Type { primitive, array } = t; + if !*array { + return false; + } + match primitive { + PrimitiveType::Output => true, + _ => false, + } + } +} + +#[derive(Debug, Clone)] +pub struct MessageArrayValue(Vec); + +impl Display for MessageArrayValue { + fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result { + write!(f, "{{")?; + let mut first = true; + for v in self.0.iter() { + if !first { + write!(f, ",")?; + } + write!(f, "{}", v)?; + first = false; + } + write!(f, "}}") + } +} + +impl ValueImpl for MessageArrayValue { + fn exact_type(&self) -> Type { + Type::MESSAGE_ARRAY + } + fn is_type_compatible(&self, t: &Type) -> bool { + let Type { primitive, array } = t; + if !*array { + return false; + } + match primitive { + PrimitiveType::Message => true, + _ => false, + } + } +} + +#[derive(Debug, Clone)] +pub struct BooleanArrayValue(Vec); + +impl Display for BooleanArrayValue { + fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result { + write!(f, "{{")?; + let mut first = true; + for v in self.0.iter() { + if !first { + write!(f, ",")?; + } + write!(f, "{}", v)?; + first = false; + } + write!(f, "}}") + } +} + +impl ValueImpl for BooleanArrayValue { + fn exact_type(&self) -> Type { + Type::BOOLEAN_ARRAY + } + fn is_type_compatible(&self, t: &Type) -> bool { + let Type { primitive, array } = t; + if !*array { + return false; + } + match primitive { + PrimitiveType::Boolean => true, + _ => false, + } + } +} + +#[derive(Debug, Clone)] +pub struct ByteArrayValue(Vec); + +impl Display for ByteArrayValue { + fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result { + write!(f, "{{")?; + let mut first = true; + for v in self.0.iter() { + if !first { + write!(f, ",")?; + } + write!(f, "{}", v)?; + first = false; + } + write!(f, "}}") + } +} + +impl ValueImpl for ByteArrayValue { + fn exact_type(&self) -> Type { + Type::BYTE_ARRAY + } + fn is_type_compatible(&self, t: &Type) -> bool { + let Type { primitive, array } = t; + if !*array { + return false; + } + match primitive { + PrimitiveType::Byte => true, + _ => false, + } + } +} + +#[derive(Debug, Clone)] +pub struct ShortArrayValue(Vec); + +impl Display for ShortArrayValue { + fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result { + write!(f, "{{")?; + let mut first = true; + for v in self.0.iter() { + if !first { + write!(f, ",")?; + } + write!(f, "{}", v)?; + first = false; + } + write!(f, "}}") + } +} + +impl ValueImpl for ShortArrayValue { + fn exact_type(&self) -> Type { + Type::SHORT_ARRAY + } + fn is_type_compatible(&self, t: &Type) -> bool { + let Type { primitive, array } = t; + if !*array { + return false; + } + match primitive { + PrimitiveType::Short => true, + _ => false, + } + } +} + +#[derive(Debug, Clone)] +pub struct IntArrayValue(Vec); + +impl Display for IntArrayValue { + fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result { + write!(f, "{{")?; + let mut first = true; + for v in self.0.iter() { + if !first { + write!(f, ",")?; + } + write!(f, "{}", v)?; + first = false; + } + write!(f, "}}") + } +} + +impl ValueImpl for IntArrayValue { + fn exact_type(&self) -> Type { + Type::INT_ARRAY + } + fn is_type_compatible(&self, t: &Type) -> bool { + let Type { primitive, array } = t; + if !*array { + return false; + } + match primitive { + PrimitiveType::Int => true, + _ => false, + } + } +} + +#[derive(Debug, Clone)] +pub struct LongArrayValue(Vec); + +impl Display for LongArrayValue { + fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result { + write!(f, "{{")?; + let mut first = true; + for v in self.0.iter() { + if !first { + write!(f, ",")?; + } + write!(f, "{}", v)?; + first = false; + } + write!(f, "}}") + } +} + +impl ValueImpl for LongArrayValue { + fn exact_type(&self) -> Type { + Type::LONG_ARRAY + } + fn is_type_compatible(&self, t: &Type) -> bool { + let Type { primitive, array } = t; + if !*array { + return false; + } + match primitive { + PrimitiveType::Long => true, + _ => false, + } + } +} + +#[derive(Debug, Clone)] +struct Store { + map: HashMap, +} +impl Store { + fn new() -> Self { + Store { map: HashMap::new() } + } + fn initialize(&mut self, h: &Heap, var: VariableId, value: Value) { + // Ensure value is compatible with type of variable + let the_type = h[var].the_type(h); + assert!(value.is_type_compatible(the_type)); + // Overwrite mapping + self.map.insert(var, value.clone()); + } + fn update(&mut self, h: &Heap, ctx: &mut EvalContext, lexpr: ExpressionId, value: Value) -> EvalResult { + match &h[lexpr] { + Expression::Variable(var) => { + let var = var.declaration.unwrap(); + // Ensure value is compatible with type of variable + let the_type = h[var].the_type(h); + assert!(value.is_type_compatible(the_type)); + // Overwrite mapping + self.map.insert(var, value.clone()); + Ok(value) + } + Expression::Indexing(indexing) => { + // Evaluate index expression, which must be some integral type + let index = self.eval(h, ctx, indexing.index)?; + // Mutable reference to the subject + let subject; + match &h[indexing.subject] { + Expression::Variable(var) => { + let var = var.declaration.unwrap(); + subject = self.map.get_mut(&var).unwrap(); + } + _ => unreachable!(), + } + match subject.set(&index, &value) { + Some(value) => Ok(value), + None => Err(EvalContinuation::Inconsistent) + } + } + _ => unimplemented!("{:?}", h[lexpr]), + } + } + fn get(&mut self, h: &Heap, rexpr: ExpressionId) -> EvalResult { + match &h[rexpr] { + Expression::Variable(var) => { + let var = var.declaration.unwrap(); + let value = self.map.get(&var).unwrap(); + Ok(value.clone()) + } + _ => unimplemented!("{:?}", h[rexpr]), + } + } + fn eval(&mut self, h: &Heap, ctx: &mut EvalContext, expr: ExpressionId) -> EvalResult { + match &h[expr] { + Expression::Assignment(expr) => { + let value = self.eval(h, ctx, expr.right)?; + match expr.operation { + AssignmentOperator::Set => { + self.update(h, ctx, expr.left, value.clone()); + } + AssignmentOperator::Added => { + let old = self.get(h, expr.left)?; + self.update(h, ctx, expr.left, old.plus(&value)); + } + AssignmentOperator::Subtracted => { + let old = self.get(h, expr.left)?; + self.update(h, ctx, expr.left, old.minus(&value)); + } + _ => unimplemented!("{:?}", expr), + } + Ok(value) + } + Expression::Conditional(expr) => { + let test = self.eval(h, ctx, expr.test)?; + if test.as_boolean().0 { + self.eval(h, ctx, expr.true_expression) + } else { + self.eval(h, ctx, expr.false_expression) + } + } + Expression::Binary(expr) => { + let left = self.eval(h, ctx, expr.left)?; + let right = self.eval(h, ctx,expr.right)?; + match expr.operation { + BinaryOperator::Equality => Ok(left.eq(&right)), + BinaryOperator::Inequality => Ok(left.neq(&right)), + BinaryOperator::LessThan => Ok(left.lt(&right)), + BinaryOperator::LessThanEqual => Ok(left.lte(&right)), + BinaryOperator::GreaterThan => Ok(left.gt(&right)), + BinaryOperator::GreaterThanEqual => Ok(left.gte(&right)), + BinaryOperator::Remainder => Ok(left.modulus(&right)), + _ => unimplemented!(), + } + } + Expression::Unary(expr) => { + let mut value = self.eval(h, ctx, expr.expression)?; + match expr.operation { + UnaryOperation::PostIncrement => { + self.update(h, ctx, expr.expression, value.plus(&ONE)); + } + UnaryOperation::PreIncrement => { + value = value.plus(&ONE); + self.update(h, ctx, expr.expression, value.clone()); + } + UnaryOperation::PostDecrement => { + self.update(h, ctx, expr.expression, value.minus(&ONE)); + } + UnaryOperation::PreDecrement => { + value = value.minus(&ONE); + self.update(h, ctx, expr.expression, value.clone()); + } + _ => unimplemented!(), + } + Ok(value) + } + Expression::Indexing(expr) => self.get(h, expr.this.upcast()), + Expression::Slicing(expr) => unimplemented!(), + Expression::Select(expr) => self.get(h, expr.this.upcast()), + Expression::Array(expr) => unimplemented!(), + Expression::Constant(expr) => Ok(Value::from_constant(&expr.value)), + Expression::Call(expr) => { + match expr.method { + Method::Create => { + assert_eq!(1, expr.arguments.len()); + let length = self.eval(h, ctx, expr.arguments[0])?; + Ok(Value::create_message(length)) + } + Method::Fires => { + assert_eq!(1, expr.arguments.len()); + let value = self.eval(h, ctx, expr.arguments[0])?; + match ctx.fires(value.clone()) { + None => Err(EvalContinuation::BlockFires(value)), + Some(result) => Ok(result), + } + } + Method::Get => { + assert_eq!(1, expr.arguments.len()); + let value = self.eval(h, ctx, expr.arguments[0])?; + match ctx.get(value.clone()) { + None => Err(EvalContinuation::BlockGet(value)), + Some(result) => Ok(result) + } + } + Method::Symbolic(symbol) => unimplemented!() + } + } + Expression::Variable(expr) => self.get(h, expr.this.upcast()), + } + } +} + +type EvalResult = Result; +pub enum EvalContinuation { + Stepping, + Inconsistent, + Terminal, + SyncBlockStart, + SyncBlockEnd, + NewComponent(Vec), + BlockFires(Value), + BlockGet(Value), + Put(Value, Value), +} + +#[derive(Debug, Clone)] +pub struct Prompt { + definition: DefinitionId, + store: Store, + position: Option, +} + +impl Prompt { + pub fn new(h: &Heap, def: DefinitionId, args: &Vec) -> Self { + let mut prompt = Prompt { + definition: def, + store: Store::new(), + position: Some((&h[def]).body()) + }; + prompt.set_arguments(h, args); + prompt + } + fn set_arguments(&mut self, h: &Heap, args: &Vec) { + let def = &h[self.definition]; + let params = def.parameters(); + assert_eq!(params.len(), args.len()); + for (param, value) in params.iter().zip(args.iter()) { + let hparam = &h[*param]; + let type_annot = &h[hparam.type_annotation]; + assert!(value.is_type_compatible(&type_annot.the_type)); + self.store.initialize(h, param.upcast(), value.clone()); + } + } + pub fn step(&mut self, h: &Heap, ctx: &mut EvalContext) -> EvalResult { + if let Some(stmt) = self.position { + let stmt = &h[stmt]; + match stmt { + Statement::Block(stmt) => { + // Continue to first statement + self.position = Some(stmt.first()); + Err(EvalContinuation::Stepping) + } + Statement::Local(stmt) => { + match stmt { + LocalStatement::Memory(stmt) => { + // Evaluate initial expression + let value = self.store.eval(h, ctx, stmt.initial)?; + // Update store + self.store.initialize(h, stmt.variable.upcast(), value); + } + LocalStatement::Channel(stmt) => unimplemented!(), + } + // Continue to next statement + self.position = stmt.next(); + Err(EvalContinuation::Stepping) + } + Statement::Skip(stmt) => { + // Continue to next statement + self.position = stmt.next; + Err(EvalContinuation::Stepping) + } + Statement::Labeled(stmt) => { + // Continue to next statement + self.position = Some(stmt.body); + Err(EvalContinuation::Stepping) + } + Statement::If(stmt) => { + // Evaluate test + let value = self.store.eval(h, ctx, stmt.test)?; + // Continue with either branch + if value.as_boolean().0 { + self.position = Some(stmt.true_body); + } else { + self.position = Some(stmt.false_body); + } + Err(EvalContinuation::Stepping) + } + Statement::EndIf(stmt) => { + // Continue to next statement + self.position = stmt.next; + Err(EvalContinuation::Stepping) + } + Statement::While(stmt) => { + // Evaluate test + let value = self.store.eval(h, ctx, stmt.test)?; + // Either continue with body, or go to next + if value.as_boolean().0 { + self.position = Some(stmt.body); + } else { + self.position = stmt.next.map(|x| x.upcast()); + } + Err(EvalContinuation::Stepping) + } + Statement::EndWhile(stmt) => { + // Continue to next statement + self.position = stmt.next; + Err(EvalContinuation::Stepping) + } + Statement::Synchronous(stmt) => { + // Continue to next statement, and signal upward + self.position = Some(stmt.body); + Err(EvalContinuation::SyncBlockStart) + } + Statement::EndSynchronous(stmt) => { + // Continue to next statement, and signal upward + self.position = stmt.next; + Err(EvalContinuation::SyncBlockEnd) + } + Statement::Return(stmt) => { + // Evaluate expression + let value = self.store.eval(h, ctx, stmt.expression)?; + // Done with evaluation + Ok(value) + } + Statement::Goto(stmt) => { + // Continue to target + self.position = stmt.target.map(|x| x.upcast()); + Err(EvalContinuation::Stepping) + } + Statement::New(stmt) => todo!(), + Statement::Put(stmt) => { + // Evaluate port and message + let port = self.store.eval(h, ctx, stmt.port)?; + let message = self.store.eval(h, ctx, stmt.message)?; + // Continue to next statement + self.position = stmt.next; + // Signal the put upwards + Err(EvalContinuation::Put(port, message)) + } + Statement::Expression(stmt) => { + // Evaluate expression + let value = self.store.eval(h, ctx, stmt.expression)?; + // Continue to next statement + self.position = stmt.next; + Err(EvalContinuation::Stepping) + } + _ => unimplemented!("{:?}", stmt), + } + } else { + Err(EvalContinuation::Terminal) + } + } + fn compute_function(h: &Heap, fun: FunctionId, args: &Vec) -> Option { + let mut prompt = Self::new(h, fun.upcast(), args); + let mut context = EvalContext::None; + loop { + let result = prompt.step(h, &mut context); + match result { + Ok(val) => return Some(val), + Err(cont) => match cont { + EvalContinuation::Stepping => continue, + EvalContinuation::Inconsistent => return None, + // Functions never terminate without returning + EvalContinuation::Terminal => unreachable!(), + // Functions never encounter any blocking behavior + EvalContinuation::SyncBlockStart => unreachable!(), + EvalContinuation::SyncBlockEnd => unreachable!(), + EvalContinuation::NewComponent(args) => unreachable!(), + EvalContinuation::BlockFires(val) => unreachable!(), + EvalContinuation::BlockGet(val) => unreachable!(), + EvalContinuation::Put(port, msg) => unreachable!() + } + } + } + } +} + +#[cfg(test)] +mod tests { + extern crate test_generator; + + use std::fs::File; + use std::io::Read; + use std::path::Path; + use test_generator::test_resources; + + use super::*; + + #[test_resources("testdata/eval/positive/*.pdl")] + fn batch1(resource: &str) { + let path = Path::new(resource); + let expect = path.with_extension("txt"); + let mut heap = Heap::new(); + let mut source = InputSource::from_file(&path).unwrap(); + let mut parser = Parser::new(&mut source); + let pd = parser.parse(&mut heap).unwrap(); + let test = heap.get_external_identifier(b"test"); + let def = heap[pd].get_definition(&heap, test.upcast()).unwrap(); + let fun = heap[def].as_function().this; + let args = Vec::new(); + let result = Prompt::compute_function(&heap, fun, &args).unwrap(); + let valstr: String = format!("{}", result); + println!("{}", valstr); + + let mut cev: Vec = Vec::new(); + let mut f = File::open(expect).unwrap(); + f.read_to_end(&mut cev).unwrap(); + let lavstr = String::from_utf8_lossy(&cev); + println!("{}", lavstr); + + assert_eq!(valstr, lavstr); + } +} diff --git a/src/protocol/inputsource.rs b/src/protocol/inputsource.rs new file mode 100644 index 0000000000000000000000000000000000000000..06cfb95660242428ee94d8e17847f1909e55e0dd --- /dev/null +++ b/src/protocol/inputsource.rs @@ -0,0 +1,379 @@ +use std::fmt; +use std::fs::File; +use std::io; +use std::path::Path; + +use backtrace::Backtrace; + +#[derive(Clone)] +pub struct InputSource { + filename: String, + input: Vec, + line: usize, + column: usize, + offset: usize, +} + +impl InputSource { + // Constructors + pub fn new(filename: S, reader: &mut A) -> io::Result { + let mut vec = Vec::new(); + reader.read_to_end(&mut vec)?; + Ok(InputSource { + filename: filename.to_string(), + input: vec, + line: 1, + column: 1, + offset: 0, + }) + } + // Constructor helpers + pub fn from_file(path: &Path) -> io::Result { + let filename = path.file_name(); + match filename { + Some(filename) => { + let mut f = File::open(path)?; + InputSource::new(filename.to_string_lossy(), &mut f) + } + None => Err(io::Error::new(io::ErrorKind::NotFound, "Invalid path")), + } + } + pub fn from_string(string: &str) -> io::Result { + let buffer = Box::new(string); + let mut bytes = buffer.as_bytes(); + InputSource::new(String::new(), &mut bytes) + } + pub fn from_buffer(buffer: &[u8]) -> io::Result { + InputSource::new(String::new(), &mut Box::new(buffer)) + } + // Internal methods + pub fn pos(&self) -> InputPosition { + InputPosition { line: self.line, column: self.column, offset: self.offset } + } + pub fn error(&self, message: S) -> ParseError { + self.pos().parse_error(message) + } + pub fn is_eof(&self) -> bool { + self.next() == None + } + pub fn next(&self) -> Option { + if self.offset < self.input.len() { + Some((*self.input)[self.offset]) + } else { + None + } + } + pub fn lookahead(&self, pos: usize) -> Option { + if let Some(x) = usize::checked_add(self.offset, pos) { + if x < self.input.len() { + return Some((*self.input)[x]); + } + } + None + } + pub fn consume(&mut self) { + match self.next() { + Some(x) if x == b'\r' && self.lookahead(1) != Some(b'\n') || x == b'\n' => { + self.line += 1; + self.offset += 1; + self.column = 1; + } + Some(_) => { + self.offset += 1; + self.column += 1; + } + None => {} + } + } +} + +impl fmt::Display for InputSource { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + self.pos().fmt(f) + } +} + +#[derive(Debug, Clone, Copy, Default)] +pub struct InputPosition { + line: usize, + column: usize, + offset: usize, +} + +impl InputPosition { + fn context<'a>(&self, source: &'a InputSource) -> &'a [u8] { + let start = self.offset - (self.column - 1); + let mut end = self.offset; + while end < source.input.len() { + let cur = (*source.input)[end]; + if cur == b'\n' || cur == b'\r' { + break; + } + end += 1; + } + &source.input[start..end] + } + fn parse_error(&self, message: S) -> ParseError { + ParseError { position: *self, message: message.to_string(), backtrace: Backtrace::new() } + } + fn eval_error(&self, message: S) -> EvalError { + EvalError { position: *self, message: message.to_string(), backtrace: Backtrace::new() } + } +} + +impl fmt::Display for InputPosition { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + write!(f, "{}:{}", self.line, self.column) + } +} + +pub trait SyntaxElement { + fn position(&self) -> InputPosition; + fn error(&self, message: S) -> EvalError { + self.position().eval_error(message) + } +} + +#[derive(Debug, Clone)] +pub struct ParseError { + position: InputPosition, + message: String, + backtrace: Backtrace, +} + +impl ParseError { + pub fn new(position: InputPosition, message: S) -> ParseError { + ParseError { position, message: message.to_string(), backtrace: Backtrace::new() } + } + // Diagnostic methods + pub fn write(&self, source: &InputSource, writer: &mut A) -> io::Result<()> { + if !source.filename.is_empty() { + writeln!( + writer, + "Parse error at {}:{}: {}", + source.filename, self.position, self.message + )?; + } else { + writeln!(writer, "Parse error at {}: {}", self.position, self.message)?; + } + let line = self.position.context(source); + writeln!(writer, "{}", String::from_utf8_lossy(line))?; + let mut arrow: Vec = Vec::new(); + for pos in 1..self.position.column { + let c = line[pos - 1]; + if c == b'\t' { + arrow.push(b'\t') + } else { + arrow.push(b' ') + } + } + arrow.push(b'^'); + writeln!(writer, "{}", String::from_utf8_lossy(&arrow)) + } + pub fn print(&self, source: &InputSource) { + self.write(source, &mut std::io::stdout()).unwrap() + } + pub fn display<'a>(&'a self, source: &'a InputSource) -> DisplayParseError<'a> { + DisplayParseError::new(self, source) + } +} + +impl From for io::Error { + fn from(_: ParseError) -> io::Error { + io::Error::new(io::ErrorKind::InvalidInput, "parse error") + } +} + +#[derive(Clone, Copy)] +pub struct DisplayParseError<'a> { + error: &'a ParseError, + source: &'a InputSource, +} + +impl DisplayParseError<'_> { + fn new<'a>(error: &'a ParseError, source: &'a InputSource) -> DisplayParseError<'a> { + DisplayParseError { error, source } + } +} + +impl fmt::Display for DisplayParseError<'_> { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + let mut vec: Vec = Vec::new(); + match self.error.write(self.source, &mut vec) { + Err(_) => { + return fmt::Result::Err(fmt::Error); + } + Ok(_) => {} + } + write!(f, "{}", String::from_utf8_lossy(&vec)) + } +} + +#[derive(Debug, Clone)] +pub struct EvalError { + position: InputPosition, + message: String, + backtrace: Backtrace, +} + +impl EvalError { + pub fn new(position: InputPosition, message: S) -> EvalError { + EvalError { position, message: message.to_string(), backtrace: Backtrace::new() } + } + // Diagnostic methods + pub fn write(&self, source: &InputSource, writer: &mut A) -> io::Result<()> { + if !source.filename.is_empty() { + writeln!( + writer, + "Evaluation error at {}:{}: {}", + source.filename, self.position, self.message + )?; + } else { + writeln!(writer, "Evaluation error at {}: {}", self.position, self.message)?; + } + let line = self.position.context(source); + writeln!(writer, "{}", String::from_utf8_lossy(line))?; + let mut arrow: Vec = Vec::new(); + for pos in 1..self.position.column { + let c = line[pos - 1]; + if c == b'\t' { + arrow.push(b'\t') + } else { + arrow.push(b' ') + } + } + arrow.push(b'^'); + writeln!(writer, "{}", String::from_utf8_lossy(&arrow)) + } + pub fn print(&self, source: &InputSource) { + self.write(source, &mut std::io::stdout()).unwrap() + } + pub fn display<'a>(&'a self, source: &'a InputSource) -> DisplayEvalError<'a> { + DisplayEvalError::new(self, source) + } +} + +impl From for io::Error { + fn from(_: EvalError) -> io::Error { + io::Error::new(io::ErrorKind::InvalidInput, "eval error") + } +} + +#[derive(Clone, Copy)] +pub struct DisplayEvalError<'a> { + error: &'a EvalError, + source: &'a InputSource, +} + +impl DisplayEvalError<'_> { + fn new<'a>(error: &'a EvalError, source: &'a InputSource) -> DisplayEvalError<'a> { + DisplayEvalError { error, source } + } +} + +impl fmt::Display for DisplayEvalError<'_> { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + let mut vec: Vec = Vec::new(); + match self.error.write(self.source, &mut vec) { + Err(_) => { + return fmt::Result::Err(fmt::Error); + } + Ok(_) => {} + } + write!(f, "{}", String::from_utf8_lossy(&vec)) + } +} + +#[cfg(test)] +mod tests { + use super::*; + + #[test] + fn test_from_string() { + let mut is = InputSource::from_string("#version 100\n").unwrap(); + assert!(is.input.len() == 13); + assert!(is.line == 1); + assert!(is.column == 1); + assert!(is.offset == 0); + let ps = is.pos(); + assert!(ps.line == 1); + assert!(ps.column == 1); + assert!(ps.offset == 0); + assert!(is.next() == Some(b'#')); + is.consume(); + assert!(is.next() == Some(b'v')); + assert!(is.lookahead(1) == Some(b'e')); + is.consume(); + assert!(is.next() == Some(b'e')); + is.consume(); + assert!(is.next() == Some(b'r')); + is.consume(); + assert!(is.next() == Some(b's')); + is.consume(); + assert!(is.next() == Some(b'i')); + is.consume(); + { + let ps = is.pos(); + assert_eq!(b"#version 100", ps.context(&is)); + let er = is.error("hello world!"); + let mut vec: Vec = Vec::new(); + er.write(&is, &mut vec).unwrap(); + assert_eq!( + "Parse error at 1:7: hello world!\n#version 100\n ^\n", + String::from_utf8_lossy(&vec) + ); + } + assert!(is.next() == Some(b'o')); + is.consume(); + assert!(is.next() == Some(b'n')); + is.consume(); + assert!(is.input.len() == 13); + assert!(is.line == 1); + assert!(is.column == 9); + assert!(is.offset == 8); + assert!(is.next() == Some(b' ')); + is.consume(); + assert!(is.next() == Some(b'1')); + is.consume(); + assert!(is.next() == Some(b'0')); + is.consume(); + assert!(is.next() == Some(b'0')); + is.consume(); + assert!(is.input.len() == 13); + assert!(is.line == 1); + assert!(is.column == 13); + assert!(is.offset == 12); + assert!(is.next() == Some(b'\n')); + is.consume(); + assert!(is.input.len() == 13); + assert!(is.line == 2); + assert!(is.column == 1); + assert!(is.offset == 13); + { + let ps = is.pos(); + assert_eq!(b"", ps.context(&is)); + } + assert!(is.next() == None); + is.consume(); + assert!(is.next() == None); + } + + #[test] + fn test_split() { + let mut is = InputSource::from_string("#version 100\n").unwrap(); + let backup = is.clone(); + assert!(is.next() == Some(b'#')); + is.consume(); + assert!(is.next() == Some(b'v')); + is.consume(); + assert!(is.next() == Some(b'e')); + is.consume(); + is = backup; + assert!(is.next() == Some(b'#')); + is.consume(); + assert!(is.next() == Some(b'v')); + is.consume(); + assert!(is.next() == Some(b'e')); + is.consume(); + } +} diff --git a/src/protocol/lexer.rs b/src/protocol/lexer.rs new file mode 100644 index 0000000000000000000000000000000000000000..48caa4716e6ca486c2436181f3453cfcb8ae7396 --- /dev/null +++ b/src/protocol/lexer.rs @@ -0,0 +1,1782 @@ +use crate::protocol::ast::*; +use crate::protocol::inputsource::*; + +const MAX_LEVEL: usize = 128; + +fn is_vchar(x: Option) -> bool { + if let Some(c) = x { + c >= 0x21 && c <= 0x7E + } else { + false + } +} + +fn is_wsp(x: Option) -> bool { + if let Some(c) = x { + c == b' ' || c == b'\t' + } else { + false + } +} + +fn is_ident_start(x: Option) -> bool { + if let Some(c) = x { + c >= b'A' && c <= b'Z' || c >= b'a' && c <= b'z' + } else { + false + } +} + +fn is_ident_rest(x: Option) -> bool { + if let Some(c) = x { + c >= b'A' && c <= b'Z' || c >= b'a' && c <= b'z' || c >= b'0' && c <= b'9' || c == b'_' + } else { + false + } +} + +fn is_constant(x: Option) -> bool { + if let Some(c) = x { + c >= b'0' && c <= b'9' || c == b'\'' + } else { + false + } +} + +fn is_integer_start(x: Option) -> bool { + if let Some(c) = x { + c >= b'0' && c <= b'9' + } else { + false + } +} + +fn is_integer_rest(x: Option) -> bool { + if let Some(c) = x { + c >= b'0' && c <= b'9' + || c >= b'a' && c <= b'f' + || c >= b'A' && c <= b'F' + || c == b'x' + || c == b'X' + } else { + false + } +} + +fn lowercase(x: u8) -> u8 { + if x >= b'A' && x <= b'Z' { + x - b'A' + b'a' + } else { + x + } +} + +pub struct Lexer<'a> { + source: &'a mut InputSource, + level: usize, +} + +impl Lexer<'_> { + pub fn new(source: &mut InputSource) -> Lexer { + Lexer { source, level: 0 } + } + fn consume_line(&mut self) -> Result, ParseError> { + let mut result: Vec = Vec::new(); + let mut next = self.source.next(); + while next.is_some() && next != Some(b'\n') && next != Some(b'\r') { + if !(is_vchar(next) || is_wsp(next)) { + return Err(self.source.error("Expected visible character or whitespace")); + } + result.push(next.unwrap()); + self.source.consume(); + next = self.source.next(); + } + if next.is_some() { + self.source.consume(); + } + if next == Some(b'\r') && self.source.next() == Some(b'\n') { + self.source.consume(); + } + Ok(result) + } + fn consume_whitespace(&mut self, expected: bool) -> Result<(), ParseError> { + let mut found = false; + let mut next = self.source.next(); + while next.is_some() { + if next == Some(b' ') + || next == Some(b'\t') + || next == Some(b'\r') + || next == Some(b'\n') + { + self.source.consume(); + next = self.source.next(); + found = true; + continue; + } + if next == Some(b'/') { + next = self.source.lookahead(1); + if next == Some(b'/') { + self.source.consume(); // slash + self.source.consume(); // slash + self.consume_line()?; + next = self.source.next(); + found = true; + continue; + } + if next == Some(b'*') { + self.source.consume(); // slash + self.source.consume(); // star + next = self.source.next(); + while next.is_some() { + if next == Some(b'*') { + next = self.source.lookahead(1); + if next == Some(b'/') { + self.source.consume(); // star + self.source.consume(); // slash + break; + } + } + self.source.consume(); + next = self.source.next(); + } + next = self.source.next(); + found = true; + continue; + } + } + break; + } + if expected && !found { + Err(self.source.error("Expected whitespace")) + } else { + Ok(()) + } + } + fn has_keyword(&self, keyword: &[u8]) -> bool { + let len = keyword.len(); + for i in 0..len { + let expected = Some(lowercase(keyword[i])); + let next = self.source.lookahead(i).map(lowercase); + if next != expected { + return false; + } + } + // Word boundary + if let Some(next) = self.source.lookahead(len) { + !(next >= b'A' && next <= b'Z' || next >= b'a' && next <= b'z') + } else { + true + } + } + fn consume_keyword(&mut self, keyword: &[u8]) -> Result<(), ParseError> { + let len = keyword.len(); + for i in 0..len { + let expected = Some(lowercase(keyword[i])); + let next = self.source.next(); + if next != expected { + return Err(self + .source + .error(format!("Expected keyword: {}", String::from_utf8_lossy(keyword)))); + } + self.source.consume(); + } + if let Some(next) = self.source.next() { + if next >= b'A' && next <= b'Z' || next >= b'a' && next <= b'z' { + return Err(self.source.error(format!( + "Expected word boundary after keyword: {}", + String::from_utf8_lossy(keyword) + ))); + } + } + Ok(()) + } + fn has_string(&self, string: &[u8]) -> bool { + let len = string.len(); + for i in 0..len { + let expected = Some(string[i]); + let next = self.source.lookahead(i); + if next != expected { + return false; + } + } + true + } + fn consume_string(&mut self, string: &[u8]) -> Result<(), ParseError> { + let len = string.len(); + for i in 0..len { + let expected = Some(string[i]); + let next = self.source.next(); + if next != expected { + return Err(self + .source + .error(format!("Expected {}", String::from_utf8_lossy(string)))); + } + self.source.consume(); + } + Ok(()) + } + fn consume_ident(&mut self) -> Result, ParseError> { + if !self.has_identifier() { + return Err(self.source.error("Expected identifier")); + } + let mut result = Vec::new(); + let mut next = self.source.next(); + result.push(next.unwrap()); + self.source.consume(); + next = self.source.next(); + while is_ident_rest(next) { + result.push(next.unwrap()); + self.source.consume(); + next = self.source.next(); + } + Ok(result) + } + + // Statement keywords + + fn has_statement_keyword(&self) -> bool { + self.has_keyword(b"channel") + || self.has_keyword(b"skip") + || self.has_keyword(b"if") + || self.has_keyword(b"while") + || self.has_keyword(b"break") + || self.has_keyword(b"continue") + || self.has_keyword(b"synchronous") + || self.has_keyword(b"return") + || self.has_keyword(b"assert") + || self.has_keyword(b"goto") + || self.has_keyword(b"new") + || self.has_keyword(b"put") + } + fn has_type_keyword(&self) -> bool { + self.has_keyword(b"in") + || self.has_keyword(b"out") + || self.has_keyword(b"msg") + || self.has_keyword(b"boolean") + || self.has_keyword(b"byte") + || self.has_keyword(b"short") + || self.has_keyword(b"int") + || self.has_keyword(b"long") + } + fn has_builtin_keyword(&self) -> bool { + self.has_keyword(b"get") + || self.has_keyword(b"fires") + || self.has_keyword(b"create") + || self.has_keyword(b"length") + } + + // Identifiers + + fn has_identifier(&self) -> bool { + if self.has_statement_keyword() || self.has_type_keyword() || self.has_builtin_keyword() { + return false; + } + let next = self.source.next(); + is_ident_start(next) + } + fn consume_identifier(&mut self, h: &mut Heap) -> Result { + if self.has_statement_keyword() || self.has_type_keyword() || self.has_builtin_keyword() { + return Err(self.source.error("Expected identifier")); + } + let position = self.source.pos(); + let value = self.consume_ident()?; + let id = h.alloc_source_identifier(|this| SourceIdentifier { this, position, value }); + Ok(id) + } + fn consume_identifier_spilled(&mut self) -> Result<(), ParseError> { + if self.has_statement_keyword() || self.has_type_keyword() || self.has_builtin_keyword() { + return Err(self.source.error("Expected identifier")); + } + self.consume_ident()?; + Ok(()) + } + + // Types and type annotations + + fn consume_primitive_type(&mut self) -> Result { + if self.has_keyword(b"in") { + self.consume_keyword(b"in")?; + Ok(PrimitiveType::Input) + } else if self.has_keyword(b"out") { + self.consume_keyword(b"out")?; + Ok(PrimitiveType::Output) + } else if self.has_keyword(b"msg") { + self.consume_keyword(b"msg")?; + Ok(PrimitiveType::Message) + } else if self.has_keyword(b"boolean") { + self.consume_keyword(b"boolean")?; + Ok(PrimitiveType::Boolean) + } else if self.has_keyword(b"byte") { + self.consume_keyword(b"byte")?; + Ok(PrimitiveType::Byte) + } else if self.has_keyword(b"short") { + self.consume_keyword(b"short")?; + Ok(PrimitiveType::Short) + } else if self.has_keyword(b"int") { + self.consume_keyword(b"int")?; + Ok(PrimitiveType::Int) + } else if self.has_keyword(b"long") { + self.consume_keyword(b"long")?; + Ok(PrimitiveType::Long) + } else { + let data = self.consume_ident()?; + Ok(PrimitiveType::Symbolic(data)) + } + } + fn has_array(&mut self) -> bool { + let backup = self.source.clone(); + let mut result = false; + match self.consume_whitespace(false) { + Ok(_) => result = self.has_string(b"["), + Err(_) => {} + } + *self.source = backup; + return result; + } + fn consume_type(&mut self) -> Result { + let primitive = self.consume_primitive_type()?; + let array; + if self.has_array() { + self.consume_string(b"[]")?; + array = true; + } else { + array = false; + } + Ok(Type { primitive, array }) + } + fn create_type_annotation_input(&self, h: &mut Heap) -> Result { + let position = self.source.pos(); + let the_type = Type::INPUT; + let id = h.alloc_type_annotation(|this| TypeAnnotation { this, position, the_type }); + Ok(id) + } + fn create_type_annotation_output(&self, h: &mut Heap) -> Result { + let position = self.source.pos(); + let the_type = Type::OUTPUT; + let id = h.alloc_type_annotation(|this| TypeAnnotation { this, position, the_type }); + Ok(id) + } + fn consume_type_annotation(&mut self, h: &mut Heap) -> Result { + let position = self.source.pos(); + let the_type = self.consume_type()?; + let id = h.alloc_type_annotation(|this| TypeAnnotation { this, position, the_type }); + Ok(id) + } + fn consume_type_annotation_spilled(&mut self) -> Result<(), ParseError> { + self.consume_type()?; + Ok(()) + } + + // Parameters + + fn consume_parameter(&mut self, h: &mut Heap) -> Result { + let position = self.source.pos(); + let type_annotation = self.consume_type_annotation(h)?; + self.consume_whitespace(true)?; + let identifier = self.consume_identifier(h)?; + let id = + h.alloc_parameter(|this| Parameter { this, position, type_annotation, identifier }); + Ok(id) + } + fn consume_parameters( + &mut self, + h: &mut Heap, + params: &mut Vec, + ) -> Result<(), ParseError> { + self.consume_string(b"(")?; + self.consume_whitespace(false)?; + if !self.has_string(b")") { + while self.source.next().is_some() { + params.push(self.consume_parameter(h)?); + self.consume_whitespace(false)?; + if self.has_string(b")") { + break; + } + self.consume_string(b",")?; + self.consume_whitespace(false)?; + } + } + self.consume_string(b")") + } + + // ==================== + // Expressions + // ==================== + + fn consume_paren_expression(&mut self, h: &mut Heap) -> Result { + self.consume_string(b"(")?; + self.consume_whitespace(false)?; + let result = self.consume_expression(h)?; + self.consume_whitespace(false)?; + self.consume_string(b")")?; + Ok(result) + } + fn consume_expression(&mut self, h: &mut Heap) -> Result { + if self.level >= MAX_LEVEL { + return Err(self.source.error("Too deeply nested expression")); + } + self.level += 1; + let result = self.consume_assignment_expression(h); + self.level -= 1; + result + } + fn consume_assignment_expression(&mut self, h: &mut Heap) -> Result { + let result = self.consume_conditional_expression(h)?; + self.consume_whitespace(false)?; + if self.has_assignment_operator() { + let position = self.source.pos(); + let left = result; + let operation = self.consume_assignment_operator()?; + self.consume_whitespace(false)?; + let right = self.consume_expression(h)?; + Ok(h.alloc_assignment_expression(|this| AssignmentExpression { + this, + position, + left, + operation, + right, + }) + .upcast()) + } else { + Ok(result) + } + } + fn has_assignment_operator(&self) -> bool { + self.has_string(b"=") + || self.has_string(b"*=") + || self.has_string(b"/=") + || self.has_string(b"%=") + || self.has_string(b"+=") + || self.has_string(b"-=") + || self.has_string(b"<<=") + || self.has_string(b">>=") + || self.has_string(b"&=") + || self.has_string(b"^=") + || self.has_string(b"|=") + } + fn consume_assignment_operator(&mut self) -> Result { + if self.has_string(b"=") { + self.consume_string(b"=")?; + Ok(AssignmentOperator::Set) + } else if self.has_string(b"*=") { + self.consume_string(b"*=")?; + Ok(AssignmentOperator::Multiplied) + } else if self.has_string(b"/=") { + self.consume_string(b"/=")?; + Ok(AssignmentOperator::Divided) + } else if self.has_string(b"%=") { + self.consume_string(b"%=")?; + Ok(AssignmentOperator::Remained) + } else if self.has_string(b"+=") { + self.consume_string(b"+=")?; + Ok(AssignmentOperator::Added) + } else if self.has_string(b"-=") { + self.consume_string(b"-=")?; + Ok(AssignmentOperator::Subtracted) + } else if self.has_string(b"<<=") { + self.consume_string(b"<<=")?; + Ok(AssignmentOperator::ShiftedLeft) + } else if self.has_string(b">>=") { + self.consume_string(b">>=")?; + Ok(AssignmentOperator::ShiftedRight) + } else if self.has_string(b"&=") { + self.consume_string(b"&=")?; + Ok(AssignmentOperator::BitwiseAnded) + } else if self.has_string(b"^=") { + self.consume_string(b"^=")?; + Ok(AssignmentOperator::BitwiseXored) + } else if self.has_string(b"|=") { + self.consume_string(b"|=")?; + Ok(AssignmentOperator::BitwiseOred) + } else { + Err(self.source.error("Expected assignment operator")) + } + } + fn consume_conditional_expression(&mut self, h: &mut Heap) -> Result { + let result = self.consume_concat_expression(h)?; + self.consume_whitespace(false)?; + if self.has_string(b"?") { + let position = self.source.pos(); + let test = result; + self.consume_string(b"?")?; + self.consume_whitespace(false)?; + let true_expression = self.consume_expression(h)?; + self.consume_whitespace(false)?; + self.consume_string(b":")?; + self.consume_whitespace(false)?; + let false_expression = self.consume_expression(h)?; + Ok(h.alloc_conditional_expression(|this| ConditionalExpression { + this, + position, + test, + true_expression, + false_expression, + }) + .upcast()) + } else { + Ok(result) + } + } + fn consume_concat_expression(&mut self, h: &mut Heap) -> Result { + let mut result = self.consume_lor_expression(h)?; + self.consume_whitespace(false)?; + while self.has_string(b"@") { + let position = self.source.pos(); + let left = result; + self.consume_string(b"@")?; + let operation = BinaryOperator::Concatenate; + self.consume_whitespace(false)?; + let right = self.consume_lor_expression(h)?; + self.consume_whitespace(false)?; + result = h + .alloc_binary_expression(|this| BinaryExpression { + this, + position, + left, + operation, + right, + }) + .upcast(); + } + Ok(result) + } + fn consume_lor_expression(&mut self, h: &mut Heap) -> Result { + let mut result = self.consume_land_expression(h)?; + self.consume_whitespace(false)?; + while self.has_string(b"||") { + let position = self.source.pos(); + let left = result; + self.consume_string(b"||")?; + let operation = BinaryOperator::LogicalOr; + self.consume_whitespace(false)?; + let right = self.consume_land_expression(h)?; + self.consume_whitespace(false)?; + result = h + .alloc_binary_expression(|this| BinaryExpression { + this, + position, + left, + operation, + right, + }) + .upcast(); + } + Ok(result) + } + fn consume_land_expression(&mut self, h: &mut Heap) -> Result { + let mut result = self.consume_bor_expression(h)?; + self.consume_whitespace(false)?; + while self.has_string(b"&&") { + let position = self.source.pos(); + let left = result; + self.consume_string(b"&&")?; + let operation = BinaryOperator::LogicalAnd; + self.consume_whitespace(false)?; + let right = self.consume_bor_expression(h)?; + self.consume_whitespace(false)?; + result = h + .alloc_binary_expression(|this| BinaryExpression { + this, + position, + left, + operation, + right, + }) + .upcast(); + } + Ok(result) + } + fn consume_bor_expression(&mut self, h: &mut Heap) -> Result { + let mut result = self.consume_xor_expression(h)?; + self.consume_whitespace(false)?; + while self.has_string(b"|") && !self.has_string(b"||") && !self.has_string(b"|=") { + let position = self.source.pos(); + let left = result; + self.consume_string(b"|")?; + let operation = BinaryOperator::BitwiseOr; + self.consume_whitespace(false)?; + let right = self.consume_xor_expression(h)?; + self.consume_whitespace(false)?; + result = h + .alloc_binary_expression(|this| BinaryExpression { + this, + position, + left, + operation, + right, + }) + .upcast(); + } + Ok(result) + } + fn consume_xor_expression(&mut self, h: &mut Heap) -> Result { + let mut result = self.consume_band_expression(h)?; + self.consume_whitespace(false)?; + while self.has_string(b"^") && !self.has_string(b"^=") { + let position = self.source.pos(); + let left = result; + self.consume_string(b"^")?; + let operation = BinaryOperator::BitwiseXor; + self.consume_whitespace(false)?; + let right = self.consume_band_expression(h)?; + self.consume_whitespace(false)?; + result = h + .alloc_binary_expression(|this| BinaryExpression { + this, + position, + left, + operation, + right, + }) + .upcast(); + } + Ok(result) + } + fn consume_band_expression(&mut self, h: &mut Heap) -> Result { + let mut result = self.consume_eq_expression(h)?; + self.consume_whitespace(false)?; + while self.has_string(b"&") && !self.has_string(b"&&") && !self.has_string(b"&=") { + let position = self.source.pos(); + let left = result; + self.consume_string(b"&")?; + let operation = BinaryOperator::BitwiseAnd; + self.consume_whitespace(false)?; + let right = self.consume_eq_expression(h)?; + self.consume_whitespace(false)?; + result = h + .alloc_binary_expression(|this| BinaryExpression { + this, + position, + left, + operation, + right, + }) + .upcast(); + } + Ok(result) + } + fn consume_eq_expression(&mut self, h: &mut Heap) -> Result { + let mut result = self.consume_rel_expression(h)?; + self.consume_whitespace(false)?; + while self.has_string(b"==") || self.has_string(b"!=") { + let position = self.source.pos(); + let left = result; + let operation; + if self.has_string(b"==") { + self.consume_string(b"==")?; + operation = BinaryOperator::Equality; + } else { + self.consume_string(b"!=")?; + operation = BinaryOperator::Inequality; + } + self.consume_whitespace(false)?; + let right = self.consume_rel_expression(h)?; + self.consume_whitespace(false)?; + result = h + .alloc_binary_expression(|this| BinaryExpression { + this, + position, + left, + operation, + right, + }) + .upcast(); + } + Ok(result) + } + fn consume_rel_expression(&mut self, h: &mut Heap) -> Result { + let mut result = self.consume_shift_expression(h)?; + self.consume_whitespace(false)?; + while self.has_string(b"<=") + || self.has_string(b">=") + || self.has_string(b"<") && !self.has_string(b"<<=") + || self.has_string(b">") && !self.has_string(b">>=") + { + let position = self.source.pos(); + let left = result; + let operation; + if self.has_string(b"<=") { + self.consume_string(b"<=")?; + operation = BinaryOperator::LessThanEqual; + } else if self.has_string(b">=") { + self.consume_string(b">=")?; + operation = BinaryOperator::GreaterThanEqual; + } else if self.has_string(b"<") { + self.consume_string(b"<")?; + operation = BinaryOperator::LessThan; + } else { + self.consume_string(b">")?; + operation = BinaryOperator::GreaterThan; + } + self.consume_whitespace(false)?; + let right = self.consume_shift_expression(h)?; + self.consume_whitespace(false)?; + result = h + .alloc_binary_expression(|this| BinaryExpression { + this, + position, + left, + operation, + right, + }) + .upcast(); + } + Ok(result) + } + fn consume_shift_expression(&mut self, h: &mut Heap) -> Result { + let mut result = self.consume_add_expression(h)?; + self.consume_whitespace(false)?; + while self.has_string(b"<<") && !self.has_string(b"<<=") + || self.has_string(b">>") && !self.has_string(b">>=") + { + let position = self.source.pos(); + let left = result; + let operation; + if self.has_string(b"<<") { + self.consume_string(b"<<")?; + operation = BinaryOperator::ShiftLeft; + } else { + self.consume_string(b">>")?; + operation = BinaryOperator::ShiftRight; + } + self.consume_whitespace(false)?; + let right = self.consume_add_expression(h)?; + self.consume_whitespace(false)?; + result = h + .alloc_binary_expression(|this| BinaryExpression { + this, + position, + left, + operation, + right, + }) + .upcast(); + } + Ok(result) + } + fn consume_add_expression(&mut self, h: &mut Heap) -> Result { + let mut result = self.consume_mul_expression(h)?; + self.consume_whitespace(false)?; + while self.has_string(b"+") && !self.has_string(b"+=") + || self.has_string(b"-") && !self.has_string(b"-=") + { + let position = self.source.pos(); + let left = result; + let operation; + if self.has_string(b"+") { + self.consume_string(b"+")?; + operation = BinaryOperator::Add; + } else { + self.consume_string(b"-")?; + operation = BinaryOperator::Subtract; + } + self.consume_whitespace(false)?; + let right = self.consume_mul_expression(h)?; + self.consume_whitespace(false)?; + result = h + .alloc_binary_expression(|this| BinaryExpression { + this, + position, + left, + operation, + right, + }) + .upcast(); + } + Ok(result) + } + fn consume_mul_expression(&mut self, h: &mut Heap) -> Result { + let mut result = self.consume_prefix_expression(h)?; + self.consume_whitespace(false)?; + while self.has_string(b"*") && !self.has_string(b"*=") + || self.has_string(b"/") && !self.has_string(b"/=") + || self.has_string(b"%") && !self.has_string(b"%=") + { + let position = self.source.pos(); + let left = result; + let operation; + if self.has_string(b"*") { + self.consume_string(b"*")?; + operation = BinaryOperator::Multiply; + } else if self.has_string(b"/") { + self.consume_string(b"/")?; + operation = BinaryOperator::Divide; + } else { + self.consume_string(b"%")?; + operation = BinaryOperator::Remainder; + } + self.consume_whitespace(false)?; + let right = self.consume_prefix_expression(h)?; + self.consume_whitespace(false)?; + result = h + .alloc_binary_expression(|this| BinaryExpression { + this, + position, + left, + operation, + right, + }) + .upcast(); + } + Ok(result) + } + fn consume_prefix_expression(&mut self, h: &mut Heap) -> Result { + if self.has_string(b"+") + || self.has_string(b"-") + || self.has_string(b"~") + || self.has_string(b"!") + { + let position = self.source.pos(); + let operation; + if self.has_string(b"+") { + self.consume_string(b"+")?; + if self.has_string(b"+") { + self.consume_string(b"+")?; + operation = UnaryOperation::PreIncrement; + } else { + operation = UnaryOperation::Positive; + } + } else if self.has_string(b"-") { + self.consume_string(b"-")?; + if self.has_string(b"-") { + self.consume_string(b"-")?; + operation = UnaryOperation::PreDecrement; + } else { + operation = UnaryOperation::Negative; + } + } else if self.has_string(b"~") { + self.consume_string(b"~")?; + operation = UnaryOperation::BitwiseNot; + } else { + self.consume_string(b"!")?; + operation = UnaryOperation::LogicalNot; + } + self.consume_whitespace(false)?; + if self.level >= MAX_LEVEL { + return Err(self.source.error("Too deeply nested expression")); + } + self.level += 1; + let result = self.consume_prefix_expression(h); + self.level -= 1; + let expression = result?; + return Ok(h + .alloc_unary_expression(|this| UnaryExpression { + this, + position, + operation, + expression, + }) + .upcast()); + } + self.consume_postfix_expression(h) + } + fn consume_postfix_expression(&mut self, h: &mut Heap) -> Result { + let mut result = self.consume_primary_expression(h)?; + self.consume_whitespace(false)?; + while self.has_string(b"++") + || self.has_string(b"--") + || self.has_string(b"[") + || (self.has_string(b".") && !self.has_string(b"..")) + { + let mut position = self.source.pos(); + if self.has_string(b"++") { + self.consume_string(b"++")?; + let operation = UnaryOperation::PostIncrement; + let expression = result; + self.consume_whitespace(false)?; + result = h + .alloc_unary_expression(|this| UnaryExpression { + this, + position, + operation, + expression, + }) + .upcast(); + } else if self.has_string(b"--") { + self.consume_string(b"--")?; + let operation = UnaryOperation::PostDecrement; + let expression = result; + self.consume_whitespace(false)?; + result = h + .alloc_unary_expression(|this| UnaryExpression { + this, + position, + operation, + expression, + }) + .upcast(); + } else if self.has_string(b"[") { + self.consume_string(b"[")?; + self.consume_whitespace(false)?; + let subject = result; + let index = self.consume_expression(h)?; + self.consume_whitespace(false)?; + if self.has_string(b"..") || self.has_string(b":") { + position = self.source.pos(); + if self.has_string(b"..") { + self.consume_string(b"..")?; + } else { + self.consume_string(b":")?; + } + self.consume_whitespace(false)?; + let to_index = self.consume_expression(h)?; + self.consume_whitespace(false)?; + result = h + .alloc_slicing_expression(|this| SlicingExpression { + this, + position, + subject, + from_index: index, + to_index, + }) + .upcast(); + } else { + result = h + .alloc_indexing_expression(|this| IndexingExpression { + this, + position, + subject, + index, + }) + .upcast(); + } + self.consume_string(b"]")?; + self.consume_whitespace(false)?; + } else { + assert!(self.has_string(b".")); + self.consume_string(b".")?; + self.consume_whitespace(false)?; + let subject = result; + let field; + if self.has_keyword(b"length") { + self.consume_keyword(b"length")?; + field = Field::Length; + } else { + field = Field::Symbolic(self.consume_identifier(h)?); + } + result = h + .alloc_select_expression(|this| SelectExpression { + this, + position, + subject, + field, + }) + .upcast(); + } + } + Ok(result) + } + fn consume_primary_expression(&mut self, h: &mut Heap) -> Result { + if self.has_string(b"(") { + return self.consume_paren_expression(h); + } + if self.has_string(b"{") { + return Ok(self.consume_array_expression(h)?.upcast()); + } + if self.has_constant() + || self.has_keyword(b"null") + || self.has_keyword(b"true") + || self.has_keyword(b"false") + { + return Ok(self.consume_constant_expression(h)?.upcast()); + } + if self.has_call_expression() { + return Ok(self.consume_call_expression(h)?.upcast()); + } + Ok(self.consume_variable_expression(h)?.upcast()) + } + fn consume_array_expression(&mut self, h: &mut Heap) -> Result { + let position = self.source.pos(); + let mut elements = Vec::new(); + self.consume_string(b"{")?; + self.consume_whitespace(false)?; + if !self.has_string(b"}") { + while self.source.next().is_some() { + elements.push(self.consume_expression(h)?); + self.consume_whitespace(false)?; + if self.has_string(b"}") { + break; + } + self.consume_string(b",")?; + self.consume_whitespace(false)?; + } + } + self.consume_string(b"}")?; + Ok(h.alloc_array_expression(|this| ArrayExpression { this, position, elements })) + } + fn has_constant(&self) -> bool { + is_constant(self.source.next()) + } + fn consume_constant_expression( + &mut self, + h: &mut Heap, + ) -> Result { + let position = self.source.pos(); + let value; + if self.has_keyword(b"null") { + self.consume_keyword(b"null")?; + value = Constant::Null; + } else if self.has_keyword(b"true") { + self.consume_keyword(b"true")?; + value = Constant::True; + } else if self.has_keyword(b"false") { + self.consume_keyword(b"false")?; + value = Constant::False; + } else if self.source.next() == Some(b'\'') { + self.source.consume(); + let mut data = Vec::new(); + let mut next = self.source.next(); + while next != Some(b'\'') && (is_vchar(next) || next == Some(b' ')) { + data.push(next.unwrap()); + self.source.consume(); + next = self.source.next(); + } + if next != Some(b'\'') || data.len() == 0 { + return Err(self.source.error("Expected character constant")); + } + self.source.consume(); + value = Constant::Character(data); + } else { + let mut data = Vec::new(); + let mut next = self.source.next(); + if !is_integer_start(next) { + return Err(self.source.error("Expected integer constant")); + } + while is_integer_rest(next) { + data.push(next.unwrap()); + self.source.consume(); + next = self.source.next(); + } + value = Constant::Integer(data); + } + Ok(h.alloc_constant_expression(|this| ConstantExpression { this, position, value })) + } + fn has_call_expression(&mut self) -> bool { + /* We prevent ambiguity with variables, by looking ahead + the identifier to see if we can find an opening + parenthesis: this signals a call expression. */ + if self.has_builtin_keyword() { + return true; + } + let backup = self.source.clone(); + let mut result = false; + match self.consume_identifier_spilled() { + Ok(_) => match self.consume_whitespace(false) { + Ok(_) => { + result = self.has_string(b"("); + } + Err(_) => {} + }, + Err(_) => {} + } + *self.source = backup; + return result; + } + fn consume_call_expression(&mut self, h: &mut Heap) -> Result { + let position = self.source.pos(); + let method; + if self.has_keyword(b"get") { + self.consume_keyword(b"get")?; + method = Method::Get; + } else if self.has_keyword(b"fires") { + self.consume_keyword(b"fires")?; + method = Method::Fires; + } else if self.has_keyword(b"create") { + self.consume_keyword(b"create")?; + method = Method::Create; + } else { + let identifier = self.consume_identifier(h)?; + method = Method::Symbolic(identifier) + } + self.consume_whitespace(false)?; + let mut arguments = Vec::new(); + self.consume_string(b"(")?; + self.consume_whitespace(false)?; + if !self.has_string(b")") { + while self.source.next().is_some() { + arguments.push(self.consume_expression(h)?); + self.consume_whitespace(false)?; + if self.has_string(b")") { + break; + } + self.consume_string(b",")?; + self.consume_whitespace(false)? + } + } + self.consume_string(b")")?; + Ok(h.alloc_call_expression(|this| CallExpression { + this, + position, + method, + arguments, + declaration: None, + })) + } + fn consume_variable_expression( + &mut self, + h: &mut Heap, + ) -> Result { + let position = self.source.pos(); + let identifier = self.consume_identifier(h)?; + Ok(h.alloc_variable_expression(|this| VariableExpression { + this, + position, + identifier, + declaration: None, + })) + } + + // ==================== + // Statements + // ==================== + + fn consume_statement(&mut self, h: &mut Heap) -> Result { + if self.level >= MAX_LEVEL { + return Err(self.source.error("Too deeply nested statement")); + } + self.level += 1; + let result = self.consume_statement_impl(h); + self.level -= 1; + result + } + fn has_label(&mut self) -> bool { + /* To prevent ambiguity with expression statements consisting + only of an identifier, we look ahead and match the colon + that signals a labeled statement. */ + let backup = self.source.clone(); + let mut result = false; + match self.consume_identifier_spilled() { + Ok(_) => match self.consume_whitespace(false) { + Ok(_) => { + result = self.has_string(b":"); + } + Err(_) => {} + }, + Err(_) => {} + } + *self.source = backup; + return result; + } + fn consume_statement_impl(&mut self, h: &mut Heap) -> Result { + if self.has_string(b"{") { + Ok(self.consume_block_statement(h)?) + } else if self.has_keyword(b"skip") { + Ok(self.consume_skip_statement(h)?.upcast()) + } else if self.has_keyword(b"if") { + Ok(self.consume_if_statement(h)?.upcast()) + } else if self.has_keyword(b"while") { + Ok(self.consume_while_statement(h)?.upcast()) + } else if self.has_keyword(b"break") { + Ok(self.consume_break_statement(h)?.upcast()) + } else if self.has_keyword(b"continue") { + Ok(self.consume_continue_statement(h)?.upcast()) + } else if self.has_keyword(b"synchronous") { + Ok(self.consume_synchronous_statement(h)?.upcast()) + } else if self.has_keyword(b"return") { + Ok(self.consume_return_statement(h)?.upcast()) + } else if self.has_keyword(b"assert") { + Ok(self.consume_assert_statement(h)?.upcast()) + } else if self.has_keyword(b"goto") { + Ok(self.consume_goto_statement(h)?.upcast()) + } else if self.has_keyword(b"new") { + Ok(self.consume_new_statement(h)?.upcast()) + } else if self.has_keyword(b"put") { + Ok(self.consume_put_statement(h)?.upcast()) + } else if self.has_label() { + Ok(self.consume_labeled_statement(h)?.upcast()) + } else { + Ok(self.consume_expression_statement(h)?.upcast()) + } + } + fn has_local_statement(&mut self) -> bool { + /* To avoid ambiguity, we look ahead to find either the + channel keyword that signals a variable declaration, or + a type annotation followed by another identifier. + Example: + my_type[] x = {5}; // memory statement + my_var[5] = x; // assignment expression, expression statement + Note how both the local and the assignment + start with arbitrary identifier followed by [. */ + if self.has_keyword(b"channel") { + return true; + } + if self.has_statement_keyword() { + return false; + } + let backup = self.source.clone(); + let mut result = false; + match self.consume_type_annotation_spilled() { + Ok(_) => match self.consume_whitespace(false) { + Ok(_) => { + result = self.has_identifier(); + } + Err(_) => {} + }, + Err(_) => {} + } + *self.source = backup; + return result; + } + fn consume_block_statement(&mut self, h: &mut Heap) -> Result { + let position = self.source.pos(); + let mut statements = Vec::new(); + self.consume_string(b"{")?; + self.consume_whitespace(false)?; + while self.has_local_statement() { + statements.push(self.consume_local_statement(h)?.upcast()); + self.consume_whitespace(false)?; + } + while !self.has_string(b"}") { + statements.push(self.consume_statement(h)?); + self.consume_whitespace(false)?; + } + self.consume_string(b"}")?; + if statements.len() == 0 { + Ok(h.alloc_skip_statement(|this| SkipStatement { this, position, next: None }).upcast()) + } else { + Ok(h.alloc_block_statement(|this| BlockStatement { + this, + position, + statements, + parent_scope: None, + locals: Vec::new(), + labels: Vec::new(), + }) + .upcast()) + } + } + fn consume_local_statement(&mut self, h: &mut Heap) -> Result { + if self.has_keyword(b"channel") { + Ok(self.consume_channel_statement(h)?.upcast()) + } else { + Ok(self.consume_memory_statement(h)?.upcast()) + } + } + fn consume_channel_statement( + &mut self, + h: &mut Heap, + ) -> Result { + let position = self.source.pos(); + self.consume_keyword(b"channel")?; + self.consume_whitespace(true)?; + let from_annotation = self.create_type_annotation_output(h)?; + let from_identifier = self.consume_identifier(h)?; + self.consume_whitespace(false)?; + self.consume_string(b"->")?; + self.consume_whitespace(false)?; + let to_annotation = self.create_type_annotation_input(h)?; + let to_identifier = self.consume_identifier(h)?; + self.consume_whitespace(false)?; + self.consume_string(b";")?; + let from = h.alloc_local(|this| Local { + this, + position, + type_annotation: from_annotation, + identifier: from_identifier, + }); + let to = h.alloc_local(|this| Local { + this, + position, + type_annotation: to_annotation, + identifier: to_identifier, + }); + Ok(h.alloc_channel_statement(|this| ChannelStatement { + this, + position, + from, + to, + next: None, + })) + } + fn consume_memory_statement(&mut self, h: &mut Heap) -> Result { + let position = self.source.pos(); + let type_annotation = self.consume_type_annotation(h)?; + self.consume_whitespace(true)?; + let identifier = self.consume_identifier(h)?; + self.consume_whitespace(false)?; + self.consume_string(b"=")?; + self.consume_whitespace(false)?; + let initial = self.consume_expression(h)?; + let variable = h.alloc_local(|this| Local { this, position, type_annotation, identifier }); + self.consume_whitespace(false)?; + self.consume_string(b";")?; + Ok(h.alloc_memory_statement(|this| MemoryStatement { + this, + position, + variable, + initial, + next: None, + })) + } + fn consume_labeled_statement( + &mut self, + h: &mut Heap, + ) -> Result { + let position = self.source.pos(); + let label = self.consume_identifier(h)?; + self.consume_whitespace(false)?; + self.consume_string(b":")?; + self.consume_whitespace(false)?; + let body = self.consume_statement(h)?; + Ok(h.alloc_labeled_statement(|this| LabeledStatement { + this, + position, + label, + body, + in_sync: None, + })) + } + fn consume_skip_statement(&mut self, h: &mut Heap) -> Result { + let position = self.source.pos(); + self.consume_keyword(b"skip")?; + self.consume_whitespace(false)?; + self.consume_string(b";")?; + Ok(h.alloc_skip_statement(|this| SkipStatement { this, position, next: None })) + } + fn consume_if_statement(&mut self, h: &mut Heap) -> Result { + let position = self.source.pos(); + self.consume_keyword(b"if")?; + self.consume_whitespace(false)?; + let test = self.consume_paren_expression(h)?; + self.consume_whitespace(false)?; + let true_body = self.consume_statement(h)?; + self.consume_whitespace(false)?; + let false_body; + if self.has_keyword(b"else") { + self.consume_keyword(b"else")?; + self.consume_whitespace(false)?; + false_body = self.consume_statement(h)?; + } else { + false_body = h + .alloc_skip_statement(|this| SkipStatement { this, position, next: None }) + .upcast(); + } + Ok(h.alloc_if_statement(|this| IfStatement { this, position, test, true_body, false_body })) + } + fn consume_while_statement(&mut self, h: &mut Heap) -> Result { + let position = self.source.pos(); + self.consume_keyword(b"while")?; + self.consume_whitespace(false)?; + let test = self.consume_paren_expression(h)?; + self.consume_whitespace(false)?; + let body = self.consume_statement(h)?; + Ok(h.alloc_while_statement(|this| WhileStatement { + this, + position, + test, + body, + next: None, + in_sync: None, + })) + } + fn consume_break_statement(&mut self, h: &mut Heap) -> Result { + let position = self.source.pos(); + self.consume_keyword(b"break")?; + self.consume_whitespace(false)?; + let label; + if self.has_identifier() { + label = Some(self.consume_identifier(h)?); + self.consume_whitespace(false)?; + } else { + label = None; + } + self.consume_string(b";")?; + Ok(h.alloc_break_statement(|this| BreakStatement { this, position, label, target: None })) + } + fn consume_continue_statement( + &mut self, + h: &mut Heap, + ) -> Result { + let position = self.source.pos(); + self.consume_keyword(b"continue")?; + self.consume_whitespace(false)?; + let label; + if self.has_identifier() { + label = Some(self.consume_identifier(h)?); + self.consume_whitespace(false)?; + } else { + label = None; + } + self.consume_string(b";")?; + Ok(h.alloc_continue_statement(|this| ContinueStatement { + this, + position, + label, + target: None, + })) + } + fn consume_synchronous_statement( + &mut self, + h: &mut Heap, + ) -> Result { + let position = self.source.pos(); + self.consume_keyword(b"synchronous")?; + self.consume_whitespace(false)?; + let mut parameters = Vec::new(); + if self.has_string(b"(") { + self.consume_parameters(h, &mut parameters)?; + self.consume_whitespace(false)?; + } else if !self.has_keyword(b"skip") && !self.has_string(b"{") { + return Err(self.source.error("Expected block statement")); + } + let body = self.consume_statement(h)?; + Ok(h.alloc_synchronous_statement(|this| SynchronousStatement { + this, + position, + parameters, + body, + parent_scope: None, + })) + } + fn consume_return_statement(&mut self, h: &mut Heap) -> Result { + let position = self.source.pos(); + self.consume_keyword(b"return")?; + self.consume_whitespace(false)?; + let expression; + if self.has_string(b"(") { + expression = self.consume_paren_expression(h)?; + } else { + expression = self.consume_expression(h)?; + } + self.consume_whitespace(false)?; + self.consume_string(b";")?; + Ok(h.alloc_return_statement(|this| ReturnStatement { this, position, expression })) + } + fn consume_assert_statement(&mut self, h: &mut Heap) -> Result { + let position = self.source.pos(); + self.consume_keyword(b"assert")?; + self.consume_whitespace(false)?; + let expression; + if self.has_string(b"(") { + expression = self.consume_paren_expression(h)?; + } else { + expression = self.consume_expression(h)?; + } + self.consume_whitespace(false)?; + self.consume_string(b";")?; + Ok(h.alloc_assert_statement(|this| AssertStatement { + this, + position, + expression, + next: None, + })) + } + fn consume_goto_statement(&mut self, h: &mut Heap) -> Result { + let position = self.source.pos(); + self.consume_keyword(b"goto")?; + self.consume_whitespace(false)?; + let label = self.consume_identifier(h)?; + self.consume_whitespace(false)?; + self.consume_string(b";")?; + Ok(h.alloc_goto_statement(|this| GotoStatement { this, position, label, target: None })) + } + fn consume_new_statement(&mut self, h: &mut Heap) -> Result { + let position = self.source.pos(); + self.consume_keyword(b"new")?; + self.consume_whitespace(false)?; + let expression = self.consume_call_expression(h)?; + self.consume_whitespace(false)?; + self.consume_string(b";")?; + Ok(h.alloc_new_statement(|this| NewStatement { this, position, expression, next: None })) + } + fn consume_put_statement(&mut self, h: &mut Heap) -> Result { + let position = self.source.pos(); + self.consume_keyword(b"put")?; + self.consume_whitespace(false)?; + self.consume_string(b"(")?; + let port = self.consume_expression(h)?; + self.consume_whitespace(false)?; + self.consume_string(b",")?; + self.consume_whitespace(false)?; + let message = self.consume_expression(h)?; + self.consume_whitespace(false)?; + self.consume_string(b")")?; + self.consume_whitespace(false)?; + self.consume_string(b";")?; + Ok(h.alloc_put_statement(|this| PutStatement { this, position, port, message, next: None })) + } + fn consume_expression_statement( + &mut self, + h: &mut Heap, + ) -> Result { + let position = self.source.pos(); + let expression = self.consume_expression(h)?; + self.consume_whitespace(false)?; + self.consume_string(b";")?; + Ok(h.alloc_expression_statement(|this| ExpressionStatement { + this, + position, + expression, + next: None, + })) + } + + // ==================== + // Symbol definitions + // ==================== + + fn has_symbol_definition(&self) -> bool { + self.has_keyword(b"composite") + || self.has_keyword(b"primitive") + || self.has_type_keyword() + || self.has_identifier() + } + fn consume_symbol_definition(&mut self, h: &mut Heap) -> Result { + if self.has_keyword(b"composite") || self.has_keyword(b"primitive") { + Ok(self.consume_component_definition(h)?.upcast()) + } else { + Ok(self.consume_function_definition(h)?.upcast()) + } + } + fn consume_component_definition(&mut self, h: &mut Heap) -> Result { + if self.has_keyword(b"composite") { + Ok(self.consume_composite_definition(h)?.upcast()) + } else { + Ok(self.consume_primitive_definition(h)?.upcast()) + } + } + fn consume_composite_definition(&mut self, h: &mut Heap) -> Result { + let position = self.source.pos(); + self.consume_keyword(b"composite")?; + self.consume_whitespace(true)?; + let identifier = self.consume_identifier(h)?; + self.consume_whitespace(false)?; + let mut parameters = Vec::new(); + self.consume_parameters(h, &mut parameters)?; + self.consume_whitespace(false)?; + let body = self.consume_block_statement(h)?; + Ok(h.alloc_composite(|this| Composite { this, position, identifier, parameters, body })) + } + fn consume_primitive_definition(&mut self, h: &mut Heap) -> Result { + let position = self.source.pos(); + self.consume_keyword(b"primitive")?; + self.consume_whitespace(true)?; + let identifier = self.consume_identifier(h)?; + self.consume_whitespace(false)?; + let mut parameters = Vec::new(); + self.consume_parameters(h, &mut parameters)?; + self.consume_whitespace(false)?; + let body = self.consume_block_statement(h)?; + Ok(h.alloc_primitive(|this| Primitive { this, position, identifier, parameters, body })) + } + fn consume_function_definition(&mut self, h: &mut Heap) -> Result { + let position = self.source.pos(); + let return_type = self.consume_type_annotation(h)?; + self.consume_whitespace(true)?; + let identifier = self.consume_identifier(h)?; + self.consume_whitespace(false)?; + let mut parameters = Vec::new(); + self.consume_parameters(h, &mut parameters)?; + self.consume_whitespace(false)?; + let body = self.consume_block_statement(h)?; + Ok(h.alloc_function(|this| Function { + this, + position, + return_type, + identifier, + parameters, + body, + })) + } + fn has_pragma(&self) -> bool { + if let Some(c) = self.source.next() { + c == b'#' + } else { + false + } + } + fn consume_pragma(&mut self, h: &mut Heap) -> Result { + let position = self.source.pos(); + let next = self.source.next(); + if next != Some(b'#') { + return Err(self.source.error("Expected pragma")); + } + self.source.consume(); + if !is_vchar(self.source.next()) { + return Err(self.source.error("Expected pragma")); + } + let value = self.consume_line()?; + Ok(h.alloc_pragma(|this| Pragma { this, position, value })) + } + fn has_import(&self) -> bool { + self.has_keyword(b"import") + } + fn consume_import(&mut self, h: &mut Heap) -> Result { + let position = self.source.pos(); + self.consume_keyword(b"import")?; + self.consume_whitespace(true)?; + let mut value = Vec::new(); + let mut ident = self.consume_ident()?; + value.append(&mut ident); + while self.has_string(b".") { + self.consume_string(b".")?; + value.push(b'.'); + ident = self.consume_ident()?; + value.append(&mut ident); + } + self.consume_whitespace(false)?; + self.consume_string(b";")?; + Ok(h.alloc_import(|this| Import { this, position, value })) + } + pub fn consume_protocol_description( + &mut self, + h: &mut Heap, + ) -> Result { + let position = self.source.pos(); + let mut pragmas = Vec::new(); + let mut imports = Vec::new(); + let mut definitions = Vec::new(); + self.consume_whitespace(false)?; + while self.has_pragma() { + let pragma = self.consume_pragma(h)?; + pragmas.push(pragma); + self.consume_whitespace(false)?; + } + while self.has_import() { + let import = self.consume_import(h)?; + imports.push(import); + self.consume_whitespace(false)?; + } + // do-while block + while { + let def = self.consume_symbol_definition(h)?; + definitions.push(def); + self.consume_whitespace(false)?; + self.has_symbol_definition() + } {} + // end of file + if !self.source.is_eof() { + return Err(self.source.error("Expected end of file")); + } + Ok(h.alloc_protocol_description(|this| Root { + this, + position, + pragmas, + imports, + definitions, + declarations: Vec::new(), + })) + } +} + +#[cfg(test)] +mod tests { + use crate::protocol::ast::Expression::*; + use crate::protocol::{ast, lexer::*}; + + #[test] + fn test_lowercase() { + assert_eq!(lowercase(b'a'), b'a'); + assert_eq!(lowercase(b'A'), b'a'); + assert_eq!(lowercase(b'z'), b'z'); + assert_eq!(lowercase(b'Z'), b'z'); + } + + #[test] + fn test_basic_expression() { + let mut h = Heap::new(); + let mut is = InputSource::from_string("a+b;").unwrap(); + let mut lex = Lexer::new(&mut is); + match lex.consume_expression(&mut h) { + Ok(expr) => { + println!("{:?}", expr); + if let Binary(bin) = &h[expr] { + if let Variable(left) = &h[bin.left] { + if let Variable(right) = &h[bin.right] { + assert_eq!("a", format!("{}", h[left.identifier])); + assert_eq!("b", format!("{}", h[right.identifier])); + assert_eq!(Some(b';'), is.next()); + return; + } + } + } + assert!(false); + } + Err(err) => { + err.print(&is); + assert!(false); + } + } + } + + #[test] + fn test_paren_expression() { + let mut h = Heap::new(); + let mut is = InputSource::from_string("(true)").unwrap(); + let mut lex = Lexer::new(&mut is); + match lex.consume_paren_expression(&mut h) { + Ok(expr) => { + println!("{:#?}", expr); + if let Constant(con) = &h[expr] { + if let ast::Constant::True = con.value { + return; + } + } + assert!(false); + } + Err(err) => { + err.print(&is); + assert!(false); + } + } + } + + #[test] + fn test_expression() { + let mut h = Heap::new(); + let mut is = InputSource::from_string("(x(1+5,get(y))-w[5])+z++\n").unwrap(); + let mut lex = Lexer::new(&mut is); + match lex.consume_expression(&mut h) { + Ok(expr) => { + println!("{:#?}", expr); + } + Err(err) => { + err.print(&is); + assert!(false); + } + } + } + + #[test] + fn test_basic_statement() { + let mut h = Heap::new(); + let mut is = InputSource::from_string("while (true) { skip; }").unwrap(); + let mut lex = Lexer::new(&mut is); + match lex.consume_statement(&mut h) { + Ok(stmt) => { + println!("{:#?}", stmt); + if let Statement::While(w) = &h[stmt] { + if let Expression::Constant(_) = h[w.test] { + if let Statement::Block(_) = h[w.body] { + return; + } + } + } + assert!(false); + } + Err(err) => { + err.print(&is); + assert!(false); + } + } + } + + #[test] + fn test_statement() { + let mut h = Heap::new(); + let mut is = InputSource::from_string( + "label: while (true) { if (x++ > y[0]) break label; else continue; }\n", + ) + .unwrap(); + let mut lex = Lexer::new(&mut is); + match lex.consume_statement(&mut h) { + Ok(stmt) => { + println!("{:#?}", stmt); + } + Err(err) => { + err.print(&is); + assert!(false); + } + } + } +} diff --git a/src/protocol/library.rs b/src/protocol/library.rs new file mode 100644 index 0000000000000000000000000000000000000000..8bdbd29972c8d3aeb45be3474c8cd07c8d0d3619 --- /dev/null +++ b/src/protocol/library.rs @@ -0,0 +1,62 @@ +use crate::protocol::ast::*; +use crate::protocol::inputsource::*; + +pub fn get_declarations(h: &mut Heap, i: ImportId) -> Result, ParseError> { + if h[i].value == b"std.reo" { + let mut vec = Vec::new(); + vec.push(cd(h, i, b"sync", &[Type::INPUT, Type::OUTPUT])); + vec.push(cd(h, i, b"syncdrain", &[Type::INPUT, Type::INPUT])); + vec.push(cd(h, i, b"syncspout", &[Type::OUTPUT, Type::OUTPUT])); + vec.push(cd(h, i, b"asyncdrain", &[Type::INPUT, Type::INPUT])); + vec.push(cd(h, i, b"asyncspout", &[Type::OUTPUT, Type::OUTPUT])); + vec.push(cd(h, i, b"merger", &[Type::INPUT_ARRAY, Type::OUTPUT])); + vec.push(cd(h, i, b"router", &[Type::INPUT, Type::OUTPUT_ARRAY])); + vec.push(cd(h, i, b"consensus", &[Type::INPUT_ARRAY, Type::OUTPUT])); + vec.push(cd(h, i, b"replicator", &[Type::INPUT, Type::OUTPUT_ARRAY])); + vec.push(cd(h, i, b"alternator", &[Type::INPUT_ARRAY, Type::OUTPUT])); + vec.push(cd(h, i, b"roundrobin", &[Type::INPUT, Type::OUTPUT_ARRAY])); + vec.push(cd(h, i, b"node", &[Type::INPUT_ARRAY, Type::OUTPUT_ARRAY])); + vec.push(cd(h, i, b"fifo", &[Type::INPUT, Type::OUTPUT])); + vec.push(cd(h, i, b"xfifo", &[Type::INPUT, Type::OUTPUT, Type::MESSAGE])); + vec.push(cd(h, i, b"nfifo", &[Type::INPUT, Type::OUTPUT, Type::INT])); + vec.push(cd(h, i, b"ufifo", &[Type::INPUT, Type::OUTPUT])); + Ok(vec) + } else if h[i].value == b"std.buf" { + let mut vec = Vec::new(); + vec.push(fd(h, i, b"writeByte", Type::BYTE, &[Type::MESSAGE, Type::INT, Type::BYTE])); + vec.push(fd(h, i, b"writeShort", Type::SHORT, &[Type::MESSAGE, Type::INT, Type::SHORT])); + vec.push(fd(h, i, b"writeInt", Type::INT, &[Type::MESSAGE, Type::INT, Type::INT])); + vec.push(fd(h, i, b"writeLong", Type::LONG, &[Type::MESSAGE, Type::INT, Type::LONG])); + vec.push(fd(h, i, b"readByte", Type::BYTE, &[Type::MESSAGE, Type::INT])); + vec.push(fd(h, i, b"readShort", Type::SHORT, &[Type::MESSAGE, Type::INT])); + vec.push(fd(h, i, b"readInt", Type::INT, &[Type::MESSAGE, Type::INT])); + vec.push(fd(h, i, b"readLong", Type::LONG, &[Type::MESSAGE, Type::INT])); + Ok(vec) + } else { + Err(ParseError::new(h[i].position, "Unknown import")) + } +} + +fn cd(h: &mut Heap, import: ImportId, ident: &[u8], sig: &[Type]) -> DeclarationId { + let identifier = h.get_external_identifier(ident).upcast(); + h.alloc_imported_declaration(|this| ImportedDeclaration { + this, + import, + signature: Signature::Component(ComponentSignature { identifier, arity: sig.to_vec() }), + }) + .upcast() +} + +fn fd(h: &mut Heap, import: ImportId, ident: &[u8], ret: Type, sig: &[Type]) -> DeclarationId { + let identifier = h.get_external_identifier(ident).upcast(); + h.alloc_imported_declaration(|this| ImportedDeclaration { + this, + import, + signature: Signature::Function(FunctionSignature { + return_type: ret, + identifier, + arity: sig.to_vec(), + }), + }) + .upcast() +} diff --git a/src/protocol/mod.rs b/src/protocol/mod.rs new file mode 100644 index 0000000000000000000000000000000000000000..d770d19f288f2d7bb087fcbfdbca58a2de1899fd --- /dev/null +++ b/src/protocol/mod.rs @@ -0,0 +1,243 @@ +mod ast; +mod eval; +pub mod inputsource; +mod lexer; +mod library; +mod parser; + +use crate::common::*; +use crate::protocol::ast::*; +use crate::protocol::inputsource::*; +use crate::protocol::parser::*; +use crate::protocol::eval::*; +use std::hint::unreachable_unchecked; + +pub struct ProtocolDescriptionImpl { + heap: Heap, + source: InputSource, + root: RootId, + main: ComponentId, +} + +impl std::fmt::Debug for ProtocolDescriptionImpl { + fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result { + write!(f, "Protocol") + } +} + +impl ProtocolDescription for ProtocolDescriptionImpl { + type S = ComponentStateImpl; + + fn parse(buffer: &[u8]) -> Result { + let mut heap = Heap::new(); + let mut source = InputSource::from_buffer(buffer).unwrap(); + let mut parser = Parser::new(&mut source); + match parser.parse(&mut heap) { + Ok(root) => { + // Find main definition (grammar rule ensures this exists) + let sym = heap.get_external_identifier(b"main"); + let def = heap[root].get_definition(&heap, sym.upcast()).unwrap(); + let main = heap[def].as_component().this(); + return Ok(ProtocolDescriptionImpl { heap, source, root, main }); + } + Err(err) => { + let mut vec: Vec = Vec::new(); + err.write(&source, &mut vec).unwrap(); + Err(String::from_utf8_lossy(&vec).to_string()) + } + } + } + fn main_interface_polarities(&self) -> Vec { + let def = &self.heap[self.main]; + let mut result = Vec::new(); + for ¶m in def.parameters().iter() { + let param = &self.heap[param]; + let type_annot = &self.heap[param.type_annotation]; + let ptype = &type_annot.the_type.primitive; + if ptype == &PrimitiveType::Input { + result.push(Polarity::Getter) + } else if ptype == &PrimitiveType::Output { + result.push(Polarity::Putter) + } else { + unreachable!() + } + } + result + } + fn new_main_component(&self, interface: &[Key]) -> ComponentStateImpl { + let mut args = Vec::new(); + for (&x, y) in interface.iter().zip(self.main_interface_polarities()) { + match y { + Polarity::Getter => args.push(Value::Input(InputValue(x))), + Polarity::Putter => args.push(Value::Output(OutputValue(x))) + } + } + ComponentStateImpl { + prompt: Prompt::new(&self.heap, self.main.upcast(), &args) + } + } +} + +#[derive(Debug, Clone)] +pub struct ComponentStateImpl { + prompt: Prompt +} +impl ComponentState for ComponentStateImpl { + type D = ProtocolDescriptionImpl; + + fn pre_sync_run>( + &mut self, context: &mut C, pd: &ProtocolDescriptionImpl, + ) -> MonoBlocker { + let mut context = EvalContext::Mono(context); + loop { + let result = self.prompt.step(&pd.heap, &mut context); + match result { + // In component definitions, there are no return statements + Ok(_) => unreachable!(), + Err(cont) => match cont { + EvalContinuation::Stepping => continue, + EvalContinuation::Inconsistent => return MonoBlocker::Inconsistent, + EvalContinuation::Terminal => return MonoBlocker::ComponentExit, + EvalContinuation::SyncBlockStart => return MonoBlocker::SyncBlockStart, + // Not possible to end sync block if never entered one + EvalContinuation::SyncBlockEnd => unreachable!(), + EvalContinuation::NewComponent(args) => { + todo!(); + continue + } + // Outside synchronous blocks, no fires/get/put happens + EvalContinuation::BlockFires(val) => unreachable!(), + EvalContinuation::BlockGet(val) => unreachable!(), + EvalContinuation::Put(port, msg) => unreachable!() + } + } + } + } + + fn sync_run>( + &mut self, context: &mut C, pd: &ProtocolDescriptionImpl, + ) -> PolyBlocker { + let mut context = EvalContext::Poly(context); + loop { + let result = self.prompt.step(&pd.heap, &mut context); + match result { + // Inside synchronous blocks, there are no return statements + Ok(_) => unreachable!(), + Err(cont) => match cont { + EvalContinuation::Stepping => continue, + EvalContinuation::Inconsistent => return PolyBlocker::Inconsistent, + // First need to exit synchronous block before definition may end + EvalContinuation::Terminal => unreachable!(), + // No nested synchronous blocks + EvalContinuation::SyncBlockStart => unreachable!(), + EvalContinuation::SyncBlockEnd => return PolyBlocker::SyncBlockEnd, + // Not possible to create component in sync block + EvalContinuation::NewComponent(args) => unreachable!(), + EvalContinuation::BlockFires(port) => { + match port { + Value::Output(OutputValue(key)) => { + return PolyBlocker::CouldntCheckFiring(key); + } + Value::Input(InputValue(key)) => { + return PolyBlocker::CouldntCheckFiring(key); + } + _ => unreachable!() + } + } + EvalContinuation::BlockGet(port) => { + match port { + Value::Output(OutputValue(key)) => { + return PolyBlocker::CouldntReadMsg(key); + } + Value::Input(InputValue(key)) => { + return PolyBlocker::CouldntReadMsg(key); + } + _ => unreachable!() + } + } + EvalContinuation::Put(port, message) => { + let key; + match port { + Value::Output(OutputValue(the_key)) => { + key = the_key; + } + Value::Input(InputValue(the_key)) => { + key = the_key; + } + _ => unreachable!() + } + let payload; + match message { + Value::Message(MessageValue(None)) => { + // Putting a null message is inconsistent + return PolyBlocker::Inconsistent; + } + Value::Message(MessageValue(Some(buffer))) => { + // Create a copy of the payload + payload = buffer.clone(); + } + _ => unreachable!() + } + return PolyBlocker::PutMsg(key, payload); + } + } + } + } + } +} + +pub enum EvalContext<'a> { + Mono(&'a mut dyn MonoContext), + Poly(&'a mut dyn PolyContext), + None +} +impl EvalContext<'_> { + fn random(&mut self) -> LongValue { + match self { + EvalContext::None => unreachable!(), + EvalContext::Mono(context) => todo!(), + EvalContext::Poly(context) => unreachable!(), + } + } + fn channel(&mut self) -> (Value, Value) { + match self { + EvalContext::None => unreachable!(), + EvalContext::Mono(context) => unreachable!(), + EvalContext::Poly(context) => todo!(), + } + } + fn fires(&mut self, port: Value) -> Option { + match self { + EvalContext::None => unreachable!(), + EvalContext::Mono(context) => unreachable!(), + EvalContext::Poly(context) => { + match port { + Value::Output(OutputValue(key)) => { + context.is_firing(key).map(Value::from) + } + Value::Input(InputValue(key)) => { + context.is_firing(key).map(Value::from) + } + _ => unreachable!() + } + } + } + } + fn get(&mut self, port: Value) -> Option { + match self { + EvalContext::None => unreachable!(), + EvalContext::Mono(context) => unreachable!(), + EvalContext::Poly(context) => { + match port { + Value::Output(OutputValue(key)) => { + context.read_msg(key).map(Value::receive_message) + } + Value::Input(InputValue(key)) => { + context.read_msg(key).map(Value::receive_message) + } + _ => unreachable!() + } + }, + } + } +} \ No newline at end of file diff --git a/src/protocol/parser.rs b/src/protocol/parser.rs new file mode 100644 index 0000000000000000000000000000000000000000..098dc481f9808d9f4ef97d12548e388c1fd405b5 --- /dev/null +++ b/src/protocol/parser.rs @@ -0,0 +1,1914 @@ +use crate::protocol::ast::*; +use crate::protocol::inputsource::*; +use crate::protocol::lexer::*; +use crate::protocol::library; + +// The following indirection is needed due to a bug in the cbindgen tool. +type Unit = (); +type VisitorResult = Result; + +trait Visitor: Sized { + fn visit_protocol_description(&mut self, h: &mut Heap, pd: RootId) -> VisitorResult { + recursive_protocol_description(self, h, pd) + } + fn visit_pragma(&mut self, _h: &mut Heap, _pragma: PragmaId) -> VisitorResult { + Ok(()) + } + fn visit_import(&mut self, _h: &mut Heap, _import: ImportId) -> VisitorResult { + Ok(()) + } + + fn visit_symbol_definition(&mut self, h: &mut Heap, def: DefinitionId) -> VisitorResult { + recursive_symbol_definition(self, h, def) + } + fn visit_component_definition(&mut self, h: &mut Heap, def: ComponentId) -> VisitorResult { + recursive_component_definition(self, h, def) + } + fn visit_composite_definition(&mut self, h: &mut Heap, def: CompositeId) -> VisitorResult { + recursive_composite_definition(self, h, def) + } + fn visit_primitive_definition(&mut self, h: &mut Heap, def: PrimitiveId) -> VisitorResult { + recursive_primitive_definition(self, h, def) + } + fn visit_function_definition(&mut self, h: &mut Heap, def: FunctionId) -> VisitorResult { + recursive_function_definition(self, h, def) + } + + fn visit_variable_declaration(&mut self, h: &mut Heap, decl: VariableId) -> VisitorResult { + recursive_variable_declaration(self, h, decl) + } + fn visit_parameter_declaration(&mut self, _h: &mut Heap, _decl: ParameterId) -> VisitorResult { + Ok(()) + } + fn visit_local_declaration(&mut self, _h: &mut Heap, _decl: LocalId) -> VisitorResult { + Ok(()) + } + + fn visit_statement(&mut self, h: &mut Heap, stmt: StatementId) -> VisitorResult { + recursive_statement(self, h, stmt) + } + fn visit_local_statement(&mut self, h: &mut Heap, stmt: LocalStatementId) -> VisitorResult { + recursive_local_statement(self, h, stmt) + } + fn visit_memory_statement(&mut self, h: &mut Heap, stmt: MemoryStatementId) -> VisitorResult { + recursive_memory_statement(self, h, stmt) + } + fn visit_channel_statement( + &mut self, + _h: &mut Heap, + _stmt: ChannelStatementId, + ) -> VisitorResult { + Ok(()) + } + fn visit_block_statement(&mut self, h: &mut Heap, stmt: BlockStatementId) -> VisitorResult { + recursive_block_statement(self, h, stmt) + } + fn visit_labeled_statement(&mut self, h: &mut Heap, stmt: LabeledStatementId) -> VisitorResult { + recursive_labeled_statement(self, h, stmt) + } + fn visit_skip_statement(&mut self, _h: &mut Heap, _stmt: SkipStatementId) -> VisitorResult { + Ok(()) + } + fn visit_if_statement(&mut self, h: &mut Heap, stmt: IfStatementId) -> VisitorResult { + recursive_if_statement(self, h, stmt) + } + fn visit_while_statement(&mut self, h: &mut Heap, stmt: WhileStatementId) -> VisitorResult { + recursive_while_statement(self, h, stmt) + } + fn visit_break_statement(&mut self, _h: &mut Heap, _stmt: BreakStatementId) -> VisitorResult { + Ok(()) + } + fn visit_continue_statement( + &mut self, + _h: &mut Heap, + _stmt: ContinueStatementId, + ) -> VisitorResult { + Ok(()) + } + fn visit_synchronous_statement( + &mut self, + h: &mut Heap, + stmt: SynchronousStatementId, + ) -> VisitorResult { + recursive_synchronous_statement(self, h, stmt) + } + fn visit_return_statement(&mut self, h: &mut Heap, stmt: ReturnStatementId) -> VisitorResult { + recursive_return_statement(self, h, stmt) + } + fn visit_assert_statement(&mut self, h: &mut Heap, stmt: AssertStatementId) -> VisitorResult { + recursive_assert_statement(self, h, stmt) + } + fn visit_goto_statement(&mut self, _h: &mut Heap, _stmt: GotoStatementId) -> VisitorResult { + Ok(()) + } + fn visit_new_statement(&mut self, h: &mut Heap, stmt: NewStatementId) -> VisitorResult { + recursive_new_statement(self, h, stmt) + } + fn visit_put_statement(&mut self, h: &mut Heap, stmt: PutStatementId) -> VisitorResult { + recursive_put_statement(self, h, stmt) + } + fn visit_expression_statement( + &mut self, + h: &mut Heap, + stmt: ExpressionStatementId, + ) -> VisitorResult { + recursive_expression_statement(self, h, stmt) + } + + fn visit_expression(&mut self, h: &mut Heap, expr: ExpressionId) -> VisitorResult { + recursive_expression(self, h, expr) + } + fn visit_assignment_expression( + &mut self, + h: &mut Heap, + expr: AssignmentExpressionId, + ) -> VisitorResult { + recursive_assignment_expression(self, h, expr) + } + fn visit_conditional_expression( + &mut self, + h: &mut Heap, + expr: ConditionalExpressionId, + ) -> VisitorResult { + recursive_conditional_expression(self, h, expr) + } + fn visit_binary_expression(&mut self, h: &mut Heap, expr: BinaryExpressionId) -> VisitorResult { + recursive_binary_expression(self, h, expr) + } + fn visit_unary_expression(&mut self, h: &mut Heap, expr: UnaryExpressionId) -> VisitorResult { + recursive_unary_expression(self, h, expr) + } + fn visit_indexing_expression( + &mut self, + h: &mut Heap, + expr: IndexingExpressionId, + ) -> VisitorResult { + recursive_indexing_expression(self, h, expr) + } + fn visit_slicing_expression( + &mut self, + h: &mut Heap, + expr: SlicingExpressionId, + ) -> VisitorResult { + recursive_slicing_expression(self, h, expr) + } + fn visit_select_expression(&mut self, h: &mut Heap, expr: SelectExpressionId) -> VisitorResult { + recursive_select_expression(self, h, expr) + } + fn visit_array_expression(&mut self, h: &mut Heap, expr: ArrayExpressionId) -> VisitorResult { + recursive_array_expression(self, h, expr) + } + fn visit_call_expression(&mut self, h: &mut Heap, expr: CallExpressionId) -> VisitorResult { + recursive_call_expression(self, h, expr) + } + fn visit_constant_expression( + &mut self, + _h: &mut Heap, + _expr: ConstantExpressionId, + ) -> VisitorResult { + Ok(()) + } + fn visit_variable_expression( + &mut self, + _h: &mut Heap, + _expr: VariableExpressionId, + ) -> VisitorResult { + Ok(()) + } +} + +// Bubble-up helpers +fn recursive_parameter_as_variable( + this: &mut T, + h: &mut Heap, + param: ParameterId, +) -> VisitorResult { + this.visit_variable_declaration(h, param.upcast()) +} + +fn recursive_local_as_variable( + this: &mut T, + h: &mut Heap, + local: LocalId, +) -> VisitorResult { + this.visit_variable_declaration(h, local.upcast()) +} + +fn recursive_call_expression_as_expression( + this: &mut T, + h: &mut Heap, + call: CallExpressionId, +) -> VisitorResult { + this.visit_expression(h, call.upcast()) +} + +// Recursive procedures +fn recursive_protocol_description( + this: &mut T, + h: &mut Heap, + pd: RootId, +) -> VisitorResult { + for &pragma in h[pd].pragmas.clone().iter() { + this.visit_pragma(h, pragma)?; + } + for &import in h[pd].imports.clone().iter() { + this.visit_import(h, import)?; + } + for &def in h[pd].definitions.clone().iter() { + this.visit_symbol_definition(h, def)?; + } + Ok(()) +} + +fn recursive_symbol_definition( + this: &mut T, + h: &mut Heap, + def: DefinitionId, +) -> VisitorResult { + // We clone the definition in case it is modified + match h[def].clone() { + Definition::Component(cdef) => this.visit_component_definition(h, cdef.this()), + Definition::Function(fdef) => this.visit_function_definition(h, fdef.this), + } +} + +fn recursive_component_definition( + this: &mut T, + h: &mut Heap, + def: ComponentId, +) -> VisitorResult { + match h[def].clone() { + Component::Composite(cdef) => this.visit_composite_definition(h, cdef.this), + Component::Primitive(pdef) => this.visit_primitive_definition(h, pdef.this), + } +} + +fn recursive_composite_definition( + this: &mut T, + h: &mut Heap, + def: CompositeId, +) -> VisitorResult { + for ¶m in h[def].parameters.clone().iter() { + recursive_parameter_as_variable(this, h, param)?; + } + this.visit_statement(h, h[def].body) +} + +fn recursive_primitive_definition( + this: &mut T, + h: &mut Heap, + def: PrimitiveId, +) -> VisitorResult { + for ¶m in h[def].parameters.clone().iter() { + recursive_parameter_as_variable(this, h, param)?; + } + this.visit_statement(h, h[def].body) +} + +fn recursive_function_definition( + this: &mut T, + h: &mut Heap, + def: FunctionId, +) -> VisitorResult { + for ¶m in h[def].parameters.clone().iter() { + recursive_parameter_as_variable(this, h, param)?; + } + this.visit_statement(h, h[def].body) +} + +fn recursive_variable_declaration( + this: &mut T, + h: &mut Heap, + decl: VariableId, +) -> VisitorResult { + match h[decl].clone() { + Variable::Parameter(decl) => this.visit_parameter_declaration(h, decl.this), + Variable::Local(decl) => this.visit_local_declaration(h, decl.this), + } +} + +fn recursive_statement(this: &mut T, h: &mut Heap, stmt: StatementId) -> VisitorResult { + match h[stmt].clone() { + Statement::Block(stmt) => this.visit_block_statement(h, stmt.this), + Statement::Local(stmt) => this.visit_local_statement(h, stmt.this()), + Statement::Skip(stmt) => this.visit_skip_statement(h, stmt.this), + Statement::Labeled(stmt) => this.visit_labeled_statement(h, stmt.this), + Statement::If(stmt) => this.visit_if_statement(h, stmt.this), + Statement::EndIf(stmt) => unreachable!(), // pseudo-statement + Statement::While(stmt) => this.visit_while_statement(h, stmt.this), + Statement::EndWhile(stmt) => unreachable!(), // pseudo-statement + Statement::Break(stmt) => this.visit_break_statement(h, stmt.this), + Statement::Continue(stmt) => this.visit_continue_statement(h, stmt.this), + Statement::Synchronous(stmt) => this.visit_synchronous_statement(h, stmt.this), + Statement::EndSynchronous(stmt) => unreachable!(), // pseudo-statement + Statement::Return(stmt) => this.visit_return_statement(h, stmt.this), + Statement::Assert(stmt) => this.visit_assert_statement(h, stmt.this), + Statement::Goto(stmt) => this.visit_goto_statement(h, stmt.this), + Statement::New(stmt) => this.visit_new_statement(h, stmt.this), + Statement::Put(stmt) => this.visit_put_statement(h, stmt.this), + Statement::Expression(stmt) => this.visit_expression_statement(h, stmt.this), + } +} + +fn recursive_block_statement( + this: &mut T, + h: &mut Heap, + block: BlockStatementId, +) -> VisitorResult { + for &local in h[block].locals.clone().iter() { + recursive_local_as_variable(this, h, local)?; + } + for &stmt in h[block].statements.clone().iter() { + this.visit_statement(h, stmt)?; + } + Ok(()) +} + +fn recursive_local_statement( + this: &mut T, + h: &mut Heap, + stmt: LocalStatementId, +) -> VisitorResult { + match h[stmt].clone() { + LocalStatement::Channel(stmt) => this.visit_channel_statement(h, stmt.this), + LocalStatement::Memory(stmt) => this.visit_memory_statement(h, stmt.this), + } +} + +fn recursive_memory_statement( + this: &mut T, + h: &mut Heap, + stmt: MemoryStatementId, +) -> VisitorResult { + this.visit_expression(h, h[stmt].initial) +} + +fn recursive_labeled_statement( + this: &mut T, + h: &mut Heap, + stmt: LabeledStatementId, +) -> VisitorResult { + this.visit_statement(h, h[stmt].body) +} + +fn recursive_if_statement( + this: &mut T, + h: &mut Heap, + stmt: IfStatementId, +) -> VisitorResult { + this.visit_expression(h, h[stmt].test)?; + this.visit_statement(h, h[stmt].true_body)?; + this.visit_statement(h, h[stmt].false_body) +} + +fn recursive_while_statement( + this: &mut T, + h: &mut Heap, + stmt: WhileStatementId, +) -> VisitorResult { + this.visit_expression(h, h[stmt].test)?; + this.visit_statement(h, h[stmt].body) +} + +fn recursive_synchronous_statement( + this: &mut T, + h: &mut Heap, + stmt: SynchronousStatementId, +) -> VisitorResult { + for ¶m in h[stmt].parameters.clone().iter() { + recursive_parameter_as_variable(this, h, param)?; + } + this.visit_statement(h, h[stmt].body) +} + +fn recursive_return_statement( + this: &mut T, + h: &mut Heap, + stmt: ReturnStatementId, +) -> VisitorResult { + this.visit_expression(h, h[stmt].expression) +} + +fn recursive_assert_statement( + this: &mut T, + h: &mut Heap, + stmt: AssertStatementId, +) -> VisitorResult { + this.visit_expression(h, h[stmt].expression) +} + +fn recursive_new_statement( + this: &mut T, + h: &mut Heap, + stmt: NewStatementId, +) -> VisitorResult { + recursive_call_expression_as_expression(this, h, h[stmt].expression) +} + +fn recursive_put_statement( + this: &mut T, + h: &mut Heap, + stmt: PutStatementId, +) -> VisitorResult { + this.visit_expression(h, h[stmt].port)?; + this.visit_expression(h, h[stmt].message) +} + +fn recursive_expression_statement( + this: &mut T, + h: &mut Heap, + stmt: ExpressionStatementId, +) -> VisitorResult { + this.visit_expression(h, h[stmt].expression) +} + +fn recursive_expression( + this: &mut T, + h: &mut Heap, + expr: ExpressionId, +) -> VisitorResult { + match h[expr].clone() { + Expression::Assignment(expr) => this.visit_assignment_expression(h, expr.this), + Expression::Conditional(expr) => this.visit_conditional_expression(h, expr.this), + Expression::Binary(expr) => this.visit_binary_expression(h, expr.this), + Expression::Unary(expr) => this.visit_unary_expression(h, expr.this), + Expression::Indexing(expr) => this.visit_indexing_expression(h, expr.this), + Expression::Slicing(expr) => this.visit_slicing_expression(h, expr.this), + Expression::Select(expr) => this.visit_select_expression(h, expr.this), + Expression::Array(expr) => this.visit_array_expression(h, expr.this), + Expression::Constant(expr) => this.visit_constant_expression(h, expr.this), + Expression::Call(expr) => this.visit_call_expression(h, expr.this), + Expression::Variable(expr) => this.visit_variable_expression(h, expr.this), + } +} + +fn recursive_assignment_expression( + this: &mut T, + h: &mut Heap, + expr: AssignmentExpressionId, +) -> VisitorResult { + this.visit_expression(h, h[expr].left)?; + this.visit_expression(h, h[expr].right) +} + +fn recursive_conditional_expression( + this: &mut T, + h: &mut Heap, + expr: ConditionalExpressionId, +) -> VisitorResult { + this.visit_expression(h, h[expr].test)?; + this.visit_expression(h, h[expr].true_expression)?; + this.visit_expression(h, h[expr].false_expression) +} + +fn recursive_binary_expression( + this: &mut T, + h: &mut Heap, + expr: BinaryExpressionId, +) -> VisitorResult { + this.visit_expression(h, h[expr].left)?; + this.visit_expression(h, h[expr].right) +} + +fn recursive_unary_expression( + this: &mut T, + h: &mut Heap, + expr: UnaryExpressionId, +) -> VisitorResult { + this.visit_expression(h, h[expr].expression) +} + +fn recursive_indexing_expression( + this: &mut T, + h: &mut Heap, + expr: IndexingExpressionId, +) -> VisitorResult { + this.visit_expression(h, h[expr].subject)?; + this.visit_expression(h, h[expr].index) +} + +fn recursive_slicing_expression( + this: &mut T, + h: &mut Heap, + expr: SlicingExpressionId, +) -> VisitorResult { + this.visit_expression(h, h[expr].subject)?; + this.visit_expression(h, h[expr].from_index)?; + this.visit_expression(h, h[expr].to_index) +} + +fn recursive_select_expression( + this: &mut T, + h: &mut Heap, + expr: SelectExpressionId, +) -> VisitorResult { + this.visit_expression(h, h[expr].subject) +} + +fn recursive_array_expression( + this: &mut T, + h: &mut Heap, + expr: ArrayExpressionId, +) -> VisitorResult { + for &expr in h[expr].elements.clone().iter() { + this.visit_expression(h, expr)?; + } + Ok(()) +} + +fn recursive_call_expression( + this: &mut T, + h: &mut Heap, + expr: CallExpressionId, +) -> VisitorResult { + for &expr in h[expr].arguments.clone().iter() { + this.visit_expression(h, expr)?; + } + Ok(()) +} + +// ==================== +// Grammar Rules +// ==================== + +struct NestedSynchronousStatements { + illegal: bool, +} + +impl NestedSynchronousStatements { + fn new() -> Self { + NestedSynchronousStatements { illegal: false } + } +} + +impl Visitor for NestedSynchronousStatements { + fn visit_composite_definition(&mut self, h: &mut Heap, def: CompositeId) -> VisitorResult { + assert!(!self.illegal); + self.illegal = true; + recursive_composite_definition(self, h, def)?; + self.illegal = false; + Ok(()) + } + fn visit_function_definition(&mut self, h: &mut Heap, def: FunctionId) -> VisitorResult { + assert!(!self.illegal); + self.illegal = true; + recursive_function_definition(self, h, def)?; + self.illegal = false; + Ok(()) + } + fn visit_synchronous_statement( + &mut self, + h: &mut Heap, + stmt: SynchronousStatementId, + ) -> VisitorResult { + if self.illegal { + return Err(ParseError::new( + h[stmt].position(), + "Illegal nested synchronous statement", + )); + } + self.illegal = true; + recursive_synchronous_statement(self, h, stmt)?; + self.illegal = false; + Ok(()) + } + fn visit_expression(&mut self, _h: &mut Heap, _expr: ExpressionId) -> VisitorResult { + Ok(()) + } +} + +struct ChannelStatementOccurrences { + illegal: bool, +} + +impl ChannelStatementOccurrences { + fn new() -> Self { + ChannelStatementOccurrences { illegal: false } + } +} + +impl Visitor for ChannelStatementOccurrences { + fn visit_primitive_definition(&mut self, h: &mut Heap, def: PrimitiveId) -> VisitorResult { + assert!(!self.illegal); + self.illegal = true; + recursive_primitive_definition(self, h, def)?; + self.illegal = false; + Ok(()) + } + fn visit_function_definition(&mut self, h: &mut Heap, def: FunctionId) -> VisitorResult { + assert!(!self.illegal); + self.illegal = true; + recursive_function_definition(self, h, def)?; + self.illegal = false; + Ok(()) + } + fn visit_channel_statement(&mut self, h: &mut Heap, stmt: ChannelStatementId) -> VisitorResult { + if self.illegal { + return Err(ParseError::new(h[stmt].position(), "Illegal channel delcaration")); + } + Ok(()) + } + fn visit_expression(&mut self, _h: &mut Heap, _expr: ExpressionId) -> VisitorResult { + Ok(()) + } +} + +struct FunctionStatementReturns {} + +impl FunctionStatementReturns { + fn new() -> Self { + FunctionStatementReturns {} + } + fn function_error(&self, position: InputPosition) -> VisitorResult { + Err(ParseError::new(position, "Function definition must return")) + } +} + +impl Visitor for FunctionStatementReturns { + fn visit_component_definition(&mut self, _h: &mut Heap, _def: ComponentId) -> VisitorResult { + Ok(()) + } + fn visit_variable_declaration(&mut self, _h: &mut Heap, _decl: VariableId) -> VisitorResult { + Ok(()) + } + fn visit_block_statement(&mut self, h: &mut Heap, block: BlockStatementId) -> VisitorResult { + let len = h[block].statements.len(); + assert!(len > 0); + self.visit_statement(h, h[block].statements[len - 1]) + } + fn visit_skip_statement(&mut self, h: &mut Heap, stmt: SkipStatementId) -> VisitorResult { + self.function_error(h[stmt].position) + } + fn visit_break_statement(&mut self, h: &mut Heap, stmt: BreakStatementId) -> VisitorResult { + self.function_error(h[stmt].position) + } + fn visit_continue_statement( + &mut self, + h: &mut Heap, + stmt: ContinueStatementId, + ) -> VisitorResult { + self.function_error(h[stmt].position) + } + fn visit_assert_statement(&mut self, h: &mut Heap, stmt: AssertStatementId) -> VisitorResult { + self.function_error(h[stmt].position) + } + fn visit_new_statement(&mut self, h: &mut Heap, stmt: NewStatementId) -> VisitorResult { + self.function_error(h[stmt].position) + } + fn visit_expression_statement( + &mut self, + h: &mut Heap, + stmt: ExpressionStatementId, + ) -> VisitorResult { + self.function_error(h[stmt].position) + } + fn visit_expression(&mut self, _h: &mut Heap, _expr: ExpressionId) -> VisitorResult { + Ok(()) + } +} + +struct ComponentStatementReturnNew { + illegal_new: bool, + illegal_return: bool, +} + +impl ComponentStatementReturnNew { + fn new() -> Self { + ComponentStatementReturnNew { illegal_new: false, illegal_return: false } + } +} + +impl Visitor for ComponentStatementReturnNew { + fn visit_component_definition(&mut self, h: &mut Heap, def: ComponentId) -> VisitorResult { + assert!(!(self.illegal_new || self.illegal_return)); + self.illegal_return = true; + recursive_component_definition(self, h, def)?; + self.illegal_return = false; + Ok(()) + } + fn visit_primitive_definition(&mut self, h: &mut Heap, def: PrimitiveId) -> VisitorResult { + assert!(!self.illegal_new); + self.illegal_new = true; + recursive_primitive_definition(self, h, def)?; + self.illegal_new = false; + Ok(()) + } + fn visit_function_definition(&mut self, h: &mut Heap, def: FunctionId) -> VisitorResult { + assert!(!(self.illegal_new || self.illegal_return)); + self.illegal_new = true; + recursive_function_definition(self, h, def)?; + self.illegal_new = false; + Ok(()) + } + fn visit_variable_declaration(&mut self, _h: &mut Heap, _decl: VariableId) -> VisitorResult { + Ok(()) + } + fn visit_return_statement(&mut self, h: &mut Heap, stmt: ReturnStatementId) -> VisitorResult { + if self.illegal_return { + Err(ParseError::new(h[stmt].position, "Component definition must not return")) + } else { + recursive_return_statement(self, h, stmt) + } + } + fn visit_new_statement(&mut self, h: &mut Heap, stmt: NewStatementId) -> VisitorResult { + if self.illegal_new { + Err(ParseError::new( + h[stmt].position, + "Symbol definition contains illegal new statement", + )) + } else { + recursive_new_statement(self, h, stmt) + } + } + fn visit_expression(&mut self, _h: &mut Heap, _expr: ExpressionId) -> VisitorResult { + Ok(()) + } +} + +struct CheckBuiltinOccurrences { + legal: bool, +} + +impl CheckBuiltinOccurrences { + fn new() -> Self { + CheckBuiltinOccurrences { legal: false } + } +} + +impl Visitor for CheckBuiltinOccurrences { + fn visit_synchronous_statement( + &mut self, + h: &mut Heap, + stmt: SynchronousStatementId, + ) -> VisitorResult { + assert!(!self.legal); + self.legal = true; + recursive_synchronous_statement(self, h, stmt)?; + self.legal = false; + Ok(()) + } + fn visit_call_expression(&mut self, h: &mut Heap, expr: CallExpressionId) -> VisitorResult { + match h[expr].method { + Method::Get | Method::Fires => { + if !self.legal { + return Err(ParseError::new(h[expr].position, "Illegal built-in occurrence")); + } + } + _ => {} + } + recursive_call_expression(self, h, expr) + } +} + +struct BuildSymbolDeclarations { + declarations: Vec, +} + +impl BuildSymbolDeclarations { + fn new() -> Self { + BuildSymbolDeclarations { declarations: Vec::new() } + } + fn checked_add(&mut self, h: &mut Heap, decl: DeclarationId) -> VisitorResult { + for &old in self.declarations.iter() { + let id = h[decl].identifier(); + if h[id] == h[h[old].identifier()] { + return match h[decl].clone() { + Declaration::Defined(defined) => Err(ParseError::new( + h[defined.definition].position(), + format!("Defined symbol clash: {}", h[id]), + )), + Declaration::Imported(imported) => Err(ParseError::new( + h[imported.import].position(), + format!("Imported symbol clash: {}", h[id]), + )), + }; + } + } + self.declarations.push(decl); + Ok(()) + } +} + +impl Visitor for BuildSymbolDeclarations { + fn visit_protocol_description(&mut self, h: &mut Heap, pd: RootId) -> VisitorResult { + recursive_protocol_description(self, h, pd)?; + // Move all collected declarations to the protocol description + h[pd].declarations.append(&mut self.declarations); + Ok(()) + } + fn visit_import(&mut self, h: &mut Heap, import: ImportId) -> VisitorResult { + let vec = library::get_declarations(h, import)?; + // Destructively iterate over the vector + for decl in vec { + self.checked_add(h, decl)?; + } + Ok(()) + } + fn visit_symbol_definition(&mut self, h: &mut Heap, definition: DefinitionId) -> VisitorResult { + let signature = Signature::from_definition(h, definition); + let decl = h + .alloc_defined_declaration(|this| DefinedDeclaration { this, definition, signature }) + .upcast(); + self.checked_add(h, decl)?; + Ok(()) + } +} + +struct LinkCallExpressions { + pd: Option, + composite: bool, + new_statement: bool, +} + +impl LinkCallExpressions { + fn new() -> Self { + LinkCallExpressions { pd: None, composite: false, new_statement: false } + } + fn get_declaration( + &self, + h: &Heap, + id: SourceIdentifierId, + ) -> Result { + match h[self.pd.unwrap()].get_declaration(h, id.upcast()) { + Some(id) => Ok(id), + None => Err(ParseError::new(h[id].position, "Unresolved method")), + } + } +} + +impl Visitor for LinkCallExpressions { + fn visit_protocol_description(&mut self, h: &mut Heap, pd: RootId) -> VisitorResult { + self.pd = Some(pd); + recursive_protocol_description(self, h, pd)?; + self.pd = None; + Ok(()) + } + fn visit_composite_definition(&mut self, h: &mut Heap, def: CompositeId) -> VisitorResult { + assert!(!self.composite); + self.composite = true; + recursive_composite_definition(self, h, def)?; + self.composite = false; + Ok(()) + } + fn visit_new_statement(&mut self, h: &mut Heap, stmt: NewStatementId) -> VisitorResult { + assert!(self.composite); + assert!(!self.new_statement); + self.new_statement = true; + recursive_new_statement(self, h, stmt)?; + self.new_statement = false; + Ok(()) + } + fn visit_call_expression(&mut self, h: &mut Heap, expr: CallExpressionId) -> VisitorResult { + if let Method::Symbolic(id) = h[expr].method { + let decl = self.get_declaration(h, id)?; + if self.new_statement && h[decl].is_function() { + return Err(ParseError::new(h[id].position, "Illegal call expression")); + } + if !self.new_statement && h[decl].is_component() { + return Err(ParseError::new(h[id].position, "Illegal call expression")); + } + // Set the corresponding declaration of the call + h[expr].declaration = Some(decl); + } + // A new statement's call expression may have as arguments function calls + let old = self.new_statement; + self.new_statement = false; + recursive_call_expression(self, h, expr)?; + self.new_statement = old; + Ok(()) + } +} + +struct BuildScope { + scope: Option, +} + +impl BuildScope { + fn new() -> Self { + BuildScope { scope: None } + } +} + +impl Visitor for BuildScope { + fn visit_symbol_definition(&mut self, h: &mut Heap, def: DefinitionId) -> VisitorResult { + assert!(self.scope.is_none()); + self.scope = Some(Scope::Definition(def)); + recursive_symbol_definition(self, h, def)?; + self.scope = None; + Ok(()) + } + fn visit_block_statement(&mut self, h: &mut Heap, stmt: BlockStatementId) -> VisitorResult { + assert!(!self.scope.is_none()); + let old = self.scope; + // First store the current scope + h[stmt].parent_scope = self.scope; + // Then move scope down to current block + self.scope = Some(Scope::Block(stmt)); + recursive_block_statement(self, h, stmt)?; + // Move scope back up + self.scope = old; + Ok(()) + } + fn visit_synchronous_statement( + &mut self, + h: &mut Heap, + stmt: SynchronousStatementId, + ) -> VisitorResult { + assert!(!self.scope.is_none()); + let old = self.scope; + // First store the current scope + h[stmt].parent_scope = self.scope; + // Then move scope down to current sync + self.scope = Some(Scope::Synchronous(stmt)); + recursive_synchronous_statement(self, h, stmt)?; + // Move scope back up + self.scope = old; + Ok(()) + } + fn visit_expression(&mut self, h: &mut Heap, expr: ExpressionId) -> VisitorResult { + Ok(()) + } +} + +struct ResolveVariables { + scope: Option, +} + +impl ResolveVariables { + fn new() -> Self { + ResolveVariables { scope: None } + } + fn get_variable(&self, h: &Heap, id: SourceIdentifierId) -> Result { + if let Some(var) = self.find_variable(h, id) { + Ok(var) + } else { + Err(ParseError::new(h[id].position, "Unresolved variable")) + } + } + fn find_variable(&self, h: &Heap, id: SourceIdentifierId) -> Option { + ResolveVariables::find_variable_impl(h, self.scope, id) + } + fn find_variable_impl( + h: &Heap, + scope: Option, + id: SourceIdentifierId, + ) -> Option { + if let Some(scope) = scope { + // The order in which we check for variables is important: + // otherwise, two variables with the same name are shadowed. + if let Some(var) = ResolveVariables::find_variable_impl(h, scope.parent_scope(h), id) { + Some(var) + } else { + scope.get_variable(h, id) + } + } else { + None + } + } +} + +impl Visitor for ResolveVariables { + fn visit_symbol_definition(&mut self, h: &mut Heap, def: DefinitionId) -> VisitorResult { + assert!(self.scope.is_none()); + self.scope = Some(Scope::Definition(def)); + recursive_symbol_definition(self, h, def)?; + self.scope = None; + Ok(()) + } + fn visit_variable_declaration(&mut self, h: &mut Heap, decl: VariableId) -> VisitorResult { + // This is only called for parameters of definitions and synchronous statements, + // since the local variables of block statements are still empty + // the moment it is traversed. After resolving variables, this + // function is also called for every local variable declaration. + + // We want to make sure that the resolved variable is the variable declared itself; + // otherwise, there is some variable defined in the parent scope. This check + // imposes that the order in which find_variable looks is significant! + let id = h[decl].identifier(); + let check_same = self.find_variable(h, id); + if let Some(check_same) = check_same { + if check_same != decl { + return Err(ParseError::new(h[id].position, "Declared variable clash")); + } + } + recursive_variable_declaration(self, h, decl) + } + fn visit_memory_statement(&mut self, h: &mut Heap, stmt: MemoryStatementId) -> VisitorResult { + assert!(!self.scope.is_none()); + let var = h[stmt].variable; + let id = h[var].identifier; + // First check whether variable with same identifier is in scope + let check_duplicate = self.find_variable(h, id); + if !check_duplicate.is_none() { + return Err(ParseError::new(h[id].position, "Declared variable clash")); + } + // Then check the expression's variables (this should not refer to own variable) + recursive_memory_statement(self, h, stmt)?; + // Finally, we may add the variable to the scope, which is guaranteed to be a block + { + let mut block = &mut h[self.scope.unwrap().to_block()]; + block.locals.push(var); + } + Ok(()) + } + fn visit_channel_statement(&mut self, h: &mut Heap, stmt: ChannelStatementId) -> VisitorResult { + assert!(!self.scope.is_none()); + // First handle the from variable + { + let var = h[stmt].from; + let id = h[var].identifier; + let check_duplicate = self.find_variable(h, id); + if !check_duplicate.is_none() { + return Err(ParseError::new(h[id].position, "Declared variable clash")); + } + let mut block = &mut h[self.scope.unwrap().to_block()]; + block.locals.push(var); + } + // Then handle the to variable (which may not be the same as the from) + { + let var = h[stmt].to; + let id = h[var].identifier; + let check_duplicate = self.find_variable(h, id); + if !check_duplicate.is_none() { + return Err(ParseError::new(h[id].position, "Declared variable clash")); + } + let mut block = &mut h[self.scope.unwrap().to_block()]; + block.locals.push(var); + } + Ok(()) + } + fn visit_block_statement(&mut self, h: &mut Heap, stmt: BlockStatementId) -> VisitorResult { + assert!(!self.scope.is_none()); + let old = self.scope; + self.scope = Some(Scope::Block(stmt)); + recursive_block_statement(self, h, stmt)?; + self.scope = old; + Ok(()) + } + fn visit_synchronous_statement( + &mut self, + h: &mut Heap, + stmt: SynchronousStatementId, + ) -> VisitorResult { + assert!(!self.scope.is_none()); + let old = self.scope; + self.scope = Some(Scope::Synchronous(stmt)); + recursive_synchronous_statement(self, h, stmt)?; + self.scope = old; + Ok(()) + } + fn visit_variable_expression( + &mut self, + h: &mut Heap, + expr: VariableExpressionId, + ) -> VisitorResult { + let var = self.get_variable(h, h[expr].identifier)?; + h[expr].declaration = Some(var); + Ok(()) + } +} + +struct UniqueStatementId(StatementId); + +struct LinkStatements { + prev: Option, +} + +impl LinkStatements { + fn new() -> Self { + LinkStatements { prev: None } + } +} + +impl Visitor for LinkStatements { + fn visit_statement(&mut self, h: &mut Heap, stmt: StatementId) -> VisitorResult { + if let Some(UniqueStatementId(prev)) = std::mem::replace(&mut self.prev, None) { + h[prev].link_next(stmt); + } + recursive_statement(self, h, stmt) + } + fn visit_local_statement(&mut self, _h: &mut Heap, stmt: LocalStatementId) -> VisitorResult { + self.prev = Some(UniqueStatementId(stmt.upcast())); + Ok(()) + } + fn visit_labeled_statement(&mut self, h: &mut Heap, stmt: LabeledStatementId) -> VisitorResult { + recursive_labeled_statement(self, h, stmt) + } + fn visit_skip_statement(&mut self, _h: &mut Heap, stmt: SkipStatementId) -> VisitorResult { + self.prev = Some(UniqueStatementId(stmt.upcast())); + Ok(()) + } + fn visit_if_statement(&mut self, h: &mut Heap, stmt: IfStatementId) -> VisitorResult { + // We allocate a pseudo-statement, which combines both branches into one next statement + let position = h[stmt].position; + let pseudo = + h.alloc_end_if_statement(|this| EndIfStatement { this, position, next: None }).upcast(); + assert!(self.prev.is_none()); + self.visit_statement(h, h[stmt].true_body)?; + if let Some(UniqueStatementId(prev)) = std::mem::replace(&mut self.prev, None) { + h[prev].link_next(pseudo); + } + assert!(self.prev.is_none()); + self.visit_statement(h, h[stmt].false_body)?; + if let Some(UniqueStatementId(prev)) = std::mem::replace(&mut self.prev, None) { + h[prev].link_next(pseudo); + } + // Use the pseudo-statement as the statement where to update the next pointer + self.prev = Some(UniqueStatementId(pseudo)); + Ok(()) + } + fn visit_while_statement(&mut self, h: &mut Heap, stmt: WhileStatementId) -> VisitorResult { + // We allocate a pseudo-statement, to which the break statement finds its target + let position = h[stmt].position; + let pseudo = + h.alloc_end_while_statement(|this| EndWhileStatement { this, position, next: None }); + // Update the while's next statement to point to the pseudo-statement + h[stmt].next = Some(pseudo); + assert!(self.prev.is_none()); + self.visit_statement(h, h[stmt].body)?; + // The body's next statement loops back to the while statement itself + // Note: continue statements also loop back to the while statement itself + if let Some(UniqueStatementId(prev)) = std::mem::replace(&mut self.prev, None) { + h[prev].link_next(stmt.upcast()); + } + // Use the while statement as the statement where the next pointer is updated + self.prev = Some(UniqueStatementId(pseudo.upcast())); + Ok(()) + } + fn visit_break_statement(&mut self, _h: &mut Heap, _stmt: BreakStatementId) -> VisitorResult { + Ok(()) + } + fn visit_continue_statement( + &mut self, + _h: &mut Heap, + _stmt: ContinueStatementId, + ) -> VisitorResult { + Ok(()) + } + fn visit_synchronous_statement( + &mut self, + h: &mut Heap, + stmt: SynchronousStatementId, + ) -> VisitorResult { + // Allocate a pseudo-statement, that is added for helping the evaluator to issue a command + // that marks the end of the synchronous block. Every evaluation has to pause at this + // point, only to resume later when the thread is selected as unique thread to continue. + let position = h[stmt].position; + let pseudo = h + .alloc_end_synchronous_statement(|this| EndSynchronousStatement { + this, + position, + next: None, + }) + .upcast(); + assert!(self.prev.is_none()); + self.visit_statement(h, h[stmt].body)?; + // The body's next statement points to the pseudo element + if let Some(UniqueStatementId(prev)) = std::mem::replace(&mut self.prev, None) { + h[prev].link_next(pseudo); + } + // Use the pseudo-statement as the statement where the next pointer is updated + self.prev = Some(UniqueStatementId(pseudo)); + Ok(()) + } + fn visit_return_statement(&mut self, h: &mut Heap, stmt: ReturnStatementId) -> VisitorResult { + Ok(()) + } + fn visit_assert_statement(&mut self, h: &mut Heap, stmt: AssertStatementId) -> VisitorResult { + self.prev = Some(UniqueStatementId(stmt.upcast())); + Ok(()) + } + fn visit_goto_statement(&mut self, _h: &mut Heap, _stmt: GotoStatementId) -> VisitorResult { + Ok(()) + } + fn visit_new_statement(&mut self, h: &mut Heap, stmt: NewStatementId) -> VisitorResult { + self.prev = Some(UniqueStatementId(stmt.upcast())); + Ok(()) + } + fn visit_put_statement(&mut self, h: &mut Heap, stmt: PutStatementId) -> VisitorResult { + self.prev = Some(UniqueStatementId(stmt.upcast())); + Ok(()) + } + fn visit_expression_statement( + &mut self, + h: &mut Heap, + stmt: ExpressionStatementId, + ) -> VisitorResult { + self.prev = Some(UniqueStatementId(stmt.upcast())); + Ok(()) + } + fn visit_expression(&mut self, h: &mut Heap, expr: ExpressionId) -> VisitorResult { + Ok(()) + } +} + +struct BuildLabels { + block: Option, + sync_enclosure: Option, +} + +impl BuildLabels { + fn new() -> Self { + BuildLabels { block: None, sync_enclosure: None } + } +} + +impl Visitor for BuildLabels { + fn visit_block_statement(&mut self, h: &mut Heap, stmt: BlockStatementId) -> VisitorResult { + assert_eq!(self.block, h[stmt].parent_block(h)); + let old = self.block; + self.block = Some(stmt); + recursive_block_statement(self, h, stmt)?; + self.block = old; + Ok(()) + } + fn visit_labeled_statement(&mut self, h: &mut Heap, stmt: LabeledStatementId) -> VisitorResult { + assert!(!self.block.is_none()); + // Store label in current block (on the fly) + h[self.block.unwrap()].labels.push(stmt); + // Update synchronous scope of label + h[stmt].in_sync = self.sync_enclosure; + recursive_labeled_statement(self, h, stmt) + } + fn visit_while_statement(&mut self, h: &mut Heap, stmt: WhileStatementId) -> VisitorResult { + h[stmt].in_sync = self.sync_enclosure; + recursive_while_statement(self, h, stmt) + } + fn visit_synchronous_statement( + &mut self, + h: &mut Heap, + stmt: SynchronousStatementId, + ) -> VisitorResult { + assert!(self.sync_enclosure.is_none()); + self.sync_enclosure = Some(stmt); + recursive_synchronous_statement(self, h, stmt)?; + self.sync_enclosure = None; + Ok(()) + } + fn visit_expression(&mut self, h: &mut Heap, expr: ExpressionId) -> VisitorResult { + Ok(()) + } +} + +struct ResolveLabels { + block: Option, + while_enclosure: Option, + sync_enclosure: Option, +} + +impl ResolveLabels { + fn new() -> Self { + ResolveLabels { block: None, while_enclosure: None, sync_enclosure: None } + } + fn check_duplicate_impl( + h: &Heap, + block: Option, + stmt: LabeledStatementId, + ) -> VisitorResult { + if let Some(block) = block { + // Checking the parent first is important. Otherwise, labels + // overshadow previously defined labels: and this is illegal! + ResolveLabels::check_duplicate_impl(h, h[block].parent_block(h), stmt)?; + // For the current block, check for a duplicate. + for &other_stmt in h[block].labels.iter() { + if other_stmt == stmt { + continue; + } else { + if h[h[other_stmt].label] == h[h[stmt].label] { + return Err(ParseError::new(h[stmt].position, "Duplicate label")); + } + } + } + } + Ok(()) + } + fn check_duplicate(&self, h: &Heap, stmt: LabeledStatementId) -> VisitorResult { + ResolveLabels::check_duplicate_impl(h, self.block, stmt) + } + fn get_target( + &self, + h: &Heap, + id: SourceIdentifierId, + ) -> Result { + if let Some(stmt) = ResolveLabels::find_target(h, self.block, id) { + Ok(stmt) + } else { + Err(ParseError::new(h[id].position, "Unresolved label")) + } + } + fn find_target( + h: &Heap, + block: Option, + id: SourceIdentifierId, + ) -> Option { + if let Some(block) = block { + // It does not matter in what order we find the labels. + // If there are duplicates: that is checked elsewhere. + for &stmt in h[block].labels.iter() { + if h[h[stmt].label] == h[id] { + return Some(stmt); + } + } + if let Some(stmt) = ResolveLabels::find_target(h, h[block].parent_block(h), id) { + return Some(stmt); + } + } + None + } +} + +impl Visitor for ResolveLabels { + fn visit_block_statement(&mut self, h: &mut Heap, stmt: BlockStatementId) -> VisitorResult { + assert_eq!(self.block, h[stmt].parent_block(h)); + let old = self.block; + self.block = Some(stmt); + recursive_block_statement(self, h, stmt)?; + self.block = old; + Ok(()) + } + fn visit_labeled_statement(&mut self, h: &mut Heap, stmt: LabeledStatementId) -> VisitorResult { + assert!(!self.block.is_none()); + self.check_duplicate(h, stmt)?; + recursive_labeled_statement(self, h, stmt) + } + fn visit_while_statement(&mut self, h: &mut Heap, stmt: WhileStatementId) -> VisitorResult { + let old = self.while_enclosure; + self.while_enclosure = Some(stmt); + recursive_while_statement(self, h, stmt)?; + self.while_enclosure = old; + Ok(()) + } + fn visit_break_statement(&mut self, h: &mut Heap, stmt: BreakStatementId) -> VisitorResult { + let the_while; + if let Some(label) = h[stmt].label { + let target = self.get_target(h, label)?; + let target = &h[h[target].body]; + if !target.is_while() { + return Err(ParseError::new( + h[stmt].position, + "Illegal break: target not a while statement", + )); + } + the_while = target.as_while(); + // TODO: check if break is nested under while + } else { + if self.while_enclosure.is_none() { + return Err(ParseError::new( + h[stmt].position, + "Illegal break: no surrounding while statement", + )); + } + the_while = &h[self.while_enclosure.unwrap()]; + // break is always nested under while, by recursive vistor + } + if the_while.in_sync != self.sync_enclosure { + return Err(ParseError::new( + h[stmt].position, + "Illegal break: synchronous statement escape", + )); + } + h[stmt].target = the_while.next; + Ok(()) + } + fn visit_continue_statement( + &mut self, + h: &mut Heap, + stmt: ContinueStatementId, + ) -> VisitorResult { + let the_while; + if let Some(label) = h[stmt].label { + let target = self.get_target(h, label)?; + let target = &h[h[target].body]; + if !target.is_while() { + return Err(ParseError::new( + h[stmt].position, + "Illegal continue: target not a while statement", + )); + } + the_while = target.as_while(); + // TODO: check if continue is nested under while + } else { + if self.while_enclosure.is_none() { + return Err(ParseError::new( + h[stmt].position, + "Illegal continue: no surrounding while statement", + )); + } + the_while = &h[self.while_enclosure.unwrap()]; + // continue is always nested under while, by recursive vistor + } + if the_while.in_sync != self.sync_enclosure { + return Err(ParseError::new( + h[stmt].position, + "Illegal continue: synchronous statement escape", + )); + } + h[stmt].target = Some(the_while.this); + Ok(()) + } + fn visit_synchronous_statement( + &mut self, + h: &mut Heap, + stmt: SynchronousStatementId, + ) -> VisitorResult { + assert!(self.sync_enclosure.is_none()); + self.sync_enclosure = Some(stmt); + recursive_synchronous_statement(self, h, stmt)?; + self.sync_enclosure = None; + Ok(()) + } + fn visit_goto_statement(&mut self, h: &mut Heap, stmt: GotoStatementId) -> VisitorResult { + let target = self.get_target(h, h[stmt].label)?; + if h[target].in_sync != self.sync_enclosure { + return Err(ParseError::new( + h[stmt].position, + "Illegal goto: synchronous statement escape", + )); + } + h[stmt].target = Some(target); + Ok(()) + } + fn visit_expression(&mut self, h: &mut Heap, expr: ExpressionId) -> VisitorResult { + Ok(()) + } +} + +struct AssignableExpressions { + assignable: bool, +} + +impl AssignableExpressions { + fn new() -> Self { + AssignableExpressions { assignable: false } + } + fn error(&self, position: InputPosition) -> VisitorResult { + Err(ParseError::new(position, "Unassignable expression")) + } +} + +impl Visitor for AssignableExpressions { + fn visit_assignment_expression( + &mut self, + h: &mut Heap, + expr: AssignmentExpressionId, + ) -> VisitorResult { + if self.assignable { + self.error(h[expr].position) + } else { + self.assignable = true; + self.visit_expression(h, h[expr].left)?; + self.assignable = false; + self.visit_expression(h, h[expr].right) + } + } + fn visit_conditional_expression( + &mut self, + h: &mut Heap, + expr: ConditionalExpressionId, + ) -> VisitorResult { + if self.assignable { + self.error(h[expr].position) + } else { + recursive_conditional_expression(self, h, expr) + } + } + fn visit_binary_expression(&mut self, h: &mut Heap, expr: BinaryExpressionId) -> VisitorResult { + if self.assignable { + self.error(h[expr].position) + } else { + recursive_binary_expression(self, h, expr) + } + } + fn visit_unary_expression(&mut self, h: &mut Heap, expr: UnaryExpressionId) -> VisitorResult { + if self.assignable { + self.error(h[expr].position) + } else { + match h[expr].operation { + UnaryOperation::PostDecrement + | UnaryOperation::PreDecrement + | UnaryOperation::PostIncrement + | UnaryOperation::PreIncrement => { + self.assignable = true; + recursive_unary_expression(self, h, expr)?; + self.assignable = false; + Ok(()) + } + _ => recursive_unary_expression(self, h, expr), + } + } + } + fn visit_indexing_expression( + &mut self, + h: &mut Heap, + expr: IndexingExpressionId, + ) -> VisitorResult { + let old = self.assignable; + self.assignable = false; + recursive_indexing_expression(self, h, expr)?; + self.assignable = old; + Ok(()) + } + fn visit_slicing_expression( + &mut self, + h: &mut Heap, + expr: SlicingExpressionId, + ) -> VisitorResult { + let old = self.assignable; + self.assignable = false; + recursive_slicing_expression(self, h, expr)?; + self.assignable = old; + Ok(()) + } + fn visit_select_expression(&mut self, h: &mut Heap, expr: SelectExpressionId) -> VisitorResult { + if h[expr].field.is_length() && self.assignable { + return self.error(h[expr].position); + } + let old = self.assignable; + self.assignable = false; + recursive_select_expression(self, h, expr)?; + self.assignable = old; + Ok(()) + } + fn visit_array_expression(&mut self, h: &mut Heap, expr: ArrayExpressionId) -> VisitorResult { + if self.assignable { + self.error(h[expr].position) + } else { + recursive_array_expression(self, h, expr) + } + } + fn visit_call_expression(&mut self, h: &mut Heap, expr: CallExpressionId) -> VisitorResult { + if self.assignable { + self.error(h[expr].position) + } else { + recursive_call_expression(self, h, expr) + } + } + fn visit_constant_expression( + &mut self, + h: &mut Heap, + expr: ConstantExpressionId, + ) -> VisitorResult { + if self.assignable { + self.error(h[expr].position) + } else { + Ok(()) + } + } + fn visit_variable_expression( + &mut self, + h: &mut Heap, + expr: VariableExpressionId, + ) -> VisitorResult { + Ok(()) + } +} + +struct IndexableExpressions { + indexable: bool, +} + +impl IndexableExpressions { + fn new() -> Self { + IndexableExpressions { indexable: false } + } + fn error(&self, position: InputPosition) -> VisitorResult { + Err(ParseError::new(position, "Unindexable expression")) + } +} + +impl Visitor for IndexableExpressions { + fn visit_assignment_expression( + &mut self, + h: &mut Heap, + expr: AssignmentExpressionId, + ) -> VisitorResult { + if self.indexable { + self.error(h[expr].position) + } else { + recursive_assignment_expression(self, h, expr) + } + } + fn visit_conditional_expression( + &mut self, + h: &mut Heap, + expr: ConditionalExpressionId, + ) -> VisitorResult { + let old = self.indexable; + self.indexable = false; + self.visit_expression(h, h[expr].test)?; + self.indexable = old; + self.visit_expression(h, h[expr].true_expression)?; + self.visit_expression(h, h[expr].false_expression) + } + fn visit_binary_expression(&mut self, h: &mut Heap, expr: BinaryExpressionId) -> VisitorResult { + if self.indexable && h[expr].operation != BinaryOperator::Concatenate { + self.error(h[expr].position) + } else { + recursive_binary_expression(self, h, expr) + } + } + fn visit_unary_expression(&mut self, h: &mut Heap, expr: UnaryExpressionId) -> VisitorResult { + if self.indexable { + self.error(h[expr].position) + } else { + recursive_unary_expression(self, h, expr) + } + } + fn visit_indexing_expression( + &mut self, + h: &mut Heap, + expr: IndexingExpressionId, + ) -> VisitorResult { + if self.indexable { + self.error(h[expr].position) + } else { + self.indexable = true; + self.visit_expression(h, h[expr].subject)?; + self.indexable = false; + self.visit_expression(h, h[expr].index) + } + } + fn visit_slicing_expression( + &mut self, + h: &mut Heap, + expr: SlicingExpressionId, + ) -> VisitorResult { + let old = self.indexable; + self.indexable = true; + self.visit_expression(h, h[expr].subject)?; + self.indexable = false; + self.visit_expression(h, h[expr].from_index)?; + self.visit_expression(h, h[expr].to_index)?; + self.indexable = old; + Ok(()) + } + fn visit_select_expression(&mut self, h: &mut Heap, expr: SelectExpressionId) -> VisitorResult { + let old = self.indexable; + self.indexable = false; + recursive_select_expression(self, h, expr)?; + self.indexable = old; + Ok(()) + } + fn visit_array_expression(&mut self, h: &mut Heap, expr: ArrayExpressionId) -> VisitorResult { + let old = self.indexable; + self.indexable = false; + recursive_array_expression(self, h, expr)?; + self.indexable = old; + Ok(()) + } + fn visit_call_expression(&mut self, h: &mut Heap, expr: CallExpressionId) -> VisitorResult { + let old = self.indexable; + self.indexable = false; + recursive_call_expression(self, h, expr)?; + self.indexable = old; + Ok(()) + } + fn visit_constant_expression( + &mut self, + h: &mut Heap, + expr: ConstantExpressionId, + ) -> VisitorResult { + if self.indexable { + self.error(h[expr].position) + } else { + Ok(()) + } + } +} + +struct SelectableExpressions { + selectable: bool, +} + +impl SelectableExpressions { + fn new() -> Self { + SelectableExpressions { selectable: false } + } + fn error(&self, position: InputPosition) -> VisitorResult { + Err(ParseError::new(position, "Unselectable expression")) + } +} + +impl Visitor for SelectableExpressions { + fn visit_assignment_expression( + &mut self, + h: &mut Heap, + expr: AssignmentExpressionId, + ) -> VisitorResult { + // left-hand side of assignment can be skipped + let old = self.selectable; + self.selectable = false; + self.visit_expression(h, h[expr].right)?; + self.selectable = old; + Ok(()) + } + fn visit_conditional_expression( + &mut self, + h: &mut Heap, + expr: ConditionalExpressionId, + ) -> VisitorResult { + let old = self.selectable; + self.selectable = false; + self.visit_expression(h, h[expr].test)?; + self.selectable = old; + self.visit_expression(h, h[expr].true_expression)?; + self.visit_expression(h, h[expr].false_expression) + } + fn visit_binary_expression(&mut self, h: &mut Heap, expr: BinaryExpressionId) -> VisitorResult { + if self.selectable && h[expr].operation != BinaryOperator::Concatenate { + self.error(h[expr].position) + } else { + recursive_binary_expression(self, h, expr) + } + } + fn visit_unary_expression(&mut self, h: &mut Heap, expr: UnaryExpressionId) -> VisitorResult { + if self.selectable { + self.error(h[expr].position) + } else { + recursive_unary_expression(self, h, expr) + } + } + fn visit_indexing_expression( + &mut self, + h: &mut Heap, + expr: IndexingExpressionId, + ) -> VisitorResult { + let old = self.selectable; + self.selectable = false; + recursive_indexing_expression(self, h, expr)?; + self.selectable = old; + Ok(()) + } + fn visit_slicing_expression( + &mut self, + h: &mut Heap, + expr: SlicingExpressionId, + ) -> VisitorResult { + let old = self.selectable; + self.selectable = false; + recursive_slicing_expression(self, h, expr)?; + self.selectable = old; + Ok(()) + } + fn visit_select_expression(&mut self, h: &mut Heap, expr: SelectExpressionId) -> VisitorResult { + let old = self.selectable; + self.selectable = false; + recursive_select_expression(self, h, expr)?; + self.selectable = old; + Ok(()) + } + fn visit_array_expression(&mut self, h: &mut Heap, expr: ArrayExpressionId) -> VisitorResult { + let old = self.selectable; + self.selectable = false; + recursive_array_expression(self, h, expr)?; + self.selectable = old; + Ok(()) + } + fn visit_call_expression(&mut self, h: &mut Heap, expr: CallExpressionId) -> VisitorResult { + let old = self.selectable; + self.selectable = false; + recursive_call_expression(self, h, expr)?; + self.selectable = old; + Ok(()) + } + fn visit_constant_expression( + &mut self, + h: &mut Heap, + expr: ConstantExpressionId, + ) -> VisitorResult { + if self.selectable { + self.error(h[expr].position) + } else { + Ok(()) + } + } +} + +struct CheckMainComponent {} + +impl CheckMainComponent { + fn new() -> Self { + CheckMainComponent {} + } + fn visit_protocol_description(&mut self, h: &mut Heap, root: RootId) -> VisitorResult { + let sym = h.get_external_identifier(b"main"); + let root = &h[root]; + let def = root.get_definition(h, sym.upcast()); + if def.is_none() { + return Err(ParseError::new(root.position, "Missing main definition")); + } + let def = &h[def.unwrap()]; + if !def.is_component() { + return Err(ParseError::new(def.position(), "Main definition must be a component")); + } + for ¶m in def.parameters().iter() { + let param = &h[param]; + let type_annot = &h[param.type_annotation]; + if type_annot.the_type.array { + return Err(ParseError::new(type_annot.position, "Illegal type")); + } + match type_annot.the_type.primitive { + PrimitiveType::Input | PrimitiveType::Output => continue, + _ => { + return Err(ParseError::new(type_annot.position, "Illegal type")); + } + } + } + Ok(()) + } +} + +pub struct Parser<'a> { + source: &'a mut InputSource, +} + +impl<'a> Parser<'a> { + pub fn new(source: &'a mut InputSource) -> Self { + Parser { source } + } + pub fn parse(&mut self, h: &mut Heap) -> Result { + let mut lex = Lexer::new(self.source); + let pd = lex.consume_protocol_description(h)?; + NestedSynchronousStatements::new().visit_protocol_description(h, pd)?; + ChannelStatementOccurrences::new().visit_protocol_description(h, pd)?; + FunctionStatementReturns::new().visit_protocol_description(h, pd)?; + ComponentStatementReturnNew::new().visit_protocol_description(h, pd)?; + CheckBuiltinOccurrences::new().visit_protocol_description(h, pd)?; + BuildSymbolDeclarations::new().visit_protocol_description(h, pd)?; + LinkCallExpressions::new().visit_protocol_description(h, pd)?; + BuildScope::new().visit_protocol_description(h, pd)?; + ResolveVariables::new().visit_protocol_description(h, pd)?; + LinkStatements::new().visit_protocol_description(h, pd)?; + BuildLabels::new().visit_protocol_description(h, pd)?; + ResolveLabels::new().visit_protocol_description(h, pd)?; + AssignableExpressions::new().visit_protocol_description(h, pd)?; + IndexableExpressions::new().visit_protocol_description(h, pd)?; + SelectableExpressions::new().visit_protocol_description(h, pd)?; + CheckMainComponent::new().visit_protocol_description(h, pd)?; + Ok(pd) + } +} + +#[cfg(test)] +mod tests { + extern crate test_generator; + + use std::fs::File; + use std::io::Read; + use std::path::Path; + + use test_generator::test_resources; + + use super::*; + + #[test_resources("testdata/parser/positive/*.pdl")] + fn batch1(resource: &str) { + let path = Path::new(resource); + let mut heap = Heap::new(); + let mut source = InputSource::from_file(&path).unwrap(); + let mut parser = Parser::new(&mut source); + match parser.parse(&mut heap) { + Ok(_) => {} + Err(err) => { + println!("{}", err.display(&source)); + println!("{:?}", err); + assert!(false); + } + } + } + + #[test_resources("testdata/parser/negative/*.pdl")] + fn batch2(resource: &str) { + let path = Path::new(resource); + let expect = path.with_extension("txt"); + let mut heap = Heap::new(); + let mut source = InputSource::from_file(&path).unwrap(); + let mut parser = Parser::new(&mut source); + match parser.parse(&mut heap) { + Ok(pd) => { + println!("{:?}", heap[pd]); + println!("Expected parse error:"); + + let mut cev: Vec = Vec::new(); + let mut f = File::open(expect).unwrap(); + f.read_to_end(&mut cev).unwrap(); + println!("{}", String::from_utf8_lossy(&cev)); + assert!(false); + } + Err(err) => { + println!("{:?}", err); + + let mut vec: Vec = Vec::new(); + err.write(&source, &mut vec).unwrap(); + println!("{}", String::from_utf8_lossy(&vec)); + + let mut cev: Vec = Vec::new(); + let mut f = File::open(expect).unwrap(); + f.read_to_end(&mut cev).unwrap(); + println!("{}", String::from_utf8_lossy(&cev)); + + assert_eq!(vec, cev); + } + } + } +} diff --git a/src/runtime/actors.rs b/src/runtime/actors.rs new file mode 100644 index 0000000000000000000000000000000000000000..ed3c75146dcbd158ade0e1044c0438d372bfab38 --- /dev/null +++ b/src/runtime/actors.rs @@ -0,0 +1,250 @@ +use crate::common::*; +use crate::runtime::{endpoint::*, *}; + +#[derive(Debug)] +pub(crate) struct MonoN { + pub ekeys: HashSet, + pub result: Option<(usize, HashMap)>, +} +#[derive(Debug)] +pub(crate) struct PolyN { + pub ekeys: HashSet, + pub branches: HashMap, +} +#[derive(Debug, Clone)] +pub(crate) struct BranchN { + pub to_get: HashSet, + pub gotten: HashMap, + pub sync_batch_index: usize, +} + +#[derive(Debug)] +pub struct MonoP { + pub state: ProtocolS, + pub ekeys: HashSet, +} +#[derive(Debug)] +pub(crate) struct PolyP { + pub incomplete: HashMap, + pub complete: HashMap, + pub ekeys: HashSet, +} +#[derive(Debug, Clone)] +pub(crate) struct BranchP { + pub inbox: HashMap, + pub state: ProtocolS, +} + +////////////////////////////////////////////////////////////////// + +impl PolyP { + pub(crate) fn poly_run( + &mut self, + m_ctx: PolyPContext, + protocol_description: &ProtocolD, + ) -> Result { + let to_run: Vec<_> = self.incomplete.drain().collect(); + self.poly_run_these_branches(m_ctx, protocol_description, to_run) + } + + pub(crate) fn poly_run_these_branches( + &mut self, + mut m_ctx: PolyPContext, + protocol_description: &ProtocolD, + mut to_run: Vec<(Predicate, BranchP)>, + ) -> Result { + use SyncRunResult as Srr; + while let Some((mut predicate, mut branch)) = to_run.pop() { + let mut r_ctx = BranchPContext { + m_ctx: m_ctx.reborrow(), + ekeys: &self.ekeys, + predicate: &predicate, + inbox: &branch.inbox, + }; + use PolyBlocker as Sb; + let blocker = branch.state.sync_run(&mut r_ctx, protocol_description); + match blocker { + Sb::Inconsistent => {} // DROP + Sb::CouldntReadMsg(ekey) => { + assert!(self.ekeys.contains(&ekey)); + let channel_id = + r_ctx.m_ctx.inner.endpoint_exts.get(ekey).unwrap().info.channel_id; + if predicate.replace_assignment(channel_id, true) != Some(false) { + // don't rerun now. Rerun at next `sync_run` + self.incomplete.insert(predicate, branch); + } + // ELSE DROP + } + Sb::CouldntCheckFiring(ekey) => { + assert!(self.ekeys.contains(&ekey)); + let channel_id = + r_ctx.m_ctx.inner.endpoint_exts.get(ekey).unwrap().info.channel_id; + // split the branch! + let branch_f = branch.clone(); + let mut predicate_f = predicate.clone(); + if predicate_f.replace_assignment(channel_id, false).is_some() { + panic!("OI HANS QUERY FIRST!"); + } + assert!(predicate.replace_assignment(channel_id, true).is_none()); + to_run.push((predicate, branch)); + to_run.push((predicate_f, branch_f)); + } + Sb::SyncBlockEnd => { + // come up with the predicate for this local solution + let ekeys_channel_id_iter = self + .ekeys + .iter() + .map(|&ekey| m_ctx.inner.endpoint_exts.get(ekey).unwrap().info.channel_id); + predicate.batch_assign_nones(ekeys_channel_id_iter, false); + // report the local solution + m_ctx + .solution_storage + .submit_and_digest_subtree_solution(m_ctx.my_subtree_id, predicate.clone()); + // store the solution for recovering later + self.complete.insert(predicate, branch); + } + Sb::PutMsg(ekey, payload) => { + assert!(self.ekeys.contains(&ekey)); + let EndpointExt { info, endpoint } = + m_ctx.inner.endpoint_exts.get_mut(ekey).unwrap(); + if predicate.replace_assignment(info.channel_id, true) != Some(false) { + let msg = CommMsgContents::SendPayload { + payload_predicate: predicate.clone(), + payload, + } + .into_msg(m_ctx.inner.round_index); + endpoint.send(msg)?; + to_run.push((predicate, branch)); + } + // ELSE DROP + } + } + } + // all in self.incomplete most recently returned Blocker::CouldntReadMsg + Ok(if self.incomplete.is_empty() { + if self.complete.is_empty() { + Srr::NoBranches + } else { + Srr::AllBranchesComplete + } + } else { + Srr::BlockingForRecv + }) + } + + pub(crate) fn poly_recv_run( + &mut self, + m_ctx: PolyPContext, + protocol_description: &ProtocolD, + ekey: Key, + payload_predicate: Predicate, + payload: Payload, + ) -> Result { + // try exact match + let to_run = if self.complete.contains_key(&payload_predicate) { + // exact match with stopped machine + vec![] + } else if let Some(mut branch) = self.incomplete.remove(&payload_predicate) { + // exact match with running machine + branch.inbox.insert(ekey, payload); + vec![(payload_predicate, branch)] + } else { + let mut incomplete2 = HashMap::<_, _>::default(); + let to_run = self + .incomplete + .drain() + .filter_map(|(old_predicate, mut branch)| { + use CommonSatResult as Csr; + match old_predicate.common_satisfier(&payload_predicate) { + Csr::FormerNotLatter | Csr::Equivalent => { + // old_predicate COVERS the assumptions of payload_predicate + let was = branch.inbox.insert(ekey, payload.clone()); + assert!(was.is_none()); // INBOX MUST BE EMPTY! + Some((old_predicate, branch)) + } + Csr::New(unified) => { + // payload_predicate has new assumptions. FORK! + let mut payload_branch = branch.clone(); + let was = payload_branch.inbox.insert(ekey, payload.clone()); + assert!(was.is_none()); // INBOX MUST BE EMPTY! + + // put the original back untouched + incomplete2.insert(old_predicate, branch); + Some((unified, payload_branch)) + } + Csr::LatterNotFormer => { + // payload_predicate has new assumptions. FORK! + let mut payload_branch = branch.clone(); + let was = payload_branch.inbox.insert(ekey, payload.clone()); + assert!(was.is_none()); // INBOX MUST BE EMPTY! + + // put the original back untouched + incomplete2.insert(old_predicate, branch); + Some((payload_predicate.clone(), payload_branch)) + } + Csr::Nonexistant => { + // predicates contradict + incomplete2.insert(old_predicate, branch); + None + } + } + }) + .collect(); + std::mem::swap(&mut self.incomplete, &mut incomplete2); + to_run + }; + self.poly_run_these_branches(m_ctx, protocol_description, to_run) + } + + pub(crate) fn become_mono( + mut self, + decision: &Predicate, + all_inboxes: &mut HashMap, + ) -> MonoP { + if let Some((_, branch)) = self.complete.drain().find(|(p, _)| decision.satisfies(p)) { + let BranchP { inbox, state } = branch; + for (key, payload) in inbox { + assert!(all_inboxes.insert(key, payload).is_none()); + } + self.incomplete.clear(); + MonoP { state, ekeys: self.ekeys } + } else { + panic!("No such solution!") + } + } +} + +impl PolyN { + pub fn sync_recv( + &mut self, + ekey: Key, + payload: Payload, + solution_storage: &mut SolutionStorage, + ) { + for (predicate, branch) in self.branches.iter_mut() { + if branch.to_get.remove(&ekey) { + branch.gotten.insert(ekey, payload.clone()); + if branch.to_get.is_empty() { + solution_storage + .submit_and_digest_subtree_solution(SubtreeId::PolyN, predicate.clone()); + } + } + } + } + + pub fn become_mono( + mut self, + decision: &Predicate, + all_inboxes: &mut HashMap, + ) -> MonoN { + if let Some((_, branch)) = self.branches.drain().find(|(p, _)| decision.satisfies(p)) { + let BranchN { gotten, sync_batch_index, .. } = branch; + for (&key, payload) in gotten.iter() { + assert!(all_inboxes.insert(key, payload.clone()).is_none()); + } + MonoN { ekeys: self.ekeys, result: Some((sync_batch_index, gotten)) } + } else { + panic!("No such solution!") + } + } +} diff --git a/src/runtime/communication.rs b/src/runtime/communication.rs new file mode 100644 index 0000000000000000000000000000000000000000..1c0749e848722b157d134d84e1ac6713b79813ea --- /dev/null +++ b/src/runtime/communication.rs @@ -0,0 +1,582 @@ +use crate::common::*; +use crate::runtime::{actors::*, endpoint::*, errors::*, *}; + +macro_rules! lockprintln { + () => (print!("\n")); + ($($arg:tt)*) => ({ + use std::io::Write; + std::writeln!(std::io::stdout().lock(), $($arg)*).expect("LPRINTLN"); + }) +} + +impl Controller { + fn end_round_with_decision(&mut self, decision: Predicate) -> Result<(), SyncErr> { + let mut all_inboxes = HashMap::default(); + self.inner.mono_n = self + .ephemeral + .poly_n + .take() + .map(|poly_n| poly_n.become_mono(&decision, &mut all_inboxes)); + self.inner.mono_ps.extend( + self.ephemeral.poly_ps.drain(..).map(|m| m.become_mono(&decision, &mut all_inboxes)), + ); + let valuations: HashMap<_, _> = all_inboxes + .drain() + .map(|(ekey, payload)| { + let channel_id = self.inner.endpoint_exts.get(ekey).unwrap().info.channel_id; + (channel_id, Some(payload)) + }) + .collect(); + for (channel_id, value) in decision.assigned.iter() { + if !value { + println!("VALUE {:?} => *", channel_id); + } else if let Some(payload) = valuations.get(channel_id) { + println!("VALUE {:?} => Message({:?})", channel_id, payload); + } else { + println!("VALUE {:?} => Message(?)", channel_id); + } + } + let announcement = + CommMsgContents::Announce { oracle: decision }.into_msg(self.inner.round_index); + for &child_ekey in self.inner.family.children_ekeys.iter() { + self.inner + .endpoint_exts + .get_mut(child_ekey) + .expect("eefef") + .endpoint + .send(announcement.clone())?; + } + self.inner.round_index += 1; + self.ephemeral.clear(); + Ok(()) + } + + // Drain self.ephemeral.solution_storage and handle the new locals. Return decision if one is found + fn handle_locals_maybe_decide(&mut self) -> Result { + if let Some(parent_ekey) = self.inner.family.parent_ekey { + // I have a parent -> I'm not the leader + let parent_endpoint = + &mut self.inner.endpoint_exts.get_mut(parent_ekey).expect("huu").endpoint; + for partial_oracle in self.ephemeral.solution_storage.iter_new_local_make_old() { + let msg = + CommMsgContents::Elaborate { partial_oracle }.into_msg(self.inner.round_index); + parent_endpoint.send(msg)?; + } + Ok(false) + } else { + // I have no parent -> I'm the leader + assert!(self.inner.family.parent_ekey.is_none()); + let maybe_decision = self.ephemeral.solution_storage.iter_new_local_make_old().next(); + Ok(if let Some(decision) = maybe_decision { + self.end_round_with_decision(decision)?; + true + } else { + false + }) + } + } + + fn kick_off_native( + &mut self, + sync_batches: impl Iterator, + ) -> Result { + let MonoN { ekeys, .. } = self.inner.mono_n.take().unwrap(); + let Self { inner: ControllerInner { endpoint_exts, round_index, .. }, .. } = self; + let mut branches = HashMap::<_, _>::default(); + for (sync_batch_index, SyncBatch { puts, gets }) in sync_batches.enumerate() { + let ekey_to_channel_id = |ekey| endpoint_exts.get(ekey).unwrap().info.channel_id; + let all_ekeys = ekeys.iter().copied(); + let all_channel_ids = all_ekeys.map(ekey_to_channel_id); + + let mut predicate = Predicate::new_trivial(); + + // assign TRUE for puts and gets + let true_ekeys = puts.keys().chain(gets.iter()).copied(); + let true_channel_ids = true_ekeys.clone().map(ekey_to_channel_id); + predicate.batch_assign_nones(true_channel_ids, true); + + // assign FALSE for all in interface not assigned true + predicate.batch_assign_nones(all_channel_ids.clone(), false); + + if branches.contains_key(&predicate) { + // TODO what do I do with redundant predicates? + unimplemented!( + "Having multiple batches with the same + predicate requires the support of oracle boolean variables" + ) + } + let branch = BranchN { + to_get: true_ekeys.collect(), + gotten: Default::default(), + sync_batch_index, + }; + for (ekey, payload) in puts { + let msg = + CommMsgContents::SendPayload { payload_predicate: predicate.clone(), payload } + .into_msg(*round_index); + endpoint_exts.get_mut(ekey).unwrap().endpoint.send(msg)?; + } + if branch.to_get.is_empty() { + self.ephemeral + .solution_storage + .submit_and_digest_subtree_solution(SubtreeId::PolyN, predicate.clone()); + } + branches.insert(predicate, branch); + } + Ok(PolyN { ekeys, branches }) + } + + // Runs a synchronous round until all the actors are in decided state OR 1+ are inconsistent. + // If a native requires setting up, arg `sync_batches` is Some, and those are used as the sync batches. + pub fn sync_round( + &mut self, + deadline: Instant, + sync_batches: Option>, + ) -> Result<(), SyncErr> { + // TODO! fuse handle_locals_return_decision and end_round_return_decision + + assert!(self.ephemeral.is_clear()); + + let cid = self.inner.channel_id_stream.controller_id; + lockprintln!(); + lockprintln!("~~~~~~ {:?}: SYNC ROUND STARTS! ROUND={}", cid, self.inner.round_index); + + // 1. Run the Mono for each Mono actor (stored in `self.mono_ps`). + // Some actors are dropped. some new actors are created. + // Ultimately, we have 0 Mono actors and a list of unnamed sync_actors + lockprintln!("{:?}: Got {} MonoP's to run!", cid, self.inner.mono_ps.len()); + self.ephemeral.poly_ps.clear(); + // let mut poly_ps: Vec = vec![]; + while let Some(mut mono_p) = self.inner.mono_ps.pop() { + let mut m_ctx = MonoPContext { + ekeys: &mut mono_p.ekeys, + inner: &mut self.inner, + // endpoint_exts: &mut self.endpoint_exts, + // mono_ps: &mut self.mono_ps, + // channel_id_stream: &mut self.channel_id_stream, + }; + // cross boundary into crate::protocol + let blocker = mono_p.state.pre_sync_run(&mut m_ctx, &self.protocol_description); + lockprintln!("{:?}: ... MonoP's pre_sync_run got blocker {:?}", cid, &blocker); + match blocker { + MonoBlocker::Inconsistent => return Err(SyncErr::Inconsistent), + MonoBlocker::ComponentExit => drop(mono_p), + MonoBlocker::SyncBlockStart => self.ephemeral.poly_ps.push(mono_p.into()), + } + } + lockprintln!( + "{:?}: Finished running all MonoPs! Have {} PolyPs waiting", + cid, + self.ephemeral.poly_ps.len() + ); + + // 3. define the mapping from ekey -> actor + // this is needed during the event loop to determine which actor + // should receive the incoming message. + // TODO: store and update this mapping rather than rebuilding it each round. + let ekey_to_holder: HashMap = { + use PolyId::*; + let n = self.inner.mono_n.iter().flat_map(|m| m.ekeys.iter().map(move |&e| (e, N))); + let p = self + .ephemeral + .poly_ps + .iter() + .enumerate() + .flat_map(|(index, m)| m.ekeys.iter().map(move |&e| (e, P { index }))); + n.chain(p).collect() + }; + lockprintln!( + "{:?}: SET OF PolyPs and MonoPs final! ekey lookup map is {:?}", + cid, + &ekey_to_holder + ); + + // 4. Create the solution storage. it tracks the solutions of "subtrees" + // of the controller in the overlay tree. + self.ephemeral.solution_storage.reset({ + let n = self.inner.mono_n.iter().map(|_| SubtreeId::PolyN); + let m = (0..self.ephemeral.poly_ps.len()).map(|index| SubtreeId::PolyP { index }); + let c = self + .inner + .family + .children_ekeys + .iter() + .map(|&ekey| SubtreeId::ChildController { ekey }); + let subtree_id_iter = n.chain(m).chain(c); + lockprintln!( + "{:?}: Solution Storage has subtree Ids: {:?}", + cid, + &subtree_id_iter.clone().collect::>() + ); + subtree_id_iter + }); + + // 5. kick off the synchronous round of the native actor if it exists + + lockprintln!("{:?}: Kicking off native's synchronous round...", cid); + assert_eq!(sync_batches.is_some(), self.inner.mono_n.is_some()); // TODO better err + self.ephemeral.poly_n = if let Some(sync_batches) = sync_batches { + // using if let because of nested ? operator + // TODO check that there are 1+ branches or NO SOLUTION + let poly_n = self.kick_off_native(sync_batches)?; + lockprintln!( + "{:?}: PolyN kicked off, and has branches with predicates... {:?}", + cid, + poly_n.branches.keys().collect::>() + ); + Some(poly_n) + } else { + lockprintln!("{:?}: NO NATIVE COMPONENT", cid); + None + }; + + // 6. Kick off the synchronous round of each protocol actor + // If just one actor becomes inconsistent now, there can be no solution! + // TODO distinguish between completed and not completed poly_p's? + lockprintln!("{:?}: Kicking off {} PolyP's.", cid, self.ephemeral.poly_ps.len()); + for (index, poly_p) in self.ephemeral.poly_ps.iter_mut().enumerate() { + let my_subtree_id = SubtreeId::PolyP { index }; + let m_ctx = PolyPContext { + my_subtree_id, + inner: &mut self.inner, + solution_storage: &mut self.ephemeral.solution_storage, + }; + use SyncRunResult as Srr; + let blocker = poly_p.poly_run(m_ctx, &self.protocol_description)?; + lockprintln!("{:?}: ... PolyP's poly_run got blocker {:?}", cid, &blocker); + match blocker { + Srr::NoBranches => return Err(SyncErr::Inconsistent), + Srr::AllBranchesComplete | Srr::BlockingForRecv => (), + } + } + lockprintln!("{:?}: All Poly machines have been kicked off!", cid); + + // 7. `solution_storage` may have new solutions for this controller + // handle their discovery. LEADER => announce, otherwise => send to parent + { + let peeked = self.ephemeral.solution_storage.peek_new_locals().collect::>(); + lockprintln!( + "{:?}: Got {} controller-local solutions before a single RECV: {:?}", + cid, + peeked.len(), + peeked + ); + } + if self.handle_locals_maybe_decide()? { + return Ok(()); + } + + // 4. Receive incoming messages until the DECISION is made + lockprintln!("{:?}: No decision yet. Time to recv messages", cid); + self.undelay_all(); + 'recv_loop: loop { + let received = self.recv(deadline)?.ok_or(SyncErr::Timeout)?; + let current_content = match received.msg { + Msg::SetupMsg(_) => { + lockprintln!("{:?}: recvd message {:?} and its SETUP :(", cid, &received); + // This occurs in the event the connector was malformed during connect() + return Err(SyncErr::UnexpectedSetupMsg); + } + Msg::CommMsg(CommMsg { round_index, .. }) + if round_index < self.inner.round_index => + { + // Old message! Can safely discard + lockprintln!("{:?}: recvd message {:?} and its OLD! :(", cid, &received); + drop(received); + continue 'recv_loop; + } + Msg::CommMsg(CommMsg { round_index, .. }) + if round_index > self.inner.round_index => + { + // Message from a next round. Keep for later! + lockprintln!( + "{:?}: recvd message {:?} and its for later. DELAY! :(", + cid, + &received + ); + self.delay(received); + continue 'recv_loop; + } + Msg::CommMsg(CommMsg { contents, round_index }) => { + lockprintln!("{:?}: recvd a round-appropriate CommMsg {:?}", cid, &contents); + assert_eq!(round_index, self.inner.round_index); + contents + } + }; + match current_content { + CommMsgContents::Elaborate { partial_oracle } => { + // Child controller submitted a subtree solution. + if !self.inner.family.children_ekeys.contains(&received.recipient) { + return Err(SyncErr::ElaborateFromNonChild); + } + let subtree_id = SubtreeId::ChildController { ekey: received.recipient }; + lockprintln!( + "{:?}: Received elaboration from child for subtree {:?}: {:?}", + cid, + subtree_id, + &partial_oracle + ); + self.ephemeral + .solution_storage + .submit_and_digest_subtree_solution(subtree_id, partial_oracle); + + if self.handle_locals_maybe_decide()? { + return Ok(()); + } + } + CommMsgContents::Announce { oracle } => { + if self.inner.family.parent_ekey != Some(received.recipient) { + return Err(SyncErr::AnnounceFromNonParent); + } + lockprintln!( + "{:?}: Received ANNOUNCEMENT from from parent {:?}: {:?}", + cid, + received.recipient, + &oracle + ); + return self.end_round_with_decision(oracle); + } + CommMsgContents::SendPayload { payload_predicate, payload } => { + // message for some actor. Feed it to the appropriate actor + // and then give them another chance to run. + let subtree_id = ekey_to_holder.get(&received.recipient); + lockprintln!( + "{:?}: Received SendPayload for subtree {:?} with pred {:?} and payload {:?}", + cid, subtree_id, &payload_predicate, &payload + ); + match subtree_id { + None => { + // this happens when a message is sent to a component that has exited. + // It's safe to drop this message; + // The sender branch will certainly not be part of the solution + continue 'recv_loop; + } + Some(PolyId::N) => { + // Message for NativeMachine + self.ephemeral.poly_n.as_mut().unwrap().sync_recv( + received.recipient, + payload, + &mut self.ephemeral.solution_storage, + ); + } + Some(PolyId::P { index }) => { + // Message for protocol actor + let channel_id = self + .inner + .endpoint_exts + .get(received.recipient) + .expect("UEHFU") + .info + .channel_id; + if payload_predicate.query(channel_id) != Some(true) { + // sender didn't preserve the invariant + return Err(SyncErr::PayloadPremiseExcludesTheChannel(channel_id)); + } + let poly_p = &mut self.ephemeral.poly_ps[*index]; + + let m_ctx = PolyPContext { + my_subtree_id: SubtreeId::PolyP { index: *index }, + inner: &mut self.inner, + solution_storage: &mut self.ephemeral.solution_storage, + }; + use SyncRunResult as Srr; + let blocker = poly_p.poly_recv_run( + m_ctx, + &self.protocol_description, + received.recipient, + payload_predicate, + payload, + )?; + lockprintln!( + "{:?}: ... Fed the msg to PolyP {:?} and ran it to blocker {:?}", + cid, + subtree_id, + blocker + ); + match blocker { + Srr::NoBranches => return Err(SyncErr::Inconsistent), + Srr::BlockingForRecv | Srr::AllBranchesComplete => { + continue 'recv_loop + } + } + } + }; + { + let peeked = + self.ephemeral.solution_storage.peek_new_locals().collect::>(); + lockprintln!( + "{:?}: Got {} new controller-local solutions from RECV: {:?}", + cid, + peeked.len(), + peeked + ); + } + if self.handle_locals_maybe_decide()? { + return Ok(()); + } + } + } + } + } +} +impl ControllerEphemeral { + fn is_clear(&self) -> bool { + self.solution_storage.is_clear() + && self.poly_n.is_none() + && self.poly_ps.is_empty() + && self.ekey_to_holder.is_empty() + } + fn clear(&mut self) { + self.solution_storage.clear(); + self.poly_n.take(); + self.poly_ps.clear(); + self.ekey_to_holder.clear(); + } +} +impl Into for MonoP { + fn into(self) -> PolyP { + PolyP { + complete: Default::default(), + incomplete: hashmap! { + Predicate::new_trivial() => + BranchP { + state: self.state, + inbox: Default::default(), + } + }, + ekeys: self.ekeys, + } + } +} + +impl From for SyncErr { + fn from(e: EndpointErr) -> SyncErr { + SyncErr::EndpointErr(e) + } +} + +impl MonoContext for MonoPContext<'_> { + type D = ProtocolD; + type S = ProtocolS; + fn new_component(&mut self, moved_ekeys: HashSet, init_state: Self::S) { + if moved_ekeys.is_subset(self.ekeys) { + self.ekeys.retain(|x| !moved_ekeys.contains(x)); + self.inner.mono_ps.push(MonoP { state: init_state, ekeys: moved_ekeys }); + } else { + panic!("MachineP attempting to move alien ekey!"); + } + } + fn new_channel(&mut self) -> [Key; 2] { + let [a, b] = Endpoint::new_memory_pair(); + let channel_id = self.inner.channel_id_stream.next(); + let kp = self.inner.endpoint_exts.alloc(EndpointExt { + info: EndpointInfo { polarity: Putter, channel_id }, + endpoint: a, + }); + let kg = self.inner.endpoint_exts.alloc(EndpointExt { + info: EndpointInfo { polarity: Putter, channel_id }, + endpoint: b, + }); + [kp, kg] + } + fn new_random(&self) -> u64 { + type Bytes8 = [u8; std::mem::size_of::()]; + let mut bytes = Bytes8::default(); + getrandom::getrandom(&mut bytes).unwrap(); + unsafe { std::mem::transmute::(bytes) } + } +} + +impl SolutionStorage { + fn is_clear(&self) -> bool { + self.subtree_id_to_index.is_empty() + && self.subtree_solutions.is_empty() + && self.old_local.is_empty() + && self.new_local.is_empty() + } + fn clear(&mut self) { + self.subtree_id_to_index.clear(); + self.subtree_solutions.clear(); + self.old_local.clear(); + self.new_local.clear(); + } + pub(crate) fn reset(&mut self, subtree_ids: impl Iterator) { + self.subtree_id_to_index.clear(); + self.subtree_solutions.clear(); + self.old_local.clear(); + self.new_local.clear(); + for key in subtree_ids { + self.subtree_id_to_index.insert(key, self.subtree_solutions.len()); + self.subtree_solutions.push(Default::default()) + } + } + + pub(crate) fn peek_new_locals(&self) -> impl Iterator + '_ { + self.new_local.iter() + } + + pub(crate) fn iter_new_local_make_old(&mut self) -> impl Iterator + '_ { + let Self { old_local, new_local, .. } = self; + new_local.drain().map(move |local| { + old_local.insert(local.clone()); + local + }) + } + + pub(crate) fn submit_and_digest_subtree_solution( + &mut self, + subtree_id: SubtreeId, + predicate: Predicate, + ) { + let index = self.subtree_id_to_index[&subtree_id]; + let left = 0..index; + let right = (index + 1)..self.subtree_solutions.len(); + + let Self { subtree_solutions, new_local, old_local, .. } = self; + let was_new = subtree_solutions[index].insert(predicate.clone()); + if was_new { + let set_visitor = left.chain(right).map(|index| &subtree_solutions[index]); + Self::elaborate_into_new_local_rec(predicate, set_visitor, old_local, new_local); + } + } + + fn elaborate_into_new_local_rec<'a, 'b>( + partial: Predicate, + mut set_visitor: impl Iterator> + Clone, + old_local: &'b HashSet, + new_local: &'a mut HashSet, + ) { + if let Some(set) = set_visitor.next() { + // incomplete solution. keep traversing + for pred in set.iter() { + if let Some(elaborated) = pred.union_with(&partial) { + Self::elaborate_into_new_local_rec( + elaborated, + set_visitor.clone(), + old_local, + new_local, + ) + } + } + } else { + // recursive stop condition. `partial` is a local subtree solution + if !old_local.contains(&partial) { + // ... and it hasn't been found before + new_local.insert(partial); + } + } + } +} +impl PolyContext for BranchPContext<'_, '_> { + type D = ProtocolD; + + fn is_firing(&self, ekey: Key) -> Option { + assert!(self.ekeys.contains(&ekey)); + let channel_id = self.m_ctx.inner.endpoint_exts.get(ekey).unwrap().info.channel_id; + self.predicate.query(channel_id) + } + fn read_msg(&self, ekey: Key) -> Option<&Payload> { + assert!(self.ekeys.contains(&ekey)); + self.inbox.get(&ekey) + } +} diff --git a/src/runtime/connector.rs b/src/runtime/connector.rs new file mode 100644 index 0000000000000000000000000000000000000000..f4ffd3baaebf388e8895263ae7c009750667e422 --- /dev/null +++ b/src/runtime/connector.rs @@ -0,0 +1,175 @@ +use crate::common::*; +use crate::runtime::{errors::*, *}; + +pub fn random_controller_id() -> ControllerId { + type Bytes8 = [u8; std::mem::size_of::()]; + let mut bytes = Bytes8::default(); + getrandom::getrandom(&mut bytes).unwrap(); + unsafe { std::mem::transmute::(bytes) } +} + +impl Default for Unconfigured { + fn default() -> Self { + let controller_id = random_controller_id(); + Self { controller_id } + } +} +impl Default for Connector { + fn default() -> Self { + Self::Unconfigured(Unconfigured::default()) + } +} +impl Connector { + /// Configure the Connector with the given Pdl description. + pub fn configure(&mut self, pdl: &[u8]) -> Result<(), ConfigErr> { + use ConfigErr::*; + let controller_id = match self { + Connector::Configured(_) => return Err(AlreadyConfigured), + Connector::Connected(_) => return Err(AlreadyConnected), + Connector::Unconfigured(Unconfigured { controller_id }) => *controller_id, + }; + let protocol_description = Arc::new(ProtocolD::parse(pdl).map_err(ParseErr)?); + let proto_maybe_bindings = protocol_description + .main_interface_polarities() + .into_iter() + .zip(std::iter::repeat(None)) + .collect(); + let configured = Configured { controller_id, protocol_description, proto_maybe_bindings }; + *self = Connector::Configured(configured); + Ok(()) + } + + /// Bind the (configured) connector's port corresponding to the + pub fn bind_port( + &mut self, + proto_port_index: usize, + binding: PortBinding, + ) -> Result<(), PortBindErr> { + use PortBindErr::*; + match self { + Connector::Unconfigured { .. } => Err(NotConfigured), + Connector::Connected(_) => Err(AlreadyConnected), + Connector::Configured(configured) => { + match configured.proto_maybe_bindings.get_mut(proto_port_index) { + None => Err(IndexOutOfBounds), + Some((_polarity, Some(_))) => Err(PortAlreadyBound), + Some((_polarity, x @ None)) => { + *x = Some(binding); + Ok(()) + } + } + } + } + } + pub fn connect(&mut self, timeout: Duration) -> Result<(), ConnectErr> { + let deadline = Instant::now() + timeout; + use ConnectErr::*; + let configured = match self { + Connector::Unconfigured { .. } => return Err(NotConfigured), + Connector::Connected(_) => return Err(AlreadyConnected), + Connector::Configured(configured) => configured, + }; + // 1. Unwrap bindings or err + let bound_proto_interface: Vec<(_, _)> = configured + .proto_maybe_bindings + .iter() + .copied() + .enumerate() + .map(|(native_index, (polarity, maybe_binding))| { + Ok((maybe_binding.ok_or(PortNotBound { native_index })?, polarity)) + }) + .collect::, ConnectErr>>()?; + let (controller, native_interface) = Controller::connect( + configured.controller_id, + configured.protocol_description.clone(), + &bound_proto_interface[..], + deadline, + )?; + *self = Connector::Connected(Connected { + native_interface, + sync_batches: vec![Default::default()], + controller, + }); + Ok(()) + } + + pub fn put(&mut self, native_port_index: usize, payload: Payload) -> Result<(), PortOpErr> { + use PortOpErr::*; + let connected = match self { + Connector::Connected(connected) => connected, + _ => return Err(NotConnected), + }; + let (ekey, native_polarity) = + *connected.native_interface.get(native_port_index).ok_or(IndexOutOfBounds)?; + if native_polarity != Putter { + return Err(WrongPolarity); + } + let sync_batch = connected.sync_batches.iter_mut().last().unwrap(); + if sync_batch.puts.contains_key(&ekey) { + return Err(DuplicateOperation); + } + sync_batch.puts.insert(ekey, payload); + Ok(()) + } + + pub fn get(&mut self, native_port_index: usize) -> Result<(), PortOpErr> { + use PortOpErr::*; + let connected = match self { + Connector::Connected(connected) => connected, + _ => return Err(NotConnected), + }; + let (ekey, native_polarity) = + *connected.native_interface.get(native_port_index).ok_or(IndexOutOfBounds)?; + if native_polarity != Getter { + return Err(WrongPolarity); + } + let sync_batch = connected.sync_batches.iter_mut().last().unwrap(); + if sync_batch.gets.contains(&ekey) { + return Err(DuplicateOperation); + } + sync_batch.gets.insert(ekey); + Ok(()) + } + pub fn next_batch(&mut self) -> Result { + let connected = match self { + Connector::Connected(connected) => connected, + _ => return Err(()), + }; + connected.sync_batches.push(SyncBatch::default()); + Ok(connected.sync_batches.len() - 1) + } + + pub fn sync(&mut self, timeout: Duration) -> Result { + let deadline = Instant::now() + timeout; + use SyncErr::*; + let connected = match self { + Connector::Connected(connected) => connected, + _ => return Err(NotConnected), + }; + + // do the synchronous round! + connected.controller.sync_round(deadline, Some(connected.sync_batches.drain(..)))?; + connected.sync_batches.push(SyncBatch::default()); + + let mono_n = connected.controller.inner.mono_n.as_mut().unwrap(); + let result = mono_n.result.as_mut().unwrap(); + Ok(result.0) + } + + pub fn read_gotten(&self, native_port_index: usize) -> Result<&[u8], ReadGottenErr> { + use ReadGottenErr::*; + let connected = match self { + Connector::Connected(connected) => connected, + _ => return Err(NotConnected), + }; + let &(key, polarity) = + connected.native_interface.get(native_port_index).ok_or(IndexOutOfBounds)?; + if polarity != Getter { + return Err(WrongPolarity); + } + let mono_n = connected.controller.inner.mono_n.as_ref().expect("controller has no mono_n?"); + let result = mono_n.result.as_ref().ok_or(NoPreviousRound)?; + let payload = result.1.get(&key).ok_or(DidntGet)?; + Ok(payload) + } +} diff --git a/src/runtime/endpoint.rs b/src/runtime/endpoint.rs new file mode 100644 index 0000000000000000000000000000000000000000..342db8d7230a5216faee4e3464b721fea2b8b0b4 --- /dev/null +++ b/src/runtime/endpoint.rs @@ -0,0 +1,212 @@ +use crate::common::*; +use crate::runtime::{errors::*, Predicate}; +use mio::{Evented, PollOpt, Ready}; + +pub(crate) enum Endpoint { + Memory { s: mio_extras::channel::Sender, r: mio_extras::channel::Receiver }, + Network(NetworkEndpoint), +} + +#[derive(Debug)] +pub(crate) struct EndpointExt { + pub endpoint: Endpoint, + pub info: EndpointInfo, +} +#[derive(Debug, Copy, Clone)] +pub struct EndpointInfo { + pub polarity: Polarity, + pub channel_id: ChannelId, +} + +#[derive(Clone, Debug)] +pub(crate) enum Msg { + SetupMsg(SetupMsg), + CommMsg(CommMsg), +} +#[derive(Clone, Debug)] +pub(crate) enum SetupMsg { + // sent by the passive endpoint to the active endpoint + ChannelSetup { info: EndpointInfo }, + LeaderEcho { maybe_leader: ControllerId }, + LeaderAnnounce { leader: ControllerId }, + YouAreMyParent, +} +impl Into for SetupMsg { + fn into(self) -> Msg { + Msg::SetupMsg(self) + } +} + +#[derive(Clone, Debug)] +pub(crate) struct CommMsg { + pub round_index: usize, + pub contents: CommMsgContents, +} +#[derive(Clone, Debug)] +pub(crate) enum CommMsgContents { + SendPayload { payload_predicate: Predicate, payload: Payload }, + Elaborate { partial_oracle: Predicate }, + Announce { oracle: Predicate }, +} + +pub struct NetworkEndpoint { + stream: mio::net::TcpStream, + inbox: Vec, + outbox: Vec, +} + +impl std::fmt::Debug for Endpoint { + fn fmt(&self, f: &mut std::fmt::Formatter) -> Result<(), std::fmt::Error> { + let s = match self { + Endpoint::Memory { .. } => "Memory", + Endpoint::Network(..) => "Network", + }; + write!(f, "Endpoint::{}", s) + } +} + +impl CommMsgContents { + pub fn into_msg(self, round_index: usize) -> Msg { + Msg::CommMsg(CommMsg { round_index, contents: self }) + } +} + +impl From for ConnectErr { + fn from(e: EndpointErr) -> Self { + match e { + EndpointErr::Disconnected => ConnectErr::Disconnected, + EndpointErr::MetaProtocolDeviation => ConnectErr::MetaProtocolDeviation, + } + } +} +impl Endpoint { + // asymmetric + pub(crate) fn from_fresh_stream(stream: mio::net::TcpStream) -> Self { + Self::Network(NetworkEndpoint { stream, inbox: vec![], outbox: vec![] }) + } + + // symmetric + pub fn new_memory_pair() -> [Self; 2] { + let (s1, r1) = mio_extras::channel::channel::(); + let (s2, r2) = mio_extras::channel::channel::(); + [Self::Memory { s: s1, r: r2 }, Self::Memory { s: s2, r: r1 }] + } + pub fn send(&mut self, msg: Msg) -> Result<(), EndpointErr> { + match self { + Self::Memory { s, .. } => s.send(msg).map_err(|_| EndpointErr::Disconnected), + Self::Network(NetworkEndpoint { stream, outbox, .. }) => { + use crate::runtime::serde::Ser; + outbox.ser(&msg).expect("ser failed"); + loop { + use std::io::Write; + match stream.write(outbox) { + Ok(0) => return Ok(()), + Ok(bytes_written) => { + outbox.drain(0..bytes_written); + } + Err(e) if e.kind() == std::io::ErrorKind::WouldBlock => { + panic!("sending shouldn't WouldBlock") + } + Err(_e) => return Err(EndpointErr::Disconnected), + } + } + } + } + } + pub fn recv(&mut self) -> Result, EndpointErr> { + match self { + Self::Memory { r, .. } => match r.try_recv() { + Ok(msg) => Ok(Some(msg)), + Err(std::sync::mpsc::TryRecvError::Empty) => Ok(None), + Err(std::sync::mpsc::TryRecvError::Disconnected) => Err(EndpointErr::Disconnected), + }, + Self::Network(NetworkEndpoint { stream, inbox, .. }) => { + // populate inbox as much as possible + 'read_loop: loop { + use std::io::Read; + match stream.read_to_end(inbox) { + Err(e) if e.kind() == std::io::ErrorKind::WouldBlock => break 'read_loop, + Ok(0) => break 'read_loop, + Ok(_) => (), + Err(e) => { + println!("BAD IS {:?}", e); + panic!("BAD"); + } + } + } + use crate::runtime::serde::{De, MonitoredReader}; + let mut monitored = MonitoredReader::from(&inbox[..]); + match De::::de(&mut monitored) { + Ok(msg) => { + let msg_size2 = monitored.bytes_read(); + inbox.drain(0..(msg_size2.try_into().unwrap())); + Ok(Some(msg)) + } + Err(e) if e.kind() == std::io::ErrorKind::UnexpectedEof => Ok(None), + Err(_) => Err(EndpointErr::MetaProtocolDeviation), + } + } + } + } +} + +impl Evented for Endpoint { + fn register( + &self, + poll: &Poll, + token: Token, + interest: Ready, + opts: PollOpt, + ) -> Result<(), std::io::Error> { + match self { + Self::Memory { r, .. } => r.register(poll, token, interest, opts), + Self::Network(n) => n.register(poll, token, interest, opts), + } + } + + fn reregister( + &self, + poll: &Poll, + token: Token, + interest: Ready, + opts: PollOpt, + ) -> Result<(), std::io::Error> { + match self { + Self::Memory { r, .. } => r.reregister(poll, token, interest, opts), + Self::Network(n) => n.reregister(poll, token, interest, opts), + } + } + + fn deregister(&self, poll: &Poll) -> Result<(), std::io::Error> { + match self { + Self::Memory { r, .. } => r.deregister(poll), + Self::Network(n) => n.deregister(poll), + } + } +} + +impl Evented for NetworkEndpoint { + fn register( + &self, + poll: &Poll, + token: Token, + interest: Ready, + opts: PollOpt, + ) -> Result<(), std::io::Error> { + self.stream.register(poll, token, interest, opts) + } + + fn reregister( + &self, + poll: &Poll, + token: Token, + interest: Ready, + opts: PollOpt, + ) -> Result<(), std::io::Error> { + self.stream.reregister(poll, token, interest, opts) + } + + fn deregister(&self, poll: &Poll) -> Result<(), std::io::Error> { + self.stream.deregister(poll) + } +} diff --git a/src/runtime/errors.rs b/src/runtime/errors.rs new file mode 100644 index 0000000000000000000000000000000000000000..e3fff2963004eca66c6090b0289be109a4315383 --- /dev/null +++ b/src/runtime/errors.rs @@ -0,0 +1,82 @@ +use crate::common::*; + +#[derive(Debug)] +pub enum PortBindErr { + AlreadyConnected, + IndexOutOfBounds, + PortAlreadyBound, + NotConfigured, + ParseErr, + AlreadyConfigured, +} +#[derive(Debug)] +pub enum ReadGottenErr { + NotConnected, + IndexOutOfBounds, + WrongPolarity, + NoPreviousRound, + DidntGet, +} +#[derive(Debug)] +pub enum PortOpErr { + IndexOutOfBounds, + NotConnected, + WrongPolarity, + DuplicateOperation, +} +#[derive(Debug)] +pub enum ConfigErr { + AlreadyConnected, + ParseErr(String), + AlreadyConfigured, +} +#[derive(Debug, Clone)] +pub enum ConnectErr { + PortNotBound { native_index: usize }, + NotConfigured, + AlreadyConnected, + MetaProtocolDeviation, + Disconnected, + PollInitFailed, + MessengerRecvErr(MessengerRecvErr), + Timeout, + PollingFailed, + PolarityMatched(SocketAddr), + AcceptFailed(SocketAddr), + PassiveConnectFailed(SocketAddr), + BindFailed(SocketAddr), +} +#[derive(Debug, Clone)] +pub enum PollDeadlineErr { + PollingFailed, + Timeout, +} + +#[derive(Debug, Clone)] +pub enum EndpointErr { + Disconnected, + MetaProtocolDeviation, +} + +#[derive(Debug, Clone)] +pub enum SyncErr { + NotConnected, + MessengerRecvErr(MessengerRecvErr), + Inconsistent, + Timeout, + ElaborateFromNonChild, + AnnounceFromNonParent, + PayloadPremiseExcludesTheChannel(ChannelId), + UnexpectedSetupMsg, + EndpointErr(EndpointErr), + EvalErr(EvalErr), +} +#[derive(Debug, Clone)] +pub enum EvalErr { + ComponentExitWhileBranching, +} +#[derive(Debug, Clone)] +pub enum MessengerRecvErr { + PollingFailed, + EndpointErr(EndpointErr), +} diff --git a/src/runtime/ffi.rs b/src/runtime/ffi.rs new file mode 100644 index 0000000000000000000000000000000000000000..ddada037c9be6a85e0a518b9d59c6ea85b68a27b --- /dev/null +++ b/src/runtime/ffi.rs @@ -0,0 +1,358 @@ +use crate::common::*; +use crate::runtime::*; + +use core::cell::RefCell; +use std::os::raw::{c_char, c_int, c_uchar, c_uint}; + +struct StoredError { + filled: bool, + buf: Vec, +} +thread_local! { + // stores a string. DOES store the null terminator + static LAST_ERROR: RefCell = RefCell::new(StoredError { filled: false, buf: Vec::with_capacity(128) } ); +} + +const NULL_TERMINATOR: c_char = b'\0' as c_char; +// Silly HACK: rust uses MAX alignment of 128 bytes for fields (no effect) but causes +// cbindgen tool to make this struct OPAQUE (which is what we want). + +// NOT null terminated +fn overwrite_last_error(error_msg: &[u8]) { + LAST_ERROR.with(|stored_error| { + let mut stored_error = stored_error.borrow_mut(); + stored_error.filled = true; + stored_error.buf.clear(); + let error_msg = unsafe { &*(error_msg as *const [u8] as *const [i8]) }; + stored_error.buf.extend_from_slice(error_msg); + stored_error.buf.push(NULL_TERMINATOR); + }) +} + +unsafe fn as_rust_str R>(s: *const c_char, f: F) -> Option { + as_rust_bytes(s, |bytes| { + let s = std::str::from_utf8(bytes).ok()?; + Some(f(s)) + }) +} + +unsafe fn as_rust_bytes R>(s: *const c_char, f: F) -> R { + let len = c_str_len(s); + let s = s as *const u8; + let bytes: &[u8] = std::slice::from_raw_parts(s, len); + f(bytes) +} + +unsafe fn c_str_len(s: *const c_char) -> usize { + let mut len = 0; + while *(s.offset(len.try_into().unwrap())) != NULL_TERMINATOR { + len += 1; + } + len +} + +unsafe fn try_parse_addr(s: *const c_char) -> Option { + as_rust_str(s, |s| s.parse().ok()).and_then(|x| x) +} + +/////////////////////////////////////// + +/// Returns a pointer into the error buffer for reading as a null-terminated string +/// Returns null if there is no error in the buffer. +/// # Safety +/// TODO +#[no_mangle] +pub unsafe extern "C" fn connector_error_peek() -> *const c_char { + LAST_ERROR.with(|stored_error| { + let stored_error = stored_error.borrow(); + if stored_error.filled { + stored_error.buf.as_ptr() + } else { + std::ptr::null() + } + }) +} + +/// Resets the error message buffer. +/// Returns: +/// - 0 if an error was cleared +/// - 1 if there was no error to clear +/// # Safety +/// TODO +#[no_mangle] +pub extern "C" fn connector_error_clear() -> c_int { + LAST_ERROR.with(|stored_error| { + let mut stored_error = stored_error.borrow_mut(); + if stored_error.filled { + stored_error.buf.clear(); + stored_error.filled = false; + 0 + } else { + 1 + } + }) +} + +/// Creates and returns Reowolf Connector structure allocated on the heap. +#[no_mangle] +pub extern "C" fn connector_new() -> *mut Connector { + Box::into_raw(Box::new(Connector::default())) +} + +/// Creates and returns Reowolf Connector structure allocated on the heap. +#[no_mangle] +pub extern "C" fn connector_with_controller_id(controller_id: ControllerId) -> *mut Connector { + Box::into_raw(Box::new(Connector::Unconfigured(Unconfigured { controller_id }))) +} + +/// Configures the given Reowolf connector with a protocol description in PDL. +/// Returns: +/// # Safety +/// TODO +#[no_mangle] +pub unsafe extern "C" fn connector_configure(connector: *mut Connector, pdl: *mut c_char) -> c_int { + let mut b = Box::from_raw(connector); // unsafe! + let ret = as_rust_bytes(pdl, |pdl_bytes| match b.configure(pdl_bytes) { + Ok(()) => 0, + Err(e) => { + overwrite_last_error(format!("{:?}", e).as_bytes()); + -1 + } + }); + Box::into_raw(b); // don't drop! + ret +} + +/// Provides a binding annotation for the port with the given index with "native": +/// (The port is exposed for reading and writing from the application) +/// Returns: +/// # Safety +/// TODO +#[no_mangle] +pub unsafe extern "C" fn port_bind_native( + connector: *mut Connector, + proto_port_index: usize, +) -> c_int { + // use PortBindErr::*; + let mut b = Box::from_raw(connector); // unsafe! + let ret = match b.bind_port(proto_port_index, PortBinding::Native) { + Ok(()) => 0, + Err(e) => { + overwrite_last_error(format!("{:?}", e).as_bytes()); + -1 + } + }; + Box::into_raw(b); // don't drop! + ret +} + +/// Provides a binding annotation for the port with the given index with "native": +/// (The port is exposed for reading and writing from the application) +/// Returns: +/// # Safety +/// TODO +#[no_mangle] +pub unsafe extern "C" fn port_bind_passive( + connector: *mut Connector, + proto_port_index: c_uint, + address: *const c_char, +) -> c_int { + if let Some(addr) = try_parse_addr(address) { + // use PortBindErr::*; + let mut b = Box::from_raw(connector); // unsafe! + let ret = + match b.bind_port(proto_port_index.try_into().unwrap(), PortBinding::Passive(addr)) { + Ok(()) => 0, + Err(e) => { + overwrite_last_error(format!("{:?}", e).as_bytes()); + -1 + } + }; + Box::into_raw(b); // don't drop! + ret + } else { + overwrite_last_error(b"Failed to parse input as ip address!"); + -1 + } +} + +/// Provides a binding annotation for the port with the given index with "active": +/// (The port will conenct to a "passive" port at the given address during connect()) +/// Returns: +/// - 0 for success +/// - 1 if the port was already bound and was left unchanged +/// # Safety +/// TODO +#[no_mangle] +pub unsafe extern "C" fn port_bind_active( + connector: *mut Connector, + proto_port_index: c_uint, + address: *const c_char, +) -> c_int { + if let Some(addr) = try_parse_addr(address) { + // use PortBindErr::*; + let mut b = Box::from_raw(connector); // unsafe! + let ret = match b.bind_port(proto_port_index.try_into().unwrap(), PortBinding::Active(addr)) + { + Ok(()) => 0, + Err(e) => { + overwrite_last_error(format!("{:?}", e).as_bytes()); + -1 + } + }; + Box::into_raw(b); // don't drop! + ret + } else { + overwrite_last_error(b"Failed to parse input as ip address!"); + -1 + } +} + +/// Provides a binding annotation for the port with the given index with "active": +/// (The port will conenct to a "passive" port at the given address during connect()) +/// Returns: +/// - 0 SUCCESS: connected successfully +/// - TODO error codes +/// # Safety +/// TODO +#[no_mangle] +pub unsafe extern "C" fn connector_connect( + connector: *mut Connector, + timeout_millis: u64, +) -> c_int { + let mut b = Box::from_raw(connector); // unsafe! + let ret = match b.connect(Duration::from_millis(timeout_millis)) { + Ok(()) => 0, + Err(e) => { + overwrite_last_error(format!("{:?}", e).as_bytes()); + -1 + } + }; + Box::into_raw(b); // don't drop! + ret +} + +/// Destroys the given connector, freeing its underlying resources. +/// # Safety +/// TODO +#[no_mangle] +pub unsafe extern "C" fn connector_destroy(connector: *mut Connector) { + let c = Box::from_raw(connector); // unsafe! + drop(c); // for readability +} + +/// Prepares to synchronously put a message at the given port, reading it from the given buffer. +/// # Safety +/// TODO +#[no_mangle] +pub unsafe extern "C" fn port_put( + connector: *mut Connector, + proto_port_index: c_uint, + buf_ptr: *mut c_uchar, + msg_len: c_uint, +) -> c_int { + let buf = std::slice::from_raw_parts_mut(buf_ptr, msg_len.try_into().unwrap()); + let payload = buf.to_vec(); // unsafe + let mut b = Box::from_raw(connector); // unsafe! + let ret = b.put(proto_port_index.try_into().unwrap(), payload); + Box::into_raw(b); // don't drop! + match ret { + Ok(()) => 0, + Err(e) => { + overwrite_last_error(format!("{:?}", e).as_bytes()); + -1 + } + } +} + +/// Prepares to synchronously put a message at the given port, writing it to the given buffer. +/// - 0 SUCCESS +/// - 1 this port has the wrong direction +/// - 2 this port is already marked to get +/// # Safety +/// TODO +#[no_mangle] +pub unsafe extern "C" fn port_get(connector: *mut Connector, proto_port_index: c_uint) -> c_int { + let mut b = Box::from_raw(connector); // unsafe! + let ret = b.get(proto_port_index.try_into().unwrap()); + Box::into_raw(b); // don't drop! + // use PortOperationErr::*; + match ret { + Ok(()) => 0, + Err(e) => { + overwrite_last_error(format!("{:?}", e).as_bytes()); + -1 + } + } +} + +/// # Safety +/// TODO +#[no_mangle] +pub unsafe extern "C" fn read_gotten( + connector: *mut Connector, + proto_port_index: c_uint, + buf_ptr_outptr: *mut *const c_uchar, + len_outptr: *mut c_uint, +) -> c_int { + let b = Box::from_raw(connector); // unsafe! + let ret = b.read_gotten(proto_port_index.try_into().unwrap()); + // use ReadGottenErr::*; + let result = match ret { + Ok(ptr_slice) => { + let buf_ptr = ptr_slice.as_ptr(); + let len = ptr_slice.len().try_into().unwrap(); + buf_ptr_outptr.write(buf_ptr); + len_outptr.write(len); + 0 + } + Err(e) => { + overwrite_last_error(format!("{:?}", e).as_bytes()); + -1 + } + }; + Box::into_raw(b); // don't drop! + result +} + +/// # Safety +/// TODO +#[no_mangle] +pub unsafe extern "C" fn port_close(connector: *mut Connector, _proto_port_index: c_uint) -> c_int { + let b = Box::from_raw(connector); // unsafe! + // TODO + Box::into_raw(b); // don't drop! + 0 +} + +/// # Safety +/// TODO +#[no_mangle] +pub unsafe extern "C" fn connector_next_batch(connector: *mut Connector) -> c_int { + let mut b = Box::from_raw(connector); // unsafe! + let result = match b.next_batch() { + Ok(batch_index) => batch_index.try_into().unwrap(), + Err(e) => { + overwrite_last_error(format!("{:?}", e).as_bytes()); + -1 + } + }; + Box::into_raw(b); // don't drop! + result +} + +/// # Safety +/// TODO +#[no_mangle] +pub unsafe extern "C" fn connector_sync(connector: *mut Connector, timeout_millis: u64) -> c_int { + let mut b = Box::from_raw(connector); // unsafe! + let result = match b.sync(Duration::from_millis(timeout_millis)) { + Ok(batch_index) => batch_index.try_into().unwrap(), + Err(e) => { + overwrite_last_error(format!("{:?}", e).as_bytes()); + -1 + } + }; + Box::into_raw(b); // don't drop! + result +} diff --git a/src/runtime/mod.rs b/src/runtime/mod.rs new file mode 100644 index 0000000000000000000000000000000000000000..5dbac51a7f84823a0de49e6b4521231d187e60f6 --- /dev/null +++ b/src/runtime/mod.rs @@ -0,0 +1,507 @@ +#[cfg(feature = "ffi")] +pub mod ffi; + +mod actors; +pub(crate) mod communication; +pub(crate) mod connector; +pub(crate) mod endpoint; +pub mod errors; +mod predicate; // TODO later +mod serde; +pub(crate) mod setup; + +pub(crate) type ProtocolD = crate::protocol::ProtocolDescriptionImpl; +pub(crate) type ProtocolS = crate::protocol::ComponentStateImpl; + +use crate::common::*; +use actors::*; +use endpoint::*; +use errors::*; + +#[derive(Debug, PartialEq)] +pub(crate) enum CommonSatResult { + FormerNotLatter, + LatterNotFormer, + Equivalent, + New(Predicate), + Nonexistant, +} + +#[derive(Clone, Eq, PartialEq, Hash)] +pub(crate) struct Predicate { + pub assigned: BTreeMap, +} + +#[derive(Debug, Default)] +struct SyncBatch { + puts: HashMap, + gets: HashSet, +} + +#[derive(Debug)] +pub enum Connector { + Unconfigured(Unconfigured), + Configured(Configured), + Connected(Connected), // TODO consider boxing. currently takes up a lot of stack real estate +} +#[derive(Debug)] +pub struct Unconfigured { + pub controller_id: ControllerId, +} +#[derive(Debug)] +pub struct Connected { + native_interface: Vec<(Key, Polarity)>, + sync_batches: Vec, + controller: Controller, +} +#[derive(Debug)] +pub struct Configured { + // invariant: proto_maybe_bindings.len() is the size of the protocol's interface + controller_id: ControllerId, + proto_maybe_bindings: Vec<(Polarity, Option)>, + protocol_description: Arc, +} + +#[derive(Debug, Copy, Clone)] +pub enum PortBinding { + Native, + Active(SocketAddr), + Passive(SocketAddr), +} + +#[derive(Debug)] +struct Arena { + storage: Vec, +} + +#[derive(Debug)] +struct ReceivedMsg { + recipient: Key, + msg: Msg, +} + +#[derive(Debug)] +struct MessengerState { + poll: Poll, + events: Events, + delayed: Vec, + undelayed: Vec, + polled_undrained: IndexSet, +} +#[derive(Debug)] +struct ChannelIdStream { + controller_id: ControllerId, + next_channel_index: ChannelIndex, +} + +#[derive(Debug)] +struct Controller { + protocol_description: Arc, + inner: ControllerInner, + ephemeral: ControllerEphemeral, +} +#[derive(Debug)] +struct ControllerInner { + round_index: usize, + channel_id_stream: ChannelIdStream, + endpoint_exts: Arena, + messenger_state: MessengerState, + mono_n: Option, + mono_ps: Vec, + family: ControllerFamily, +} + +/// This structure has its state entirely reset between synchronous rounds +#[derive(Debug, Default)] +struct ControllerEphemeral { + solution_storage: SolutionStorage, + poly_n: Option, + poly_ps: Vec, + ekey_to_holder: HashMap, +} + +#[derive(Debug)] +struct ControllerFamily { + parent_ekey: Option, + children_ekeys: Vec, +} + +#[derive(Debug)] +pub(crate) enum SyncRunResult { + BlockingForRecv, + AllBranchesComplete, + NoBranches, +} + +// Used to identify poly actors +#[derive(Debug, Copy, Clone, Eq, PartialEq, Ord, PartialOrd, Hash)] +enum PolyId { + N, + P { index: usize }, +} + +#[derive(Debug, Copy, Clone, Eq, PartialEq, Ord, PartialOrd, Hash)] +pub(crate) enum SubtreeId { + PolyN, + PolyP { index: usize }, + ChildController { ekey: Key }, +} + +pub(crate) struct MonoPContext<'a> { + inner: &'a mut ControllerInner, + ekeys: &'a mut HashSet, +} +pub(crate) struct PolyPContext<'a> { + my_subtree_id: SubtreeId, + inner: &'a mut ControllerInner, + solution_storage: &'a mut SolutionStorage, +} +impl PolyPContext<'_> { + #[inline(always)] + fn reborrow<'a>(&'a mut self) -> PolyPContext<'a> { + let Self { solution_storage, my_subtree_id, inner } = self; + PolyPContext { solution_storage, my_subtree_id: *my_subtree_id, inner } + } +} +struct BranchPContext<'m, 'r> { + m_ctx: PolyPContext<'m>, + ekeys: &'r HashSet, + predicate: &'r Predicate, + inbox: &'r HashMap, +} + +#[derive(Debug, Default)] +pub(crate) struct SolutionStorage { + old_local: HashSet, + new_local: HashSet, + // this pair acts as SubtreeId -> HashSet which is friendlier to iteration + subtree_solutions: Vec>, + subtree_id_to_index: HashMap, +} + +trait Messengerlike { + fn get_state_mut(&mut self) -> &mut MessengerState; + fn get_endpoint_mut(&mut self, eekey: Key) -> &mut Endpoint; + + fn delay(&mut self, received: ReceivedMsg) { + self.get_state_mut().delayed.push(received); + } + fn undelay_all(&mut self) { + let MessengerState { delayed, undelayed, .. } = self.get_state_mut(); + undelayed.extend(delayed.drain(..)) + } + + fn send(&mut self, to: Key, msg: Msg) -> Result<(), EndpointErr> { + self.get_endpoint_mut(to).send(msg) + } + + // attempt to receive a message from one of the endpoints before the deadline + fn recv(&mut self, deadline: Instant) -> Result, MessengerRecvErr> { + // try get something buffered + if let Some(x) = self.get_state_mut().undelayed.pop() { + return Ok(Some(x)); + } + + loop { + // polled_undrained may not be empty + while let Some(eekey) = self.get_state_mut().polled_undrained.pop() { + if let Some(msg) = self.get_endpoint_mut(eekey).recv()? { + // this endpoint MAY still have messages! check again in future + self.get_state_mut().polled_undrained.insert(eekey); + return Ok(Some(ReceivedMsg { recipient: eekey, msg })); + } + } + + let state = self.get_state_mut(); + match state.poll_events(deadline) { + Ok(()) => { + for e in state.events.iter() { + state.polled_undrained.insert(Key::from_token(e.token())); + } + } + Err(PollDeadlineErr::PollingFailed) => return Err(MessengerRecvErr::PollingFailed), + Err(PollDeadlineErr::Timeout) => return Ok(None), + } + } + } +} + +///////////////////////////////// + +impl From for SyncErr { + fn from(e: EvalErr) -> SyncErr { + SyncErr::EvalErr(e) + } +} +impl From for SyncErr { + fn from(e: MessengerRecvErr) -> SyncErr { + SyncErr::MessengerRecvErr(e) + } +} +impl From for ConnectErr { + fn from(e: MessengerRecvErr) -> ConnectErr { + ConnectErr::MessengerRecvErr(e) + } +} +impl From for MessengerRecvErr { + fn from(e: EndpointErr) -> MessengerRecvErr { + MessengerRecvErr::EndpointErr(e) + } +} +impl Default for Arena { + fn default() -> Self { + Self { storage: vec![] } + } +} +impl Arena { + pub fn alloc(&mut self, t: T) -> Key { + self.storage.push(t); + Key::from_raw(self.storage.len() as u64 - 1) + } + pub fn get(&self, key: Key) -> Option<&T> { + self.storage.get(key.to_raw() as usize) + } + pub fn get_mut(&mut self, key: Key) -> Option<&mut T> { + self.storage.get_mut(key.to_raw() as usize) + } + pub fn type_convert(self, f: impl FnMut((Key, T)) -> X) -> Arena { + Arena { storage: self.keyspace().zip(self.storage.into_iter()).map(f).collect() } + } + pub fn iter(&self) -> impl Iterator { + self.keyspace().zip(self.storage.iter()) + } + pub fn len(&self) -> usize { + self.storage.len() + } + pub fn keyspace(&self) -> impl Iterator { + (0..(self.storage.len() as u64)).map(Key::from_raw) + } +} + +impl ChannelIdStream { + fn new(controller_id: ControllerId) -> Self { + Self { controller_id, next_channel_index: 0 } + } + fn next(&mut self) -> ChannelId { + self.next_channel_index += 1; + ChannelId { controller_id: self.controller_id, channel_index: self.next_channel_index - 1 } + } +} + +impl MessengerState { + // does NOT guarantee that events is non-empty + fn poll_events(&mut self, deadline: Instant) -> Result<(), PollDeadlineErr> { + use PollDeadlineErr::*; + self.events.clear(); + let poll_timeout = deadline.checked_duration_since(Instant::now()).ok_or(Timeout)?; + self.poll.poll(&mut self.events, Some(poll_timeout)).map_err(|_| PollingFailed)?; + Ok(()) + } +} +impl From for ConnectErr { + fn from(e: PollDeadlineErr) -> ConnectErr { + match e { + PollDeadlineErr::Timeout => ConnectErr::Timeout, + PollDeadlineErr::PollingFailed => ConnectErr::PollingFailed, + } + } +} + +impl std::ops::Not for Polarity { + type Output = Self; + fn not(self) -> Self::Output { + use Polarity::*; + match self { + Putter => Getter, + Getter => Putter, + } + } +} + +impl Predicate { + // returns true IFF self.unify would return Equivalent OR FormerNotLatter + pub fn satisfies(&self, other: &Self) -> bool { + let mut s_it = self.assigned.iter(); + let mut s = if let Some(s) = s_it.next() { + s + } else { + return other.assigned.is_empty(); + }; + for (oid, ob) in other.assigned.iter() { + while s.0 < oid { + s = if let Some(s) = s_it.next() { + s + } else { + return false; + }; + } + if s.0 > oid || s.1 != ob { + return false; + } + } + true + } + + /// Given self and other, two predicates, return the most general Predicate possible, N + /// such that n.satisfies(self) && n.satisfies(other). + /// If none exists Nonexistant is returned. + /// If the resulting predicate is equivlanet to self, other, or both, + /// FormerNotLatter, LatterNotFormer and Equivalent are returned respectively. + /// otherwise New(N) is returned. + pub fn common_satisfier(&self, other: &Self) -> CommonSatResult { + use CommonSatResult::*; + // iterators over assignments of both predicates. Rely on SORTED ordering of BTreeMap's keys. + let [mut s_it, mut o_it] = [self.assigned.iter(), other.assigned.iter()]; + let [mut s, mut o] = [s_it.next(), o_it.next()]; + // lists of assignments in self but not other and vice versa. + let [mut s_not_o, mut o_not_s] = [vec![], vec![]]; + loop { + match [s, o] { + [None, None] => break, + [None, Some(x)] => { + o_not_s.push(x); + o_not_s.extend(o_it); + break; + } + [Some(x), None] => { + s_not_o.push(x); + s_not_o.extend(s_it); + break; + } + [Some((sid, sb)), Some((oid, ob))] => { + if sid < oid { + // o is missing this element + s_not_o.push((sid, sb)); + s = s_it.next(); + } else if sid > oid { + // s is missing this element + o_not_s.push((sid, sb)); + o = o_it.next(); + } else if sb != ob { + assert_eq!(sid, oid); + // both predicates assign the variable but differ on the value + return Nonexistant; + } else { + // both predicates assign the variable to the same value + s = s_it.next(); + o = o_it.next(); + } + } + } + } + // Observed zero inconsistencies. A unified predicate exists... + match [s_not_o.is_empty(), o_not_s.is_empty()] { + [true, true] => Equivalent, // ... equivalent to both. + [false, true] => FormerNotLatter, // ... equivalent to self. + [true, false] => LatterNotFormer, // ... equivalent to other. + [false, false] => { + // ... which is the union of the predicates' assignments but + // is equivalent to neither self nor other. + let mut predicate = self.clone(); + for (&id, &b) in o_not_s { + predicate.assigned.insert(id, b); + } + New(predicate) + } + } + } + + pub fn batch_assign_nones( + &mut self, + channel_ids: impl Iterator, + value: bool, + ) { + for channel_id in channel_ids { + self.assigned.entry(channel_id).or_insert(value); + } + } + pub fn replace_assignment(&mut self, channel_id: ChannelId, value: bool) -> Option { + self.assigned.insert(channel_id, value) + } + pub fn union_with(&self, other: &Self) -> Option { + let mut res = self.clone(); + for (&channel_id, &assignment_1) in other.assigned.iter() { + match res.assigned.insert(channel_id, assignment_1) { + Some(assignment_2) if assignment_1 != assignment_2 => return None, + _ => {} + } + } + Some(res) + } + pub fn query(&self, x: ChannelId) -> Option { + self.assigned.get(&x).copied() + } + pub fn new_trivial() -> Self { + Self { assigned: Default::default() } + } +} +impl Debug for Predicate { + fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result { + for (ChannelId { controller_id, channel_index }, &v) in self.assigned.iter() { + write!(f, "{:?}=>{}", (controller_id, channel_index), if v { 'T' } else { 'F' })?; + } + Ok(()) + } +} + +#[test] +fn pred_sat() { + use maplit::btreemap; + let mut c = ChannelIdStream::new(0); + let ch = std::iter::repeat_with(move || c.next()).take(5).collect::>(); + let p = Predicate::new_trivial(); + let p_0t = Predicate { assigned: btreemap! { ch[0] => true } }; + let p_0f = Predicate { assigned: btreemap! { ch[0] => false } }; + let p_0f_3f = Predicate { assigned: btreemap! { ch[0] => false, ch[3] => false } }; + let p_0f_3t = Predicate { assigned: btreemap! { ch[0] => false, ch[3] => true } }; + + assert!(p.satisfies(&p)); + assert!(p_0t.satisfies(&p_0t)); + assert!(p_0f.satisfies(&p_0f)); + assert!(p_0f_3f.satisfies(&p_0f_3f)); + assert!(p_0f_3t.satisfies(&p_0f_3t)); + + assert!(p_0t.satisfies(&p)); + assert!(p_0f.satisfies(&p)); + assert!(p_0f_3f.satisfies(&p_0f)); + assert!(p_0f_3t.satisfies(&p_0f)); + + assert!(!p.satisfies(&p_0t)); + assert!(!p.satisfies(&p_0f)); + assert!(!p_0f.satisfies(&p_0t)); + assert!(!p_0t.satisfies(&p_0f)); + assert!(!p_0f_3f.satisfies(&p_0f_3t)); + assert!(!p_0f_3t.satisfies(&p_0f_3f)); + assert!(!p_0t.satisfies(&p_0f_3f)); + assert!(!p_0f.satisfies(&p_0f_3f)); + assert!(!p_0t.satisfies(&p_0f_3t)); + assert!(!p_0f.satisfies(&p_0f_3t)); +} + +#[test] +fn pred_common_sat() { + use maplit::btreemap; + use CommonSatResult::*; + + let mut c = ChannelIdStream::new(0); + let ch = std::iter::repeat_with(move || c.next()).take(5).collect::>(); + let p = Predicate::new_trivial(); + let p_0t = Predicate { assigned: btreemap! { ch[0] => true } }; + let p_0f = Predicate { assigned: btreemap! { ch[0] => false } }; + let p_3f = Predicate { assigned: btreemap! { ch[3] => false } }; + let p_0f_3f = Predicate { assigned: btreemap! { ch[0] => false, ch[3] => false } }; + let p_0f_3t = Predicate { assigned: btreemap! { ch[0] => false, ch[3] => true } }; + + assert_eq![p.common_satisfier(&p), Equivalent]; + assert_eq![p_0t.common_satisfier(&p_0t), Equivalent]; + + assert_eq![p.common_satisfier(&p_0t), LatterNotFormer]; + assert_eq![p_0t.common_satisfier(&p), FormerNotLatter]; + + assert_eq![p_0t.common_satisfier(&p_0f), Nonexistant]; + assert_eq![p_0f_3t.common_satisfier(&p_0f_3f), Nonexistant]; + assert_eq![p_0f_3t.common_satisfier(&p_3f), Nonexistant]; + assert_eq![p_3f.common_satisfier(&p_0f_3t), Nonexistant]; + + assert_eq![p_0f.common_satisfier(&p_3f), New(p_0f_3f)]; +} diff --git a/src/runtime/predicate.rs b/src/runtime/predicate.rs new file mode 100644 index 0000000000000000000000000000000000000000..00920ec84a49458360252c9707558e4139609a57 --- /dev/null +++ b/src/runtime/predicate.rs @@ -0,0 +1,237 @@ +use crate::common::ChannelId; +use crate::common::ChannelIndex; +use crate::common::ControllerId; + +use std::collections::BTreeMap; + +// we assume a dense ChannelIndex domain! + +enum CommonSatisfier { + FormerNotLatter, + LatterNotFormer, + Equivalent, + New(T), + Nonexistant, +} + +type ChunkType = u16; +const MASK_BITS: ChunkType = 0x_AA_AA; // 101010... + +#[test] +fn mask_ok() { + assert_eq!(!0, MASK_BITS | (MASK_BITS >> 1)); + assert_eq!(0, MASK_BITS & (MASK_BITS >> 1)); +} + +#[derive(Debug, Copy, Clone, PartialEq, Eq)] +struct TernChunk(ChunkType); // invariant: no pair is 01 + +impl TernChunk { + fn overwrite(&mut self, index: usize, value: bool) -> Option { + assert!(index < Self::vars_per_chunk()); + let mask_bit_mask = 1 << (index * 2 + 1); + let bool_bit_mask = 1 << (index * 2); + let ret = if self.0 & mask_bit_mask != 0 { + let was_value = self.0 & bool_bit_mask != 0; + if was_value != value { + // flip the value bit + self.0 ^= bool_bit_mask; + } + Some(was_value) + } else { + if value { + // set the value bit + self.0 |= bool_bit_mask; + } + None + }; + // set the mask bit + self.0 |= mask_bit_mask; + ret + } + fn new_singleton(index: usize, value: bool) -> Self { + assert!(index < Self::vars_per_chunk()); + let mask_bits = 1 << (index * 2 + 1); + let maybe_bit: ChunkType = value as ChunkType; + assert_eq!(maybe_bit == 1, value); + assert!(maybe_bit <= 1); + let bool_bits = maybe_bit << (index * 2); + Self(mask_bits | bool_bits) + } + const fn vars_per_chunk() -> usize { + std::mem::size_of::() / 2 + } + #[inline] + fn query(self, index: usize) -> Option { + assert!(index < Self::vars_per_chunk()); + let mask_bit_mask = 1 << (index * 2 + 1); + let bool_bit_mask = 1 << (index * 2); + if self.0 & mask_bit_mask != 0 { + Some(self.0 & bool_bit_mask != 0) + } else { + None + } + } + fn mutual_satisfaction(self, othe: Self) -> [bool; 2] { + let s_mask = self.0 & MASK_BITS; + let o_mask = othe.0 & MASK_BITS; + let both_mask = s_mask & o_mask; + let diff = self.0 ^ othe.0; + let masked_diff = diff & (both_mask >> 1); + if masked_diff != 0 { + [false; 2] + } else { + let s_sat_o = s_mask & !o_mask == 0; + let o_sat_s = o_mask & !s_mask == 0; + [s_sat_o, o_sat_s] + } + } + + /// Returns whether self satisfies other + /// false iff either: + /// 1. there exists a pair which you specify and I dont. + // i.e., self has 00, othe has 1? + /// 2. we both specify a variable with different values. + /// i.e., self has 10, othe has 11 or vice versa. + fn satisfies(self, othe: Self) -> bool { + let s_mask = self.0 & MASK_BITS; + let o_mask = othe.0 & MASK_BITS; + let both_mask = s_mask & o_mask; + let diff = self.0 ^ othe.0; + + // FALSE if othe has a 1X pair where self has a 1(!X) pair + let masked_diff = diff & (both_mask >> 1); + + // FALSE if othe has a 1X pair where self has a 0Y pair. + let o_not_s_mask = o_mask & !s_mask; + + o_not_s_mask | masked_diff == 0 + } + + fn common_satisfier(self, othe: Self) -> Option { + let s_mask = self.0 & MASK_BITS; + let o_mask = othe.0 & MASK_BITS; + let both_mask = s_mask & o_mask; + let diff = self.0 ^ othe.0; + let masked_diff = diff & (both_mask >> 1); + if masked_diff != 0 { + // an inconsistency exists + None + } else { + let s_vals = (s_mask >> 1) & self.0; + let o_vals = (o_mask >> 1) & othe.0; + let new = s_mask | o_mask | s_vals | o_vals; + Some(Self(new)) + } + } +} + +struct TernSet(Vec); // invariant: last byte != 00 +impl TernSet { + fn new_singleton(index: ChannelIndex, value: bool) -> Self { + let which_chunk = index as usize / TernChunk::vars_per_chunk(); + let inner_index = index as usize % TernChunk::vars_per_chunk(); + let it = std::iter::repeat(TernChunk(0)) + .take(which_chunk) + .chain(std::iter::once(TernChunk::new_singleton(inner_index, value))); + Self(it.collect()) + } + fn overwrite(&mut self, index: ChannelIndex, value: bool) -> Option { + let which_chunk = index as usize / TernChunk::vars_per_chunk(); + let inner_index = index as usize % TernChunk::vars_per_chunk(); + if let Some(tern_chunk) = self.0.get_mut(which_chunk) { + tern_chunk.overwrite(inner_index, value) + } else { + self.0.reserve(which_chunk - self.0.len()); + self.0.resize(which_chunk, TernChunk(0)); + self.0.push(TernChunk::new_singleton(inner_index, value)); + None + } + } + + fn query(&self, index: ChannelIndex) -> Option { + let which_chunk = index as usize / TernChunk::vars_per_chunk(); + self.0.get(which_chunk).copied().and_then(move |tern_chunk| { + tern_chunk.query(index as usize % TernChunk::vars_per_chunk()) + }) + } + fn satisfies(&self, othe: &Self) -> bool { + self.0.len() >= othe.0.len() && self.0.iter().zip(&othe.0).all(|(s, o)| s.satisfies(*o)) + } + fn common_satisfier(&self, othe: &Self) -> CommonSatisfier { + use CommonSatisfier as Cs; + let [slen, olen] = [self.0.len(), othe.0.len()]; + let [mut s_sat_o, mut o_sat_s] = [slen >= olen, slen <= olen]; + for (s, o) in self.0.iter().zip(&othe.0) { + let [s2, o2] = s.mutual_satisfaction(*o); + s_sat_o &= s2; + o_sat_s &= o2; + } + match [s_sat_o, o_sat_s] { + [true, true] => Cs::Equivalent, + [true, false] => Cs::FormerNotLatter, + [false, true] => Cs::LatterNotFormer, + [false, false] => Cs::New(Self( + self.0.iter().zip(&othe.0).map(|(s, o)| s.common_satisfier(*o).unwrap()).collect(), + )), + } + } + #[inline] + fn restore_invariant(&mut self) { + while self.0.iter().copied().last() == Some(TernChunk(0)) { + self.0.pop(); + } + } + fn is_empty(&self) -> bool { + self.0.is_empty() + } +} + +struct Predicate(BTreeMap); +impl Predicate { + pub fn overwrite(&mut self, channel_id: ChannelId, value: bool) -> Option { + let ChannelId { controller_id, channel_index } = channel_id; + use std::collections::btree_map::Entry; + match self.0.entry(controller_id) { + Entry::Occupied(mut x) => x.get_mut().overwrite(channel_index, value), + Entry::Vacant(x) => { + x.insert(TernSet::new_singleton(channel_index, value)); + None + } + } + } + pub fn query(&self, channel_id: ChannelId) -> Option { + let ChannelId { controller_id, channel_index } = channel_id; + self.0.get(&controller_id).and_then(move |tern_set| tern_set.query(channel_index)) + } + pub fn satisfies(&self, other: &Self) -> bool { + let mut s_it = self.0.iter(); + let mut s = if let Some(s) = s_it.next() { + s + } else { + return other.0.is_empty(); + }; + for (oid, ob) in other.0.iter() { + while s.0 < oid { + s = if let Some(s) = s_it.next() { + s + } else { + return false; + }; + } + if s.0 > oid || !s.1.satisfies(ob) { + return false; + } + } + true + } + + pub fn common_satisfier(&self, othe: &Self) -> CommonSatisfier { + // use CommonSatisfier as Cs; + // let [slen, olen] = [self.0.len(), othe.0.len()]; + // let [mut s_sat_o, mut o_sat_s] = [slen >= olen, slen <= olen]; + // let [mut s_it, mut o_it] = [self.0.iter(), othe.0.iter()]; + // let [mut s, mut o] = [s_it.next(), o_it.next()]; + todo!() + } +} diff --git a/src/runtime/serde.rs b/src/runtime/serde.rs new file mode 100644 index 0000000000000000000000000000000000000000..d44f535bed337d08a847d52a5bcaefd104872ffc --- /dev/null +++ b/src/runtime/serde.rs @@ -0,0 +1,308 @@ +use crate::common::*; +use crate::runtime::{ + endpoint::{CommMsg, CommMsgContents, EndpointInfo, Msg, SetupMsg}, + Predicate, +}; +use byteorder::{BigEndian, ReadBytesExt, WriteBytesExt}; +use std::io::{ErrorKind::InvalidData, Read, Write}; + +pub trait Ser: Write { + fn ser(&mut self, t: &T) -> Result<(), std::io::Error>; +} +pub trait De: Read { + fn de(&mut self) -> Result; +} + +pub struct MonitoredReader { + bytes: usize, + r: R, +} +impl From for MonitoredReader { + fn from(r: R) -> Self { + Self { r, bytes: 0 } + } +} +impl MonitoredReader { + pub fn bytes_read(&self) -> usize { + self.bytes + } +} +impl Read for MonitoredReader { + fn read(&mut self, buf: &mut [u8]) -> Result { + let n = self.r.read(buf)?; + self.bytes += n; + Ok(n) + } +} + +///////////////////////////////////////// + +macro_rules! ser_seq { + ( $w:expr ) => {{ + io::Result::Ok(()) + }}; + ( $w:expr, $first:expr ) => {{ + $w.ser($first) + }}; + ( $w:expr, $first:expr, $( $x:expr ),+ ) => {{ + $w.ser($first)?; + ser_seq![$w, $( $x ),*] + }}; +} +///////////////////////////////////////// + +impl Ser for W { + fn ser(&mut self, t: &u8) -> Result<(), std::io::Error> { + self.write_u8(*t) + } +} +impl De for R { + fn de(&mut self) -> Result { + self.read_u8() + } +} + +impl Ser for W { + fn ser(&mut self, t: &u16) -> Result<(), std::io::Error> { + self.write_u16::(*t) + } +} +impl De for R { + fn de(&mut self) -> Result { + self.read_u16::() + } +} + +impl Ser for W { + fn ser(&mut self, t: &u32) -> Result<(), std::io::Error> { + self.write_u32::(*t) + } +} +impl De for R { + fn de(&mut self) -> Result { + self.read_u32::() + } +} + +impl Ser for W { + fn ser(&mut self, t: &u64) -> Result<(), std::io::Error> { + self.write_u64::(*t) + } +} +impl De for R { + fn de(&mut self) -> Result { + self.read_u64::() + } +} + +impl Ser for W { + fn ser(&mut self, t: &Payload) -> Result<(), std::io::Error> { + self.ser(&ZigZag(t.len() as u64))?; + for byte in t { + self.ser(byte)?; + } + Ok(()) + } +} +impl De for R { + fn de(&mut self) -> Result { + let ZigZag(len) = self.de()?; + let mut x = Vec::with_capacity(len as usize); + for _ in 0..len { + x.push(self.de()?); + } + Ok(x) + } +} + +struct ZigZag(u64); +impl Ser for W { + fn ser(&mut self, t: &ZigZag) -> Result<(), std::io::Error> { + integer_encoding::VarIntWriter::write_varint(self, t.0).map(|_| ()) + } +} +impl De for R { + fn de(&mut self) -> Result { + integer_encoding::VarIntReader::read_varint(self).map(ZigZag) + } +} + +impl Ser for W { + fn ser(&mut self, t: &ChannelId) -> Result<(), std::io::Error> { + self.ser(&t.controller_id)?; + self.ser(&ZigZag(t.channel_index as u64)) + } +} +impl De for R { + fn de(&mut self) -> Result { + Ok(ChannelId { + controller_id: self.de()?, + channel_index: De::::de(self)?.0 as ChannelIndex, + }) + } +} + +impl Ser for W { + fn ser(&mut self, t: &bool) -> Result<(), std::io::Error> { + self.ser(&match t { + true => b'T', + false => b'F', + }) + } +} +impl De for R { + fn de(&mut self) -> Result { + let b: u8 = self.de()?; + Ok(match b { + b'T' => true, + b'F' => false, + _ => return Err(InvalidData.into()), + }) + } +} + +impl Ser for W { + fn ser(&mut self, t: &Predicate) -> Result<(), std::io::Error> { + self.ser(&ZigZag(t.assigned.len() as u64))?; + for (channel_id, boolean) in &t.assigned { + ser_seq![self, channel_id, boolean]?; + } + Ok(()) + } +} +impl De for R { + fn de(&mut self) -> Result { + let ZigZag(len) = self.de()?; + let mut assigned = BTreeMap::::default(); + for _ in 0..len { + assigned.insert(self.de()?, self.de()?); + } + Ok(Predicate { assigned }) + } +} + +impl Ser for W { + fn ser(&mut self, t: &Polarity) -> Result<(), std::io::Error> { + self.ser(&match t { + Polarity::Putter => b'P', + Polarity::Getter => b'G', + }) + } +} +impl De for R { + fn de(&mut self) -> Result { + let b: u8 = self.de()?; + Ok(match b { + b'P' => Polarity::Putter, + b'G' => Polarity::Getter, + _ => return Err(InvalidData.into()), + }) + } +} + +impl Ser for W { + fn ser(&mut self, t: &EndpointInfo) -> Result<(), std::io::Error> { + let EndpointInfo { channel_id, polarity } = t; + ser_seq![self, channel_id, polarity] + } +} +impl De for R { + fn de(&mut self) -> Result { + Ok(EndpointInfo { channel_id: self.de()?, polarity: self.de()? }) + } +} + +impl Ser for W { + fn ser(&mut self, t: &Msg) -> Result<(), std::io::Error> { + use {CommMsgContents::*, SetupMsg::*}; + match t { + Msg::SetupMsg(s) => match s { + ChannelSetup { info } => ser_seq![self, &0u8, info], + LeaderEcho { maybe_leader } => ser_seq![self, &1u8, maybe_leader], + LeaderAnnounce { leader } => ser_seq![self, &2u8, leader], + YouAreMyParent => ser_seq![self, &3u8], + }, + Msg::CommMsg(CommMsg { round_index, contents }) => { + let zig = &ZigZag(*round_index as u64); + match contents { + SendPayload { payload_predicate, payload } => { + ser_seq![self, &4u8, zig, payload_predicate, payload] + } + Elaborate { partial_oracle } => ser_seq![self, &5u8, zig, partial_oracle], + Announce { oracle } => ser_seq![self, &6u8, zig, oracle], + } + } + } + } +} +impl De for R { + fn de(&mut self) -> Result { + use {CommMsgContents::*, SetupMsg::*}; + let b: u8 = self.de()?; + Ok(match b { + 0..=3 => Msg::SetupMsg(match b { + 0 => ChannelSetup { info: self.de()? }, + 1 => LeaderEcho { maybe_leader: self.de()? }, + 2 => LeaderAnnounce { leader: self.de()? }, + 3 => YouAreMyParent, + _ => unreachable!(), + }), + _ => { + let ZigZag(zig) = self.de()?; + let contents = match b { + 4 => SendPayload { payload_predicate: self.de()?, payload: self.de()? }, + 5 => Elaborate { partial_oracle: self.de()? }, + 6 => Announce { oracle: self.de()? }, + _ => return Err(InvalidData.into()), + }; + Msg::CommMsg(CommMsg { round_index: zig as usize, contents }) + } + }) + } +} + +///////////////// + +// #[test] +// fn my_serde() -> Result<(), std::io::Error> { +// let payload_predicate = Predicate { +// assigned: maplit::btreemap! { ChannelId {controller_id: 3, channel_index: 9} => false }, +// }; +// let msg = Msg::CommMsg(CommMsg { +// round_index: !0, +// contents: CommMsgContents::SendPayload { +// payload_predicate, +// payload: (0..).take(2).collect(), +// }, +// }); +// let mut v = vec![]; +// v.ser(&msg)?; +// print!("["); +// for (i, &x) in v.iter().enumerate() { +// print!("{:02x}", x); +// if i % 4 == 3 { +// print!(" "); +// } +// } +// println!("]"); + +// let msg2: Msg = (&v[..]).de()?; +// println!("msg2 {:#?}", msg2); +// Ok(()) +// } + +// #[test] +// fn varint() { +// let mut v = vec![]; +// v.ser(&ZigZag(!0)).unwrap(); +// for (i, x) in v.iter_mut().enumerate() { +// print!("{:02x}", x); +// if i % 4 == 3 { +// print!(" "); +// } +// } +// *v.iter_mut().last().unwrap() |= 3; + +// let ZigZag(x) = De::de(&mut &v[..]).unwrap(); +// println!(""); +// } diff --git a/src/runtime/setup.rs b/src/runtime/setup.rs new file mode 100644 index 0000000000000000000000000000000000000000..1b585c2df61455e2e3e3dd3574a63f498f72fba4 --- /dev/null +++ b/src/runtime/setup.rs @@ -0,0 +1,440 @@ +use crate::common::*; +use crate::runtime::{ + actors::{MonoN, MonoP}, + endpoint::*, + errors::*, + *, +}; + +#[derive(Debug)] +enum EndpointExtTodo { + Finished(EndpointExt), + ActiveConnecting { addr: SocketAddr, polarity: Polarity, stream: TcpStream }, + ActiveRecving { addr: SocketAddr, polarity: Polarity, endpoint: Endpoint }, + PassiveAccepting { addr: SocketAddr, info: EndpointInfo, listener: TcpListener }, + PassiveConnecting { addr: SocketAddr, info: EndpointInfo, stream: TcpStream }, +} + +///////////////////// IMPL ///////////////////// +impl Controller { + // Given port bindings and a protocol config, create a connector with 1 native node + pub fn connect( + major: ControllerId, + protocol_description: Arc, + bound_proto_interface: &[(PortBinding, Polarity)], + deadline: Instant, + ) -> Result<(Self, Vec<(Key, Polarity)>), ConnectErr> { + use ConnectErr::*; + + let mut channel_id_stream = ChannelIdStream::new(major); + let mut endpoint_ext_todos = Arena::default(); + + let mut ekeys_native = vec![]; + let mut ekeys_proto = vec![]; + let mut ekeys_network = vec![]; + + let mut native_interface = vec![]; + + /* + 1. - allocate an EndpointExtTodo for every native and interface port + - store all the resulting keys in two keylists for the interfaces of the native and proto components + native: [a, c, f] + | | | + | | | + proto: [b, d, e, g] + ^todo + arena: + */ + for &(binding, polarity) in bound_proto_interface.iter() { + match binding { + PortBinding::Native => { + let channel_id = channel_id_stream.next(); + let ([ekey_native, ekey_proto], native_polarity) = { + let [p, g] = Endpoint::new_memory_pair(); + let mut endpoint_to_key = |endpoint, polarity| { + endpoint_ext_todos.alloc(EndpointExtTodo::Finished(EndpointExt { + endpoint, + info: EndpointInfo { polarity, channel_id }, + })) + }; + let pkey = endpoint_to_key(p, Putter); + let gkey = endpoint_to_key(g, Getter); + let key_pair = match polarity { + Putter => [gkey, pkey], + Getter => [pkey, gkey], + }; + (key_pair, !polarity) + }; + native_interface.push((ekey_native, native_polarity)); + ekeys_native.push(ekey_native); + ekeys_proto.push(ekey_proto); + } + PortBinding::Passive(addr) => { + let channel_id = channel_id_stream.next(); + let ekey_proto = endpoint_ext_todos.alloc(EndpointExtTodo::PassiveAccepting { + addr, + info: EndpointInfo { polarity, channel_id }, + listener: TcpListener::bind(&addr).map_err(|_| BindFailed(addr))?, + }); + ekeys_network.push(ekey_proto); + ekeys_proto.push(ekey_proto); + } + PortBinding::Active(addr) => { + let ekey_proto = endpoint_ext_todos.alloc(EndpointExtTodo::ActiveConnecting { + addr, + polarity, + stream: TcpStream::connect(&addr).unwrap(), + }); + ekeys_network.push(ekey_proto); + ekeys_proto.push(ekey_proto); + } + } + } + println!("{:03?} setup todos...", major); + + // 2. convert the arena to Arena and return the + let (mut messenger_state, mut endpoint_exts) = + Self::finish_endpoint_ext_todos(major, endpoint_ext_todos, deadline)?; + + let n_mono = Some(MonoN { ekeys: ekeys_native.into_iter().collect(), result: None }); + let p_monos = vec![MonoP { + state: protocol_description.new_main_component(&ekeys_proto), + ekeys: ekeys_proto.into_iter().collect(), + }]; + + // 6. Become a node in a sink tree, computing {PARENT, CHILDREN} from {NEIGHBORS} + let family = Self::setup_sink_tree_family( + major, + &mut endpoint_exts, + &mut messenger_state, + ekeys_network, + deadline, + )?; + + let inner = ControllerInner { + family, + messenger_state, + channel_id_stream, + endpoint_exts, + mono_ps: p_monos, + mono_n: n_mono, + round_index: 0, + }; + let controller = Self { protocol_description, inner, ephemeral: Default::default() }; + Ok((controller, native_interface)) + } + + fn test_stream_connectivity(stream: &mut TcpStream) -> bool { + use std::io::Write; + stream.write(&[]).is_ok() + } + + // inserts + fn finish_endpoint_ext_todos( + major: ControllerId, + mut endpoint_ext_todos: Arena, + deadline: Instant, + ) -> Result<(MessengerState, Arena), ConnectErr> { + use {ConnectErr::*, EndpointExtTodo::*}; + + // 1. define and setup a poller and event loop + let edge = PollOpt::edge(); + let [ready_r, ready_w] = [Ready::readable(), Ready::writable()]; + let mut ms = MessengerState { + poll: Poll::new().map_err(|_| PollInitFailed)?, + events: Events::with_capacity(endpoint_ext_todos.len()), + delayed: vec![], + undelayed: vec![], + polled_undrained: Default::default(), + }; + + // 2. Register all EndpointExtTodos with ms.poll. each has one of {Endpoint, TcpStream, TcpListener} + // 3. store the keyset of EndpointExtTodos which are not Finished in `to_finish`. + let mut to_finish = HashSet::<_>::default(); + println!("endpoint_ext_todos len {:?}", endpoint_ext_todos.len()); + for (key, t) in endpoint_ext_todos.iter() { + let token = key.to_token(); + match t { + ActiveRecving { .. } | PassiveConnecting { .. } => unreachable!(), + Finished(EndpointExt { endpoint, .. }) => { + ms.poll.register(endpoint, token, ready_r, edge) + } + ActiveConnecting { stream, .. } => { + to_finish.insert(key); + ms.poll.register(stream, token, ready_w, edge) + } + PassiveAccepting { listener, .. } => { + to_finish.insert(key); + ms.poll.register(listener, token, ready_r, edge) + } + } + .expect("register first"); + } + // invariant: every EndpointExtTodo has one thing registered with mio + + // 4. until all in endpoint_ext_todos are Finished variant, handle events + let mut polled_undrained_later = IndexSet::<_>::default(); + let mut backoff_millis = 10; + while !to_finish.is_empty() { + ms.poll_events(deadline)?; + for event in ms.events.iter() { + let token = event.token(); + let ekey = Key::from_token(token); + let entry = endpoint_ext_todos.get_mut(ekey).unwrap(); + match entry { + Finished(_) => { + polled_undrained_later.insert(ekey); + } + PassiveAccepting { addr, listener, .. } => { + println!("{:03?} start PassiveAccepting...", major); + assert!(event.readiness().is_readable()); + let (stream, _peer_addr) = + listener.accept().map_err(|_| AcceptFailed(*addr))?; + ms.poll.deregister(listener).expect("wer"); + ms.poll.register(&stream, token, ready_w, edge).expect("3y5"); + take_mut::take(entry, |e| { + assert_let![PassiveAccepting { addr, info, .. } = e => { + PassiveConnecting { addr, info, stream } + }] + }); + println!("{:03?} ... end PassiveAccepting", major); + } + PassiveConnecting { addr, stream, .. } => { + println!("{:03?} start PassiveConnecting...", major); + assert!(event.readiness().is_writable()); + if !Self::test_stream_connectivity(stream) { + return Err(PassiveConnectFailed(*addr)); + } + ms.poll.reregister(stream, token, ready_r, edge).expect("52"); + let mut res = Ok(()); + take_mut::take(entry, |e| { + assert_let![PassiveConnecting { info, stream, .. } = e => { + let mut endpoint = Endpoint::from_fresh_stream(stream); + let msg = Msg::SetupMsg(SetupMsg::ChannelSetup { info }); + res = endpoint.send(msg); + Finished(EndpointExt { info, endpoint }) + }] + }); + res?; + println!("{:03?} ... end PassiveConnecting", major); + assert!(to_finish.remove(&ekey)); + } + ActiveConnecting { addr, stream, .. } => { + println!("{:03?} start ActiveConnecting...", major); + assert!(event.readiness().is_writable()); + if Self::test_stream_connectivity(stream) { + // connect successful + println!("CONNECT SUCCESS"); + ms.poll.reregister(stream, token, ready_r, edge).expect("52"); + take_mut::take(entry, |e| { + assert_let![ActiveConnecting { stream, polarity, addr } = e => { + let endpoint = Endpoint::from_fresh_stream(stream); + ActiveRecving { endpoint, polarity, addr } + }] + }); + println!(".. ok"); + } else { + // connect failure. retry! + println!("CONNECT FAIL"); + ms.poll.deregister(stream).expect("wt"); + std::thread::sleep(Duration::from_millis(backoff_millis)); + backoff_millis = ((backoff_millis as f32) * 1.2) as u64 + 3; + let mut new_stream = TcpStream::connect(addr).unwrap(); + ms.poll.register(&new_stream, token, ready_w, edge).expect("PAC 3"); + std::mem::swap(stream, &mut new_stream); + } + println!("{:03?} ... end ActiveConnecting", major); + } + ActiveRecving { addr, polarity, endpoint } => { + println!("{:03?} start ActiveRecving...", major); + println!("{:03?} start ActiveRecving...", major); + println!("{:03?} start ActiveRecving...", major); + assert!(event.readiness().is_readable()); + 'recv_loop: while let Some(msg) = endpoint.recv()? { + if let Msg::SetupMsg(SetupMsg::ChannelSetup { info }) = msg { + if info.polarity == *polarity { + return Err(PolarityMatched(*addr)); + } + take_mut::take(entry, |e| { + assert_let![ActiveRecving { polarity, endpoint, .. } = e => { + let info = EndpointInfo { polarity, channel_id: info.channel_id }; + Finished(EndpointExt { info, endpoint }) + }] + }); + ms.polled_undrained.insert(ekey); + assert!(to_finish.remove(&ekey)); + break 'recv_loop; + } else { + ms.delayed.push(ReceivedMsg { recipient: ekey, msg }); + } + } + println!("{:03?} ... end ActiveRecving", major); + } + } + } + } + for ekey in polled_undrained_later { + ms.polled_undrained.insert(ekey); + } + let endpoint_exts = endpoint_ext_todos.type_convert(|(_, todo)| match todo { + Finished(endpoint_ext) => endpoint_ext, + _ => unreachable!(), + }); + Ok((ms, endpoint_exts)) + } + + fn setup_sink_tree_family( + major: ControllerId, + endpoint_exts: &mut Arena, + messenger_state: &mut MessengerState, + neighbors: Vec, + deadline: Instant, + ) -> Result { + use {ConnectErr::*, Msg::SetupMsg as S, SetupMsg::*}; + + println!("neighbors {:?}", &neighbors); + + let mut messenger = (messenger_state, endpoint_exts); + impl Messengerlike for (&mut MessengerState, &mut Arena) { + fn get_state_mut(&mut self) -> &mut MessengerState { + self.0 + } + fn get_endpoint_mut(&mut self, ekey: Key) -> &mut Endpoint { + &mut self.1.get_mut(ekey).expect("OUT OF BOUNDS").endpoint + } + } + + // 1. broadcast my ID as the first echo. await reply from all in net_keylist + let echo = S(LeaderEcho { maybe_leader: major }); + let mut awaiting = IndexSet::with_capacity(neighbors.len()); + for &n in neighbors.iter() { + println!("{:?}'s initial echo to {:?}, {:?}", major, n, &echo); + messenger.send(n, echo.clone())?; + awaiting.insert(n); + } + + // 2. Receive incoming replies. whenever a higher-id echo arrives, + // adopt it as leader, sender as parent, and reset the await set. + let mut parent: Option = None; + let mut my_leader = major; + messenger.undelay_all(); + 'echo_loop: while !awaiting.is_empty() || parent.is_some() { + let ReceivedMsg { recipient, msg } = messenger.recv(deadline)?.ok_or(Timeout)?; + println!("{:?} GOT {:?} {:?}", major, &recipient, &msg); + match msg { + S(LeaderAnnounce { leader }) => { + // someone else completed the echo and became leader first! + // the sender is my parent + parent = Some(recipient); + my_leader = leader; + awaiting.clear(); + break 'echo_loop; + } + S(LeaderEcho { maybe_leader }) => { + use Ordering::*; + match maybe_leader.cmp(&my_leader) { + Less => { /* ignore */ } + Equal => { + awaiting.remove(&recipient); + if awaiting.is_empty() { + if let Some(p) = parent { + // return the echo to my parent + messenger.send(p, S(LeaderEcho { maybe_leader }))?; + } else { + // DECIDE! + break 'echo_loop; + } + } + } + Greater => { + // join new echo + println!("{:?} setting leader to {:?}", major, recipient); + parent = Some(recipient); + my_leader = maybe_leader; + let echo = S(LeaderEcho { maybe_leader: my_leader }); + awaiting.clear(); + if neighbors.len() == 1 { + // immediately reply to parent + println!( + "{:?} replying echo to parent {:?} immediately", + major, recipient + ); + messenger.send(recipient, echo.clone())?; + } else { + for &n in neighbors.iter() { + if n != recipient { + println!( + "{:?} repeating echo {:?} to {:?}", + major, &echo, n + ); + messenger.send(n, echo.clone())?; + awaiting.insert(n); + } + } + } + } + } + } + msg => messenger.delay(ReceivedMsg { recipient, msg }), + } + } + match parent { + None => assert_eq!( + my_leader, major, + "I've got no parent, but I consider {:?} the leader?", + my_leader + ), + Some(parent) => assert_ne!( + my_leader, major, + "I have {:?} as parent, but I consider myself ({:?}) the leader?", + parent, major + ), + } + println!("{:?} DONE WITH ECHO", major); + + // 3. broadcast leader announcement (except to parent: confirm they are your parent) + // in this loop, every node sends 1 message to each neighbor + let msg_for_non_parents = S(LeaderAnnounce { leader: my_leader }); + for &k in neighbors.iter() { + let msg = + if Some(k) == parent { S(YouAreMyParent) } else { msg_for_non_parents.clone() }; + println!("{:?} ANNOUNCING to {:?} {:?}", major, k, &msg); + messenger.send(k, msg)?; + } + + // await 1 message from all non-parents + for &n in neighbors.iter() { + if Some(n) != parent { + awaiting.insert(n); + } + } + let mut children = Vec::default(); + messenger.undelay_all(); + while !awaiting.is_empty() { + let ReceivedMsg { recipient, msg } = messenger.recv(deadline)?.ok_or(Timeout)?; + match msg { + S(YouAreMyParent) => { + assert!(awaiting.remove(&recipient)); + children.push(recipient); + } + S(SetupMsg::LeaderAnnounce { leader }) => { + assert!(awaiting.remove(&recipient)); + assert!(leader == my_leader); + assert!(Some(recipient) != parent); + // they wouldn't send me this if they considered me their parent + } + _ => messenger.delay(ReceivedMsg { recipient, msg }), + } + } + Ok(ControllerFamily { parent_ekey: parent, children_ekeys: children }) + } +} + +impl Messengerlike for Controller { + fn get_state_mut(&mut self) -> &mut MessengerState { + &mut self.inner.messenger_state + } + fn get_endpoint_mut(&mut self, ekey: Key) -> &mut Endpoint { + &mut self.inner.endpoint_exts.get_mut(ekey).expect("OUT OF BOUNDS").endpoint + } +} diff --git a/src/test/connector.rs b/src/test/connector.rs new file mode 100644 index 0000000000000000000000000000000000000000..9c80bba69266419dedbd025808a678de844be1fe --- /dev/null +++ b/src/test/connector.rs @@ -0,0 +1,55 @@ +extern crate test_generator; + +use super::*; + +use std::fs; +use std::path::Path; +use std::thread; +use test_generator::test_resources; + +use crate::common::*; +use crate::runtime::*; + +#[test_resources("testdata/connector/duo/*.apdl")] +fn batch1(resource: &str) { + let a = Path::new(resource); + let b = a.with_extension("bpdl"); + let a = fs::read_to_string(a).unwrap(); + let b = fs::read_to_string(b).unwrap(); + duo(a, b); +} + +fn duo(one: String, two: String) { + let a = thread::spawn(move || { + let timeout = Duration::from_millis(1_500); + let addrs = ["127.0.0.1:7010".parse().unwrap(), "127.0.0.1:7011".parse().unwrap()]; + let mut x = Connector::Unconfigured(Unconfigured { controller_id: 0 }); + x.configure(one.as_bytes()).unwrap(); + x.bind_port(0, PortBinding::Passive(addrs[0])).unwrap(); + x.bind_port(1, PortBinding::Active(addrs[1])).unwrap(); + x.connect(timeout).unwrap(); + assert_eq!(0, x.sync(timeout).unwrap()); + assert_eq!(0, x.sync(timeout).unwrap()); + assert_eq!(0, x.sync(timeout).unwrap()); + assert_eq!(0, x.sync(timeout).unwrap()); + assert_eq!(0, x.sync(timeout).unwrap()); + assert_eq!(0, x.sync(timeout).unwrap()); + }); + let b = thread::spawn(move || { + let timeout = Duration::from_millis(1_500); + let addrs = ["127.0.0.1:7010".parse().unwrap(), "127.0.0.1:7011".parse().unwrap()]; + let mut x = Connector::Unconfigured(Unconfigured { controller_id: 1 }); + x.configure(two.as_bytes()).unwrap(); + x.bind_port(0, PortBinding::Passive(addrs[1])).unwrap(); + x.bind_port(1, PortBinding::Active(addrs[0])).unwrap(); + x.connect(timeout).unwrap(); + assert_eq!(0, x.sync(timeout).unwrap()); + assert_eq!(0, x.sync(timeout).unwrap()); + assert_eq!(0, x.sync(timeout).unwrap()); + assert_eq!(0, x.sync(timeout).unwrap()); + assert_eq!(0, x.sync(timeout).unwrap()); + assert_eq!(0, x.sync(timeout).unwrap()); + }); + handle(a.join()); + handle(b.join()); +} diff --git a/src/test/mod.rs b/src/test/mod.rs new file mode 100644 index 0000000000000000000000000000000000000000..19df860ad7ae855b0c1309654005b8f1956e7aa3 --- /dev/null +++ b/src/test/mod.rs @@ -0,0 +1,25 @@ +use core::fmt::Debug; + +mod connector; +mod setup; + +struct Panicked(Box); +impl Debug for Panicked { + fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result { + if let Some(str_slice) = self.0.downcast_ref::<&'static str>() { + f.pad(str_slice) + } else if let Some(string) = self.0.downcast_ref::() { + f.pad(string) + } else { + f.pad("Box") + } + } +} +fn handle(result: Result<(), std::boxed::Box<(dyn std::any::Any + std::marker::Send + 'static)>>) { + match result { + Ok(_) => {} + Err(x) => { + panic!("Worker panicked: {:?}", Panicked(x)); + } + } +} diff --git a/src/test/setup.rs b/src/test/setup.rs new file mode 100644 index 0000000000000000000000000000000000000000..64c28d7c4002d429c8818381adb507b381c1a097 --- /dev/null +++ b/src/test/setup.rs @@ -0,0 +1,111 @@ +use crate::common::*; +use crate::runtime::*; + +use PortBinding::*; + +use super::*; + +#[test] +fn config_ok_0() { + let pdl = b"primitive main() {}"; + let d = ProtocolD::parse(pdl).unwrap(); + let pol = d.main_interface_polarities(); + assert_eq!(&pol[..], &[]); +} + +#[test] +fn config_ok_2() { + let pdl = b"primitive main(in x, out y) {}"; + let d = ProtocolD::parse(pdl).unwrap(); + let pol = d.main_interface_polarities(); + assert_eq!(&pol[..], &[Polarity::Getter, Polarity::Putter]); +} + +#[test] +#[should_panic] +fn config_non_port() { + let pdl = b"primitive main(in q, int q) {}"; + ProtocolD::parse(pdl).unwrap(); +} + +#[test] +fn config_and_connect_2() { + let timeout = Duration::from_millis(1_500); + let addrs = ["127.0.0.1:9000".parse().unwrap(), "127.0.0.1:9001".parse().unwrap()]; + use std::thread; + let handles = vec![ + // + thread::spawn(move || { + let mut x = Connector::Unconfigured(Unconfigured { controller_id: 0 }); + x.configure(b"primitive main(in a, out b) {}").unwrap(); + x.bind_port(0, Passive(addrs[0])).unwrap(); + x.bind_port(1, Passive(addrs[1])).unwrap(); + x.connect(timeout).unwrap(); + }), + thread::spawn(move || { + let mut x = Connector::Unconfigured(Unconfigured { controller_id: 1 }); + x.configure(b"primitive main(out a, in b) {}").unwrap(); + x.bind_port(0, Active(addrs[0])).unwrap(); + x.bind_port(1, Active(addrs[1])).unwrap(); + x.connect(timeout).unwrap(); + }), + ]; + for h in handles { + handle(h.join()) + } +} + +#[test] +fn bind_too_much() { + let mut x = Connector::Unconfigured(Unconfigured { controller_id: 0 }); + x.configure(b"primitive main(in a) {}").unwrap(); + x.bind_port(0, Native).unwrap(); + assert!(x.bind_port(1, Native).is_err()); +} + +#[test] +fn config_and_connect_chain() { + let timeout = Duration::from_millis(1_500); + let addrs = [ + "127.0.0.1:9002".parse().unwrap(), + "127.0.0.1:9003".parse().unwrap(), + "127.0.0.1:9004".parse().unwrap(), + ]; + use std::thread; + let handles = vec![ + // + thread::spawn(move || { + // PRODUCER A-> + let mut x = Connector::Unconfigured(Unconfigured { controller_id: 0 }); + x.configure(b"primitive main(out a) {}").unwrap(); + x.bind_port(0, Active(addrs[0])).unwrap(); + x.connect(timeout).unwrap(); + }), + thread::spawn(move || { + // FORWARDER ->B-> + let mut x = Connector::Unconfigured(Unconfigured { controller_id: 0 }); + x.configure(b"primitive main(in a, out b) {}").unwrap(); + x.bind_port(0, Passive(addrs[0])).unwrap(); + x.bind_port(1, Active(addrs[1])).unwrap(); + x.connect(timeout).unwrap(); + }), + thread::spawn(move || { + // FORWARDER ->C-> + let mut x = Connector::Unconfigured(Unconfigured { controller_id: 2 }); + x.configure(b"primitive main(in a, out b) {}").unwrap(); + x.bind_port(0, Passive(addrs[1])).unwrap(); + x.bind_port(1, Active(addrs[2])).unwrap(); + x.connect(timeout).unwrap(); + }), + thread::spawn(move || { + // CONSUMER ->D + let mut x = Connector::Unconfigured(Unconfigured { controller_id: 3 }); + x.configure(b"primitive main(in a) {}").unwrap(); + x.bind_port(0, Passive(addrs[2])).unwrap(); + x.connect(timeout).unwrap(); + }), + ]; + for h in handles { + handle(h.join()) + } +}