Changeset - 46d10288234a
[Not reviewed]
Merge
1 30 4
Max Henger - 3 years ago 2022-03-02 11:38:38
henger@cwi.nl
Merge branch 'feat-select-block-evaluation' into 'master'

feat: select block evaluation

See merge request nl-cwi-csy/reowolf!3
20 files changed:
0 comments (0 inline, 0 general)
Cargo.toml
Show inline comments
 
@@ -32,7 +32,7 @@ lazy_static = "1.4.0"
 
libc = { version = "^0.2", optional = true }
 
os_socketaddr = { version = "0.1.0", optional = true }
 

	
 
[dev-dependencies]
 
# randomness
 
rand = "0.8.4"
 
rand_pcg = "0.3.1"
 

	
docs/runtime/sync.md
Show inline comments
 
new file 100644
 
# Synchronous Communication
 

	
 
## 
 
\ No newline at end of file
src/collections/raw_vec.rs
Show inline comments
 
@@ -62,20 +62,6 @@ impl<T: Sized> RawVec<T> {
 
        }
 
    }
 

	
 
    /// Moves the elements in the range [from_idx, from_idx + num_to_move) to
 
    /// the range [to_idx, to_idx + num_to_move). Caller must make sure that all
 
    /// non-overlapping elements of the second range had their destructor called
 
    /// in case those elements were used.
 
    pub fn move_range(&mut self, from_idx: usize, to_idx: usize, num_to_move: usize) {
 
        debug_assert!(from_idx + num_to_move <= self.len);
 
        debug_assert!(to_idx + num_to_move <= self.len); // maybe not in future, for now this is fine
 
        unsafe {
 
            let source = self.base.add(from_idx);
 
            let target = self.base.add(to_idx);
 
            std::ptr::copy(source, target, num_to_move);
 
        }
 
    }
 

	
 
    pub fn len(&self) -> usize {
 
        return self.len;
 
    }
 
@@ -140,9 +126,7 @@ impl<T: Sized> Drop for RawVec<T> {
 
            let (_, layout) = self.current_layout();
 
            unsafe {
 
                dealloc(self.base as *mut u8, layout);
 
                if cfg!(debug_assertions) {
 
                    self.base = ptr::null_mut();
 
                }
 
                dbg_code!({ self.base = ptr::null_mut(); });
 
            }
 
        }
 
    }
src/collections/scoped_buffer.rs
Show inline comments
 
@@ -73,54 +73,80 @@ pub(crate) struct ScopedSection<T: Sized> {
 
}
 

	
 
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};
 
        hide!(debug_assert_eq!(
 
            vec.len(), self.cur_size as usize,
 
            "trying to push onto section, but size is larger than expected"
 
        ));
 
        vec.push(value);
 
        hide!(self.cur_size += 1);
 
    }
 

	
 
    #[inline]
 
    pub(crate) fn len(&self) -> usize {
 
        self.check_length();
 
        let vec = unsafe{&mut *self.inner};
 
        hide!(debug_assert_eq!(
 
            vec.len(), self.cur_size as usize,
 
            "trying to get section length, but size is larger than expected"
 
        ));
 
        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!({
 
            debug_assert_eq!(
 
                vec.len(), self.cur_size as usize,
 
                "trying to forget section, but size is larger than expected"
 
            );
 
            self.cur_size = self.start_size;
 
        });
 
        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,
 
                "trying to turn section into vec, but size is larger than expected"
 
            );
 
            self.cur_size = self.start_size;
 
        });
 
        let section = Vec::from_iter(vec.drain(self.start_size as usize..));
 
        section
 
                "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;
 
    }
 
}
 

	
src/collections/string_pool.rs
Show inline comments
 
use std::ptr::null_mut;
 
use std::ptr::{null_mut, null};
 
use std::hash::{Hash, Hasher};
 
use std::marker::PhantomData;
 
use std::fmt::{Debug, Display, Formatter, Result as FmtResult};
 
@@ -29,6 +29,12 @@ impl<'a> StringRef<'a> {
 
        StringRef{ data, length, _phantom: PhantomData }
 
    }
 

	
 
    /// `new_empty` creates a empty StringRef. It is a null pointer with a
 
    /// length of zero.
 
    pub(crate) const fn new_empty() -> StringRef<'static> {
 
        StringRef{ data: null(), length: 0, _phantom: PhantomData }
 
    }
 

	
 
    pub fn as_str(&self) -> &'a str {
 
        unsafe {
 
            let slice = std::slice::from_raw_parts::<'a, u8>(self.data, self.length);
 
@@ -161,6 +167,13 @@ unsafe impl Send for StringPool {}
 
mod tests {
 
    use super::*;
 

	
 
    #[test]
 
    fn display_empty_string_ref() {
 
        // Makes sure that null pointer inside StringRef will not cause issues
 
        let v = StringRef::new_empty();
 
        let _val = format!("{}{:?}", v, v); // calls Format and Debug on StringRef
 
    }
 

	
 
    #[test]
 
    fn test_string_just_fits() {
 
        let large = "0".repeat(SLAB_SIZE);
src/common.rs
Show inline comments
 
deleted file
src/lib.rs
Show inline comments
 
@@ -6,5 +6,6 @@ mod protocol;
 
pub mod runtime;
 
pub mod runtime2;
 
mod collections;
 
mod random;
 

	
 
pub use protocol::{ProtocolDescription, ProtocolDescriptionBuilder, ComponentCreationError};
 
\ No newline at end of file
src/macros.rs
Show inline comments
 
@@ -18,4 +18,32 @@ macro_rules! dbg_code {
 
    ($code:stmt) => {
 
        #[cfg(debug_assertions)] $code
 
    }
 
}
 

	
 
// Given a function name, return type and variant, will generate the all-so
 
// common `union_value.as_variant()` method. The return value is the reference
 
// to the embedded union type.
 
macro_rules! union_cast_to_ref_method_impl {
 
    ($func_name:ident, $ret_type:ty, $variant:path) => {
 
        fn $func_name(&self) -> &$ret_type {
 
            match self {
 
                $variant(content) => return content,
 
                _ => unreachable!(),
 
            }
 
        }
 
    }
 
}
 

	
 
// Another union cast, but now returning a copy of the value
 
macro_rules! union_cast_to_value_method_impl {
 
    ($func_name:ident, $ret_type:ty, $variant:path) => {
 
        impl Value {
 
            pub(crate) fn $func_name(&self) -> $ret_type {
 
                match self {
 
                    $variant(v) => *v,
 
                    _ => unreachable!(),
 
                }
 
            }
 
        }
 
    }
 
}
 
\ No newline at end of file
src/protocol/ast.rs
Show inline comments
 
@@ -5,6 +5,7 @@ use std::ops::{Index, IndexMut};
 
use super::arena::{Arena, Id};
 
use crate::collections::StringRef;
 
use crate::protocol::input_source::InputSpan;
 
use crate::protocol::TypeId;
 

	
 
/// Helper macro that defines a type alias for a AST element ID. In this case 
 
/// only used to alias the `Id<T>` types.
 
@@ -117,8 +118,7 @@ define_aliased_ast_id!(DefinitionId, Id<Definition>, index(Definition, definitio
 
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!(ComponentDefinitionId, DefinitionId, index(ComponentDefinition, Definition::Component, definitions), alloc(alloc_component_definition));
 
define_new_ast_id!(FunctionDefinitionId, DefinitionId, index(FunctionDefinition, Definition::Function, definitions), alloc(alloc_function_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));
 
@@ -158,6 +158,8 @@ define_new_ast_id!(CastExpressionId, ExpressionId, index(CastExpression, Express
 
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
 
@@ -170,6 +172,7 @@ pub struct Heap {
 
    pub(crate) definitions: Arena<Definition>,
 
    pub(crate) statements: Arena<Statement>,
 
    pub(crate) expressions: Arena<Expression>,
 
    pub(crate) scopes: Arena<Scope>,
 
}
 

	
 
impl Heap {
 
@@ -183,6 +186,7 @@ impl Heap {
 
            definitions: Arena::new(),
 
            statements: Arena::new(),
 
            expressions: Arena::new(),
 
            scopes: Arena::new(),
 
        }
 
    }
 
    pub fn alloc_memory_statement(
 
@@ -210,14 +214,20 @@ impl Heap {
 
impl Index<MemoryStatementId> for Heap {
 
    type Output = MemoryStatement;
 
    fn index(&self, index: MemoryStatementId) -> &Self::Output {
 
        &self.statements[index.0.0].as_memory()
 
        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 {
 
        &self.statements[index.0.0].as_channel()
 
        match &self.statements[index.0.0] {
 
            Statement::Local(LocalStatement::Channel(v)) => v,
 
            _ => unreachable!(),
 
        }
 
    }
 
}
 

	
 
@@ -340,6 +350,15 @@ pub struct Identifier {
 
    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
 
@@ -497,12 +516,13 @@ pub enum ConcreteTypePart {
 
    Slice,
 
    Input,
 
    Output,
 
    Pointer,
 
    // Tuple: variable number of nested types, will never be 1
 
    Tuple(u32),
 
    // User defined type with any number of nested types
 
    Instance(DefinitionId, u32),    // instance of data type
 
    Function(DefinitionId, u32),    // instance of function
 
    Component(DefinitionId, u32),   // instance of a connector
 
    Function(ProcedureDefinitionId, u32),    // instance of function
 
    Component(ProcedureDefinitionId, u32),   // instance of a connector
 
}
 

	
 
impl ConcreteTypePart {
 
@@ -515,7 +535,7 @@ impl ConcreteTypePart {
 
            SInt8 | SInt16 | SInt32 | SInt64 |
 
            Character | String =>
 
                0,
 
            Array | Slice | Input | Output =>
 
            Array | Slice | Input | Output | Pointer =>
 
                1,
 
            Tuple(num_embedded) => *num_embedded,
 
            Instance(_, num_embedded) => *num_embedded,
 
@@ -622,6 +642,10 @@ impl ConcreteType {
 
                idx = Self::render_type_part_at(parts, heap, idx, target);
 
                target.push('>');
 
            },
 
            CTP::Pointer => {
 
                target.push('*');
 
                idx = Self::render_type_part_at(parts, heap, idx, target);
 
            }
 
            CTP::Tuple(num_parts) => {
 
                target.push('(');
 
                if num_parts != 0 {
 
@@ -633,27 +657,35 @@ impl ConcreteType {
 
                }
 
                target.push(')');
 
            },
 
            CTP::Instance(definition_id, num_poly_args) |
 
            CTP::Instance(definition_id, num_poly_args) => {
 
                idx = Self::render_definition_type_parts_at(parts, heap, definition_id, num_poly_args, idx, target);
 
            }
 
            CTP::Function(definition_id, num_poly_args) |
 
            CTP::Component(definition_id, num_poly_args) => {
 
                let definition = &heap[definition_id];
 
                target.push_str(definition.identifier().value.as_str());
 

	
 
                if num_poly_args != 0 {
 
                    target.push('<');
 
                    for poly_arg_idx in 0..num_poly_args {
 
                        if poly_arg_idx != 0 {
 
                            target.push(',');
 
                        }
 
                        idx = Self::render_type_part_at(parts, heap, idx, target);
 
                    }
 
                    target.push('>');
 
                }
 
                idx = Self::render_definition_type_parts_at(parts, heap, definition_id.upcast(), num_poly_args, idx, target);
 
            }
 
        }
 

	
 
        idx
 
    }
 

	
 
    fn render_definition_type_parts_at(parts: &[ConcreteTypePart], heap: &Heap, definition_id: DefinitionId, num_poly_args: u32, mut idx: usize, target: &mut String) -> usize {
 
        let definition = &heap[definition_id];
 
        target.push_str(definition.identifier().value.as_str());
 

	
 
        if num_poly_args != 0 {
 
            target.push('<');
 
            for poly_arg_idx in 0..num_poly_args {
 
                if poly_arg_idx != 0 {
 
                    target.push(',');
 
                }
 
                idx = Self::render_type_part_at(parts, heap, idx, target);
 
            }
 
            target.push('>');
 
        }
 

	
 
        return idx;
 
    }
 
}
 

	
 
#[derive(Debug)]
 
@@ -696,60 +728,66 @@ impl<'a> Iterator for ConcreteTypeIter<'a> {
 
}
 

	
 
#[derive(Debug, Clone, Copy)]
 
pub enum Scope {
 
pub enum ScopeAssociation {
 
    Definition(DefinitionId),
 
    Regular(BlockStatementId),
 
    Synchronous(SynchronousStatementId, BlockStatementId),
 
}
 

	
 
impl Scope {
 
    pub(crate) fn new_invalid() -> Scope {
 
        return Scope::Definition(DefinitionId::new_invalid());
 
    }
 

	
 
    pub(crate) fn is_invalid(&self) -> bool {
 
        match self {
 
            Scope::Definition(id) => id.is_invalid(),
 
            _ => false,
 
        }
 
    }
 

	
 
    pub fn is_block(&self) -> bool {
 
        match &self {
 
            Scope::Definition(_) => false,
 
            Scope::Regular(_) => true,
 
            Scope::Synchronous(_, _) => true,
 
        }
 
    }
 
    pub fn to_block(&self) -> BlockStatementId {
 
        match &self {
 
            Scope::Regular(id) => *id,
 
            Scope::Synchronous(_, id) => *id,
 
            _ => panic!("unable to get BlockStatement from Scope")
 
        }
 
    }
 
    Block(BlockStatementId),
 
    If(IfStatementId, bool), // if true, then body of "if", otherwise body of "else"
 
    While(WhileStatementId),
 
    Synchronous(SynchronousStatementId),
 
    SelectCase(SelectStatementId, u32), // index is select case
 
}
 

	
 
/// `ScopeNode` is a helper that links scopes in two directions. It doesn't
 
/// actually contain any information associated with the scope, this may be
 
/// found on the AST elements that `Scope` points to.
 
#[derive(Debug, Clone)]
 
pub struct ScopeNode {
 
    pub parent: Scope,
 
    pub nested: Vec<Scope>,
 
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 ScopeNode {
 
    pub(crate) fn new_invalid() -> Self {
 
        ScopeNode{
 
            parent: Scope::new_invalid(),
 
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
 
@@ -765,17 +803,16 @@ pub struct Variable {
 
    pub parser_type: ParserType,
 
    pub identifier: Identifier,
 
    // Validator/linker
 
    pub relative_pos_in_block: i32,
 
    pub relative_pos_in_parent: i32,
 
    pub unique_id_in_scope: i32, // Temporary fix until proper bytecode/asm is generated
 
}
 

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

	
 
impl Definition {
 
@@ -827,71 +864,50 @@ impl Definition {
 
            _ => 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_component(&self) -> bool {
 
        match self {
 
            Definition::Component(_) => true,
 
            _ => false,
 
        }
 
    }
 
    pub(crate) fn as_component(&self) -> &ComponentDefinition {
 
        match self {
 
            Definition::Component(result) => result,
 
            _ => panic!("Unable to cast `Definition` to `Component`"),
 
        }
 
    }
 
    pub(crate) fn as_component_mut(&mut self) -> &mut ComponentDefinition {
 
        match self {
 
            Definition::Component(result) => result,
 
            _ => panic!("Unable to cast `Definition` to `Component`"),
 
        }
 
    }
 
    pub fn is_function(&self) -> bool {
 

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

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

	
 
    pub(crate) fn as_procedure_mut(&mut self) -> &mut ProcedureDefinition {
 
        match self {
 
            Definition::Function(result) => result,
 
            Definition::Procedure(result) => result,
 
            _ => panic!("Unable to cast `Definition` to `Function`"),
 
        }
 
    }
 
    pub fn parameters(&self) -> &Vec<VariableId> {
 
        match self {
 
            Definition::Component(def) => &def.parameters,
 
            Definition::Function(def) => &def.parameters,
 
            _ => panic!("Called parameters() on {:?}", self)
 
        }
 
    }
 

	
 
    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::Component(def) => def.defined_in,
 
            Definition::Function(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::Component(def) => &def.identifier,
 
            Definition::Function(def) => &def.identifier,
 
            Definition::Procedure(def) => &def.identifier,
 
        }
 
    }
 
    pub fn poly_vars(&self) -> &Vec<Identifier> {
 
@@ -899,8 +915,7 @@ impl Definition {
 
            Definition::Struct(def) => &def.poly_vars,
 
            Definition::Enum(def) => &def.poly_vars,
 
            Definition::Union(def) => &def.poly_vars,
 
            Definition::Component(def) => &def.poly_vars,
 
            Definition::Function(def) => &def.poly_vars,
 
            Definition::Procedure(def) => &def.poly_vars,
 
        }
 
    }
 
}
 
@@ -994,75 +1009,106 @@ impl UnionDefinition {
 
    }
 
}
 

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

	
 
#[derive(Debug, Clone)]
 
pub struct ComponentDefinition {
 
    pub this: ComponentDefinitionId,
 
    pub defined_in: RootId,
 
    // Symbol scanning
 
    pub span: InputSpan,
 
    pub variant: ComponentVariant,
 
    pub identifier: Identifier,
 
    pub poly_vars: Vec<Identifier>,
 
    // Parsing
 
    pub parameters: Vec<VariableId>,
 
    pub body: BlockStatementId,
 
    // Validation/linking
 
    pub num_expressions_in_body: i32,
 
/// 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 ComponentDefinition {
 
    // Used for preallocation during symbol scanning
 
    pub(crate) fn new_empty(
 
        this: ComponentDefinitionId, defined_in: RootId, span: InputSpan,
 
        variant: ComponentVariant, identifier: Identifier, poly_vars: Vec<Identifier>
 
    ) -> Self {
 
        Self{ 
 
            this, defined_in, span, variant, identifier, poly_vars,
 
            parameters: Vec::new(), 
 
            body: BlockStatementId::new_invalid(),
 
            num_expressions_in_body: -1,
 
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!(),
 
        }
 
    }
 
}
 

	
 
/// 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, Clone)]
 
pub struct FunctionDefinition {
 
    pub this: FunctionDefinitionId,
 
#[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 return_types: Vec<ParserType>,
 
    pub return_type: Option<ParserType>, // present on functions, not components
 
    pub parameters: Vec<VariableId>,
 
    pub scope: ScopeId,
 
    pub body: BlockStatementId,
 
    // Validation/linking
 
    pub num_expressions_in_body: i32,
 
    // Monomorphization of typed procedures
 
    pub monomorphs: Vec<ProcedureDefinitionMonomorph>,
 
}
 

	
 
impl FunctionDefinition {
 
impl ProcedureDefinition {
 
    pub(crate) fn new_empty(
 
        this: FunctionDefinitionId, defined_in: RootId, span: InputSpan,
 
        identifier: Identifier, poly_vars: Vec<Identifier>
 
        this: ProcedureDefinitionId, defined_in: RootId, span: InputSpan,
 
        kind: ProcedureKind, identifier: Identifier, poly_vars: Vec<Identifier>
 
    ) -> Self {
 
        Self {
 
            this, defined_in,
 
            builtin: false,
 
            span, identifier, poly_vars,
 
            return_types: Vec::new(),
 
            span,
 
            kind, identifier, poly_vars,
 
            return_type: None,
 
            parameters: Vec::new(),
 
            scope: ScopeId::new_invalid(),
 
            body: BlockStatementId::new_invalid(),
 
            num_expressions_in_body: -1,
 
            monomorphs: Vec::new(),
 
        }
 
    }
 
}
 
@@ -1092,25 +1138,6 @@ pub enum Statement {
 
}
 

	
 
impl Statement {
 
    pub fn as_block(&self) -> &BlockStatement {
 
        match self {
 
            Statement::Block(result) => result,
 
            _ => panic!("Unable to cast `Statement` to `BlockStatement`"),
 
        }
 
    }
 
    pub fn as_local(&self) -> &LocalStatement {
 
        match self {
 
            Statement::Local(result) => result,
 
            _ => panic!("Unable to cast `Statement` to `LocalStatement`"),
 
        }
 
    }
 
    pub fn as_memory(&self) -> &MemoryStatement {
 
        self.as_local().as_memory()
 
    }
 
    pub fn as_channel(&self) -> &ChannelStatement {
 
        self.as_local().as_channel()
 
    }
 

	
 
    pub fn as_new(&self) -> &NewStatement {
 
        match self {
 
            Statement::New(result) => result,
 
@@ -1169,22 +1196,18 @@ impl Statement {
 
            | Statement::If(_) => unreachable!(),
 
        }
 
    }
 

	
 
}
 

	
 
#[derive(Debug, Clone)]
 
pub struct BlockStatement {
 
    pub this: BlockStatementId,
 
    // Phase 1: parser
 
    pub is_implicit: bool,
 
    pub span: InputSpan, // of the complete block
 
    pub statements: Vec<StatementId>,
 
    pub end_block: EndBlockStatementId,
 
    // Phase 2: linker
 
    pub scope_node: ScopeNode,
 
    pub first_unique_id_in_scope: i32, // Temporary fix until proper bytecode/asm is generated
 
    pub next_unique_id_in_scope: i32, // Temporary fix until proper bytecode/asm is generated
 
    pub locals: Vec<VariableId>,
 
    pub labels: Vec<LabeledStatementId>,
 
    pub scope: ScopeId,
 
    pub next: StatementId,
 
}
 

	
 
@@ -1210,18 +1233,6 @@ impl LocalStatement {
 
            LocalStatement::Channel(stmt) => stmt.this.upcast(),
 
        }
 
    }
 
    pub fn as_memory(&self) -> &MemoryStatement {
 
        match self {
 
            LocalStatement::Memory(result) => result,
 
            _ => panic!("Unable to cast `LocalStatement` to `MemoryStatement`"),
 
        }
 
    }
 
    pub fn as_channel(&self) -> &ChannelStatement {
 
        match self {
 
            LocalStatement::Channel(result) => result,
 
            _ => panic!("Unable to cast `LocalStatement` to `ChannelStatement`"),
 
        }
 
    }
 
    pub fn span(&self) -> InputSpan {
 
        match self {
 
            LocalStatement::Channel(v) => v.span,
 
@@ -1254,7 +1265,7 @@ pub struct ChannelStatement {
 
    pub from: VariableId, // output
 
    pub to: VariableId,   // input
 
    // Phase 2: linker
 
    pub relative_pos_in_block: i32,
 
    pub relative_pos_in_parent: i32,
 
    pub next: StatementId,
 
}
 

	
 
@@ -1265,7 +1276,7 @@ pub struct LabeledStatement {
 
    pub label: Identifier,
 
    pub body: StatementId,
 
    // Phase 2: linker
 
    pub relative_pos_in_block: i32,
 
    pub relative_pos_in_parent: i32,
 
    pub in_sync: SynchronousStatementId, // may be invalid
 
}
 

	
 
@@ -1275,11 +1286,17 @@ pub struct IfStatement {
 
    // Phase 1: parser
 
    pub span: InputSpan, // of the "if" keyword
 
    pub test: ExpressionId,
 
    pub true_body: BlockStatementId,
 
    pub false_body: Option<BlockStatementId>,
 
    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,
 
}
 

	
 
#[derive(Debug, Clone)]
 
pub struct EndIfStatement {
 
    pub this: EndIfStatementId,
 
@@ -1293,7 +1310,8 @@ pub struct WhileStatement {
 
    // Phase 1: parser
 
    pub span: InputSpan, // of the "while" keyword
 
    pub test: ExpressionId,
 
    pub body: BlockStatementId,
 
    pub scope: ScopeId,
 
    pub body: StatementId,
 
    pub end_while: EndWhileStatementId,
 
    pub in_sync: SynchronousStatementId, // may be invalid
 
}
 
@@ -1331,7 +1349,8 @@ pub struct SynchronousStatement {
 
    pub this: SynchronousStatementId,
 
    // Phase 1: parser
 
    pub span: InputSpan, // of the "sync" keyword
 
    pub body: BlockStatementId,
 
    pub scope: ScopeId,
 
    pub body: StatementId,
 
    pub end_sync: EndSynchronousStatementId,
 
}
 

	
 
@@ -1348,8 +1367,8 @@ pub struct ForkStatement {
 
    pub this: ForkStatementId,
 
    // Phase 1: parser
 
    pub span: InputSpan, // of the "fork" keyword
 
    pub left_body: BlockStatementId,
 
    pub right_body: Option<BlockStatementId>,
 
    pub left_body: StatementId,
 
    pub right_body: Option<StatementId>,
 
    pub end_fork: EndForkStatementId,
 
}
 

	
 
@@ -1366,6 +1385,8 @@ pub struct SelectStatement {
 
    pub span: InputSpan, // of the "select" keyword
 
    pub cases: Vec<SelectCase>,
 
    pub end_select: EndSelectStatementId,
 
    pub relative_pos_in_parent: i32,
 
    pub next: StatementId, // note: the select statement will be transformed into other AST elements, this `next` jumps to those replacement statements
 
}
 

	
 
#[derive(Debug, Clone)]
 
@@ -1373,7 +1394,8 @@ pub struct SelectCase {
 
    // The guard statement of a `select` is either a MemoryStatement or an
 
    // ExpressionStatement. Nothing else is allowed by the initial parsing
 
    pub guard: StatementId,
 
    pub block: BlockStatementId,
 
    pub body: StatementId,
 
    pub scope: ScopeId,
 
    // Phase 2: Validation and Linking
 
    pub involved_ports: Vec<(CallExpressionId, ExpressionId)>, // call to `get` and its port argument
 
}
 
@@ -1432,7 +1454,7 @@ pub enum ExpressionParent {
 
    Return(ReturnStatementId),
 
    New(NewStatementId),
 
    ExpressionStmt(ExpressionStatementId),
 
    Expression(ExpressionId, u32) // index within expression (e.g LHS or RHS of expression)
 
    Expression(ExpressionId, u32) // index within expression (e.g LHS or RHS of expression, or index in array literal, etc.)
 
}
 

	
 
impl ExpressionParent {
 
@@ -1530,6 +1552,23 @@ impl Expression {
 
        }
 
    }
 

	
 
    pub fn parent_mut(&mut self) -> &mut ExpressionParent {
 
        match self {
 
            Expression::Assignment(expr) => &mut expr.parent,
 
            Expression::Binding(expr) => &mut expr.parent,
 
            Expression::Conditional(expr) => &mut expr.parent,
 
            Expression::Binary(expr) => &mut expr.parent,
 
            Expression::Unary(expr) => &mut expr.parent,
 
            Expression::Indexing(expr) => &mut expr.parent,
 
            Expression::Slicing(expr) => &mut expr.parent,
 
            Expression::Select(expr) => &mut expr.parent,
 
            Expression::Literal(expr) => &mut expr.parent,
 
            Expression::Cast(expr) => &mut expr.parent,
 
            Expression::Call(expr) => &mut expr.parent,
 
            Expression::Variable(expr) => &mut expr.parent,
 
        }
 
    }
 

	
 
    pub fn parent_expr_id(&self) -> Option<ExpressionId> {
 
        if let ExpressionParent::Expression(id, _) = self.parent() {
 
            Some(*id)
 
@@ -1538,20 +1577,37 @@ impl Expression {
 
        }
 
    }
 

	
 
    pub fn get_unique_id_in_definition(&self) -> i32 {
 
    pub fn type_index(&self) -> i32 {
 
        match self {
 
            Expression::Assignment(expr) => expr.type_index,
 
            Expression::Binding(expr) => expr.type_index,
 
            Expression::Conditional(expr) => expr.type_index,
 
            Expression::Binary(expr) => expr.type_index,
 
            Expression::Unary(expr) => expr.type_index,
 
            Expression::Indexing(expr) => expr.type_index,
 
            Expression::Slicing(expr) => expr.type_index,
 
            Expression::Select(expr) => expr.type_index,
 
            Expression::Literal(expr) => expr.type_index,
 
            Expression::Cast(expr) => expr.type_index,
 
            Expression::Call(expr) => expr.type_index,
 
            Expression::Variable(expr) => expr.type_index,
 
        }
 
    }
 

	
 
    pub fn type_index_mut(&mut self) -> &mut i32 {
 
        match self {
 
            Expression::Assignment(expr) => expr.unique_id_in_definition,
 
            Expression::Binding(expr) => expr.unique_id_in_definition,
 
            Expression::Conditional(expr) => expr.unique_id_in_definition,
 
            Expression::Binary(expr) => expr.unique_id_in_definition,
 
            Expression::Unary(expr) => expr.unique_id_in_definition,
 
            Expression::Indexing(expr) => expr.unique_id_in_definition,
 
            Expression::Slicing(expr) => expr.unique_id_in_definition,
 
            Expression::Select(expr) => expr.unique_id_in_definition,
 
            Expression::Literal(expr) => expr.unique_id_in_definition,
 
            Expression::Cast(expr) => expr.unique_id_in_definition,
 
            Expression::Call(expr) => expr.unique_id_in_definition,
 
            Expression::Variable(expr) => expr.unique_id_in_definition,
 
            Expression::Assignment(expr) => &mut expr.type_index,
 
            Expression::Binding(expr) => &mut expr.type_index,
 
            Expression::Conditional(expr) => &mut expr.type_index,
 
            Expression::Binary(expr) => &mut expr.type_index,
 
            Expression::Unary(expr) => &mut expr.type_index,
 
            Expression::Indexing(expr) => &mut expr.type_index,
 
            Expression::Slicing(expr) => &mut expr.type_index,
 
            Expression::Select(expr) => &mut expr.type_index,
 
            Expression::Literal(expr) => &mut expr.type_index,
 
            Expression::Cast(expr) => &mut expr.type_index,
 
            Expression::Call(expr) => &mut expr.type_index,
 
            Expression::Variable(expr) => &mut expr.type_index,
 
        }
 
    }
 
}
 
@@ -1583,7 +1639,8 @@ pub struct AssignmentExpression {
 
    pub right: ExpressionId,
 
    // Validator/Linker
 
    pub parent: ExpressionParent,
 
    pub unique_id_in_definition: i32,
 
    // Typing
 
    pub type_index: i32,
 
}
 

	
 
#[derive(Debug, Clone)]
 
@@ -1596,7 +1653,8 @@ pub struct BindingExpression {
 
    pub bound_from: ExpressionId,
 
    // Validator/Linker
 
    pub parent: ExpressionParent,
 
    pub unique_id_in_definition: i32,
 
    // Typing
 
    pub type_index: i32,
 
}
 

	
 
#[derive(Debug, Clone)]
 
@@ -1610,7 +1668,8 @@ pub struct ConditionalExpression {
 
    pub false_expression: ExpressionId,
 
    // Validator/Linking
 
    pub parent: ExpressionParent,
 
    pub unique_id_in_definition: i32,
 
    // Typing
 
    pub type_index: i32,
 
}
 

	
 
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
 
@@ -1647,7 +1706,8 @@ pub struct BinaryExpression {
 
    pub right: ExpressionId,
 
    // Validator/Linker
 
    pub parent: ExpressionParent,
 
    pub unique_id_in_definition: i32,
 
    // Typing
 
    pub type_index: i32,
 
}
 

	
 
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
 
@@ -1668,7 +1728,8 @@ pub struct UnaryExpression {
 
    pub expression: ExpressionId,
 
    // Validator/Linker
 
    pub parent: ExpressionParent,
 
    pub unique_id_in_definition: i32,
 
    // Typing
 
    pub type_index: i32,
 
}
 

	
 
#[derive(Debug, Clone)]
 
@@ -1681,7 +1742,8 @@ pub struct IndexingExpression {
 
    pub index: ExpressionId,
 
    // Validator/Linker
 
    pub parent: ExpressionParent,
 
    pub unique_id_in_definition: i32,
 
    // Typing
 
    pub type_index: i32,
 
}
 

	
 
#[derive(Debug, Clone)]
 
@@ -1695,7 +1757,8 @@ pub struct SlicingExpression {
 
    pub to_index: ExpressionId,
 
    // Validator/Linker
 
    pub parent: ExpressionParent,
 
    pub unique_id_in_definition: i32,
 
    // Typing
 
    pub type_index: i32,
 
}
 

	
 
#[derive(Debug, Clone)]
 
@@ -1714,7 +1777,8 @@ pub struct SelectExpression {
 
    pub kind: SelectKind,
 
    // Validator/Linker
 
    pub parent: ExpressionParent,
 
    pub unique_id_in_definition: i32,
 
    // Typing
 
    pub type_index: i32,
 
}
 

	
 
#[derive(Debug, Clone)]
 
@@ -1727,7 +1791,8 @@ pub struct CastExpression {
 
    pub subject: ExpressionId,
 
    // Validator/linker
 
    pub parent: ExpressionParent,
 
    pub unique_id_in_definition: i32,
 
    // Typing
 
    pub type_index: i32,
 
}
 

	
 
#[derive(Debug, Clone)]
 
@@ -1739,15 +1804,16 @@ pub struct CallExpression {
 
    pub parser_type: ParserType, // of the function call, not the return type
 
    pub method: Method,
 
    pub arguments: Vec<ExpressionId>,
 
    pub definition: DefinitionId,
 
    pub procedure: ProcedureDefinitionId,
 
    // Validator/Linker
 
    pub parent: ExpressionParent,
 
    pub unique_id_in_definition: i32,
 
    // Typing
 
    pub type_index: i32,
 
}
 

	
 
#[derive(Debug, Clone, PartialEq, Eq)]
 
pub enum Method {
 
    // Builtin
 
    // Builtin, accessible by programmer
 
    Get,
 
    Put,
 
    Fires,
 
@@ -1755,14 +1821,31 @@ pub enum Method {
 
    Length,
 
    Assert,
 
    Print,
 
    // Builtin, not accessible by programmer
 
    SelectStart, // SelectStart(total_num_cases, total_num_ports)
 
    SelectRegisterCasePort, // SelectRegisterCasePort(case_index, port_index, port_id)
 
    SelectWait, // SelectWait() -> u32
 
    // User-defined
 
    UserFunction,
 
    UserComponent,
 
}
 

	
 
#[derive(Debug, Clone)]
 
pub struct MethodSymbolic {
 
    pub(crate) parser_type: ParserType,
 
    pub(crate) definition: DefinitionId
 
impl Method {
 
    pub(crate) fn is_public_builtin(&self) -> bool {
 
        use Method::*;
 
        match self {
 
            Get | Put | Fires | Create | Length | Assert | Print => true,
 
            _ => false,
 
        }
 
    }
 

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

	
 
#[derive(Debug, Clone)]
 
@@ -1773,7 +1856,8 @@ pub struct LiteralExpression {
 
    pub value: Literal,
 
    // Validator/Linker
 
    pub parent: ExpressionParent,
 
    pub unique_id_in_definition: i32,
 
    // Typing
 
    pub type_index: i32,
 
}
 

	
 
#[derive(Debug, Clone)]
 
@@ -1870,5 +1954,6 @@ pub struct VariableExpression {
 
    pub declaration: Option<VariableId>,
 
    pub used_as_binding_target: bool,
 
    pub parent: ExpressionParent,
 
    pub unique_id_in_definition: i32,
 
    // Typing
 
    pub type_index: i32,
 
}
 
\ No newline at end of file
src/protocol/ast_printer.rs
Show inline comments
 
@@ -345,7 +345,7 @@ impl ASTWriter {
 
                    }
 
                }
 
            }
 
            Definition::Function(def) => {
 
            Definition::Procedure(def) => {
 
                self.kv(indent).with_id(PREFIX_FUNCTION_ID, def.this.0.index)
 
                    .with_s_key("DefinitionFunction");
 

	
 
@@ -354,10 +354,10 @@ impl ASTWriter {
 
                    self.kv(indent3).with_s_key("PolyVar").with_identifier_val(&poly_var_id);
 
                }
 

	
 
                self.kv(indent2).with_s_key("ReturnParserTypes");
 
                for return_type in &def.return_types {
 
                    self.kv(indent3).with_s_key("ReturnParserType")
 
                        .with_custom_val(|s| write_parser_type(s, heap, return_type));
 
                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");
 
@@ -368,26 +368,6 @@ impl ASTWriter {
 
                self.kv(indent2).with_s_key("Body");
 
                self.write_stmt(heap, def.body.upcast(), indent3);
 
            },
 
            Definition::Component(def) => {
 
                self.kv(indent).with_id(PREFIX_COMPONENT_ID,def.this.0.index)
 
                    .with_s_key("DefinitionComponent");
 

	
 
                self.kv(indent2).with_s_key("Name").with_identifier_val(&def.identifier);
 
                self.kv(indent2).with_s_key("Variant").with_debug_val(&def.variant);
 

	
 
                self.kv(indent2).with_s_key("PolymorphicVariables");
 
                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("Parameters");
 
                for variable_id in &def.parameters {
 
                    self.write_variable(heap, *variable_id, indent3)
 
                }
 

	
 
                self.kv(indent2).with_s_key("Body");
 
                self.write_stmt(heap, def.body.upcast(), indent3);
 
            }
 
        }
 
    }
 

	
 
@@ -401,9 +381,7 @@ impl ASTWriter {
 
                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("FirstUniqueScopeID").with_disp_val(&stmt.first_unique_id_in_scope);
 
                self.kv(indent2).with_s_key("NextUniqueScopeID").with_disp_val(&stmt.next_unique_id_in_scope);
 
                self.kv(indent2).with_s_key("RelativePos").with_disp_val(&stmt.scope_node.relative_pos_in_parent);
 
                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 {
 
@@ -457,11 +435,11 @@ impl ASTWriter {
 
                self.write_expr(heap, stmt.test, indent3);
 

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

	
 
                if let Some(false_body) = stmt.false_body {
 
                if let Some(false_body) = stmt.false_case {
 
                    self.kv(indent2).with_s_key("FalseBody");
 
                    self.write_stmt(heap, false_body.upcast(), indent3);
 
                    self.write_stmt(heap, false_body.body, indent3);
 
                }
 
            },
 
            Statement::EndIf(stmt) => {
 
@@ -480,7 +458,7 @@ impl ASTWriter {
 
                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.upcast(), indent3);
 
                self.write_stmt(heap, stmt.body, indent3);
 
            },
 
            Statement::EndWhile(stmt) => {
 
                self.kv(indent).with_id(PREFIX_ENDWHILE_STMT_ID, stmt.this.0.index)
 
@@ -509,7 +487,7 @@ impl ASTWriter {
 
                    .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.upcast(), indent3);
 
                self.write_stmt(heap, stmt.body, indent3);
 
            },
 
            Statement::EndSynchronous(stmt) => {
 
                self.kv(indent).with_id(PREFIX_ENDSYNC_STMT_ID, stmt.this.0.index)
 
@@ -522,11 +500,11 @@ impl ASTWriter {
 
                    .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.upcast(), indent3);
 
                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.upcast(), indent3);
 
                    self.write_stmt(heap, right_body_id, indent3);
 
                }
 
            },
 
            Statement::EndFork(stmt) => {
 
@@ -547,8 +525,10 @@ impl ASTWriter {
 
                    self.write_stmt(heap, case.guard, indent4);
 

	
 
                    self.kv(indent3).with_s_key("Block");
 
                    self.write_stmt(heap, case.block.upcast(), indent4);
 
                    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)
 
@@ -596,6 +576,7 @@ impl ASTWriter {
 
            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);
 
@@ -607,6 +588,7 @@ impl ASTWriter {
 
            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");
 
@@ -617,6 +599,7 @@ impl ASTWriter {
 
            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");
 
@@ -629,6 +612,7 @@ impl ASTWriter {
 
            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);
 
@@ -640,6 +624,7 @@ impl ASTWriter {
 
            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);
 
@@ -649,6 +634,7 @@ impl ASTWriter {
 
            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");
 
@@ -659,6 +645,7 @@ impl ASTWriter {
 
            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");
 
@@ -671,6 +658,7 @@ impl ASTWriter {
 
            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);
 

	
 
@@ -690,6 +678,7 @@ impl ASTWriter {
 
                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"); },
 
@@ -765,6 +754,7 @@ impl ASTWriter {
 
            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");
 
@@ -776,21 +766,16 @@ impl ASTWriter {
 
                self.kv(indent).with_id(PREFIX_CALL_EXPR_ID, expr.this.0.index)
 
                    .with_s_key("CallExpr");
 

	
 
                let definition = &heap[expr.definition];
 
                match definition {
 
                    Definition::Component(definition) => {
 
                        self.kv(indent2).with_s_key("BuiltIn").with_disp_val(&false);
 
                        self.kv(indent2).with_s_key("Variant").with_debug_val(&definition.variant);
 
                    },
 
                    Definition::Function(definition) => {
 
                        self.kv(indent2).with_s_key("BuiltIn").with_disp_val(&definition.builtin);
 
                        self.kv(indent2).with_s_key("Variant").with_s_val("Function");
 
                    },
 
                    _ => unreachable!()
 
                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("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));
 
                }
 
                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");
 
@@ -805,6 +790,7 @@ impl ASTWriter {
 
            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));
 
@@ -825,7 +811,7 @@ impl ASTWriter {
 
        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_block);
 
        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);
 
    }
 

	
 
@@ -854,6 +840,11 @@ fn write_option<V: Display>(target: &mut String, value: Option<V>) {
 
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 {
 
@@ -950,7 +941,7 @@ fn write_concrete_type(target: &mut String, heap: &Heap, def_id: DefinitionId, t
 
        match &t.parts[idx] {
 
            CTP::Void => target.push_str("void"),
 
            CTP::Message => target.push_str("msg"),
 
            CTP::Bool => target.push_str("bool"),
 
            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),
 
@@ -961,6 +952,7 @@ fn write_concrete_type(target: &mut String, heap: &Heap, def_id: DefinitionId, t
 
            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("[]");
src/protocol/eval/error.rs
Show inline comments
 
@@ -52,18 +52,8 @@ impl EvalError {
 
            let statement = &heap[frame.position];
 
            let statement_span = statement.span();
 

	
 
            let (root_id, procedure, is_func) = match definition {
 
                Definition::Function(def) => {
 
                    (def.defined_in, def.identifier.value.as_str().to_string(), true)
 
                },
 
                Definition::Component(def) => {
 
                    (def.defined_in, def.identifier.value.as_str().to_string(), false)
 
                },
 
                _ => unreachable!("construct stack frame with definition pointing to data type")
 
            };
 

	
 
            // Lookup module name, if it has one
 
            let module = modules.iter().find(|m| m.root_id == root_id).unwrap();
 
            let module = modules.iter().find(|m| m.root_id == definition.defined_in).unwrap();
 
            let module_name = if let Some(name) = &module.name {
 
                name.as_str().to_string()
 
            } else {
 
@@ -74,8 +64,8 @@ impl EvalError {
 
            frames.push(EvalFrame{
 
                line: statement_span.begin.line,
 
                module_name,
 
                procedure,
 
                is_func
 
                procedure: definition.identifier.value.as_str().to_string(),
 
                is_func: definition.kind == ProcedureKind::Function,
 
            });
 
        }
 

	
src/protocol/eval/executor.rs
Show inline comments
 
@@ -26,8 +26,9 @@ pub(crate) enum ExprInstruction {
 

	
 
#[derive(Debug, Clone)]
 
pub(crate) struct Frame {
 
    pub(crate) definition: DefinitionId,
 
    pub(crate) monomorph_idx: i32,
 
    pub(crate) definition: ProcedureDefinitionId,
 
    pub(crate) monomorph_type_id: TypeId,
 
    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
 
@@ -36,37 +37,34 @@ pub(crate) struct Frame {
 

	
 
impl Frame {
 
    /// Creates a new execution frame. Does not modify the stack in any way.
 
    pub fn new(heap: &Heap, definition_id: DefinitionId, monomorph_idx: i32) -> Self {
 
    pub fn new(heap: &Heap, definition_id: ProcedureDefinitionId, monomorph_type_id: TypeId, monomorph_index: u32) -> Self {
 
        let definition = &heap[definition_id];
 
        let first_statement = match definition {
 
            Definition::Component(definition) => definition.body,
 
            Definition::Function(definition) => definition.body,
 
            _ => unreachable!("initializing frame with {:?} instead of a function/component", definition),
 
        };
 
        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, block_id: BlockStatementId, max_size: &mut u32) {
 
            let block_stmt = &heap[block_id];
 
            debug_assert!(block_stmt.next_unique_id_in_scope >= 0);
 
        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 = block_stmt.next_unique_id_in_scope as u32;
 
            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 &block_stmt.scope_node.nested {
 
                determine_max_stack_size(heap, child_scope.to_block(), max_size);
 
            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, first_statement, &mut max_stack_size);
 
        determine_max_stack_size(heap, outer_scope_id, &mut max_stack_size);
 

	
 
        Frame{
 
            definition: definition_id,
 
            monomorph_idx,
 
            position: first_statement.upcast(),
 
            monomorph_type_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,
 
@@ -209,10 +207,13 @@ pub enum EvalContinuation {
 
    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(DefinitionId, i32, ValueGroup),
 
    NewComponent(ProcedureDefinitionId, TypeId, ValueGroup),
 
    NewChannel,
 
}
 

	
 
@@ -225,14 +226,15 @@ pub struct Prompt {
 
}
 

	
 
impl Prompt {
 
    pub fn new(_types: &TypeTable, heap: &Heap, def: DefinitionId, monomorph_idx: i32, args: ValueGroup) -> Self {
 
    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 new_frame = Frame::new(heap, def, monomorph_idx);
 
        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);
 
@@ -292,13 +294,13 @@ impl Prompt {
 
        // 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].is_function() {
 
            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());
 
        debug_log!("Taking step in '{}'", heap[cur_frame.definition].identifier.value.as_str());
 

	
 
        // Execute all pending expressions
 
        while !cur_frame.expr_stack.is_empty() {
 
@@ -480,8 +482,8 @@ impl Prompt {
 
                        },
 
                        Expression::Select(expr) => {
 
                            let subject= cur_frame.expr_values.pop_back().unwrap();
 
                            let mono_data = types.get_procedure_monomorph(cur_frame.monomorph_idx);
 
                            let field_idx = mono_data.expr_data[expr.unique_id_in_definition as usize].field_or_monomorph_idx as u32;
 
                            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.
 
@@ -528,8 +530,9 @@ impl Prompt {
 
                                }
 
                                Literal::Integer(lit_value) => {
 
                                    use ConcreteTypePart as CTP;
 
                                    let def_types = types.get_procedure_monomorph(cur_frame.monomorph_idx);
 
                                    let concrete_type = &def_types.expr_data[expr.unique_id_in_definition as usize].expr_type;
 
                                    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] {
 
@@ -576,14 +579,15 @@ impl Prompt {
 
                            cur_frame.expr_values.push_back(value);
 
                        },
 
                        Expression::Cast(expr) => {
 
                            let mono_data = types.get_procedure_monomorph(cur_frame.monomorph_idx);
 
                            let output_type = &mono_data.expr_data[expr.unique_id_in_definition as usize].expr_type;
 
                            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, output_type, &subject) {
 
                            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));
 
@@ -651,12 +655,7 @@ impl Prompt {
 
                                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 = match port_value_deref {
 
                                        Value::Input(port_id) => port_id,
 
                                        Value::Output(port_id) => port_id,
 
                                        _ => unreachable!("executor calling 'fires' on value {:?}", port_value_deref),
 
                                    };
 
                                    let port_id = port_value_deref.as_port_id();
 

	
 
                                    match ctx.fires(port_id) {
 
                                        None => {
 
@@ -734,10 +733,34 @@ impl Prompt {
 
                                    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::UserComponent => {
 
                                    // This is actually handled by the evaluation
 
                                    // of the statement.
 
                                    debug_assert_eq!(heap[expr.definition].parameters().len(), cur_frame.expr_values.len());
 
                                    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::UserFunction => {
 
@@ -758,11 +781,11 @@ impl Prompt {
 
                                    }
 

	
 
                                    // Determine the monomorph index of the function we're calling
 
                                    let mono_data = types.get_procedure_monomorph(cur_frame.monomorph_idx);
 
                                    let call_data = &mono_data.expr_data[expr.unique_id_in_definition as usize];
 
                                    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.definition, call_data.field_or_monomorph_idx);
 
                                    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;
 
@@ -771,7 +794,7 @@ impl Prompt {
 
                                    // 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) => {
 
@@ -817,7 +840,8 @@ impl Prompt {
 
            },
 
            Statement::EndBlock(stmt) => {
 
                let block = &heap[stmt.start_block];
 
                self.store.clear_stack(block.first_unique_id_in_scope as usize);
 
                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)
 
@@ -825,13 +849,13 @@ impl Prompt {
 
            Statement::Local(stmt) => {
 
                match stmt {
 
                    LocalStatement::Memory(stmt) => {
 
                        if cfg!(debug_assertions) {
 
                        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)
 
@@ -842,7 +866,7 @@ impl Prompt {
 
                        match ctx.created_channel() {
 
                            None => {
 
                                // No channel is pending. So request one
 
                                Ok(EvalContinuation::NewChannel)
 
                                    Ok(EvalContinuation::NewChannel)
 
                            },
 
                            Some((put_port, get_port)) => {
 
                                self.store.write(ValueId::Stack(heap[stmt.from].unique_id_in_scope as u32), put_port);
 
@@ -864,9 +888,9 @@ impl Prompt {
 
                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_body.upcast();
 
                } else if let Some(false_body) = stmt.false_body {
 
                    cur_frame.position = false_body.upcast();
 
                    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();
 
@@ -876,6 +900,13 @@ impl Prompt {
 
            },
 
            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) => {
 
@@ -883,7 +914,7 @@ impl Prompt {
 
                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.upcast();
 
                    cur_frame.position = stmt.body;
 
                } else {
 
                    cur_frame.position = stmt.end_while.upcast();
 
                }
 
@@ -892,7 +923,9 @@ impl Prompt {
 
            },
 
            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) => {
 
@@ -906,27 +939,30 @@ impl Prompt {
 
                Ok(EvalContinuation::Stepping)
 
            },
 
            Statement::Synchronous(stmt) => {
 
                cur_frame.position = stmt.body.upcast();
 
                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
 
                    cur_frame.position = stmt.left_body.upcast();
 
                    cur_frame.position = stmt.left_body;
 
                } else {
 
                    // Need to fork
 
                    if let Some(go_left) = ctx.performed_fork() {
 
                        // Runtime has created a fork
 
                        if go_left {
 
                            cur_frame.position = stmt.left_body.upcast();
 
                            cur_frame.position = stmt.left_body;
 
                        } else {
 
                            cur_frame.position = stmt.right_body.unwrap().upcast();
 
                            cur_frame.position = stmt.right_body.unwrap();
 
                        }
 
                    } else {
 
                        // Request the runtime to create a fork of the current
 
@@ -942,16 +978,24 @@ impl Prompt {
 

	
 
                Ok(EvalContinuation::Stepping)
 
            },
 
            Statement::Select(_stmt) => {
 
                todo!("implement select evaluation")
 
            Statement::Select(stmt) => {
 
                // This is a trampoline for the statements that were placed by
 
                // the AST transformation pass
 
                cur_frame.position = stmt.next;
 

	
 
                Ok(EvalContinuation::Stepping)
 
            },
 
            Statement::EndSelect(stmt) => {
 
                cur_frame.position = stmt.next;
 
                let start_select = &heap[stmt.start_select];
 
                if let Some(select_case) = start_select.cases.first() {
 
                    let scope = &heap[select_case.scope];
 
                    self.store.clear_stack(scope.first_unique_id_in_scope as usize);
 
                }
 

	
 
                Ok(EvalContinuation::Stepping)
 
            },
 
            Statement::Return(_stmt) => {
 
                debug_assert!(heap[cur_frame.definition].is_function());
 
                debug_assert_eq!(cur_frame.expr_values.len(), 1, "expected one expr value for return statement");
 

	
 
                // The preceding frame has executed a call, so is expecting the
 
@@ -998,14 +1042,13 @@ impl Prompt {
 
            },
 
            Statement::New(stmt) => {
 
                let call_expr = &heap[stmt.expression];
 
                debug_assert!(heap[call_expr.definition].is_component());
 
                debug_assert_eq!(
 
                    cur_frame.expr_values.len(), heap[call_expr.definition].parameters().len(),
 
                    cur_frame.expr_values.len(), heap[call_expr.procedure].parameters.len(),
 
                    "mismatch in expr stack size and number of arguments for new statement"
 
                );
 

	
 
                let mono_data = types.get_procedure_monomorph(cur_frame.monomorph_idx);
 
                let expr_data = &mono_data.expr_data[call_expr.unique_id_in_definition as usize];
 
                let mono_data = &heap[cur_frame.definition].monomorphs[cur_frame.monomorph_index];
 
                let type_id = mono_data.expr_info[call_expr.type_index as usize].variant.as_procedure().0;
 

	
 
                // Note that due to expression value evaluation they exist in
 
                // reverse order on the stack.
 
@@ -1017,7 +1060,6 @@ impl Prompt {
 

	
 
                // Construct argument group, thereby copying heap regions
 
                let argument_group = ValueGroup::from_store(&self.store, &args);
 
                // println!("Creating {} with\n{:#?}", heap[call_expr.definition].identifier().value.as_str(), argument_group);
 

	
 
                // Clear any heap regions
 
                for arg in &args {
 
@@ -1026,7 +1068,7 @@ impl Prompt {
 

	
 
                cur_frame.position = stmt.next;
 

	
 
                Ok(EvalContinuation::NewComponent(call_expr.definition, expr_data.field_or_monomorph_idx, argument_group))
 
                Ok(EvalContinuation::NewComponent(call_expr.procedure, type_id, argument_group))
 
            },
 
            Statement::Expression(stmt) => {
 
                // The expression has just been completely evaluated. Some
src/protocol/eval/value.rs
Show inline comments
 
@@ -65,39 +65,26 @@ pub enum Value {
 
    Struct(HeapPos),
 
}
 

	
 
macro_rules! impl_union_unpack_as_value {
 
    ($func_name:ident, $variant_name:path, $return_type:ty) => {
 
        impl Value {
 
            pub(crate) fn $func_name(&self) -> $return_type {
 
                match self {
 
                    $variant_name(v) => *v,
 
                    _ => panic!(concat!("called ", stringify!($func_name()), " on {:?}"), self),
 
                }
 
            }
 
        }
 
    }
 
}
 

	
 
impl_union_unpack_as_value!(as_stack_boundary, Value::PrevStackBoundary, isize);
 
impl_union_unpack_as_value!(as_ref,     Value::Ref,     ValueId);
 
impl_union_unpack_as_value!(as_input,   Value::Input,   PortId);
 
impl_union_unpack_as_value!(as_output,  Value::Output,  PortId);
 
impl_union_unpack_as_value!(as_message, Value::Message, HeapPos);
 
impl_union_unpack_as_value!(as_bool,    Value::Bool,    bool);
 
impl_union_unpack_as_value!(as_char,    Value::Char,    char);
 
impl_union_unpack_as_value!(as_string,  Value::String,  HeapPos);
 
impl_union_unpack_as_value!(as_uint8,   Value::UInt8,   u8);
 
impl_union_unpack_as_value!(as_uint16,  Value::UInt16,  u16);
 
impl_union_unpack_as_value!(as_uint32,  Value::UInt32,  u32);
 
impl_union_unpack_as_value!(as_uint64,  Value::UInt64,  u64);
 
impl_union_unpack_as_value!(as_sint8,   Value::SInt8,   i8);
 
impl_union_unpack_as_value!(as_sint16,  Value::SInt16,  i16);
 
impl_union_unpack_as_value!(as_sint32,  Value::SInt32,  i32);
 
impl_union_unpack_as_value!(as_sint64,  Value::SInt64,  i64);
 
impl_union_unpack_as_value!(as_array,   Value::Array,   HeapPos);
 
impl_union_unpack_as_value!(as_tuple,   Value::Tuple,   HeapPos);
 
impl_union_unpack_as_value!(as_enum,    Value::Enum,    i64);
 
impl_union_unpack_as_value!(as_struct,  Value::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) {
 
@@ -107,6 +94,14 @@ impl Value {
 
        }
 
    }
 

	
 
    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(_) |
 
@@ -520,7 +515,7 @@ pub(crate) fn apply_unary_operator(store: &mut Store, op: UnaryOperator, value:
 
                Value::SInt32(v) => Value::SInt32($apply *v),
 
                Value::SInt64(v) => Value::SInt64($apply *v),
 
                _ => unreachable!("apply_unary_operator {:?} on value {:?}", $op, $value),
 
            };
 
            }
 
        }
 
    }
 

	
 
@@ -542,7 +537,7 @@ pub(crate) fn apply_unary_operator(store: &mut Store, op: UnaryOperator, value:
 
                _ => unreachable!("apply_unary_operator {:?} on value {:?}", op, value),
 
            }
 
        },
 
        UO::BitwiseNot => { apply_int_expr_and_return!(value, !, op)},
 
        UO::BitwiseNot => { apply_int_expr_and_return!(value, !, op); },
 
        UO::LogicalNot => { return Value::Bool(!value.as_bool()); },
 
    }
 
}
src/protocol/input_source.rs
Show inline comments
 
@@ -21,9 +21,10 @@ pub struct InputSpan {
 
}
 

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

	
src/protocol/mod.rs
Show inline comments
 
@@ -16,6 +16,8 @@ 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,
 
@@ -92,29 +94,31 @@ impl ProtocolDescription {
 
        let definition_id = definition_id.unwrap();
 

	
 
        let ast_definition = &self.heap[definition_id];
 
        if !ast_definition.is_component() {
 
        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_component();
 
        if !ast_definition.poly_vars.is_empty() {
 
        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(definition_id, 0)] };
 
        let mono_index = self.types.get_procedure_monomorph_index(&definition_id, &concrete_type.parts).unwrap();
 
        let mono_type = self.types.get_procedure_monomorph(mono_index);
 
        if mono_type.arg_types.len() != arguments.values.len() {
 
        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_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 = &mono_type.arg_types[arg_idx];
 
            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));
 
@@ -123,7 +127,7 @@ impl ProtocolDescription {
 

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

	
 
    fn lookup_module_root(&self, module_name: &[u8]) -> Option<RootId> {
 
@@ -145,7 +149,7 @@ impl ProtocolDescription {
 
        use ConcreteTypePart as CTP;
 

	
 
        match &expected.parts[expected_idx] {
 
            CTP::Void | CTP::Message | CTP::Slice | CTP::Function(_, _) | CTP::Component(_, _) => unreachable!(),
 
            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 },
 
@@ -211,6 +215,7 @@ pub trait RunContext {
 
    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 {
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;
 
@@ -8,8 +9,9 @@ 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;
 
mod visitor;
 
pub(crate) mod pass_stack_size;
 

	
 
use tokens::*;
 
use crate::collections::*;
 
@@ -20,13 +22,16 @@ 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::TypeTable;
 
use type_table::*;
 

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

	
 
use crate::protocol::ast_printer::ASTWriter;
 
use crate::protocol::parser::type_table::PolymorphicVariable;
 

	
 
#[derive(Debug, PartialEq, Eq, PartialOrd, Ord)]
 
pub enum ModuleCompilationPhase {
 
@@ -36,8 +41,10 @@ pub enum ModuleCompilationPhase {
 
    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:
 
    // Typed,                  // Type inference and checking has been performed
 
    // StackSize
 
}
 

	
 
pub struct Module {
 
@@ -51,16 +58,50 @@ pub struct Module {
 
    pub phase: ModuleCompilationPhase,
 
}
 

	
 
// TODO: This is kind of wrong. Because when we're producing bytecode we would
 
//       like the bytecode itself to not have the notion of the size of a pointer
 
//       type. But until I figure out what we do want I'll just set everything
 
//       to a 64-bit architecture.
 
pub struct TargetArch {
 
    pub array_size_alignment: (usize, usize),
 
    pub slice_size_alignment: (usize, usize),
 
    pub string_size_alignment: (usize, usize),
 
    pub port_size_alignment: (usize, usize),
 
    pub pointer_size_alignment: (usize, usize),
 
    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> {
 
@@ -85,6 +126,8 @@ pub struct Parser {
 
    pass_definitions: PassDefinitions,
 
    pass_validation: PassValidationLinking,
 
    pass_typing: PassTyping,
 
    pass_rewriting: PassRewriting,
 
    pass_stack_size: PassStackSize,
 
    // Compiler options
 
    pub write_ast_to: Option<String>,
 
    pub(crate) arch: TargetArch,
 
@@ -104,18 +147,36 @@ impl Parser {
 
            pass_definitions: PassDefinitions::new(),
 
            pass_validation: PassValidationLinking::new(),
 
            pass_typing: PassTyping::new(),
 
            pass_rewriting: PassRewriting::new(),
 
            pass_stack_size: PassStackSize::new(),
 
            write_ast_to: None,
 
            arch: TargetArch {
 
                array_size_alignment: (3*8, 8), // pointer, length, capacity
 
                slice_size_alignment: (2*8, 8), // pointer, length
 
                string_size_alignment: (3*8, 8), // pointer, length, capacity
 
                port_size_alignment: (3*4, 4), // two u32s: connector + port ID
 
                pointer_size_alignment: (8, 8),
 
            }
 
            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 {
 
@@ -237,10 +298,10 @@ impl Parser {
 
                types: &mut self.type_table,
 
                arch: &self.arch,
 
            };
 
            PassTyping::queue_module_definitions(&mut ctx, &mut queue);
 
            self.pass_typing.queue_module_definitions(&mut ctx, &mut queue);
 
        };
 
        while !queue.is_empty() {
 
            let top = queue.pop().unwrap();
 
            let top = queue.pop_front().unwrap();
 
            let mut ctx = visitor::Ctx{
 
                heap: &mut self.heap,
 
                modules: &mut self.modules,
 
@@ -252,6 +313,21 @@ impl Parser {
 
            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();
 
@@ -263,49 +339,74 @@ impl Parser {
 
    }
 
}
 

	
 
// Note: args and return type need to be a function because we need to know the function ID.
 
fn insert_builtin_function<T: Fn(FunctionDefinitionId) -> (Vec<(&'static str, ParserType)>, ParserType)> (
 
    p: &mut Parser, func_name: &str, polymorphic: &[&str], arg_and_return_fn: T) {
 
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 mut poly_vars = Vec::with_capacity(polymorphic.len());
 
    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);
 
}
 

	
 
// 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 {
 
        poly_vars.push(Identifier{ span: InputSpan::new(), value: p.string_pool.intern(poly_var.as_bytes()) });
 
        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 func_id = p.heap.alloc_function_definition(|this| FunctionDefinition{
 
    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,
 
        return_types: Vec::new(),
 
        poly_vars: ast_poly_vars,
 
        return_type: None,
 
        parameters: Vec::new(),
 
        scope: ScopeId::new_invalid(),
 
        body: BlockStatementId::new_invalid(),
 
        num_expressions_in_body: -1,
 
        monomorphs: Vec::new(),
 
    });
 

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

	
 
    let mut parameters = Vec::with_capacity(args.len());
 
    for (arg_name, arg_type) in args {
 
    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_block: 0,
 
            relative_pos_in_parent: 0,
 
            unique_id_in_scope: 0
 
        });
 
        parameters.push(param_id);
 
    }
 

	
 
    let func = &mut p.heap[func_id];
 
    let func = &mut p.heap[procedure_id];
 
    func.parameters = parameters;
 
    func.return_types.push(ret);
 
    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{
 
@@ -315,7 +416,16 @@ fn insert_builtin_function<T: Fn(FunctionDefinitionId) -> (Vec<(&'static str, Pa
 
            identifier_span: InputSpan::new(),
 
            imported_at: None,
 
            class: DefinitionClass::Function,
 
            definition_id: func_id.upcast(),
 
            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
src/protocol/parser/pass_definitions.rs
Show inline comments
 
@@ -273,35 +273,22 @@ impl PassDefinitions {
 

	
 
        // Consume return types
 
        consume_token(&module.source, iter, TokenKind::ArrowRight)?;
 
        let mut return_types = self.parser_types.start_section();
 
        let mut open_curly_pos = iter.last_valid_pos(); // bogus value
 
        consume_comma_separated_until(
 
            TokenKind::OpenCurly, &module.source, iter, ctx,
 
            |source, iter, ctx| {
 
                let poly_vars = ctx.heap[definition_id].poly_vars();
 
                self.type_parser.consume_parser_type(
 
                    iter, &ctx.heap, source, &ctx.symbols, poly_vars, definition_id,
 
                    module_scope, false, None
 
                )
 
            },
 
            &mut return_types, "a return type", Some(&mut open_curly_pos)
 
        let poly_vars = ctx.heap[definition_id].poly_vars();
 
        let parser_type = self.type_parser.consume_parser_type(
 
            iter, &ctx.heap, &module.source, &ctx.symbols, poly_vars, definition_id,
 
            module_scope, false, None
 
        )?;
 
        let return_types = return_types.into_vec();
 

	
 
        match return_types.len() {
 
            0 => return Err(ParseError::new_error_str_at_pos(&module.source, open_curly_pos, "expected a return type")),
 
            1 => {},
 
            _ => return Err(ParseError::new_error_str_at_pos(&module.source, open_curly_pos, "multiple return types are not (yet) allowed")),
 
        }
 

	
 
        // Consume block
 
        let body = self.consume_block_statement_without_leading_curly(module, iter, ctx, open_curly_pos)?;
 
        // Consume block and the definition's scope
 
        let body_id = self.consume_block_statement(module, iter, ctx)?;
 
        let scope_id = ctx.heap.alloc_scope(|this| Scope::new(this, ScopeAssociation::Definition(definition_id)));
 

	
 
        // Assign everything in the preallocated AST node
 
        let function = ctx.heap[definition_id].as_function_mut();
 
        function.return_types = return_types;
 
        let function = ctx.heap[definition_id].as_procedure_mut();
 
        function.return_type = Some(parser_type);
 
        function.parameters = parameters;
 
        function.body = body;
 
        function.scope = scope_id;
 
        function.body = body_id;
 

	
 
        Ok(())
 
    }
 
@@ -330,197 +317,109 @@ impl PassDefinitions {
 
        let parameters = parameter_section.into_vec();
 

	
 
        // Consume block
 
        let body = self.consume_block_statement(module, iter, ctx)?;
 
        let body_id = self.consume_block_statement(module, iter, ctx)?;
 
        let scope_id = ctx.heap.alloc_scope(|this| Scope::new(this, ScopeAssociation::Definition(definition_id)));
 

	
 
        // Assign everything in the AST node
 
        let component = ctx.heap[definition_id].as_component_mut();
 
        let component = ctx.heap[definition_id].as_procedure_mut();
 
        debug_assert!(component.return_type.is_none());
 
        component.parameters = parameters;
 
        component.body = body;
 
        component.scope = scope_id;
 
        component.body = body_id;
 

	
 
        Ok(())
 
    }
 

	
 
    /// Consumes a block statement. If the resulting statement is not a block
 
    /// (e.g. for a shorthand "if (expr) single_statement") then it will be
 
    /// wrapped in one
 
    fn consume_block_or_wrapped_statement(
 
        &mut self, module: &Module, iter: &mut TokenIter, ctx: &mut PassCtx
 
    ) -> Result<BlockStatementId, ParseError> {
 
        if Some(TokenKind::OpenCurly) == iter.next() {
 
            // This is a block statement
 
            self.consume_block_statement(module, iter, ctx)
 
        } else {
 
            // Not a block statement, so wrap it in one
 
            let mut statements = self.statements.start_section();
 
            let wrap_begin_pos = iter.last_valid_pos();
 
            self.consume_statement(module, iter, ctx, &mut statements)?;
 
            let wrap_end_pos = iter.last_valid_pos();
 

	
 
            let statements = statements.into_vec();
 

	
 
            let id = ctx.heap.alloc_block_statement(|this| BlockStatement{
 
                this,
 
                is_implicit: true,
 
                span: InputSpan::from_positions(wrap_begin_pos, wrap_end_pos),
 
                statements,
 
                end_block: EndBlockStatementId::new_invalid(),
 
                scope_node: ScopeNode::new_invalid(),
 
                first_unique_id_in_scope: -1,
 
                next_unique_id_in_scope: -1,
 
                locals: Vec::new(),
 
                labels: Vec::new(),
 
                next: StatementId::new_invalid(),
 
            });
 

	
 
            let end_block = ctx.heap.alloc_end_block_statement(|this| EndBlockStatement{
 
                this, start_block: id, next: StatementId::new_invalid()
 
            });
 

	
 
            let block_stmt = &mut ctx.heap[id];
 
            block_stmt.end_block = end_block;
 

	
 
            Ok(id)
 
        }
 
    }
 

	
 
    /// Consumes a statement and returns a boolean indicating whether it was a
 
    /// block or not.
 
    fn consume_statement(
 
        &mut self, module: &Module, iter: &mut TokenIter, ctx: &mut PassCtx, section: &mut ScopedSection<StatementId>
 
    ) -> Result<(), ParseError> {
 
    fn consume_statement(&mut self, module: &Module, iter: &mut TokenIter, ctx: &mut PassCtx) -> Result<StatementId, ParseError> {
 
        let next = iter.next().expect("consume_statement has a next token");
 

	
 
        if next == TokenKind::OpenCurly {
 
            let id = self.consume_block_statement(module, iter, ctx)?;
 
            section.push(id.upcast());
 
            return Ok(id.upcast());
 
        } else if next == TokenKind::Ident {
 
            let ident = peek_ident(&module.source, iter).unwrap();
 
            if ident == KW_STMT_IF {
 
                // Consume if statement and place end-if statement directly
 
                // after it.
 
                let id = self.consume_if_statement(module, iter, ctx)?;
 
                section.push(id.upcast());
 

	
 
                let end_if = ctx.heap.alloc_end_if_statement(|this| EndIfStatement {
 
                    this,
 
                    start_if: id,
 
                    next: StatementId::new_invalid()
 
                });
 
                section.push(end_if.upcast());
 

	
 
                let if_stmt = &mut ctx.heap[id];
 
                if_stmt.end_if = end_if;
 
                return Ok(id.upcast());
 
            } else if ident == KW_STMT_WHILE {
 
                let id = self.consume_while_statement(module, iter, ctx)?;
 
                section.push(id.upcast());
 

	
 
                let end_while = ctx.heap.alloc_end_while_statement(|this| EndWhileStatement {
 
                    this,
 
                    start_while: id,
 
                    next: StatementId::new_invalid()
 
                });
 
                section.push(end_while.upcast());
 

	
 
                let while_stmt = &mut ctx.heap[id];
 
                while_stmt.end_while = end_while;
 
                return Ok(id.upcast());
 
            } else if ident == KW_STMT_BREAK {
 
                let id = self.consume_break_statement(module, iter, ctx)?;
 
                section.push(id.upcast());
 
                return Ok(id.upcast());
 
            } else if ident == KW_STMT_CONTINUE {
 
                let id = self.consume_continue_statement(module, iter, ctx)?;
 
                section.push(id.upcast());
 
                return Ok(id.upcast());
 
            } else if ident == KW_STMT_SYNC {
 
                let id = self.consume_synchronous_statement(module, iter, ctx)?;
 
                section.push(id.upcast());
 

	
 
                let end_sync = ctx.heap.alloc_end_synchronous_statement(|this| EndSynchronousStatement {
 
                    this,
 
                    start_sync: id,
 
                    next: StatementId::new_invalid()
 
                });
 
                section.push(end_sync.upcast());
 

	
 
                let sync_stmt = &mut ctx.heap[id];
 
                sync_stmt.end_sync = end_sync;
 
                return Ok(id.upcast());
 
            } else if ident == KW_STMT_FORK {
 
                let id = self.consume_fork_statement(module, iter, ctx)?;
 
                section.push(id.upcast());
 

	
 
                let end_fork = ctx.heap.alloc_end_fork_statement(|this| EndForkStatement {
 
                    this,
 
                    start_fork: id,
 
                    next: StatementId::new_invalid(),
 
                });
 
                section.push(end_fork.upcast());
 

	
 
                let fork_stmt = &mut ctx.heap[id];
 
                fork_stmt.end_fork = end_fork;
 

	
 
                return Ok(id.upcast());
 
            } else if ident == KW_STMT_SELECT {
 
                let id = self.consume_select_statement(module, iter, ctx)?;
 
                section.push(id.upcast());
 

	
 
                let end_select = ctx.heap.alloc_end_select_statement(|this| EndSelectStatement{
 
                    this,
 
                    start_select: id,
 
                    next: StatementId::new_invalid(),
 
                });
 
                section.push(end_select.upcast());
 

	
 
                let select_stmt = &mut ctx.heap[id];
 
                select_stmt.end_select = end_select;
 
                return Ok(id.upcast());
 
            } else if ident == KW_STMT_RETURN {
 
                let id = self.consume_return_statement(module, iter, ctx)?;
 
                section.push(id.upcast());
 
                return Ok(id.upcast());
 
            } else if ident == KW_STMT_GOTO {
 
                let id = self.consume_goto_statement(module, iter, ctx)?;
 
                section.push(id.upcast());
 
                return Ok(id.upcast());
 
            } else if ident == KW_STMT_NEW {
 
                let id = self.consume_new_statement(module, iter, ctx)?;
 
                section.push(id.upcast());
 
                return Ok(id.upcast());
 
            } else if ident == KW_STMT_CHANNEL {
 
                let id = self.consume_channel_statement(module, iter, ctx)?;
 
                section.push(id.upcast().upcast());
 
                return Ok(id.upcast().upcast());
 
            } else if iter.peek() == Some(TokenKind::Colon) {
 
                self.consume_labeled_statement(module, iter, ctx, section)?;
 
                let id = self.consume_labeled_statement(module, iter, ctx)?;
 
                return Ok(id.upcast());
 
            } else {
 
                // Two fallback possibilities: the first one is a memory
 
                // declaration, the other one is to parse it as a normal
 
                // expression. This is a bit ugly.
 
                if let Some(memory_stmt_id) = self.maybe_consume_memory_statement_without_semicolon(module, iter, ctx)? {
 
                    consume_token(&module.source, iter, TokenKind::SemiColon)?;
 
                    section.push(memory_stmt_id.upcast().upcast());
 
                    return Ok(memory_stmt_id.upcast().upcast());
 
                } else {
 
                    let id = self.consume_expression_statement(module, iter, ctx)?;
 
                    section.push(id.upcast());
 
                    return Ok(id.upcast());
 
                }
 
            }
 
        } else if next == TokenKind::OpenParen {
 
            // Same as above: memory statement or normal expression
 
            if let Some(memory_stmt_id) = self.maybe_consume_memory_statement_without_semicolon(module, iter, ctx)? {
 
                consume_token(&module.source, iter, TokenKind::SemiColon)?;
 
                section.push(memory_stmt_id.upcast().upcast());
 
                return Ok(memory_stmt_id.upcast().upcast());
 
            } else {
 
                let id = self.consume_expression_statement(module, iter, ctx)?;
 
                section.push(id.upcast());
 
                return Ok(id.upcast());
 
            }
 
        } else {
 
            let id = self.consume_expression_statement(module, iter, ctx)?;
 
            section.push(id.upcast());
 
            return Ok(id.upcast());
 
        }
 

	
 
        return Ok(());
 
    }
 

	
 
    fn consume_block_statement(
 
        &mut self, module: &Module, iter: &mut TokenIter, ctx: &mut PassCtx
 
    ) -> Result<BlockStatementId, ParseError> {
 
        let open_span = consume_token(&module.source, iter, TokenKind::OpenCurly)?;
 
        self.consume_block_statement_without_leading_curly(module, iter, ctx, open_span.begin)
 
    }
 
        let open_curly_span = consume_token(&module.source, iter, TokenKind::OpenCurly)?;
 

	
 
    fn consume_block_statement_without_leading_curly(
 
        &mut self, module: &Module, iter: &mut TokenIter, ctx: &mut PassCtx, open_curly_pos: InputPosition
 
    ) -> Result<BlockStatementId, ParseError> {
 
        let mut stmt_section = self.statements.start_section();
 
        let mut next = iter.next();
 
        while next != Some(TokenKind::CloseCurly) {
 
@@ -529,36 +428,34 @@ impl PassDefinitions {
 
                    &module.source, iter.last_valid_pos(), "expected a statement or '}'"
 
                ));
 
            }
 
            self.consume_statement(module, iter, ctx, &mut stmt_section)?;
 
            let stmt_id = self.consume_statement(module, iter, ctx)?;
 
            stmt_section.push(stmt_id);
 
            next = iter.next();
 
        }
 

	
 
        let statements = stmt_section.into_vec();
 
        let mut block_span = consume_token(&module.source, iter, TokenKind::CloseCurly)?;
 
        block_span.begin = open_curly_pos;
 
        block_span.begin = open_curly_span.begin;
 

	
 
        let id = ctx.heap.alloc_block_statement(|this| BlockStatement{
 
        let block_id = ctx.heap.alloc_block_statement(|this| BlockStatement{
 
            this,
 
            is_implicit: false,
 
            span: block_span,
 
            statements,
 
            end_block: EndBlockStatementId::new_invalid(),
 
            scope_node: ScopeNode::new_invalid(),
 
            first_unique_id_in_scope: -1,
 
            next_unique_id_in_scope: -1,
 
            locals: Vec::new(),
 
            labels: Vec::new(),
 
            scope: ScopeId::new_invalid(),
 
            next: StatementId::new_invalid(),
 
        });
 
        let scope_id = ctx.heap.alloc_scope(|this| Scope::new(this, ScopeAssociation::Block(block_id)));
 

	
 
        let end_block = ctx.heap.alloc_end_block_statement(|this| EndBlockStatement{
 
            this, start_block: id, next: StatementId::new_invalid()
 
        let end_block_id = ctx.heap.alloc_end_block_statement(|this| EndBlockStatement{
 
            this, start_block: block_id, next: StatementId::new_invalid()
 
        });
 

	
 
        let block_stmt = &mut ctx.heap[id];
 
        block_stmt.end_block = end_block;
 
        let block_stmt = &mut ctx.heap[block_id];
 
        block_stmt.end_block = end_block_id;
 
        block_stmt.scope = scope_id;
 

	
 
        Ok(id)
 
        Ok(block_id)
 
    }
 

	
 
    fn consume_if_statement(
 
@@ -568,24 +465,54 @@ impl PassDefinitions {
 
        consume_token(&module.source, iter, TokenKind::OpenParen)?;
 
        let test = self.consume_expression(module, iter, ctx)?;
 
        consume_token(&module.source, iter, TokenKind::CloseParen)?;
 
        let true_body = self.consume_block_or_wrapped_statement(module, iter, ctx)?;
 

	
 
        // Consume bodies of if-statement
 
        let true_body = IfStatementCase{
 
            body: self.consume_statement(module, iter, ctx)?,
 
            scope: ScopeId::new_invalid(),
 
        };
 

	
 
        let false_body = if has_ident(&module.source, iter, KW_STMT_ELSE) {
 
            iter.consume();
 
            let false_body = self.consume_block_or_wrapped_statement(module, iter, ctx)?;
 
            let false_body = IfStatementCase{
 
                body: self.consume_statement(module, iter, ctx)?,
 
                scope: ScopeId::new_invalid(),
 
            };
 

	
 
            Some(false_body)
 
        } else {
 
            None
 
        };
 

	
 
        Ok(ctx.heap.alloc_if_statement(|this| IfStatement{
 
        // Construct AST elements
 
        let if_stmt_id = ctx.heap.alloc_if_statement(|this| IfStatement{
 
            this,
 
            span: if_span,
 
            test,
 
            true_body,
 
            false_body,
 
            true_case: true_body,
 
            false_case: false_body,
 
            end_if: EndIfStatementId::new_invalid(),
 
        }))
 
        });
 
        let end_if_stmt_id = ctx.heap.alloc_end_if_statement(|this| EndIfStatement{
 
            this,
 
            start_if: if_stmt_id,
 
            next: StatementId::new_invalid(),
 
        });
 
        let true_scope_id = ctx.heap.alloc_scope(|this| Scope::new(this, ScopeAssociation::If(if_stmt_id, true)));
 
        let false_scope_id = if false_body.is_some() {
 
            Some(ctx.heap.alloc_scope(|this| Scope::new(this, ScopeAssociation::If(if_stmt_id, false))))
 
        } else {
 
            None
 
        };
 

	
 
        let if_stmt = &mut ctx.heap[if_stmt_id];
 
        if_stmt.end_if = end_if_stmt_id;
 
        if_stmt.true_case.scope = true_scope_id;
 
        if let Some(false_case) = &mut if_stmt.false_case {
 
            false_case.scope = false_scope_id.unwrap();
 
        }
 

	
 
        return Ok(if_stmt_id);
 
    }
 

	
 
    fn consume_while_statement(
 
@@ -595,16 +522,29 @@ impl PassDefinitions {
 
        consume_token(&module.source, iter, TokenKind::OpenParen)?;
 
        let test = self.consume_expression(module, iter, ctx)?;
 
        consume_token(&module.source, iter, TokenKind::CloseParen)?;
 
        let body = self.consume_block_or_wrapped_statement(module, iter, ctx)?;
 
        let body = self.consume_statement(module, iter, ctx)?;
 

	
 
        Ok(ctx.heap.alloc_while_statement(|this| WhileStatement{
 
        let while_stmt_id = ctx.heap.alloc_while_statement(|this| WhileStatement{
 
            this,
 
            span: while_span,
 
            test,
 
            scope: ScopeId::new_invalid(),
 
            body,
 
            end_while: EndWhileStatementId::new_invalid(),
 
            in_sync: SynchronousStatementId::new_invalid(),
 
        }))
 
        });
 
        let end_while_stmt_id = ctx.heap.alloc_end_while_statement(|this| EndWhileStatement{
 
            this,
 
            start_while: while_stmt_id,
 
            next: StatementId::new_invalid(),
 
        });
 
        let scope_id = ctx.heap.alloc_scope(|this| Scope::new(this, ScopeAssociation::While(while_stmt_id)));
 

	
 
        let while_stmt = &mut ctx.heap[while_stmt_id];
 
        while_stmt.scope = scope_id;
 
        while_stmt.end_while = end_while_stmt_id;
 

	
 
        Ok(while_stmt_id)
 
    }
 

	
 
    fn consume_break_statement(
 
@@ -649,25 +589,38 @@ impl PassDefinitions {
 
        &mut self, module: &Module, iter: &mut TokenIter, ctx: &mut PassCtx
 
    ) -> Result<SynchronousStatementId, ParseError> {
 
        let synchronous_span = consume_exact_ident(&module.source, iter, KW_STMT_SYNC)?;
 
        let body = self.consume_block_or_wrapped_statement(module, iter, ctx)?;
 
        let body = self.consume_statement(module, iter, ctx)?;
 

	
 
        Ok(ctx.heap.alloc_synchronous_statement(|this| SynchronousStatement{
 
        let sync_stmt_id = ctx.heap.alloc_synchronous_statement(|this| SynchronousStatement{
 
            this,
 
            span: synchronous_span,
 
            scope: ScopeId::new_invalid(),
 
            body,
 
            end_sync: EndSynchronousStatementId::new_invalid(),
 
        }))
 
        });
 
        let end_sync_stmt_id = ctx.heap.alloc_end_synchronous_statement(|this| EndSynchronousStatement{
 
            this,
 
            start_sync: sync_stmt_id,
 
            next: StatementId::new_invalid(),
 
        });
 
        let scope_id = ctx.heap.alloc_scope(|this| Scope::new(this, ScopeAssociation::Synchronous(sync_stmt_id)));
 

	
 
        let sync_stmt = &mut ctx.heap[sync_stmt_id];
 
        sync_stmt.scope = scope_id;
 
        sync_stmt.end_sync = end_sync_stmt_id;
 

	
 
        return Ok(sync_stmt_id);
 
    }
 

	
 
    fn consume_fork_statement(
 
        &mut self, module: &Module, iter: &mut TokenIter, ctx: &mut PassCtx
 
    ) -> Result<ForkStatementId, ParseError> {
 
        let fork_span = consume_exact_ident(&module.source, iter, KW_STMT_FORK)?;
 
        let left_body = self.consume_block_or_wrapped_statement(module, iter, ctx)?;
 
        let left_body = self.consume_statement(module, iter, ctx)?;
 

	
 
        let right_body = if has_ident(&module.source, iter, KW_STMT_OR) {
 
            iter.consume();
 
            let right_body = self.consume_block_or_wrapped_statement(module, iter, ctx)?;
 
            let right_body = self.consume_statement(module, iter, ctx)?;
 
            Some(right_body)
 
        } else {
 
            None
 
@@ -710,9 +663,11 @@ impl PassDefinitions {
 
                },
 
            };
 
            consume_token(&module.source, iter, TokenKind::ArrowRight)?;
 
            let block = self.consume_block_or_wrapped_statement(module, iter, ctx)?;
 
            let block = self.consume_statement(module, iter, ctx)?;
 
            cases.push(SelectCase{
 
                guard, block,
 
                guard,
 
                body: block,
 
                scope: ScopeId::new_invalid(),
 
                involved_ports: Vec::with_capacity(1)
 
            });
 

	
 
@@ -721,12 +676,33 @@ impl PassDefinitions {
 

	
 
        consume_token(&module.source, iter, TokenKind::CloseCurly)?;
 

	
 
        Ok(ctx.heap.alloc_select_statement(|this| SelectStatement{
 
        let num_cases = cases.len();
 
        let select_stmt_id = ctx.heap.alloc_select_statement(|this| SelectStatement{
 
            this,
 
            span: select_span,
 
            cases,
 
            end_select: EndSelectStatementId::new_invalid(),
 
        }))
 
            relative_pos_in_parent: -1,
 
            next: StatementId::new_invalid(),
 
        });
 

	
 
        let end_select_stmt_id = ctx.heap.alloc_end_select_statement(|this| EndSelectStatement{
 
            this,
 
            start_select: select_stmt_id,
 
            next: StatementId::new_invalid(),
 
        });
 

	
 
        let select_stmt = &mut ctx.heap[select_stmt_id];
 
        select_stmt.end_select = end_select_stmt_id;
 

	
 
        for case_index in 0..num_cases {
 
            let scope_id = ctx.heap.alloc_scope(|this| Scope::new(this, ScopeAssociation::SelectCase(select_stmt_id, case_index as u32)));
 
            let select_stmt = &mut ctx.heap[select_stmt_id];
 
            let select_case = &mut select_stmt.cases[case_index];
 
            select_case.scope = scope_id;
 
        }
 

	
 
        return Ok(select_stmt_id)
 
    }
 

	
 
    fn consume_return_statement(
 
@@ -855,7 +831,7 @@ impl PassDefinitions {
 
            kind: VariableKind::Local,
 
            identifier: from_identifier,
 
            parser_type: from_port_type,
 
            relative_pos_in_block: 0,
 
            relative_pos_in_parent: 0,
 
            unique_id_in_scope: -1,
 
        });
 

	
 
@@ -870,7 +846,7 @@ impl PassDefinitions {
 
            kind: VariableKind::Local,
 
            identifier: to_identifier,
 
            parser_type: to_port_type,
 
            relative_pos_in_block: 0,
 
            relative_pos_in_parent: 0,
 
            unique_id_in_scope: -1,
 
        });
 

	
 
@@ -879,49 +855,25 @@ impl PassDefinitions {
 
            this,
 
            span: channel_span,
 
            from, to,
 
            relative_pos_in_block: 0,
 
            relative_pos_in_parent: 0,
 
            next: StatementId::new_invalid(),
 
        }))
 
    }
 

	
 
    fn consume_labeled_statement(
 
        &mut self, module: &Module, iter: &mut TokenIter, ctx: &mut PassCtx, section: &mut ScopedSection<StatementId>
 
    ) -> Result<(), ParseError> {
 
    fn consume_labeled_statement(&mut self, module: &Module, iter: &mut TokenIter, ctx: &mut PassCtx) -> Result<LabeledStatementId, ParseError> {
 
        let label = consume_ident_interned(&module.source, iter, ctx)?;
 
        consume_token(&module.source, iter, TokenKind::Colon)?;
 

	
 
        // Not pretty: consume_statement may produce more than one statement.
 
        // The values in the section need to be in the correct order if some
 
        // kind of outer block is consumed, so we take another section, push
 
        // the expressions in that one, and then allocate the labeled statement.
 
        let mut inner_section = self.statements.start_section();
 
        self.consume_statement(module, iter, ctx, &mut inner_section)?;
 
        debug_assert!(inner_section.len() >= 1);
 

	
 
        let inner_stmt_id = self.consume_statement(module, iter, ctx)?;
 
        let stmt_id = ctx.heap.alloc_labeled_statement(|this| LabeledStatement {
 
            this,
 
            label,
 
            body: inner_section[0],
 
            relative_pos_in_block: 0,
 
            body: inner_stmt_id,
 
            relative_pos_in_parent: 0,
 
            in_sync: SynchronousStatementId::new_invalid(),
 
        });
 

	
 
        if inner_section.len() == 1 {
 
            // Produce the labeled statement pointing to the first statement.
 
            // This is by far the most common case.
 
            inner_section.forget();
 
            section.push(stmt_id.upcast());
 
        } else {
 
            // Produce the labeled statement using the first statement, and push
 
            // the remaining ones at the end.
 
            let inner_statements = inner_section.into_vec();
 
            section.push(stmt_id.upcast());
 
            for idx in 1..inner_statements.len() {
 
                section.push(inner_statements[idx])
 
            }
 
        }
 

	
 
        Ok(())
 
        return Ok(stmt_id);
 
    }
 

	
 
    /// Attempts to consume a memory statement (a statement along the lines of
 
@@ -960,7 +912,7 @@ impl PassDefinitions {
 
                    kind: VariableKind::Local,
 
                    identifier: identifier.clone(),
 
                    parser_type,
 
                    relative_pos_in_block: 0,
 
                    relative_pos_in_parent: 0,
 
                    unique_id_in_scope: -1,
 
                });
 

	
 
@@ -972,7 +924,7 @@ impl PassDefinitions {
 
                    declaration: Some(local_id),
 
                    used_as_binding_target: false,
 
                    parent: ExpressionParent::None,
 
                    unique_id_in_definition: -1,
 
                    type_index: -1,
 
                });
 
                let assignment_expr_id = ctx.heap.alloc_assignment_expression(|this| AssignmentExpression{
 
                    this,
 
@@ -982,7 +934,7 @@ impl PassDefinitions {
 
                    operation: AssignmentOperator::Set,
 
                    right: initial_expr_id,
 
                    parent: ExpressionParent::None,
 
                    unique_id_in_definition: -1,
 
                    type_index: -1,
 
                });
 

	
 
                // Put both together in the memory statement
 
@@ -1077,7 +1029,7 @@ impl PassDefinitions {
 
            Ok(ctx.heap.alloc_assignment_expression(|this| AssignmentExpression{
 
                this, operator_span, full_span, left, operation, right,
 
                parent: ExpressionParent::None,
 
                unique_id_in_definition: -1,
 
                type_index: -1,
 
            }).upcast())
 
        } else {
 
            Ok(expr)
 
@@ -1105,7 +1057,7 @@ impl PassDefinitions {
 
            Ok(ctx.heap.alloc_conditional_expression(|this| ConditionalExpression{
 
                this, operator_span, full_span, test, true_expression, false_expression,
 
                parent: ExpressionParent::None,
 
                unique_id_in_definition: -1,
 
                type_index: -1,
 
            }).upcast())
 
        } else {
 
            Ok(result)
 
@@ -1290,7 +1242,7 @@ impl PassDefinitions {
 
            Ok(ctx.heap.alloc_unary_expression(|this| UnaryExpression {
 
                this, operator_span, full_span, operation, expression,
 
                parent: ExpressionParent::None,
 
                unique_id_in_definition: -1,
 
                type_index: -1,
 
            }).upcast())
 
        } else if next == Some(TokenKind::PlusPlus) {
 
            return Err(ParseError::new_error_str_at_span(
 
@@ -1354,7 +1306,7 @@ impl PassDefinitions {
 
                        slicing_span: operator_span,
 
                        full_span, subject, from_index, to_index,
 
                        parent: ExpressionParent::None,
 
                        unique_id_in_definition: -1,
 
                        type_index: -1,
 
                    }).upcast();
 
                } else if Some(TokenKind::CloseSquare) == next {
 
                    let end_span = consume_token(&module.source, iter, TokenKind::CloseSquare)?;
 
@@ -1368,7 +1320,7 @@ impl PassDefinitions {
 
                        this, operator_span, full_span, subject,
 
                        index: from_index,
 
                        parent: ExpressionParent::None,
 
                        unique_id_in_definition: -1,
 
                        type_index: -1,
 
                    }).upcast();
 
                } else {
 
                    return Err(ParseError::new_error_str_at_pos(
 
@@ -1409,7 +1361,7 @@ impl PassDefinitions {
 
                    this, operator_span, full_span, subject,
 
                    kind: select_kind,
 
                    parent: ExpressionParent::None,
 
                    unique_id_in_definition: -1,
 
                    type_index: -1,
 
                }).upcast();
 
            }
 

	
 
@@ -1445,7 +1397,7 @@ impl PassDefinitions {
 
                    span: InputSpan::from_positions(open_paren_pos, close_paren_pos),
 
                    value: Literal::Tuple(Vec::new()),
 
                    parent: ExpressionParent::None,
 
                    unique_id_in_definition: -1,
 
                    type_index: -1,
 
                });
 

	
 
                literal_id.upcast()
 
@@ -1471,7 +1423,7 @@ impl PassDefinitions {
 
                        span: InputSpan::from_positions(open_paren_pos, close_paren_pos),
 
                        value: Literal::Tuple(scoped_section.into_vec()),
 
                        parent: ExpressionParent::None,
 
                        unique_id_in_definition: -1,
 
                        type_index: -1,
 
                    });
 

	
 
                    literal_id.upcast()
 
@@ -1499,7 +1451,7 @@ impl PassDefinitions {
 
                span: InputSpan::from_positions(start_pos, end_pos),
 
                value: Literal::Array(scoped_section.into_vec()),
 
                parent: ExpressionParent::None,
 
                unique_id_in_definition: -1,
 
                type_index: -1,
 
            }).upcast()
 
        } else if next == Some(TokenKind::Integer) {
 
            let (literal, span) = consume_integer_literal(&module.source, iter, &mut self.buffer)?;
 
@@ -1508,7 +1460,7 @@ impl PassDefinitions {
 
                this, span,
 
                value: Literal::Integer(LiteralInteger{ unsigned_value: literal, negated: false }),
 
                parent: ExpressionParent::None,
 
                unique_id_in_definition: -1,
 
                type_index: -1,
 
            }).upcast()
 
        } else if next == Some(TokenKind::String) {
 
            let span = consume_string_literal(&module.source, iter, &mut self.buffer)?;
 
@@ -1518,7 +1470,7 @@ impl PassDefinitions {
 
                this, span,
 
                value: Literal::String(interned),
 
                parent: ExpressionParent::None,
 
                unique_id_in_definition: -1,
 
                type_index: -1,
 
            }).upcast()
 
        } else if next == Some(TokenKind::Character) {
 
            let (character, span) = consume_character_literal(&module.source, iter)?;
 
@@ -1527,7 +1479,7 @@ impl PassDefinitions {
 
                this, span,
 
                value: Literal::Character(character),
 
                parent: ExpressionParent::None,
 
                unique_id_in_definition: -1,
 
                type_index: -1,
 
            }).upcast()
 
        } else if next == Some(TokenKind::Ident) {
 
            // May be a variable, a type instantiation or a function call. If we
 
@@ -1579,7 +1531,7 @@ impl PassDefinitions {
 
                                        definition: target_definition_id,
 
                                    }),
 
                                    parent: ExpressionParent::None,
 
                                    unique_id_in_definition: -1,
 
                                    type_index: -1,
 
                                }).upcast()
 
                            },
 
                            Definition::Enum(_) => {
 
@@ -1597,7 +1549,7 @@ impl PassDefinitions {
 
                                        variant_idx: 0
 
                                    }),
 
                                    parent: ExpressionParent::None,
 
                                    unique_id_in_definition: -1,
 
                                    type_index: -1,
 
                                }).upcast()
 
                            },
 
                            Definition::Union(_) => {
 
@@ -1622,31 +1574,14 @@ impl PassDefinitions {
 
                                        variant_idx: 0,
 
                                    }),
 
                                    parent: ExpressionParent::None,
 
                                    unique_id_in_definition: -1,
 
                                    type_index: -1,
 
                                }).upcast()
 
                            },
 
                            Definition::Component(_) => {
 
                                // Component instantiation
 
                                let func_span = parser_type.full_span;
 
                                let mut full_span = func_span;
 
                                let arguments = self.consume_expression_list(
 
                                    module, iter, ctx, Some(&mut full_span.end)
 
                                )?;
 

	
 
                                ctx.heap.alloc_call_expression(|this| CallExpression{
 
                                    this, func_span, full_span,
 
                                    parser_type,
 
                                    method: Method::UserComponent,
 
                                    arguments,
 
                                    definition: target_definition_id,
 
                                    parent: ExpressionParent::None,
 
                                    unique_id_in_definition: -1,
 
                                }).upcast()
 
                            },
 
                            Definition::Function(function_definition) => {
 
                            Definition::Procedure(proc_def) => {
 
                                // Check whether it is a builtin function
 
                                let method = if function_definition.builtin {
 
                                    match function_definition.identifier.value.as_bytes() {
 
                                let procedure_id = proc_def.this;
 
                                let method = if proc_def.builtin {
 
                                    match proc_def.identifier.value.as_bytes() {
 
                                        KW_FUNC_GET => Method::Get,
 
                                        KW_FUNC_PUT => Method::Put,
 
                                        KW_FUNC_FIRES => Method::Fires,
 
@@ -1656,8 +1591,10 @@ impl PassDefinitions {
 
                                        KW_FUNC_PRINT => Method::Print,
 
                                        _ => unreachable!(),
 
                                    }
 
                                } else {
 
                                } else if proc_def.kind == ProcedureKind::Function {
 
                                    Method::UserFunction
 
                                } else {
 
                                    Method::UserComponent
 
                                };
 

	
 
                                // Function call: consume the arguments
 
@@ -1669,9 +1606,9 @@ impl PassDefinitions {
 

	
 
                                ctx.heap.alloc_call_expression(|this| CallExpression{
 
                                    this, func_span, full_span, parser_type, method, arguments,
 
                                    definition: target_definition_id,
 
                                    procedure: procedure_id,
 
                                    parent: ExpressionParent::None,
 
                                    unique_id_in_definition: -1,
 
                                    type_index: -1,
 
                                }).upcast()
 
                            }
 
                        }
 
@@ -1700,7 +1637,7 @@ impl PassDefinitions {
 
                        span: ident_span,
 
                        value,
 
                        parent: ExpressionParent::None,
 
                        unique_id_in_definition: -1,
 
                        type_index: -1,
 
                    }).upcast()
 
                } else if ident_text == KW_LET {
 
                    // Binding expression
 
@@ -1718,7 +1655,7 @@ impl PassDefinitions {
 
                    ctx.heap.alloc_binding_expression(|this| BindingExpression{
 
                        this, operator_span, full_span, bound_to, bound_from,
 
                        parent: ExpressionParent::None,
 
                        unique_id_in_definition: -1,
 
                        type_index: -1,
 
                    }).upcast()
 
                } else if ident_text == KW_CAST {
 
                    // Casting expression
 
@@ -1755,7 +1692,7 @@ impl PassDefinitions {
 
                        cast_span: to_type.full_span,
 
                        full_span, to_type, subject,
 
                        parent: ExpressionParent::None,
 
                        unique_id_in_definition: -1,
 
                        type_index: -1,
 
                    }).upcast()
 
                } else {
 
                    // Not a builtin literal, but also not a known type. So we
 
@@ -1790,7 +1727,7 @@ impl PassDefinitions {
 
                        declaration: None,
 
                        used_as_binding_target: false,
 
                        parent: ExpressionParent::None,
 
                        unique_id_in_definition: -1,
 
                        type_index: -1,
 
                    }).upcast()
 
                }
 
            }
 
@@ -1830,7 +1767,7 @@ impl PassDefinitions {
 
            result = ctx.heap.alloc_binary_expression(|this| BinaryExpression{
 
                this, operator_span, full_span, left, operation, right,
 
                parent: ExpressionParent::None,
 
                unique_id_in_definition: -1,
 
                type_index: -1,
 
            }).upcast();
 
        }
 

	
 
@@ -1883,7 +1820,7 @@ fn consume_parameter_list(
 
                kind: VariableKind::Parameter,
 
                parser_type,
 
                identifier,
 
                relative_pos_in_block: 0,
 
                relative_pos_in_parent: 0,
 
                unique_id_in_scope: -1,
 
            });
 
            Ok(parameter_id)
src/protocol/parser/pass_rewriting.rs
Show inline comments
 
new file 100644
 
use crate::collections::*;
 
use crate::protocol::*;
 

	
 
use super::visitor::*;
 

	
 
pub(crate) struct PassRewriting {
 
    current_scope: ScopeId,
 
    current_procedure_id: ProcedureDefinitionId,
 
    definition_buffer: ScopedBuffer<DefinitionId>,
 
    statement_buffer: ScopedBuffer<StatementId>,
 
    call_expr_buffer: ScopedBuffer<CallExpressionId>,
 
    expression_buffer: ScopedBuffer<ExpressionId>,
 
    scope_buffer: ScopedBuffer<ScopeId>,
 
}
 

	
 
impl PassRewriting {
 
    pub(crate) fn new() -> Self {
 
        Self{
 
            current_scope: ScopeId::new_invalid(),
 
            current_procedure_id: ProcedureDefinitionId::new_invalid(),
 
            definition_buffer: ScopedBuffer::with_capacity(BUFFER_INIT_CAP_LARGE),
 
            statement_buffer: ScopedBuffer::with_capacity(BUFFER_INIT_CAP_SMALL),
 
            call_expr_buffer: ScopedBuffer::with_capacity(BUFFER_INIT_CAP_SMALL),
 
            expression_buffer: ScopedBuffer::with_capacity(BUFFER_INIT_CAP_SMALL),
 
            scope_buffer: ScopedBuffer::with_capacity(BUFFER_INIT_CAP_SMALL),
 
        }
 
    }
 
}
 

	
 
impl Visitor for PassRewriting {
 
    fn visit_module(&mut self, ctx: &mut Ctx) -> VisitorResult {
 
        let module = ctx.module();
 
        debug_assert_eq!(module.phase, ModuleCompilationPhase::Typed);
 

	
 
        let root_id = module.root_id;
 
        let root = &ctx.heap[root_id];
 
        let definition_section = self.definition_buffer.start_section_initialized(&root.definitions);
 
        for definition_index in 0..definition_section.len() {
 
            let definition_id = definition_section[definition_index];
 
            self.visit_definition(ctx, definition_id)?;
 
        }
 

	
 
        definition_section.forget();
 
        ctx.module_mut().phase = ModuleCompilationPhase::Rewritten;
 
        return Ok(())
 
    }
 

	
 
    // --- Visiting procedures
 

	
 
    fn visit_procedure_definition(&mut self, ctx: &mut Ctx, id: ProcedureDefinitionId) -> VisitorResult {
 
        let definition = &ctx.heap[id];
 
        let body_id = definition.body;
 
        self.current_scope = definition.scope;
 
        self.current_procedure_id = id;
 
        return self.visit_block_stmt(ctx, body_id);
 
    }
 

	
 
    // --- Visiting statements (that are not the select statement)
 

	
 
    fn visit_block_stmt(&mut self, ctx: &mut Ctx, id: BlockStatementId) -> VisitorResult {
 
        let block_stmt = &ctx.heap[id];
 
        let stmt_section = self.statement_buffer.start_section_initialized(&block_stmt.statements);
 

	
 
        self.current_scope = block_stmt.scope;
 
        for stmt_idx in 0..stmt_section.len() {
 
            self.visit_stmt(ctx, stmt_section[stmt_idx])?;
 
        }
 

	
 
        stmt_section.forget();
 
        return Ok(())
 
    }
 

	
 
    fn visit_labeled_stmt(&mut self, ctx: &mut Ctx, id: LabeledStatementId) -> VisitorResult {
 
        let labeled_stmt = &ctx.heap[id];
 
        let body_id = labeled_stmt.body;
 
        return self.visit_stmt(ctx, body_id);
 
    }
 

	
 
    fn visit_if_stmt(&mut self, ctx: &mut Ctx, id: IfStatementId) -> VisitorResult {
 
        let if_stmt = &ctx.heap[id];
 
        let true_case = if_stmt.true_case;
 
        let false_case = if_stmt.false_case;
 

	
 
        self.current_scope = true_case.scope;
 
        self.visit_stmt(ctx, true_case.body)?;
 
        if let Some(false_case) = false_case {
 
            self.current_scope = false_case.scope;
 
            self.visit_stmt(ctx, false_case.body)?;
 
        }
 

	
 
        return Ok(())
 
    }
 

	
 
    fn visit_while_stmt(&mut self, ctx: &mut Ctx, id: WhileStatementId) -> VisitorResult {
 
        let while_stmt = &ctx.heap[id];
 
        let body_id = while_stmt.body;
 
        self.current_scope = while_stmt.scope;
 
        return self.visit_stmt(ctx, body_id);
 
    }
 

	
 
    fn visit_synchronous_stmt(&mut self, ctx: &mut Ctx, id: SynchronousStatementId) -> VisitorResult {
 
        let sync_stmt = &ctx.heap[id];
 
        let body_id = sync_stmt.body;
 
        self.current_scope = sync_stmt.scope;
 
        return self.visit_stmt(ctx, body_id);
 
    }
 

	
 
    // --- Visiting the select statement
 

	
 
    fn visit_select_stmt(&mut self, ctx: &mut Ctx, id: SelectStatementId) -> VisitorResult {
 
        // Utility for the last stage of rewriting process. Note that caller
 
        // still needs to point the end of the if-statement to the end of the
 
        // replacement statement of the select statement.
 
        fn transform_select_case_code(
 
            ctx: &mut Ctx, containing_procedure_id: ProcedureDefinitionId,
 
            select_id: SelectStatementId, case_index: usize,
 
            select_var_id: VariableId, select_var_type_id: TypeIdReference
 
        ) -> (IfStatementId, EndIfStatementId, ScopeId) {
 
            // Retrieve statement IDs associated with case
 
            let case = &ctx.heap[select_id].cases[case_index];
 
            let case_guard_id = case.guard;
 
            let case_body_id = case.body;
 
            let case_scope_id = case.scope;
 

	
 
            // Create the if-statement for the result of the select statement
 
            let compare_expr_id = create_ast_equality_comparison_expr(ctx, containing_procedure_id, select_var_id, select_var_type_id, case_index as u64);
 
            let true_case = IfStatementCase{
 
                body: case_guard_id, // which is linked up to the body
 
                scope: case_scope_id,
 
            };
 
            let (if_stmt_id, end_if_stmt_id) = create_ast_if_stmt(ctx, compare_expr_id.upcast(), true_case, None);
 

	
 
            // Link up body statement to end-if
 
            set_ast_statement_next(ctx, case_body_id, end_if_stmt_id.upcast());
 

	
 
            return (if_stmt_id, end_if_stmt_id, case_scope_id);
 
        }
 

	
 
        // Precreate the block that will end up containing all of the
 
        // transformed statements. Also precreate the scope associated with it
 
        let (outer_block_id, outer_end_block_id, outer_scope_id) =
 
            create_ast_block_stmt(ctx, Vec::new());
 

	
 
        // The "select" and the "end select" statement will act like trampolines
 
        // that jump to the replacement block. So set the child/parent
 
        // relationship already.
 
        // --- for the statements
 
        let select_stmt = &mut ctx.heap[id];
 
        select_stmt.next = outer_block_id.upcast();
 
        let end_select_stmt_id = select_stmt.end_select;
 
        let select_stmt_relative_pos = select_stmt.relative_pos_in_parent;
 

	
 
        let outer_end_block_stmt = &mut ctx.heap[outer_end_block_id];
 
        outer_end_block_stmt.next = end_select_stmt_id.upcast();
 

	
 
        // --- for the scopes
 
        link_new_child_to_existing_parent_scope(ctx, &mut self.scope_buffer, self.current_scope, outer_scope_id, select_stmt_relative_pos);
 

	
 
        // Create statements that will create temporary variables for all of the
 
        // ports passed to the "get" calls in the select case guards.
 
        let select_stmt = &ctx.heap[id];
 
        let total_num_cases = select_stmt.cases.len();
 
        let mut total_num_ports = 0;
 
        let end_select_stmt_id = select_stmt.end_select;
 
        let _end_select = &ctx.heap[end_select_stmt_id];
 

	
 
        // Put heap IDs into temporary buffers to handle borrowing rules
 
        let mut call_id_section = self.call_expr_buffer.start_section();
 
        let mut expr_id_section = self.expression_buffer.start_section();
 

	
 
        for case in select_stmt.cases.iter() {
 
            total_num_ports += case.involved_ports.len();
 
            for (call_id, expr_id) in case.involved_ports.iter().copied() {
 
                call_id_section.push(call_id);
 
                expr_id_section.push(expr_id);
 
            }
 
        }
 

	
 
        // Transform all of the call expressions by takings its argument (the
 
        // port from which we `get`) and turning it into a temporary variable.
 
        let mut transformed_stmts = Vec::with_capacity(total_num_ports); // TODO: Recompute this preallocated length, put assert at the end
 
        let mut locals = Vec::with_capacity(total_num_ports);
 

	
 
        for port_var_idx in 0..call_id_section.len() {
 
            let get_call_expr_id = call_id_section[port_var_idx];
 
            let port_expr_id = expr_id_section[port_var_idx];
 
            let port_type_index = ctx.heap[port_expr_id].type_index();
 
            let port_type_ref = TypeIdReference::IndirectSameAsExpr(port_type_index);
 

	
 
            // Move the port expression such that it gets assigned to a temporary variable
 
            let variable_id = create_ast_variable(ctx, outer_scope_id);
 
            let variable_decl_stmt_id = create_ast_variable_declaration_stmt(ctx, self.current_procedure_id, variable_id, port_type_ref, port_expr_id);
 

	
 
            // Replace the original port expression in the call with a reference
 
            // to the replacement variable
 
            let variable_expr_id = create_ast_variable_expr(ctx, self.current_procedure_id, variable_id, port_type_ref);
 
            let call_expr = &mut ctx.heap[get_call_expr_id];
 
            call_expr.arguments[0] = variable_expr_id.upcast();
 

	
 
            transformed_stmts.push(variable_decl_stmt_id.upcast().upcast());
 
            locals.push((variable_id, port_type_ref));
 
        }
 

	
 
        // Insert runtime calls that facilitate the semantics of the select
 
        // block.
 

	
 
        // Create the call that indicates the start of the select block
 
        {
 
            let num_cases_expression_id = create_ast_literal_integer_expr(ctx, self.current_procedure_id, total_num_cases as u64, ctx.arch.uint32_type_id);
 
            let num_ports_expression_id = create_ast_literal_integer_expr(ctx, self.current_procedure_id, total_num_ports as u64, ctx.arch.uint32_type_id);
 
            let arguments = vec![
 
                num_cases_expression_id.upcast(),
 
                num_ports_expression_id.upcast()
 
            ];
 

	
 
            let call_expression_id = create_ast_call_expr(ctx, self.current_procedure_id, Method::SelectStart, &mut self.expression_buffer, arguments);
 
            let call_statement_id = create_ast_expression_stmt(ctx, call_expression_id.upcast());
 

	
 
            transformed_stmts.push(call_statement_id.upcast());
 
        }
 

	
 
        // Create calls for each select case that will register the ports that
 
        // we are waiting on at the runtime.
 
        {
 
            let mut total_port_index = 0;
 
            for case_index in 0..total_num_cases {
 
                let case = &ctx.heap[id].cases[case_index];
 
                let case_num_ports = case.involved_ports.len();
 

	
 
                for case_port_index in 0..case_num_ports {
 
                    // Arguments to runtime call
 
                    let (port_variable_id, port_variable_type) = locals[total_port_index]; // so far this variable contains the temporary variables for the port expressions
 
                    let case_index_expr_id = create_ast_literal_integer_expr(ctx, self.current_procedure_id, case_index as u64, ctx.arch.uint32_type_id);
 
                    let port_index_expr_id = create_ast_literal_integer_expr(ctx, self.current_procedure_id, case_port_index as u64, ctx.arch.uint32_type_id);
 
                    let port_variable_expr_id = create_ast_variable_expr(ctx, self.current_procedure_id, port_variable_id, port_variable_type);
 
                    let runtime_call_arguments = vec![
 
                        case_index_expr_id.upcast(),
 
                        port_index_expr_id.upcast(),
 
                        port_variable_expr_id.upcast()
 
                    ];
 

	
 
                    // Create runtime call, then store it
 
                    let runtime_call_expr_id = create_ast_call_expr(ctx, self.current_procedure_id, Method::SelectRegisterCasePort, &mut self.expression_buffer, runtime_call_arguments);
 
                    let runtime_call_stmt_id = create_ast_expression_stmt(ctx, runtime_call_expr_id.upcast());
 

	
 
                    transformed_stmts.push(runtime_call_stmt_id.upcast());
 

	
 
                    total_port_index += 1;
 
                }
 
            }
 
        }
 

	
 
        // Create the variable that will hold the result of a completed select
 
        // block. Then create the runtime call that will produce this result
 
        let select_variable_id = create_ast_variable(ctx, outer_scope_id);
 
        let select_variable_type = TypeIdReference::DirectTypeId(ctx.arch.uint32_type_id);
 
        locals.push((select_variable_id, select_variable_type));
 

	
 
        {
 
            let runtime_call_expr_id = create_ast_call_expr(ctx, self.current_procedure_id, Method::SelectWait, &mut self.expression_buffer, Vec::new());
 
            let variable_stmt_id = create_ast_variable_declaration_stmt(ctx, self.current_procedure_id, select_variable_id, select_variable_type, runtime_call_expr_id.upcast());
 
            transformed_stmts.push(variable_stmt_id.upcast().upcast());
 
        }
 

	
 
        call_id_section.forget();
 
        expr_id_section.forget();
 

	
 
        // Now we transform each of the select block case's guard and code into
 
        // a chained if-else statement.
 
        let mut relative_pos = transformed_stmts.len() as i32;
 
        if total_num_cases > 0 {
 
            let (if_stmt_id, end_if_stmt_id, scope_id) = transform_select_case_code(ctx, self.current_procedure_id, id, 0, select_variable_id, select_variable_type);
 
            link_existing_child_to_new_parent_scope(ctx, &mut self.scope_buffer, outer_scope_id, scope_id, relative_pos);
 
            let first_end_if_stmt = &mut ctx.heap[end_if_stmt_id];
 
            first_end_if_stmt.next = outer_end_block_id.upcast();
 

	
 
            let mut last_if_stmt_id = if_stmt_id;
 
            let mut last_end_if_stmt_id = end_if_stmt_id;
 
            let mut last_parent_scope_id = outer_scope_id;
 
            let mut last_relative_pos = transformed_stmts.len() as i32 + 1;
 
            transformed_stmts.push(last_if_stmt_id.upcast());
 

	
 
            for case_index in 1..total_num_cases {
 
                let (if_stmt_id, end_if_stmt_id, scope_id) = transform_select_case_code(ctx, self.current_procedure_id, id, case_index, select_variable_id, select_variable_type);
 
                let false_case_scope_id = ctx.heap.alloc_scope(|this| Scope::new(this, ScopeAssociation::If(last_if_stmt_id, false)));
 
                link_existing_child_to_new_parent_scope(ctx, &mut self.scope_buffer, false_case_scope_id, scope_id, 0);
 
                link_new_child_to_existing_parent_scope(ctx, &mut self.scope_buffer, last_parent_scope_id, false_case_scope_id, last_relative_pos);
 
                set_ast_if_statement_false_body(ctx, last_if_stmt_id, last_end_if_stmt_id, IfStatementCase{ body: if_stmt_id.upcast(), scope: false_case_scope_id });
 

	
 
                let end_if_stmt = &mut ctx.heap[end_if_stmt_id];
 
                end_if_stmt.next = last_end_if_stmt_id.upcast();
 

	
 
                last_if_stmt_id = if_stmt_id;
 
                last_end_if_stmt_id = end_if_stmt_id;
 
                last_parent_scope_id = false_case_scope_id;
 
                last_relative_pos = 0;
 
            }
 
        }
 

	
 
        // Final steps: set the statements of the replacement block statement,
 
        // link all of those statements together, and update the scopes.
 
        let first_stmt_id = transformed_stmts[0];
 
        let mut last_stmt_id = transformed_stmts[0];
 
        for stmt_id in transformed_stmts.iter().skip(1).copied() {
 
            set_ast_statement_next(ctx, last_stmt_id, stmt_id);
 
            last_stmt_id = stmt_id;
 
        }
 

	
 
        if total_num_cases == 0 {
 
            // If we don't have any cases, then we didn't connect the statements
 
            // up to the end of the outer block, so do that here
 
            set_ast_statement_next(ctx, last_stmt_id, outer_end_block_id.upcast());
 
        }
 

	
 
        let outer_block_stmt = &mut ctx.heap[outer_block_id];
 
        outer_block_stmt.next = first_stmt_id;
 
        outer_block_stmt.statements = transformed_stmts;
 

	
 
        return Ok(())
 
    }
 
}
 

	
 
// -----------------------------------------------------------------------------
 
// Utilities to create compiler-generated AST nodes
 
// -----------------------------------------------------------------------------
 

	
 
#[derive(Clone, Copy)]
 
enum TypeIdReference {
 
    DirectTypeId(TypeId),
 
    IndirectSameAsExpr(i32), // by type index
 
}
 

	
 
fn create_ast_variable(ctx: &mut Ctx, scope_id: ScopeId) -> VariableId {
 
    let variable_id = ctx.heap.alloc_variable(|this| Variable{
 
        this,
 
        kind: VariableKind::Local,
 
        parser_type: ParserType{
 
            elements: Vec::new(),
 
            full_span: InputSpan::new(),
 
        },
 
        identifier: Identifier::new_empty(InputSpan::new()),
 
        relative_pos_in_parent: -1,
 
        unique_id_in_scope: -1,
 
    });
 
    let scope = &mut ctx.heap[scope_id];
 
    scope.variables.push(variable_id);
 

	
 
    return variable_id;
 
}
 

	
 
fn create_ast_variable_expr(ctx: &mut Ctx, containing_procedure_id: ProcedureDefinitionId, variable_id: VariableId, variable_type_id: TypeIdReference) -> VariableExpressionId {
 
    let variable_type_index = add_new_procedure_expression_type(ctx, containing_procedure_id, variable_type_id);
 
    return ctx.heap.alloc_variable_expression(|this| VariableExpression{
 
        this,
 
        identifier: Identifier::new_empty(InputSpan::new()),
 
        declaration: Some(variable_id),
 
        used_as_binding_target: false,
 
        parent: ExpressionParent::None,
 
        type_index: variable_type_index,
 
    });
 
}
 

	
 
fn create_ast_call_expr(ctx: &mut Ctx, containing_procedure_id: ProcedureDefinitionId, method: Method, buffer: &mut ScopedBuffer<ExpressionId>, arguments: Vec<ExpressionId>) -> CallExpressionId {
 
    let call_type_id = match method {
 
        Method::SelectStart => ctx.arch.void_type_id,
 
        Method::SelectRegisterCasePort => ctx.arch.void_type_id,
 
        Method::SelectWait => ctx.arch.uint32_type_id, // TODO: Not pretty, this. Pretty error prone
 
        _ => unreachable!(), // if this goes of, add the appropriate method here.
 
    };
 

	
 
    let expression_ids = buffer.start_section_initialized(&arguments);
 
    let call_type_index = add_new_procedure_expression_type(ctx, containing_procedure_id, TypeIdReference::DirectTypeId(call_type_id));
 
    let call_expression_id = ctx.heap.alloc_call_expression(|this| CallExpression{
 
        func_span: InputSpan::new(),
 
        this,
 
        full_span: InputSpan::new(),
 
        parser_type: ParserType{
 
            elements: Vec::new(),
 
            full_span: InputSpan::new(),
 
        },
 
        method,
 
        arguments,
 
        procedure: ProcedureDefinitionId::new_invalid(),
 
        parent: ExpressionParent::None,
 
        type_index: call_type_index,
 
    });
 

	
 
    for argument_index in 0..expression_ids.len() {
 
        let argument_id = expression_ids[argument_index];
 
        let argument_expr = &mut ctx.heap[argument_id];
 
        *argument_expr.parent_mut() = ExpressionParent::Expression(call_expression_id.upcast(), argument_index as u32);
 
    }
 

	
 
    return call_expression_id;
 
}
 

	
 
fn create_ast_literal_integer_expr(ctx: &mut Ctx, containing_procedure_id: ProcedureDefinitionId, unsigned_value: u64, type_id: TypeId) -> LiteralExpressionId {
 
    let literal_type_index = add_new_procedure_expression_type(ctx, containing_procedure_id, TypeIdReference::DirectTypeId(type_id));
 
    return ctx.heap.alloc_literal_expression(|this| LiteralExpression{
 
        this,
 
        span: InputSpan::new(),
 
        value: Literal::Integer(LiteralInteger{
 
            unsigned_value,
 
            negated: false,
 
        }),
 
        parent: ExpressionParent::None,
 
        type_index: literal_type_index,
 
    });
 
}
 

	
 
fn create_ast_equality_comparison_expr(
 
    ctx: &mut Ctx, containing_procedure_id: ProcedureDefinitionId,
 
    variable_id: VariableId, variable_type: TypeIdReference, value: u64
 
) -> BinaryExpressionId {
 
    let var_expr_id = create_ast_variable_expr(ctx, containing_procedure_id, variable_id, variable_type);
 
    let int_expr_id = create_ast_literal_integer_expr(ctx, containing_procedure_id, value, ctx.arch.uint32_type_id);
 
    let cmp_type_index = add_new_procedure_expression_type(ctx, containing_procedure_id, TypeIdReference::DirectTypeId(ctx.arch.bool_type_id));
 
    let cmp_expr_id = ctx.heap.alloc_binary_expression(|this| BinaryExpression{
 
        this,
 
        operator_span: InputSpan::new(),
 
        full_span: InputSpan::new(),
 
        left: var_expr_id.upcast(),
 
        operation: BinaryOperator::Equality,
 
        right: int_expr_id.upcast(),
 
        parent: ExpressionParent::None,
 
        type_index: cmp_type_index,
 
    });
 

	
 
    let var_expr = &mut ctx.heap[var_expr_id];
 
    var_expr.parent = ExpressionParent::Expression(cmp_expr_id.upcast(), 0);
 
    let int_expr = &mut ctx.heap[int_expr_id];
 
    int_expr.parent = ExpressionParent::Expression(cmp_expr_id.upcast(), 1);
 

	
 
    return cmp_expr_id;
 
}
 

	
 
fn create_ast_expression_stmt(ctx: &mut Ctx, expression_id: ExpressionId) -> ExpressionStatementId {
 
    let statement_id = ctx.heap.alloc_expression_statement(|this| ExpressionStatement{
 
        this,
 
        span: InputSpan::new(),
 
        expression: expression_id,
 
        next: StatementId::new_invalid(),
 
    });
 

	
 
    let expression = &mut ctx.heap[expression_id];
 
    *expression.parent_mut() = ExpressionParent::ExpressionStmt(statement_id);
 

	
 
    return statement_id;
 
}
 

	
 
fn create_ast_variable_declaration_stmt(
 
    ctx: &mut Ctx, containing_procedure_id: ProcedureDefinitionId,
 
    variable_id: VariableId, variable_type: TypeIdReference, initial_value_expr_id: ExpressionId
 
) -> MemoryStatementId {
 
    // Create the assignment expression, assigning the initial value to the variable
 
    let variable_expr_id = create_ast_variable_expr(ctx, containing_procedure_id, variable_id, variable_type);
 
    let void_type_index = add_new_procedure_expression_type(ctx, containing_procedure_id, TypeIdReference::DirectTypeId(ctx.arch.void_type_id));
 
    let assignment_expr_id = ctx.heap.alloc_assignment_expression(|this| AssignmentExpression{
 
        this,
 
        operator_span: InputSpan::new(),
 
        full_span: InputSpan::new(),
 
        left: variable_expr_id.upcast(),
 
        operation: AssignmentOperator::Set,
 
        right: initial_value_expr_id,
 
        parent: ExpressionParent::None,
 
        type_index: void_type_index,
 
    });
 

	
 
    // Create the memory statement
 
    let memory_stmt_id = ctx.heap.alloc_memory_statement(|this| MemoryStatement{
 
        this,
 
        span: InputSpan::new(),
 
        variable: variable_id,
 
        initial_expr: assignment_expr_id,
 
        next: StatementId::new_invalid(),
 
    });
 

	
 
    // Set all parents which we can access
 
    let variable_expr = &mut ctx.heap[variable_expr_id];
 
    variable_expr.parent = ExpressionParent::Expression(assignment_expr_id.upcast(), 0);
 
    let value_expr = &mut ctx.heap[initial_value_expr_id];
 
    *value_expr.parent_mut() = ExpressionParent::Expression(assignment_expr_id.upcast(), 1);
 
    let assignment_expr = &mut ctx.heap[assignment_expr_id];
 
    assignment_expr.parent = ExpressionParent::Memory(memory_stmt_id);
 

	
 
    return memory_stmt_id;
 
}
 

	
 
fn create_ast_block_stmt(ctx: &mut Ctx, statements: Vec<StatementId>) -> (BlockStatementId, EndBlockStatementId, ScopeId) {
 
    let block_stmt_id = ctx.heap.alloc_block_statement(|this| BlockStatement{
 
        this,
 
        span: InputSpan::new(),
 
        statements,
 
        end_block: EndBlockStatementId::new_invalid(),
 
        scope: ScopeId::new_invalid(),
 
        next: StatementId::new_invalid(),
 
    });
 
    let end_block_stmt_id = ctx.heap.alloc_end_block_statement(|this| EndBlockStatement{
 
        this,
 
        start_block: block_stmt_id,
 
        next: StatementId::new_invalid(),
 
    });
 
    let scope_id = ctx.heap.alloc_scope(|this| Scope::new(this, ScopeAssociation::Block(block_stmt_id)));
 

	
 
    let block_stmt = &mut ctx.heap[block_stmt_id];
 
    block_stmt.end_block = end_block_stmt_id;
 
    block_stmt.scope = scope_id;
 

	
 
    return (block_stmt_id, end_block_stmt_id, scope_id);
 
}
 

	
 
fn create_ast_if_stmt(ctx: &mut Ctx, condition_expression_id: ExpressionId, true_case: IfStatementCase, false_case: Option<IfStatementCase>) -> (IfStatementId, EndIfStatementId) {
 
    // Create if statement and the end-if statement
 
    let if_stmt_id = ctx.heap.alloc_if_statement(|this| IfStatement{
 
        this,
 
        span: InputSpan::new(),
 
        test: condition_expression_id,
 
        true_case,
 
        false_case,
 
        end_if: EndIfStatementId::new_invalid()
 
    });
 

	
 
    let end_if_stmt_id = ctx.heap.alloc_end_if_statement(|this| EndIfStatement{
 
        this,
 
        start_if: if_stmt_id,
 
        next: StatementId::new_invalid(),
 
    });
 

	
 
    // Link the statements up as much as we can
 
    let if_stmt = &mut ctx.heap[if_stmt_id];
 
    if_stmt.end_if = end_if_stmt_id;
 

	
 
    let condition_expr = &mut ctx.heap[condition_expression_id];
 
    *condition_expr.parent_mut() = ExpressionParent::If(if_stmt_id);
 

	
 

	
 

	
 
    return (if_stmt_id, end_if_stmt_id);
 
}
 

	
 
/// Sets the false body for a given
 
fn set_ast_if_statement_false_body(ctx: &mut Ctx, if_statement_id: IfStatementId, end_if_statement_id: EndIfStatementId, false_case: IfStatementCase) {
 
    // Point if-statement to "false body"
 
    let if_stmt = &mut ctx.heap[if_statement_id];
 
    debug_assert!(if_stmt.false_case.is_none()); // simplifies logic, not necessary
 
    if_stmt.false_case = Some(false_case);
 

	
 
    // Point end of false body to the end of the if statement
 
    set_ast_statement_next(ctx, false_case.body, end_if_statement_id.upcast());
 
}
 

	
 
/// Sets the specified AST statement's control flow such that it will be
 
/// followed by the target statement. This may seem obvious, but may imply that
 
/// a statement associated with, but different from, the source statement is
 
/// modified.
 
fn set_ast_statement_next(ctx: &mut Ctx, source_stmt_id: StatementId, target_stmt_id: StatementId) {
 
    let source_stmt = &mut ctx.heap[source_stmt_id];
 
    match source_stmt {
 
        Statement::Block(stmt) => {
 
            let end_id = stmt.end_block;
 
            ctx.heap[end_id].next = target_stmt_id
 
        },
 
        Statement::EndBlock(stmt) => stmt.next = target_stmt_id,
 
        Statement::Local(stmt) => {
 
            match stmt {
 
                LocalStatement::Memory(stmt) => stmt.next = target_stmt_id,
 
                LocalStatement::Channel(stmt) => stmt.next = target_stmt_id,
 
            }
 
        },
 
        Statement::Labeled(stmt) => {
 
            let body_id = stmt.body;
 
            set_ast_statement_next(ctx, body_id, target_stmt_id);
 
        },
 
        Statement::If(stmt) => {
 
            let end_id = stmt.end_if;
 
            ctx.heap[end_id].next = target_stmt_id;
 
        },
 
        Statement::EndIf(stmt) => stmt.next = target_stmt_id,
 
        Statement::While(stmt) => {
 
            let end_id = stmt.end_while;
 
            ctx.heap[end_id].next = target_stmt_id;
 
        },
 
        Statement::EndWhile(stmt) => stmt.next = target_stmt_id,
 

	
 
        Statement::Break(_stmt) => {},
 
        Statement::Continue(_stmt) => {},
 
        Statement::Synchronous(stmt) => {
 
            let end_id = stmt.end_sync;
 
            ctx.heap[end_id].next = target_stmt_id;
 
        },
 
        Statement::EndSynchronous(stmt) => {
 
            stmt.next = target_stmt_id;
 
        },
 
        Statement::Fork(_) | Statement::EndFork(_) => {
 
            todo!("remove fork from language");
 
        },
 
        Statement::Select(stmt) => {
 
            let end_id = stmt.end_select;
 
            ctx.heap[end_id].next = target_stmt_id;
 
        },
 
        Statement::EndSelect(stmt) => stmt.next = target_stmt_id,
 
        Statement::Return(_stmt) => {},
 
        Statement::Goto(_stmt) => {},
 
        Statement::New(stmt) => stmt.next = target_stmt_id,
 
        Statement::Expression(stmt) => stmt.next = target_stmt_id,
 
    }
 
}
 

	
 
/// Links a new scope to an existing scope as its child.
 
fn link_new_child_to_existing_parent_scope(ctx: &mut Ctx, scope_buffer: &mut ScopedBuffer<ScopeId>, parent_scope_id: ScopeId, child_scope_id: ScopeId, relative_pos_hint: i32) {
 
    let child_scope = &mut ctx.heap[child_scope_id];
 
    debug_assert!(child_scope.parent.is_none());
 

	
 
    child_scope.parent = Some(parent_scope_id);
 
    child_scope.relative_pos_in_parent = relative_pos_hint;
 

	
 
    add_child_scope_to_parent(ctx, scope_buffer, parent_scope_id, child_scope_id, relative_pos_hint);
 
}
 

	
 
/// Relinks an existing scope to a new scope as its child. Will also break the
 
/// link of the child scope's old parent.
 
fn link_existing_child_to_new_parent_scope(ctx: &mut Ctx, scope_buffer: &mut ScopedBuffer<ScopeId>, new_parent_scope_id: ScopeId, child_scope_id: ScopeId, new_relative_pos_in_parent: i32) {
 
    let child_scope = &mut ctx.heap[child_scope_id];
 
    let old_parent_scope_id = child_scope.parent.unwrap();
 
    child_scope.parent = Some(new_parent_scope_id);
 
    child_scope.relative_pos_in_parent = new_relative_pos_in_parent;
 

	
 
    // Remove from old parent
 
    let old_parent = &mut ctx.heap[old_parent_scope_id];
 
    let scope_index = old_parent.nested.iter()
 
        .position(|v| *v == child_scope_id)
 
        .unwrap();
 
    old_parent.nested.remove(scope_index);
 

	
 
    // Add to new parent
 
    add_child_scope_to_parent(ctx, scope_buffer, new_parent_scope_id, child_scope_id, new_relative_pos_in_parent);
 
}
 

	
 
/// Will add a child scope to a parent scope using the relative position hint.
 
fn add_child_scope_to_parent(ctx: &mut Ctx, scope_buffer: &mut ScopedBuffer<ScopeId>, parent_scope_id: ScopeId, child_scope_id: ScopeId, relative_pos_hint: i32) {
 
    let parent_scope = &ctx.heap[parent_scope_id];
 

	
 
    let existing_scope_ids = scope_buffer.start_section_initialized(&parent_scope.nested);
 
    let mut insert_pos = existing_scope_ids.len();
 
    for index in 0..existing_scope_ids.len() {
 
        let existing_scope_id = existing_scope_ids[index];
 
        let existing_scope = &ctx.heap[existing_scope_id];
 
        if relative_pos_hint <= existing_scope.relative_pos_in_parent {
 
            insert_pos = index;
 
            break;
 
        }
 
    }
 
    existing_scope_ids.forget();
 

	
 
    let parent_scope = &mut ctx.heap[parent_scope_id];
 
    parent_scope.nested.insert(insert_pos, child_scope_id);
 
}
 

	
 
fn add_new_procedure_expression_type(ctx: &mut Ctx, procedure_id: ProcedureDefinitionId, type_id: TypeIdReference) -> i32 {
 
    let procedure = &mut ctx.heap[procedure_id];
 
    let type_index = procedure.monomorphs[0].expr_info.len();
 

	
 
    match type_id {
 
        TypeIdReference::DirectTypeId(type_id) => {
 
            for monomorph in procedure.monomorphs.iter_mut() {
 
                debug_assert_eq!(monomorph.expr_info.len(), type_index);
 
                monomorph.expr_info.push(ExpressionInfo{
 
                    type_id,
 
                    variant: ExpressionInfoVariant::Generic
 
                });
 
            }
 
        },
 
        TypeIdReference::IndirectSameAsExpr(source_type_index) => {
 
            for monomorph in procedure.monomorphs.iter_mut() {
 
                debug_assert_eq!(monomorph.expr_info.len(), type_index);
 
                let copied_expr_info = monomorph.expr_info[source_type_index as usize];
 
                monomorph.expr_info.push(copied_expr_info)
 
            }
 
        }
 
    }
 

	
 
    return type_index as i32;
 
}
 
\ No newline at end of file
src/protocol/parser/pass_stack_size.rs
Show inline comments
 
new file 100644
 
use crate::collections::*;
 
use crate::protocol::*;
 

	
 
use super::visitor::*;
 

	
 
// Will get a rename. Will probably become bytecode emitter or something. For
 
// now it just scans the scopes and assigns a unique number for each variable
 
// such that, at any point in the program's execution, all accessible in-scope
 
// variables will have a unique position "on the stack".
 
pub(crate) struct PassStackSize {
 
    definition_buffer: ScopedBuffer<DefinitionId>,
 
    variable_buffer: ScopedBuffer<VariableId>,
 
    scope_buffer: ScopedBuffer<ScopeId>,
 
}
 

	
 
impl PassStackSize {
 
    pub(crate) fn new() -> Self {
 
        return Self{
 
            definition_buffer: ScopedBuffer::with_capacity(BUFFER_INIT_CAP_LARGE),
 
            variable_buffer: ScopedBuffer::with_capacity(BUFFER_INIT_CAP_SMALL),
 
            scope_buffer: ScopedBuffer::with_capacity(BUFFER_INIT_CAP_SMALL),
 
        }
 
    }
 
}
 

	
 
impl Visitor for PassStackSize {
 
    // Top level visitors
 

	
 
    fn visit_module(&mut self, ctx: &mut Ctx) -> VisitorResult {
 
        let module = ctx.module();
 
        debug_assert_eq!(module.phase, ModuleCompilationPhase::Rewritten);
 

	
 
        let root_id = module.root_id;
 
        let root = &ctx.heap[root_id];
 
        let definition_section = self.definition_buffer.start_section_initialized(&root.definitions);
 
        for definition_index in 0..definition_section.len() {
 
            let definition_id = definition_section[definition_index];
 
            self.visit_definition(ctx, definition_id)?
 
        }
 

	
 
        definition_section.forget();
 
        // ctx.module_mut().phase = ModuleCompilationPhase::StackSizeStuffAndStuff;
 
        return Ok(())
 
    }
 

	
 
    fn visit_procedure_definition(&mut self, ctx: &mut Ctx, id: ProcedureDefinitionId) -> VisitorResult {
 
        let definition = &ctx.heap[id];
 
        let scope_id = definition.scope;
 

	
 
        self.visit_scope_and_assign_local_ids(ctx, scope_id, 0);
 
        return Ok(());
 
    }
 
}
 

	
 
impl PassStackSize {
 
    fn visit_scope_and_assign_local_ids(&mut self, ctx: &mut Ctx, scope_id: ScopeId, mut variable_counter: i32) {
 
        let scope = &mut ctx.heap[scope_id];
 
        scope.first_unique_id_in_scope = variable_counter;
 

	
 
        let variable_section = self.variable_buffer.start_section_initialized(&scope.variables);
 
        let child_scope_section = self.scope_buffer.start_section_initialized(&scope.nested);
 

	
 
        let mut variable_index = 0;
 
        let mut child_scope_index = 0;
 

	
 
        loop {
 
            // Determine relative positions of variable and scope to determine
 
            // which one occurs first within the current scope.
 
            let variable_relative_pos;
 
            if variable_index < variable_section.len() {
 
                let variable_id = variable_section[variable_index];
 
                let variable = &ctx.heap[variable_id];
 
                variable_relative_pos = variable.relative_pos_in_parent;
 
            } else {
 
                variable_relative_pos = i32::MAX;
 
            }
 

	
 
            let child_scope_relative_pos;
 
            if child_scope_index < child_scope_section.len() {
 
                let child_scope_id = child_scope_section[child_scope_index];
 
                let child_scope = &ctx.heap[child_scope_id];
 
                child_scope_relative_pos = child_scope.relative_pos_in_parent;
 
            } else {
 
                child_scope_relative_pos = i32::MAX;
 
            }
 

	
 
            if variable_relative_pos == i32::MAX && child_scope_relative_pos == i32::MAX {
 
                // Done, no more elements in the scope to consider
 
                break;
 
            }
 

	
 
            // Label the variable/scope, whichever comes first.
 
            if variable_relative_pos <= child_scope_relative_pos {
 
                debug_assert_ne!(variable_relative_pos, child_scope_relative_pos, "checking if this ever happens");
 
                let variable = &mut ctx.heap[variable_section[variable_index]];
 
                variable.unique_id_in_scope = variable_counter;
 
                variable_counter += 1;
 
                variable_index += 1;
 
            } else {
 
                let child_scope_id = child_scope_section[child_scope_index];
 
                self.visit_scope_and_assign_local_ids(ctx, child_scope_id, variable_counter);
 
                child_scope_index += 1;
 
            }
 
        }
 

	
 
        variable_section.forget();
 
        child_scope_section.forget();
 

	
 
        let scope = &mut ctx.heap[scope_id];
 
        scope.next_unique_id_in_scope = variable_counter;
 
    }
 
}
 
\ No newline at end of file
src/protocol/parser/pass_symbols.rs
Show inline comments
 
@@ -230,23 +230,23 @@ impl PassSymbols {
 
                ast_definition_id = union_def_id.upcast()
 
            },
 
            KW_FUNCTION => {
 
                let func_def_id = ctx.heap.alloc_function_definition(|this| {
 
                    FunctionDefinition::new_empty(this, module.root_id, definition_span, identifier, poly_vars)
 
                let proc_def_id = ctx.heap.alloc_procedure_definition(|this| {
 
                    ProcedureDefinition::new_empty(this, module.root_id, definition_span, ProcedureKind::Function, identifier, poly_vars)
 
                });
 
                definition_class = DefinitionClass::Function;
 
                ast_definition_id = func_def_id.upcast();
 
                ast_definition_id = proc_def_id.upcast();
 
            },
 
            KW_PRIMITIVE | KW_COMPOSITE => {
 
                let component_variant = if kw_text == KW_PRIMITIVE {
 
                    ComponentVariant::Primitive
 
                let procedure_kind = if kw_text == KW_PRIMITIVE {
 
                    ProcedureKind::Primitive
 
                } else {
 
                    ComponentVariant::Composite
 
                    ProcedureKind::Composite
 
                };
 
                let comp_def_id = ctx.heap.alloc_component_definition(|this| {
 
                    ComponentDefinition::new_empty(this, module.root_id, definition_span, component_variant, identifier, poly_vars)
 
                let proc_def_id = ctx.heap.alloc_procedure_definition(|this| {
 
                    ProcedureDefinition::new_empty(this, module.root_id, definition_span, procedure_kind, identifier, poly_vars)
 
                });
 
                definition_class = DefinitionClass::Component;
 
                ast_definition_id = comp_def_id.upcast();
 
                ast_definition_id = proc_def_id.upcast();
 
            },
 
            _ => unreachable!("encountered keyword '{}' in definition range", String::from_utf8_lossy(kw_text)),
 
        }

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

0 comments (0 inline, 0 general)