Changeset - 6717437470eb
[Not reviewed]
2 12 0
MH - 4 years ago 2021-03-02 16:14:17
contact@maxhenger.nl
WIP on bugfixing new visitor
14 files changed with 415 insertions and 478 deletions:
0 comments (0 inline, 0 general)
src/protocol/ast.rs
Show inline comments
 
@@ -80,13 +80,14 @@ impl FunctionId {
 
}
 

	
 
#[derive(Debug, Clone, Copy, PartialEq, serde::Serialize, serde::Deserialize)]
 
pub struct StatementId(Id<Statement>);
 

	
 
#[derive(Debug, Clone, Copy, PartialEq, serde::Serialize, serde::Deserialize)]
 
pub struct BlockStatementId(StatementId);
 
// TODO: Remove pub
 
pub struct BlockStatementId(pub StatementId);
 

	
 
impl BlockStatementId {
 
    pub fn upcast(self) -> StatementId {
 
        self.0
 
    }
 
}
 
@@ -815,12 +816,18 @@ impl Index<LocalId> for Heap {
 
    type Output = Local;
 
    fn index(&self, index: LocalId) -> &Self::Output {
 
        &self.variables[(index.0).0].as_local()
 
    }
 
}
 

	
 
impl IndexMut<LocalId> for Heap {
 
    fn index_mut(&mut self, index: LocalId) -> &mut Self::Output {
 
        self.variables[index.0.0].as_local_mut()
 
    }
 
}
 

	
 
impl Index<DefinitionId> for Heap {
 
    type Output = Definition;
 
    fn index(&self, index: DefinitionId) -> &Self::Output {
 
        &self.definitions[index.0]
 
    }
 
}
 
@@ -910,12 +917,18 @@ impl Index<IfStatementId> for Heap {
 
    type Output = IfStatement;
 
    fn index(&self, index: IfStatementId) -> &Self::Output {
 
        &self.statements[(index.0).0].as_if()
 
    }
 
}
 

	
 
impl IndexMut<IfStatementId> for Heap {
 
    fn index_mut(&mut self, index: IfStatementId) -> &mut Self::Output {
 
        self.statements[(index.0).0].as_if_mut()
 
    }
 
}
 

	
 
impl Index<EndIfStatementId> for Heap {
 
    type Output = EndIfStatement;
 
    fn index(&self, index: EndIfStatementId) -> &Self::Output {
 
        &self.statements[(index.0).0].as_end_if()
 
    }
 
}
 
@@ -1524,36 +1537,30 @@ impl Field {
 
            _ => false,
 
        }
 
    }
 
}
 

	
 
#[derive(Debug, Clone, Copy, serde::Serialize, serde::Deserialize)]
 
pub enum ScopeVariant {
 
pub enum Scope {
 
    Definition(DefinitionId),
 
    Regular(BlockStatementId),
 
    Synchronous((SynchronousStatementId, BlockStatementId)),
 
}
 

	
 
// TODO: Cleanup
 
#[derive(Debug, Clone, serde::Serialize, serde::Deserialize)]
 
pub struct Scope {
 
    pub variant: ScopeVariant
 
}
 

	
 
impl Scope {
 
    pub fn is_block(&self) -> bool {
 
        match &self.variant {
 
            ScopeVariant::Definition(_) => false,
 
            ScopeVariant::Regular(_) => true,
 
            ScopeVariant::Synchronous(_) => true,
 
        match &self {
 
            Scope::Definition(_) => false,
 
            Scope::Regular(_) => true,
 
            Scope::Synchronous(_) => true,
 
        }
 
    }
 
    pub fn to_block(&self) -> BlockStatementId {
 
        match &self.variant {
 
            ScopeVariant::Regular(id) => *id,
 
            ScopeVariant::Synchronous((_, id)) => *id,
 
        match &self {
 
            Scope::Regular(id) => *id,
 
            Scope::Synchronous((_, id)) => *id,
 
            _ => panic!("unable to get BlockStatement from Scope")
 
        }
 
    }
 
}
 

	
 
pub trait VariableScope {
 
@@ -1562,21 +1569,21 @@ pub trait VariableScope {
 
}
 

	
 
impl VariableScope for Scope {
 
    fn parent_scope(&self, h: &Heap) -> Option<Scope> {
 
        match self {
 
            Scope::Definition(def) => h[*def].parent_scope(h),
 
            Scope::Block(stmt) => h[*stmt].parent_scope(h),
 
            Scope::Synchronous(stmt) => h[*stmt].parent_scope(h),
 
            Scope::Regular(stmt) => h[*stmt].parent_scope(h),
 
            Scope::Synchronous((stmt, _)) => h[*stmt].parent_scope(h),
 
        }
 
    }
 
    fn get_variable(&self, h: &Heap, id: &Identifier) -> Option<VariableId> {
 
        match self {
 
            Scope::Definition(def) => h[*def].get_variable(h, id),
 
            Scope::Block(stmt) => h[*stmt].get_variable(h, id),
 
            Scope::Synchronous(stmt) => h[*stmt].get_variable(h, id),
 
            Scope::Regular(stmt) => h[*stmt].get_variable(h, id),
 
            Scope::Synchronous((stmt, _)) => h[*stmt].get_variable(h, id),
 
        }
 
    }
 
}
 

	
 
#[derive(Debug, Clone, serde::Serialize, serde::Deserialize)]
 
pub enum Variable {
 
@@ -1606,12 +1613,18 @@ impl Variable {
 
    pub fn as_local(&self) -> &Local {
 
        match self {
 
            Variable::Local(result) => result,
 
            _ => panic!("Unable to cast `Variable` to `Local`"),
 
        }
 
    }
 
    pub fn as_local_mut(&mut self) -> &mut Local {
 
        match self {
 
            Variable::Local(result) => result,
 
            _ => panic!("Unable to cast 'Variable' to 'Local'"),
 
        }
 
    }
 
    pub fn the_type<'b>(&self, h: &'b Heap) -> &'b Type {
 
        match self {
 
            Variable::Parameter(param) => &h[param.type_annotation].the_type,
 
            Variable::Local(local) => &h[local.type_annotation].the_type,
 
        }
 
    }
 
@@ -2016,12 +2029,18 @@ impl Statement {
 
    pub fn as_if(&self) -> &IfStatement {
 
        match self {
 
            Statement::If(result) => result,
 
            _ => panic!("Unable to cast `Statement` to `IfStatement`"),
 
        }
 
    }
 
    pub fn as_if_mut(&mut self) -> &mut IfStatement {
 
        match self {
 
            Statement::If(result) => result,
 
            _ => panic!("Unable to cast 'Statement' to 'IfStatement'"),
 
        }
 
    }
 
    pub fn as_end_if(&self) -> &EndIfStatement {
 
        match self {
 
            Statement::EndIf(result) => result,
 
            _ => panic!("Unable to cast `Statement` to `EndIfStatement`"),
 
        }
 
    }
 
@@ -2204,21 +2223,21 @@ impl BlockStatement {
 
        match parent {
 
            Scope::Definition(_) => {
 
                // If the parent scope is a definition, then there is no
 
                // parent block.
 
                None
 
            }
 
            Scope::Synchronous(parent) => {
 
            Scope::Synchronous((parent, _)) => {
 
                // It is always the case that when this function is called,
 
                // the parent of a synchronous statement is a block statement:
 
                // nested synchronous statements are flagged illegal,
 
                // and that happens before resolving variables that
 
                // creates the parent_scope references in the first place.
 
                Some(h[parent].parent_scope(h).unwrap().to_block())
 
            }
 
            Scope::Block(parent) => {
 
            Scope::Regular(parent) => {
 
                // A variable scope is either a definition, sync, or block.
 
                Some(parent)
 
            }
 
        }
 
    }
 
    pub fn first(&self) -> StatementId {
 
@@ -2410,13 +2429,13 @@ impl SyntaxElement for WhileStatement {
 
}
 

	
 
#[derive(Debug, Clone, serde::Serialize, serde::Deserialize)]
 
pub struct EndWhileStatement {
 
    pub this: EndWhileStatementId,
 
    // Phase 2: linker
 
    pub start_while: Option<WhileStatementId>,
 
    pub start_while: WhileStatementId,
 
    pub position: InputPosition, // of corresponding while
 
    pub next: Option<StatementId>,
 
}
 

	
 
impl SyntaxElement for EndWhileStatement {
 
    fn position(&self) -> InputPosition {
 
@@ -2475,19 +2494,20 @@ impl SyntaxElement for SynchronousStatement {
 
}
 

	
 
impl VariableScope for SynchronousStatement {
 
    fn parent_scope(&self, _h: &Heap) -> Option<Scope> {
 
        self.parent_scope.clone()
 
    }
 
    fn get_variable(&self, h: &Heap, id: &Identifier) -> Option<VariableId> {
 
        for parameter_id in self.parameters.iter() {
 
            let parameter = &h[*parameter_id];
 
            if parameter.identifier.value == id.value {
 
                return Some(parameter_id.0);
 
            }
 
        }
 
    fn get_variable(&self, _h: &Heap, _id: &Identifier) -> Option<VariableId> {
 
        // TODO: Another case of "where was this used for?"
 
        // for parameter_id in self.parameters.iter() {
 
        //     let parameter = &h[*parameter_id];
 
        //     if parameter.identifier.value == id.value {
 
        //         return Some(parameter_id.0);
 
        //     }
 
        // }
 
        None
 
    }
 
}
 

	
 
#[derive(Debug, Clone, serde::Serialize, serde::Deserialize)]
 
pub struct EndSynchronousStatement {
src/protocol/containers.rs
Show inline comments
 
deleted file
src/protocol/eval.rs
Show inline comments
 
@@ -1602,13 +1602,13 @@ type EvalResult = Result<Value, EvalContinuation>;
 
pub enum EvalContinuation {
 
    Stepping,
 
    Inconsistent,
 
    Terminal,
 
    SyncBlockStart,
 
    SyncBlockEnd,
 
    NewComponent(DeclarationId, Vec<Value>),
 
    NewComponent(DefinitionId, Vec<Value>),
 
    BlockFires(Value),
 
    BlockGet(Value),
 
    Put(Value, Value),
 
}
 

	
 
#[derive(Debug, Clone, serde::Serialize, serde::Deserialize)]
 
@@ -1696,13 +1696,13 @@ impl Prompt {
 
                // Evaluate test
 
                let value = self.store.eval(h, ctx, stmt.test)?;
 
                // Either continue with body, or go to next
 
                if value.as_boolean().0 {
 
                    self.position = Some(stmt.body);
 
                } else {
 
                    self.position = stmt.next.map(|x| x.upcast());
 
                    self.position = stmt.end_while.map(|x| x.upcast());
 
                }
 
                Err(EvalContinuation::Stepping)
 
            }
 
            Statement::EndWhile(stmt) => {
 
                // Continue to next statement
 
                self.position = stmt.next;
 
@@ -1756,13 +1756,18 @@ impl Prompt {
 
                let mut args = Vec::new();
 
                for &arg in expr.arguments.iter() {
 
                    let value = self.store.eval(h, ctx, arg)?;
 
                    args.push(value);
 
                }
 
                self.position = stmt.next;
 
                Err(EvalContinuation::NewComponent(expr.declaration.unwrap(), args))
 
                match &expr.method {
 
                    Method::Symbolic(symbolic) => {
 
                         Err(EvalContinuation::NewComponent(symbolic.definition.unwrap(), args))
 
                    },
 
                    _ => unreachable!("not a symbolic call expression")
 
                }
 
            }
 
            Statement::Put(stmt) => {
 
                // Evaluate port and message
 
                let port = self.store.eval(h, ctx, stmt.port)?;
 
                let message = self.store.eval(h, ctx, stmt.message)?;
 
                // Continue to next statement
src/protocol/inputsource.rs
Show inline comments
 
@@ -13,21 +13,21 @@ pub struct InputSource {
 
    column: usize,
 
    offset: usize,
 
}
 

	
 
static STD_LIB_PDL: &'static [u8] = b"
 
primitive forward(in i, out o) {
 
    while(true) synchronous() put(o, get(i));
 
    while(true) synchronous put(o, get(i));
 
}
 
primitive sync(in i, out o) {
 
    while(true) synchronous() if(fires(i)) put(o, get(i));
 
    while(true) synchronous if(fires(i)) put(o, get(i));
 
}
 
primitive alternator(in i, out l, out r) {
 
    while(true) {
 
        synchronous() if(fires(i)) put(l, get(i));
 
        synchronous() if(fires(i)) put(r, get(i));
 
        synchronous if(fires(i)) put(l, get(i));
 
        synchronous if(fires(i)) put(r, get(i));
 
    }
 
}
 
primitive replicator(in i, out l, out r) {
 
    while(true) synchronous {
 
        if(fires(i)) {
 
            msg m = get(i);
src/protocol/lexer.rs
Show inline comments
 
@@ -340,12 +340,14 @@ impl Lexer<'_> {
 
        let mut num_namespaces = 1;
 
        while self.has_string(b"::") {
 
            if num_namespaces >= MAX_NAMESPACES {
 
                return Err(self.error_at_pos("Too many namespaces in identifier"));
 
            }
 
            let new_ident = self.consume_ident()?;
 
            ns_ident.extend(b"::");
 
            ns_ident.extend(new_ident);
 
            num_namespaces += 1;
 
        }
 

	
 
        Ok(NamespacedIdentifier{
 
            position,
 
            value: ns_ident,
 
@@ -1170,14 +1172,13 @@ impl Lexer<'_> {
 
        }
 
        self.consume_string(b")")?;
 
        Ok(h.alloc_call_expression(|this| CallExpression {
 
            this,
 
            position,
 
            method,
 
            arguments,
 
            declaration: None,
 
            arguments
 
        }))
 
    }
 
    fn consume_variable_expression(
 
        &mut self,
 
        h: &mut Heap,
 
    ) -> Result<VariableExpressionId, ParseError2> {
 
@@ -1297,12 +1298,13 @@ impl Lexer<'_> {
 
        } else {
 
            Ok(h.alloc_block_statement(|this| BlockStatement {
 
                this,
 
                position,
 
                statements,
 
                parent_scope: None,
 
                relative_pos_in_parent: 0,
 
                locals: Vec::new(),
 
                labels: Vec::new(),
 
            })
 
            .upcast())
 
        }
 
    }
 
@@ -1331,37 +1333,46 @@ impl Lexer<'_> {
 
        self.consume_string(b";")?;
 
        let from = h.alloc_local(|this| Local {
 
            this,
 
            position,
 
            type_annotation: from_annotation,
 
            identifier: from_identifier,
 
            relative_pos_in_block: 0
 
        });
 
        let to = h.alloc_local(|this| Local {
 
            this,
 
            position,
 
            type_annotation: to_annotation,
 
            identifier: to_identifier,
 
            relative_pos_in_block: 0
 
        });
 
        Ok(h.alloc_channel_statement(|this| ChannelStatement {
 
            this,
 
            position,
 
            from,
 
            to,
 
            relative_pos_in_block: 0,
 
            next: None,
 
        }))
 
    }
 
    fn consume_memory_statement(&mut self, h: &mut Heap) -> Result<MemoryStatementId, ParseError2> {
 
        let position = self.source.pos();
 
        let type_annotation = self.consume_type_annotation(h)?;
 
        self.consume_whitespace(true)?;
 
        let identifier = self.consume_identifier()?;
 
        self.consume_whitespace(false)?;
 
        self.consume_string(b"=")?;
 
        self.consume_whitespace(false)?;
 
        let initial = self.consume_expression(h)?;
 
        let variable = h.alloc_local(|this| Local { this, position, type_annotation, identifier });
 
        let variable = h.alloc_local(|this| Local {
 
            this,
 
            position,
 
            type_annotation,
 
            identifier,
 
            relative_pos_in_block: 0
 
        });
 
        self.consume_whitespace(false)?;
 
        self.consume_string(b";")?;
 
        Ok(h.alloc_memory_statement(|this| MemoryStatement {
 
            this,
 
            position,
 
            variable,
 
@@ -1381,12 +1392,13 @@ impl Lexer<'_> {
 
        let body = self.consume_statement(h)?;
 
        Ok(h.alloc_labeled_statement(|this| LabeledStatement {
 
            this,
 
            position,
 
            label,
 
            body,
 
            relative_pos_in_block: 0,
 
            in_sync: None,
 
        }))
 
    }
 
    fn consume_skip_statement(&mut self, h: &mut Heap) -> Result<SkipStatementId, ParseError2> {
 
        let position = self.source.pos();
 
        self.consume_keyword(b"skip")?;
 
@@ -1406,13 +1418,13 @@ impl Lexer<'_> {
 
            self.consume_keyword(b"else")?;
 
            self.consume_whitespace(false)?;
 
            self.consume_statement(h)?
 
        } else {
 
            h.alloc_skip_statement(|this| SkipStatement { this, position, next: None }).upcast()
 
        };
 
        Ok(h.alloc_if_statement(|this| IfStatement { this, position, test, true_body, false_body }))
 
        Ok(h.alloc_if_statement(|this| IfStatement { this, position, test, true_body, false_body, end_if: None }))
 
    }
 
    fn consume_while_statement(&mut self, h: &mut Heap) -> Result<WhileStatementId, ParseError2> {
 
        let position = self.source.pos();
 
        self.consume_keyword(b"while")?;
 
        self.consume_whitespace(false)?;
 
        let test = self.consume_paren_expression(h)?;
 
@@ -1420,13 +1432,13 @@ impl Lexer<'_> {
 
        let body = self.consume_statement(h)?;
 
        Ok(h.alloc_while_statement(|this| WhileStatement {
 
            this,
 
            position,
 
            test,
 
            body,
 
            next: None,
 
            end_while: None,
 
            in_sync: None,
 
        }))
 
    }
 
    fn consume_break_statement(&mut self, h: &mut Heap) -> Result<BreakStatementId, ParseError2> {
 
        let position = self.source.pos();
 
        self.consume_keyword(b"break")?;
 
@@ -1480,12 +1492,13 @@ impl Lexer<'_> {
 
        // }
 
        let body = self.consume_statement(h)?;
 
        Ok(h.alloc_synchronous_statement(|this| SynchronousStatement {
 
            this,
 
            position,
 
            body,
 
            end_sync: None,
 
            parent_scope: None,
 
        }))
 
    }
 
    fn consume_return_statement(&mut self, h: &mut Heap) -> Result<ReturnStatementId, ParseError2> {
 
        let position = self.source.pos();
 
        self.consume_keyword(b"return")?;
src/protocol/library.rs
Show inline comments
 
use crate::protocol::ast::*;
 
use crate::protocol::inputsource::*;
 

	
 
// TABBED OUT FOR NOW
 

	
 
pub fn get_declarations(h: &mut Heap, i: ImportId) -> Result<Vec<DeclarationId>, ParseError2> {
 
pub fn get_declarations(_h: &mut Heap, _i: ImportId) -> Result<Vec<DeclarationId>, ParseError2> {
 
    todo!("implement me");
 
    // if h[i].value == b"std.reo" {
 
    //     let mut vec = Vec::new();
 
    //     vec.push(cd(h, i, b"sync", &[Type::INPUT, Type::OUTPUT]));
 
    //     vec.push(cd(h, i, b"syncdrain", &[Type::INPUT, Type::INPUT]));
 
    //     vec.push(cd(h, i, b"syncspout", &[Type::OUTPUT, Type::OUTPUT]));
src/protocol/mod.rs
Show inline comments
 
@@ -2,13 +2,12 @@ mod arena;
 
// mod ast;
 
mod eval;
 
pub(crate) mod inputsource;
 
// mod lexer;
 
mod library;
 
mod parser;
 
mod containers;
 

	
 
// TODO: Remove when not benchmarking
 
pub(crate) mod ast;
 
pub(crate) mod lexer;
 

	
 
lazy_static::lazy_static! {
 
@@ -140,17 +139,16 @@ impl ComponentState {
 
                    EvalContinuation::Stepping => continue,
 
                    EvalContinuation::Inconsistent => return NonsyncBlocker::Inconsistent,
 
                    EvalContinuation::Terminal => return NonsyncBlocker::ComponentExit,
 
                    EvalContinuation::SyncBlockStart => return NonsyncBlocker::SyncBlockStart,
 
                    // Not possible to end sync block if never entered one
 
                    EvalContinuation::SyncBlockEnd => unreachable!(),
 
                    EvalContinuation::NewComponent(decl, args) => {
 
                    EvalContinuation::NewComponent(definition_id, args) => {
 
                        // Look up definition (TODO for now, assume it is a definition)
 
                        let h = &pd.heap;
 
                        let def = h[decl].as_defined().definition;
 
                        let init_state = ComponentState { prompt: Prompt::new(h, def, &args) };
 
                        let init_state = ComponentState { prompt: Prompt::new(h, definition_id, &args) };
 
                        context.new_component(&args, init_state);
 
                        // Continue stepping
 
                        continue;
 
                    }
 
                    // Outside synchronous blocks, no fires/get/put happens
 
                    EvalContinuation::BlockFires(_) => unreachable!(),
src/protocol/parser/depth_visitor.rs
Show inline comments
 
@@ -18,16 +18,16 @@ pub(crate) trait Visitor: Sized {
 
        Ok(())
 
    }
 

	
 
    fn visit_symbol_definition(&mut self, h: &mut Heap, def: DefinitionId) -> VisitorResult {
 
        recursive_symbol_definition(self, h, def)
 
    }
 
    fn visit_struct_definition(&mut self, h: &mut Heap, def: StructId) -> VisitorResult {
 
    fn visit_struct_definition(&mut self, _h: &mut Heap, _def: StructId) -> VisitorResult {
 
        Ok(())
 
    }
 
    fn visit_enum_definition(&mut self, h: &mut Heap, def: EnumId) -> VisitorResult {
 
    fn visit_enum_definition(&mut self, _h: &mut Heap, _def: EnumId) -> VisitorResult {
 
        Ok(())
 
    }
 
    fn visit_component_definition(&mut self, h: &mut Heap, def: ComponentId) -> VisitorResult {
 
        recursive_component_definition(self, h, def)
 
    }
 
    fn visit_composite_definition(&mut self, h: &mut Heap, def: ComponentId) -> VisitorResult {
 
@@ -382,15 +382,16 @@ fn recursive_while_statement<T: Visitor>(
 

	
 
fn recursive_synchronous_statement<T: Visitor>(
 
    this: &mut T,
 
    h: &mut Heap,
 
    stmt: SynchronousStatementId,
 
) -> VisitorResult {
 
    for &param in h[stmt].parameters.clone().iter() {
 
        recursive_parameter_as_variable(this, h, param)?;
 
    }
 
    // TODO: Check where this was used for
 
    // for &param in h[stmt].parameters.clone().iter() {
 
    //     recursive_parameter_as_variable(this, h, param)?;
 
    // }
 
    this.visit_statement(h, h[stmt].body)
 
}
 

	
 
fn recursive_return_statement<T: Visitor>(
 
    this: &mut T,
 
    h: &mut Heap,
 
@@ -891,13 +892,14 @@ impl Visitor for LinkCallExpressions {
 
                return Err((id.identifier.position, "Illegal call expression".to_string()));
 
            }
 
            if !self.new_statement && h[decl].is_component() {
 
                return Err((id.identifier.position, "Illegal call expression".to_string()));
 
            }
 
            // Set the corresponding declaration of the call
 
            h[expr].declaration = Some(decl);
 
            // TODO: This should not be necessary anymore once parser is rewritten
 
            // h[expr]. = Some(decl);
 
        }
 
        // A new statement's call expression may have as arguments function calls
 
        let old = self.new_statement;
 
        self.new_statement = false;
 
        recursive_call_expression(self, h, expr)?;
 
        self.new_statement = old;
 
@@ -926,13 +928,13 @@ impl Visitor for BuildScope {
 
    fn visit_block_statement(&mut self, h: &mut Heap, stmt: BlockStatementId) -> VisitorResult {
 
        assert!(!self.scope.is_none());
 
        let old = self.scope;
 
        // First store the current scope
 
        h[stmt].parent_scope = self.scope;
 
        // Then move scope down to current block
 
        self.scope = Some(Scope::Block(stmt));
 
        self.scope = Some(Scope::Regular(stmt));
 
        recursive_block_statement(self, h, stmt)?;
 
        // Move scope back up
 
        self.scope = old;
 
        Ok(())
 
    }
 
    fn visit_synchronous_statement(
 
@@ -942,13 +944,14 @@ impl Visitor for BuildScope {
 
    ) -> VisitorResult {
 
        assert!(!self.scope.is_none());
 
        let old = self.scope;
 
        // First store the current scope
 
        h[stmt].parent_scope = self.scope;
 
        // Then move scope down to current sync
 
        self.scope = Some(Scope::Synchronous(stmt));
 
        // TODO: Should be legal-ish, but very wrong
 
        self.scope = Some(Scope::Synchronous((stmt, BlockStatementId(stmt.upcast()))));
 
        recursive_synchronous_statement(self, h, stmt)?;
 
        // Move scope back up
 
        self.scope = old;
 
        Ok(())
 
    }
 
    fn visit_expression(&mut self, _h: &mut Heap, _expr: ExpressionId) -> VisitorResult {
 
@@ -1063,35 +1066,36 @@ impl Visitor for ResolveVariables {
 
        }
 
        Ok(())
 
    }
 
    fn visit_block_statement(&mut self, h: &mut Heap, stmt: BlockStatementId) -> VisitorResult {
 
        assert!(!self.scope.is_none());
 
        let old = self.scope;
 
        self.scope = Some(Scope::Block(stmt));
 
        self.scope = Some(Scope::Regular(stmt));
 
        recursive_block_statement(self, h, stmt)?;
 
        self.scope = old;
 
        Ok(())
 
    }
 
    fn visit_synchronous_statement(
 
        &mut self,
 
        h: &mut Heap,
 
        stmt: SynchronousStatementId,
 
    ) -> VisitorResult {
 
        assert!(!self.scope.is_none());
 
        let old = self.scope;
 
        self.scope = Some(Scope::Synchronous(stmt));
 
        self.scope = Some(Scope::Synchronous((stmt, BlockStatementId(stmt.upcast())))); // TODO: WRONG!
 
        recursive_synchronous_statement(self, h, stmt)?;
 
        self.scope = old;
 
        Ok(())
 
    }
 
    fn visit_variable_expression(
 
        &mut self,
 
        h: &mut Heap,
 
        expr: VariableExpressionId,
 
    ) -> VisitorResult {
 
        let var = self.get_variable(h, &h[expr].identifier)?;
 
        let ident = Identifier{ position: Default::default(), value: h[expr].identifier.value.clone() };
 
        let var = self.get_variable(h, &ident)?;
 
        h[expr].declaration = Some(var);
 
        Ok(())
 
    }
 
}
 

	
 
pub(crate) struct UniqueStatementId(StatementId);
 
@@ -1132,13 +1136,13 @@ impl Visitor for LinkStatements {
 
        Ok(())
 
    }
 
    fn visit_if_statement(&mut self, h: &mut Heap, stmt: IfStatementId) -> VisitorResult {
 
        // We allocate a pseudo-statement, which combines both branches into one next statement
 
        let position = h[stmt].position;
 
        let pseudo =
 
            h.alloc_end_if_statement(|this| EndIfStatement { this, position, next: None }).upcast();
 
            h.alloc_end_if_statement(|this| EndIfStatement { this, start_if: stmt, position, next: None }).upcast();
 
        assert!(self.prev.is_none());
 
        self.visit_statement(h, h[stmt].true_body)?;
 
        if let Some(UniqueStatementId(prev)) = self.prev.take() {
 
            h[prev].link_next(pseudo);
 
        }
 
        assert!(self.prev.is_none());
 
@@ -1151,15 +1155,15 @@ impl Visitor for LinkStatements {
 
        Ok(())
 
    }
 
    fn visit_while_statement(&mut self, h: &mut Heap, stmt: WhileStatementId) -> VisitorResult {
 
        // We allocate a pseudo-statement, to which the break statement finds its target
 
        let position = h[stmt].position;
 
        let pseudo =
 
            h.alloc_end_while_statement(|this| EndWhileStatement { this, position, next: None });
 
            h.alloc_end_while_statement(|this| EndWhileStatement { this, start_while: stmt, position, next: None });
 
        // Update the while's next statement to point to the pseudo-statement
 
        h[stmt].next = Some(pseudo);
 
        h[stmt].end_while = Some(pseudo);
 
        assert!(self.prev.is_none());
 
        self.visit_statement(h, h[stmt].body)?;
 
        // The body's next statement loops back to the while statement itself
 
        // Note: continue statements also loop back to the while statement itself
 
        if let Some(UniqueStatementId(prev)) = std::mem::replace(&mut self.prev, None) {
 
            h[prev].link_next(stmt.upcast());
 
@@ -1187,12 +1191,13 @@ impl Visitor for LinkStatements {
 
        // that marks the end of the synchronous block. Every evaluation has to pause at this
 
        // point, only to resume later when the thread is selected as unique thread to continue.
 
        let position = h[stmt].position;
 
        let pseudo = h
 
            .alloc_end_synchronous_statement(|this| EndSynchronousStatement {
 
                this,
 
                start_sync: stmt,
 
                position,
 
                next: None,
 
            })
 
            .upcast();
 
        assert!(self.prev.is_none());
 
        self.visit_statement(h, h[stmt].body)?;
 
@@ -1397,13 +1402,13 @@ impl Visitor for ResolveLabels {
 
        if the_while.in_sync != self.sync_enclosure {
 
            return Err((
 
                h[stmt].position,
 
                "Illegal break: synchronous statement escape".to_string(),
 
            ));
 
        }
 
        h[stmt].target = the_while.next;
 
        h[stmt].target = the_while.end_while;
 
        Ok(())
 
    }
 
    fn visit_continue_statement(
 
        &mut self,
 
        h: &mut Heap,
 
        stmt: ContinueStatementId,
src/protocol/parser/functions.rs
Show inline comments
 
deleted file
src/protocol/parser/mod.rs
Show inline comments
 
mod depth_visitor;
 
mod symbol_table;
 
mod type_table;
 
mod visitor;
 
mod functions;
 

	
 
use depth_visitor::*;
 
use symbol_table::SymbolTable;
 
use visitor::{Visitor2, ValidityAndLinkerVisitor};
 
use type_table::TypeTable;
 

	
 
use crate::protocol::ast::*;
 
use crate::protocol::inputsource::*;
 
use crate::protocol::lexer::*;
 

	
 

	
 
use std::collections::HashMap;
 
use crate::protocol::parser::visitor::Ctx;
 

	
 
// TODO: @fixme, pub qualifier
 
pub(crate) struct LexedModule {
 
    pub(crate) source: InputSource,
 
    module_name: Vec<u8>,
 
    version: Option<u64>,
 
@@ -122,13 +123,13 @@ impl Parser {
 
    }
 

	
 
    pub fn compile(&mut self) {
 
        // Build module lookup
 
    }
 

	
 
    fn resolve_symbols_and_types(&mut self) -> Result<(), ParseError2> {
 
    fn resolve_symbols_and_types(&mut self) -> Result<(SymbolTable, TypeTable), ParseError2> {
 
        // Construct the symbol table to resolve any imports and/or definitions,
 
        // then use the symbol table to actually annotate all of the imports.
 
        // If the type table is constructed correctly then all imports MUST be
 
        // resolvable.
 
        // TODO: Update once namespaced identifiers are implemented
 
        let symbol_table = SymbolTable::new(&self.heap, &self.modules)?;
 
@@ -183,67 +184,56 @@ impl Parser {
 
        }
 

	
 
        // All imports in the AST are now annotated. We now use the symbol table
 
        // to construct the type table.
 
        let type_table = TypeTable::new(&symbol_table, &self.heap, &self.modules)?;
 

	
 
        // We should now be able to resolve all definitions and statements
 
        // using those definitions.
 
        // Temporary personal notes
 
        //  memory declaration = Statement::Local(Local::Memory(...))
 
        //      -> VariableId
 
        //      -> TypeAnnotationId
 
        //      -> PrimitiveType::Symbolic variant
 
        //      -> Option<DefinitionId> field
 
        //  method call = Expression::Call
 
        //      -> Method::Symbolic field
 
        //      -> Option<DefinitionId> field
 
        // TODO: I might not actually want to do this here
 
        module_index = 0;
 
        let mut definition_index = 0;
 
        let mut statement_index = 0;
 
        loop {
 
            if module_index >= self.modules.len() {
 
                break;
 
            }
 

	
 
            let module_root_id = self.modules[module_index].root_id;
 
            let statement_id = {
 
                let root = &self.heap[module_root_id];
 
                if statement_index >= root.
 
            }
 
        }
 

	
 
        Ok(())
 
        Ok((symbol_table, type_table))
 
    }
 

	
 
    // TODO: @fix, temporary impl to keep code compilable
 
    pub fn parse(&mut self) -> Result<RootId, ParseError2> {
 
        assert_eq!(self.modules.len(), 1, "Fix meeeee");
 
        let root_id = self.modules[0].root_id;
 
        println!("DEBUG: With root id {:?}\nSource: {}", root_id, String::from_utf8_lossy(&self.modules[0].source.input));
 

	
 
        let (mut symbol_table, mut type_table) = self.resolve_symbols_and_types()?;
 

	
 
        // TODO: @cleanup
 
        let mut ctx = visitor::Ctx{
 
            heap: &mut self.heap,
 
            module: &self.modules[0],
 
            symbols: &mut symbol_table,
 
            types: &mut type_table,
 
        };
 
        let mut visit = ValidityAndLinkerVisitor::new();
 
        if let Err(err) = visit.visit_module(&mut ctx) {
 
            println!("ERROR:\n{}", err);
 
            return Err(err)
 
        }
 

	
 
        if let Err((position, message)) = Self::parse_inner(&mut self.heap, root_id) {
 
            return Err(ParseError2::new_error(&self.modules[0].source, position, &message))
 
        }
 

	
 
        Ok(root_id)
 
    }
 

	
 
    pub fn parse_inner(h: &mut Heap, pd: RootId) -> VisitorResult {
 
        // TODO: @cleanup, slowly phasing out old compiler
 
        NestedSynchronousStatements::new().visit_protocol_description(h, pd)?;
 
        ChannelStatementOccurrences::new().visit_protocol_description(h, pd)?;
 
        FunctionStatementReturns::new().visit_protocol_description(h, pd)?;
 
        ComponentStatementReturnNew::new().visit_protocol_description(h, pd)?;
 
        CheckBuiltinOccurrences::new().visit_protocol_description(h, pd)?;
 
        BuildSymbolDeclarations::new().visit_protocol_description(h, pd)?;
 
        LinkCallExpressions::new().visit_protocol_description(h, pd)?;
 
        BuildScope::new().visit_protocol_description(h, pd)?;
 
        ResolveVariables::new().visit_protocol_description(h, pd)?;
 
        LinkStatements::new().visit_protocol_description(h, pd)?;
 
        BuildLabels::new().visit_protocol_description(h, pd)?;
 
        ResolveLabels::new().visit_protocol_description(h, pd)?;
 
        // BuildLabels::new().visit_protocol_description(h, pd)?;
 
        // ResolveLabels::new().visit_protocol_description(h, pd)?;
 
        AssignableExpressions::new().visit_protocol_description(h, pd)?;
 
        IndexableExpressions::new().visit_protocol_description(h, pd)?;
 
        SelectableExpressions::new().visit_protocol_description(h, pd)?;
 

	
 
        Ok(())
 
    }
 
@@ -260,13 +250,13 @@ mod tests {
 
    // #[test]
 
    fn positive_tests() {
 
        for resource in TestFileIter::new("testdata/parser/positive", "pdl") {
 
            let resource = resource.expect("read testdata filepath");
 
            // println!(" * running: {}", &resource);
 
            let path = Path::new(&resource);
 
            let mut source = InputSource::from_file(&path).unwrap();
 
            let source = InputSource::from_file(&path).unwrap();
 
            // println!("DEBUG -- input:\n{}", String::from_utf8_lossy(&source.input));
 
            let mut parser = Parser::new_with_source(source).expect("parse source");
 
            match parser.parse() {
 
                Ok(_) => {}
 
                Err(err) => {
 
                    println!(" > file: {}", &resource);
src/protocol/parser/symbol_table.rs
Show inline comments
 
@@ -111,16 +111,20 @@ impl SymbolTable {
 
                                None => {
 
                                    return Err(
 
                                        ParseError2::new_error(&module.source, import.position, "Cannot resolve module")
 
                                    );
 
                                }
 
                            }
 
                        } else {
 
                            lookup_reserve_size += import.symbols.len();
 
                        }
 
                    }
 
                }
 
            }
 

	
 
            lookup_reserve_size += module_root.definitions.len();
 
        }
 

	
 
        let mut table = Self{
 
            module_lookup,
 
            symbol_lookup: HashMap::with_capacity(lookup_reserve_size)
 
        };
 
@@ -263,12 +267,39 @@ impl SymbolTable {
 
                            }
 
                        }
 
                    }
 
                }
 
            }
 
        }
 
        fn find_name(heap: &Heap, root_id: RootId) -> String {
 
            let root = &heap[root_id];
 
            for pragma_id in &root.pragmas {
 
                match &heap[*pragma_id] {
 
                    Pragma::Module(module) => {
 
                        return String::from_utf8_lossy(&module.value).to_string()
 
                    },
 
                    _ => {},
 
                }
 
            }
 

	
 
            return String::from("Unknown")
 
        }
 

	
 
        for (k, v) in table.symbol_lookup.iter() {
 
            let key = String::from_utf8_lossy(&k.symbol_name).to_string();
 
            let value = match v.symbol {
 
                Symbol::Definition((a, b)) => {
 
                    let utf8 = String::from_utf8_lossy(&heap[b].identifier().value);
 
                    format!("Definition({}) in Root({})", utf8, find_name(heap, a))
 
                },
 
                Symbol::Namespace(a) => {
 
                    format!("Root({})", find_name(heap, a))
 
                }
 
            };
 
            println!("{} => {}", key, value);
 
        }
 

	
 
        debug_assert_eq!(
 
            table.symbol_lookup.len(), lookup_reserve_size,
 
            "miscalculated reserved size for symbol lookup table"
 
        );
 
        Ok(table)
src/protocol/parser/type_table.rs
Show inline comments
 
@@ -201,24 +201,23 @@ impl TypeTable {
 

	
 
            // Construct breadcrumbs in case we need to follow some types around
 
            debug_assert!(breadcrumbs.is_empty());
 
            breadcrumbs.push(Breadcrumb::Linear((module_index, definition_index)));
 
            'resolve_loop: while !breadcrumbs.is_empty() {
 
                // Retrieve module, the module's root and the definition
 
                let (module, root, definition_id) = match breadcrumbs.last().unwrap() {
 
                let (module, definition_id) = match breadcrumbs.last().unwrap() {
 
                    Breadcrumb::Linear((module_index, definition_index)) => {
 
                        let module = &modules[*module_index];
 
                        let root = &heap[module.root_id];
 
                        let definition_id = root.definitions[*definition_index];
 
                        (module, root, definition_id)
 
                        (module, definition_id)
 
                    },
 
                    Breadcrumb::Jumping((root_id, definition_id)) => {
 
                        let module = &modules[root_id.0.index as usize];
 
                        debug_assert_eq!(module.root_id, *root_id);
 
                        let root = &heap[*root_id];
 
                        (module, root, *definition_id)
 
                        (module, *definition_id)
 
                    }
 
                };
 

	
 
                let definition = &heap[definition_id];
 

	
 
                // Because we might have chased around to this particular 
src/protocol/parser/visitor.rs
Show inline comments
 
use crate::protocol::ast::*;
 
use crate::protocol::inputsource::*;
 
use crate::protocol::library;
 
use crate::protocol::parser::{symbol_table::*, type_table::*, LexedModule};
 

	
 
type Unit = ();
 
pub(crate) type VisitorResult = Result<Unit, ParseError2>;
 

	
 
pub(crate) struct Ctx<'p> {
 
    heap: &'p mut Heap,
 
    module: &'p LexedModule,
 
    symbols: &'p mut SymbolTable,
 
    types: &'p mut TypeTable,
 
    pub heap: &'p mut Heap,
 
    pub module: &'p LexedModule,
 
    pub symbols: &'p mut SymbolTable,
 
    pub types: &'p mut TypeTable,
 
}
 

	
 
/// Visitor is a generic trait that will fully walk the AST. The default
 
/// implementation of the visitors is to not recurse. The exception is the
 
/// top-level `visit_definition`, `visit_stmt` and `visit_expr` methods, which
 
/// call the appropriate visitor function.
 
@@ -28,62 +27,126 @@ pub(crate) trait Visitor2 {
 
                    return Ok(())
 
                }
 

	
 
                root.definitions[def_index]
 
            };
 

	
 
            self.visit_definition(ctx, definition_id)
 
            self.visit_definition(ctx, definition_id)?;
 
            def_index += 1;
 
        }
 
    }
 

	
 
    // Definitions
 
    // --- enum matching
 
    fn visit_definition(&mut self, ctx: &mut Ctx, id: DefinitionId) -> VisitorResult {
 
        match &ctx.heap[id] {
 
            Definition::Enum(def) => self.visit_enum_definition(ctx, def.this),
 
            Definition::Struct(def) => self.visit_struct_definition(ctx, def.this),
 
            Definition::Component(def) => self.visit_component_definition(ctx, def.this),
 
            Definition::Function(def) => self.visit_function_definition(ctx, def.this)
 
            Definition::Enum(def) => {
 
                let def = def.this;
 
                self.visit_enum_definition(ctx, def)
 
            },
 
            Definition::Struct(def) => {
 
                let def = def.this;
 
                self.visit_struct_definition(ctx, def)
 
            },
 
            Definition::Component(def) => {
 
                let def = def.this;
 
                self.visit_component_definition(ctx, def)
 
            },
 
            Definition::Function(def) => {
 
                let def = def.this;
 
                self.visit_function_definition(ctx, def)
 
            }
 
        }
 
    }
 

	
 
    // --- enum variant handling
 
    fn visit_enum_definition(&mut self, _ctx: &mut Ctx, id: EnumId) -> VisitorResult { Ok(()) }
 
    fn visit_struct_definition(&mut self, _ctx: &mut Ctx, id: StructId) -> VisitorResult { Ok(()) }
 
    fn visit_component_definition(&mut self, _ctx: &mut Ctx, id: ComponentId) -> VisitorResult { Ok(()) }
 
    fn visit_function_definition(&mut self, _ctx: &mut Ctx, id: FunctionId) -> VisitorResult { Ok(()) }
 
    fn visit_enum_definition(&mut self, _ctx: &mut Ctx, _id: EnumId) -> VisitorResult { Ok(()) }
 
    fn visit_struct_definition(&mut self, _ctx: &mut Ctx, _id: StructId) -> VisitorResult { Ok(()) }
 
    fn visit_component_definition(&mut self, _ctx: &mut Ctx, _id: ComponentId) -> VisitorResult { Ok(()) }
 
    fn visit_function_definition(&mut self, _ctx: &mut Ctx, _id: FunctionId) -> VisitorResult { Ok(()) }
 

	
 
    // Statements
 
    // --- enum matching
 
    fn visit_stmt(&mut self, ctx: &mut Ctx, id: StatementId) -> VisitorResult {
 
        match &ctx.heap[id] {
 
            Statement::Block(stmt) => self.visit_block_stmt(ctx, stmt.this),
 
            Statement::Local(stmt) => self.visit_local_stmt(ctx, stmt.this),
 
            Statement::Skip(stmt) => self.visit_skip_stmt(ctx, stmt.this),
 
            Statement::Labeled(stmt) => self.visit_labeled_stmt(ctx, stmt.this),
 
            Statement::If(stmt) => self.visit_if_stmt(ctx, stmt.this),
 
            Statement::Block(stmt) => {
 
                let this = stmt.this;
 
                self.visit_block_stmt(ctx, this)
 
            },
 
            Statement::Local(stmt) => {
 
                let this = stmt.this();
 
                self.visit_local_stmt(ctx, this)
 
            },
 
            Statement::Skip(stmt) => {
 
                let this = stmt.this;
 
                self.visit_skip_stmt(ctx, this)
 
            },
 
            Statement::Labeled(stmt) => {
 
                let this = stmt.this;
 
                self.visit_labeled_stmt(ctx, this)
 
            },
 
            Statement::If(stmt) => {
 
                let this = stmt.this;
 
                self.visit_if_stmt(ctx, this)
 
            },
 
            Statement::EndIf(_stmt) => Ok(()),
 
            Statement::While(stmt) => self.visit_while_stmt(ctx, stmt.this),
 
            Statement::While(stmt) => {
 
                let this = stmt.this;
 
                self.visit_while_stmt(ctx, this)
 
            },
 
            Statement::EndWhile(_stmt) => Ok(()),
 
            Statement::Break(stmt) => self.visit_break_stmt(ctx, stmt.this),
 
            Statement::Continue(stmt) => self.visit_continue_stmt(ctx, stmt.this),
 
            Statement::Synchronous(stmt) => self.visit_synchronous_stmt(ctx, stmt.this),
 
            Statement::Break(stmt) => {
 
                let this = stmt.this;
 
                self.visit_break_stmt(ctx, this)
 
            },
 
            Statement::Continue(stmt) => {
 
                let this = stmt.this;
 
                self.visit_continue_stmt(ctx, this)
 
            },
 
            Statement::Synchronous(stmt) => {
 
                let this = stmt.this;
 
                self.visit_synchronous_stmt(ctx, this)
 
            },
 
            Statement::EndSynchronous(_stmt) => Ok(()),
 
            Statement::Return(stmt) => self.visit_return_stmt(ctx, stmt.this),
 
            Statement::Assert(stmt) => self.visit_assert_stmt(ctx, stmt.this),
 
            Statement::Goto(stmt) => self.visit_goto_stmt(ctx, stmt.this),
 
            Statement::New(stmt) => self.visit_new_stmt(ctx, stmt.this),
 
            Statement::Put(stmt) => self.visit_put_stmt(ctx, stmt.this),
 
            Statement::Expression(stmt) => self.visit_expr_stmt(ctx, stmt.this),
 
            Statement::Return(stmt) => {
 
                let this = stmt.this;
 
                self.visit_return_stmt(ctx, this)
 
            },
 
            Statement::Assert(stmt) => {
 
                let this = stmt.this;
 
                self.visit_assert_stmt(ctx, this)
 
            },
 
            Statement::Goto(stmt) => {
 
                let this = stmt.this;
 
                self.visit_goto_stmt(ctx, this)
 
            },
 
            Statement::New(stmt) => {
 
                let this = stmt.this;
 
                self.visit_new_stmt(ctx, this)
 
            },
 
            Statement::Put(stmt) => {
 
                let this = stmt.this;
 
                self.visit_put_stmt(ctx, this)
 
            },
 
            Statement::Expression(stmt) => {
 
                let this = stmt.this;
 
                self.visit_expr_stmt(ctx, this)
 
            }
 
        }
 
    }
 

	
 
    fn visit_local_stmt(&mut self, ctx: &mut Ctx, id: LocalStatementId) -> VisitorResult {
 
        match &ctx.heap[id] {
 
            LocalStatement::Channel(stmt) => self.visit_local_channel_stmt(ctx, stmt.this),
 
            LocalStatement::Memory(stmt) => self.visit_local_memory_stmt(ctx, stmt.this),
 
            LocalStatement::Channel(stmt) => {
 
                let this = stmt.this;
 
                self.visit_local_channel_stmt(ctx, this)
 
            },
 
            LocalStatement::Memory(stmt) => {
 
                let this = stmt.this;
 
                self.visit_local_memory_stmt(ctx, this)
 
            },
 
        }
 
    }
 

	
 
    // --- enum variant handling
 
    fn visit_block_stmt(&mut self, _ctx: &mut Ctx, _id: BlockStatementId) -> VisitorResult { Ok(()) }
 
    fn visit_local_memory_stmt(&mut self, _ctx: &mut Ctx, _id: MemoryStatementId) -> VisitorResult { Ok(()) }
 
@@ -103,23 +166,56 @@ pub(crate) trait Visitor2 {
 
    fn visit_expr_stmt(&mut self, _ctx: &mut Ctx, _id: ExpressionStatementId) -> VisitorResult { Ok(()) }
 

	
 
    // Expressions
 
    // --- enum matching
 
    fn visit_expr(&mut self, ctx: &mut Ctx, id: ExpressionId) -> VisitorResult {
 
        match &ctx.heap[id] {
 
            Expression::Assignment(expr) => self.visit_assignment_expr(ctx, expr.this),
 
            Expression::Conditional(expr) => self.visit_conditional_expr(ctx, expr.this),
 
            Expression::Binary(expr) => self.visit_binary_expr(ctx, expr.this),
 
            Expression::Unary(expr) => self.visit_unary_expr(ctx, expr.this),
 
            Expression::Indexing(expr) => self.visit_indexing_expr(ctx, expr.this),
 
            Expression::Slicing(expr) => self.visit_slicing_expr(ctx, expr.this),
 
            Expression::Select(expr) => self.visit_select_expr(ctx, expr.this),
 
            Expression::Array(expr) => self.visit_array_expr(ctx, expr.this),
 
            Expression::Constant(expr) => self.visit_constant_expr(ctx, expr.this),
 
            Expression::Call(expr) => self.visit_call_expr(ctx, expr.this),
 
            Expression::Variable(expr) => self.visit_variable_expr(ctx, expr.this),
 
            Expression::Assignment(expr) => {
 
                let this = expr.this;
 
                self.visit_assignment_expr(ctx, this)
 
            },
 
            Expression::Conditional(expr) => {
 
                let this = expr.this;
 
                self.visit_conditional_expr(ctx, this)
 
            }
 
            Expression::Binary(expr) => {
 
                let this = expr.this;
 
                self.visit_binary_expr(ctx, this)
 
            }
 
            Expression::Unary(expr) => {
 
                let this = expr.this;
 
                self.visit_unary_expr(ctx, this)
 
            }
 
            Expression::Indexing(expr) => {
 
                let this = expr.this;
 
                self.visit_indexing_expr(ctx, this)
 
            }
 
            Expression::Slicing(expr) => {
 
                let this = expr.this;
 
                self.visit_slicing_expr(ctx, this)
 
            }
 
            Expression::Select(expr) => {
 
                let this = expr.this;
 
                self.visit_select_expr(ctx, this)
 
            }
 
            Expression::Array(expr) => {
 
                let this = expr.this;
 
                self.visit_array_expr(ctx, this)
 
            }
 
            Expression::Constant(expr) => {
 
                let this = expr.this;
 
                self.visit_constant_expr(ctx, this)
 
            }
 
            Expression::Call(expr) => {
 
                let this = expr.this;
 
                self.visit_call_expr(ctx, this)
 
            }
 
            Expression::Variable(expr) => {
 
                let this = expr.this;
 
                self.visit_variable_expr(ctx, this)
 
            }
 
        }
 
    }
 

	
 
    fn visit_assignment_expr(&mut self, _ctx: &mut Ctx, _id: AssignmentExpressionId) -> VisitorResult { Ok(()) }
 
    fn visit_conditional_expr(&mut self, _ctx: &mut Ctx, _id: ConditionalExpressionId) -> VisitorResult { Ok(()) }
 
    fn visit_binary_expr(&mut self, _ctx: &mut Ctx, _id: BinaryExpressionId) -> VisitorResult { Ok(()) }
 
@@ -130,12 +226,13 @@ pub(crate) trait Visitor2 {
 
    fn visit_array_expr(&mut self, _ctx: &mut Ctx, _id: ArrayExpressionId) -> VisitorResult { Ok(()) }
 
    fn visit_constant_expr(&mut self, _ctx: &mut Ctx, _id: ConstantExpressionId) -> VisitorResult { Ok(()) }
 
    fn visit_call_expr(&mut self, _ctx: &mut Ctx, _id: CallExpressionId) -> VisitorResult { Ok(()) }
 
    fn visit_variable_expr(&mut self, _ctx: &mut Ctx, _id: VariableExpressionId) -> VisitorResult { Ok(()) }
 
}
 

	
 
#[derive(PartialEq, Eq)]
 
enum DefinitionType {
 
    Primitive,
 
    Composite,
 
    Function
 
}
 

	
 
@@ -156,13 +253,13 @@ enum DefinitionType {
 
/// (e.g. the insertion of an `EndIf` statement for a particular `If`
 
/// statement). These will be inserted after visiting every node, after which
 
/// the visitor recurses into each statement in a block.
 
///
 
/// Because of this scheme expressions will not be visited in the breadth-first
 
/// pass.
 
struct ValidityAndLinkerVisitor {
 
pub(crate) struct ValidityAndLinkerVisitor {
 
    /// `in_sync` is `Some(id)` if the visitor is visiting the children of a
 
    /// synchronous statement. A single value is sufficient as nested
 
    /// synchronous statements are not allowed
 
    in_sync: Option<SynchronousStatementId>,
 
    /// `in_while` contains the last encountered `While` statement. This is used
 
    /// to resolve unlabeled `Continue`/`Break` statements.
 
@@ -183,13 +280,13 @@ struct ValidityAndLinkerVisitor {
 
    statement_buffer: Vec<StatementId>,
 
    // Statements to insert after the breadth pass in a single block
 
    insert_buffer: Vec<(u32, StatementId)>,
 
}
 

	
 
impl ValidityAndLinkerVisitor {
 
    fn new() -> Self {
 
    pub(crate) fn new() -> Self {
 
        Self{
 
            in_sync: None,
 
            in_while: None,
 
            cur_scope: None,
 
            def_type: DefinitionType::Primitive,
 
            performing_breadth_pass: false,
 
@@ -216,102 +313,80 @@ impl Visitor2 for ValidityAndLinkerVisitor {
 
    // Definition visitors
 
    //--------------------------------------------------------------------------
 

	
 
    fn visit_component_definition(&mut self, ctx: &mut Ctx, id: ComponentId) -> VisitorResult {
 
        self.reset_state();
 

	
 
        let block_id = {
 
            let def = &ctx.heap[id];
 
            match def.variant {
 
                ComponentVariant::Primitive => self.def_type = DefinitionType::Primitive,
 
                ComponentVariant::Composite => self.def_type = DefinitionType::Composite,
 
            }
 

	
 
            let body = ctx.heap[def.body].as_block_mut();
 

	
 
            self.statement_buffer.extend_from_slice(&body.statements);
 
            self.statement_stack_indices.push(0);
 
            body.this
 
        self.def_type = match &ctx.heap[id].variant {
 
            ComponentVariant::Primitive => DefinitionType::Primitive,
 
            ComponentVariant::Composite => DefinitionType::Composite,
 
        };
 

	
 
        self.cur_scope = Some(Scope {
 
            variant: ScopeVariant::Definition(id.upcast()),
 
        });
 
        self.cur_scope = Some(Scope::Definition(id.upcast()));
 
        let body_id = ctx.heap[id].body;
 

	
 
        self.performing_breadth_pass = true;
 
        self.visit_block_stmt(ctx, block_id)?;
 
        self.visit_stmt(ctx, body_id)?;
 
        self.performing_breadth_pass = false;
 
        self.visit_block_stmt(ctx, block_id)
 
        self.visit_stmt(ctx, body_id)
 
    }
 

	
 
    fn visit_function_definition(&mut self, ctx: &mut Ctx, id: FunctionId) -> VisitorResult {
 
        self.reset_state();
 

	
 
        // Set internal statement indices
 
        let block_id = {
 
            let def = &ctx.heap[id];
 
        self.def_type = DefinitionType::Function;
 
            let body = ctx.heap[def.body].as_block_mut();
 

	
 
            self.statement_buffer.extend_from_slice(&body.statements);
 
            self.statement_stack_indices.push(0);
 
            body.this
 
        };
 

	
 
        self.cur_scope = Some(Scope {
 
            variant: ScopeVariant::Definition(id.upcast()),
 
        });
 
        self.cur_scope = Some(Scope::Definition(id.upcast()));
 
        let body_id = ctx.heap[id].body;
 

	
 
        self.performing_breadth_pass = true;
 
        self.visit_block_stmt(ctx, block_id)?;
 
        self.visit_stmt(ctx, body_id)?;
 
        self.performing_breadth_pass = false;
 
        self.visit_block_stmt(ctx, block_id)
 
        self.visit_stmt(ctx, body_id)
 
    }
 

	
 
    //--------------------------------------------------------------------------
 
    // Statement visitors
 
    //--------------------------------------------------------------------------
 

	
 
    fn visit_block_stmt(&mut self, ctx: &mut Ctx, id: BlockStatementId) -> VisitorResult {
 
        self.visit_block_stmt_with_hint(ctx, id, None)
 
    }
 

	
 
    fn visit_local_memory_stmt(&mut self, ctx: &mut Ctx, id: MemoryStatementId) -> VisitorResult {
 
        if self.performing_breadth_pass {
 
            let stmt = &ctx.heap[id];
 
            stmt.relative_pos_in_block = self.relative_pos_in_block;
 
            self.checked_local_add(ctx, stmt.variable)?;
 
            let variable_id = ctx.heap[id].variable;
 
            self.checked_local_add(ctx, self.relative_pos_in_block, variable_id)?;
 
        } else {
 
            self.visit_expr(ctx, ctx.heap[id].initial)?;
 
        }
 

	
 
        Ok(())
 
    }
 

	
 
    fn visit_labeled_stmt(&mut self, ctx: &mut Ctx, id: LabeledStatementId) -> VisitorResult {
 
    fn visit_local_channel_stmt(&mut self, ctx: &mut Ctx, id: ChannelStatementId) -> VisitorResult {
 
        if self.performing_breadth_pass {
 
            // Retrieve scope
 
            let scope = self.cur_scope.as_ref().unwrap();
 
            debug_assert!(scope.statement.is_some(), "expected scope statement at labeled stmt");
 
            debug_assert_eq!(
 
                scope.variant == ScopeVariant::Synchronous,
 
                self.in_sync.is_some(),
 
                "in synchronous scope variant, but 'in_sync' not set"
 
            );
 
            let (from_id, to_id) = {
 
                let stmt = &ctx.heap[id];
 
                (stmt.from, stmt.to)
 
            };
 
            self.checked_local_add(ctx, self.relative_pos_in_block, from_id)?;
 
            self.checked_local_add(ctx, self.relative_pos_in_block, to_id)?;
 
        }
 

	
 
        Ok(())
 
    }
 

	
 
    fn visit_labeled_stmt(&mut self, ctx: &mut Ctx, id: LabeledStatementId) -> VisitorResult {
 
        if self.performing_breadth_pass {
 
            // Add label to block lookup
 
            self.checked_label_add(ctx, id)?;
 

	
 
            // Modify labeled statement itself
 
            let labeled = &mut ctx.heap[id];
 
            labeled.relative_pos_in_block = self.relative_pos_in_block;
 
            labeled.in_sync = if scope.variant == ScopeVariant::Synchronous {
 
                self.in_sync.clone()
 
            } else {
 
                None
 
            };
 
            labeled.in_sync = self.in_sync.clone();
 
        }
 

	
 
        let body_id = ctx.heap[id].body;
 
        self.visit_stmt(ctx, body_id)?;
 

	
 
        Ok(())
 
@@ -344,23 +419,17 @@ impl Visitor2 for ValidityAndLinkerVisitor {
 

	
 
        Ok(())
 
    }
 

	
 
    fn visit_while_stmt(&mut self, ctx: &mut Ctx, id: WhileStatementId) -> VisitorResult {
 
        if self.performing_breadth_pass {
 
            let scope = self.cur_scope.as_ref().unwrap();
 
            let position = ctx.heap[id].position;
 
            debug_assert_eq!(
 
                scope.variant == ScopeVariant::Synchronous,
 
                self.in_sync.is_some(),
 
                "in synchronous scope variant, but 'in_sync' not set"
 
            );
 
            let end_while_id = ctx.heap.alloc_end_while_statement(|this| {
 
                EndWhileStatement {
 
                    this,
 
                    start_while: Some(id),
 
                    start_while: id,
 
                    position,
 
                    next: None,
 
                }
 
            });
 
            let stmt = &mut ctx.heap[id];
 
            stmt.end_while = Some(end_while_id);
 
@@ -414,42 +483,43 @@ impl Visitor2 for ValidityAndLinkerVisitor {
 
        Ok(())
 
    }
 

	
 
    fn visit_synchronous_stmt(&mut self, ctx: &mut Ctx, id: SynchronousStatementId) -> VisitorResult {
 
        if self.performing_breadth_pass {
 
            // Check for validity of synchronous statement
 
            let cur_sync = &ctx.heap[id];
 
            let cur_sync_position = ctx.heap[id].position;
 
            if self.in_sync.is_some() {
 
                // Nested synchronous statement
 
                let old_sync = &ctx.heap[self.in_sync.unwrap()];
 
                return Err(
 
                    ParseError2::new_error(&ctx.module.source, cur_sync.position, "Illegal nested synchronous statement")
 
                    ParseError2::new_error(&ctx.module.source, cur_sync_position, "Illegal nested synchronous statement")
 
                        .with_postfixed_info(&ctx.module.source, old_sync.position, "It is nested in this synchronous statement")
 
                );
 
            }
 

	
 
            if self.def_type != DefinitionType::Primitive {
 
                return Err(ParseError2::new_error(
 
                    &ctx.module.source, cur_sync.position,
 
                    &ctx.module.source, cur_sync_position,
 
                    "Synchronous statements may only be used in primitive components"
 
                ));
 
            }
 

	
 
            // Append SynchronousEnd pseudo-statement
 
            let sync_end_id = ctx.heap.alloc_end_synchronous_statement(|this| EndSynchronousStatement{
 
                this,
 
                position: stmt.position,
 
                position: cur_sync_position,
 
                start_sync: id,
 
                next: None,
 
            });
 
            let sync_start = &mut ctx.heap[id];
 
            sync_start.end_sync = Some(sync_end_id);
 
            self.insert_buffer.push((self.relative_pos_in_block + 1, sync_end_id.upcast()));
 
        } else {
 
            let sync_body = ctx.heap[id].body;
 
            let old = self.in_sync.replace(id);
 
            self.visit_stmt_with_hint(ctx, stmt.body, Some(id))?;
 
            self.visit_stmt_with_hint(ctx, sync_body, Some(id))?;
 
            self.in_sync = old;
 
        }
 

	
 
        Ok(())
 
    }
 

	
 
@@ -487,33 +557,34 @@ impl Visitor2 for ValidityAndLinkerVisitor {
 
            if self.in_sync.is_none() {
 
                return Err(
 
                    ParseError2::new_error(&ctx.module.source, stmt.position, "Illegal assert statement outside of a synchronous block")
 
                );
 
            }
 
        } else {
 
            self.visit_expr(ctx, stmt.expression)?;
 
            let expr_id = stmt.expression;
 
            self.visit_expr(ctx, expr_id)?;
 
        }
 

	
 
        Ok(())
 
    }
 

	
 
    fn visit_goto_stmt(&mut self, ctx: &mut Ctx, id: GotoStatementId) -> VisitorResult {
 
        if !self.performing_breadth_pass {
 
            // Must perform goto label resolving after the breadth pass, this
 
            // way we are able to find all the labels in current and outer
 
            // scopes.
 
            let goto_stmt = &mut ctx.heap[id];
 
            let target_id = self.find_label(ctx, &goto_stmt.label)?;
 
            goto_stmt.target = Some(target_id);
 
            let target_id = self.find_label(ctx, &ctx.heap[id].label)?;
 
            ctx.heap[id].target = Some(target_id);
 

	
 
            let target = &ctx.heap[target_id];
 
            if self.in_sync != target.in_sync {
 
                // We can only goto the current scope or outer scopes. Because
 
                // nested sync statements are not allowed so if the value does
 
                // not match, then we must be inside a sync scope
 
                debug_assert!(self.in_sync.is_some());
 
                let goto_stmt = &ctx.heap[id];
 
                let sync_stmt = &ctx.heap[self.in_sync.unwrap()];
 
                return Err(
 
                    ParseError2::new_error(&ctx.module.source, goto_stmt.position, "Goto may not escape the surrounding synchronous block")
 
                        .with_postfixed_info(&ctx.module.source, target.position, "This is the target of the goto statement")
 
                        .with_postfixed_info(&ctx.module.source, sync_stmt.position, "Which will jump past this statement")
 
                );
 
@@ -524,38 +595,42 @@ impl Visitor2 for ValidityAndLinkerVisitor {
 
    }
 

	
 
    fn visit_new_stmt(&mut self, ctx: &mut Ctx, id: NewStatementId) -> VisitorResult {
 
        if self.performing_breadth_pass {
 
            // TODO: Cleanup error messages, can be done cleaner
 
            // Make sure new statement occurs within a composite component
 
            let new_stmt = &ctx.heap[id];
 
            let call_expr_id = ctx.heap[id].expression;
 
            if self.def_type != DefinitionType::Composite {
 
                let new_stmt = &ctx.heap[id];
 
                return Err(
 
                    ParseError2::new_error(&ctx.module.source, new_stmt.position, "Instantiating components may only be done in composite components")
 
                );
 
            }
 

	
 
            // No fancy recursive parsing, must be followed by a call expression
 
            let definition_id = {
 
                let call_expr = &ctx.heap[new_stmt.expression];
 
                if let Expression::Call(call_expr) = call_expr {
 
                let call_expr = &ctx.heap[call_expr_id];
 
                if let Method::Symbolic(symbolic) = &call_expr.method {
 
                    // Resolve method
 
                        let (symbol, iter) = ctx.symbols.resolve_namespaced_symbol(ctx.module.root_id, &symbolic.identifier)?;
 
                    let maybe_symbol = ctx.symbols.resolve_namespaced_symbol(ctx.module.root_id, &symbolic.identifier);
 
                    if maybe_symbol.is_none() {
 
                        return Err(ParseError2::new_error(&ctx.module.source, symbolic.identifier.position, "Unknown component"));
 
                    }
 
                    let (symbol, iter) = maybe_symbol.unwrap();
 
                    if iter.num_remaining() != 0 {
 
                        return Err(
 
                            ParseError2::new_error(&ctx.module.source, symbolic.identifier.position, "Unknown component")
 
                        )
 
                    }
 

	
 
                    match symbol.symbol {
 
                        Symbol::Namespace(_) => return Err(
 
                            ParseError2::new_error(&ctx.module.source, symbolic.identifier.position, "Unknown component")
 
                                .with_postfixed_info(&ctx.module.source, symbol.position, "The identifier points to this import")
 
                        ),
 
                            Symbol::Definition((target_root_id, target_definition_id)) => {
 
                        Symbol::Definition((_target_root_id, target_definition_id)) => {
 
                            match &ctx.heap[target_definition_id] {
 
                                Definition::Component(_) => target_definition_id,
 
                                _ => return Err(
 
                                    ParseError2::new_error(&ctx.module.source, symbolic.identifier.position, "Must instantiate a component")
 
                                )
 
                            }
 
@@ -563,22 +638,17 @@ impl Visitor2 for ValidityAndLinkerVisitor {
 
                    }
 
                } else {
 
                    return Err(
 
                        ParseError2::new_error(&ctx.module.source, call_expr.position, "Must instantiate a component")
 
                    );
 
                }
 
                } else {
 
                    return Err(
 
                        ParseError2::new_error(&ctx.module.source, call_expr.position, "Must instantiate a component")
 
                    );
 
                }
 
            };
 

	
 
            // Modify new statement's symbolic call to point to the appropriate
 
            // definition.
 
            let call_expr = &mut ctx.heap[new_stmt.expression];
 
            let call_expr = &mut ctx.heap[call_expr_id];
 
            match &mut call_expr.method {
 
                Method::Symbolic(method) => method.definition = Some(definition_id),
 
                _ => unreachable!()
 
            }
 
        }
 

	
 
@@ -594,23 +664,25 @@ impl Visitor2 for ValidityAndLinkerVisitor {
 
                return Err(ParseError2::new_error(
 
                    &ctx.module.source, put_stmt.position, "Put must be called in a synchronous block"
 
                ));
 
            }
 
        } else {
 
            let put_stmt = &ctx.heap[id];
 
            self.visit_expr(ctx, put_stmt.port)?;
 
            self.visit_expr(ctx, put_stmt.message)?;
 
            let port = put_stmt.port;
 
            let message = put_stmt.message;
 
            self.visit_expr(ctx, port)?;
 
            self.visit_expr(ctx, message)?;
 
        }
 

	
 
        Ok(())
 
    }
 

	
 
    fn visit_expr_stmt(&mut self, ctx: &mut Ctx, id: ExpressionStatementId) -> VisitorResult {
 
        if !self.performing_breadth_pass {
 
            let expr = &ctx.heap[id];
 
            self.visit_expr(ctx, expr.expression)?;
 
            let expr_id = ctx.heap[id].expression;
 
            self.visit_expr(ctx, expr_id)?;
 
        }
 

	
 
        Ok(())
 
    }
 

	
 

	
 
@@ -623,75 +695,85 @@ impl Visitor2 for ValidityAndLinkerVisitor {
 
        Ok(())
 
    }
 

	
 
    fn visit_conditional_expr(&mut self, ctx: &mut Ctx, id: ConditionalExpressionId) -> VisitorResult {
 
        debug_assert!(!self.performing_breadth_pass);
 
        let conditional_expr = &ctx.heap[id];
 
        self.visit_expr(ctx, conditional_expr.test)?;
 
        self.visit_expr(ctx, conditional_expr.true_expression)?;
 
        self.visit_expr(ctx, conditional_expr.false_expression)?;
 
        let test_expr_id = conditional_expr.test;
 
        let true_expr_id = conditional_expr.true_expression;
 
        let false_expr_id = conditional_expr.false_expression;
 
        self.visit_expr(ctx, test_expr_id)?;
 
        self.visit_expr(ctx, true_expr_id)?;
 
        self.visit_expr(ctx, false_expr_id)?;
 
        Ok(())
 
    }
 

	
 
    fn visit_binary_expr(&mut self, ctx: &mut Ctx, id: BinaryExpressionId) -> VisitorResult {
 
        debug_assert!(!self.performing_breadth_pass);
 
        let binary_expr = &ctx.heap[id];
 
        self.visit_expr(ctx, binary_expr.left)?;
 
        self.visit_expr(ctx, binary_expr.right)?;
 
        let left_expr_id = binary_expr.left;
 
        let right_expr_id = binary_expr.right;
 
        self.visit_expr(ctx, left_expr_id)?;
 
        self.visit_expr(ctx, right_expr_id)?;
 
        Ok(())
 
    }
 

	
 
    fn visit_unary_expr(&mut self, ctx: &mut Ctx, id: UnaryExpressionId) -> VisitorResult {
 
        debug_assert!(!self.performing_breadth_pass);
 
        let unary_expr = &ctx.heap[id];
 
        self.visit_expr(ctx, unary_expr.expression)?;
 
        let expr_id = ctx.heap[id].expression;
 
        self.visit_expr(ctx, expr_id)?;
 
        Ok(())
 
    }
 

	
 
    fn visit_indexing_expr(&mut self, ctx: &mut Ctx, id: IndexingExpressionId) -> VisitorResult {
 
        debug_assert!(!self.performing_breadth_pass);
 
        let indexing_expr = &ctx.heap[id];
 
        self.visit_expr(ctx, indexing_expr.subject)?;
 
        self.visit_expr(ctx, indexing_expr.index)?;
 
        let subject_expr_id = indexing_expr.subject;
 
        let index_expr_id = indexing_expr.index;
 
        self.visit_expr(ctx, subject_expr_id)?;
 
        self.visit_expr(ctx, index_expr_id)?;
 
        Ok(())
 
    }
 

	
 
    fn visit_slicing_expr(&mut self, ctx: &mut Ctx, id: SlicingExpressionId) -> VisitorResult {
 
        debug_assert!(!self.performing_breadth_pass);
 
        // TODO: Same as the select expression: slicing depends on the type of
 
        //  the thing that is being sliced.
 
        let slicing_expr = &ctx.heap[id];
 
        self.visit_expr(ctx, slicing_expr.subject)?;
 
        self.visit_expr(ctx, slicing_expr.from_index)?;
 
        self.visit_expr(ctx, slicing_expr.to_index)?;
 
        let subject_expr_id = slicing_expr.subject;
 
        let from_expr_id = slicing_expr.from_index;
 
        let to_expr_id = slicing_expr.to_index;
 
        self.visit_expr(ctx, subject_expr_id)?;
 
        self.visit_expr(ctx, from_expr_id)?;
 
        self.visit_expr(ctx, to_expr_id)?;
 
        Ok(())
 
    }
 

	
 
    fn visit_select_expr(&mut self, ctx: &mut Ctx, id: SelectExpressionId) -> VisitorResult {
 
        debug_assert!(!self.performing_breadth_pass);
 
        // TODO: Is it true that this always depends on the return value? I
 
        //  mean: the following should be a valid expression:
 
        //  int i = some_call_that_returns_a_struct(5, 2).field_of_struct
 
        //  We could rule out some things (of which we're sure what the type is)
 
        //  but it seems better to do this later
 
        let select_expr = &ctx.heap[id];
 
        self.visit_expr(ctx, select_expr.subject)?;
 
        let expr_id = ctx.heap[id].subject;
 
        self.visit_expr(ctx, expr_id)?;
 

	
 
        Ok(())
 
    }
 

	
 
    fn visit_array_expr(&mut self, ctx: &mut Ctx, id: ArrayExpressionId) -> VisitorResult {
 
        debug_assert!(!self.performing_breadth_pass);
 
        let array_expr = &ctx.heap[id];
 
        for field in &array_expr.elements {
 
            self.visit_expr(ctx, *field)?;
 
        for field_expr_id in array_expr.elements.clone() { // TODO: @performance
 
            self.visit_expr(ctx, field_expr_id)?;
 
        }
 
        Ok(())
 
    }
 

	
 
    fn visit_constant_expr(&mut self, ctx: &mut Ctx, id: ConstantExpressionId) -> VisitorResult {
 
    fn visit_constant_expr(&mut self, _ctx: &mut Ctx, _id: ConstantExpressionId) -> VisitorResult {
 
        debug_assert!(!self.performing_breadth_pass);
 
        Ok(())
 
    }
 

	
 
    fn visit_call_expr(&mut self, ctx: &mut Ctx, id: CallExpressionId) -> VisitorResult {
 
        debug_assert!(!self.performing_breadth_pass);
 
@@ -757,12 +839,13 @@ impl Visitor2 for ValidityAndLinkerVisitor {
 
    }
 

	
 
    fn visit_variable_expr(&mut self, ctx: &mut Ctx, id: VariableExpressionId) -> VisitorResult {
 
        debug_assert!(!self.performing_breadth_pass);
 

	
 
        let var_expr = &ctx.heap[id];
 
        println!("DEBUG: Finding variable {}", String::from_utf8_lossy(&var_expr.identifier.value));
 
        let variable_id = self.find_variable(ctx, self.relative_pos_in_block, &var_expr.identifier)?;
 
        let var_expr = &mut ctx.heap[id];
 
        var_expr.declaration = Some(variable_id);
 

	
 
        Ok(())
 
    }
 
@@ -771,14 +854,15 @@ impl Visitor2 for ValidityAndLinkerVisitor {
 
impl ValidityAndLinkerVisitor {
 
    //--------------------------------------------------------------------------
 
    // Special traversal
 
    //--------------------------------------------------------------------------
 

	
 
    fn visit_stmt_with_hint(&mut self, ctx: &mut Ctx, id: StatementId, hint: Option<SynchronousStatementId>) -> VisitorResult {
 
        if let Statement::Block(block) = &ctx.heap[id] {
 
            self.visit_block_stmt_with_hint(ctx, block.this, hint)
 
        if let Statement::Block(block_stmt) = &ctx.heap[id] {
 
            let block_id = block_stmt.this;
 
            self.visit_block_stmt_with_hint(ctx, block_id, hint)
 
        } else {
 
            self.visit_stmt(ctx, id)
 
        }
 
    }
 

	
 
    fn visit_block_stmt_with_hint(&mut self, ctx: &mut Ctx, id: BlockStatementId, hint: Option<SynchronousStatementId>) -> VisitorResult {
 
@@ -791,20 +875,18 @@ impl ValidityAndLinkerVisitor {
 

	
 
            return Ok(())
 
        }
 

	
 
        // We may descend into children of this block. However, this is
 
        // where we first perform a breadth-first pass
 
        // TODO: This is where crap goes wrong! If we are performing the first
 
        //  breadth pass then we should take care of the scopes properly!
 
        self.performing_breadth_pass = true;
 
        let old_scope = self.cur_scope.replace(match hint {
 
            Some(sync_id) => Scope{
 
                variant: ScopeVariant::Synchronous((sync_id, id)),
 
            },
 
            None => Scope{
 
                variant: ScopeVariant::Regular(id)
 
            }
 
            Some(sync_id) => Scope::Synchronous((sync_id, id)),
 
            None => Scope::Regular(id),
 
        });
 
        let first_statement_index = self.statement_buffer.len();
 

	
 
        {
 
            let body = &ctx.heap[id];
 
            self.statement_buffer.extend_from_slice(&body.statements);
 
@@ -845,13 +927,15 @@ impl ValidityAndLinkerVisitor {
 
    }
 

	
 
    //--------------------------------------------------------------------------
 
    // Utilities
 
    //--------------------------------------------------------------------------
 

	
 
    fn checked_local_add(&mut self, ctx: &mut Ctx, id: LocalId) -> Result<(), ParseError2> {
 
    /// Adds a local variable to the current scope. It will also annotate the
 
    /// `Local` in the AST with its relative position in the block.
 
    fn checked_local_add(&mut self, ctx: &mut Ctx, relative_pos: u32, id: LocalId) -> Result<(), ParseError2> {
 
        debug_assert!(self.cur_scope.is_some());
 

	
 
        // Make sure we do not conflict with any global symbols
 
        {
 
            let ident = &ctx.heap[id].identifier;
 
            if let Some(symbol) = ctx.symbols.resolve_symbol(ctx.module.root_id, &ident.value) {
 
@@ -859,12 +943,15 @@ impl ValidityAndLinkerVisitor {
 
                    ParseError2::new_error(&ctx.module.source, ident.position, "Local variable declaration conflicts with symbol")
 
                        .with_postfixed_info(&ctx.module.source, symbol.position, "Conflicting symbol is found here")
 
                );
 
            }
 
        }
 

	
 
        let local = &mut ctx.heap[id];
 
        local.relative_pos_in_block = relative_pos;
 

	
 
        // Make sure we do not shadow any variables in any of the scopes. Note
 
        // that variables in parent scopes may be declared later
 
        let local = &ctx.heap[id];
 
        let mut scope = self.cur_scope.as_ref().unwrap();
 
        let mut local_relative_pos = self.relative_pos_in_block;
 

	
 
@@ -873,27 +960,27 @@ impl ValidityAndLinkerVisitor {
 
            let block = &ctx.heap[scope.to_block()];
 
            for other_local_id in &block.locals {
 
                let other_local = &ctx.heap[*other_local_id];
 
                // Position check in case another variable with the same name
 
                // is defined in a higher-level scope, but later than the scope
 
                // in which the current variable resides.
 
                if local_relative_pos >= other_local.relative_pos_in_block && local.identifier.value == other_local.identifier.pos {
 
                if local_relative_pos > other_local.relative_pos_in_block && local.identifier.value == other_local.identifier.value {
 
                    // Collision within this scope
 
                    return Err(
 
                        ParseError2::new_error(&ctx.module.source, local.position, "Local variable name conflicts with another variable")
 
                            .with_postfixed_info(&ctx.module.source, other_local.position, "Previous variable is found here")
 
                    );
 
                }
 
            }
 

	
 
            // Current scope is fine, move to parent scope if any
 
            debug_assert!(scope.parent.is_some(), "block scope does not have a parent");
 
            scope = scope.parent.as_ref().unwrap();
 
            if let ScopeVariant::Definition(definition_id) = scope.variant {
 
            debug_assert!(block.parent_scope.is_some(), "block scope does not have a parent");
 
            scope = block.parent_scope.as_ref().unwrap();
 
            if let Scope::Definition(definition_id) = scope {
 
                // At outer scope, check parameters of function/component
 
                for parameter_id in ctx.heap[definition_id].parameters() {
 
                for parameter_id in ctx.heap[*definition_id].parameters() {
 
                    let parameter = &ctx.heap[*parameter_id];
 
                    if local.identifier.value == parameter.identifier.value {
 
                        return Err(
 
                            ParseError2::new_error(&ctx.module.source, local.position, "Local variable name conflicts with parameter")
 
                                .with_postfixed_info(&ctx.module.source, parameter.position, "Parameter definition is found here")
 
                        );
 
@@ -927,28 +1014,30 @@ impl ValidityAndLinkerVisitor {
 

	
 
        // TODO: May still refer to an alias of a global symbol using a single
 
        //  identifier in the namespace.
 
        // No need to use iterator over namespaces if here
 
        let mut scope = self.cur_scope.as_ref().unwrap();
 
        loop {
 
            println!("DEBUG: Looking at block {}...", scope.);
 
            debug_assert!(scope.is_block());
 
            let block = &ctx.heap[scope.to_block()];
 
            for local_id in &block.locals {
 
                let local = &ctx.heap[*local_id];
 
                println!("DEBUG: With local {}", String::from_utf8_lossy(&local.identifier.value));
 
                if local.relative_pos_in_block < relative_pos && local.identifier.value == identifier.value {
 
                    return Ok(local_id.upcast());
 
                }
 
            }
 

	
 
            debug_assert!(block.parent_scope.is_some());
 
            scope = block.parent_scope.as_ref().unwrap();
 
            if !scope.is_block() {
 
                // Definition scope, need to check arguments to definition
 
                match scope.variant {
 
                    ScopeVariant::Definition(definition_id) => {
 
                        let definition = &ctx.heap[definition_id];
 
                match scope {
 
                    Scope::Definition(definition_id) => {
 
                        let definition = &ctx.heap[*definition_id];
 
                        for parameter_id in definition.parameters() {
 
                            let parameter = &ctx.heap[*parameter_id];
 
                            if parameter.identifier.value == identifier.value {
 
                                return Ok(parameter_id.upcast());
 
                            }
 
                        }
 
@@ -1032,14 +1121,14 @@ impl ValidityAndLinkerVisitor {
 
                        }
 
                    }
 
                    return Ok(*label_id);
 
                }
 
            }
 

	
 
            debug_assert!(scope.parent.is_some(), "block scope does not have a parent");
 
            scope = scope.parent.as_ref().unwrap();
 
            debug_assert!(block.parent_scope.is_some(), "block scope does not have a parent");
 
            scope = block.parent_scope.as_ref().unwrap();
 
            if !scope.is_block() {
 
                return Err(ParseError2::new_error(&ctx.module.source, identifier.position, "Could not find this label"));
 
            }
 

	
 
        }
 
    }
 
@@ -1054,14 +1143,15 @@ impl ValidityAndLinkerVisitor {
 
            debug_assert!(scope.is_block());
 
            let block = scope.to_block();
 
            if while_stmt.body == block.upcast() {
 
                return true;
 
            }
 

	
 
            debug_assert!(scope.parent.is_some(), "block scope does not have a parent");
 
            scope = scope.parent.as_ref().unwrap();
 
            let block = &ctx.heap[block];
 
            debug_assert!(block.parent_scope.is_some(), "block scope does not have a parent");
 
            scope = block.parent_scope.as_ref().unwrap();
 
            if !scope.is_block() {
 
                return false;
 
            }
 
        }
 
    }
 

	
 
@@ -1075,13 +1165,13 @@ impl ValidityAndLinkerVisitor {
 
        let target = match label {
 
            Some(label) => {
 
                let target_id = self.find_label(ctx, label)?;
 

	
 
                // Make sure break target is a while statement
 
                let target = &ctx.heap[target_id];
 
                if let Statement::While(target_stmt) = &target.body {
 
                if let Statement::While(target_stmt) = &ctx.heap[target.body] {
 
                    // Even though we have a target while statement, the break might not be
 
                    // present underneath this particular labeled while statement
 
                    if !self.has_parent_while_scope(ctx, target_stmt.this) {
 
                        ParseError2::new_error(&ctx.module.source, label.position, "Break statement is not nested under the target label's while statement")
 
                            .with_postfixed_info(&ctx.module.source, target.position, "The targeted label is found here");
 
                    }
src/runtime/tests.rs
Show inline comments
 
@@ -35,13 +35,13 @@ fn file_logged_configured_connector(
 
    let file = File::create(path).expect("Failed to create log output file!");
 
    let file_logger = Box::new(FileLogger::new(connector_id, file));
 
    Connector::new(file_logger, pd, connector_id)
 
}
 
static MINIMAL_PDL: &'static [u8] = b"
 
primitive together(in ia, in ib, out oa, out ob){
 
  while(true) synchronous() {
 
  while(true) synchronous {
 
    if(fires(ia)) {
 
      put(oa, get(ia));
 
      put(ob, get(ib));
 
    }
 
  } 
 
}
 
@@ -1288,13 +1288,13 @@ fn for_msg_byte() {
 
    let pdl = b"
 
    primitive for_msg_byte(out o) {
 
        byte i = 0;
 
        while(i<8) {
 
            msg m = create(1);
 
            m[0] = i;
 
            synchronous() put(o, m);
 
            synchronous put(o, m);
 
            i++;
 
        }
 
    }
 
    ";
 
    let pd = reowolf::ProtocolDescription::parse(pdl).unwrap();
 
    let mut c = file_logged_configured_connector(0, test_log_path, Arc::new(pd));
0 comments (0 inline, 0 general)