Changeset - 113e4349a706
[Not reviewed]
1 32 12
Max Henger - 3 years ago 2022-04-13 14:57:03
henger@cwi.nl
feat: Builtin internet component
10 files changed:
0 comments (0 inline, 0 general)
Cargo.toml
Show inline comments
 
[package]
 
name = "reowolf_rs"
 
version = "1.2.0"
 
authors = [
 
	"Max Henger <henger@cwi.nl>",
 
	"Christopher Esterhuyse <esterhuy@cwi.nl>",
 
	"Hans-Dieter Hiep <hdh@cwi.nl>"
 
]
 
edition = "2021"
 

	
 
[dependencies]
 
# convenience macros
 
maplit = "1.0.2"
 
derive_more = "0.99.2"
 

	
 
# runtime
 
bincode = "1.3.1"
 
serde = { version = "1.0.114", features = ["derive"] }
 
getrandom = "0.1.14" # tiny crate. used to guess controller-id
 

	
 
# network
 
mio = { version = "0.7.0", package = "mio", features = ["udp", "tcp", "os-poll"] }
 
socket2 = { version = "0.3.12", optional = true }
 
[features]
 
default=["internet"]
 
internet=["libc"]
 

	
 
# protocol
 
backtrace = "0.3"
 
lazy_static = "1.4.0"
 

	
 
# ffi
 
[dependencies]
 

	
 
# socket ffi
 
libc = { version = "^0.2", optional = true }
 
os_socketaddr = { version = "0.1.0", optional = true }
 
libc = { version = "^0.2", optional = true } # raw sockets
 
mio = { version = "0.8", features = ["os-poll"] } # cross-platform IO notification queue
 

	
 
# randomness
 
rand = "0.8.4"
 
rand_pcg = "0.3.1"
 

	
 
[lib]
 
crate-type = [
 
	"rlib", # for use as a Rust dependency.
 
]
 
\ No newline at end of file
bin-compiler/src/main.rs
Show inline comments
 
use std::fs::File;
 
use std::io::Read;
 

	
 
use clap::{App, Arg};
 
use reowolf_rs as rw;
 

	
 
fn main() {
 
    let app = App::new("rwc")
 
        .author("Henger, M.")
 
        .version(env!("CARGO_PKG_VERSION"))
 
        .about("Reowolf compiler")
 
        .arg(
 
            Arg::new("input")
 
                .long("input")
 
                .short('i')
 
                .help("input files")
 
                .required(true)
 
                .takes_value(true)
 
                .multiple_occurrences(true)
 
        )
 
        .arg(
 
            Arg::new("threads")
 
                .long("threads")
 
                .short('t')
 
                .help("number of runtime threads")
 
                .default_value("1")
 
                .takes_value(true)
 
        )
 
        .arg(
 
            Arg::new("debug")
 
                .long("debug")
 
                .short('d')
 
                .help("enable debug logging")
 
        )
 
        .arg(
 
            Arg::new("stdlib")
 
                .long("stdlib")
 
                .short('s')
 
                .help("standard library directory (overrides default)")
 
                .takes_value(true)
 
        );
 

	
 
    // Retrieve arguments and convert
 
    let app = app.get_matches();
 
    let input_files = app.values_of("input");
 
    if input_files.is_none() {
 
        println!("ERROR: Expected at least one input file");
 
        return;
 
    }
 

	
 
    let num_threads = app.value_of("threads").unwrap();
 
    let num_threads = match num_threads.parse::<i32>() {
 
        Ok(num_threads) => {
 
            if num_threads < 0 || num_threads > 255 {
 
                println!("ERROR: Number of threads must be a number between 0 and 256");
 
                return;
 
            }
 

	
 
            num_threads as u32
 
        },
 
        Err(err) => {
 
            println!("ERROR: Failed to parse number of threads\nbecause: {}", err);
 
            return;
 
        }
 
    };
 

	
 
    let debug_enabled = app.is_present("debug");
 

	
 
    let standard_library_dir = app.value_of("stdlib")
 
        .map(|v| v.to_string());
 

	
 
    // Add input files to file buffer
 
    let input_files = input_files.unwrap();
 
    assert!(input_files.len() > 0); // because arg is required
 

	
 
    let mut builder = rw::ProtocolDescriptionBuilder::new();
 
    let mut builder = rw::ProtocolDescriptionBuilder::new(standard_library_dir)
 
        .expect("create protocol description builder");
 
    let mut file_buffer = Vec::with_capacity(4096);
 

	
 
    for input_file in input_files {
 
        print!("Adding file: {} ... ", input_file);
 
        let mut file = match File::open(input_file) {
 
            Ok(file) => file,
 
            Err(err) => {
 
                println!("FAILED (to open file)\nbecause:\n{}", err);
 
                return;
 
            }
 
        };
 

	
 
        file_buffer.clear();
 
        if let Err(err) = file.read_to_end(&mut file_buffer) {
 
            println!("FAILED (to read file)\nbecause:\n{}", err);
 
            return;
 
        }
 

	
 
        if let Err(err) = builder.add(input_file.to_string(), file_buffer.clone()) {
 
            println!("FAILED (to tokenize file)\nbecause:\n{}", err);
 
        }
 

	
 
        println!("Success");
 
    }
 

	
 
    // Compile the program
 
    print!("Compiling program ... ");
 
    let protocol_description = match builder.compile() {
 
        Ok(pd) => pd,
 
        Err(err) => {
 
            println!("FAILED\nbecause:\n{}", err);
 
            return;
 
        }
 
    };
 

	
 
    println!("Success");
 

	
 
    // Start runtime
 
    print!("Startup of runtime ... ");
 
    let runtime = rw::runtime2::Runtime::new(num_threads, debug_enabled, protocol_description);
 
    if let Err(err) = &runtime {
 
        println!("FAILED\nbecause:\n{}", err);
 
    }
 
    println!("Success");
 

	
 
    // Make sure there is a nameless module with a main component
 
    print!("Creating main component ... ");
 
    let runtime = rw::runtime2::Runtime::new(num_threads, debug_enabled, protocol_description);
 
    let runtime = runtime.unwrap();
 
    if let Err(err) = runtime.create_component(b"", b"main") {
 
        use rw::ComponentCreationError as CCE;
 
        let reason = match err {
 
            CCE::ModuleDoesntExist => "Input files did not contain a nameless module (that should contain the 'main' component)",
 
            CCE::DefinitionDoesntExist => "Input files did not contain a component called 'main'",
 
            CCE::DefinitionNotComponent => "Input file contained a 'main' function, but not a 'main' component",
 
            _ => "Unexpected error"
 
        };
 
        println!("FAILED\nbecause:\n{} (raw error: {:?})", reason, err);
 
        return;
 
    }
 

	
 
    println!("Success");
 
    println!("Now running until all components have exited");
 
    println!("--------------------------------------------\n\n");
 
}
 
\ No newline at end of file
src/collections/scoped_buffer.rs
Show inline comments
 
/// scoped_buffer.rs
 
///
 
/// Solves the common pattern where we are performing some kind of recursive
 
/// pattern while using a temporary buffer. At the start, or during the
 
/// procedure, we push stuff into the buffer. At the end we take out what we
 
/// have put in.
 
///
 
/// It is unsafe because we're using pointers to circumvent borrowing rules in
 
/// the name of code cleanliness. The correctness of use is checked in debug
 
/// mode at runtime.
 

	
 
use std::iter::FromIterator;
 

	
 
macro_rules! hide {
 
    ($v:block) => {
 
        #[cfg(debug_assertions)] $v
 
    };
 
    ($v:expr) => {
 
        #[cfg(debug_assertions)] $v
 
    };
 
}
 

	
 
pub(crate) struct ScopedBuffer<T: Sized> {
 
    pub inner: Vec<T>,
 
}
 

	
 
impl<T: Sized> ScopedBuffer<T> {
 
    pub(crate) fn with_capacity(capacity: usize) -> Self {
 
        Self {
 
            inner: Vec::with_capacity(capacity),
 
        }
 
    }
 

	
 
    pub(crate) fn start_section(&mut self) -> ScopedSection<T> {
 
        let start_size = self.inner.len() as u32;
 
        ScopedSection {
 
            inner: &mut self.inner,
 
            start_size,
 
            #[cfg(debug_assertions)] cur_size: start_size,
 
        }
 
    }
 
}
 

	
 
impl<T: Clone> ScopedBuffer<T> {
 
    pub(crate) fn start_section_initialized(&mut self, initialize_with: &[T]) -> ScopedSection<T> {
 
        let start_size = self.inner.len() as u32;
 
        let _data_size = initialize_with.len() as u32;
 
        self.inner.extend_from_slice(initialize_with);
 
        ScopedSection{
 
            inner: &mut self.inner,
 
            start_size,
 
            #[cfg(debug_assertions)] cur_size: start_size + _data_size,
 
        }
 
    }
 
}
 

	
 
#[cfg(debug_assertions)]
 
impl<T: Sized> Drop for ScopedBuffer<T> {
 
    fn drop(&mut self) {
 
        // Make sure that everyone cleaned up the buffer neatly
 
        debug_assert!(self.inner.is_empty(), "dropped non-empty scoped buffer");
 
    }
 
}
 

	
 
/// A section of the buffer. Keeps track of where we started the section. When
 
/// done with the section one must call `into_vec` or `forget` to remove the
 
/// section from the underlying buffer. This will also be done upon dropping the
 
/// ScopedSection in case errors are being handled.
 
pub(crate) struct ScopedSection<T: Sized> {
 
    inner: *mut Vec<T>,
 
    start_size: u32,
 
    #[cfg(debug_assertions)] cur_size: u32,
 
}
 

	
 
impl<T: Sized> ScopedSection<T> {
 
    /// Pushes value into section
 
    #[inline]
 
    pub(crate) fn push(&mut self, value: T) {
 
        self.check_length();
 
        let vec = unsafe{&mut *self.inner};
 
        vec.push(value);
 
        hide!(self.cur_size += 1);
 
    }
 

	
 
    #[inline]
 
    pub(crate) fn len(&self) -> usize {
 
        self.check_length();
 
        let vec = unsafe{&mut *self.inner};
 
        return vec.len() - self.start_size as usize;
 
    }
 

	
 
    #[inline]
 
    #[allow(unused_mut)] // used in debug mode
 
    pub(crate) fn forget(mut self) {
 
        self.check_length();
 
        let vec = unsafe{&mut *self.inner};
 
        hide!(self.cur_size = self.start_size);
 
        vec.truncate(self.start_size as usize);
 
    }
 

	
 
    #[inline]
 
    #[allow(unused_mut)] // used in debug mode
 
    pub(crate) fn into_vec(mut self) -> Vec<T> {
 
        self.check_length();
 
        let vec = unsafe{&mut *self.inner};
 
        hide!(self.cur_size = self.start_size);
 
        let section = Vec::from_iter(vec.drain(self.start_size as usize..));
 
        section
 
    }
 

	
 
    #[inline]
 
    pub(crate) fn check_length(&self) {
 
        hide!({
 
            let vec = unsafe{&*self.inner};
 
            debug_assert_eq!(
 
                vec.len(), self.cur_size as usize,
 
                "incorrect use of ScopedSection: underlying storage vector has changed size"
 
            )
 
        })
 
    }
 
}
 

	
 
impl<T: Sized + PartialEq> ScopedSection<T> {
 
    #[inline]
 
    pub(crate) fn push_unique(&mut self, value: T) {
 
        self.check_length();
 
        let vec = unsafe{&mut *self.inner};
 
        for item in &vec[self.start_size as usize..] {
 
            if *item == value {
 
                // item already exists
 
                return;
 
            }
 
        }
 

	
 
        vec.push(value);
 
        hide!(self.cur_size += 1);
 
    }
 

	
 
    #[inline]
 
    pub(crate) fn contains(&self, value: &T) -> bool {
 
        self.check_length();
 
        let vec = unsafe{&*self.inner};
 
        for index in self.start_size as usize..vec.len() {
 
            if &vec[index] == value {
 
                return true;
 
            }
 
        }
 

	
 
        return false;
 
    }
 
}
 

	
 
impl<T: Copy> ScopedSection<T> {
 
    pub(crate) fn iter_copied(&self) -> ScopedIter<T> {
 
        return ScopedIter{
 
            inner: self.inner,
 
            cur_index: self.start_size,
 
            last_index: unsafe{ (*self.inner).len() as u32 },
 
        }
 
    }
 
}
 

	
 
impl<T> std::ops::Index<usize> for ScopedSection<T> {
 
    type Output = T;
 

	
 
    fn index(&self, index: usize) -> &Self::Output {
 
        let vec = unsafe{&*self.inner};
 
        return &vec[self.start_size as usize + index]
 
    }
 
}
 

	
 
impl<T> std::ops::IndexMut<usize> for ScopedSection<T> {
 
    fn index_mut(&mut self, index: usize) -> &mut Self::Output {
 
        let vec = unsafe{&mut *self.inner};
 
        return &mut vec[self.start_size as usize + index]
 
    }
 
}
 

	
 
#[cfg(debug_assertions)]
 
// note: this `Drop` impl used to be debug-only, requiring the programmer to
 
// call `into_vec` or `forget`. But this is rather error prone. So we'll check
 
// in debug mode, but always truncate in release mode (even though this is a
 
// noop in most cases).
 
impl<T: Sized> Drop for ScopedSection<T> {
 
    fn drop(&mut self) {
 
        let vec = unsafe{&mut *self.inner};
 
        hide!(debug_assert_eq!(vec.len(), self.cur_size as usize));
 
        vec.truncate(self.start_size as usize);
 
    }
 
}
 

	
 
/// Small utility for iterating over a section of the buffer. Same conditions as
 
/// the buffer apply: each time we retrieve an element the buffer must have the
 
/// same size as the moment of creation.
 
pub(crate) struct ScopedIter<T: Copy> {
 
    inner: *mut Vec<T>,
 
    cur_index: u32,
 
    last_index: u32,
 
}
 

	
 
impl<T: Copy> Iterator for ScopedIter<T> {
 
    type Item = T;
 

	
 
    fn next(&mut self) -> Option<Self::Item> {
 
        hide!(debug_assert_eq!(self.last_index as usize, unsafe { (*self.inner).len() }));
 
        if self.cur_index >= self.last_index {
 
            return None;
 
        }
 

	
 
        let vec = unsafe{ &*self.inner };
 
        let index = self.cur_index as usize;
 
        self.cur_index += 1;
 
        return Some(vec[index]);
 
    }
 
}
 
\ No newline at end of file
src/protocol/ast.rs
Show inline comments
 
@@ -53,385 +53,385 @@ macro_rules! define_aliased_ast_id {
 
macro_rules! define_new_ast_id {
 
    // Variant where we just defined the new type, without any indexing
 
    ($name:ident, $parent:ty) => {
 
        #[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
 
        pub struct $name (pub(crate) $parent);
 

	
 
        #[allow(dead_code)]
 
        impl $name {
 
            pub(crate) fn new_invalid() -> Self     { Self(<$parent>::new_invalid()) }
 
            pub(crate) fn is_invalid(&self) -> bool { self.0.is_invalid() }
 
            pub fn upcast(self) -> $parent          { self.0 }
 
        }
 
    };
 
    // Variant where we define the type, and the Index and IndexMut traits
 
    (
 
        $name:ident, $parent:ty, 
 
        index($indexed_type:ty, $wrapper_type:path, $indexed_arena:ident)
 
    ) => {
 
        define_new_ast_id!($name, $parent);
 
        impl Index<$name> for Heap {
 
            type Output = $indexed_type;
 
            fn index(&self, index: $name) -> &Self::Output {
 
                if let $wrapper_type(v) = &self.$indexed_arena[index.0] {
 
                    v
 
                } else {
 
                    unreachable!()
 
                }
 
            }
 
        }
 

	
 
        impl IndexMut<$name> for Heap {
 
            fn index_mut(&mut self, index: $name) -> &mut Self::Output {
 
                if let $wrapper_type(v) = &mut self.$indexed_arena[index.0] {
 
                    v
 
                } else {
 
                    unreachable!()
 
                }
 
            }
 
        }
 
    };
 
    // Variant where we define the type, the Index and IndexMut traits, and an allocation function
 
    (
 
        $name:ident, $parent:ty, 
 
        index($indexed_type:ty, $wrapper_type:path, $indexed_arena:ident),
 
        alloc($fn_name:ident)
 
    ) => {
 
        define_new_ast_id!($name, $parent, index($indexed_type, $wrapper_type, $indexed_arena));
 
        impl Heap {
 
            pub fn $fn_name(&mut self, f: impl FnOnce($name) -> $indexed_type) -> $name {
 
                $name(
 
                    self.$indexed_arena.alloc_with_id(|id| {
 
                        $wrapper_type(f($name(id)))
 
                    })
 
                )
 
            }
 
        }
 
    }
 
}
 

	
 
define_aliased_ast_id!(RootId, Id<Root>, index(Root, protocol_descriptions), alloc(alloc_protocol_description));
 
define_aliased_ast_id!(PragmaId, Id<Pragma>, index(Pragma, pragmas), alloc(alloc_pragma));
 
define_aliased_ast_id!(ImportId, Id<Import>, index(Import, imports), alloc(alloc_import));
 
define_aliased_ast_id!(VariableId, Id<Variable>, index(Variable, variables), alloc(alloc_variable));
 

	
 
define_aliased_ast_id!(DefinitionId, Id<Definition>, index(Definition, definitions));
 
define_new_ast_id!(StructDefinitionId, DefinitionId, index(StructDefinition, Definition::Struct, definitions), alloc(alloc_struct_definition));
 
define_new_ast_id!(EnumDefinitionId, DefinitionId, index(EnumDefinition, Definition::Enum, definitions), alloc(alloc_enum_definition));
 
define_new_ast_id!(UnionDefinitionId, DefinitionId, index(UnionDefinition, Definition::Union, definitions), alloc(alloc_union_definition));
 
define_new_ast_id!(ProcedureDefinitionId, DefinitionId, index(ProcedureDefinition, Definition::Procedure, definitions), alloc(alloc_procedure_definition));
 

	
 
define_aliased_ast_id!(StatementId, Id<Statement>, index(Statement, statements));
 
define_new_ast_id!(BlockStatementId, StatementId, index(BlockStatement, Statement::Block, statements), alloc(alloc_block_statement));
 
define_new_ast_id!(EndBlockStatementId, StatementId, index(EndBlockStatement, Statement::EndBlock, statements), alloc(alloc_end_block_statement));
 
define_new_ast_id!(LocalStatementId, StatementId, index(LocalStatement, Statement::Local, statements));
 
define_new_ast_id!(MemoryStatementId, LocalStatementId);
 
define_new_ast_id!(ChannelStatementId, LocalStatementId);
 
define_new_ast_id!(LabeledStatementId, StatementId, index(LabeledStatement, Statement::Labeled, statements), alloc(alloc_labeled_statement));
 
define_new_ast_id!(IfStatementId, StatementId, index(IfStatement, Statement::If, statements), alloc(alloc_if_statement));
 
define_new_ast_id!(EndIfStatementId, StatementId, index(EndIfStatement, Statement::EndIf, statements), alloc(alloc_end_if_statement));
 
define_new_ast_id!(WhileStatementId, StatementId, index(WhileStatement, Statement::While, statements), alloc(alloc_while_statement));
 
define_new_ast_id!(EndWhileStatementId, StatementId, index(EndWhileStatement, Statement::EndWhile, statements), alloc(alloc_end_while_statement));
 
define_new_ast_id!(BreakStatementId, StatementId, index(BreakStatement, Statement::Break, statements), alloc(alloc_break_statement));
 
define_new_ast_id!(ContinueStatementId, StatementId, index(ContinueStatement, Statement::Continue, statements), alloc(alloc_continue_statement));
 
define_new_ast_id!(SynchronousStatementId, StatementId, index(SynchronousStatement, Statement::Synchronous, statements), alloc(alloc_synchronous_statement));
 
define_new_ast_id!(EndSynchronousStatementId, StatementId, index(EndSynchronousStatement, Statement::EndSynchronous, statements), alloc(alloc_end_synchronous_statement));
 
define_new_ast_id!(ForkStatementId, StatementId, index(ForkStatement, Statement::Fork, statements), alloc(alloc_fork_statement));
 
define_new_ast_id!(EndForkStatementId, StatementId, index(EndForkStatement, Statement::EndFork, statements), alloc(alloc_end_fork_statement));
 
define_new_ast_id!(SelectStatementId, StatementId, index(SelectStatement, Statement::Select, statements), alloc(alloc_select_statement));
 
define_new_ast_id!(EndSelectStatementId, StatementId, index(EndSelectStatement, Statement::EndSelect, statements), alloc(alloc_end_select_statement));
 
define_new_ast_id!(ReturnStatementId, StatementId, index(ReturnStatement, Statement::Return, statements), alloc(alloc_return_statement));
 
define_new_ast_id!(GotoStatementId, StatementId, index(GotoStatement, Statement::Goto, statements), alloc(alloc_goto_statement));
 
define_new_ast_id!(NewStatementId, StatementId, index(NewStatement, Statement::New, statements), alloc(alloc_new_statement));
 
define_new_ast_id!(ExpressionStatementId, StatementId, index(ExpressionStatement, Statement::Expression, statements), alloc(alloc_expression_statement));
 

	
 
define_aliased_ast_id!(ExpressionId, Id<Expression>, index(Expression, expressions));
 
define_new_ast_id!(AssignmentExpressionId, ExpressionId, index(AssignmentExpression, Expression::Assignment, expressions), alloc(alloc_assignment_expression));
 
define_new_ast_id!(BindingExpressionId, ExpressionId, index(BindingExpression, Expression::Binding, expressions), alloc(alloc_binding_expression));
 
define_new_ast_id!(ConditionalExpressionId, ExpressionId, index(ConditionalExpression, Expression::Conditional, expressions), alloc(alloc_conditional_expression));
 
define_new_ast_id!(BinaryExpressionId, ExpressionId, index(BinaryExpression, Expression::Binary, expressions), alloc(alloc_binary_expression));
 
define_new_ast_id!(UnaryExpressionId, ExpressionId, index(UnaryExpression, Expression::Unary, expressions), alloc(alloc_unary_expression));
 
define_new_ast_id!(IndexingExpressionId, ExpressionId, index(IndexingExpression, Expression::Indexing, expressions), alloc(alloc_indexing_expression));
 
define_new_ast_id!(SlicingExpressionId, ExpressionId, index(SlicingExpression, Expression::Slicing, expressions), alloc(alloc_slicing_expression));
 
define_new_ast_id!(SelectExpressionId, ExpressionId, index(SelectExpression, Expression::Select, expressions), alloc(alloc_select_expression));
 
define_new_ast_id!(LiteralExpressionId, ExpressionId, index(LiteralExpression, Expression::Literal, expressions), alloc(alloc_literal_expression));
 
define_new_ast_id!(CastExpressionId, ExpressionId, index(CastExpression, Expression::Cast, expressions), alloc(alloc_cast_expression));
 
define_new_ast_id!(CallExpressionId, ExpressionId, index(CallExpression, Expression::Call, expressions), alloc(alloc_call_expression));
 
define_new_ast_id!(VariableExpressionId, ExpressionId, index(VariableExpression, Expression::Variable, expressions), alloc(alloc_variable_expression));
 

	
 
define_aliased_ast_id!(ScopeId, Id<Scope>, index(Scope, scopes), alloc(alloc_scope));
 

	
 
#[derive(Debug)]
 
pub struct Heap {
 
    // Root arena, contains the entry point for different modules. Each root
 
    // contains lists of IDs that correspond to the other arenas.
 
    pub(crate) protocol_descriptions: Arena<Root>,
 
    // Contents of a file, these are the elements the `Root` elements refer to
 
    pragmas: Arena<Pragma>,
 
    pub(crate) imports: Arena<Import>,
 
    pub(crate) variables: Arena<Variable>,
 
    pub(crate) definitions: Arena<Definition>,
 
    pub(crate) statements: Arena<Statement>,
 
    pub(crate) expressions: Arena<Expression>,
 
    pub(crate) scopes: Arena<Scope>,
 
}
 

	
 
impl Heap {
 
    pub fn new() -> Heap {
 
        Heap {
 
            // string_alloc: StringAllocator::new(),
 
            protocol_descriptions: Arena::new(),
 
            pragmas: Arena::new(),
 
            imports: Arena::new(),
 
            variables: Arena::new(),
 
            definitions: Arena::new(),
 
            statements: Arena::new(),
 
            expressions: Arena::new(),
 
            scopes: Arena::new(),
 
        }
 
    }
 
    pub fn alloc_memory_statement(
 
        &mut self,
 
        f: impl FnOnce(MemoryStatementId) -> MemoryStatement,
 
    ) -> MemoryStatementId {
 
        MemoryStatementId(LocalStatementId(self.statements.alloc_with_id(|id| {
 
            Statement::Local(LocalStatement::Memory(
 
                f(MemoryStatementId(LocalStatementId(id)))
 
            ))
 
        })))
 
    }
 
    pub fn alloc_channel_statement(
 
        &mut self,
 
        f: impl FnOnce(ChannelStatementId) -> ChannelStatement,
 
    ) -> ChannelStatementId {
 
        ChannelStatementId(LocalStatementId(self.statements.alloc_with_id(|id| {
 
            Statement::Local(LocalStatement::Channel(
 
                f(ChannelStatementId(LocalStatementId(id)))
 
            ))
 
        })))
 
    }
 
}
 

	
 
impl Index<MemoryStatementId> for Heap {
 
    type Output = MemoryStatement;
 
    fn index(&self, index: MemoryStatementId) -> &Self::Output {
 
        match &self.statements[index.0.0] {
 
            Statement::Local(LocalStatement::Memory(v)) => v,
 
            _ => unreachable!(),
 
        }
 
    }
 
}
 

	
 
impl Index<ChannelStatementId> for Heap {
 
    type Output = ChannelStatement;
 
    fn index(&self, index: ChannelStatementId) -> &Self::Output {
 
        match &self.statements[index.0.0] {
 
            Statement::Local(LocalStatement::Channel(v)) => v,
 
            _ => unreachable!(),
 
        }
 
    }
 
}
 

	
 
#[derive(Debug, Clone)]
 
pub struct Root {
 
    pub this: RootId,
 
    // Phase 1: parser
 
    // pub position: InputPosition,
 
    pub pragmas: Vec<PragmaId>,
 
    pub imports: Vec<ImportId>,
 
    pub definitions: Vec<DefinitionId>,
 
}
 

	
 
impl Root {
 
    pub fn get_definition_ident(&self, h: &Heap, id: &[u8]) -> Option<DefinitionId> {
 
    pub fn get_definition_by_ident(&self, h: &Heap, id: &[u8]) -> Option<DefinitionId> {
 
        for &def in self.definitions.iter() {
 
            if h[def].identifier().value.as_bytes() == id {
 
                return Some(def);
 
            }
 
        }
 
        None
 
    }
 
}
 

	
 
#[derive(Debug, Clone)]
 
pub enum Pragma {
 
    Version(PragmaVersion),
 
    Module(PragmaModule),
 
}
 

	
 
impl Pragma {
 
    pub(crate) fn as_module(&self) -> &PragmaModule {
 
        match self {
 
            Pragma::Module(pragma) => pragma,
 
            _ => unreachable!("Tried to obtain {:?} as PragmaModule", self),
 
        }
 
    }
 
}
 

	
 
#[derive(Debug, Clone)]
 
pub struct PragmaVersion {
 
    pub this: PragmaId,
 
    // Phase 1: parser
 
    pub span: InputSpan, // of full pragma
 
    pub version: u64,
 
}
 

	
 
#[derive(Debug, Clone)]
 
pub struct PragmaModule {
 
    pub this: PragmaId,
 
    // Phase 1: parser
 
    pub span: InputSpan, // of full pragma
 
    pub value: Identifier,
 
}
 

	
 
#[derive(Debug, Clone)]
 
pub enum Import {
 
    Module(ImportModule),
 
    Symbols(ImportSymbols)
 
}
 

	
 
impl Import {
 
    pub(crate) fn span(&self) -> InputSpan {
 
        match self {
 
            Import::Module(v) => v.span,
 
            Import::Symbols(v) => v.span,
 
        }
 
    }
 

	
 
    pub(crate) fn as_module(&self) -> &ImportModule {
 
        match self {
 
            Import::Module(m) => m,
 
            _ => unreachable!("Unable to cast 'Import' to 'ImportModule'")
 
        }
 
    }
 
    pub(crate) fn as_symbols(&self) -> &ImportSymbols {
 
        match self {
 
            Import::Symbols(m) => m,
 
            _ => unreachable!("Unable to cast 'Import' to 'ImportSymbols'")
 
        }
 
    }
 
    pub(crate) fn as_symbols_mut(&mut self) -> &mut ImportSymbols {
 
        match self {
 
            Import::Symbols(m) => m,
 
            _ => unreachable!("Unable to cast 'Import' to 'ImportSymbols'")
 
        }
 
    }
 
}
 

	
 
#[derive(Debug, Clone)]
 
pub struct ImportModule {
 
    pub this: ImportId,
 
    // Phase 1: parser
 
    pub span: InputSpan,
 
    pub module: Identifier,
 
    pub alias: Identifier,
 
    pub module_id: RootId,
 
}
 

	
 
#[derive(Debug, Clone)]
 
pub struct AliasedSymbol {
 
    pub name: Identifier,
 
    pub alias: Option<Identifier>,
 
    pub definition_id: DefinitionId,
 
}
 

	
 
#[derive(Debug, Clone)]
 
pub struct ImportSymbols {
 
    pub this: ImportId,
 
    // Phase 1: parser
 
    pub span: InputSpan,
 
    pub module: Identifier,
 
    pub module_id: RootId,
 
    pub symbols: Vec<AliasedSymbol>,
 
}
 

	
 
#[derive(Debug, Clone)]
 
pub struct Identifier {
 
    pub span: InputSpan,
 
    pub value: StringRef<'static>,
 
}
 

	
 
impl Identifier {
 
    pub(crate) const fn new_empty(span: InputSpan) -> Identifier {
 
        return Identifier{
 
            span,
 
            value: StringRef::new_empty(),
 
        };
 
    }
 
}
 

	
 
impl PartialEq for Identifier {
 
    fn eq(&self, other: &Self) -> bool {
 
        return self.value == other.value
 
    }
 
}
 

	
 
impl Display for Identifier {
 
    fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result {
 
        write!(f, "{}", self.value.as_str())
 
    }
 
}
 

	
 
#[derive(Debug, Clone, PartialEq, Eq)]
 
pub enum ParserTypeVariant {
 
    // Special builtin, only usable by the compiler and not constructable by the
 
    // programmer
 
    Void,
 
    InputOrOutput,
 
    ArrayLike,
 
    IntegerLike,
 
    // Basic builtin
 
    Message,
 
    Bool,
 
    UInt8, UInt16, UInt32, UInt64,
 
    SInt8, SInt16, SInt32, SInt64,
 
    Character, String,
 
    // Literals (need to get concrete builtin type during typechecking)
 
    IntegerLiteral,
 
    // Marker for inference
 
    Inferred,
 
    // Builtins expecting one subsequent type
 
    Array,
 
    Input,
 
    Output,
 
    // Tuple: expecting any number of elements. Note that the parser type can
 
    // have one-valued tuples, these will be filtered out later during type
 
    // checking.
 
    Tuple(u32), // u32 = number of subsequent types
 
    // User-defined types
 
    PolymorphicArgument(DefinitionId, u32), // u32 = index into polymorphic variables
 
    Definition(DefinitionId, u32), // u32 = number of subsequent types in the type tree.
 
}
 

	
 
impl ParserTypeVariant {
 
    pub(crate) fn num_embedded(&self) -> usize {
 
        use ParserTypeVariant::*;
 

	
 
        match self {
 
            Void | IntegerLike |
 
            Message | Bool |
 
            UInt8 | UInt16 | UInt32 | UInt64 |
 
            SInt8 | SInt16 | SInt32 | SInt64 |
 
            Character | String | IntegerLiteral |
 
            Inferred | PolymorphicArgument(_, _) =>
 
                0,
 
            ArrayLike | InputOrOutput | Array | Input | Output =>
 
                1,
 
            Definition(_, num) | Tuple(num) => *num as usize,
 
        }
 
    }
 
}
 

	
 
/// ParserTypeElement is an element of the type tree. An element may be
 
/// implicit, meaning that the user didn't specify the type, but it was set by
 
/// the compiler.
 
#[derive(Debug, Clone)]
 
pub struct ParserTypeElement {
 
    pub element_span: InputSpan, // span of this element, not including the child types
 
    pub variant: ParserTypeVariant,
 
}
 

	
 
/// ParserType is a specification of a type during the parsing phase and initial
 
/// linker/validator phase of the compilation process. These types may be
 
/// (partially) inferred or represent literals (e.g. a integer whose bytesize is
 
/// not yet determined).
 
///
 
@@ -743,556 +743,582 @@ pub enum ScopeAssociation {
 
#[derive(Debug, Clone)]
 
pub struct Scope {
 
    // Relation to other scopes
 
    pub this: ScopeId,
 
    pub parent: Option<ScopeId>,
 
    pub nested: Vec<ScopeId>,
 
    // Locally available variables/labels
 
    pub association: ScopeAssociation,
 
    pub variables: Vec<VariableId>,
 
    pub labels: Vec<LabeledStatementId>,
 
    // Location trackers/counters
 
    pub relative_pos_in_parent: i32,
 
    pub first_unique_id_in_scope: i32,
 
    pub next_unique_id_in_scope: i32,
 
}
 

	
 
impl Scope {
 
    pub(crate) fn new(id: ScopeId, association: ScopeAssociation) -> Self {
 
        return Self{
 
            this: id,
 
            parent: None,
 
            nested: Vec::new(),
 
            association,
 
            variables: Vec::new(),
 
            labels: Vec::new(),
 
            relative_pos_in_parent: -1,
 
            first_unique_id_in_scope: -1,
 
            next_unique_id_in_scope: -1,
 
        }
 
    }
 
}
 

	
 
impl Scope {
 
    pub(crate) fn new_invalid(this: ScopeId) -> Self {
 
        return Self{
 
            this,
 
            parent: None,
 
            nested: Vec::new(),
 
            association: ScopeAssociation::Definition(DefinitionId::new_invalid()),
 
            variables: Vec::new(),
 
            labels: Vec::new(),
 
            relative_pos_in_parent: -1,
 
            first_unique_id_in_scope: -1,
 
            next_unique_id_in_scope: -1,
 
        };
 
    }
 
}
 

	
 
#[derive(Debug, Clone, PartialEq, Eq)]
 
pub enum VariableKind {
 
    Parameter,      // in parameter list of function/component
 
    Local,          // declared in function/component body
 
    Binding,        // may be bound to in a binding expression (determined in validator/linker)
 
}
 

	
 
#[derive(Debug, Clone)]
 
pub struct Variable {
 
    pub this: VariableId,
 
    // Parsing
 
    pub kind: VariableKind,
 
    pub parser_type: ParserType,
 
    pub identifier: Identifier,
 
    // Validator/linker
 
    pub relative_pos_in_parent: i32,
 
    pub unique_id_in_scope: i32, // Temporary fix until proper bytecode/asm is generated
 
}
 

	
 
#[derive(Debug)]
 
pub enum Definition {
 
    Struct(StructDefinition),
 
    Enum(EnumDefinition),
 
    Union(UnionDefinition),
 
    Procedure(ProcedureDefinition),
 
}
 

	
 
impl Definition {
 
    pub fn is_struct(&self) -> bool {
 
        match self {
 
            Definition::Struct(_) => true,
 
            _ => false
 
        }
 
    }
 
    pub(crate) fn as_struct(&self) -> &StructDefinition {
 
        match self {
 
            Definition::Struct(result) => result,
 
            _ => panic!("Unable to cast 'Definition' to 'StructDefinition'"),
 
        }
 
    }
 
    pub(crate) fn as_struct_mut(&mut self) -> &mut StructDefinition {
 
        match self {
 
            Definition::Struct(result) => result,
 
            _ => panic!("Unable to cast 'Definition' to 'StructDefinition'"),
 
        }
 
    }
 
    pub fn is_enum(&self) -> bool {
 
        match self {
 
            Definition::Enum(_) => true,
 
            _ => false,
 
        }
 
    }
 
    pub(crate) fn as_enum(&self) -> &EnumDefinition {
 
        match self {
 
            Definition::Enum(result) => result,
 
            _ => panic!("Unable to cast 'Definition' to 'EnumDefinition'"),
 
        }
 
    }
 
    pub(crate) fn as_enum_mut(&mut self) -> &mut EnumDefinition {
 
        match self {
 
            Definition::Enum(result) => result,
 
            _ => panic!("Unable to cast 'Definition' to 'EnumDefinition'"),
 
        }
 
    }
 
    pub fn is_union(&self) -> bool {
 
        match self {
 
            Definition::Union(_) => true,
 
            _ => false,
 
        }
 
    }
 
    pub(crate) fn as_union(&self) -> &UnionDefinition {
 
        match self {
 
            Definition::Union(result) => result, 
 
            _ => panic!("Unable to cast 'Definition' to 'UnionDefinition'"),
 
        }
 
    }
 

	
 
    pub(crate) fn as_union_mut(&mut self) -> &mut UnionDefinition {
 
        match self {
 
            Definition::Union(result) => result,
 
            _ => panic!("Unable to cast 'Definition' to 'UnionDefinition'"),
 
        }
 
    }
 

	
 
    pub fn is_procedure(&self) -> bool {
 
        match self {
 
            Definition::Procedure(_) => true,
 
            _ => false,
 
        }
 
    }
 

	
 
    pub(crate) fn as_procedure(&self) -> &ProcedureDefinition {
 
        match self {
 
            Definition::Procedure(result) => result,
 
            _ => panic!("Unable to cast `Definition` to `Function`"),
 
        }
 
    }
 

	
 
    pub(crate) fn as_procedure_mut(&mut self) -> &mut ProcedureDefinition {
 
        match self {
 
            Definition::Procedure(result) => result,
 
            _ => panic!("Unable to cast `Definition` to `Function`"),
 
        }
 
    }
 

	
 
    pub fn defined_in(&self) -> RootId {
 
        match self {
 
            Definition::Struct(def) => def.defined_in,
 
            Definition::Enum(def) => def.defined_in,
 
            Definition::Union(def) => def.defined_in,
 
            Definition::Procedure(def) => def.defined_in,
 
        }
 
    }
 

	
 
    pub fn identifier(&self) -> &Identifier {
 
        match self {
 
            Definition::Struct(def) => &def.identifier,
 
            Definition::Enum(def) => &def.identifier,
 
            Definition::Union(def) => &def.identifier,
 
            Definition::Procedure(def) => &def.identifier,
 
        }
 
    }
 
    pub fn poly_vars(&self) -> &Vec<Identifier> {
 
        match self {
 
            Definition::Struct(def) => &def.poly_vars,
 
            Definition::Enum(def) => &def.poly_vars,
 
            Definition::Union(def) => &def.poly_vars,
 
            Definition::Procedure(def) => &def.poly_vars,
 
        }
 
    }
 
}
 

	
 
#[derive(Debug, Clone)]
 
pub struct StructFieldDefinition {
 
    pub span: InputSpan,
 
    pub field: Identifier,
 
    pub parser_type: ParserType,
 
}
 

	
 
#[derive(Debug, Clone)]
 
pub struct StructDefinition {
 
    pub this: StructDefinitionId,
 
    pub defined_in: RootId,
 
    // Symbol scanning
 
    pub span: InputSpan,
 
    pub identifier: Identifier,
 
    pub poly_vars: Vec<Identifier>,
 
    // Parsing
 
    pub fields: Vec<StructFieldDefinition>
 
}
 

	
 
impl StructDefinition {
 
    pub(crate) fn new_empty(
 
        this: StructDefinitionId, defined_in: RootId, span: InputSpan,
 
        this: StructDefinitionId, defined_in: RootId,
 
        identifier: Identifier, poly_vars: Vec<Identifier>
 
    ) -> Self {
 
        Self{ this, defined_in, span, identifier, poly_vars, fields: Vec::new() }
 
        Self{ this, defined_in, identifier, poly_vars, fields: Vec::new() }
 
    }
 
}
 

	
 
#[derive(Debug, Clone, Copy)]
 
pub enum EnumVariantValue {
 
    None,
 
    Integer(i64),
 
}
 

	
 
#[derive(Debug, Clone)]
 
pub struct EnumVariantDefinition {
 
    pub identifier: Identifier,
 
    pub value: EnumVariantValue,
 
}
 

	
 
#[derive(Debug, Clone)]
 
pub struct EnumDefinition {
 
    pub this: EnumDefinitionId,
 
    pub defined_in: RootId,
 
    // Symbol scanning
 
    pub span: InputSpan,
 
    pub identifier: Identifier,
 
    pub poly_vars: Vec<Identifier>,
 
    // Parsing
 
    pub variants: Vec<EnumVariantDefinition>,
 
}
 

	
 
impl EnumDefinition {
 
    pub(crate) fn new_empty(
 
        this: EnumDefinitionId, defined_in: RootId, span: InputSpan,
 
        this: EnumDefinitionId, defined_in: RootId,
 
        identifier: Identifier, poly_vars: Vec<Identifier>
 
    ) -> Self {
 
        Self{ this, defined_in, span, identifier, poly_vars, variants: Vec::new() }
 
        Self{ this, defined_in, identifier, poly_vars, variants: Vec::new() }
 
    }
 
}
 

	
 
#[derive(Debug, Clone)]
 
pub struct UnionVariantDefinition {
 
    pub span: InputSpan,
 
    pub identifier: Identifier,
 
    pub value: Vec<ParserType>, // if empty, then union variant does not contain any embedded types
 
}
 

	
 
#[derive(Debug, Clone)]
 
pub struct UnionDefinition {
 
    pub this: UnionDefinitionId,
 
    pub defined_in: RootId,
 
    // Phase 1: symbol scanning
 
    pub span: InputSpan,
 
    pub identifier: Identifier,
 
    pub poly_vars: Vec<Identifier>,
 
    // Phase 2: parsing
 
    pub variants: Vec<UnionVariantDefinition>,
 
}
 

	
 
impl UnionDefinition {
 
    pub(crate) fn new_empty(
 
        this: UnionDefinitionId, defined_in: RootId, span: InputSpan,
 
        this: UnionDefinitionId, defined_in: RootId,
 
        identifier: Identifier, poly_vars: Vec<Identifier>
 
    ) -> Self {
 
        Self{ this, defined_in, span, identifier, poly_vars, variants: Vec::new() }
 
        Self{ this, defined_in, identifier, poly_vars, variants: Vec::new() }
 
    }
 
}
 

	
 
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
 
pub enum ProcedureKind {
 
    Function, // with return type
 
    Primitive, // without return type
 
    Composite,
 
}
 

	
 
/// Monomorphed instantiation of a procedure (or the sole instantiation of a
 
/// non-polymorphic procedure).
 
#[derive(Debug)]
 
pub struct ProcedureDefinitionMonomorph {
 
    pub argument_types: Vec<TypeId>,
 
    pub expr_info: Vec<ExpressionInfo>
 
}
 

	
 
impl ProcedureDefinitionMonomorph {
 
    pub(crate) fn new_invalid() -> Self {
 
        return Self{
 
            argument_types: Vec::new(),
 
            expr_info: Vec::new(),
 
        }
 
    }
 
}
 

	
 
#[derive(Debug, Clone, Copy)]
 
pub struct ExpressionInfo {
 
    pub type_id: TypeId,
 
    pub variant: ExpressionInfoVariant,
 
}
 

	
 
impl ExpressionInfo {
 
    pub(crate) fn new_invalid() -> Self {
 
        return Self{
 
            type_id: TypeId::new_invalid(),
 
            variant: ExpressionInfoVariant::Generic,
 
        }
 
    }
 
}
 

	
 
#[derive(Debug, Clone, Copy)]
 
pub enum ExpressionInfoVariant {
 
    Generic,
 
    Procedure(TypeId, u32), // procedure TypeID and its monomorph index
 
    Select(i32), // index
 
}
 

	
 
impl ExpressionInfoVariant {
 
    pub(crate) fn as_select(&self) -> i32 {
 
        match self {
 
            ExpressionInfoVariant::Select(v) => *v,
 
            _ => unreachable!(),
 
        }
 
    }
 

	
 
    pub(crate) fn as_procedure(&self) -> (TypeId, u32) {
 
        match self {
 
            ExpressionInfoVariant::Procedure(type_id, monomorph_index) => (*type_id, *monomorph_index),
 
            _ => unreachable!(),
 
        }
 
    }
 
}
 

	
 
#[derive(Debug)]
 
pub enum ProcedureSource {
 
    FuncUserDefined,
 
    CompUserDefined,
 
    // Builtin functions, available to user
 
    FuncGet,
 
    FuncPut,
 
    FuncFires,
 
    FuncCreate,
 
    FuncLength,
 
    FuncAssert,
 
    FuncPrint,
 
    // Buitlin functions, not available to user
 
    FuncSelectStart,
 
    FuncSelectRegisterCasePort,
 
    FuncSelectWait,
 
    // Builtin components, available to user
 
    CompRandomU32, // TODO: Remove, temporary thing
 
    CompTcpClient,
 
}
 

	
 
impl ProcedureSource {
 
    pub(crate) fn is_builtin(&self) -> bool {
 
        match self {
 
            ProcedureSource::FuncUserDefined | ProcedureSource::CompUserDefined => false,
 
            _ => true,
 
        }
 
    }
 
}
 

	
 

	
 
/// Generic storage for functions, primitive components and composite
 
/// components.
 
// Note that we will have function definitions for builtin functions as well. In
 
// that case the span, the identifier span and the body are all invalid.
 
#[derive(Debug)]
 
pub struct ProcedureDefinition {
 
    pub this: ProcedureDefinitionId,
 
    pub defined_in: RootId,
 
    // Symbol scanning
 
    pub builtin: bool,
 
    pub kind: ProcedureKind,
 
    pub span: InputSpan,
 
    pub identifier: Identifier,
 
    pub poly_vars: Vec<Identifier>,
 
    // Parser
 
    pub source: ProcedureSource,
 
    pub return_type: Option<ParserType>, // present on functions, not components
 
    pub parameters: Vec<VariableId>,
 
    pub scope: ScopeId,
 
    pub body: BlockStatementId,
 
    // Monomorphization of typed procedures
 
    pub monomorphs: Vec<ProcedureDefinitionMonomorph>,
 
}
 

	
 
impl ProcedureDefinition {
 
    pub(crate) fn new_empty(
 
        this: ProcedureDefinitionId, defined_in: RootId, span: InputSpan,
 
        this: ProcedureDefinitionId, defined_in: RootId,
 
        kind: ProcedureKind, identifier: Identifier, poly_vars: Vec<Identifier>
 
    ) -> Self {
 
        Self {
 
            this, defined_in,
 
            builtin: false,
 
            span,
 
            kind, identifier, poly_vars,
 
            source: ProcedureSource::FuncUserDefined,
 
            return_type: None,
 
            parameters: Vec::new(),
 
            scope: ScopeId::new_invalid(),
 
            body: BlockStatementId::new_invalid(),
 
            monomorphs: Vec::new(),
 
        }
 
    }
 
}
 

	
 
#[derive(Debug, Clone)]
 
pub enum Statement {
 
    Block(BlockStatement),
 
    EndBlock(EndBlockStatement),
 
    Local(LocalStatement),
 
    Labeled(LabeledStatement),
 
    If(IfStatement),
 
    EndIf(EndIfStatement),
 
    While(WhileStatement),
 
    EndWhile(EndWhileStatement),
 
    Break(BreakStatement),
 
    Continue(ContinueStatement),
 
    Synchronous(SynchronousStatement),
 
    EndSynchronous(EndSynchronousStatement),
 
    Fork(ForkStatement),
 
    EndFork(EndForkStatement),
 
    Select(SelectStatement),
 
    EndSelect(EndSelectStatement),
 
    Return(ReturnStatement),
 
    Goto(GotoStatement),
 
    New(NewStatement),
 
    Expression(ExpressionStatement),
 
}
 

	
 
impl Statement {
 
    pub fn as_new(&self) -> &NewStatement {
 
        match self {
 
            Statement::New(result) => result,
 
            _ => panic!("Unable to cast `Statement` to `NewStatement`"),
 
        }
 
    }
 

	
 
    pub fn span(&self) -> InputSpan {
 
        match self {
 
            Statement::Block(v) => v.span,
 
            Statement::Local(v) => v.span(),
 
            Statement::Labeled(v) => v.label.span,
 
            Statement::If(v) => v.span,
 
            Statement::While(v) => v.span,
 
            Statement::Break(v) => v.span,
 
            Statement::Continue(v) => v.span,
 
            Statement::Synchronous(v) => v.span,
 
            Statement::Fork(v) => v.span,
 
            Statement::Select(v) => v.span,
 
            Statement::Return(v) => v.span,
 
            Statement::Goto(v) => v.span,
 
            Statement::New(v) => v.span,
 
            Statement::Expression(v) => v.span,
 
            Statement::EndBlock(_)
 
            | Statement::EndIf(_)
 
            | Statement::EndWhile(_)
 
            | Statement::EndSynchronous(_)
 
            | Statement::EndFork(_)
 
            | Statement::EndSelect(_) => unreachable!(),
 
        }
 
    }
 
    pub fn link_next(&mut self, next: StatementId) {
 
        match self {
 
            Statement::Block(stmt) => stmt.next = next,
 
            Statement::EndBlock(stmt) => stmt.next = next,
 
            Statement::Local(stmt) => match stmt {
 
                LocalStatement::Channel(stmt) => stmt.next = next,
 
                LocalStatement::Memory(stmt) => stmt.next = next,
 
            },
 
            Statement::EndIf(stmt) => stmt.next = next,
 
            Statement::EndWhile(stmt) => stmt.next = next,
 
            Statement::EndSynchronous(stmt) => stmt.next = next,
 
            Statement::EndFork(stmt) => stmt.next = next,
 
            Statement::EndSelect(stmt) => stmt.next = next,
 
            Statement::New(stmt) => stmt.next = next,
 
            Statement::Expression(stmt) => stmt.next = next,
 
            Statement::Return(_)
 
            | Statement::Break(_)
 
            | Statement::Continue(_)
 
            | Statement::Synchronous(_)
 
            | Statement::Fork(_)
 
            | Statement::Select(_)
 
            | Statement::Goto(_)
 
            | Statement::While(_)
 
            | Statement::Labeled(_)
 
            | Statement::If(_) => unreachable!(),
 
        }
 
    }
 

	
 
}
 

	
 
#[derive(Debug, Clone)]
 
pub struct BlockStatement {
 
    pub this: BlockStatementId,
 
    // Phase 1: parser
 
    pub span: InputSpan, // of the complete block
 
    pub statements: Vec<StatementId>,
 
    pub end_block: EndBlockStatementId,
 
    // Phase 2: linker
 
    pub scope: ScopeId,
 
    pub next: StatementId,
 
}
 

	
 
#[derive(Debug, Clone)]
 
pub struct EndBlockStatement {
 
    pub this: EndBlockStatementId,
 
    // Parser
 
    pub start_block: BlockStatementId,
 
    // Validation/Linking
 
    pub next: StatementId,
 
}
 

	
 
#[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 span(&self) -> InputSpan {
 
        match self {
 
            LocalStatement::Channel(v) => v.span,
 
            LocalStatement::Memory(v) => v.span,
 
        }
 
    }
 
}
 

	
 
#[derive(Debug, Clone)]
 
pub struct MemoryStatement {
 
    pub this: MemoryStatementId,
 
    // Phase 1: parser
 
    pub span: InputSpan,
 
    pub variable: VariableId,
 
    pub initial_expr: AssignmentExpressionId,
 
    // Phase 2: linker
 
    pub next: StatementId,
 
}
 

	
 
/// ChannelStatement is the declaration of an input and output port associated
 
/// with the same channel. Note that the polarity of the ports are from the
 
/// point of view of the component. So an output port is something that a
 
/// component uses to send data over (i.e. it is the "input end" of the
 
/// channel), and vice versa.
 
#[derive(Debug, Clone)]
 
pub struct ChannelStatement {
 
    pub this: ChannelStatementId,
 
    // Phase 1: parser
 
    pub span: InputSpan, // of the "channel" keyword
 
    pub from: VariableId, // output
 
    pub to: VariableId,   // input
 
    // Phase 2: linker
 
    pub relative_pos_in_parent: i32,
 
    pub next: StatementId,
 
}
 

	
 
#[derive(Debug, Clone)]
 
pub struct LabeledStatement {
 
    pub this: LabeledStatementId,
 
    // Phase 1: parser
 
    pub label: Identifier,
 
    pub body: StatementId,
 
    // Phase 2: linker
 
    pub relative_pos_in_parent: i32,
 
    pub in_sync: SynchronousStatementId, // may be invalid
 
}
 

	
 
#[derive(Debug, Clone)]
 
pub struct IfStatement {
 
    pub this: IfStatementId,
 
    // Phase 1: parser
 
    pub span: InputSpan, // of the "if" keyword
 
    pub test: ExpressionId,
 
    pub true_case: IfStatementCase,
 
    pub false_case: Option<IfStatementCase>,
 
    pub end_if: EndIfStatementId,
 
}
 

	
 
#[derive(Debug, Clone, Copy)]
 
pub struct IfStatementCase {
 
    pub body: StatementId,
 
    pub scope: ScopeId,
 
}
 
@@ -1624,336 +1650,341 @@ pub enum AssignmentOperator {
 
    ShiftedLeft,
 
    ShiftedRight,
 
    BitwiseAnded,
 
    BitwiseXored,
 
    BitwiseOred,
 
}
 

	
 
#[derive(Debug, Clone)]
 
pub struct AssignmentExpression {
 
    pub this: AssignmentExpressionId,
 
    // Parsing
 
    pub operator_span: InputSpan,
 
    pub full_span: InputSpan,
 
    pub left: ExpressionId,
 
    pub operation: AssignmentOperator,
 
    pub right: ExpressionId,
 
    // Validator/Linker
 
    pub parent: ExpressionParent,
 
    // Typing
 
    pub type_index: i32,
 
}
 

	
 
#[derive(Debug, Clone)]
 
pub struct BindingExpression {
 
    pub this: BindingExpressionId,
 
    // Parsing
 
    pub operator_span: InputSpan,
 
    pub full_span: InputSpan,
 
    pub bound_to: ExpressionId,
 
    pub bound_from: ExpressionId,
 
    // Validator/Linker
 
    pub parent: ExpressionParent,
 
    // Typing
 
    pub type_index: i32,
 
}
 

	
 
#[derive(Debug, Clone)]
 
pub struct ConditionalExpression {
 
    pub this: ConditionalExpressionId,
 
    // Parsing
 
    pub operator_span: InputSpan,
 
    pub full_span: InputSpan,
 
    pub test: ExpressionId,
 
    pub true_expression: ExpressionId,
 
    pub false_expression: ExpressionId,
 
    // Validator/Linking
 
    pub parent: ExpressionParent,
 
    // Typing
 
    pub type_index: i32,
 
}
 

	
 
#[derive(Debug, Clone, Copy, 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,
 
    // Parsing
 
    pub operator_span: InputSpan,
 
    pub full_span: InputSpan,
 
    pub left: ExpressionId,
 
    pub operation: BinaryOperator,
 
    pub right: ExpressionId,
 
    // Validator/Linker
 
    pub parent: ExpressionParent,
 
    // Typing
 
    pub type_index: i32,
 
}
 

	
 
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
 
pub enum UnaryOperator {
 
    Positive,
 
    Negative,
 
    BitwiseNot,
 
    LogicalNot,
 
}
 

	
 
#[derive(Debug, Clone)]
 
pub struct UnaryExpression {
 
    pub this: UnaryExpressionId,
 
    // Parsing
 
    pub operator_span: InputSpan,
 
    pub full_span: InputSpan,
 
    pub operation: UnaryOperator,
 
    pub expression: ExpressionId,
 
    // Validator/Linker
 
    pub parent: ExpressionParent,
 
    // Typing
 
    pub type_index: i32,
 
}
 

	
 
#[derive(Debug, Clone)]
 
pub struct IndexingExpression {
 
    pub this: IndexingExpressionId,
 
    // Parsing
 
    pub operator_span: InputSpan,
 
    pub full_span: InputSpan,
 
    pub subject: ExpressionId,
 
    pub index: ExpressionId,
 
    // Validator/Linker
 
    pub parent: ExpressionParent,
 
    // Typing
 
    pub type_index: i32,
 
}
 

	
 
#[derive(Debug, Clone)]
 
pub struct SlicingExpression {
 
    pub this: SlicingExpressionId,
 
    // Parsing
 
    pub slicing_span: InputSpan, // from '[' to ']'
 
    pub full_span: InputSpan, // includes subject
 
    pub subject: ExpressionId,
 
    pub from_index: ExpressionId,
 
    pub to_index: ExpressionId,
 
    // Validator/Linker
 
    pub parent: ExpressionParent,
 
    // Typing
 
    pub type_index: i32,
 
}
 

	
 
#[derive(Debug, Clone)]
 
pub enum SelectKind {
 
    StructField(Identifier),
 
    TupleMember(u64), // u64 is overkill, but space is taken up by `StructField` variant anyway
 
}
 

	
 
#[derive(Debug, Clone)]
 
pub struct SelectExpression {
 
    pub this: SelectExpressionId,
 
    // Parsing
 
    pub operator_span: InputSpan, // of the '.'
 
    pub full_span: InputSpan, // includes subject and field
 
    pub subject: ExpressionId,
 
    pub kind: SelectKind,
 
    // Validator/Linker
 
    pub parent: ExpressionParent,
 
    // Typing
 
    pub type_index: i32,
 
}
 

	
 
#[derive(Debug, Clone)]
 
pub struct CastExpression {
 
    pub this: CastExpressionId,
 
    // Parsing
 
    pub cast_span: InputSpan, // of the "cast" keyword,
 
    pub full_span: InputSpan, // includes the cast subject
 
    pub to_type: ParserType,
 
    pub subject: ExpressionId,
 
    // Validator/linker
 
    pub parent: ExpressionParent,
 
    // Typing
 
    pub type_index: i32,
 
}
 

	
 
#[derive(Debug, Clone)]
 
pub struct CallExpression {
 
    pub this: CallExpressionId,
 
    // Parsing
 
    pub func_span: InputSpan, // of the function name
 
    pub full_span: InputSpan, // includes the arguments and parentheses
 
    pub parser_type: ParserType, // of the function call, not the return type
 
    pub method: Method,
 
    pub arguments: Vec<ExpressionId>,
 
    pub procedure: ProcedureDefinitionId,
 
    // Validator/Linker
 
    pub parent: ExpressionParent,
 
    // Typing
 
    pub type_index: i32,
 
}
 

	
 
#[derive(Debug, Clone, PartialEq, Eq)]
 
pub enum Method {
 
    // Builtin, accessible by programmer
 
    // Builtin function, accessible by programmer
 
    Get,
 
    Put,
 
    Fires,
 
    Create,
 
    Length,
 
    Assert,
 
    Print,
 
    // Builtin, not accessible by programmer
 
    // Builtin function, not accessible by programmer
 
    SelectStart, // SelectStart(total_num_cases, total_num_ports)
 
    SelectRegisterCasePort, // SelectRegisterCasePort(case_index, port_index, port_id)
 
    SelectWait, // SelectWait() -> u32
 
    // Builtin component,
 
    ComponentRandomU32,
 
    ComponentTcpClient,
 
    // User-defined
 
    UserFunction,
 
    UserComponent,
 
}
 

	
 
impl Method {
 
    pub(crate) fn is_public_builtin(&self) -> bool {
 
        use Method::*;
 
        match self {
 
            Get | Put | Fires | Create | Length | Assert | Print => true,
 
            ComponentRandomU32 | ComponentTcpClient => true,
 
            _ => false,
 
        }
 
    }
 

	
 
    pub(crate) fn is_user_defined(&self) -> bool {
 
        use Method::*;
 
        match self {
 
            UserFunction | UserComponent => true,
 
            _ => false,
 
        }
 
    }
 
}
 

	
 
#[derive(Debug, Clone)]
 
pub struct LiteralExpression {
 
    pub this: LiteralExpressionId,
 
    // Parsing
 
    pub span: InputSpan,
 
    pub value: Literal,
 
    // Validator/Linker
 
    pub parent: ExpressionParent,
 
    // Typing
 
    pub type_index: i32,
 
}
 

	
 
#[derive(Debug, Clone)]
 
pub enum Literal {
 
    Null, // message
 
    True,
 
    False,
 
    Character(char),
 
    Bytestring(Vec<u8>),
 
    String(StringRef<'static>),
 
    Integer(LiteralInteger),
 
    Struct(LiteralStruct),
 
    Enum(LiteralEnum),
 
    Union(LiteralUnion),
 
    Array(Vec<ExpressionId>),
 
    Tuple(Vec<ExpressionId>),
 
}
 

	
 
impl Literal {
 
    pub(crate) fn as_struct(&self) -> &LiteralStruct {
 
        if let Literal::Struct(literal) = self{
 
            literal
 
        } else {
 
            unreachable!("Attempted to obtain {:?} as Literal::Struct", self)
 
        }
 
    }
 

	
 
    pub(crate) fn as_enum(&self) -> &LiteralEnum {
 
        if let Literal::Enum(literal) = self {
 
            literal
 
        } else {
 
            unreachable!("Attempted to obtain {:?} as Literal::Enum", self)
 
        }
 
    }
 

	
 
    pub(crate) fn as_union(&self) -> &LiteralUnion {
 
        if let Literal::Union(literal) = self {
 
            literal
 
        } else {
 
            unreachable!("Attempted to obtain {:?} as Literal::Union", self)
 
        }
 
    }
 
}
 

	
 
#[derive(Debug, Clone)]
 
pub struct LiteralInteger {
 
    pub(crate) unsigned_value: u64,
 
    pub(crate) negated: bool, // for constant expression evaluation, TODO: @Int
 
}
 

	
 
#[derive(Debug, Clone)]
 
pub struct LiteralStructField {
 
    // Phase 1: parser
 
    pub(crate) identifier: Identifier,
 
    pub(crate) value: ExpressionId,
 
    // Phase 2: linker
 
    pub(crate) field_idx: usize, // in struct definition
 
}
 

	
 
#[derive(Debug, Clone)]
 
pub struct LiteralStruct {
 
    // Phase 1: parser
 
    pub(crate) parser_type: ParserType,
 
    pub(crate) fields: Vec<LiteralStructField>,
 
    pub(crate) definition: DefinitionId,
 
}
 

	
 
#[derive(Debug, Clone)]
 
pub struct LiteralEnum {
 
    // Phase 1: parser
 
    pub(crate) parser_type: ParserType,
 
    pub(crate) variant: Identifier,
 
    pub(crate) definition: DefinitionId,
 
    // Phase 2: linker
 
    pub(crate) variant_idx: usize, // as present in the type table
 
}
 

	
 
#[derive(Debug, Clone)]
 
pub struct LiteralUnion {
 
    // Phase 1: parser
 
    pub(crate) parser_type: ParserType,
 
    pub(crate) variant: Identifier,
 
    pub(crate) values: Vec<ExpressionId>,
 
    pub(crate) definition: DefinitionId,
 
    // Phase 2: linker
 
    pub(crate) variant_idx: usize, // as present in type table
 
}
 

	
 
#[derive(Debug, Clone)]
 
pub struct VariableExpression {
 
    pub this: VariableExpressionId,
 
    // Parsing
 
    pub identifier: Identifier,
 
    // Validator/Linker
 
    pub declaration: Option<VariableId>,
 
    pub used_as_binding_target: bool,
 
    pub parent: ExpressionParent,
 
    // Typing
 
    pub type_index: i32,
 
}
 
\ No newline at end of file
src/protocol/ast_writer.rs
Show inline comments
 
file renamed from src/protocol/ast_printer.rs to src/protocol/ast_writer.rs
 
@@ -176,790 +176,799 @@ pub(crate) struct ASTWriter {
 
    buffer: String,
 
    temp1: String,
 
    temp2: String,
 
}
 

	
 
impl ASTWriter {
 
    pub(crate) fn new() -> Self {
 
        Self{
 
            cur_definition: None,
 
            buffer: String::with_capacity(4096),
 
            temp1: String::with_capacity(256),
 
            temp2: String::with_capacity(256),
 
        }
 
    }
 
    pub(crate) fn write_ast<W: IOWrite>(&mut self, w: &mut W, heap: &Heap) {
 
        for root_id in heap.protocol_descriptions.iter().map(|v| v.this) {
 
            self.write_module(heap, root_id);
 
            w.write_all(self.buffer.as_bytes()).expect("flush buffer");
 
            self.buffer.clear();
 
        }
 
    }
 

	
 
    //--------------------------------------------------------------------------
 
    // Top-level module writing
 
    //--------------------------------------------------------------------------
 

	
 
    fn write_module(&mut self, heap: &Heap, root_id: RootId) {
 
        self.kv(0).with_id(PREFIX_ROOT_ID, root_id.index)
 
            .with_s_key("Module");
 

	
 
        let root = &heap[root_id];
 
        self.kv(1).with_s_key("Pragmas");
 
        for pragma_id in &root.pragmas {
 
            self.write_pragma(heap, *pragma_id, 2);
 
        }
 

	
 
        self.kv(1).with_s_key("Imports");
 
        for import_id in &root.imports {
 
            self.write_import(heap, *import_id, 2);
 
        }
 

	
 
        self.kv(1).with_s_key("Definitions");
 
        for def_id in &root.definitions {
 
            self.write_definition(heap, *def_id, 2);
 
        }
 
    }
 

	
 
    fn write_pragma(&mut self, heap: &Heap, pragma_id: PragmaId, indent: usize) {
 
        match &heap[pragma_id] {
 
            Pragma::Version(pragma) => {
 
                self.kv(indent).with_id(PREFIX_PRAGMA_ID, pragma.this.index)
 
                    .with_s_key("PragmaVersion")
 
                    .with_disp_val(&pragma.version);
 
            },
 
            Pragma::Module(pragma) => {
 
                self.kv(indent).with_id(PREFIX_PRAGMA_ID, pragma.this.index)
 
                    .with_s_key("PragmaModule")
 
                    .with_identifier_val(&pragma.value);
 
            }
 
        }
 
    }
 

	
 
    fn write_import(&mut self, heap: &Heap, import_id: ImportId, indent: usize) {
 
        let import = &heap[import_id];
 
        let indent2 = indent + 1;
 

	
 
        match import {
 
            Import::Module(import) => {
 
                self.kv(indent).with_id(PREFIX_IMPORT_ID, import.this.index)
 
                    .with_s_key("ImportModule");
 

	
 
                self.kv(indent2).with_s_key("Name").with_identifier_val(&import.module);
 
                self.kv(indent2).with_s_key("Alias").with_identifier_val(&import.alias);
 
                self.kv(indent2).with_s_key("Target").with_disp_val(&import.module_id.index);
 
            },
 
            Import::Symbols(import) => {
 
                self.kv(indent).with_id(PREFIX_IMPORT_ID, import.this.index)
 
                    .with_s_key("ImportSymbol");
 

	
 
                self.kv(indent2).with_s_key("Name").with_identifier_val(&import.module);
 
                self.kv(indent2).with_s_key("Target").with_disp_val(&import.module_id.index);
 

	
 
                self.kv(indent2).with_s_key("Symbols");
 

	
 
                let indent3 = indent2 + 1;
 
                let indent4 = indent3 + 1;
 
                for symbol in &import.symbols {
 
                    self.kv(indent3).with_s_key("AliasedSymbol");
 
                    self.kv(indent4).with_s_key("Name").with_identifier_val(&symbol.name);
 
                    self.kv(indent4).with_s_key("Alias").with_opt_identifier_val(symbol.alias.as_ref());
 
                    self.kv(indent4).with_s_key("Definition").with_disp_val(&symbol.definition_id.index);
 
                }
 
            }
 
        }
 
    }
 

	
 
    //--------------------------------------------------------------------------
 
    // Top-level definition writing
 
    //--------------------------------------------------------------------------
 

	
 
    fn write_definition(&mut self, heap: &Heap, def_id: DefinitionId, indent: usize) {
 
        self.cur_definition = Some(def_id);
 
        let indent2 = indent + 1;
 
        let indent3 = indent2 + 1;
 
        let indent4 = indent3 + 1;
 

	
 
        match &heap[def_id] {
 
            Definition::Struct(def) => {
 
                self.kv(indent).with_id(PREFIX_STRUCT_ID, def.this.0.index)
 
                    .with_s_key("DefinitionStruct");
 

	
 
                self.kv(indent2).with_s_key("Name").with_identifier_val(&def.identifier);
 
                for poly_var_id in &def.poly_vars {
 
                    self.kv(indent3).with_s_key("PolyVar").with_identifier_val(&poly_var_id);
 
                }
 

	
 
                self.kv(indent2).with_s_key("Fields");
 
                for field in &def.fields {
 
                    self.kv(indent3).with_s_key("Field");
 
                    self.kv(indent4).with_s_key("Name")
 
                        .with_identifier_val(&field.field);
 
                    self.kv(indent4).with_s_key("Type")
 
                        .with_custom_val(|s| write_parser_type(s, heap, &field.parser_type));
 
                }
 
            },
 
            Definition::Enum(def) => {
 
                self.kv(indent).with_id(PREFIX_ENUM_ID, def.this.0.index)
 
                    .with_s_key("DefinitionEnum");
 

	
 
                self.kv(indent2).with_s_key("Name").with_identifier_val(&def.identifier);
 
                for poly_var_id in &def.poly_vars {
 
                    self.kv(indent3).with_s_key("PolyVar").with_identifier_val(&poly_var_id);
 
                }
 

	
 
                self.kv(indent2).with_s_key("Variants");
 
                for variant in &def.variants {
 
                    self.kv(indent3).with_s_key("Variant");
 
                    self.kv(indent4).with_s_key("Name")
 
                        .with_identifier_val(&variant.identifier);
 
                    let variant_value = self.kv(indent4).with_s_key("Value");
 
                    match &variant.value {
 
                        EnumVariantValue::None => variant_value.with_s_val("None"),
 
                        EnumVariantValue::Integer(value) => variant_value.with_disp_val(value),
 
                    };
 
                }
 
            },
 
            Definition::Union(def) => {
 
                self.kv(indent).with_id(PREFIX_UNION_ID, def.this.0.index)
 
                    .with_s_key("DefinitionUnion");
 

	
 
                self.kv(indent2).with_s_key("Name").with_identifier_val(&def.identifier);
 
                for poly_var_id in &def.poly_vars {
 
                    self.kv(indent3).with_s_key("PolyVar").with_identifier_val(&poly_var_id);
 
                }
 

	
 
                self.kv(indent2).with_s_key("Variants");
 
                for variant in &def.variants {
 
                    self.kv(indent3).with_s_key("Variant");
 
                    self.kv(indent4).with_s_key("Name")
 
                        .with_identifier_val(&variant.identifier);
 
                        
 
                    if variant.value.is_empty() {
 
                        self.kv(indent4).with_s_key("Value").with_s_val("None");
 
                    } else {
 
                        self.kv(indent4).with_s_key("Values");
 
                        for embedded in &variant.value {
 
                            self.kv(indent4+1).with_s_key("Value")
 
                                .with_custom_val(|v| write_parser_type(v, heap, embedded));
 
                        }
 
                    }
 
                }
 
            }
 
            Definition::Procedure(def) => {
 
                self.kv(indent).with_id(PREFIX_FUNCTION_ID, def.this.0.index)
 
                    .with_s_key("DefinitionFunction");
 

	
 
                self.kv(indent2).with_s_key("Name").with_identifier_val(&def.identifier);
 
                for poly_var_id in &def.poly_vars {
 
                    self.kv(indent3).with_s_key("PolyVar").with_identifier_val(&poly_var_id);
 
                }
 

	
 
                self.kv(indent2).with_s_key("Kind").with_debug_val(&def.kind);
 
                if let Some(parser_type) = &def.return_type {
 
                    self.kv(indent2).with_s_key("ReturnParserType")
 
                        .with_custom_val(|s| write_parser_type(s, heap, parser_type));
 
                }
 

	
 
                self.kv(indent2).with_s_key("Parameters");
 
                for variable_id in &def.parameters {
 
                    self.write_variable(heap, *variable_id, indent3);
 
                }
 

	
 
                if def.source.is_builtin() {
 
                    self.kv(indent2).with_s_key("Body").with_s_val("Builtin");
 
                } else {
 
                    self.kv(indent2).with_s_key("Body");
 
                    self.write_stmt(heap, def.body.upcast(), indent3);
 
                }
 
            },
 
        }
 
    }
 

	
 
    fn write_stmt(&mut self, heap: &Heap, stmt_id: StatementId, indent: usize) {
 
        let stmt = &heap[stmt_id];
 
        let indent2 = indent + 1;
 
        let indent3 = indent2 + 1;
 

	
 
        match stmt {
 
            Statement::Block(stmt) => {
 
                self.kv(indent).with_id(PREFIX_BLOCK_STMT_ID, stmt.this.0.index)
 
                    .with_s_key("Block");
 
                self.kv(indent2).with_s_key("EndBlockID").with_disp_val(&stmt.end_block.0.index);
 
                self.kv(indent2).with_s_key("ScopeID").with_disp_val(&stmt.scope.index);
 

	
 
                self.kv(indent2).with_s_key("Statements");
 
                for stmt_id in &stmt.statements {
 
                    self.write_stmt(heap, *stmt_id, indent3);
 
                }
 
            },
 
            Statement::EndBlock(stmt) => {
 
                self.kv(indent).with_id(PREFIX_ENDBLOCK_STMT_ID, stmt.this.0.index)
 
                    .with_s_key("EndBlock");
 
                self.kv(indent2).with_s_key("StartBlockID").with_disp_val(&stmt.start_block.0.index);
 
            }
 
            Statement::Local(stmt) => {
 
                match stmt {
 
                    LocalStatement::Channel(stmt) => {
 
                        self.kv(indent).with_id(PREFIX_CHANNEL_STMT_ID, stmt.this.0.0.index)
 
                            .with_s_key("LocalChannel");
 

	
 
                        self.kv(indent2).with_s_key("From");
 
                        self.write_variable(heap, stmt.from, indent3);
 
                        self.kv(indent2).with_s_key("To");
 
                        self.write_variable(heap, stmt.to, indent3);
 
                        self.kv(indent2).with_s_key("Next").with_disp_val(&stmt.next.index);
 
                    },
 
                    LocalStatement::Memory(stmt) => {
 
                        self.kv(indent).with_id(PREFIX_MEM_STMT_ID, stmt.this.0.0.index)
 
                            .with_s_key("LocalMemory");
 

	
 
                        self.kv(indent2).with_s_key("Variable");
 
                        self.write_variable(heap, stmt.variable, indent3);
 
                        self.kv(indent2).with_s_key("InitialValue");
 
                        self.write_expr(heap, stmt.initial_expr.upcast(), indent3);
 
                        self.kv(indent2).with_s_key("Next").with_disp_val(&stmt.next.index);
 
                    }
 
                }
 
            },
 
            Statement::Labeled(stmt) => {
 
                self.kv(indent).with_id(PREFIX_LABELED_STMT_ID, stmt.this.0.index)
 
                    .with_s_key("Labeled");
 

	
 
                self.kv(indent2).with_s_key("Label").with_identifier_val(&stmt.label);
 
                self.kv(indent2).with_s_key("Statement");
 
                self.write_stmt(heap, stmt.body, indent3);
 
            },
 
            Statement::If(stmt) => {
 
                self.kv(indent).with_id(PREFIX_IF_STMT_ID, stmt.this.0.index)
 
                    .with_s_key("If");
 

	
 
                self.kv(indent2).with_s_key("EndIf").with_disp_val(&stmt.end_if.0.index);
 

	
 
                self.kv(indent2).with_s_key("Condition");
 
                self.write_expr(heap, stmt.test, indent3);
 

	
 
                self.kv(indent2).with_s_key("TrueBody");
 
                self.write_stmt(heap, stmt.true_case.body, indent3);
 

	
 
                if let Some(false_body) = stmt.false_case {
 
                    self.kv(indent2).with_s_key("FalseBody");
 
                    self.write_stmt(heap, false_body.body, indent3);
 
                }
 
            },
 
            Statement::EndIf(stmt) => {
 
                self.kv(indent).with_id(PREFIX_ENDIF_STMT_ID, stmt.this.0.index)
 
                    .with_s_key("EndIf");
 
                self.kv(indent2).with_s_key("StartIf").with_disp_val(&stmt.start_if.0.index);
 
                self.kv(indent2).with_s_key("Next").with_disp_val(&stmt.next.index);
 
            },
 
            Statement::While(stmt) => {
 
                self.kv(indent).with_id(PREFIX_WHILE_STMT_ID, stmt.this.0.index)
 
                    .with_s_key("While");
 

	
 
                self.kv(indent2).with_s_key("EndWhile").with_disp_val(&stmt.end_while.0.index);
 
                self.kv(indent2).with_s_key("InSync")
 
                    .with_disp_val(&stmt.in_sync.0.index);
 
                self.kv(indent2).with_s_key("Condition");
 
                self.write_expr(heap, stmt.test, indent3);
 
                self.kv(indent2).with_s_key("Body");
 
                self.write_stmt(heap, stmt.body, indent3);
 
            },
 
            Statement::EndWhile(stmt) => {
 
                self.kv(indent).with_id(PREFIX_ENDWHILE_STMT_ID, stmt.this.0.index)
 
                    .with_s_key("EndWhile");
 
                self.kv(indent2).with_s_key("StartWhile").with_disp_val(&stmt.start_while.0.index);
 
                self.kv(indent2).with_s_key("Next").with_disp_val(&stmt.next.index);
 
            },
 
            Statement::Break(stmt) => {
 
                self.kv(indent).with_id(PREFIX_BREAK_STMT_ID, stmt.this.0.index)
 
                    .with_s_key("Break");
 
                self.kv(indent2).with_s_key("Label")
 
                    .with_opt_identifier_val(stmt.label.as_ref());
 
                self.kv(indent2).with_s_key("Target")
 
                    .with_disp_val(&stmt.target.0.index);
 
            },
 
            Statement::Continue(stmt) => {
 
                self.kv(indent).with_id(PREFIX_CONTINUE_STMT_ID, stmt.this.0.index)
 
                    .with_s_key("Continue");
 
                self.kv(indent2).with_s_key("Label")
 
                    .with_opt_identifier_val(stmt.label.as_ref());
 
                self.kv(indent2).with_s_key("Target")
 
                    .with_disp_val(&stmt.target.0.index);
 
            },
 
            Statement::Synchronous(stmt) => {
 
                self.kv(indent).with_id(PREFIX_SYNC_STMT_ID, stmt.this.0.index)
 
                    .with_s_key("Synchronous");
 
                self.kv(indent2).with_s_key("EndSync").with_disp_val(&stmt.end_sync.0.index);
 
                self.kv(indent2).with_s_key("Body");
 
                self.write_stmt(heap, stmt.body, indent3);
 
            },
 
            Statement::EndSynchronous(stmt) => {
 
                self.kv(indent).with_id(PREFIX_ENDSYNC_STMT_ID, stmt.this.0.index)
 
                    .with_s_key("EndSynchronous");
 
                self.kv(indent2).with_s_key("StartSync").with_disp_val(&stmt.start_sync.0.index);
 
                self.kv(indent2).with_s_key("Next").with_disp_val(&stmt.next.index);
 
            },
 
            Statement::Fork(stmt) => {
 
                self.kv(indent).with_id(PREFIX_FORK_STMT_ID, stmt.this.0.index)
 
                    .with_s_key("Fork");
 
                self.kv(indent2).with_s_key("EndFork").with_disp_val(&stmt.end_fork.0.index);
 
                self.kv(indent2).with_s_key("LeftBody");
 
                self.write_stmt(heap, stmt.left_body, indent3);
 

	
 
                if let Some(right_body_id) = stmt.right_body {
 
                    self.kv(indent2).with_s_key("RightBody");
 
                    self.write_stmt(heap, right_body_id, indent3);
 
                }
 
            },
 
            Statement::EndFork(stmt) => {
 
                self.kv(indent).with_id(PREFIX_END_FORK_STMT_ID, stmt.this.0.index)
 
                    .with_s_key("EndFork");
 
                self.kv(indent2).with_s_key("StartFork").with_disp_val(&stmt.start_fork.0.index);
 
                self.kv(indent2).with_s_key("Next").with_disp_val(&stmt.next.index);
 
            },
 
            Statement::Select(stmt) => {
 
                self.kv(indent).with_id(PREFIX_SELECT_STMT_ID, stmt.this.0.index)
 
                    .with_s_key("Select");
 
                self.kv(indent2).with_s_key("EndSelect").with_disp_val(&stmt.end_select.0.index);
 
                self.kv(indent2).with_s_key("Cases");
 
                let indent3 = indent2 + 1;
 
                let indent4 = indent3 + 1;
 
                for case in &stmt.cases {
 
                    self.kv(indent3).with_s_key("Guard");
 
                    self.write_stmt(heap, case.guard, indent4);
 

	
 
                    self.kv(indent3).with_s_key("Block");
 
                    self.write_stmt(heap, case.body, indent4);
 
                }
 
                self.kv(indent2).with_s_key("Replacement");
 
                self.write_stmt(heap, stmt.next, indent3);
 
            },
 
            Statement::EndSelect(stmt) => {
 
                self.kv(indent).with_id(PREFIX_END_SELECT_STMT_ID, stmt.this.0.index)
 
                    .with_s_key("EndSelect");
 
                self.kv(indent2).with_s_key("StartSelect").with_disp_val(&stmt.start_select.0.index);
 
                self.kv(indent2).with_s_key("Next").with_disp_val(&stmt.next.index);
 
            }
 
            Statement::Return(stmt) => {
 
                self.kv(indent).with_id(PREFIX_RETURN_STMT_ID, stmt.this.0.index)
 
                    .with_s_key("Return");
 
                self.kv(indent2).with_s_key("Expressions");
 
                for expr_id in &stmt.expressions {
 
                    self.write_expr(heap, *expr_id, indent3);
 
                }
 
            },
 
            Statement::Goto(stmt) => {
 
                self.kv(indent).with_id(PREFIX_GOTO_STMT_ID, stmt.this.0.index)
 
                    .with_s_key("Goto");
 
                self.kv(indent2).with_s_key("Label").with_identifier_val(&stmt.label);
 
                self.kv(indent2).with_s_key("Target")
 
                    .with_disp_val(&stmt.target.0.index);
 
            },
 
            Statement::New(stmt) => {
 
                self.kv(indent).with_id(PREFIX_NEW_STMT_ID, stmt.this.0.index)
 
                    .with_s_key("New");
 
                self.kv(indent2).with_s_key("Expression");
 
                self.write_expr(heap, stmt.expression.upcast(), indent3);
 
                self.kv(indent2).with_s_key("Next").with_disp_val(&stmt.next.index);
 
            },
 
            Statement::Expression(stmt) => {
 
                self.kv(indent).with_id(PREFIX_EXPR_STMT_ID, stmt.this.0.index)
 
                    .with_s_key("ExpressionStatement");
 
                self.write_expr(heap, stmt.expression, indent2);
 
                self.kv(indent2).with_s_key("Next").with_disp_val(&stmt.next.index);
 
            }
 
        }
 
    }
 

	
 
    fn write_expr(&mut self, heap: &Heap, expr_id: ExpressionId, indent: usize) {
 
        let expr = &heap[expr_id];
 
        let indent2 = indent + 1;
 
        let indent3 = indent2 + 1;
 

	
 
        match expr {
 
            Expression::Assignment(expr) => {
 
                self.kv(indent).with_id(PREFIX_ASSIGNMENT_EXPR_ID, expr.this.0.index)
 
                    .with_s_key("AssignmentExpr");
 
                self.kv(indent2).with_s_key("TypeIndex").with_disp_val(&expr.type_index);
 
                self.kv(indent2).with_s_key("Operation").with_debug_val(&expr.operation);
 
                self.kv(indent2).with_s_key("Left");
 
                self.write_expr(heap, expr.left, indent3);
 
                self.kv(indent2).with_s_key("Right");
 
                self.write_expr(heap, expr.right, indent3);
 
                self.kv(indent2).with_s_key("Parent")
 
                    .with_custom_val(|v| write_expression_parent(v, &expr.parent));
 
            },
 
            Expression::Binding(expr) => {
 
                self.kv(indent).with_id(PREFIX_BINARY_EXPR_ID, expr.this.0.index)
 
                    .with_s_key("BindingExpr");
 
                self.kv(indent2).with_s_key("TypeIndex").with_disp_val(&expr.type_index);
 
                self.kv(indent2).with_s_key("BindToExpression");
 
                self.write_expr(heap, expr.bound_to, indent3);
 
                self.kv(indent2).with_s_key("BindFromExpression");
 
                self.write_expr(heap, expr.bound_from, indent3);
 
                self.kv(indent2).with_s_key("Parent")
 
                    .with_custom_val(|v| write_expression_parent(v, &expr.parent));
 
            },
 
            Expression::Conditional(expr) => {
 
                self.kv(indent).with_id(PREFIX_CONDITIONAL_EXPR_ID, expr.this.0.index)
 
                    .with_s_key("ConditionalExpr");
 
                self.kv(indent2).with_s_key("TypeIndex").with_disp_val(&expr.type_index);
 
                self.kv(indent2).with_s_key("Condition");
 
                self.write_expr(heap, expr.test, indent3);
 
                self.kv(indent2).with_s_key("TrueExpression");
 
                self.write_expr(heap, expr.true_expression, indent3);
 
                self.kv(indent2).with_s_key("FalseExpression");
 
                self.write_expr(heap, expr.false_expression, indent3);
 
                self.kv(indent2).with_s_key("Parent")
 
                    .with_custom_val(|v| write_expression_parent(v, &expr.parent));
 
            },
 
            Expression::Binary(expr) => {
 
                self.kv(indent).with_id(PREFIX_BINARY_EXPR_ID, expr.this.0.index)
 
                    .with_s_key("BinaryExpr");
 
                self.kv(indent2).with_s_key("TypeIndex").with_disp_val(&expr.type_index);
 
                self.kv(indent2).with_s_key("Operation").with_debug_val(&expr.operation);
 
                self.kv(indent2).with_s_key("Left");
 
                self.write_expr(heap, expr.left, indent3);
 
                self.kv(indent2).with_s_key("Right");
 
                self.write_expr(heap, expr.right, indent3);
 
                self.kv(indent2).with_s_key("Parent")
 
                    .with_custom_val(|v| write_expression_parent(v, &expr.parent));
 
            },
 
            Expression::Unary(expr) => {
 
                self.kv(indent).with_id(PREFIX_UNARY_EXPR_ID, expr.this.0.index)
 
                    .with_s_key("UnaryExpr");
 
                self.kv(indent2).with_s_key("TypeIndex").with_disp_val(&expr.type_index);
 
                self.kv(indent2).with_s_key("Operation").with_debug_val(&expr.operation);
 
                self.kv(indent2).with_s_key("Argument");
 
                self.write_expr(heap, expr.expression, indent3);
 
                self.kv(indent2).with_s_key("Parent")
 
                    .with_custom_val(|v| write_expression_parent(v, &expr.parent));
 
            },
 
            Expression::Indexing(expr) => {
 
                self.kv(indent).with_id(PREFIX_INDEXING_EXPR_ID, expr.this.0.index)
 
                    .with_s_key("IndexingExpr");
 
                self.kv(indent2).with_s_key("TypeIndex").with_disp_val(&expr.type_index);
 
                self.kv(indent2).with_s_key("Subject");
 
                self.write_expr(heap, expr.subject, indent3);
 
                self.kv(indent2).with_s_key("Index");
 
                self.write_expr(heap, expr.index, indent3);
 
                self.kv(indent2).with_s_key("Parent")
 
                    .with_custom_val(|v| write_expression_parent(v, &expr.parent));
 
            },
 
            Expression::Slicing(expr) => {
 
                self.kv(indent).with_id(PREFIX_SLICING_EXPR_ID, expr.this.0.index)
 
                    .with_s_key("SlicingExpr");
 
                self.kv(indent2).with_s_key("TypeIndex").with_disp_val(&expr.type_index);
 
                self.kv(indent2).with_s_key("Subject");
 
                self.write_expr(heap, expr.subject, indent3);
 
                self.kv(indent2).with_s_key("FromIndex");
 
                self.write_expr(heap, expr.from_index, indent3);
 
                self.kv(indent2).with_s_key("ToIndex");
 
                self.write_expr(heap, expr.to_index, indent3);
 
                self.kv(indent2).with_s_key("Parent")
 
                    .with_custom_val(|v| write_expression_parent(v, &expr.parent));
 
            },
 
            Expression::Select(expr) => {
 
                self.kv(indent).with_id(PREFIX_SELECT_EXPR_ID, expr.this.0.index)
 
                    .with_s_key("SelectExpr");
 
                self.kv(indent2).with_s_key("TypeIndex").with_disp_val(&expr.type_index);
 
                self.kv(indent2).with_s_key("Subject");
 
                self.write_expr(heap, expr.subject, indent3);
 

	
 
                match &expr.kind {
 
                    SelectKind::StructField(field_name) => {
 
                        self.kv(indent2).with_s_key("StructField").with_identifier_val(field_name);
 
                    },
 
                    SelectKind::TupleMember(member_index) => {
 
                        self.kv(indent2).with_s_key("TupleMember").with_disp_val(member_index);
 
                    },
 
                }
 

	
 
                self.kv(indent2).with_s_key("Parent")
 
                    .with_custom_val(|v| write_expression_parent(v, &expr.parent));
 
            },
 
            Expression::Literal(expr) => {
 
                self.kv(indent).with_id(PREFIX_LITERAL_EXPR_ID, expr.this.0.index)
 
                    .with_s_key("LiteralExpr");
 

	
 
                self.kv(indent2).with_s_key("TypeIndex").with_disp_val(&expr.type_index);
 
                let val = self.kv(indent2).with_s_key("Value");
 
                match &expr.value {
 
                    Literal::Null => { val.with_s_val("null"); },
 
                    Literal::True => { val.with_s_val("true"); },
 
                    Literal::False => { val.with_s_val("false"); },
 
                    Literal::Character(data) => { val.with_disp_val(data); },
 
                    Literal::Bytestring(bytes) => {
 
                        // Bytestrings are ASCII, so just convert back
 
                        let string = String::from_utf8_lossy(bytes.as_slice());
 
                        val.with_disp_val(&string);
 
                    },
 
                    Literal::String(data) => {
 
                        // Stupid hack
 
                        let string = String::from(data.as_str());
 
                        val.with_disp_val(&string);
 
                    },
 
                    Literal::Integer(data) => { val.with_debug_val(data); },
 
                    Literal::Struct(data) => {
 
                        val.with_s_val("Struct");
 
                        let indent4 = indent3 + 1;
 

	
 
                        self.kv(indent3).with_s_key("ParserType")
 
                            .with_custom_val(|t| write_parser_type(t, heap, &data.parser_type));
 
                        self.kv(indent3).with_s_key("Definition").with_disp_val(&data.definition.index);
 

	
 
                        for field in &data.fields {
 
                            self.kv(indent3).with_s_key("Field");
 
                            self.kv(indent4).with_s_key("Name").with_identifier_val(&field.identifier);
 
                            self.kv(indent4).with_s_key("Index").with_disp_val(&field.field_idx);
 
                            self.kv(indent4).with_s_key("ParserType");
 
                            self.write_expr(heap, field.value, indent4 + 1);
 
                        }
 
                    },
 
                    Literal::Enum(data) => {
 
                        val.with_s_val("Enum");
 

	
 
                        self.kv(indent3).with_s_key("ParserType")
 
                            .with_custom_val(|t| write_parser_type(t, heap, &data.parser_type));
 
                        self.kv(indent3).with_s_key("Definition").with_disp_val(&data.definition.index);
 
                        self.kv(indent3).with_s_key("VariantIdx").with_disp_val(&data.variant_idx);
 
                    },
 
                    Literal::Union(data) => {
 
                        val.with_s_val("Union");
 
                        let indent4 = indent3 + 1;
 

	
 
                        self.kv(indent3).with_s_key("ParserType")
 
                            .with_custom_val(|t| write_parser_type(t, heap, &data.parser_type));
 
                        self.kv(indent3).with_s_key("Definition").with_disp_val(&data.definition.index);
 
                        self.kv(indent3).with_s_key("VariantIdx").with_disp_val(&data.variant_idx);
 

	
 
                        for value in &data.values {
 
                            self.kv(indent3).with_s_key("Value");
 
                            self.write_expr(heap, *value, indent4);
 
                        }
 
                    },
 
                    Literal::Array(data) => {
 
                        val.with_s_val("Array");
 
                        let indent4 = indent3 + 1;
 

	
 
                        self.kv(indent3).with_s_key("Elements");
 
                        for expr_id in data {
 
                            self.write_expr(heap, *expr_id, indent4);
 
                        }
 
                    },
 
                    Literal::Tuple(data) => {
 
                        val.with_s_val("Tuple");
 
                        let indent4 = indent3 + 1;
 
                        self.kv(indent3).with_s_key("Elements");
 
                        for expr_id in data {
 
                            self.write_expr(heap, *expr_id, indent4);
 
                        }
 
                    }
 
                }
 

	
 
                self.kv(indent2).with_s_key("Parent")
 
                    .with_custom_val(|v| write_expression_parent(v, &expr.parent));
 
            },
 
            Expression::Cast(expr) => {
 
                self.kv(indent).with_id(PREFIX_CAST_EXPR_ID, expr.this.0.index)
 
                    .with_s_key("CallExpr");
 
                self.kv(indent2).with_s_key("TypeIndex").with_disp_val(&expr.type_index);
 
                self.kv(indent2).with_s_key("ToType")
 
                    .with_custom_val(|t| write_parser_type(t, heap, &expr.to_type));
 
                self.kv(indent2).with_s_key("Subject");
 
                self.write_expr(heap, expr.subject, indent3);
 
                self.kv(indent2).with_s_key("Parent")
 
                    .with_custom_val(|v| write_expression_parent(v, &expr.parent));
 
            }
 
            Expression::Call(expr) => {
 
                self.kv(indent).with_id(PREFIX_CALL_EXPR_ID, expr.this.0.index)
 
                    .with_s_key("CallExpr");
 

	
 
                self.kv(indent2).with_s_key("TypeIndex").with_disp_val(&expr.type_index);
 
                self.kv(indent2).with_s_key("Method").with_debug_val(&expr.method);
 
                if !expr.procedure.is_invalid() {
 
                    let definition = &heap[expr.procedure];
 
                    self.kv(indent2).with_s_key("BuiltIn").with_disp_val(&definition.builtin);
 
                    self.kv(indent2).with_s_key("Source").with_debug_val(&definition.source);
 
                    self.kv(indent2).with_s_key("Variant").with_debug_val(&definition.kind);
 
                    self.kv(indent2).with_s_key("MethodName").with_identifier_val(&definition.identifier);
 
                    self.kv(indent2).with_s_key("ParserType")
 
                        .with_custom_val(|t| write_parser_type(t, heap, &expr.parser_type));
 
                }
 

	
 
                // Arguments
 
                self.kv(indent2).with_s_key("Arguments");
 
                for arg_id in &expr.arguments {
 
                    self.write_expr(heap, *arg_id, indent3);
 
                }
 

	
 
                // Parent
 
                self.kv(indent2).with_s_key("Parent")
 
                    .with_custom_val(|v| write_expression_parent(v, &expr.parent));
 
            },
 
            Expression::Variable(expr) => {
 
                self.kv(indent).with_id(PREFIX_VARIABLE_EXPR_ID, expr.this.0.index)
 
                    .with_s_key("VariableExpr");
 
                self.kv(indent2).with_s_key("TypeIndex").with_disp_val(&expr.type_index);
 
                self.kv(indent2).with_s_key("Name").with_identifier_val(&expr.identifier);
 
                self.kv(indent2).with_s_key("Definition")
 
                    .with_opt_disp_val(expr.declaration.as_ref().map(|v| &v.index));
 
                self.kv(indent2).with_s_key("Parent")
 
                    .with_custom_val(|v| write_expression_parent(v, &expr.parent));
 
            }
 
        }
 
    }
 

	
 
    fn write_variable(&mut self, heap: &Heap, variable_id: VariableId, indent: usize) {
 
        let var = &heap[variable_id];
 
        let indent2 = indent + 1;
 

	
 
        self.kv(indent).with_id(PREFIX_VARIABLE_ID, variable_id.index)
 
            .with_s_key("Variable");
 

	
 
        self.kv(indent2).with_s_key("Name").with_identifier_val(&var.identifier);
 
        self.kv(indent2).with_s_key("Kind").with_debug_val(&var.kind);
 
        self.kv(indent2).with_s_key("ParserType")
 
            .with_custom_val(|w| write_parser_type(w, heap, &var.parser_type));
 
        self.kv(indent2).with_s_key("RelativePos").with_disp_val(&var.relative_pos_in_parent);
 
        self.kv(indent2).with_s_key("UniqueScopeID").with_disp_val(&var.unique_id_in_scope);
 
    }
 

	
 
    //--------------------------------------------------------------------------
 
    // Printing Utilities
 
    //--------------------------------------------------------------------------
 

	
 
    fn kv(&mut self, indent: usize) -> KV {
 
        KV::new(&mut self.buffer, &mut self.temp1, &mut self.temp2, indent)
 
    }
 

	
 
    fn flush<W: IOWrite>(&mut self, w: &mut W) {
 
        w.write(self.buffer.as_bytes()).unwrap();
 
        self.buffer.clear()
 
    }
 
}
 

	
 
fn write_option<V: Display>(target: &mut String, value: Option<V>) {
 
    target.clear();
 
    match &value {
 
        Some(v) => target.push_str(&format!("Some({})", v)),
 
        None => target.push_str("None")
 
    };
 
}
 

	
 
fn write_parser_type(target: &mut String, heap: &Heap, t: &ParserType) {
 
    use ParserTypeVariant as PTV;
 

	
 
    if t.elements.is_empty() {
 
        target.push_str("no elements in ParserType (can happen due to compiler-inserted AST nodes)");
 
        return;
 
    }
 

	
 
    fn write_element(target: &mut String, heap: &Heap, t: &ParserType, mut element_idx: usize) -> usize {
 
        let element = &t.elements[element_idx];
 
        match &element.variant {
 
            PTV::Void => target.push_str("void"),
 
            PTV::InputOrOutput => {
 
                target.push_str("portlike<");
 
                element_idx = write_element(target, heap, t, element_idx + 1);
 
                target.push('>');
 
            },
 
            PTV::ArrayLike => {
 
                element_idx = write_element(target, heap, t, element_idx + 1);
 
                target.push_str("[???]");
 
            },
 
            PTV::IntegerLike => target.push_str("integerlike"),
 
            PTV::Message => { target.push_str(KW_TYPE_MESSAGE_STR); },
 
            PTV::Bool => { target.push_str(KW_TYPE_BOOL_STR); },
 
            PTV::UInt8 => { target.push_str(KW_TYPE_UINT8_STR); },
 
            PTV::UInt16 => { target.push_str(KW_TYPE_UINT16_STR); },
 
            PTV::UInt32 => { target.push_str(KW_TYPE_UINT32_STR); },
 
            PTV::UInt64 => { target.push_str(KW_TYPE_UINT64_STR); },
 
            PTV::SInt8 => { target.push_str(KW_TYPE_SINT8_STR); },
 
            PTV::SInt16 => { target.push_str(KW_TYPE_SINT16_STR); },
 
            PTV::SInt32 => { target.push_str(KW_TYPE_SINT32_STR); },
 
            PTV::SInt64 => { target.push_str(KW_TYPE_SINT64_STR); },
 
            PTV::Character => { target.push_str(KW_TYPE_CHAR_STR); },
 
            PTV::String => { target.push_str(KW_TYPE_STRING_STR); },
 
            PTV::IntegerLiteral => { target.push_str("int_literal"); },
 
            PTV::Inferred => { target.push_str(KW_TYPE_INFERRED_STR); },
 
            PTV::Array => {
 
                element_idx = write_element(target, heap, t, element_idx + 1);
 
                target.push_str("[]");
 
            },
 
            PTV::Input => {
 
                target.push_str(KW_TYPE_IN_PORT_STR);
 
                target.push('<');
 
                element_idx = write_element(target, heap, t, element_idx + 1);
 
                target.push('>');
 
            },
 
            PTV::Output => {
 
                target.push_str(KW_TYPE_OUT_PORT_STR);
 
                target.push('<');
 
                element_idx = write_element(target, heap, t, element_idx + 1);
 
                target.push('>');
 
            },
 
            PTV::Tuple(num_embedded) => {
 
                target.push('(');
 
                let num_embedded = *num_embedded;
 
                for embedded_idx in 0..num_embedded {
 
                    if embedded_idx != 0 {
 
                        target.push(',');
 
                    }
 
                    element_idx = write_element(target, heap, t, element_idx + 1);
 
                }
 
                target.push(')');
 
            }
 
            PTV::PolymorphicArgument(definition_id, arg_idx) => {
 
                let definition = &heap[*definition_id];
 
                let poly_var = &definition.poly_vars()[*arg_idx as usize].value;
 
                target.push_str(poly_var.as_str());
 
            },
 
            PTV::Definition(definition_id, num_embedded) => {
 
                let definition = &heap[*definition_id];
 
                let definition_ident = definition.identifier().value.as_str();
 
                target.push_str(definition_ident);
 

	
 
                let num_embedded = *num_embedded;
 
                if num_embedded != 0 {
 
                    target.push('<');
 
                    for embedded_idx in 0..num_embedded {
 
                        if embedded_idx != 0 {
 
                            target.push(',');
 
                        }
 
                        element_idx = write_element(target, heap, t, element_idx + 1);
 
                    }
 
                    target.push('>');
 
                }
 
            }
 
        }
 

	
 
        element_idx
 
    }
 

	
 
    write_element(target, heap, t, 0);
 
}
 

	
 
fn write_concrete_type(target: &mut String, heap: &Heap, def_id: DefinitionId, t: &ConcreteType) {
 
    use ConcreteTypePart as CTP;
 

	
 
    fn write_concrete_part(target: &mut String, heap: &Heap, def_id: DefinitionId, t: &ConcreteType, mut idx: usize) -> usize {
 
        if idx >= t.parts.len() {
 
            return idx;
 
        }
 

	
 
        match &t.parts[idx] {
 
            CTP::Void => target.push_str("void"),
 
            CTP::Message => target.push_str("msg"),
 
            CTP::Bool => target.push_str(KW_TYPE_BOOL_STR),
 
            CTP::UInt8 => target.push_str(KW_TYPE_UINT8_STR),
 
            CTP::UInt16 => target.push_str(KW_TYPE_UINT16_STR),
 
            CTP::UInt32 => target.push_str(KW_TYPE_UINT32_STR),
 
            CTP::UInt64 => target.push_str(KW_TYPE_UINT64_STR),
 
            CTP::SInt8 => target.push_str(KW_TYPE_SINT8_STR),
 
            CTP::SInt16 => target.push_str(KW_TYPE_SINT16_STR),
 
            CTP::SInt32 => target.push_str(KW_TYPE_SINT32_STR),
 
            CTP::SInt64 => target.push_str(KW_TYPE_SINT64_STR),
 
            CTP::Character => target.push_str(KW_TYPE_CHAR_STR),
 
            CTP::String => target.push_str(KW_TYPE_STRING_STR),
 
            CTP::Pointer => target.push('*'),
 
            CTP::Array => {
 
                idx = write_concrete_part(target, heap, def_id, t, idx + 1);
 
                target.push_str("[]");
 
            },
 
            CTP::Slice => {
 
                idx = write_concrete_part(target, heap, def_id, t, idx + 1);
 
                target.push_str("[..]");
 
            }
 
            CTP::Input => {
 
                target.push_str("in<");
src/protocol/eval/executor.rs
Show inline comments
 

	
 
use std::collections::VecDeque;
 

	
 
use super::value::*;
 
use super::store::*;
 
use super::error::*;
 
use crate::protocol::*;
 
use crate::protocol::ast::*;
 
use crate::protocol::type_table::*;
 

	
 
macro_rules! debug_enabled { () => { false }; }
 
macro_rules! debug_log {
 
    ($format:literal) => {
 
        enabled_debug_print!(false, "exec", $format);
 
    };
 
    ($format:literal, $($args:expr),*) => {
 
        enabled_debug_print!(false, "exec", $format, $($args),*);
 
    };
 
}
 

	
 
#[derive(Debug, Clone)]
 
pub(crate) enum ExprInstruction {
 
    EvalExpr(ExpressionId),
 
    PushValToFront,
 
}
 

	
 
#[derive(Debug, Clone)]
 
pub(crate) struct Frame {
 
    pub(crate) definition: ProcedureDefinitionId,
 
    pub(crate) monomorph_index: usize,
 
    pub(crate) position: StatementId,
 
    pub(crate) expr_stack: VecDeque<ExprInstruction>, // hack for expression evaluation, evaluated by popping from back
 
    pub(crate) expr_values: VecDeque<Value>, // hack for expression results, evaluated by popping from front/back
 
    pub(crate) max_stack_size: u32,
 
}
 

	
 
impl Frame {
 
    /// Creates a new execution frame. Does not modify the stack in any way.
 
    pub fn new(heap: &Heap, definition_id: ProcedureDefinitionId, _monomorph_type_id: TypeId, monomorph_index: u32) -> Self {
 
        let definition = &heap[definition_id];
 
        let outer_scope_id = definition.scope;
 
        let first_statement_id = definition.body;
 

	
 
        // Another not-so-pretty thing that has to be replaced somewhere in the
 
        // future...
 
        fn determine_max_stack_size(heap: &Heap, scope_id: ScopeId, max_size: &mut u32) {
 
            let scope = &heap[scope_id];
 

	
 
            // Check current block
 
            let cur_size = scope.next_unique_id_in_scope as u32;
 
            if cur_size > *max_size { *max_size = cur_size; }
 

	
 
            // And child blocks
 
            for child_scope in &scope.nested {
 
                determine_max_stack_size(heap, *child_scope, max_size);
 
            }
 
        }
 

	
 
        let mut max_stack_size = 0;
 
        determine_max_stack_size(heap, outer_scope_id, &mut max_stack_size);
 

	
 
        Frame{
 
            definition: definition_id,
 
            monomorph_index: monomorph_index as usize,
 
            position: first_statement_id.upcast(),
 
            expr_stack: VecDeque::with_capacity(128),
 
            expr_values: VecDeque::with_capacity(128),
 
            max_stack_size,
 
        }
 
    }
 

	
 
    /// Prepares a single expression for execution. This involves walking the
 
    /// expression tree and putting them in the `expr_stack` such that
 
    /// continuously popping from its back will evaluate the expression. The
 
    /// results of each expression will be stored by pushing onto `expr_values`.
 
    pub fn prepare_single_expression(&mut self, heap: &Heap, expr_id: ExpressionId) {
 
        debug_assert!(self.expr_stack.is_empty());
 
        self.expr_values.clear(); // May not be empty if last expression result(s) were discarded
 

	
 
        self.serialize_expression(heap, expr_id);
 
    }
 

	
 
    /// Prepares multiple expressions for execution (i.e. evaluating all
 
    /// function arguments or all elements of an array/union literal). Per
 
    /// expression this works the same as `prepare_single_expression`. However
 
    /// after each expression is evaluated we insert a `PushValToFront`
 
    /// instruction
 
    pub fn prepare_multiple_expressions(&mut self, heap: &Heap, expr_ids: &[ExpressionId]) {
 
        debug_assert!(self.expr_stack.is_empty());
 
        self.expr_values.clear();
 

	
 
        for expr_id in expr_ids {
 
            self.expr_stack.push_back(ExprInstruction::PushValToFront);
 
            self.serialize_expression(heap, *expr_id);
 
        }
 
    }
 

	
 
    /// Performs depth-first serialization of expression tree. Let's not care
 
    /// about performance for a temporary runtime implementation
 
    fn serialize_expression(&mut self, heap: &Heap, id: ExpressionId) {
 
        self.expr_stack.push_back(ExprInstruction::EvalExpr(id));
 

	
 
        match &heap[id] {
 
            Expression::Assignment(expr) => {
 
                self.serialize_expression(heap, expr.left);
 
                self.serialize_expression(heap, expr.right);
 
            },
 
            Expression::Binding(expr) => {
 
                self.serialize_expression(heap, expr.bound_to);
 
                self.serialize_expression(heap, expr.bound_from);
 
            },
 
            Expression::Conditional(expr) => {
 
                self.serialize_expression(heap, expr.test);
 
            },
 
            Expression::Binary(expr) => {
 
                self.serialize_expression(heap, expr.left);
 
                self.serialize_expression(heap, expr.right);
 
            },
 
            Expression::Unary(expr) => {
 
                self.serialize_expression(heap, expr.expression);
 
            },
 
            Expression::Indexing(expr) => {
 
                self.serialize_expression(heap, expr.index);
 
                self.serialize_expression(heap, expr.subject);
 
            },
 
            Expression::Slicing(expr) => {
 
                self.serialize_expression(heap, expr.from_index);
 
                self.serialize_expression(heap, expr.to_index);
 
                self.serialize_expression(heap, expr.subject);
 
            },
 
            Expression::Select(expr) => {
 
                self.serialize_expression(heap, expr.subject);
 
            },
 
            Expression::Literal(expr) => {
 
                // Here we only care about literals that have subexpressions
 
                match &expr.value {
 
                    Literal::Null | Literal::True | Literal::False |
 
                    Literal::Character(_) | Literal::String(_) |
 
                    Literal::Character(_) | Literal::Bytestring(_) | Literal::String(_) |
 
                    Literal::Integer(_) | Literal::Enum(_) => {
 
                        // No subexpressions
 
                    },
 
                    Literal::Struct(literal) => {
 
                        // Note: fields expressions are evaluated in programmer-
 
                        // specified order. But struct construction expects them
 
                        // in type-defined order. I might want to come back to
 
                        // this.
 
                        let mut _num_pushed = 0;
 
                        for want_field_idx in 0..literal.fields.len() {
 
                            for field in &literal.fields {
 
                                if field.field_idx == want_field_idx {
 
                                    _num_pushed += 1;
 
                                    self.expr_stack.push_back(ExprInstruction::PushValToFront);
 
                                    self.serialize_expression(heap, field.value);
 
                                }
 
                            }
 
                        }
 
                        debug_assert_eq!(_num_pushed, literal.fields.len())
 
                    },
 
                    Literal::Union(literal) => {
 
                        for value_expr_id in &literal.values {
 
                            self.expr_stack.push_back(ExprInstruction::PushValToFront);
 
                            self.serialize_expression(heap, *value_expr_id);
 
                        }
 
                    },
 
                    Literal::Array(value_expr_ids) => {
 
                        for value_expr_id in value_expr_ids {
 
                            self.expr_stack.push_back(ExprInstruction::PushValToFront);
 
                            self.serialize_expression(heap, *value_expr_id);
 
                        }
 
                    },
 
                    Literal::Tuple(value_expr_ids) => {
 
                        for value_expr_id in value_expr_ids {
 
                            self.expr_stack.push_back(ExprInstruction::PushValToFront);
 
                            self.serialize_expression(heap, *value_expr_id);
 
                        }
 
                    }
 
                }
 
            },
 
            Expression::Cast(expr) => {
 
                self.serialize_expression(heap, expr.subject);
 
            }
 
            Expression::Call(expr) => {
 
                for arg_expr_id in &expr.arguments {
 
                    self.expr_stack.push_back(ExprInstruction::PushValToFront);
 
                    self.serialize_expression(heap, *arg_expr_id);
 
                }
 
            },
 
            Expression::Variable(_expr) => {
 
                // No subexpressions
 
            }
 
        }
 
    }
 
}
 

	
 
pub type EvalResult = Result<EvalContinuation, EvalError>;
 

	
 
#[derive(Debug)]
 
pub enum EvalContinuation {
 
    // Returned in both sync and non-sync modes
 
    Stepping,
 
    // Returned only in sync mode
 
    BranchInconsistent,
 
    SyncBlockEnd,
 
    NewFork,
 
    BlockFires(PortId),
 
    BlockGet(PortId),
 
    Put(PortId, ValueGroup),
 
    SelectStart(u32, u32), // (num_cases, num_ports_total)
 
    SelectRegisterPort(u32, u32, PortId), // (case_index, port_index_in_case, port_id)
 
    SelectWait, // wait until select can continue
 
    // Returned only in non-sync mode
 
    ComponentTerminated,
 
    SyncBlockStart,
 
    NewComponent(ProcedureDefinitionId, TypeId, ValueGroup),
 
    NewChannel,
 
}
 

	
 
// Note: cloning is fine, methinks. cloning all values and the heap regions then
 
// we end up with valid "pointers" to heap regions.
 
#[derive(Debug, Clone)]
 
pub struct Prompt {
 
    pub(crate) frames: Vec<Frame>,
 
    pub(crate) store: Store,
 
}
 

	
 
impl Prompt {
 
    pub fn new(types: &TypeTable, heap: &Heap, def: ProcedureDefinitionId, type_id: TypeId, args: ValueGroup) -> Self {
 
        let mut prompt = Self{
 
            frames: Vec::new(),
 
            store: Store::new(),
 
        };
 

	
 
        // Maybe do typechecking in the future?
 
        let monomorph_index = types.get_monomorph(type_id).variant.as_procedure().monomorph_index;
 
        let new_frame = Frame::new(heap, def, type_id, monomorph_index);
 
        let max_stack_size = new_frame.max_stack_size;
 
        prompt.frames.push(new_frame);
 
        args.into_store(&mut prompt.store);
 
        prompt.store.reserve_stack(max_stack_size);
 

	
 
        prompt
 
    }
 

	
 
    /// Big 'ol function right here. Didn't want to break it up unnecessarily.
 
    /// It consists of, in sequence: executing any expressions that should be
 
    /// executed before the next statement can be evaluated, then a section that
 
    /// performs debug printing, and finally a section that takes the next
 
    /// statement and executes it. If the statement requires any expressions to
 
    /// be evaluated, then they will be added such that the next time `step` is
 
    /// called, all of these expressions are indeed evaluated.
 
    pub(crate) fn step(&mut self, types: &TypeTable, heap: &Heap, modules: &[Module], ctx: &mut impl RunContext) -> EvalResult {
 
        // Helper function to transfer multiple values from the expression value
 
        // array into a heap region (e.g. constructing arrays or structs).
 
        fn transfer_expression_values_front_into_heap(cur_frame: &mut Frame, store: &mut Store, num_values: usize) -> HeapPos {
 
            let heap_pos = store.alloc_heap();
 

	
 
            // Do the transformation first (because Rust...)
 
            for val_idx in 0..num_values {
 
                cur_frame.expr_values[val_idx] = store.read_take_ownership(cur_frame.expr_values[val_idx].clone());
 
            }
 

	
 
            // And now transfer to the heap region
 
            let values = &mut store.heap_regions[heap_pos as usize].values;
 
            debug_assert!(values.is_empty());
 
            values.reserve(num_values);
 
            for _ in 0..num_values {
 
                values.push(cur_frame.expr_values.pop_front().unwrap());
 
            }
 

	
 
            heap_pos
 
        }
 

	
 
        // Helper function to make sure that an index into an aray is valid.
 
        fn array_inclusive_index_is_invalid(store: &Store, array_heap_pos: u32, idx: i64) -> bool {
 
            let array_len = store.heap_regions[array_heap_pos as usize].values.len();
 
            return idx < 0 || idx >= array_len as i64;
 
        }
 

	
 
        fn array_exclusive_index_is_invalid(store: &Store, array_heap_pos: u32, idx: i64) -> bool {
 
            let array_len = store.heap_regions[array_heap_pos as usize].values.len();
 
            return idx < 0 || idx > array_len as i64;
 
        }
 

	
 
        fn construct_array_error(prompt: &Prompt, modules: &[Module], heap: &Heap, expr_id: ExpressionId, heap_pos: u32, idx: i64) -> EvalError {
 
            let array_len = prompt.store.heap_regions[heap_pos as usize].values.len();
 
            return EvalError::new_error_at_expr(
 
                prompt, modules, heap, expr_id,
 
                format!("index {} is out of bounds: array length is {}", idx, array_len)
 
            )
 
        }
 

	
 
        // Checking if we're at the end of execution
 
        let cur_frame = self.frames.last_mut().unwrap();
 
        if cur_frame.position.is_invalid() {
 
            if heap[cur_frame.definition].kind == ProcedureKind::Function {
 
                todo!("End of function without return, return an evaluation error");
 
            }
 
            return Ok(EvalContinuation::ComponentTerminated);
 
        }
 

	
 
        debug_log!("Taking step in '{}'", heap[cur_frame.definition].identifier.value.as_str());
 

	
 
        // Execute all pending expressions
 
        while !cur_frame.expr_stack.is_empty() {
 
            let next = cur_frame.expr_stack.pop_back().unwrap();
 
            debug_log!("Expr stack: {:?}", next);
 
            match next {
 
                ExprInstruction::PushValToFront => {
 
                    cur_frame.expr_values.rotate_right(1);
 
                },
 
                ExprInstruction::EvalExpr(expr_id) => {
 
                    let expr = &heap[expr_id];
 
                    match expr {
 
                        Expression::Assignment(expr) => {
 
                            let to = cur_frame.expr_values.pop_back().unwrap().as_ref();
 
                            let rhs = cur_frame.expr_values.pop_back().unwrap();
 

	
 
                            // Note: although not pretty, the assignment operator takes ownership
 
                            // of the right-hand side value when possible. So we do not drop the
 
                            // rhs's optionally owned heap data.
 
                            let rhs = self.store.read_take_ownership(rhs);
 
                            apply_assignment_operator(&mut self.store, to, expr.operation, rhs);
 
                        },
 
                        Expression::Binding(_expr) => {
 
                            let bind_to = cur_frame.expr_values.pop_back().unwrap();
 
                            let bind_from = cur_frame.expr_values.pop_back().unwrap();
 
                            let bind_to_heap_pos = bind_to.get_heap_pos();
 
                            let bind_from_heap_pos = bind_from.get_heap_pos();
 

	
 
                            let result = apply_binding_operator(&mut self.store, bind_to, bind_from);
 
                            self.store.drop_value(bind_to_heap_pos);
 
                            self.store.drop_value(bind_from_heap_pos);
 
                            cur_frame.expr_values.push_back(Value::Bool(result));
 
                        },
 
                        Expression::Conditional(expr) => {
 
                            // Evaluate testing expression, then extend the
 
                            // expression stack with the appropriate expression
 
                            let test_result = cur_frame.expr_values.pop_back().unwrap().as_bool();
 
                            if test_result {
 
                                cur_frame.serialize_expression(heap, expr.true_expression);
 
                            } else {
 
                                cur_frame.serialize_expression(heap, expr.false_expression);
 
                            }
 
                        },
 
                        Expression::Binary(expr) => {
 
                            let lhs = cur_frame.expr_values.pop_back().unwrap();
 
                            let rhs = cur_frame.expr_values.pop_back().unwrap();
 
                            let result = apply_binary_operator(&mut self.store, &lhs, expr.operation, &rhs);
 
                            cur_frame.expr_values.push_back(result);
 
                            self.store.drop_value(lhs.get_heap_pos());
 
                            self.store.drop_value(rhs.get_heap_pos());
 
                        },
 
                        Expression::Unary(expr) => {
 
                            let val = cur_frame.expr_values.pop_back().unwrap();
 
                            let result = apply_unary_operator(&mut self.store, expr.operation, &val);
 
                            cur_frame.expr_values.push_back(result);
 
                            self.store.drop_value(val.get_heap_pos());
 
                        },
 
                        Expression::Indexing(_expr) => {
 
                            // Evaluate index. Never heap allocated so we do
 
                            // not have to drop it.
 
                            let index = cur_frame.expr_values.pop_back().unwrap();
 
                            let index = self.store.maybe_read_ref(&index);
 

	
 
                            debug_assert!(index.is_integer());
 
                            let index = if index.is_signed_integer() {
 
                                index.as_signed_integer() as i64
 
                            } else {
 
                                index.as_unsigned_integer() as i64
 
                            };
 

	
 
                            let subject = cur_frame.expr_values.pop_back().unwrap();
 

	
 
                            let (deallocate_heap_pos, value_to_push) = match subject {
 
                                Value::Ref(value_ref) => {
 
                                    // Our expression stack value is a reference to something that
 
                                    // exists in the normal stack/heap. We don't want to deallocate
 
                                    // this thing. Rather we want to return a reference to it.
 
                                    let subject = self.store.read_ref(value_ref);
 
                                    let subject_heap_pos = match subject {
 
                                        Value::String(v) => *v,
 
                                        Value::Array(v) => *v,
 
                                        Value::Message(v) => *v,
 
                                        _ => unreachable!(),
 
                                    };
 

	
 
                                    if array_inclusive_index_is_invalid(&self.store, subject_heap_pos, index) {
 
                                        return Err(construct_array_error(self, modules, heap, expr_id, subject_heap_pos, index));
 
                                    }
 

	
 
                                    (None, Value::Ref(ValueId::Heap(subject_heap_pos, index as u32)))
 
                                },
 
                                _ => {
 
                                    // Our value lives on the expression stack, hence we need to
 
                                    // clone whatever we're referring to. Then drop the subject.
 
                                    let subject_heap_pos = match &subject {
 
                                        Value::String(v) => *v,
 
                                        Value::Array(v) => *v,
 
                                        Value::Message(v) => *v,
 
                                        _ => unreachable!(),
 
                                    };
 

	
 
                                    if array_inclusive_index_is_invalid(&self.store, subject_heap_pos, index) {
 
                                        return Err(construct_array_error(self, modules, heap, expr_id, subject_heap_pos, index));
 
                                    }
 

	
 
                                    let subject_indexed = Value::Ref(ValueId::Heap(subject_heap_pos, index as u32));
 
                                    (Some(subject_heap_pos), self.store.clone_value(subject_indexed))
 
                                },
 
                            };
 

	
 
                            cur_frame.expr_values.push_back(value_to_push);
 
                            self.store.drop_value(deallocate_heap_pos);
 
                        },
 
                        Expression::Slicing(expr) => {
 
                            // Evaluate indices
 
                            let from_index = cur_frame.expr_values.pop_back().unwrap();
 
                            let from_index = self.store.maybe_read_ref(&from_index);
 
                            let to_index = cur_frame.expr_values.pop_back().unwrap();
 
                            let to_index = self.store.maybe_read_ref(&to_index);
 

	
 
                            debug_assert!(from_index.is_integer() && to_index.is_integer());
 
                            let from_index = if from_index.is_signed_integer() {
 
                                from_index.as_signed_integer()
 
                            } else {
 
                                from_index.as_unsigned_integer() as i64
 
                            };
 
                            let to_index = if to_index.is_signed_integer() {
 
                                to_index.as_signed_integer()
 
                            } else {
 
                                to_index.as_unsigned_integer() as i64
 
                            };
 

	
 
                            // Dereference subject if needed
 
                            let subject = cur_frame.expr_values.pop_back().unwrap();
 
                            let deref_subject = self.store.maybe_read_ref(&subject);
 

	
 
                            // Slicing needs to produce a copy anyway (with the
 
                            // current evaluator implementation)
 
                            enum ValueKind{ Array, String, Message }
 
                            let (value_kind, array_heap_pos) = match deref_subject {
 
                                Value::Array(v) => (ValueKind::Array, *v),
 
                                Value::String(v) => (ValueKind::String, *v),
 
                                Value::Message(v) => (ValueKind::Message, *v),
 
                                _ => unreachable!()
 
                            };
 

	
 
                            if array_inclusive_index_is_invalid(&self.store, array_heap_pos, from_index) {
 
                                return Err(construct_array_error(self, modules, heap, expr.from_index, array_heap_pos, from_index));
 
                            }
 
                            if array_exclusive_index_is_invalid(&self.store, array_heap_pos, to_index) {
 
                                return Err(construct_array_error(self, modules, heap, expr.to_index, array_heap_pos, to_index));
 
                            }
 

	
 
                            // Again: would love to push directly, but rust...
 
                            let new_heap_pos = self.store.alloc_heap();
 
                            debug_assert!(self.store.heap_regions[new_heap_pos as usize].values.is_empty());
 
                            if to_index > from_index {
 
                                let from_index = from_index as usize;
 
                                let to_index = to_index as usize;
 
                                let mut values = Vec::with_capacity(to_index - from_index);
 
                                for idx in from_index..to_index {
 
                                    let value = self.store.heap_regions[array_heap_pos as usize].values[idx].clone();
 
                                    values.push(self.store.clone_value(value));
 
                                }
 

	
 
                                self.store.heap_regions[new_heap_pos as usize].values = values;
 

	
 
                            } // else: empty range
 

	
 
                            cur_frame.expr_values.push_back(match value_kind {
 
                                ValueKind::Array => Value::Array(new_heap_pos),
 
                                ValueKind::String => Value::String(new_heap_pos),
 
                                ValueKind::Message => Value::Message(new_heap_pos),
 
                            });
 

	
 
                            // Dropping the original subject, because we don't
 
                            // want to drop something on the stack
 
                            self.store.drop_value(subject.get_heap_pos());
 
                        },
 
                        Expression::Select(expr) => {
 
                            let subject= cur_frame.expr_values.pop_back().unwrap();
 
                            let mono_data = &heap[cur_frame.definition].monomorphs[cur_frame.monomorph_index];
 
                            let field_idx = mono_data.expr_info[expr.type_index as usize].variant.as_select() as u32;
 

	
 
                            // Note: same as above: clone if value lives on expr stack, simply
 
                            // refer to it if it already lives on the stack/heap.
 
                            let (deallocate_heap_pos, value_to_push) = match subject {
 
                                Value::Ref(value_ref) => {
 
                                    let subject = self.store.read_ref(value_ref);
 
                                    let subject_heap_pos = match expr.kind {
 
                                        SelectKind::StructField(_) => subject.as_struct(),
 
                                        SelectKind::TupleMember(_) => subject.as_tuple(),
 
                                    };
 

	
 
                                    (None, Value::Ref(ValueId::Heap(subject_heap_pos, field_idx)))
 
                                },
 
                                _ => {
 
                                    let subject_heap_pos = match expr.kind {
 
                                        SelectKind::StructField(_) => subject.as_struct(),
 
                                        SelectKind::TupleMember(_) => subject.as_tuple(),
 
                                    };
 
                                    let subject_indexed = Value::Ref(ValueId::Heap(subject_heap_pos, field_idx));
 
                                    (Some(subject_heap_pos), self.store.clone_value(subject_indexed))
 
                                },
 
                            };
 

	
 
                            cur_frame.expr_values.push_back(value_to_push);
 
                            self.store.drop_value(deallocate_heap_pos);
 
                        },
 
                        Expression::Literal(expr) => {
 
                            let value = match &expr.value {
 
                                Literal::Null => Value::Null,
 
                                Literal::True => Value::Bool(true),
 
                                Literal::False => Value::Bool(false),
 
                                Literal::Character(lit_value) => Value::Char(*lit_value),
 
                                Literal::Bytestring(lit_value) => {
 
                                    let heap_pos = self.store.alloc_heap();
 
                                    let values = &mut self.store.heap_regions[heap_pos as usize].values;
 
                                    debug_assert!(values.is_empty());
 
                                    values.reserve(lit_value.len());
 
                                    for byte in lit_value {
 
                                        values.push(Value::UInt8(*byte));
 
                                    }
 
                                    Value::Array(heap_pos)
 
                                }
 
                                Literal::String(lit_value) => {
 
                                    let heap_pos = self.store.alloc_heap();
 
                                    let values = &mut self.store.heap_regions[heap_pos as usize].values;
 
                                    let value = lit_value.as_str();
 
                                    debug_assert!(values.is_empty());
 
                                    values.reserve(value.len());
 
                                    for character in value.as_bytes() {
 
                                        debug_assert!(character.is_ascii());
 
                                        values.push(Value::Char(*character as char));
 
                                    }
 
                                    Value::String(heap_pos)
 
                                }
 
                                Literal::Integer(lit_value) => {
 
                                    use ConcreteTypePart as CTP;
 
                                    let mono_data = &heap[cur_frame.definition].monomorphs[cur_frame.monomorph_index];
 
                                    let type_id = mono_data.expr_info[expr.type_index as usize].type_id;
 
                                    let concrete_type = &types.get_monomorph(type_id).concrete_type;
 

	
 
                                    debug_assert_eq!(concrete_type.parts.len(), 1);
 
                                    match concrete_type.parts[0] {
 
                                        CTP::UInt8  => Value::UInt8(lit_value.unsigned_value as u8),
 
                                        CTP::UInt16 => Value::UInt16(lit_value.unsigned_value as u16),
 
                                        CTP::UInt32 => Value::UInt32(lit_value.unsigned_value as u32),
 
                                        CTP::UInt64 => Value::UInt64(lit_value.unsigned_value as u64),
 
                                        CTP::SInt8  => Value::SInt8(lit_value.unsigned_value as i8),
 
                                        CTP::SInt16 => Value::SInt16(lit_value.unsigned_value as i16),
 
                                        CTP::SInt32 => Value::SInt32(lit_value.unsigned_value as i32),
 
                                        CTP::SInt64 => Value::SInt64(lit_value.unsigned_value as i64),
 
                                        _ => unreachable!("got concrete type {:?} for integer literal at expr {:?}", concrete_type, expr_id),
 
                                    }
 
                                }
 
                                Literal::Struct(lit_value) => {
 
                                    let heap_pos = transfer_expression_values_front_into_heap(
 
                                        cur_frame, &mut self.store, lit_value.fields.len()
 
                                    );
 
                                    Value::Struct(heap_pos)
 
                                }
 
                                Literal::Enum(lit_value) => {
 
                                    Value::Enum(lit_value.variant_idx as i64)
 
                                }
 
                                Literal::Union(lit_value) => {
 
                                    let heap_pos = transfer_expression_values_front_into_heap(
 
                                        cur_frame, &mut self.store, lit_value.values.len()
 
                                    );
 
                                    Value::Union(lit_value.variant_idx as i64, heap_pos)
 
                                }
 
                                Literal::Array(lit_value) => {
 
                                    let heap_pos = transfer_expression_values_front_into_heap(
 
                                        cur_frame, &mut self.store, lit_value.len()
 
                                    );
 
                                    Value::Array(heap_pos)
 
                                }
 
                                Literal::Tuple(lit_value) => {
 
                                    let heap_pos = transfer_expression_values_front_into_heap(
 
                                        cur_frame, &mut self.store, lit_value.len()
 
                                    );
 
                                    Value::Tuple(heap_pos)
 
                                }
 
                            };
 

	
 
                            cur_frame.expr_values.push_back(value);
 
                        },
 
                        Expression::Cast(expr) => {
 
                            let mono_data = &heap[cur_frame.definition].monomorphs[cur_frame.monomorph_index];
 
                            let type_id = mono_data.expr_info[expr.type_index as usize].type_id;
 
                            let concrete_type = &types.get_monomorph(type_id).concrete_type;
 

	
 
                            // Typechecking reduced this to two cases: either we
 
                            // have casting noop (same types), or we're casting
 
                            // between integer/bool/char types.
 
                            let subject = cur_frame.expr_values.pop_back().unwrap();
 
                            match apply_casting(&mut self.store, concrete_type, &subject) {
 
                                Ok(value) => cur_frame.expr_values.push_back(value),
 
                                Err(msg) => {
 
                                    return Err(EvalError::new_error_at_expr(self, modules, heap, expr.this.upcast(), msg));
 
                                }
 
                            }
 

	
 
                            self.store.drop_value(subject.get_heap_pos());
 
                        }
 
                        Expression::Call(expr) => {
 
                            // If we're dealing with a builtin we don't do any
 
                            // fancy shenanigans at all, just push the result.
 
                            match expr.method {
 
                                Method::Get => {
 
                                    let value = cur_frame.expr_values.pop_front().unwrap();
 
                                    let value = self.store.maybe_read_ref(&value).clone();
 

	
 
                                    let port_id = if let Value::Input(port_id) = value {
 
                                        port_id
 
                                    } else {
 
                                        unreachable!("executor calling 'get' on value {:?}", value)
 
                                    };
 

	
 
                                    match ctx.performed_get(port_id) {
 
                                        Some(result) => {
 
                                            // We have the result. Merge the `ValueGroup` with the
 
                                            // stack/heap storage.
 
                                            debug_assert_eq!(result.values.len(), 1);
 
                                            result.into_stack(&mut cur_frame.expr_values, &mut self.store);
 
                                        },
 
                                        None => {
 
                                            // Don't have the result yet, prepare the expression to
 
                                            // get run again after we've received a message.
 
                                            cur_frame.expr_values.push_front(value.clone());
 
                                            cur_frame.expr_stack.push_back(ExprInstruction::EvalExpr(expr_id));
 
                                            return Ok(EvalContinuation::BlockGet(port_id));
 
                                        }
 
                                    }
 
                                },
 
                                Method::Put => {
 
                                    let port_value = cur_frame.expr_values.pop_front().unwrap();
 
                                    let deref_port_value = self.store.maybe_read_ref(&port_value).clone();
 

	
 
                                    let port_id = if let Value::Output(port_id) = deref_port_value {
 
                                        port_id
 
                                    } else {
 
                                        unreachable!("executor calling 'put' on value {:?}", deref_port_value)
 
                                    };
 

	
 
                                    let msg_value = cur_frame.expr_values.pop_front().unwrap();
 
                                    let deref_msg_value = self.store.maybe_read_ref(&msg_value).clone();
 

	
 
                                    if ctx.performed_put(port_id) {
 
                                        // We're fine, deallocate in case the expression value stack
 
                                        // held an owned value
 
                                        self.store.drop_value(msg_value.get_heap_pos());
 
                                    } else {
 
                                        // Prepare to execute again
 
                                        cur_frame.expr_values.push_front(msg_value);
 
                                        cur_frame.expr_values.push_front(port_value);
 
                                        cur_frame.expr_stack.push_back(ExprInstruction::EvalExpr(expr_id));
 
                                        let value_group = ValueGroup::from_store(&self.store, &[deref_msg_value]);
 
                                        return Ok(EvalContinuation::Put(port_id, value_group));
 
                                    }
 
                                },
 
                                Method::Fires => {
 
                                    let port_value = cur_frame.expr_values.pop_front().unwrap();
 
                                    let port_value_deref = self.store.maybe_read_ref(&port_value).clone();
 
                                    let port_id = port_value_deref.as_port_id();
 

	
 
                                    match ctx.fires(port_id) {
 
                                        None => {
 
                                            cur_frame.expr_values.push_front(port_value);
 
                                            cur_frame.expr_stack.push_back(ExprInstruction::EvalExpr(expr_id));
 
                                            return Ok(EvalContinuation::BlockFires(port_id));
 
                                        },
 
                                        Some(value) => {
 
                                            cur_frame.expr_values.push_back(value);
 
                                        }
 
                                    }
 
                                },
 
                                Method::Create => {
 
                                    let length_value = cur_frame.expr_values.pop_front().unwrap();
 
                                    let length_value = self.store.maybe_read_ref(&length_value);
 
                                    let length = if length_value.is_signed_integer() {
 
                                        let length_value = length_value.as_signed_integer();
 
                                        if length_value < 0 {
 
                                            return Err(EvalError::new_error_at_expr(
 
                                                self, modules, heap, expr_id,
 
                                                format!("got length '{}', can only create a message with a non-negative length", length_value)
 
                                            ));
 
                                        }
 

	
 
                                        length_value as u64
 
                                    } else {
 
                                        debug_assert!(length_value.is_unsigned_integer());
 
                                        length_value.as_unsigned_integer()
 
                                    };
 

	
 
                                    let heap_pos = self.store.alloc_heap();
 
                                    let values = &mut self.store.heap_regions[heap_pos as usize].values;
 
                                    debug_assert!(values.is_empty());
 
                                    values.resize(length as usize, Value::UInt8(0));
 
                                    cur_frame.expr_values.push_back(Value::Message(heap_pos));
 
                                },
 
                                Method::Length => {
 
                                    let value = cur_frame.expr_values.pop_front().unwrap();
 
                                    let value_heap_pos = value.get_heap_pos();
 
                                    let value = self.store.maybe_read_ref(&value);
 

	
 
                                    let heap_pos = match value {
 
                                        Value::Array(pos) => *pos,
 
                                        Value::String(pos) => *pos,
 
                                        _ => unreachable!("length(...) on {:?}", value),
 
                                    };
 

	
 
                                    let len = self.store.heap_regions[heap_pos as usize].values.len();
 

	
 
                                    // TODO: @PtrInt
 
                                    cur_frame.expr_values.push_back(Value::UInt32(len as u32));
 
                                    self.store.drop_value(value_heap_pos);
 
                                },
 
                                Method::Assert => {
 
                                    let value = cur_frame.expr_values.pop_front().unwrap();
 
                                    let value = self.store.maybe_read_ref(&value).clone();
 
                                    if !value.as_bool() {
 
                                        return Ok(EvalContinuation::BranchInconsistent)
 
                                    }
 
                                },
 
                                Method::Print => {
 
                                    // Convert the runtime-variant of a string
 
                                    // into an actual string.
 
                                    let value = cur_frame.expr_values.pop_front().unwrap();
 
                                    let mut is_literal_string = value.get_heap_pos().is_some();
 
                                    let value = self.store.maybe_read_ref(&value);
 
                                    let value_heap_pos = value.as_string();
 
                                    let elements = &self.store.heap_regions[value_heap_pos as usize].values;
 

	
 
                                    let mut message = String::with_capacity(elements.len());
 
                                    for element in elements {
 
                                        message.push(element.as_char());
 
                                    }
 

	
 
                                    // Drop the heap-allocated value from the
 
                                    // store
 
                                    if is_literal_string {
 
                                        self.store.drop_heap_pos(value_heap_pos);
 
                                    }
 

	
 
                                    println!("{}", message);
 
                                },
 
                                Method::SelectStart => {
 
                                    let num_cases = self.store.maybe_read_ref(&cur_frame.expr_values.pop_front().unwrap()).as_uint32();
 
                                    let num_ports = self.store.maybe_read_ref(&cur_frame.expr_values.pop_front().unwrap()).as_uint32();
 

	
 
                                    return Ok(EvalContinuation::SelectStart(num_cases, num_ports));
 
                                },
 
                                Method::SelectRegisterCasePort => {
 
                                    let case_index = self.store.maybe_read_ref(&cur_frame.expr_values.pop_front().unwrap()).as_uint32();
 
                                    let port_index = self.store.maybe_read_ref(&cur_frame.expr_values.pop_front().unwrap()).as_uint32();
 
                                    let port_value = self.store.maybe_read_ref(&cur_frame.expr_values.pop_front().unwrap()).as_port_id();
 

	
 
                                    return Ok(EvalContinuation::SelectRegisterPort(case_index, port_index, port_value));
 
                                },
 
                                Method::SelectWait => {
 
                                    match ctx.performed_select_wait() {
 
                                        Some(select_index) => {
 
                                            cur_frame.expr_values.push_back(Value::UInt32(select_index));
 
                                        },
 
                                        None => {
 
                                            cur_frame.expr_stack.push_back(ExprInstruction::EvalExpr(expr.this.upcast()));
 
                                            return Ok(EvalContinuation::SelectWait)
 
                                        },
 
                                    }
 
                                },
 
                                Method::ComponentRandomU32 | Method::ComponentTcpClient => {
 
                                    debug_assert_eq!(heap[expr.procedure].parameters.len(), cur_frame.expr_values.len());
 
                                    debug_assert_eq!(heap[cur_frame.position].as_new().expression, expr.this);
 
                                },
 
                                Method::UserComponent => {
 
                                    // This is actually handled by the evaluation
 
                                    // of the statement.
 
                                    debug_assert_eq!(heap[expr.procedure].parameters.len(), cur_frame.expr_values.len());
 
                                    debug_assert_eq!(heap[cur_frame.position].as_new().expression, expr.this)
 
                                    debug_assert_eq!(heap[cur_frame.position].as_new().expression, expr.this);
 
                                },
 
                                Method::UserFunction => {
 
                                    // Push a new frame. Note that all expressions have
 
                                    // been pushed to the front, so they're in the order
 
                                    // of the definition.
 
                                    let num_args = expr.arguments.len();
 

	
 
                                    // Determine stack boundaries
 
                                    let cur_stack_boundary = self.store.cur_stack_boundary;
 
                                    let new_stack_boundary = self.store.stack.len();
 

	
 
                                    // Push new boundary and function arguments for new frame
 
                                    self.store.stack.push(Value::PrevStackBoundary(cur_stack_boundary as isize));
 
                                    for _ in 0..num_args {
 
                                        let argument = self.store.read_take_ownership(cur_frame.expr_values.pop_front().unwrap());
 
                                        self.store.stack.push(argument);
 
                                    }
 

	
 
                                    // Determine the monomorph index of the function we're calling
 
                                    let mono_data = &heap[cur_frame.definition].monomorphs[cur_frame.monomorph_index];
 
                                    let (type_id, monomorph_index) = mono_data.expr_info[expr.type_index as usize].variant.as_procedure();
 

	
 
                                    // Push the new frame and reserve its stack size
 
                                    let new_frame = Frame::new(heap, expr.procedure, type_id, monomorph_index);
 
                                    let new_stack_size = new_frame.max_stack_size;
 
                                    self.frames.push(new_frame);
 
                                    self.store.cur_stack_boundary = new_stack_boundary;
 
                                    self.store.reserve_stack(new_stack_size);
 

	
 
                                    // To simplify the logic a little bit we will now
 
                                    // return and ask our caller to call us again
 
                                    return Ok(EvalContinuation::Stepping);
 
                                }
 
                            }
 
                        },
 
                        Expression::Variable(expr) => {
 
                            let variable = &heap[expr.declaration.unwrap()];
 
                            let ref_value = if expr.used_as_binding_target {
 
                                Value::Binding(variable.unique_id_in_scope as StackPos)
 
                            } else {
 
                                Value::Ref(ValueId::Stack(variable.unique_id_in_scope as StackPos))
 
                            };
 
                            cur_frame.expr_values.push_back(ref_value);
 
                        }
 
                    }
 
                }
 
            }
 
        }
 

	
 
        debug_log!("Frame [{:?}] at {:?}", cur_frame.definition, cur_frame.position);
 
        if debug_enabled!() {
 
            debug_log!("Expression value stack (size = {}):", cur_frame.expr_values.len());
 
            for (_stack_idx, _stack_val) in cur_frame.expr_values.iter().enumerate() {
 
                debug_log!("  [{:03}] {:?}", _stack_idx, _stack_val);
 
            }
 

	
 
            debug_log!("Stack (size = {}):", self.store.stack.len());
 
            for (_stack_idx, _stack_val) in self.store.stack.iter().enumerate() {
 
                debug_log!("  [{:03}] {:?}", _stack_idx, _stack_val);
 
            }
 

	
 
            debug_log!("Heap:");
 
            for (_heap_idx, _heap_region) in self.store.heap_regions.iter().enumerate() {
 
                let _is_free = self.store.free_regions.iter().any(|idx| *idx as usize == _heap_idx);
 
                debug_log!("  [{:03}] in_use: {}, len: {}, vals: {:?}", _heap_idx, !_is_free, _heap_region.values.len(), &_heap_region.values);
 
            }
 
        }
 
        // No (more) expressions to evaluate. So evaluate statement (that may
 
        // depend on the result on the last evaluated expression(s))
 
        let stmt = &heap[cur_frame.position];
 
        let return_value = match stmt {
 
            Statement::Block(stmt) => {
 
                debug_assert!(stmt.statements.is_empty() || stmt.next == stmt.statements[0]);
 
                cur_frame.position = stmt.next;
 
                Ok(EvalContinuation::Stepping)
 
            },
 
            Statement::EndBlock(stmt) => {
 
                let block = &heap[stmt.start_block];
 
                let scope = &heap[block.scope];
 
                self.store.clear_stack(scope.first_unique_id_in_scope as usize);
 
                cur_frame.position = stmt.next;
 

	
 
                Ok(EvalContinuation::Stepping)
 
            },
 
            Statement::Local(stmt) => {
 
                match stmt {
 
                    LocalStatement::Memory(stmt) => {
 
                        dbg_code!({
 
                            let variable = &heap[stmt.variable];
 
                            debug_assert!(match self.store.read_ref(ValueId::Stack(variable.unique_id_in_scope as u32)) {
 
                                Value::Unassigned => false,
 
                                _ => true,
 
                            });
 
                        });
 

	
 
                        cur_frame.position = stmt.next;
 
                        Ok(EvalContinuation::Stepping)
 
                    },
 
                    LocalStatement::Channel(stmt) => {
 
                        // Need to create a new channel by requesting it from
 
                        // the runtime.
 
                        match ctx.created_channel() {
 
                            None => {
 
                                // No channel is pending. So request one
 
                                    Ok(EvalContinuation::NewChannel)
 
                            },
 
                            Some((put_port, get_port)) => {
 
                                self.store.write(ValueId::Stack(heap[stmt.from].unique_id_in_scope as u32), put_port);
 
                                self.store.write(ValueId::Stack(heap[stmt.to].unique_id_in_scope as u32), get_port);
 
                                cur_frame.position = stmt.next;
 
                                Ok(EvalContinuation::Stepping)
 
                            }
 
                        }
 
                    }
 
                }
 
            },
 
            Statement::Labeled(stmt) => {
 
                cur_frame.position = stmt.body;
 

	
 
                Ok(EvalContinuation::Stepping)
 
            },
 
            Statement::If(stmt) => {
 
                debug_assert_eq!(cur_frame.expr_values.len(), 1, "expected one expr value for if statement");
 
                let test_value = cur_frame.expr_values.pop_back().unwrap();
 
                let test_value = self.store.maybe_read_ref(&test_value).as_bool();
 
                if test_value {
 
                    cur_frame.position = stmt.true_case.body;
 
                } else if let Some(false_body) = stmt.false_case {
 
                    cur_frame.position = false_body.body;
 
                } else {
 
                    // Not true, and no false body
 
                    cur_frame.position = stmt.end_if.upcast();
 
                }
 

	
 
                Ok(EvalContinuation::Stepping)
 
            },
 
            Statement::EndIf(stmt) => {
 
                cur_frame.position = stmt.next;
 
                let if_stmt = &heap[stmt.start_if];
 
                debug_assert_eq!(
 
                    heap[if_stmt.true_case.scope].first_unique_id_in_scope,
 
                    heap[if_stmt.false_case.unwrap_or(if_stmt.true_case).scope].first_unique_id_in_scope,
 
                );
 
                let scope = &heap[if_stmt.true_case.scope];
 
                self.store.clear_stack(scope.first_unique_id_in_scope as usize);
 
                Ok(EvalContinuation::Stepping)
 
            },
 
            Statement::While(stmt) => {
 
                debug_assert_eq!(cur_frame.expr_values.len(), 1, "expected one expr value for while statement");
 
                let test_value = cur_frame.expr_values.pop_back().unwrap();
 
                let test_value = self.store.maybe_read_ref(&test_value).as_bool();
 
                if test_value {
 
                    cur_frame.position = stmt.body;
 
                } else {
 
                    cur_frame.position = stmt.end_while.upcast();
 
                }
 

	
 
                Ok(EvalContinuation::Stepping)
 
            },
 
            Statement::EndWhile(stmt) => {
 
                cur_frame.position = stmt.next;
 
                let start_while = &heap[stmt.start_while];
 
                let scope = &heap[start_while.scope];
 
                self.store.clear_stack(scope.first_unique_id_in_scope as usize);
 
                Ok(EvalContinuation::Stepping)
 
            },
 
            Statement::Break(stmt) => {
 
                cur_frame.position = stmt.target.upcast();
 

	
 
                Ok(EvalContinuation::Stepping)
 
            },
 
            Statement::Continue(stmt) => {
 
                cur_frame.position = stmt.target.upcast();
 

	
 
                Ok(EvalContinuation::Stepping)
 
            },
 
            Statement::Synchronous(stmt) => {
 
                cur_frame.position = stmt.body;
 

	
 
                Ok(EvalContinuation::SyncBlockStart)
 
            },
 
            Statement::EndSynchronous(stmt) => {
 
                cur_frame.position = stmt.next;
 
                let start_synchronous = &heap[stmt.start_sync];
 
                let scope = &heap[start_synchronous.scope];
 
                self.store.clear_stack(scope.first_unique_id_in_scope as usize);
 

	
 
                Ok(EvalContinuation::SyncBlockEnd)
 
            },
 
            Statement::Fork(stmt) => {
 
                if stmt.right_body.is_none() {
 
                    // No reason to fork
src/protocol/eval/value.rs
Show inline comments
 
use std::collections::VecDeque;
 

	
 
use super::store::*;
 
use crate::protocol::ast::{
 
    AssignmentOperator,
 
    BinaryOperator,
 
    UnaryOperator,
 
    ConcreteType,
 
    ConcreteTypePart,
 
};
 
use crate::protocol::parser::token_parsing::*;
 

	
 
pub type StackPos = u32;
 
pub type HeapPos = u32;
 

	
 
#[derive(Debug, Copy, Clone)]
 
pub enum ValueId {
 
    Stack(StackPos), // place on stack
 
    Heap(HeapPos, u32), // allocated region + values within that region
 
}
 

	
 
#[derive(Debug, Copy, Clone, PartialEq, Eq)]
 
pub struct PortId{
 
    pub(crate) id: u32
 
}
 

	
 
impl PortId {
 
    pub fn new(id: u32) -> Self {
 
        return Self{ id };
 
    }
 
}
 

	
 
/// Represents a value stored on the stack or on the heap. Some values contain
 
/// a `HeapPos`, implying that they're stored in the store's `Heap`. Clearing
 
/// a `Value` with a `HeapPos` from a stack must also clear the associated
 
/// region from the `Heap`.
 
#[derive(Debug, Clone)]
 
pub enum Value {
 
    // Special types, never encountered during evaluation if the compiler works correctly
 
    Unassigned,                 // Marker when variables are first declared, immediately followed by assignment
 
    PrevStackBoundary(isize),   // Marker for stack frame beginning, so we can pop stack values
 
    Ref(ValueId),               // Reference to a value, used by expressions producing references
 
    Binding(StackPos),          // Reference to a binding variable (reserved on the stack)
 
    // Builtin types
 
    Input(PortId),
 
    Output(PortId),
 
    Message(HeapPos),
 
    Null,
 
    Bool(bool),
 
    Char(char),
 
    String(HeapPos),
 
    UInt8(u8),
 
    UInt16(u16),
 
    UInt32(u32),
 
    UInt64(u64),
 
    SInt8(i8),
 
    SInt16(i16),
 
    SInt32(i32),
 
    SInt64(i64),
 
    Array(HeapPos),
 
    Tuple(HeapPos),
 
    // Instances of user-defined types
 
    Enum(i64),
 
    Union(i64, HeapPos),
 
    Struct(HeapPos),
 
}
 

	
 
union_cast_to_value_method_impl!(as_stack_boundary, isize, Value::PrevStackBoundary);
 
union_cast_to_value_method_impl!(as_ref, ValueId, Value::Ref);
 
union_cast_to_value_method_impl!(as_input, PortId, Value::Input);
 
union_cast_to_value_method_impl!(as_output, PortId, Value::Output);
 
union_cast_to_value_method_impl!(as_message, HeapPos, Value::Message);
 
union_cast_to_value_method_impl!(as_bool, bool, Value::Bool);
 
union_cast_to_value_method_impl!(as_char, char, Value::Char);
 
union_cast_to_value_method_impl!(as_string, HeapPos, Value::String);
 
union_cast_to_value_method_impl!(as_uint8, u8, Value::UInt8);
 
union_cast_to_value_method_impl!(as_uint16, u16, Value::UInt16);
 
union_cast_to_value_method_impl!(as_uint32, u32, Value::UInt32);
 
union_cast_to_value_method_impl!(as_uint64, u64, Value::UInt64);
 
union_cast_to_value_method_impl!(as_sint8, i8, Value::SInt8);
 
union_cast_to_value_method_impl!(as_sint16, i16, Value::SInt16);
 
union_cast_to_value_method_impl!(as_sint32, i32, Value::SInt32);
 
union_cast_to_value_method_impl!(as_sint64, i64, Value::SInt64);
 
union_cast_to_value_method_impl!(as_array, HeapPos, Value::Array);
 
union_cast_to_value_method_impl!(as_tuple, HeapPos, Value::Tuple);
 
union_cast_to_value_method_impl!(as_enum, i64, Value::Enum);
 
union_cast_to_value_method_impl!(as_struct, HeapPos, Value::Struct);
 

	
 
impl Value {
 
    pub(crate) fn as_union(&self) -> (i64, HeapPos) {
 
        match self {
 
            Value::Union(tag, v) => (*tag, *v),
 
            _ => panic!("called as_union on {:?}", self),
 
        }
 
    }
 

	
 
    pub(crate) fn as_port_id(&self) -> PortId {
 
        match self {
 
            Value::Input(v) => *v,
 
            Value::Output(v) => *v,
 
            _ => unreachable!(),
 
        }
 
    }
 

	
 
    pub(crate) fn is_integer(&self) -> bool {
 
        match self {
 
            Value::UInt8(_) | Value::UInt16(_) | Value::UInt32(_) | Value::UInt64(_) |
 
            Value::SInt8(_) | Value::SInt16(_) | Value::SInt32(_) | Value::SInt64(_) => true,
 
            _ => false
 
        }
 
    }
 

	
 
    pub(crate) fn is_unsigned_integer(&self) -> bool {
 
        match self {
 
            Value::UInt8(_) | Value::UInt16(_) | Value::UInt32(_) | Value::UInt64(_) => true,
 
            _ => false
 
        }
 
    }
 

	
 
    pub(crate) fn is_signed_integer(&self) -> bool {
 
        match self {
 
            Value::SInt8(_) | Value::SInt16(_) | Value::SInt32(_) | Value::SInt64(_) => true,
 
            _ => false
 
        }
 
    }
 

	
 
    pub(crate) fn as_unsigned_integer(&self) -> u64 {
 
        match self {
 
            Value::UInt8(v)  => *v as u64,
 
            Value::UInt16(v) => *v as u64,
 
            Value::UInt32(v) => *v as u64,
 
            Value::UInt64(v) => *v as u64,
 
            _ => unreachable!("called as_unsigned_integer on {:?}", self),
 
        }
 
    }
 

	
 
    pub(crate) fn as_signed_integer(&self) -> i64 {
 
        match self {
 
            Value::SInt8(v)  => *v as i64,
 
            Value::SInt16(v) => *v as i64,
 
            Value::SInt32(v) => *v as i64,
 
            Value::SInt64(v) => *v as i64,
 
            _ => unreachable!("called as_signed_integer on {:?}", self)
 
        }
 
    }
 

	
 
    /// Returns the heap position associated with the value. If the value
 
    /// doesn't store anything in the heap then we return `None`.
 
    pub(crate) fn get_heap_pos(&self) -> Option<HeapPos> {
 
        match self {
 
            Value::Message(v) => Some(*v),
 
            Value::String(v) => Some(*v),
 
            Value::Array(v) => Some(*v),
 
            Value::Tuple(v) => Some(*v),
 
            Value::Union(_, v) => Some(*v),
 
            Value::Struct(v) => Some(*v),
 
            _ => None
 
        }
 
    }
 
}
 

	
 
/// When providing arguments to a new component, or when transferring values
 
/// from one component's store to a newly instantiated component, one has to
 
/// transfer stack and heap values. This `ValueGroup` represents such a
 
/// temporary group of values with potential heap allocations.
 
///
 
/// Constructing such a ValueGroup manually requires some extra care to make
 
/// sure all elements of `values` point to valid elements of `regions`.
 
///
 
/// Again: this is a temporary thing, hopefully removed once we move to a
 
/// bytecode interpreter.
 
#[derive(Clone, Debug)]
 
pub struct ValueGroup {
 
    pub(crate) values: Vec<Value>,
 
    pub(crate) regions: Vec<Vec<Value>>
 
}
 

	
 
impl ValueGroup {
 
    pub(crate) fn new_stack(values: Vec<Value>) -> Self {
 
        debug_assert!(values.iter().all(|v| v.get_heap_pos().is_none()));
 
        Self{
 
            values,
 
            regions: Vec::new(),
 
        }
 
    }
 

	
 
    pub(crate) fn from_store(store: &Store, values: &[Value]) -> Self {
 
        let mut group = ValueGroup{
 
            values: Vec::with_capacity(values.len()),
 
            regions: Vec::with_capacity(values.len()), // estimation
 
        };
 

	
 
        for value in values {
 
            let transferred = group.retrieve_value(value, store);
 
            group.values.push(transferred);
 
        }
 

	
 
        group
 
    }
 

	
 
    /// Creates a clone of the value group, but leaves the memory inside of the
 
    /// ValueGroup vectors allocated.
 
    pub(crate) fn take(&mut self) -> ValueGroup {
 
        let cloned = self.clone();
 
        self.values.clear();
 
        self.regions.clear();
 
        return cloned;
 
    }
 

	
 
    /// Transfers a provided value from a store into a local value with its
 
    /// heap allocations (if any) stored in the ValueGroup. Calling this
 
    /// function will not store the returned value in the `values` member.
 
    fn retrieve_value(&mut self, value: &Value, from_store: &Store) -> Value {
 
        let value = from_store.maybe_read_ref(value);
 
        if let Some(heap_pos) = value.get_heap_pos() {
 
            // Value points to a heap allocation, so transfer the heap values
 
            // internally.
 
            let from_region = &from_store.heap_regions[heap_pos as usize].values;
 
            let mut new_region = Vec::with_capacity(from_region.len());
 
            for value in from_region {
 
                let transferred = self.retrieve_value(value, from_store);
 
                new_region.push(transferred);
 
            }
 

	
 
            // Region is constructed, store internally and return the new value.
 
            let new_region_idx = self.regions.len() as HeapPos;
 
            self.regions.push(new_region);
 

	
 
            return match value {
 
                Value::Message(_)    => Value::Message(new_region_idx),
 
                Value::String(_)     => Value::String(new_region_idx),
 
                Value::Array(_)      => Value::Array(new_region_idx),
 
                Value::Tuple(_)      => Value::Tuple(new_region_idx),
 
                Value::Union(tag, _) => Value::Union(*tag, new_region_idx),
 
                Value::Struct(_)     => Value::Struct(new_region_idx),
 
                _ => unreachable!(),
 
            };
 
        } else {
 
            return value.clone();
 
        }
 
    }
 

	
 
    /// Transfers the heap values and the stack values into the store. Stack
 
    /// values are pushed onto the Store's stack in the order in which they
 
    /// appear in the value group.
 
    pub(crate) fn into_store(self, store: &mut Store) {
 
        for value in &self.values {
 
            let transferred = self.provide_value(value, store);
 
            store.stack.push(transferred);
 
        }
 
    }
 

	
 
    /// Transfers the heap values into the store, but will put the stack values
 
    /// into the provided `VecDeque`. This is mainly used to merge `ValueGroup`
 
    /// instances retrieved by the code by `get` calls into the expression
 
    /// stack.
 
    pub(crate) fn into_stack(self, stack: &mut VecDeque<Value>, store: &mut Store) {
 
        for value in &self.values {
 
            let transferred = self.provide_value(value, store);
 
            stack.push_back(transferred);
 
        }
 
    }
 

	
 
    fn provide_value(&self, value: &Value, to_store: &mut Store) -> Value {
 
        if let Some(from_heap_pos) = value.get_heap_pos() {
 
            let from_heap_pos = from_heap_pos as usize;
 
            let to_heap_pos = to_store.alloc_heap();
 
            let to_heap_pos_usize = to_heap_pos as usize;
 
            to_store.heap_regions[to_heap_pos_usize].values.reserve(self.regions[from_heap_pos].len());
 

	
 
            for value in &self.regions[from_heap_pos as usize] {
 
                let transferred = self.provide_value(value, to_store);
 
                to_store.heap_regions[to_heap_pos_usize].values.push(transferred);
 
            }
 

	
 
            return match value {
 
                Value::Message(_)    => Value::Message(to_heap_pos),
 
                Value::String(_)     => Value::String(to_heap_pos),
 
                Value::Array(_)      => Value::Array(to_heap_pos),
 
                Value::Tuple(_)      => Value::Tuple(to_heap_pos),
 
                Value::Union(tag, _) => Value::Union(*tag, to_heap_pos),
 
                Value::Struct(_)     => Value::Struct(to_heap_pos),
 
                _ => unreachable!(),
 
            };
 
        } else {
 
            return value.clone();
 
        }
 
    }
 
}
 

	
 
impl Default for ValueGroup {
 
    /// Returns an empty ValueGroup
 
    fn default() -> Self {
 
        Self { values: Vec::new(), regions: Vec::new() }
 
    }
 
}
 

	
 
enum ValueKind { Message, String, Array }
 

	
 
pub(crate) fn apply_assignment_operator(store: &mut Store, lhs: ValueId, op: AssignmentOperator, rhs: Value) {
 
    use AssignmentOperator as AO;
 

	
 
    macro_rules! apply_int_op {
 
        ($lhs:ident, $assignment_tokens:tt, $operator:ident, $rhs:ident) => {
 
            match $lhs {
 
                Value::UInt8(v)  => { *v $assignment_tokens $rhs.as_uint8();  },
 
                Value::UInt16(v) => { *v $assignment_tokens $rhs.as_uint16(); },
 
                Value::UInt32(v) => { *v $assignment_tokens $rhs.as_uint32(); },
 
                Value::UInt64(v) => { *v $assignment_tokens $rhs.as_uint64(); },
 
                Value::SInt8(v)  => { *v $assignment_tokens $rhs.as_sint8();  },
 
                Value::SInt16(v) => { *v $assignment_tokens $rhs.as_sint16(); },
 
                Value::SInt32(v) => { *v $assignment_tokens $rhs.as_sint32(); },
 
                Value::SInt64(v) => { *v $assignment_tokens $rhs.as_sint64(); },
 
                _ => unreachable!("apply_assignment_operator {:?} on lhs {:?} and rhs {:?}", $operator, $lhs, $rhs),
 
            }
 
        }
 
    }
 

	
 
    let lhs = store.read_mut_ref(lhs);
 

	
 
    let mut to_dealloc = None;
 
    match op {
 
        AO::Set => {
 
            match lhs {
 
                Value::Unassigned => { *lhs = rhs; },
 
                Value::Input(v)  => { *v = rhs.as_input(); },
 
                Value::Output(v) => { *v = rhs.as_output(); },
 
                Value::Message(v)  => { to_dealloc = Some(*v); *v = rhs.as_message(); },
 
                Value::Bool(v)    => { *v = rhs.as_bool(); },
 
                Value::Char(v) => { *v = rhs.as_char(); },
 
                Value::String(v) => { *v = rhs.as_string().clone(); },
 
                Value::UInt8(v) => { *v = rhs.as_uint8(); },
 
                Value::UInt16(v) => { *v = rhs.as_uint16(); },
 
                Value::UInt32(v) => { *v = rhs.as_uint32(); },
 
                Value::UInt64(v) => { *v = rhs.as_uint64(); },
 
                Value::SInt8(v) => { *v = rhs.as_sint8(); },
 
                Value::SInt16(v) => { *v = rhs.as_sint16(); },
 
                Value::SInt32(v) => { *v = rhs.as_sint32(); },
 
                Value::SInt64(v) => { *v = rhs.as_sint64(); },
 
                Value::Array(v) => { to_dealloc = Some(*v); *v = rhs.as_array(); },
 
                Value::Tuple(v) => { to_dealloc = Some(*v); *v = rhs.as_tuple(); },
 
                Value::Enum(v) => { *v = rhs.as_enum(); },
 
                Value::Union(lhs_tag, lhs_heap_pos) => {
 
                    to_dealloc = Some(*lhs_heap_pos);
 
                    let (rhs_tag, rhs_heap_pos) = rhs.as_union();
 
                    *lhs_tag = rhs_tag;
 
                    *lhs_heap_pos = rhs_heap_pos;
 
                }
 
                Value::Struct(v) => { to_dealloc = Some(*v); *v = rhs.as_struct(); },
 
                _ => unreachable!("apply_assignment_operator {:?} on lhs {:?} and rhs {:?}", op, lhs, rhs),
 
            }
 
        },
 
        AO::Concatenated => {
 
            let lhs_heap_pos = lhs.get_heap_pos().unwrap() as usize;
 
            let rhs_heap_pos = rhs.get_heap_pos().unwrap() as usize;
 

	
 
            // To prevent borrowing crap, swap out heap region with a temp empty array
 
            let mut total = Vec::new();
 
            std::mem::swap(&mut total, &mut store.heap_regions[lhs_heap_pos].values);
 

	
 
            // Push everything onto the swapped vector
 
            let rhs_len = store.heap_regions[rhs_heap_pos].values.len();
 
            total.reserve(rhs_len);
 
            for value_idx in 0..rhs_len {
 
                total.push(store.clone_value(store.heap_regions[rhs_heap_pos].values[value_idx].clone()));
 
            }
 

	
 
            // Swap back in place
 
            std::mem::swap(&mut total, &mut store.heap_regions[lhs_heap_pos].values);
 

	
 
            // We took ownership of the RHS, but we copied it into the LHS, so
 
            // different form assignment we need to drop the RHS heap pos.
 
            to_dealloc = Some(rhs_heap_pos as u32);
 
        },
 
        AO::Multiplied =>   { apply_int_op!(lhs, *=,  op, rhs) },
 
        AO::Divided =>      { apply_int_op!(lhs, /=,  op, rhs) },
 
        AO::Remained =>     { apply_int_op!(lhs, %=,  op, rhs) },
 
        AO::Added =>        { apply_int_op!(lhs, +=,  op, rhs) },
 
        AO::Subtracted =>   { apply_int_op!(lhs, -=,  op, rhs) },
 
        AO::ShiftedLeft =>  { apply_int_op!(lhs, <<=, op, rhs) },
 
        AO::ShiftedRight => { apply_int_op!(lhs, >>=, op, rhs) },
 
        AO::BitwiseAnded => { apply_int_op!(lhs, &=,  op, rhs) },
 
        AO::BitwiseXored => { apply_int_op!(lhs, ^=,  op, rhs) },
 
        AO::BitwiseOred =>  { apply_int_op!(lhs, |=,  op, rhs) },
 
    }
 

	
 
    if let Some(heap_pos) = to_dealloc {
 
        store.drop_heap_pos(heap_pos);
 
    }
 
}
 

	
 
pub(crate) fn apply_binary_operator(store: &mut Store, lhs: &Value, op: BinaryOperator, rhs: &Value) -> Value {
 
    use BinaryOperator as BO;
 

	
 
    macro_rules! apply_int_op_and_return_self {
 
        ($lhs:ident, $operator_tokens:tt, $operator:ident, $rhs:ident) => {
 
            return match $lhs {
 
                Value::UInt8(v)  => { Value::UInt8( *v $operator_tokens $rhs.as_uint8() ) },
 
                Value::UInt16(v) => { Value::UInt16(*v $operator_tokens $rhs.as_uint16()) },
 
                Value::UInt32(v) => { Value::UInt32(*v $operator_tokens $rhs.as_uint32()) },
 
                Value::UInt64(v) => { Value::UInt64(*v $operator_tokens $rhs.as_uint64()) },
src/protocol/input_source.rs
Show inline comments
 
use std::fmt;
 
use std::sync::{RwLock, RwLockReadGuard};
 
use std::fmt::Write;
 

	
 
#[derive(Debug, Clone, Copy)]
 
pub struct InputPosition {
 
    pub line: u32,
 
    pub offset: u32,
 
}
 

	
 
impl InputPosition {
 
    pub(crate) fn with_offset(&self, offset: u32) -> Self {
 
        InputPosition { line: self.line, offset: self.offset + offset }
 
    }
 
}
 

	
 
#[derive(Debug, Clone, Copy)]
 
pub struct InputSpan {
 
    pub begin: InputPosition,
 
    pub end: InputPosition,
 
}
 

	
 
impl InputSpan {
 
    // This must only be used if you're sure that the span will not be involved
 
    // in creating an error message.
 
    #[inline]
 
    pub const fn new() -> InputSpan {
 
        InputSpan{ begin: InputPosition{ line: 0, offset: 0 }, end: InputPosition{ line: 0, offset: 0 }}
 
    }
 

	
 
    #[inline]
 
    pub fn from_positions(begin: InputPosition, end: InputPosition) -> Self {
 
        Self { begin, end }
 
    }
 
}
 

	
 
/// Wrapper around source file with optional filename. Ensures that the file is
 
/// only scanned once.
 
pub struct InputSource {
 
    pub(crate) filename: String,
 
    pub(crate) input: Vec<u8>,
 
    // Iteration
 
    line: u32,
 
    offset: usize,
 
    // State tracking
 
    pub(crate) had_error: Option<ParseError>,
 
    // The offset_lookup is built on-demand upon attempting to report an error.
 
    // Only one procedure will actually create the lookup, afterwards only read
 
    // locks will be held.
 
    offset_lookup: RwLock<Vec<u32>>,
 
}
 

	
 
impl InputSource {
 
    pub fn new(filename: String, input: Vec<u8>) -> Self {
 
        Self{
 
            filename,
 
            input,
 
            line: 1,
 
            offset: 0,
 
            had_error: None,
 
            offset_lookup: RwLock::new(Vec::new()),
 
        }
 
    }
 

	
 
    #[inline]
 
    pub fn pos(&self) -> InputPosition {
 
        InputPosition { line: self.line, offset: self.offset as u32 }
 
    }
 

	
 
    pub fn next(&self) -> Option<u8> {
 
        if self.offset < self.input.len() {
 
            Some(self.input[self.offset])
 
        } else {
 
            None
 
        }
 
    }
 

	
 
    pub fn lookahead(&self, offset: usize) -> Option<u8> {
 
        let offset_pos = self.offset + offset;
 
        if offset_pos < self.input.len() {
 
            Some(self.input[offset_pos])
 
        } else {
 
            None
 
        }
 
    }
 

	
 
    #[inline]
 
    pub fn section_at_pos(&self, start: InputPosition, end: InputPosition) -> &[u8] {
 
        &self.input[start.offset as usize..end.offset as usize]
 
    }
 

	
 
    #[inline]
 
    pub fn section_at_span(&self, span: InputSpan) -> &[u8] {
 
        &self.input[span.begin.offset as usize..span.end.offset as usize]
 
    }
 

	
 
    // Consumes the next character. Will check well-formedness of newlines: \r
 
    // must be followed by a \n, because this is used for error reporting. Will
 
    // not check for ascii-ness of the file, better left to a tokenizer.
 
    pub fn consume(&mut self) {
 
        match self.next() {
 
            Some(b'\r') => {
 
                if Some(b'\n') == self.lookahead(1) {
 
                    // Well formed file
 
                    self.offset += 1;
 
                } else {
 
                    // Not a well-formed file, pretend like we can continue
 
                    self.offset += 1;
 
                    self.set_error("Encountered carriage-feed without a following newline");
 
                }
 
            },
 
            Some(b'\n') => {
 
                self.line += 1;
 
                self.offset += 1;
 
            },
 
            Some(_) => {
 
                self.offset += 1;
 
            }
 
            None => {}
 
        }
 

	
 
        // Maybe we actually want to check this in release mode. Then again:
 
        // a 4 gigabyte source file... Really?
 
        debug_assert!(self.offset < u32::max_value() as usize);
 
    }
 

	
 
    fn set_error(&mut self, msg: &str) {
 
        if self.had_error.is_none() {
 
            self.had_error = Some(ParseError::new_error_str_at_pos(self, self.pos(), msg));
 
        }
 
    }
 

	
 
    fn get_lookup(&self) -> RwLockReadGuard<Vec<u32>> {
 
        // Once constructed the lookup always contains one element. We use this
 
        // to see if it is constructed already.
 
        {
 
            let lookup = self.offset_lookup.read().unwrap();
 
            if !lookup.is_empty() {
 
                return lookup;
 
            }
 
        }
 

	
 
        // Lookup was not constructed yet
 
        let mut lookup = self.offset_lookup.write().unwrap();
 
        if !lookup.is_empty() {
 
            // Somebody created it before we had the chance
 
            drop(lookup);
 
            let lookup = self.offset_lookup.read().unwrap();
 
            return lookup;
 
        }
 

	
 
        // Build the line number (!) to offset lookup, so offset by 1. We 
 
        // assume the entire source file is scanned (most common case) for
 
        // preallocation.
 
        lookup.reserve(self.line as usize + 2);
 
        lookup.push(0); // line 0: never used
 
        lookup.push(0); // first line: first character
 

	
 
        for char_idx in 0..self.input.len() {
 
            if self.input[char_idx] == b'\n' {
 
                lookup.push(char_idx as u32 + 1);
 
            }
 
        }
 

	
 
        lookup.push(self.input.len() as u32 + 1); // for lookup_line_end, intentionally adding one character
 

	
 
        // Return created lookup
 
        drop(lookup);
 
        let lookup = self.offset_lookup.read().unwrap();
 
        return lookup;
 
    }
 

	
 
    /// Retrieves the column associated with a line. Calling this incurs a read
 
    /// lock, so don't spam it in happy-path compiler code.
 
    pub(crate) fn get_column(&self, pos: InputPosition) -> u32 {
 
        let line_start = self.lookup_line_start_offset(pos.line);
 
        return pos.offset - line_start + 1;
 
    }
 

	
 
    /// Retrieves offset at which line starts (right after newline)
 
    fn lookup_line_start_offset(&self, line_number: u32) -> u32 {
 
        let lookup = self.get_lookup();
 
        lookup[line_number as usize]
 
    }
 

	
 
    /// Retrieves offset at which line ends (at the newline character or the
 
    /// preceding carriage feed for \r\n-encoded newlines)
 
    fn lookup_line_end_offset(&self, line_number: u32) -> u32 {
 
        let lookup = self.get_lookup();
 
        let offset = lookup[(line_number + 1) as usize] - 1;
 
        let offset_usize = offset as usize;
 

	
 
        // Compensate for newlines and a potential carriage feed. Note that the
 
        // end position is exclusive. So we only need to compensate for a
 
        // "\r\n"
 
        if offset_usize > 0 && offset_usize < self.input.len() && self.input[offset_usize] == b'\n' && self.input[offset_usize - 1] == b'\r' {
 
            offset - 1
 
        } else {
 
            offset
 
        }
 
    }
 
}
 

	
 
#[derive(Debug)]
 
pub enum StatementKind {
 
    Info,
 
    Error
 
}
 

	
 
#[derive(Debug)]
 
pub enum ContextKind {
 
    SingleLine,
 
    MultiLine,
 
}
 

	
 
#[derive(Debug)]
 
pub struct ErrorStatement {
 
    pub(crate) statement_kind: StatementKind,
 
    pub(crate) context_kind: ContextKind,
 
    pub(crate) start_line: u32,
 
    pub(crate) start_column: u32,
 
    pub(crate) end_line: u32,
 
    pub(crate) end_column: u32,
 
    pub(crate) filename: String,
 
    pub(crate) context: String,
 
    pub(crate) message: String,
 
}
 

	
 
impl ErrorStatement {
 
    fn from_source_at_pos(statement_kind: StatementKind, source: &InputSource, position: InputPosition, message: String) -> Self {
 
        // Seek line start and end
 
        let line_start = source.lookup_line_start_offset(position.line);
 
        let line_end = source.lookup_line_end_offset(position.line);
 
        let context = Self::create_context(source, line_start as usize, line_end as usize);
 
        debug_assert!(position.offset >= line_start);
 
        let column = position.offset - line_start + 1;
 

	
 
        Self{
 
            statement_kind,
 
            context_kind: ContextKind::SingleLine,
 
            start_line: position.line,
 
            start_column: column,
 
            end_line: position.line,
 
            end_column: column + 1,
 
            filename: source.filename.clone(),
 
            context,
 
            message,
 
        }
 
    }
 

	
 
    pub(crate) fn from_source_at_span(statement_kind: StatementKind, source: &InputSource, span: InputSpan, message: String) -> Self {
 
        debug_assert!(span.end.line >= span.begin.line);
 
        debug_assert!(span.end.offset >= span.begin.offset);
 

	
 
        let first_line_start = source.lookup_line_start_offset(span.begin.line);
 
        let last_line_start = source.lookup_line_start_offset(span.end.line);
 
        let last_line_end = source.lookup_line_end_offset(span.end.line);
 
        let context = Self::create_context(source, first_line_start as usize, last_line_end as usize);
 
        debug_assert!(span.begin.offset >= first_line_start);
 
        let start_column = span.begin.offset - first_line_start + 1;
 
        let end_column = span.end.offset - last_line_start + 1;
 

	
 
        let context_kind = if span.begin.line == span.end.line {
 
            ContextKind::SingleLine
 
        } else {
 
            ContextKind::MultiLine
 
        };
 

	
 
        Self{
 
            statement_kind,
 
            context_kind,
 
            start_line: span.begin.line,
 
            start_column,
 
            end_line: span.end.line,
 
            end_column,
 
            filename: source.filename.clone(),
 
            context,
 
            message,
 
        }
 
    }
 

	
 
    /// Produces context from source
 
    fn create_context(source: &InputSource, start: usize, end: usize) -> String {
 
        let context_raw = &source.input[start..end];
 
        String::from_utf8_lossy(context_raw).to_string()
 
    }
 
}
 

	
 
impl fmt::Display for ErrorStatement {
 
    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
 
        // Write kind of statement and message
 
        match self.statement_kind {
 
            StatementKind::Info => f.write_str(" INFO: ")?,
 
            StatementKind::Error => f.write_str("ERROR: ")?,
 
        }
 
        f.write_str(&self.message)?;
 
        f.write_char('\n')?;
 

	
 
        // Write originating file/line/column
 
        f.write_str(" +- ")?;
 
        if !self.filename.is_empty() {
 
            write!(f, "in {} ", self.filename)?;
 
        }
 

	
 
        match self.context_kind {
 
            ContextKind::SingleLine => writeln!(f, " at {}:{}", self.start_line, self.start_column),
 
            ContextKind::MultiLine => writeln!(
 
                f, " from {}:{} to {}:{}",
 
                self.start_line, self.start_column, self.end_line, self.end_column
 
            )
 
        }?;
 

	
 
        // Helper function for writing context: converting tabs into 4 spaces
 
        // (oh, the controversy!) and creating an annotated line
 
        fn transform_context(source: &str, target: &mut String) {
 
            for char in source.chars() {
 
                if char == '\t' {
 
                    target.push_str("    ");
 
                } else {
 
                    target.push(char);
 
                }
 
            }
 
        }
 

	
 
        fn extend_annotation(first_col: u32, last_col: u32, source: &str, target: &mut String, extend_char: char) {
 
            debug_assert!(first_col > 0 && last_col > first_col);
 

	
 
            // If the first index exceeds the size of the context then we should
 
            // have a message placed at the newline character
 
            let first_idx = first_col as usize - 1;
 
            let last_idx = last_col as usize - 1;
 
            if first_idx >= source.len() {
 
                // If any of these fail then the logic behind reporting errors
 
                // is incorrect.
 
                debug_assert_eq!(first_idx, source.len());
 
                debug_assert_eq!(first_idx + 1, last_idx);
 
                target.push(extend_char);
 
            } else {
 
                for (char_idx, char) in source.chars().enumerate().skip(first_idx) {
 
                    if char_idx == last_idx as usize {
 
                        break;
 
                    }
 

	
 
                    if char == '\t' {
 
                        for _ in 0..4 { target.push(extend_char); }
 
                    } else {
 
                        target.push(extend_char);
 
                    }
 
                }
 
            }
 
        }
 

	
 
        // Write source context
 
        writeln!(f, " | ")?;
 

	
 
        let mut context = String::with_capacity(128);
 
        let mut annotation = String::with_capacity(128);
 

	
 
        match self.context_kind {
 
            ContextKind::SingleLine => {
 
                // Write single line of context with indicator for the offending
 
                // span underneath.
 
                context.push_str(" |  ");
 
                transform_context(&self.context, &mut context);
 
                context.push('\n');
 
                f.write_str(&context)?;
 

	
 
                annotation.push_str(" | ");
 
                extend_annotation(1, self.start_column + 1, &self.context, &mut annotation, ' ');
 
                extend_annotation(self.start_column, self.end_column, &self.context, &mut annotation, '~');
 
                annotation.push('\n');
src/protocol/mod.rs
Show inline comments
 
mod arena;
 
pub(crate) mod eval;
 
pub(crate) mod input_source;
 
mod parser;
 
#[cfg(test)] mod tests;
 

	
 
pub(crate) mod ast;
 
pub(crate) mod ast_printer;
 
pub(crate) mod ast_writer;
 
mod token_writer;
 

	
 
use std::sync::Mutex;
 

	
 
use crate::collections::{StringPool, StringRef};
 
use crate::protocol::ast::*;
 
pub use crate::protocol::ast::*;
 
use crate::protocol::eval::*;
 
use crate::protocol::input_source::*;
 
use crate::protocol::parser::*;
 
use crate::protocol::type_table::*;
 

	
 
pub use parser::type_table::TypeId;
 

	
 
/// A protocol description module
 
pub struct Module {
 
    pub(crate) source: InputSource,
 
    pub(crate) root_id: RootId,
 
    pub(crate) name: Option<StringRef<'static>>,
 
}
 
/// Description of a protocol object, used to configure new connectors.
 
#[repr(C)]
 
pub struct ProtocolDescription {
 
    pub(crate) modules: Vec<Module>,
 
    pub(crate) heap: Heap,
 
    pub(crate) types: TypeTable,
 
    pub(crate) pool: Mutex<StringPool>,
 
}
 
#[derive(Debug, Clone)]
 
pub(crate) struct ComponentState {
 
    pub(crate) prompt: Prompt,
 
}
 

	
 
#[derive(Debug)]
 
pub enum ComponentCreationError {
 
    ModuleDoesntExist,
 
    DefinitionDoesntExist,
 
    DefinitionNotComponent,
 
    InvalidNumArguments,
 
    InvalidArgumentType(usize),
 
    UnownedPort,
 
    InSync,
 
}
 

	
 
impl ProtocolDescription {
 
    pub fn parse(buffer: &[u8]) -> Result<Self, String> {
 
        let source = InputSource::new(String::new(), Vec::from(buffer));
 
        let mut parser = Parser::new();
 
        let mut parser = Parser::new(None)?;
 
        parser.feed(source).expect("failed to feed source");
 
        
 
        if let Err(err) = parser.parse() {
 
            println!("ERROR:\n{}", err);
 
            return Err(format!("{}", err))
 
        }
 

	
 
        debug_assert_eq!(parser.modules.len(), 1, "only supporting one module here for now");
 
        let modules: Vec<Module> = parser.modules.into_iter()
 
            .map(|module| Module{
 
                source: module.source,
 
                root_id: module.root_id,
 
                name: module.name.map(|(_, name)| name)
 
            })
 
            .collect();
 

	
 
        return Ok(ProtocolDescription {
 
            modules,
 
            heap: parser.heap,
 
            types: parser.type_table,
 
            pool: Mutex::new(parser.string_pool),
 
        });
 
    }
 

	
 
    pub(crate) fn new_component(
 
        &self, module_name: &[u8], identifier: &[u8], arguments: ValueGroup
 
    ) -> Result<Prompt, ComponentCreationError> {
 
        // Find the module in which the definition can be found
 
        let module_root = self.lookup_module_root(module_name);
 
        if module_root.is_none() {
 
            return Err(ComponentCreationError::ModuleDoesntExist);
 
        }
 
        let module_root = module_root.unwrap();
 

	
 
        let root = &self.heap[module_root];
 
        let definition_id = root.get_definition_ident(&self.heap, identifier);
 
        let definition_id = root.get_definition_by_ident(&self.heap, identifier);
 
        if definition_id.is_none() {
 
            return Err(ComponentCreationError::DefinitionDoesntExist);
 
        }
 
        let definition_id = definition_id.unwrap();
 

	
 
        let ast_definition = &self.heap[definition_id];
 
        if !ast_definition.is_procedure() {
 
            return Err(ComponentCreationError::DefinitionNotComponent);
 
        }
 

	
 
        // Make sure that the types of the provided value group matches that of
 
        // the expected types.
 
        let ast_definition = ast_definition.as_procedure();
 
        if !ast_definition.poly_vars.is_empty() || ast_definition.kind == ProcedureKind::Function {
 
            return Err(ComponentCreationError::DefinitionNotComponent);
 
        }
 

	
 
        // - check number of arguments by retrieving the one instantiated
 
        //   monomorph
 
        let concrete_type = ConcreteType{ parts: vec![ConcreteTypePart::Component(ast_definition.this, 0)] };
 
        let procedure_type_id = self.types.get_procedure_monomorph_type_id(&definition_id, &concrete_type.parts).unwrap();
 
        let procedure_type_id = self.types.get_monomorph_type_id(&definition_id, &concrete_type.parts).unwrap();
 
        let procedure_monomorph_index = self.types.get_monomorph(procedure_type_id).variant.as_procedure().monomorph_index;
 
        let monomorph_info = &ast_definition.monomorphs[procedure_monomorph_index as usize];
 
        if monomorph_info.argument_types.len() != arguments.values.len() {
 
            return Err(ComponentCreationError::InvalidNumArguments);
 
        }
 

	
 
        // - for each argument try to make sure the types match
 
        for arg_idx in 0..arguments.values.len() {
 
            let expected_type_id = monomorph_info.argument_types[arg_idx];
 
            let expected_type = &self.types.get_monomorph(expected_type_id).concrete_type;
 
            let provided_value = &arguments.values[arg_idx];
 
            if !self.verify_same_type(expected_type, 0, &arguments, provided_value) {
 
                return Err(ComponentCreationError::InvalidArgumentType(arg_idx));
 
            }
 
        }
 

	
 
        // By now we're sure that all of the arguments are correct. So create
 
        // the connector.
 
        return Ok(Prompt::new(&self.types, &self.heap, ast_definition.this, procedure_type_id, arguments));
 
    }
 

	
 
    /// A somewhat temporary method. Can be used by components to lookup type
 
    /// definitions by their name (to have their implementation somewhat
 
    /// resistant to changes in the standard library)
 
    pub(crate) fn find_type<'a>(&'a self, module_name: &[u8], type_name: &[u8]) -> Option<TypeInspector<'a>> {
 
        // Lookup type definition in module
 
        let root_id = self.lookup_module_root(module_name)?;
 
        let module = &self.heap[root_id];
 
        let definition_id = module.get_definition_by_ident(&self.heap, type_name)?;
 
        let definition = &self.heap[definition_id];
 

	
 
        // Make sure type is not polymorphic and is not a procedure
 
        if !definition.poly_vars().is_empty() {
 
            return None;
 
        }
 
        if definition.is_procedure() {
 
            return None;
 
        }
 

	
 
        // Lookup type in type table
 
        let type_parts = [ConcreteTypePart::Instance(definition_id, 0)];
 
        let type_id = self.types.get_monomorph_type_id(&definition_id, &type_parts)
 
            .expect("type ID for non-polymorphic type");
 
        let type_monomorph = self.types.get_monomorph(type_id);
 

	
 
        return Some(TypeInspector{
 
            heap: definition,
 
            type_table: type_monomorph
 
        });
 
    }
 

	
 
    fn lookup_module_root(&self, module_name: &[u8]) -> Option<RootId> {
 
        for module in self.modules.iter() {
 
            match &module.name {
 
                Some(name) => if name.as_bytes() == module_name {
 
                    return Some(module.root_id);
 
                },
 
                None => if module_name.is_empty() {
 
                    return Some(module.root_id);
 
                }
 
            }
 
        }
 

	
 
        return None;
 
    }
 

	
 
    fn verify_same_type(&self, expected: &ConcreteType, expected_idx: usize, arguments: &ValueGroup, argument: &Value) -> bool {
 
        use ConcreteTypePart as CTP;
 

	
 
        match &expected.parts[expected_idx] {
 
            CTP::Void | CTP::Message | CTP::Slice | CTP::Pointer | CTP::Function(_, _) | CTP::Component(_, _) => unreachable!(),
 
            CTP::Bool => if let Value::Bool(_) = argument { true } else { false },
 
            CTP::UInt8 => if let Value::UInt8(_) = argument { true } else { false },
 
            CTP::UInt16 => if let Value::UInt16(_) = argument { true } else { false },
 
            CTP::UInt32 => if let Value::UInt32(_) = argument { true } else { false },
 
            CTP::UInt64 => if let Value::UInt64(_) = argument { true } else { false },
 
            CTP::SInt8 => if let Value::SInt8(_) = argument { true } else { false },
 
            CTP::SInt16 => if let Value::SInt16(_) = argument { true } else { false },
 
            CTP::SInt32 => if let Value::SInt32(_) = argument { true } else { false },
 
            CTP::SInt64 => if let Value::SInt64(_) = argument { true } else { false },
 
            CTP::Character => if let Value::Char(_) = argument { true } else { false },
 
            CTP::String => {
 
                // Match outer string type and embedded character types
 
                if let Value::String(heap_pos) = argument {
 
                    for element in &arguments.regions[*heap_pos as usize] {
 
                        if let Value::Char(_) = element {} else {
 
                            return false;
 
                        }
 
                    }
 
                } else {
 
                    return false;
 
                }
 

	
 
                return true;
 
            },
 
            CTP::Array => {
 
                if let Value::Array(heap_pos) = argument {
 
                    let heap_pos = *heap_pos;
 
                    for element in &arguments.regions[heap_pos as usize] {
 
                        if !self.verify_same_type(expected, expected_idx + 1, arguments, element) {
 
                            return false;
 
                        }
 
                    }
 
                    return true;
 
                } else {
 
                    return false;
 
                }
 
            },
 
            CTP::Input => if let Value::Input(_) = argument { true } else { false },
 
            CTP::Output => if let Value::Output(_) = argument { true } else { false },
 
            CTP::Tuple(_) => todo!("implement full type checking on user-supplied arguments"),
 
            CTP::Instance(definition_id, _num_embedded) => {
 
                let definition = self.types.get_base_definition(definition_id).unwrap();
 
                match &definition.definition {
 
                    DefinedTypeVariant::Enum(definition) => {
 
                        if let Value::Enum(variant_value) = argument {
 
                            let is_valid = definition.variants.iter()
 
                                .any(|v| v.value == *variant_value);
 
                            return is_valid;
 
                        }
 
                    },
 
                    _ => todo!("implement full type checking on user-supplied arguments"),
 
                }
 

	
 
                return false;
 
            },
 
        }
 
    }
 
}
 

	
 
pub trait RunContext {
 
    fn performed_put(&mut self, port: PortId) -> bool;
 
    fn performed_get(&mut self, port: PortId) -> Option<ValueGroup>; // None if still waiting on message
 
    fn fires(&mut self, port: PortId) -> Option<Value>; // None if not yet branched
 
    fn performed_fork(&mut self) -> Option<bool>; // None if not yet forked
 
    fn created_channel(&mut self) -> Option<(Value, Value)>; // None if not yet prepared
 
    fn performed_select_wait(&mut self) -> Option<u32>; // None if not yet notified runtime of select blocker
 
}
 

	
 
pub struct ProtocolDescriptionBuilder {
 
    parser: Parser,
 
}
 

	
 
impl ProtocolDescriptionBuilder {
 
    pub fn new() -> Self {
 
        return Self{
 
            parser: Parser::new(),
 
        }
 
    pub fn new(std_lib_dir: Option<String>) -> Result<Self, String> {
 
        let mut parser = Parser::new(std_lib_dir)?;
 
        return Ok(Self{ parser })
 
    }
 

	
 
    pub fn add(&mut self, filename: String, buffer: Vec<u8>) -> Result<(), ParseError> {
 
        let input = InputSource::new(filename, buffer);
 
        self.parser.feed(input)?;
 

	
 
        return Ok(())
 
    }
 

	
 
    pub fn compile(mut self) -> Result<ProtocolDescription, ParseError> {
 
        self.parser.parse()?;
 

	
 
        let modules: Vec<Module> = self.parser.modules.into_iter()
 
            .map(|module| Module{
 
                source: module.source,
 
                root_id: module.root_id,
 
                name: module.name.map(|(_, name)| name)
 
            })
 
            .collect();
 

	
 
        return Ok(ProtocolDescription {
 
            modules,
 
            heap: self.parser.heap,
 
            types: self.parser.type_table,
 
            pool: Mutex::new(self.parser.string_pool),
 
        });
 
    }
 
}
 

	
 
pub struct TypeInspector<'a> {
 
    heap: &'a Definition,
 
    type_table: &'a MonoType,
 
}
 

	
 
impl<'a> TypeInspector<'a> {
 
    pub fn as_union(&'a self) -> UnionTypeInspector<'a> {
 
        let heap = self.heap.as_union();
 
        let type_table = self.type_table.variant.as_union();
 
        return UnionTypeInspector{ heap, type_table };
 
    }
 
}
 

	
 
pub struct UnionTypeInspector<'a> {
 
    heap: &'a UnionDefinition,
 
    type_table: &'a UnionMonomorph,
 
}
 

	
 
impl UnionTypeInspector<'_> {
 
    /// Retrieves union variant tag value.
 
    pub fn get_variant_tag_value(&self, variant_name: &[u8]) -> Option<i64> {
 
        let variant_index = self.heap.variants.iter()
 
            .position(|v| v.identifier.value.as_bytes() == variant_name)?;
 
        return Some(variant_index as i64);
 
    }
 
}
 
\ No newline at end of file
src/protocol/parser/mod.rs
Show inline comments
 
#[macro_use] mod visitor;
 
pub(crate) mod symbol_table;
 
pub(crate) mod type_table;
 
pub(crate) mod tokens;
 
pub(crate) mod token_parsing;
 
pub(crate) mod pass_tokenizer;
 
pub(crate) mod pass_symbols;
 
pub(crate) mod pass_imports;
 
pub(crate) mod pass_definitions;
 
pub(crate) mod pass_definitions_types;
 
pub(crate) mod pass_validation_linking;
 
pub(crate) mod pass_rewriting;
 
pub(crate) mod pass_typing;
 
pub(crate) mod pass_stack_size;
 

	
 
use tokens::*;
 
use crate::collections::*;
 
use visitor::Visitor;
 
use pass_tokenizer::PassTokenizer;
 
use pass_symbols::PassSymbols;
 
use pass_imports::PassImport;
 
use pass_definitions::PassDefinitions;
 
use pass_validation_linking::PassValidationLinking;
 
use pass_typing::{PassTyping, ResolveQueue};
 
use pass_rewriting::PassRewriting;
 
use pass_stack_size::PassStackSize;
 
use symbol_table::*;
 
use type_table::*;
 

	
 
use crate::protocol::ast::*;
 
use crate::protocol::input_source::*;
 

	
 
use crate::protocol::ast_printer::ASTWriter;
 
use crate::protocol::ast_writer::ASTWriter;
 
use crate::protocol::parser::type_table::PolymorphicVariable;
 
use crate::protocol::token_writer::TokenWriter;
 

	
 
const REOWOLF_PATH_ENV: &'static str = "REOWOLF_ROOT"; // first lookup reowolf path
 
const REOWOLF_PATH_DIR: &'static str = "std"; // then try folder in current working directory
 

	
 
#[derive(Debug, PartialEq, Eq, PartialOrd, Ord)]
 
pub enum ModuleCompilationPhase {
 
    Tokenized,              // source is tokenized
 
    SymbolsScanned,         // all definitions are linked to their type class
 
    ImportsResolved,        // all imports are added to the symbol table
 
    DefinitionsParsed,      // produced the AST for the entire module
 
    TypesAddedToTable,      // added all definitions to the type table
 
    ValidatedAndLinked,     // AST is traversed and has linked the required AST nodes
 
    Typed,                  // Type inference and checking has been performed
 
    Rewritten,              // Special AST nodes are rewritten into regular AST nodes
 
    // When we continue with the compiler:
 
    // StackSize
 
}
 

	
 
pub struct Module {
 
    // Buffers
 
    pub source: InputSource,
 
    pub tokens: TokenBuffer,
 
    // Identifiers
 
    pub is_compiler_file: bool, // TODO: @Hack for custom compiler-only types
 
    pub add_to_global_namespace: bool,
 
    pub root_id: RootId,
 
    pub name: Option<(PragmaId, StringRef<'static>)>,
 
    pub version: Option<(PragmaId, i64)>,
 
    pub phase: ModuleCompilationPhase,
 
}
 

	
 
pub struct TargetArch {
 
    pub void_type_id: TypeId,
 
    pub message_type_id: TypeId,
 
    pub bool_type_id: TypeId,
 
    pub uint8_type_id: TypeId,
 
    pub uint16_type_id: TypeId,
 
    pub uint32_type_id: TypeId,
 
    pub uint64_type_id: TypeId,
 
    pub sint8_type_id: TypeId,
 
    pub sint16_type_id: TypeId,
 
    pub sint32_type_id: TypeId,
 
    pub sint64_type_id: TypeId,
 
    pub char_type_id: TypeId,
 
    pub string_type_id: TypeId,
 
    pub array_type_id: TypeId,
 
    pub slice_type_id: TypeId,
 
    pub input_type_id: TypeId,
 
    pub output_type_id: TypeId,
 
    pub pointer_type_id: TypeId,
 
}
 

	
 
impl TargetArch {
 
    fn new() -> Self {
 
        return Self{
 
            void_type_id: TypeId::new_invalid(),
 
            bool_type_id: TypeId::new_invalid(),
 
            message_type_id: TypeId::new_invalid(),
 
            uint8_type_id: TypeId::new_invalid(),
 
            uint16_type_id: TypeId::new_invalid(),
 
            uint32_type_id: TypeId::new_invalid(),
 
            uint64_type_id: TypeId::new_invalid(),
 
            sint8_type_id: TypeId::new_invalid(),
 
            sint16_type_id: TypeId::new_invalid(),
 
            sint32_type_id: TypeId::new_invalid(),
 
            sint64_type_id: TypeId::new_invalid(),
 
            char_type_id: TypeId::new_invalid(),
 
            string_type_id: TypeId::new_invalid(),
 
            array_type_id: TypeId::new_invalid(),
 
            slice_type_id: TypeId::new_invalid(),
 
            input_type_id: TypeId::new_invalid(),
 
            output_type_id: TypeId::new_invalid(),
 
            pointer_type_id: TypeId::new_invalid(),
 
        }
 
    }
 
}
 

	
 
pub struct PassCtx<'a> {
 
    heap: &'a mut Heap,
 
    symbols: &'a mut SymbolTable,
 
    pool: &'a mut StringPool,
 
    arch: &'a TargetArch,
 
}
 

	
 
pub struct Parser {
 
    // Storage of all information created/gathered during compilation.
 
    pub(crate) heap: Heap,
 
    pub(crate) string_pool: StringPool, // Do not deallocate, holds all strings
 
    pub(crate) modules: Vec<Module>,
 
    pub(crate) symbol_table: SymbolTable,
 
    pub(crate) type_table: TypeTable,
 
    pub(crate) global_module_index: usize, // contains globals, implicitly imported everywhere
 
    // Compiler passes, used as little state machine that keep their memory
 
    // around.
 
    pass_tokenizer: PassTokenizer,
 
    pass_symbols: PassSymbols,
 
    pass_import: PassImport,
 
    pass_definitions: PassDefinitions,
 
    pass_validation: PassValidationLinking,
 
    pass_typing: PassTyping,
 
    pass_rewriting: PassRewriting,
 
    pass_stack_size: PassStackSize,
 
    // Compiler options
 
    pub write_tokens_to: Option<String>,
 
    pub write_ast_to: Option<String>,
 
    pub std_lib_dir: Option<String>,
 
    pub(crate) arch: TargetArch,
 
}
 

	
 
impl Parser {
 
    pub fn new() -> Self {
 
    pub fn new(std_lib_dir: Option<String>) -> Result<Self, String> {
 
        let mut parser = Parser{
 
            heap: Heap::new(),
 
            string_pool: StringPool::new(),
 
            modules: Vec::new(),
 
            symbol_table: SymbolTable::new(),
 
            type_table: TypeTable::new(),
 
            global_module_index: 0,
 
            pass_tokenizer: PassTokenizer::new(),
 
            pass_symbols: PassSymbols::new(),
 
            pass_import: PassImport::new(),
 
            pass_definitions: PassDefinitions::new(),
 
            pass_validation: PassValidationLinking::new(),
 
            pass_typing: PassTyping::new(),
 
            pass_rewriting: PassRewriting::new(),
 
            pass_stack_size: PassStackSize::new(),
 
            write_tokens_to: None,
 
            write_ast_to: None,
 
            std_lib_dir,
 
            arch: TargetArch::new(),
 
        };
 

	
 
        parser.symbol_table.insert_scope(None, SymbolScope::Global);
 

	
 
        // Insert builtin types
 
        // TODO: At some point use correct values for size/alignment
 
        parser.arch.void_type_id    = insert_builtin_type(&mut parser.type_table, vec![ConcreteTypePart::Void], false, 0, 1);
 
        parser.arch.message_type_id = insert_builtin_type(&mut parser.type_table, vec![ConcreteTypePart::Message], false, 24, 8);
 
        parser.arch.bool_type_id    = insert_builtin_type(&mut parser.type_table, vec![ConcreteTypePart::Bool], false, 1, 1);
 
        parser.arch.uint8_type_id   = insert_builtin_type(&mut parser.type_table, vec![ConcreteTypePart::UInt8], false, 1, 1);
 
        parser.arch.uint16_type_id  = insert_builtin_type(&mut parser.type_table, vec![ConcreteTypePart::UInt16], false, 2, 2);
 
        parser.arch.uint32_type_id  = insert_builtin_type(&mut parser.type_table, vec![ConcreteTypePart::UInt32], false, 4, 4);
 
        parser.arch.uint64_type_id  = insert_builtin_type(&mut parser.type_table, vec![ConcreteTypePart::UInt64], false, 8, 8);
 
        parser.arch.sint8_type_id   = insert_builtin_type(&mut parser.type_table, vec![ConcreteTypePart::SInt8], false, 1, 1);
 
        parser.arch.sint16_type_id  = insert_builtin_type(&mut parser.type_table, vec![ConcreteTypePart::SInt16], false, 2, 2);
 
        parser.arch.sint32_type_id  = insert_builtin_type(&mut parser.type_table, vec![ConcreteTypePart::SInt32], false, 4, 4);
 
        parser.arch.sint64_type_id  = insert_builtin_type(&mut parser.type_table, vec![ConcreteTypePart::SInt64], false, 8, 8);
 
        parser.arch.char_type_id    = insert_builtin_type(&mut parser.type_table, vec![ConcreteTypePart::Character], false, 4, 4);
 
        parser.arch.string_type_id  = insert_builtin_type(&mut parser.type_table, vec![ConcreteTypePart::String], false, 24, 8);
 
        parser.arch.array_type_id   = insert_builtin_type(&mut parser.type_table, vec![ConcreteTypePart::Array, ConcreteTypePart::Void], true, 24, 8);
 
        parser.arch.slice_type_id   = insert_builtin_type(&mut parser.type_table, vec![ConcreteTypePart::Slice, ConcreteTypePart::Void], true, 16, 4);
 
        parser.arch.input_type_id   = insert_builtin_type(&mut parser.type_table, vec![ConcreteTypePart::Input, ConcreteTypePart::Void], true, 8, 8);
 
        parser.arch.output_type_id  = insert_builtin_type(&mut parser.type_table, vec![ConcreteTypePart::Output, ConcreteTypePart::Void], true, 8, 8);
 
        parser.arch.pointer_type_id = insert_builtin_type(&mut parser.type_table, vec![ConcreteTypePart::Pointer, ConcreteTypePart::Void], true, 8, 8);
 

	
 
        // Insert builtin functions
 
        fn quick_type(variants: &[ParserTypeVariant]) -> ParserType {
 
            let mut t = ParserType{ elements: Vec::with_capacity(variants.len()), full_span: InputSpan::new() };
 
            for variant in variants {
 
                t.elements.push(ParserTypeElement{ element_span: InputSpan::new(), variant: variant.clone() });
 
            }
 
            t
 
        }
 
        // Parse standard library
 
        parser.feed_standard_library()?;
 

	
 
        use ParserTypeVariant as PTV;
 
        insert_builtin_function(&mut parser, "get", &["T"], |id| (
 
            vec![
 
                ("input", quick_type(&[PTV::Input, PTV::PolymorphicArgument(id.upcast(), 0)]))
 
            ],
 
            quick_type(&[PTV::PolymorphicArgument(id.upcast(), 0)])
 
        ));
 
        insert_builtin_function(&mut parser, "put", &["T"], |id| (
 
            vec![
 
                ("output", quick_type(&[PTV::Output, PTV::PolymorphicArgument(id.upcast(), 0)])),
 
                ("value", quick_type(&[PTV::PolymorphicArgument(id.upcast(), 0)])),
 
            ],
 
            quick_type(&[PTV::Void])
 
        ));
 
        insert_builtin_function(&mut parser, "fires", &["T"], |id| (
 
            vec![
 
                ("port", quick_type(&[PTV::InputOrOutput, PTV::PolymorphicArgument(id.upcast(), 0)]))
 
            ],
 
            quick_type(&[PTV::Bool])
 
        ));
 
        insert_builtin_function(&mut parser, "create", &["T"], |id| (
 
            vec![
 
                ("length", quick_type(&[PTV::IntegerLike]))
 
            ],
 
            quick_type(&[PTV::ArrayLike, PTV::PolymorphicArgument(id.upcast(), 0)])
 
        ));
 
        insert_builtin_function(&mut parser, "length", &["T"], |id| (
 
            vec![
 
                ("array", quick_type(&[PTV::ArrayLike, PTV::PolymorphicArgument(id.upcast(), 0)]))
 
            ],
 
            quick_type(&[PTV::UInt32]) // TODO: @PtrInt
 
        ));
 
        insert_builtin_function(&mut parser, "assert", &[], |_id| (
 
            vec![
 
                ("condition", quick_type(&[PTV::Bool])),
 
            ],
 
            quick_type(&[PTV::Void])
 
        ));
 
        insert_builtin_function(&mut parser, "print", &[], |_id| (
 
            vec![
 
                ("message", quick_type(&[PTV::String])),
 
            ],
 
            quick_type(&[PTV::Void])
 
        ));
 

	
 
        parser
 
        return Ok(parser)
 
    }
 

	
 
    pub fn feed(&mut self, mut source: InputSource) -> Result<(), ParseError> {
 
        let mut token_buffer = TokenBuffer::new();
 
        self.pass_tokenizer.tokenize(&mut source, &mut token_buffer)?;
 

	
 
        let module = Module{
 
            source,
 
            tokens: token_buffer,
 
            root_id: RootId::new_invalid(),
 
            name: None,
 
            version: None,
 
            phase: ModuleCompilationPhase::Tokenized,
 
        };
 
        self.modules.push(module);
 

	
 
        Ok(())
 
    /// Feeds a new InputSource to the parser, which will tokenize it and store
 
    /// it internally for later parsing (when all modules are present). Returns
 
    /// the index of the new module.
 
    pub fn feed(&mut self, mut source: InputSource) -> Result<usize, ParseError> {
 
        return self.feed_internal(source, false, false);
 
    }
 

	
 
    pub fn parse(&mut self) -> Result<(), ParseError> {
 
        let mut pass_ctx = PassCtx{
 
            heap: &mut self.heap,
 
            symbols: &mut self.symbol_table,
 
            pool: &mut self.string_pool,
 
            arch: &self.arch,
 
        };
 

	
 
        // Advance all modules to the phase where all symbols are scanned
 
        for module_idx in 0..self.modules.len() {
 
            self.pass_symbols.parse(&mut self.modules, module_idx, &mut pass_ctx)?;
 
        }
 

	
 
        // With all symbols scanned, perform further compilation until we can
 
        // add all base types to the type table.
 
        for module_idx in 0..self.modules.len() {
 
            self.pass_import.parse(&mut self.modules, module_idx, &mut pass_ctx)?;
 
            self.pass_definitions.parse(&mut self.modules, module_idx, &mut pass_ctx)?;
 
        }
 

	
 
        if let Some(filename) = &self.write_tokens_to {
 
            let mut writer = TokenWriter::new();
 
            let mut file = std::fs::File::create(std::path::Path::new(filename)).unwrap();
 
            writer.write(&mut file, &self.modules);
 
        }
 

	
 
        // Add every known type to the type table
 
        self.type_table.build_base_types(&mut self.modules, &mut pass_ctx)?;
 

	
 
        // Continue compilation with the remaining phases now that the types
 
        // are all in the type table
 
        for module_idx in 0..self.modules.len() {
 
            let mut ctx = visitor::Ctx{
 
                heap: &mut self.heap,
 
                modules: &mut self.modules,
 
                module_idx,
 
                symbols: &mut self.symbol_table,
 
                types: &mut self.type_table,
 
                arch: &self.arch,
 
            };
 
            self.pass_validation.visit_module(&mut ctx)?;
 
        }
 

	
 
        // Perform typechecking on all modules
 
        let mut queue = ResolveQueue::new();
 
        for module_idx in 0..self.modules.len() {
 
            let mut ctx = visitor::Ctx{
 
                heap: &mut self.heap,
 
                modules: &mut self.modules,
 
                module_idx,
 
                symbols: &mut self.symbol_table,
 
                types: &mut self.type_table,
 
                arch: &self.arch,
 
            };
 
            self.pass_typing.queue_module_definitions(&mut ctx, &mut queue);
 
        };
 
        while !queue.is_empty() {
 
            let top = queue.pop_front().unwrap();
 
            let mut ctx = visitor::Ctx{
 
                heap: &mut self.heap,
 
                modules: &mut self.modules,
 
                module_idx: top.root_id.index as usize,
 
                symbols: &mut self.symbol_table,
 
                types: &mut self.type_table,
 
                arch: &self.arch,
 
            };
 
            self.pass_typing.handle_module_definition(&mut ctx, &mut queue, top)?;
 
        }
 

	
 
        // Rewrite nodes in tree, then prepare for execution of code
 
        for module_idx in 0..self.modules.len() {
 
            self.modules[module_idx].phase = ModuleCompilationPhase::Typed;
 
            let mut ctx = visitor::Ctx{
 
                heap: &mut self.heap,
 
                modules: &mut self.modules,
 
                module_idx,
 
                symbols: &mut self.symbol_table,
 
                types: &mut self.type_table,
 
                arch: &self.arch,
 
            };
 
            self.pass_rewriting.visit_module(&mut ctx)?;
 
            self.pass_stack_size.visit_module(&mut ctx)?;
 
        }
 

	
 
        // Write out desired information
 
        if let Some(filename) = &self.write_ast_to {
 
            let mut writer = ASTWriter::new();
 
            let mut file = std::fs::File::create(std::path::Path::new(filename)).unwrap();
 
            writer.write_ast(&mut file, &self.heap);
 
        }
 

	
 
        Ok(())
 
    }
 

	
 
    /// Tries to find the standard library and add the files for parsing.
 
    fn feed_standard_library(&mut self) -> Result<(), String> {
 
        use std::env;
 
        use std::path::{Path, PathBuf};
 
        use std::fs;
 

	
 
        // Pair is (name, add_to_global_namespace)
 
        const FILES: [(&'static str, bool); 3] = [
 
            ("std.global.pdl", true),
 
            ("std.internet.pdl", false),
 
            ("std.random.pdl", false),
 
        ];
 

	
 
        // Determine base directory
 
        let (base_path, from_env) = if let Ok(path) = env::var(REOWOLF_PATH_ENV) {
 
            // Path variable is set
 
            (path, true)
 
        } else {
 
            let path = match self.std_lib_dir.take() {
 
                Some(path) => path,
 
                None => {
 
                    let mut path = String::with_capacity(REOWOLF_PATH_DIR.len() + 2);
 
                    path.push_str("./");
 
                    path.push_str(REOWOLF_PATH_DIR);
 
                    path
 
                }
 
            };
 

	
 
            (path, false)
 
        };
 

	
 
        // Make sure directory exists
 
        let path = Path::new(&base_path);
 
        if !path.exists() {
 
            return Err(format!("std lib root directory '{}' does not exist", base_path));
 
        }
 

	
 
        // Try to load all standard library files. We might need a more unified
 
        // way to do this in the future (i.e. a "std" package, containing all
 
        // of the modules)
 
        let mut file_path = PathBuf::new();
 
        let mut first_file = true;
 

	
 
        for (file, add_to_global_namespace) in FILES {
 
            file_path.clear();
 
            file_path.push(path);
 
            file_path.push(file);
 

	
 
            let source = fs::read(file_path.as_path());
 
            if let Err(err) = source {
 
                return Err(format!(
 
                    "failed to read std lib file '{}' in root directory '{}', because: {}",
 
                    file, base_path, err
 
                ));
 
            }
 

	
 
            let source = source.unwrap();
 
            let input_source = InputSource::new(file.to_string(), source);
 

	
 
            let module_index = self.feed_internal(input_source, true, add_to_global_namespace);
 
            if let Err(err) = module_index {
 
                // A bit of a hack, but shouldn't really happen anyway: the
 
                // compiler should ship with a decent standard library (at some
 
                // point)
 
                return Err(format!("{}", err));
 
            }
 
            let module_index = module_index.unwrap();
 

	
 
            if first_file {
 
                self.global_module_index = module_index;
 
                first_file = false;
 
            }
 
        }
 

	
 
        return Ok(())
 
    }
 

	
 
    fn feed_internal(&mut self, mut source: InputSource, is_compiler_file: bool, add_to_global_namespace: bool) -> Result<usize, ParseError> {
 
        let mut token_buffer = TokenBuffer::new();
 
        self.pass_tokenizer.tokenize(&mut source, &mut token_buffer)?;
 

	
 
        let module = Module{
 
            source,
 
            tokens: token_buffer,
 
            is_compiler_file,
 
            add_to_global_namespace,
 
            root_id: RootId::new_invalid(),
 
            name: None,
 
            version: None,
 
            phase: ModuleCompilationPhase::Tokenized,
 
        };
 
        let module_index = self.modules.len();
 
        self.modules.push(module);
 

	
 
        return Ok(module_index);
 
    }
 
}
 

	
 
fn insert_builtin_type(type_table: &mut TypeTable, parts: Vec<ConcreteTypePart>, has_poly_var: bool, size: usize, alignment: usize) -> TypeId {
 
    const POLY_VARS: [PolymorphicVariable; 1] = [PolymorphicVariable{
 
        identifier: Identifier::new_empty(InputSpan::new()),
 
        is_in_use: false,
 
    }];
 

	
 
    let concrete_type = ConcreteType{ parts };
 
    let poly_var = if has_poly_var {
 
        POLY_VARS.as_slice()
 
    } else {
 
        &[]
 
    };
 

	
 
    return type_table.add_builtin_data_type(concrete_type, poly_var, size, alignment);
 
}
 
\ No newline at end of file
 

	
 
// Note: args and return type need to be a function because we need to know the function ID.
 
fn insert_builtin_function<T: Fn(ProcedureDefinitionId) -> (Vec<(&'static str, ParserType)>, ParserType)> (
 
    p: &mut Parser, func_name: &str, polymorphic: &[&str], arg_and_return_fn: T
 
) {
 
    // Insert into AST (to get an ID), also prepare the polymorphic variables
 
    // we need later for the type table
 
    let mut ast_poly_vars = Vec::with_capacity(polymorphic.len());
 
    let mut type_poly_vars = Vec::with_capacity(polymorphic.len());
 
    for poly_var in polymorphic {
 
        let identifier = Identifier{ span: InputSpan::new(), value: p.string_pool.intern(poly_var.as_bytes()) } ;
 
        ast_poly_vars.push(identifier.clone());
 
        type_poly_vars.push(PolymorphicVariable{ identifier, is_in_use: false });
 
    }
 

	
 
    let func_ident_ref = p.string_pool.intern(func_name.as_bytes());
 
    let procedure_id = p.heap.alloc_procedure_definition(|this| ProcedureDefinition {
 
        this,
 
        defined_in: RootId::new_invalid(),
 
        builtin: true,
 
        kind: ProcedureKind::Function,
 
        span: InputSpan::new(),
 
        identifier: Identifier{ span: InputSpan::new(), value: func_ident_ref.clone() },
 
        poly_vars: ast_poly_vars,
 
        return_type: None,
 
        parameters: Vec::new(),
 
        scope: ScopeId::new_invalid(),
 
        body: BlockStatementId::new_invalid(),
 
        monomorphs: Vec::new(),
 
    });
 

	
 
    // Modify AST with more information about the procedure
 
    let (arguments, return_type) = arg_and_return_fn(procedure_id);
 

	
 
    let mut parameters = Vec::with_capacity(arguments.len());
 
    for (arg_name, arg_type) in arguments {
 
        let identifier = Identifier{ span: InputSpan::new(), value: p.string_pool.intern(arg_name.as_bytes()) };
 
        let param_id = p.heap.alloc_variable(|this| Variable{
 
            this,
 
            kind: VariableKind::Parameter,
 
            parser_type: arg_type.clone(),
 
            identifier,
 
            relative_pos_in_parent: 0,
 
            unique_id_in_scope: 0
 
        });
 
        parameters.push(param_id);
 
    }
 

	
 
    let func = &mut p.heap[procedure_id];
 
    func.parameters = parameters;
 
    func.return_type = Some(return_type);
 

	
 
    // Insert into symbol table
 
    p.symbol_table.insert_symbol(SymbolScope::Global, Symbol{
 
        name: func_ident_ref,
 
        variant: SymbolVariant::Definition(SymbolDefinition{
 
            defined_in_module: RootId::new_invalid(),
 
            defined_in_scope: SymbolScope::Global,
 
            definition_span: InputSpan::new(),
 
            identifier_span: InputSpan::new(),
 
            imported_at: None,
 
            class: DefinitionClass::Function,
 
            definition_id: procedure_id.upcast(),
 
        })
 
    }).unwrap();
 

	
 
    // Insert into type table
 
    // let mut concrete_type = ConcreteType::default();
 
    // concrete_type.parts.push(ConcreteTypePart::Function(procedure_id, type_poly_vars.len() as u32));
 
    //
 
    // for _ in 0..type_poly_vars.len() {
 
    //     concrete_type.parts.push(ConcreteTypePart::Void); // doesn't matter (I hope...)
 
    // }
 
    // p.type_table.add_builtin_procedure_type(concrete_type, &type_poly_vars);
 
}
 
\ No newline at end of file

Changeset was too big and was cut off... Show full diff anyway

0 comments (0 inline, 0 general)