Changeset - 6810fd00a570
[Not reviewed]
1 6 3
MH - 4 years ago 2021-05-07 22:49:04
contact@maxhenger.nl
initial (unused) ast executor
9 files changed with 1307 insertions and 209 deletions:
0 comments (0 inline, 0 general)
src/protocol/ast.rs
Show inline comments
 
@@ -115,10 +115,7 @@ define_aliased_ast_id!(RootId, Id<Root>, index(Root, protocol_descriptions), all
 
define_aliased_ast_id!(PragmaId, Id<Pragma>, index(Pragma, pragmas), alloc(alloc_pragma));
 
define_aliased_ast_id!(ImportId, Id<Import>, index(Import, imports), alloc(alloc_import));
 
define_aliased_ast_id!(ParserTypeId, Id<ParserType>, index(ParserType, parser_types), alloc(alloc_parser_type));
 

	
 
define_aliased_ast_id!(VariableId, Id<Variable>, index(Variable, variables));
 
define_new_ast_id!(ParameterId, VariableId, index(Parameter, Variable::Parameter, variables), alloc(alloc_parameter));
 
define_new_ast_id!(LocalId, VariableId, index(Local, Variable::Local, variables), alloc(alloc_local));
 
define_aliased_ast_id!(VariableId, Id<Variable>, index(Variable, variables), alloc(alloc_variable));
 

	
 
define_aliased_ast_id!(DefinitionId, Id<Definition>, index(Definition, definitions));
 
define_new_ast_id!(StructDefinitionId, DefinitionId, index(StructDefinition, Definition::Struct, definitions), alloc(alloc_struct_definition));
 
@@ -661,63 +658,21 @@ impl Scope {
 
}
 

	
 
#[derive(Debug, Clone)]
 
pub enum Variable {
 
    Parameter(Parameter),
 
    Local(Local),
 
pub enum VariableKind {
 
    Parameter,
 
    Local,
 
}
 

	
 
impl Variable {
 
    pub fn identifier(&self) -> &Identifier {
 
        match self {
 
            Variable::Parameter(var) => &var.identifier,
 
            Variable::Local(var) => &var.identifier,
 
        }
 
    }
 
    pub fn is_parameter(&self) -> bool {
 
        match self {
 
            Variable::Parameter(_) => true,
 
            _ => false,
 
        }
 
    }
 
    pub fn as_parameter(&self) -> &Parameter {
 
        match self {
 
            Variable::Parameter(result) => result,
 
            _ => panic!("Unable to cast `Variable` to `Parameter`"),
 
        }
 
    }
 
    pub fn as_local(&self) -> &Local {
 
        match self {
 
            Variable::Local(result) => result,
 
            _ => panic!("Unable to cast `Variable` to `Local`"),
 
        }
 
    }
 
    pub fn as_local_mut(&mut self) -> &mut Local {
 
        match self {
 
            Variable::Local(result) => result,
 
            _ => panic!("Unable to cast 'Variable' to 'Local'"),
 
        }
 
    }
 
}
 

	
 
/// TODO: Remove distinction between parameter/local and add an enum to indicate
 
///     the distinction between the two
 
#[derive(Debug, Clone)]
 
pub struct Parameter {
 
    pub this: ParameterId,
 
    // Phase 2: parser
 
    pub span: InputSpan,
 
pub struct Variable {
 
    pub this: VariableId,
 
    // Parsing
 
    pub kind: VariableKind,
 
    pub parser_type: ParserType,
 
    pub identifier: Identifier,
 
}
 

	
 
#[derive(Debug, Clone)]
 
pub struct Local {
 
    pub this: LocalId,
 
    // Phase 1: parser
 
    pub identifier: Identifier,
 
    pub parser_type: ParserType,
 
    // Phase 2: linker
 
    // Validator/linker
 
    pub relative_pos_in_block: u32,
 
    pub unique_id_in_scope: i32, // Temporary fix until proper bytecode/asm is generated
 
}
 

	
 
#[derive(Debug, Clone)]
 
@@ -820,6 +775,13 @@ impl Definition {
 
            _ => 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,
 
@@ -838,20 +800,6 @@ impl Definition {
 
            Definition::Function(def) => &def.identifier,
 
        }
 
    }
 
    pub fn parameters(&self) -> &Vec<ParameterId> {
 
        match self {
 
            Definition::Component(com) => &com.parameters,
 
            Definition::Function(fun) => &fun.parameters,
 
            _ => panic!("cannot retrieve parameters for {:?}", self),
 
        }
 
    }
 
    pub fn body(&self) -> BlockStatementId {
 
        match self {
 
            Definition::Component(com) => com.body,
 
            Definition::Function(fun) => fun.body,
 
            _ => panic!("cannot retrieve body for {:?}", self),
 
        }
 
    }
 
    pub fn poly_vars(&self) -> &Vec<Identifier> {
 
        match self {
 
            Definition::Struct(def) => &def.poly_vars,
 
@@ -974,7 +922,7 @@ pub struct ComponentDefinition {
 
    pub identifier: Identifier,
 
    pub poly_vars: Vec<Identifier>,
 
    // Phase 2: parsing
 
    pub parameters: Vec<ParameterId>,
 
    pub parameters: Vec<VariableId>,
 
    pub body: BlockStatementId,
 
}
 

	
 
@@ -1004,7 +952,7 @@ pub struct FunctionDefinition {
 
    pub poly_vars: Vec<Identifier>,
 
    // Phase 2: parsing
 
    pub return_types: Vec<ParserType>,
 
    pub parameters: Vec<ParameterId>,
 
    pub parameters: Vec<VariableId>,
 
    pub body: BlockStatementId,
 
}
 

	
 
@@ -1221,14 +1169,14 @@ impl Statement {
 
        match self {
 
            Statement::Block(_) => todo!(),
 
            Statement::Local(stmt) => match stmt {
 
                LocalStatement::Channel(stmt) => stmt.next = Some(next),
 
                LocalStatement::Memory(stmt) => stmt.next = Some(next),
 
                LocalStatement::Channel(stmt) => stmt.next = next,
 
                LocalStatement::Memory(stmt) => stmt.next = next,
 
            },
 
            Statement::EndIf(stmt) => stmt.next = Some(next),
 
            Statement::EndWhile(stmt) => stmt.next = Some(next),
 
            Statement::EndSynchronous(stmt) => stmt.next = Some(next),
 
            Statement::New(stmt) => stmt.next = Some(next),
 
            Statement::Expression(stmt) => stmt.next = Some(next),
 
            Statement::EndIf(stmt) => stmt.next = next,
 
            Statement::EndWhile(stmt) => stmt.next = next,
 
            Statement::EndSynchronous(stmt) => stmt.next = next,
 
            Statement::New(stmt) => stmt.next = next,
 
            Statement::Expression(stmt) => stmt.next = next,
 
            Statement::Return(_)
 
            | Statement::Break(_)
 
            | Statement::Continue(_)
 
@@ -1249,7 +1197,9 @@ pub struct BlockStatement {
 
    pub span: InputSpan, // of the complete block
 
    pub statements: Vec<StatementId>,
 
    // Phase 2: linker
 
    pub parent_scope: Option<Scope>,
 
    pub parent_scope: Scope,
 
    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 relative_pos_in_parent: u32,
 
    pub locals: Vec<LocalId>,
 
    pub labels: Vec<LabeledStatementId>,
 
@@ -1315,12 +1265,6 @@ impl LocalStatement {
 
            LocalStatement::Memory(v) => v.span,
 
        }
 
    }
 
    pub fn next(&self) -> Option<StatementId> {
 
        match self {
 
            LocalStatement::Memory(stmt) => stmt.next,
 
            LocalStatement::Channel(stmt) => stmt.next,
 
        }
 
    }
 
}
 

	
 
#[derive(Debug, Clone)]
 
@@ -1328,9 +1272,9 @@ pub struct MemoryStatement {
 
    pub this: MemoryStatementId,
 
    // Phase 1: parser
 
    pub span: InputSpan,
 
    pub variable: LocalId,
 
    pub variable: VariableId,
 
    // Phase 2: linker
 
    pub next: Option<StatementId>,
 
    pub next: StatementId,
 
}
 

	
 
/// ChannelStatement is the declaration of an input and output port associated
 
@@ -1343,11 +1287,11 @@ pub struct ChannelStatement {
 
    pub this: ChannelStatementId,
 
    // Phase 1: parser
 
    pub span: InputSpan, // of the "channel" keyword
 
    pub from: LocalId, // output
 
    pub to: LocalId,   // input
 
    pub from: VariableId, // output
 
    pub to: VariableId,   // input
 
    // Phase 2: linker
 
    pub relative_pos_in_block: u32,
 
    pub next: Option<StatementId>,
 
    pub next: StatementId,
 
}
 

	
 
#[derive(Debug, Clone)]
 
@@ -1378,7 +1322,7 @@ pub struct EndIfStatement {
 
    pub this: EndIfStatementId,
 
    pub start_if: IfStatementId,
 
    // Phase 2: linker
 
    pub next: Option<StatementId>,
 
    pub next: StatementId,
 
}
 

	
 
#[derive(Debug, Clone)]
 
@@ -1398,7 +1342,7 @@ pub struct EndWhileStatement {
 
    pub this: EndWhileStatementId,
 
    pub start_while: WhileStatementId,
 
    // Phase 2: linker
 
    pub next: Option<StatementId>,
 
    pub next: StatementId,
 
}
 

	
 
#[derive(Debug, Clone)]
 
@@ -1437,7 +1381,7 @@ pub struct EndSynchronousStatement {
 
    pub this: EndSynchronousStatementId,
 
    pub start_sync: SynchronousStatementId,
 
    // Phase 2: linker
 
    pub next: Option<StatementId>,
 
    pub next: StatementId,
 
}
 

	
 
#[derive(Debug, Clone)]
 
@@ -1465,7 +1409,7 @@ pub struct NewStatement {
 
    pub span: InputSpan, // of the "new" keyword
 
    pub expression: CallExpressionId,
 
    // Phase 2: linker
 
    pub next: Option<StatementId>,
 
    pub next: StatementId,
 
}
 

	
 
#[derive(Debug, Clone)]
 
@@ -1475,7 +1419,7 @@ pub struct ExpressionStatement {
 
    pub span: InputSpan,
 
    pub expression: ExpressionId,
 
    // Phase 2: linker
 
    pub next: Option<StatementId>,
 
    pub next: StatementId,
 
}
 

	
 
#[derive(Debug, PartialEq, Eq, Clone, Copy)]
 
@@ -1669,7 +1613,7 @@ impl Expression {
 
    }
 
}
 

	
 
#[derive(Debug, Clone)]
 
#[derive(Debug, Clone, Copy)]
 
pub enum AssignmentOperator {
 
    Set,
 
    Multiplied,
 
@@ -1687,45 +1631,45 @@ pub enum AssignmentOperator {
 
#[derive(Debug, Clone)]
 
pub struct AssignmentExpression {
 
    pub this: AssignmentExpressionId,
 
    // Phase 2: parser
 
    // Parsing
 
    pub span: InputSpan, // of the operator
 
    pub left: ExpressionId,
 
    pub operation: AssignmentOperator,
 
    pub right: ExpressionId,
 
    // Phase 3: linker
 
    // Validator/Linker
 
    pub parent: ExpressionParent,
 
    // Phase 4: type checking
 
    // Typing
 
    pub concrete_type: ConcreteType,
 
}
 

	
 
#[derive(Debug, Clone)]
 
pub struct BindingExpression {
 
    pub this: BindingExpressionId,
 
    // Phase 1: parser
 
    // Parsing
 
    pub span: InputSpan,
 
    pub left: LiteralExpressionId,
 
    pub right: ExpressionId,
 
    // Phase 2: linker
 
    // Validator/Linker
 
    pub parent: ExpressionParent,
 
    // Phase 3: type checking
 
    // Typing
 
    pub concrete_type: ConcreteType,
 
}
 

	
 
#[derive(Debug, Clone)]
 
pub struct ConditionalExpression {
 
    pub this: ConditionalExpressionId,
 
    // Phase 1: parser
 
    // Parsing
 
    pub span: InputSpan, // of question mark operator
 
    pub test: ExpressionId,
 
    pub true_expression: ExpressionId,
 
    pub false_expression: ExpressionId,
 
    // Phase 2: linker
 
    // Validator/Linking
 
    pub parent: ExpressionParent,
 
    // Phase 3: type checking
 
    // Typing
 
    pub concrete_type: ConcreteType,
 
}
 

	
 
#[derive(Debug, Clone, PartialEq, Eq)]
 
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
 
pub enum BinaryOperator {
 
    Concatenate,
 
    LogicalOr,
 
@@ -1751,19 +1695,19 @@ pub enum BinaryOperator {
 
#[derive(Debug, Clone)]
 
pub struct BinaryExpression {
 
    pub this: BinaryExpressionId,
 
    // Phase 1: parser
 
    // Parsing
 
    pub span: InputSpan, // of the operator
 
    pub left: ExpressionId,
 
    pub operation: BinaryOperator,
 
    pub right: ExpressionId,
 
    // Phase 2: linker
 
    // Validator/Linker
 
    pub parent: ExpressionParent,
 
    // Phase 3: type checking
 
    // Typing
 
    pub concrete_type: ConcreteType,
 
}
 

	
 
#[derive(Debug, Clone, PartialEq, Eq)]
 
pub enum UnaryOperation {
 
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
 
pub enum UnaryOperator {
 
    Positive,
 
    Negative,
 
    BitwiseNot,
 
@@ -1777,68 +1721,68 @@ pub enum UnaryOperation {
 
#[derive(Debug, Clone)]
 
pub struct UnaryExpression {
 
    pub this: UnaryExpressionId,
 
    // Phase 1: parser
 
    // Parsing
 
    pub span: InputSpan, // of the operator
 
    pub operation: UnaryOperation,
 
    pub operation: UnaryOperator,
 
    pub expression: ExpressionId,
 
    // Phase 2: linker
 
    // Validator/Linker
 
    pub parent: ExpressionParent,
 
    // Phase 3: type checking
 
    // Typing
 
    pub concrete_type: ConcreteType,
 
}
 

	
 
#[derive(Debug, Clone)]
 
pub struct IndexingExpression {
 
    pub this: IndexingExpressionId,
 
    // Phase 1: parser
 
    // Parsing
 
    pub span: InputSpan,
 
    pub subject: ExpressionId,
 
    pub index: ExpressionId,
 
    // Phase 2: linker
 
    // Validator/Linker
 
    pub parent: ExpressionParent,
 
    // Phase 3: type checking
 
    // Typing
 
    pub concrete_type: ConcreteType,
 
}
 

	
 
#[derive(Debug, Clone)]
 
pub struct SlicingExpression {
 
    pub this: SlicingExpressionId,
 
    // Phase 1: parser
 
    // Parsing
 
    pub span: InputSpan, // from '[' to ']';
 
    pub subject: ExpressionId,
 
    pub from_index: ExpressionId,
 
    pub to_index: ExpressionId,
 
    // Phase 2: linker
 
    // Validator/Linker
 
    pub parent: ExpressionParent,
 
    // Phase 3: type checking
 
    // Typing
 
    pub concrete_type: ConcreteType,
 
}
 

	
 
#[derive(Debug, Clone)]
 
pub struct SelectExpression {
 
    pub this: SelectExpressionId,
 
    // Phase 1: parser
 
    // Parsing
 
    pub span: InputSpan, // of the '.'
 
    pub subject: ExpressionId,
 
    pub field: Field,
 
    // Phase 2: linker
 
    // Validator/Linker
 
    pub parent: ExpressionParent,
 
    // Phase 3: type checking
 
    // Typing
 
    pub concrete_type: ConcreteType,
 
}
 

	
 
#[derive(Debug, Clone)]
 
pub struct CallExpression {
 
    pub this: CallExpressionId,
 
    // Phase 1: parser
 
    // Parsing
 
    pub span: InputSpan,
 
    pub parser_type: ParserType, // of the function call, not the return type
 
    pub method: Method,
 
    pub arguments: Vec<ExpressionId>,
 
    pub definition: DefinitionId,
 
    // Phase 2: linker
 
    // Validator/Linker
 
    pub parent: ExpressionParent,
 
    // Phase 3: type checking
 
    // Typing
 
    pub concrete_type: ConcreteType, // of the return type
 
}
 

	
 
@@ -1864,12 +1808,12 @@ pub struct MethodSymbolic {
 
#[derive(Debug, Clone)]
 
pub struct LiteralExpression {
 
    pub this: LiteralExpressionId,
 
    // Phase 1: parser
 
    // Parsing
 
    pub span: InputSpan,
 
    pub value: Literal,
 
    // Phase 2: linker
 
    // Validator/Linker
 
    pub parent: ExpressionParent,
 
    // Phase 3: type checking
 
    // Typing
 
    pub concrete_type: ConcreteType,
 
}
 

	
 
@@ -1968,11 +1912,11 @@ pub struct LiteralUnion {
 
#[derive(Debug, Clone)]
 
pub struct VariableExpression {
 
    pub this: VariableExpressionId,
 
    // Phase 1: parser
 
    // Parsing
 
    pub identifier: Identifier,
 
    // Phase 2: linker
 
    // Validator/Linker
 
    pub declaration: Option<VariableId>,
 
    pub parent: ExpressionParent,
 
    // Phase 3: type checking
 
    // Typing
 
    pub concrete_type: ConcreteType,
 
}
 
\ No newline at end of file
src/protocol/ast_printer.rs
Show inline comments
 
@@ -14,8 +14,6 @@ const PREFIX_PRAGMA_ID: &'static str = "Prag";
 
const PREFIX_IMPORT_ID: &'static str = "Imp ";
 
const PREFIX_TYPE_ANNOT_ID: &'static str = "TyAn";
 
const PREFIX_VARIABLE_ID: &'static str = "Var ";
 
const PREFIX_PARAMETER_ID: &'static str = "Par ";
 
const PREFIX_LOCAL_ID: &'static str = "Loc ";
 
const PREFIX_DEFINITION_ID: &'static str = "Def ";
 
const PREFIX_STRUCT_ID: &'static str = "DefS";
 
const PREFIX_ENUM_ID: &'static str = "DefE";
 
@@ -409,9 +407,13 @@ impl ASTWriter {
 
            Statement::Block(stmt) => {
 
                self.kv(indent).with_id(PREFIX_BLOCK_STMT_ID, stmt.this.0.index)
 
                    .with_s_key("Block");
 
                self.kv(indent2).with_s_key("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.relative_pos_in_parent);
 

	
 
                self.kv(indent2).with_s_key("Statements");
 
                for stmt_id in &stmt.statements {
 
                    self.write_stmt(heap, *stmt_id, indent2);
 
                    self.write_stmt(heap, *stmt_id, indent3);
 
                }
 
            },
 
            Statement::Local(stmt) => {
 
@@ -421,9 +423,9 @@ impl ASTWriter {
 
                            .with_s_key("LocalChannel");
 

	
 
                        self.kv(indent2).with_s_key("From");
 
                        self.write_local(heap, stmt.from, indent3);
 
                        self.write_variable(heap, stmt.from, indent3);
 
                        self.kv(indent2).with_s_key("To");
 
                        self.write_local(heap, stmt.to, indent3);
 
                        self.write_variable(heap, stmt.to, indent3);
 
                        self.kv(indent2).with_s_key("Next")
 
                            .with_opt_disp_val(stmt.next.as_ref().map(|v| &v.index));
 
                    },
 
@@ -432,7 +434,7 @@ impl ASTWriter {
 
                            .with_s_key("LocalMemory");
 

	
 
                        self.kv(indent2).with_s_key("Variable");
 
                        self.write_local(heap, stmt.variable, indent3);
 
                        self.write_variable(heap, stmt.variable, indent3);
 
                        self.kv(indent2).with_s_key("Next")
 
                            .with_opt_disp_val(stmt.next.as_ref().map(|v| &v.index));
 
                    }
 
@@ -788,16 +790,19 @@ impl ASTWriter {
 
        }
 
    }
 

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

	
 
        self.kv(indent).with_id(PREFIX_LOCAL_ID, local_id.0.index)
 
            .with_s_key("Local");
 
        self.kv(indent).with_id(PREFIX_VARIABLE_ID, variable_id.0.index)
 
            .with_s_key("Variable");
 

	
 
        self.kv(indent2).with_s_key("Name").with_identifier_val(&local.identifier);
 
        self.kv(indent2).with_s_key("Name").with_identifier_val(&var.identifier);
 
        self.kv(indent2).with_s_key("Kind").with_debug_val(&var.kind);
 
        self.kv(indent2).with_s_key("ParserType")
 
            .with_custom_val(|w| write_parser_type(w, heap, &local.parser_type));
 
            .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("UniqueScopeID").with_disp_val(&var.unique_id_in_scope);
 
    }
 

	
 
    //--------------------------------------------------------------------------
src/protocol/eval/mod.rs
Show inline comments
 
new file 100644
 
/// eval
 
///
 
/// Evaluator of the generated AST. Note that we use some misappropriated terms
 
/// to describe where values live and what they do. This is a temporary
 
/// implementation of an evaluator until some kind of appropriate bytecode or
 
/// machine code is generated.
 
///
 
/// Code is always executed within a "frame". For Reowolf the first frame is
 
/// usually an executed component. All subsequent frames are function calls.
 
/// Simple values live on the "stack". Each variable/parameter has a place on
 
/// the stack where its values are stored. If the value is not a primitive, then
 
/// its value will be stored in the "heap". Expressions are treated differently
 
/// and use a separate "stack" for their evaluation.
 
///
 
/// Since this is a value-based language, most values are copied. One has to be
 
/// careful with values that reside in the "heap" and make sure that copies are
 
/// properly removed from the heap..
 
///
 
/// Just to reiterate: this is a temporary implementation. A proper
 
/// implementation would fully fill out the type table with alignment/size/
 
/// offset information and lay out bytecode.
 

	
 
mod value;
 

	
 
use std::collections::VecDeque;
 

	
 
use super::value::*;
 
use crate::PortId;
 
use crate::protocol::ast::*;
 
use crate::protocol::*;
 

	
 
struct HeapAllocation {
 
    values: Vec<Value>,
 
}
 

	
 
struct Store {
 
    // The stack where variables/parameters are stored. Note that this is a
 
    // non-shrinking stack. So it may be filled with garbage.
 
    stack: Vec<Value>,
 
    // Represents the place in the stack where we find the `PrevStackBoundary`
 
    // value containing the previous stack boundary. This is so we can pop from
 
    // the stack after function calls.
 
    cur_stack_boundary: usize,
 
    // A rather ridiculous simulated heap, but this allows us to "allocate"
 
    // things that occupy more then one stack slot.
 
    heap_regions: Vec<HeapAllocation>,
 
    free_regions: VecDeque<HeapPos>,
 
}
 

	
 
impl Store {
 
    fn new() -> Self {
 
        let mut store = Self{
 
            stack: Vec::with_capacity(64),
 
            cur_stack_boundary: 0,
 
            heap_regions: Vec::new(),
 
            free_regions: VecDeque::new(),
 
        };
 

	
 
        store.stack.push(Value::PrevStackBoundary(-1));
 
        store
 
    }
 

	
 
    /// Resizes(!) the stack to fit the required number of values. Any
 
    /// unallocated slots are initialized to `Unassigned`. The specified stack
 
    /// index is exclusive.
 
    fn reserve_stack(&mut self, unique_stack_idx: usize) {
 
        let new_size = self.cur_stack_boundary + unique_stack_idx + 1;
 
        if new_size > self.stack.len() {
 
            self.stack.resize(new_size, Value::Unassigned);
 
        }
 
    }
 

	
 
    /// Clears values on the stack and removes their heap allocations when
 
    /// applicable. The specified index itself will also be cleared (so if you
 
    /// specify 0 all values in the frame will be destroyed)
 
    fn clear_stack(&mut self, unique_stack_idx: usize) {
 
        let new_size = self.cur_stack_boundary + unique_stack_idx + 1;
 
        for idx in new_size..self.stack.len() {
 
            self.drop_value(&self.stack[idx]);
 
            self.stack[idx] = Value::Unassigned;
 
        }
 
    }
 

	
 
    /// Reads a value from a specific address. The value is always copied, hence
 
    /// if the value ends up not being written, one should call `drop_value` on
 
    /// it.
 
    fn read_copy(&mut self, address: ValueId) -> Value {
 
        match value {
 
            ValueId::Stack(pos) => {
 
                let cur_pos = self.cur_stack_boundary + 1 + pos as usize;
 
                return self.clone_value(&self.stack[cur_pos]);
 
            },
 
            ValueId::Heap(heap_pos, region_idx) => {
 
                return self.clone_value(&self.heap_regions[heap_pos as usize].values[region_idx as usize])
 
            }
 
        }
 
    }
 

	
 
    /// Returns an immutable reference to the value pointed to by an address
 
    fn read_ref(&mut self, address: ValueId) -> &Value {
 
        match value {
 
            ValueId::Stack(pos) => {
 
                let cur_pos = self.cur_stack_boundary + 1 + pos as usize;
 
                return &self.stack[cur_pos];
 
            },
 
            ValueId::Heap(heap_pos, region_idx) => {
 
                return &self.heap_regions[heap_pos as usize].values[region_idx as usize];
 
            }
 
        }
 
    }
 

	
 
    /// Returns a mutable reference to the value pointed to by an address
 
    fn read_mut_ref(&mut self, address: ValueId) -> &mut Value {
 
        match value {
 
            ValueId::Stack(pos) => {
 
                let cur_pos = self.cur_stack_boundary + 1 + pos as usize;
 
                return &mut self.stack[cur_pos];
 
            },
 
            ValueId::Heap(heap_pos, region_idx) => {
 
                return &mut self.heap_regions[heap_pos as usize].values[region_idx as usize];
 
            }
 
        }
 
    }
 

	
 
    /// Writes a value
 
    fn write(&mut self, address: ValueId, value: Value) {
 
        match address {
 
            ValueId::Stack(pos) => {
 
                let cur_pos = self.cur_stack_boundary + 1 + pos as usize;
 
                self.drop_value(&self.stack[cur_pos]);
 
                self.stack[cur_pos] = value;
 
            },
 
            ValueId::Heap(heap_pos, region_idx) => {
 
                let heap_pos = heap_pos as usize;
 
                let region_idx = region_idx as usize;
 
                self.drop_value(&self.heap_regions[heap_pos].values[region_idx]);
 
                self.heap_regions[heap_pos].values[region_idx] = value
 
            }
 
        }
 
    }
 

	
 
    fn clone_value(&mut self, value: &Value) -> Value {
 
        // Quickly check if the value is not on the heap
 
        let source_heap_pos = value.get_heap_pos();
 
        if source_heap_pos.is_none() {
 
            // We can do a trivial copy
 
            return value.clone();
 
        }
 

	
 
        // Value does live on heap, copy it
 
        let source_heap_pos = source_heap_pos.unwrap() as usize;
 
        let target_heap_pos = self.alloc_heap();
 
        let target_heap_pos_usize = target_heap_pos as usize;
 

	
 
        let num_values = self.heap_regions[source_heap_pos].values.len();
 
        for value_idx in 0..num_values {
 
            let cloned = self.clone_value(&self.heap_regions[source_heap_pos].values[value_idx]);
 
            self.heap_regions[target_heap_pos_usize].values.push(cloned);
 
        }
 

	
 
        match value {
 
            Value::Message(_) => Value::Message(target_heap_pos),
 
            Value::Array(_) => Value::Array(target_heap_pos),
 
            Value::Union(tag, _) => Value::Union(*tag, target_heap_pos),
 
            Value::Struct(_) => Value::Struct(target_heap_pos),
 
            _ => unreachable!("performed clone_value on heap, but {:?} is not a heap value", value),
 
        }
 
    }
 

	
 
    fn drop_value(&mut self, value: &Value) {
 
        if let Some(heap_pos) = value.get_heap_pos() {
 
            self.drop_heap_pos(heap_pos);
 
        }
 
    }
 

	
 
    fn drop_heap_pos(&mut self, heap_pos: HeapPos) {
 
        let num_values = self.heap_regions[heap_pos as usize].values.len();
 
        for value_idx in 0..num_values {
 
            if let Some(other_heap_pos) = self.heap_regions[heap_pos as usize].values[value_idx].get_heap_pos() {
 
                self.drop_heap_pos(other_heap_pos);
 
            }
 
        }
 

	
 
        self.heap_regions[heap_pos as usize].values.clear();
 
        self.free_regions.push_back(heap_pos);
 
    }
 

	
 
    fn alloc_heap(&mut self) -> HeapPos {
 
        if self.free_regions.is_empty() {
 
            let idx = self.heap_regions.len() as HeapPos;
 
            self.heap_regions.push(HeapAllocation{ values: Vec::new() });
 
            return idx;
 
        } else {
 
            let idx = self.free_regions.pop_back().unwrap();
 
            return idx;
 
        }
 
    }
 
}
 

	
 
enum ExprInstruction {
 
    EvalExpr(ExpressionId),
 
    PushValToFront,
 
}
 

	
 
struct Frame {
 
    definition: DefinitionId,
 
    position: StatementId,
 
    expr_stack: VecDeque<ExprInstruction>, // hack for expression evaluation, evaluated by popping from back
 
    expr_values: VecDeque<Value>, // hack for expression results, evaluated by popping from front/back
 
}
 

	
 
impl Frame {
 
    /// Creates a new execution frame. Does not modify the stack in any way.
 
    pub fn new(heap: &Heap, definition_id: DefinitionId) -> 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),
 
        };
 

	
 
        Frame{
 
            definition: definition_id,
 
            position: first_statement.upcast(),
 
            expr_stack: VecDeque::with_capacity(128),
 
            expr_values: VecDeque::with_capacity(128),
 
        }
 
    }
 

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

	
 
        self.serialize_expression(heap, expr_id);
 
    }
 

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

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

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

	
 
        match &heap[id] {
 
            Expression::Assignment(expr) => {
 
                self.serialize_expression(heap, expr.left);
 
                self.serialize_expression(heap, expr.right);
 
            },
 
            Expression::Binding(expr) => {
 
                todo!("implement binding expression");
 
            },
 
            Expression::Conditional(expr) => {
 
                self.serialize_expression(heap, expr.test);
 
            },
 
            Expression::Binary(expr) => {
 
                self.serialize_expression(heap, expr.left);
 
                self.serialize_expression(heap, expr.right);
 
            },
 
            Expression::Unary(expr) => {
 
                self.serialize_expression(heap, expr.expression);
 
            },
 
            Expression::Indexing(expr) => {
 
                self.serialize_expression(heap, expr.index);
 
                self.serialize_expression(heap, expr.subject);
 
            },
 
            Expression::Slicing(expr) => {
 
                self.serialize_expression(heap, expr.from_index);
 
                self.serialize_expression(heap, expr.to_index);
 
                self.serialize_expression(heap, expr.subject);
 
            },
 
            Expression::Select(expr) => {
 
                self.serialize_expression(heap, expr.subject);
 
            },
 
            Expression::Literal(expr) => {
 
                // Here we only care about literals that have subexpressions
 
                match &expr.value {
 
                    Literal::Null | Literal::True | Literal::False |
 
                    Literal::Character(_) | Literal::String(_) |
 
                    Literal::Integer(_) | Literal::Enum(_) => {
 
                        // No subexpressions
 
                    },
 
                    Literal::Struct(literal) => {
 
                        for field in &literal.fields {
 
                            self.expr_stack.push_back(ExprInstruction::PushValToFront);
 
                            self.serialize_expression(heap, field.value);
 
                        }
 
                    },
 
                    Literal::Union(literal) => {
 
                        for value_expr_id in &literal.values {
 
                            self.expr_stack.push_back(ExprInstruction::PushValToFront);
 
                            self.serialize_expression(heap, *value_expr_id);
 
                        }
 
                    },
 
                    Literal::Array(value_expr_ids) => {
 
                        for value_expr_id in value_expr_ids {
 
                            self.expr_stack.push_back(ExprInstruction::PushValToFront);
 
                            self.serialize_expression(heap, *value_expr_id);
 
                        }
 
                    }
 
                }
 
            },
 
            Expression::Call(expr) => {
 
                for arg_expr_id in &expr.arguments {
 
                    self.expr_stack.push_back(ExprInstruction::PushValToFront);
 
                    self.serialize_expression(heap, *arg_expr_id);
 
                }
 
            },
 
            Expression::Variable(expr) => {
 
                // No subexpressions
 
            }
 
        }
 
    }
 
}
 

	
 
type EvalResult = Result<(), EvalContinuation>;
 
pub enum EvalContinuation {
 
    Stepping,
 
    Inconsistent,
 
    Terminal,
 
    SyncBlockStart,
 
    SyncBlockEnd,
 
    NewComponent(DefinitionId, Vec<Value>),
 
    BlockFires(Value),
 
    BlockGet(Value),
 
    Put(Value, Value),
 
}
 

	
 
pub struct Prompt {
 
    frames: Vec<Frame>,
 
    store: Store,
 
}
 

	
 
impl Prompt {
 
    pub fn new(heap: &Heap, def: DefinitionId) -> Self {
 
        let mut prompt = Self{
 
            frames: Vec::new(),
 
            store: Store::new(),
 
        };
 

	
 
        prompt.frames.push(Frame::new(heap, def));
 
        prompt
 
    }
 

	
 
    pub fn step(&mut self, heap: &Heap, ctx: &mut EvalContext) -> EvalResult {
 
        let cur_frame = self.frames.last_mut().unwrap();
 
        if cur_frame.position.is_invalid() {
 
            if heap[cur_frame.definition].is_function() {
 
                todo!("End of function without return, return an evaluation error");
 
            }
 
            return Err(EvalContinuation::Terminal);
 
        }
 

	
 
        while !cur_frame.expr_stack.is_empty() {
 
            let next = cur_frame.expr_stack.pop_back().unwrap();
 
            match next {
 
                ExprInstruction::PushValToFront => {
 
                    cur_frame.expr_values.rotate_right(1);
 
                },
 
                ExprInstruction::EvalExpr(expr_id) => {
 
                    let expr = &heap[expr_id];
 
                    match expr {
 
                        Expression::Assignment(expr) => {
 
                            let to = cur_frame.expr_values.pop_back().unwrap().as_ref();
 
                            let rhs = cur_frame.expr_values.pop_back().unwrap();
 
                            apply_assignment_operator(&mut self.store, to, expr.operation, rhs);
 
                            cur_frame.expr_values.push_back(self.store.read_copy(to));
 
                            self.store.drop_value(&rhs);
 
                        },
 
                        Expression::Binding(_expr) => {
 
                            todo!("Binding expression");
 
                        },
 
                        Expression::Conditional(expr) => {
 
                            // Evaluate testing expression, then extend the
 
                            // expression stack with the appropriate expression
 
                            let test_result = cur_frame.expr_values.pop_back().unwrap().as_bool();
 
                            if test_result {
 
                                cur_frame.serialize_expression(heap, expr.true_expression);
 
                            } else {
 
                                cur_frame.serialize_expression(heap, expr.false_expression);
 
                            }
 
                        },
 
                        Expression::Binary(expr) => {
 
                            let lhs = cur_frame.expr_values.pop_back().unwrap();
 
                            let rhs = cur_frame.expr_values.pop_back().unwrap();
 
                            let result = apply_binary_operator(&mut self.store, &lhs, expr.operation, &rhs);
 
                            cur_frame.expr_values.push_back(result);
 
                            self.store.drop_value(&lhs);
 
                            self.store.drop_value(&rhs);
 
                        },
 
                        Expression::Unary(expr) => {
 
                            let val = cur_frame.expr_values.pop_back().unwrap();
 
                            let result = apply_unary_operator(&mut self.store, expr.operation, &val);
 
                            cur_frame.expr_values.push_back(result);
 
                            self.store.drop_value(&val);
 
                        },
 
                        Expression::Indexing(expr) => {
 
                            // TODO: Out of bounds checking
 
                            // Evaluate index. Never heap allocated so we do
 
                            // not have to drop it.
 
                            let index = cur_frame.expr_values.pop_back().unwrap();
 
                            let index = match &index {
 
                                Value::Ref(value_ref) => self.store.read_ref(*value_ref),
 
                                index => index,
 
                            };
 
                            debug_assert!(deref.is_integer());
 
                            let index = if index.is_signed_integer() {
 
                                index.as_signed_integer() as u32
 
                            } else {
 
                                index.as_unsigned_integer() as u32
 
                            };
 

	
 
                            let subject = cur_frame.expr_values.pop_back().unwrap();
 
                            let heap_pos = match val {
 
                                Value::Ref(value_ref) => self.store.read_ref(value_ref).as_array(),
 
                                val => val.as_array(),
 
                            };
 

	
 
                            cur_frame.expr_values.push_back(Value::Ref(ValueId::Heap(heap_pos, index)));
 
                            self.store.drop_value(&subject);
 
                        },
 
                        Expression::Slicing(expr) => {
 
                            // TODO: Out of bounds checking
 
                            todo!("implement slicing")
 
                        },
 
                        Expression::Select(expr) => {
 
                            let subject= cur_frame.expr_values.pop_back().unwrap();
 
                            let heap_pos = match &subject {
 
                                Value::Ref(value_ref) => self.store.read_ref(*value_ref).as_struct(),
 
                                subject => subject.as_struct(),
 
                            };
 

	
 
                            cur_frame.expr_values.push_back(Value::Ref(ValueId::Heap(heap_pos, expr.field.as_symbolic().field_idx as u32)));
 
                            self.store.drop_value(&subject);
 
                        },
 
                        Expression::Literal(expr) => {
 
                            let value = match &expr.value {
 
                                Literal::Null => Value::Null,
 
                                Literal::True => Value::Bool(true),
 
                                Literal::False => Value::Bool(false),
 
                                Literal::Character(lit_value) => Value::Char(*lit_value),
 
                                Literal::String(lit_value) => {
 
                                    let heap_pos = self.store.alloc_heap();
 
                                    let values = &mut self.store.heap_regions[heap_pos as usize].values;
 
                                    let value = lit_value.as_str();
 
                                    debug_assert!(values.is_empty());
 
                                    values.reserve(value.len());
 
                                    for character in value.as_bytes() {
 
                                        debug_assert!(character.is_ascii());
 
                                        values.push(Value::Char(*character as char));
 
                                    }
 
                                    Value::String(heap_pos)
 
                                }
 
                                Literal::Integer(lit_value) => {
 
                                    use ConcreteTypePart as CTP;
 
                                    debug_assert_eq!(expr.concrete_type.parts.len(), 1);
 
                                    match expr.concrete_type.parts[0] {
 
                                        CTP::UInt8  => Value::UInt8(lit_value.unsigned_value as u8),
 
                                        CTP::UInt16 => Value::UInt16(lit_value.unsigned_value as u16),
 
                                        CTP::UInt32 => Value::UInt32(lit_value.unsigned_value as u32),
 
                                        CTP::UInt64 => Value::UInt64(lit_value.unsigned_value as u64),
 
                                        CTP::SInt8  => Value::SInt8(lit_value.unsigned_value as i8),
 
                                        CTP::SInt16 => Value::SInt16(lit_value.unsigned_value as i16),
 
                                        CTP::SInt32 => Value::SInt32(lit_value.unsigned_value as i32),
 
                                        CTP::SInt64 => Value::SInt64(lit_value.unsigned_value as i64),
 
                                        _ => unreachable!(),
 
                                    }
 
                                }
 
                                Literal::Struct(lit_value) => {
 
                                    let heap_pos = self.store.alloc_heap();
 
                                    let num_fields = lit_value.fields.len();
 
                                    let values = &mut self.store.heap_regions[heap_pos as usize].values;
 
                                    debug_assert!(values.is_empty());
 
                                    values.reserve(num_fields);
 
                                    for _ in 0..num_fields {
 
                                        values.push(cur_frame.expr_values.pop_front().unwrap());
 
                                    }
 
                                    Value::Struct(heap_pos)
 
                                }
 
                                Literal::Enum(lit_value) => {
 
                                    Value::Enum(lit_value.variant_idx as i64);
 
                                }
 
                                Literal::Union(lit_value) => {
 
                                    let heap_pos = self.store.alloc_heap();
 
                                    let num_values = lit_value.values.len();
 
                                    let values = &mut self.store.heap_regions[heap_pos as usize].values;
 
                                    debug_assert!(values.is_empty());
 
                                    values.reserve(num_values);
 
                                    for _ in 0..num_values {
 
                                        values.push(cur_frame.expr_values.pop_front().unwrap());
 
                                    }
 
                                    Value::Union(lit_value.variant_idx as i64, heap_pos)
 
                                }
 
                                Literal::Array(lit_value) => {
 
                                    let heap_pos = self.store.alloc_heap();
 
                                    let num_values = lit_value.len();
 
                                    let values = &mut self.store.heap_regions[heap_pos as usize].values;
 
                                    debug_assert!(values.is_empty());
 
                                    values.reserve(num_values);
 
                                    for _ in 0..num_values {
 
                                        values.push(cur_frame.expr_values.pop_front().unwrap())
 
                                    }
 
                                }
 
                            };
 

	
 
                            cur_frame.expr_values.push_back(value);
 
                        },
 
                        Expression::Call(expr) => {
 
                            // Push a new frame. Note that all expressions have
 
                            // been pushed to the front, so they're in the order
 
                            // of the definition.
 
                            let num_args = expr.arguments.len();
 

	
 
                            // Prepare stack for a new frame
 
                            let cur_stack_boundary = self.store.cur_stack_boundary;
 
                            self.store.cur_stack_boundary = self.store.stack.len();
 
                            self.store.stack.push(Value::PrevStackBoundary(cur_stack_boundary as isize));
 
                            for _ in 0..num_args {
 
                                self.store.stack.push(cur_frame.expr_values.pop_front().unwrap());
 
                            }
 

	
 
                            // Push the new frame
 
                            self.frames.push(Frame::new(heap, expr.definition));
 

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

	
 
        // No (more) expressions to evaluate. So evaluate statement (that may
 
        // depend on the result on the last evaluated expression(s))
 
        let stmt = &heap[cur_frame.position];
 
        let return_value = match stmt {
 
            Statement::Block(stmt) => {
 
                // Reserve space on stack, but also make sure excess stack space
 
                // is cleared
 
                self.store.clear_stack(stmt.first_unique_id_in_scope as usize);
 
                self.store.reserve_stack(stmt.next_unique_id_in_scope as usize);
 
                cur_frame.position = stmt.statements[0];
 

	
 
                Ok(())
 
            },
 
            Statement::Local(stmt) => {
 
                match stmt {
 
                    LocalStatement::Memory(stmt) => {
 
                        let variable = &heap[stmt.variable];
 
                        self.store.write(ValueId::Stack(variable.unique_id_in_scope as u32), Value::Unassigned);
 

	
 
                        cur_frame.position = stmt.next;
 
                    },
 
                    LocalStatement::Channel(stmt) => {
 
                        let [from_value, to_value] = ctx.new_channel();
 
                        self.store.write(ValueId::Stack(heap[stmt.from].unique_id_in_scope as u32), from_value);
 
                        self.store.write(ValueId::Stack(heap[stmt.to].unique_id_in_scope as u32), to_value);
 

	
 
                        cur_frame.position = stmt.next;
 
                    }
 
                }
 

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

	
 
                Ok(())
 
            },
 
            Statement::If(stmt) => {
 
                debug_assert_eq!(cur_frame.expr_values.len(), 1, "expected one expr value for if statement");
 
                let test_value = cur_frame.expr_values.pop_back().unwrap().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();
 
                } else {
 
                    // Not true, and no false body
 
                    cur_frame.position = stmt.end_if.unwrap().upcast();
 
                }
 

	
 
                Ok(())
 
            },
 
            Statement::EndIf(stmt) => {
 
                cur_frame.position = stmt.next;
 
                Ok(())
 
            },
 
            Statement::While(stmt) => {
 
                debug_assert_eq!(cur_frame.expr_values.len(), 1, "expected one expr value for while statement");
 
                let test_value = cur_frame.expr_values.pop_back().unwrap().as_bool();
 
                if test_value {
 
                    cur_frame.position = stmt.body.upcast();
 
                } else {
 
                    cur_frame.position = stmt.end_while.unwrap().upcast();
 
                }
 

	
 
                Ok(())
 
            },
 
            Statement::EndWhile(stmt) => {
 
                cur_frame.position = stmt.next;
 

	
 
                Ok(())
 
            },
 
            Statement::Break(stmt) => {
 
                cur_frame.position = stmt.target.unwrap().upcast();
 

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

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

	
 
                Err(EvalContinuation::SyncBlockStart)
 
            },
 
            Statement::EndSynchronous(stmt) => {
 
                cur_frame.position = stmt.next;
 

	
 
                Err(EvalContinuation::SyncBlockEnd)
 
            },
 
            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");
 

	
 
                // Clear any values in the current stack frame
 
                self.store.clear_stack(0);
 

	
 
                // The preceding frame has executed a call, so is expecting the
 
                // return expression on its expression value stack.
 
                let return_value = cur_frame.expr_values.pop_back().unwrap();
 
                let prev_stack_idx = self.store.stack[self.store.cur_stack_boundary].as_stack_boundary();
 
                debug_assert!(prev_stack_idx >= 0);
 
                self.store.cur_stack_boundary = prev_stack_idx as usize;
 
                self.frames.pop();
 
                let cur_frame = self.frames.last_mut().unwrap();
 
                cur_frame.expr_values.push_back(return_value);
 

	
 
                // Immediately return, we don't care about the current frame
 
                // anymore and there is nothing left to evaluate
 
                return Ok(());
 
            },
 
            Statement::Goto(stmt) => {
 
                cur_frame.position = stmt.target.unwrap().upcast();
 

	
 
                Ok(())
 
            },
 
            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(),
 
                    "mismatch in expr stack size and number of arguments for new statement"
 
                );
 

	
 
                // Note that due to expression value evaluation they exist in
 
                // reverse order on the stack.
 
                // TODO: Revise this code, keep it as is to be compatible with current runtime
 
                let mut args = Vec::new();
 
                while let Some(value) = cur_frame.expr_values.pop_front() {
 
                    args.push(value);
 
                }
 

	
 
                cur_frame.position = stmt.next;
 

	
 
                todo!("Make sure this is handled correctly, transfer 'heap' values to another Prompt");
 
                Err(EvalContinuation::NewComponent(call_expr.definition, args))
 
            },
 
            Statement::Expression(stmt) => {
 
                // The expression has just been completely evaluated. Some
 
                // values might have remained on the expression value stack.
 
                cur_frame.expr_values.clear();
 
                cur_frame.position = stmt.next;
 

	
 
                Ok(())
 
            },
 
        };
 

	
 
        // If the next statement requires evaluating expressions then we push
 
        // these onto the expression stack. This way we will evaluate this
 
        // stack in the next loop, then evaluate the statement using the result
 
        // from the expression evaluation.
 
        if !cur_frame.position.is_invalid() {
 
            let stmt = &heap[cur_frame.position];
 

	
 
            match stmt {
 
                Statement::If(stmt) => cur_frame.prepare_single_expression(heap, stmt.test),
 
                Statement::While(stmt) => cur_frame.prepare_single_expression(heap, stmt.test),
 
                Statement::Return(stmt) => {
 
                    debug_assert_eq!(stmt.expressions.len(), 1); // TODO: @ReturnValues
 
                    cur_frame.prepare_single_expression(heap, stmt.expressions[0]);
 
                },
 
                Statement::New(stmt) => {
 
                    // Note that we will end up not evaluating the call itself.
 
                    // Rather we will evaluate its expressions and then
 
                    // instantiate the component upon reaching the "new" stmt.
 
                    let call_expr = &heap[stmt.expression];
 
                    cur_frame.prepare_multiple_expressions(heap, &call_expr.arguments);
 
                },
 
                Statement::Expression(stmt) => {
 
                    cur_frame.prepare_single_expression(heap, stmt.expression);
 
                }
 
                _ => {},
 
            }
 
        }
 

	
 
        return_value
 
    }
 
}
 
\ No newline at end of file
src/protocol/eval/value.rs
Show inline comments
 
new file 100644
 

	
 
use crate::PortId;
 
use crate::protocol::ast::{
 
    AssignmentOperator,
 
    BinaryOperator,
 
    UnaryOperator,
 
};
 
use crate::protocol::eval::Store;
 

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

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

	
 
#[derive(Debug, Clone)]
 
pub enum Value {
 
    // Special types, never encountered during evaluation if the compiler works correctly
 
    Unassigned,                 // Marker when variables are first declared, immediately followed by assignment
 
    PrevStackBoundary(isize),   // Marker for stack frame beginning, so we can pop stack values
 
    Ref(ValueId),               // Reference to a value, used by expressions producing references
 
    // Builtin types
 
    Input(PortId),
 
    Output(PortId),
 
    Message(HeapPos),
 
    Null,
 
    Bool(bool),
 
    Char(char),
 
    String(HeapPos),
 
    UInt8(u8),
 
    UInt16(u16),
 
    UInt32(u32),
 
    UInt64(u64),
 
    SInt8(i8),
 
    SInt16(i16),
 
    SInt32(i32),
 
    SInt64(i64),
 
    Array(HeapPos),
 
    // Instances of user-defined types
 
    Enum(i64),
 
    Union(i64, HeapPos),
 
    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_enum,    Value::Enum,    i64);
 
impl_union_unpack_as_value!(as_struct,  Value::Struct,  HeapPos);
 

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

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

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

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

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

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

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

	
 
/// Applies the assignment operator. If a heap position is returned then that
 
/// heap position should be cleared
 
pub(crate) fn apply_assignment_operator(store: &mut Store, lhs: ValueRef, op: AssignmentOperator, rhs: Value) {
 
    use AssignmentOperator as AO;
 

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

	
 
    let lhs = store.read_mut_ref(lhs);
 

	
 
    let mut to_dealloc = None;
 
    match AO {
 
        AO::Set => {
 
            match lhs {
 
                Value::Unassigned => { *lhs = rhs; },
 
                Value::Input(v)  => { *v = rhs.as_input(); },
 
                Value::Output(v) => { *v = rhs.as_output(); },
 
                Value::Message(v)  => { to_dealloc = Some(*v); *v = rhs.as_message(); },
 
                Value::Bool(v)    => { *v = rhs.as_bool(); },
 
                Value::Char(v) => { *v = rhs.as_char(); },
 
                Value::String(v) => { *v = rhs.as_string().clone(); },
 
                Value::UInt8(v) => { *v = rhs.as_uint8(); },
 
                Value::UInt16(v) => { *v = rhs.as_uint16(); },
 
                Value::UInt32(v) => { *v = rhs.as_uint32(); },
 
                Value::UInt64(v) => { *v = rhs.as_uint64(); },
 
                Value::SInt8(v) => { *v = rhs.as_sint8(); },
 
                Value::SInt16(v) => { *v = rhs.as_sint16(); },
 
                Value::SInt32(v) => { *v = rhs.as_sint32(); },
 
                Value::SInt64(v) => { *v = rhs.as_sint64(); },
 
                Value::Array(v) => { to_dealloc = Some(*v); *v = rhs.as_array(); },
 
                Value::Enum(v) => { *v = rhs.as_enum(); },
 
                Value::Union(lhs_tag, lhs_heap_pos) => {
 
                    to_dealloc = Some(*lhs_heap_pos);
 
                    let (rhs_tag, rhs_heap_pos) = rhs.as_union();
 
                    *lhs_tag = rhs_tag;
 
                    *lhs_heap_pos = rhs_heap_pos;
 
                }
 
                Value::Struct(v) => { to_dealloc = Some(*v); *v = rhs.as_struct(); },
 
                _ => unreachable!("apply_assignment_operator {:?} on lhs {:?} and rhs {:?}", op, lhs, rhs),
 
            }
 
        },
 
        AO::Multiplied =>   { apply_int_op!(lhs, *=,  op, rhs) },
 
        AO::Divided =>      { apply_int_op!(lhs, /=,  op, rhs) },
 
        AO::Remained =>     { apply_int_op!(lhs, %=,  op, rhs) },
 
        AO::Added =>        { apply_int_op!(lhs, +=,  op, rhs) },
 
        AO::Subtracted =>   { apply_int_op!(lhs, -=,  op, rhs) },
 
        AO::ShiftedLeft =>  { apply_int_op!(lhs, <<=, op, rhs) },
 
        AO::ShiftedRight => { apply_int_op!(lhs, >>=, op, rhs) },
 
        AO::BitwiseAnded => { apply_int_op!(lhs, &=,  op, rhs) },
 
        AO::BitwiseXored => { apply_int_op!(lhs, ^=,  op, rhs) },
 
        AO::BitwiseOred =>  { apply_int_op!(lhs, |=,  op, rhs) },
 
    }
 

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

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

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

	
 
    match op {
 
        BO::Concatenate => {
 
            let lhs_heap_pos;
 
            let rhs_heap_pos;
 
            let construct_fn;
 
            match lhs {
 
                Value::Message(lhs_pos) => {
 
                    lhs_heap_pos = *lhs_pos;
 
                    rhs_heap_pos = rhs.as_message();
 
                    construct_fn = |pos: HeapPos| Value::Message(pos);
 
                },
 
                Value::String(lhs_pos) => {
 
                    lhs_heap_pos = *lhs_pos;
 
                    rhs_heap_pos = rhs.as_string();
 
                    construct_fn = |pos: HeapPos| Value::String(pos);
 
                },
 
                Value::Array(lhs_pos) => {
 
                    lhs_heap_pos = *lhs_pos;
 
                    rhs_heap_pos = *rhs.as_array();
 
                    construct_fn = |pos: HeapPos| Value::Array(pos);
 
                },
 
                _ => unreachable!("apply_binary_operator {:?} on lhs {:?} and rhs {:?}", op, lhs, rhs)
 
            }
 

	
 
            let target_heap_pos = store.alloc_heap();
 
            let target = &mut store.heap_regions[target_heap_pos as usize].values;
 
            target.extend(&store.heap_regions[lhs_heap_pos as usize].values);
 
            target.extend(&store.heap_regions[rhs_heap_pos as usize].values);
 
            return construct_fn(target_heap_pos);
 
        },
 
        BO::LogicalOr => {
 
            return Value::Bool(lhs.as_bool() || rhs.as_bool());
 
        },
 
        BO::LogicalAnd => {
 
            return Value::Bool(lhs.as_bool() && rhs.as_bool());
 
        },
 
        BO::BitwiseOr        => { apply_int_op_and_return!(lhs, |,  op, rhs); },
 
        BO::BitwiseXor       => { apply_int_op_and_return!(lhs, ^,  op, rhs); },
 
        BO::BitwiseAnd       => { apply_int_op_and_return!(lhs, &,  op, rhs); },
 
        BO::Equality => { todo!("implement") },
 
        BO::Inequality =>  { todo!("implement") },
 
        BO::LessThan         => { apply_int_op_and_return!(lhs, <,  op, rhs); },
 
        BO::GreaterThan      => { apply_int_op_and_return!(lhs, >,  op, rhs); },
 
        BO::LessThanEqual    => { apply_int_op_and_return!(lhs, <=, op, rhs); },
 
        BO::GreaterThanEqual => { apply_int_op_and_return!(lhs, >=, op, rhs); },
 
        BO::ShiftLeft        => { apply_int_op_and_return!(lhs, <<, op, rhs); },
 
        BO::ShiftRight       => { apply_int_op_and_return!(lhs, >>, op, rhs); },
 
        BO::Add              => { apply_int_op_and_return!(lhs, +,  op, rhs); },
 
        BO::Subtract         => { apply_int_op_and_return!(lhs, -,  op, rhs); },
 
        BO::Multiply         => { apply_int_op_and_return!(lhs, *,  op, rhs); },
 
        BO::Divide           => { apply_int_op_and_return!(lhs, /,  op, rhs); },
 
        BO::Remainder        => { apply_int_op_and_return!(lhs, %,  op, rhs); }
 
    }
 
}
 

	
 
pub(crate) fn apply_unary_operator(store: &mut Store, op: UnaryOperator, value: &Value) -> Value {
 
    use UnaryOperator as UO;
 

	
 
    macro_rules! apply_int_expr_and_return {
 
        ($value:ident, $apply:expr, $op:ident) => {
 
            return match $value {
 
                Value::UInt8(v)  => Value::UInt8($apply),
 
                Value::UInt16(v) => Value::UInt16($apply),
 
                Value::UInt32(v) => Value::UInt32($apply),
 
                Value::UInt64(v) => Value::UInt64($apply),
 
                Value::SInt8(v)  => Value::SInt8($apply),
 
                Value::SInt16(v) => Value::SInt16($apply),
 
                Value::SInt32(v) => Value::SInt32($apply),
 
                Value::SInt64(v) => Value::SInt64($apply),
 
                _ => unreachable!("apply_unary_operator {:?} on value {:?}", $op, $value),
 
            };
 
        }
 
    }
 

	
 
    match op {
 
        UO::Positive   => { apply_int_expr_and_return!(value, *v, op) },
 
        UO::Negative   => { apply_int_expr_and_return!(value, *v, op) },
 
        UO::BitwiseNot => { apply_int_expr_and_return!(value, *v, op)},
 
        UO::LogicalNot => { return Value::Bool(!value.as_bool()); },
 
        UO::PreIncrement => { todo!("implement") },
 
        UO::PreDecrement => { todo!("implement") },
 
        UO::PostIncrement => { todo!("implement") },
 
        UO::PostDecrement => { todo!("implement") },
 
    }
 
}
 
\ No newline at end of file
src/protocol/eval_old.rs
Show inline comments
 
file renamed from src/protocol/eval.rs to src/protocol/eval_old.rs
 
@@ -1507,17 +1507,17 @@ impl Store {
 
            Expression::Unary(expr) => {
 
                let mut value = self.eval(h, ctx, expr.expression)?;
 
                match expr.operation {
 
                    UnaryOperation::PostIncrement => {
 
                    UnaryOperator::PostIncrement => {
 
                        self.update(h, ctx, expr.expression, value.plus(&ONE))?;
 
                    }
 
                    UnaryOperation::PreIncrement => {
 
                    UnaryOperator::PreIncrement => {
 
                        value = value.plus(&ONE);
 
                        self.update(h, ctx, expr.expression, value.clone())?;
 
                    }
 
                    UnaryOperation::PostDecrement => {
 
                    UnaryOperator::PostDecrement => {
 
                        self.update(h, ctx, expr.expression, value.minus(&ONE))?;
 
                    }
 
                    UnaryOperation::PreDecrement => {
 
                    UnaryOperator::PreDecrement => {
 
                        value = value.minus(&ONE);
 
                        self.update(h, ctx, expr.expression, value.clone())?;
 
                    }
src/protocol/parser/depth_visitor.rs
Show inline comments
 
@@ -719,10 +719,10 @@ impl Visitor for AssignableExpressions {
 
            self.error(h[expr].span.begin)
 
        } else {
 
            match h[expr].operation {
 
                UnaryOperation::PostDecrement
 
                | UnaryOperation::PreDecrement
 
                | UnaryOperation::PostIncrement
 
                | UnaryOperation::PreIncrement => {
 
                UnaryOperator::PostDecrement
 
                | UnaryOperator::PreDecrement
 
                | UnaryOperator::PostIncrement
 
                | UnaryOperator::PreIncrement => {
 
                    self.assignable = true;
 
                    recursive_unary_expression(self, h, expr)?;
 
                    self.assignable = false;
src/protocol/parser/pass_definitions.rs
Show inline comments
 
@@ -355,7 +355,9 @@ impl PassDefinitions {
 
                is_implicit: true,
 
                span: InputSpan::from_positions(wrap_begin_pos, wrap_end_pos), // TODO: @Span
 
                statements,
 
                parent_scope: None,
 
                parent_scope: Scope::Definition(DefinitionId::new_invalid()),
 
                first_unique_id_in_scope: -1,
 
                next_unique_id_in_scope: -1,
 
                relative_pos_in_parent: 0,
 
                locals: Vec::new(),
 
                labels: Vec::new()
 
@@ -382,7 +384,7 @@ impl PassDefinitions {
 
                section.push(id.upcast());
 

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

	
 
@@ -393,7 +395,7 @@ impl PassDefinitions {
 
                section.push(id.upcast());
 

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

	
 
@@ -410,7 +412,7 @@ impl PassDefinitions {
 
                section.push(id.upcast());
 

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

	
 
                let sync_stmt = &mut ctx.heap[id];
 
@@ -477,7 +479,9 @@ impl PassDefinitions {
 
            is_implicit: false,
 
            span: block_span,
 
            statements,
 
            parent_scope: None,
 
            parent_scope: Scope::Definition(DefinitionId::new_invalid()),
 
            first_unique_id_in_scope: -1,
 
            next_unique_id_in_scope: -1,
 
            relative_pos_in_parent: 0,
 
            locals: Vec::new(),
 
            labels: Vec::new(),
 
@@ -656,7 +660,7 @@ impl PassDefinitions {
 
            this,
 
            span: new_span,
 
            expression: call_id,
 
            next: None
 
            next: StatementId::new_invalid(),
 
        }))
 
    }
 

	
 
@@ -691,17 +695,21 @@ impl PassDefinitions {
 
        consume_token(&module.source, iter, TokenKind::SemiColon)?;
 

	
 
        // Construct ports
 
        let from = ctx.heap.alloc_local(|this| Local{
 
        let from = ctx.heap.alloc_variable(|this| Variable{
 
            this,
 
            kind: VariableKind::Local,
 
            identifier: from_identifier,
 
            parser_type: channel_type.clone(),
 
            relative_pos_in_block: 0,
 
            unique_id_in_scope: -1,
 
        });
 
        let to = ctx.heap.alloc_local(|this| Local{
 
        let to = ctx.heap.alloc_variable(|this|Variable{
 
            this,
 
            kind: VariableKind::Local,
 
            identifier: to_identifier,
 
            parser_type: channel_type,
 
            relative_pos_in_block: 0,
 
            unique_id_in_scope: -1,
 
        });
 

	
 
        // Construct the channel
 
@@ -710,7 +718,7 @@ impl PassDefinitions {
 
            span: channel_span,
 
            from, to,
 
            relative_pos_in_block: 0,
 
            next: None,
 
            next: StatementId::new_invalid(),
 
        }))
 
    }
 

	
 
@@ -782,17 +790,19 @@ impl PassDefinitions {
 
                consume_token(&module.source, iter, TokenKind::SemiColon)?;
 

	
 
                // Allocate the memory statement with the variable
 
                let local_id = ctx.heap.alloc_local(|this| Local{
 
                let local_id = ctx.heap.alloc_variable(|this| Variable{
 
                    this,
 
                    kind: VariableKind::Local,
 
                    identifier: identifier.clone(),
 
                    parser_type,
 
                    relative_pos_in_block: 0,
 
                    unique_id_in_scope: -1,
 
                });
 
                let memory_stmt_id = ctx.heap.alloc_memory_statement(|this| MemoryStatement{
 
                    this,
 
                    span: memory_span,
 
                    variable: local_id,
 
                    next: None
 
                    next: StatementId::new_invalid()
 
                });
 

	
 
                // Allocate the initial assignment
 
@@ -816,7 +826,7 @@ impl PassDefinitions {
 
                    this,
 
                    span: InputSpan::from_positions(initial_expr_begin_pos, initial_expr_end_pos),
 
                    expression: assignment_expr_id.upcast(),
 
                    next: None,
 
                    next: StatementId::new_invalid(),
 
                });
 

	
 
                return Ok(Some((memory_stmt_id, assignment_stmt_id)))
 
@@ -841,7 +851,7 @@ impl PassDefinitions {
 
            this,
 
            span: InputSpan::from_positions(start_pos, end_pos),
 
            expression,
 
            next: None,
 
            next: StatementId::new_invalid(),
 
        }))
 
    }
 

	
 
@@ -849,6 +859,8 @@ impl PassDefinitions {
 
    // Expression Parsing
 
    //--------------------------------------------------------------------------
 

	
 
    // TODO: @Cleanup This is fine for now. But I prefer my stacktraces not to
 
    //  look like enterprise Java code...
 
    fn consume_expression(
 
        &mut self, module: &Module, iter: &mut TokenIter, ctx: &mut PassCtx
 
    ) -> Result<ExpressionId, ParseError> {
 
@@ -1077,9 +1089,9 @@ impl PassDefinitions {
 
    fn consume_prefix_expression(
 
        &mut self, module: &Module, iter: &mut TokenIter, ctx: &mut PassCtx
 
    ) -> Result<ExpressionId, ParseError> {
 
        fn parse_prefix_token(token: Option<TokenKind>) -> Option<UnaryOperation> {
 
        fn parse_prefix_token(token: Option<TokenKind>) -> Option<UnaryOperator> {
 
            use TokenKind as TK;
 
            use UnaryOperation as UO;
 
            use UnaryOperator as UO;
 
            match token {
 
                Some(TK::Plus) => Some(UO::Positive),
 
                Some(TK::Minus) => Some(UO::Negative),
 
@@ -1129,7 +1141,7 @@ impl PassDefinitions {
 
            if token == TokenKind::PlusPlus {
 
                result = ctx.heap.alloc_unary_expression(|this| UnaryExpression{
 
                    this, span,
 
                    operation: UnaryOperation::PostIncrement,
 
                    operation: UnaryOperator::PostIncrement,
 
                    expression: result,
 
                    parent: ExpressionParent::None,
 
                    concrete_type: ConcreteType::default()
 
@@ -1137,7 +1149,7 @@ impl PassDefinitions {
 
            } else if token == TokenKind::MinusMinus {
 
                result = ctx.heap.alloc_unary_expression(|this| UnaryExpression{
 
                    this, span,
 
                    operation: UnaryOperation::PostDecrement,
 
                    operation: UnaryOperator::PostDecrement,
 
                    expression: result,
 
                    parent: ExpressionParent::None,
 
                    concrete_type: ConcreteType::default()
 
@@ -1913,17 +1925,18 @@ fn consume_parameter_list(
 
        TokenKind::OpenParen, TokenKind::CloseParen, source, iter, ctx,
 
        |source, iter, ctx| {
 
            let poly_vars = ctx.heap[definition_id].poly_vars(); // TODO: @Cleanup, this is really ugly. But rust...
 
            let (start_pos, _) = iter.next_positions();
 
            let parser_type = consume_parser_type(
 
                source, iter, &ctx.symbols, &ctx.heap, poly_vars, scope,
 
                definition_id, false, 0
 
            )?;
 
            let identifier = consume_ident_interned(source, iter, ctx)?;
 
            let parameter_id = ctx.heap.alloc_parameter(|this| Parameter{
 
            let parameter_id = ctx.heap.alloc_variable(|this| Variable{
 
                this,
 
                span: InputSpan::from_positions(start_pos, identifier.span.end),
 
                kind: VariableKind::Parameter,
 
                parser_type,
 
                identifier
 
                identifier,
 
                relative_pos_in_block: 0,
 
                unique_id_in_scope: -1,
 
            });
 
            Ok(parameter_id)
 
        },
src/protocol/parser/pass_typing.rs
Show inline comments
 
@@ -1670,7 +1670,7 @@ impl PassTyping {
 
    }
 

	
 
    fn progress_unary_expr(&mut self, ctx: &mut Ctx, id: UnaryExpressionId) -> Result<(), ParseError> {
 
        use UnaryOperation as UO;
 
        use UnaryOperator as UO;
 

	
 
        let upcast_id = id.upcast();
 
        let expr = &ctx.heap[id];
src/protocol/parser/pass_validation_linking.rs
Show inline comments
 
@@ -12,6 +12,7 @@ use super::visitor::{
 
    VisitorResult
 
};
 
use crate::protocol::parser::ModuleCompilationPhase;
 
use crossbeam_utils::thread::scope;
 

	
 
#[derive(PartialEq, Eq)]
 
enum DefinitionType {
 
@@ -48,30 +49,32 @@ impl DefinitionType {
 
/// Because of this scheme expressions will not be visited in the breadth-first
 
/// pass.
 
pub(crate) struct PassValidationLinking {
 
    /// `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` 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.
 
    // `in_while` contains the last encountered `While` statement. This is used
 
    // to resolve unlabeled `Continue`/`Break` statements.
 
    in_while: Option<WhileStatementId>,
 
    // Traversal state: current scope (which can be used to find the parent
 
    // scope), the definition variant we are considering, and whether the
 
    // visitor is performing breadthwise block statement traversal.
 
    cur_scope: Option<Scope>,
 
    // scope) and the definition variant we are considering.
 
    cur_scope: Scope,
 
    def_type: DefinitionType,
 
    // Parent expression (the previous stmt/expression we visited that could be
 
    // used as an expression parent)
 
    expr_parent: ExpressionParent,
 
    // Set by parent to indicate that child expression must be assignable. The
 
    // child will throw an error if it is not assignable. The stored span is
 
    // used for the error's position
 
    must_be_assignable: Option<InputSpan>,
 
    // Keeping track of relative position in block in the breadth-first pass.
 
    relative_pos_in_block: u32,
 
    // Various temporary buffers for traversal. Essentially working around
 
    // Rust's borrowing rules since it cannot understand we're modifying AST
 
    // members but not the AST container.
 
    variable_buffer: ScopedBuffer<VariableId>,
 
    definition_buffer: ScopedBuffer<DefinitionId>,
 
    // Single buffer of statement IDs that we want to traverse in a block.
 
    // Required to work around Rust borrowing rules and to prevent constant
 
    // cloning of vectors.
 
    statement_buffer: ScopedBuffer<StatementId>,
 
    // Another buffer, now with expression IDs, to prevent constant cloning of
 
    // vectors while working around rust's borrowing rules
 
    expression_buffer: ScopedBuffer<ExpressionId>,
 
}
 

	
 
@@ -80,10 +83,12 @@ impl PassValidationLinking {
 
        Self{
 
            in_sync: None,
 
            in_while: None,
 
            cur_scope: None,
 
            cur_scope: Scope::Definition(DefinitionId::new_invalid()),
 
            expr_parent: ExpressionParent::None,
 
            def_type: DefinitionType::Function(FunctionDefinitionId::new_invalid()),
 
            must_be_assignable: None,
 
            relative_pos_in_block: 0,
 
            variable_buffer: ScopedBuffer::new_reserved(128),
 
            definition_buffer: ScopedBuffer::new_reserved(128),
 
            statement_buffer: ScopedBuffer::new_reserved(STMT_BUFFER_INIT_CAPACITY),
 
            expression_buffer: ScopedBuffer::new_reserved(EXPR_BUFFER_INIT_CAPACITY),
 
@@ -93,7 +98,7 @@ impl PassValidationLinking {
 
    fn reset_state(&mut self) {
 
        self.in_sync = None;
 
        self.in_while = None;
 
        self.cur_scope = None;
 
        self.cur_scope = Scope::Definition(DefinitionId::new_invalid());
 
        self.expr_parent = ExpressionParent::None;
 
        self.def_type = DefinitionType::Function(FunctionDefinitionId::new_invalid());
 
        self.relative_pos_in_block = 0;
 
@@ -126,11 +131,21 @@ impl Visitor2 for PassValidationLinking {
 
            ComponentVariant::Primitive => DefinitionType::Primitive(id),
 
            ComponentVariant::Composite => DefinitionType::Composite(id),
 
        };
 
        self.cur_scope = Some(Scope::Definition(id.upcast()));
 
        self.cur_scope = Scope::Definition(id.upcast());
 
        self.expr_parent = ExpressionParent::None;
 

	
 
        // Visit parameters and assign a unique scope ID
 
        let definition = &ctx.heap[id];
 
        let body_id = definition.body;
 
        let section = self.variable_buffer.start_section_initialized(&definition.parameters);
 
        for variable_idx in 0..section.len() {
 
            let variable_id = section[variable_idx];
 
            let variable = &mut ctx.heap[variable_id];
 
            variable.unique_id_in_scope = variable_idx as i32;
 
        }
 
        section.forget();
 

	
 
        // Visit statements in component body
 
        let body_id = ctx.heap[id].body;
 
        self.visit_block_stmt(ctx, body_id)?;
 
        Ok(())
 
    }
 
@@ -140,11 +155,21 @@ impl Visitor2 for PassValidationLinking {
 

	
 
        // Set internal statement indices
 
        self.def_type = DefinitionType::Function(id);
 
        self.cur_scope = Some(Scope::Definition(id.upcast()));
 
        self.cur_scope = Scope::Definition(id.upcast());
 
        self.expr_parent = ExpressionParent::None;
 

	
 
        // Visit parameters and assign a unique scope ID
 
        let definition = &ctx.heap[id];
 
        let body_id = definition.body;
 
        let section = self.variable_buffer.start_section_initialized(&definition.parameters);
 
        for variable_idx in 0..section.len() {
 
            let variable_id = section[variable_idx];
 
            let variable = &mut ctx.heap[variable_id];
 
            variable.unique_id_in_scope = variable_idx as i32;
 
        }
 
        section.forget();
 

	
 
        // Visit statements in function body
 
        let body_id = ctx.heap[id].body;
 
        self.visit_block_stmt(ctx, body_id)?;
 
        Ok(())
 
    }
 
@@ -356,8 +381,10 @@ impl Visitor2 for PassValidationLinking {
 
        assignment_expr.parent = old_expr_parent;
 

	
 
        self.expr_parent = ExpressionParent::Expression(upcast_id, 0);
 
        self.must_be_assignable = Some(assignment_expr.span);
 
        self.visit_expr(ctx, left_expr_id)?;
 
        self.expr_parent = ExpressionParent::Expression(upcast_id, 1);
 
        self.must_be_assignable = None;
 
        self.visit_expr(ctx, right_expr_id)?;
 
        self.expr_parent = old_expr_parent;
 
        Ok(())
 
@@ -367,6 +394,12 @@ impl Visitor2 for PassValidationLinking {
 
        let upcast_id = id.upcast();
 
        let conditional_expr = &mut ctx.heap[id];
 

	
 
        if let Some(span) = self.must_be_assignable {
 
            return Err(ParseError::new_error_str_at_span(
 
                &ctx.module.source, span, "cannot assign to the result from a conditional expression"
 
            ))
 
        }
 

	
 
        let test_expr_id = conditional_expr.test;
 
        let true_expr_id = conditional_expr.true_expression;
 
        let false_expr_id = conditional_expr.false_expression;
 
@@ -388,6 +421,13 @@ impl Visitor2 for PassValidationLinking {
 
    fn visit_binary_expr(&mut self, ctx: &mut Ctx, id: BinaryExpressionId) -> VisitorResult {
 
        let upcast_id = id.upcast();
 
        let binary_expr = &mut ctx.heap[id];
 

	
 
        if let Some(span) = self.must_be_assignable {
 
            return Err(ParseError::new_error_str_at_span(
 
                &ctx.module.source, span, "cannot assign to the result from a binary expression"
 
            ))
 
        }
 

	
 
        let left_expr_id = binary_expr.left;
 
        let right_expr_id = binary_expr.right;
 

	
 
@@ -407,6 +447,12 @@ impl Visitor2 for PassValidationLinking {
 
        let unary_expr = &mut ctx.heap[id];
 
        let expr_id = unary_expr.expression;
 

	
 
        if let Some(span) = self.must_be_assignable {
 
            return Err(ParseError::new_error_str_at_span(
 
                &ctx.module.source, span, "cannot assign to the result from a unary expression"
 
            ))
 
        }
 

	
 
        let old_expr_parent = self.expr_parent;
 
        unary_expr.parent = old_expr_parent;
 

	
 
@@ -477,6 +523,12 @@ impl Visitor2 for PassValidationLinking {
 
        let old_expr_parent = self.expr_parent;
 
        literal_expr.parent = old_expr_parent;
 

	
 
        if let Some(span) = self.must_be_assignable {
 
            return Err(ParseError::new_error_str_at_span(
 
                &ctx.module.source, span, "cannot assign to a literal expression"
 
            ))
 
        }
 

	
 
        match &mut literal_expr.value {
 
            Literal::Null | Literal::True | Literal::False |
 
            Literal::Character(_) | Literal::String(_) | Literal::Integer(_) => {
 
@@ -655,6 +707,12 @@ impl Visitor2 for PassValidationLinking {
 
    fn visit_call_expr(&mut self, ctx: &mut Ctx, id: CallExpressionId) -> VisitorResult {
 
        let call_expr = &mut ctx.heap[id];
 

	
 
        if let Some(span) = self.must_be_assignable {
 
            return Err(ParseError::new_error_str_at_span(
 
                &ctx.module.source, span, "cannot assign to the result from a call expression"
 
            ))
 
        }
 

	
 
        // Check whether the method is allowed to be called within the code's
 
        // context (in sync, definition type, etc.)
 
        let mut expected_wrapping_new_stmt = false;
 
@@ -797,14 +855,19 @@ impl PassValidationLinking {
 
        // Set parent scope and relative position in the parent scope. Remember
 
        // these values to set them back to the old values when we're done with
 
        // the traversal of the block's statements.
 
        let scope_next_unique_id = get_scope_next_unique_id(ctx, &self.cur_scope);
 

	
 
        let body = &mut ctx.heap[id];
 
        body.parent_scope = self.cur_scope.clone();
 
        body.relative_pos_in_parent = self.relative_pos_in_block;
 
        body.first_unique_id_in_scope = scope_next_unique_id;
 
        body.next_unique_id_in_scope = scope_next_unique_id;
 

	
 
        let old_scope = self.cur_scope.replace(match hint {
 
        let old_scope = self.cur_scope.clone();
 
        self.cur_scope = match hint {
 
            Some(sync_id) => Scope::Synchronous((sync_id, id)),
 
            None => Scope::Regular(id),
 
        });
 
        };
 
        let old_relative_pos = self.relative_pos_in_block;
 

	
 
        // Copy statement IDs into buffer
 
@@ -818,6 +881,7 @@ impl PassValidationLinking {
 
            self.visit_statement_for_locals_labels_and_in_sync(ctx, self.relative_pos_in_block, statement_section[stmt_idx])?;
 
        }
 

	
 
        // Perform the depth-first traversal
 
        for stmt_idx in 0..statement_section.len() {
 
            self.relative_pos_in_block = stmt_idx as u32;
 
            self.visit_stmt(ctx, statement_section[stmt_idx])?;
 
@@ -868,8 +932,8 @@ impl PassValidationLinking {
 

	
 
    /// 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<(), ParseError> {
 
        debug_assert!(self.cur_scope.is_some());
 
    fn checked_local_add(&mut self, ctx: &mut Ctx, relative_pos: u32, id: VariableId) -> Result<(), ParseError> {
 
        debug_assert!(self.cur_scope.is_block());
 

	
 
        // Make sure we do not conflict with any global symbols
 
        let cur_scope = SymbolScope::Definition(self.def_type.definition_id());
 
@@ -891,7 +955,7 @@ impl PassValidationLinking {
 
        // 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 scope = &self.cur_scope;
 
        let mut local_relative_pos = self.relative_pos_in_block;
 

	
 
        loop {
 
@@ -928,7 +992,7 @@ impl PassValidationLinking {
 
                            ParseError::new_error_str_at_span(
 
                                &ctx.module.source, local.identifier.span, "Local variable name conflicts with parameter"
 
                            ).with_info_str_at_span(
 
                                &ctx.module.source, parameter.span, "Parameter definition is found here"
 
                                &ctx.module.source, parameter.identifier.span, "Parameter definition is found here"
 
                            )
 
                        );
 
                    }
 
@@ -942,8 +1006,13 @@ impl PassValidationLinking {
 
        }
 

	
 
        // No collisions at all
 
        let block = &mut ctx.heap[self.cur_scope.as_ref().unwrap().to_block()];
 
        let block = &mut ctx.heap[self.cur_scope.to_block()];
 
        block.locals.push(id);
 
        let unique_id_in_scope = block.next_unique_id_in_scope;
 
        block.next_unique_id_in_scope += 1;
 

	
 
        let variable = &mut ctx.heap[id];
 
        variable.unique_id_in_scope = unique_id_in_scope;
 

	
 
        Ok(())
 
    }
 
@@ -951,12 +1020,12 @@ impl PassValidationLinking {
 
    /// Finds a variable in the visitor's scope that must appear before the
 
    /// specified relative position within that block.
 
    fn find_variable(&self, ctx: &Ctx, mut relative_pos: u32, identifier: &Identifier) -> Result<VariableId, ParseError> {
 
        debug_assert!(self.cur_scope.is_some());
 
        debug_assert!(self.cur_scope.is_block());
 

	
 
        // 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();
 
        let mut scope = &self.cur_scope;
 
        
 
        loop {
 
            debug_assert!(scope.is_block());
 
@@ -1000,7 +1069,7 @@ impl PassValidationLinking {
 
    /// Adds a particular label to the current scope. Will return an error if
 
    /// there is another label with the same name visible in the current scope.
 
    fn checked_label_add(&mut self, ctx: &mut Ctx, relative_pos: u32, in_sync: Option<SynchronousStatementId>, id: LabeledStatementId) -> Result<(), ParseError> {
 
        debug_assert!(self.cur_scope.is_some());
 
        debug_assert!(self.cur_scope.is_block());
 

	
 
        // Make sure label is not defined within the current scope or any of the
 
        // parent scope.
 
@@ -1009,7 +1078,7 @@ impl PassValidationLinking {
 
        label.in_sync = in_sync;
 

	
 
        let label = &ctx.heap[id];
 
        let mut scope = self.cur_scope.as_ref().unwrap();
 
        let mut scope = &self.cur_scope;
 

	
 
        loop {
 
            debug_assert!(scope.is_block(), "scope is not a block");
 
@@ -1044,9 +1113,9 @@ impl PassValidationLinking {
 
    /// will make sure that the target label does not skip over any variable
 
    /// declarations within the scope in which the label was found.
 
    fn find_label(&self, ctx: &Ctx, identifier: &Identifier) -> Result<LabeledStatementId, ParseError> {
 
        debug_assert!(self.cur_scope.is_some());
 
        debug_assert!(self.cur_scope.is_block());
 

	
 
        let mut scope = self.cur_scope.as_ref().unwrap();
 
        let mut scope = &self.cur_scope;
 
        loop {
 
            debug_assert!(scope.is_block(), "scope is not a block");
 
            let relative_scope_pos = ctx.heap[scope.to_block()].relative_pos_in_parent;
 
@@ -1088,7 +1157,7 @@ impl PassValidationLinking {
 
    /// statement that is one of our current parents.
 
    fn has_parent_while_scope(&self, ctx: &Ctx, id: WhileStatementId) -> bool {
 
        debug_assert!(self.cur_scope.is_some());
 
        let mut scope = self.cur_scope.as_ref().unwrap();
 
        let mut scope = &self.cur_scope;
 
        let while_stmt = &ctx.heap[id];
 
        loop {
 
            debug_assert!(scope.is_block());
 
@@ -1099,7 +1168,7 @@ impl PassValidationLinking {
 

	
 
            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();
 
            scope = &block.parent_scope;
 
            if !scope.is_block() {
 
                return false;
 
            }
 
@@ -1171,4 +1240,21 @@ impl PassValidationLinking {
 

	
 
        Ok(target)
 
    }
 
}
 

	
 
fn get_scope_next_unique_id(ctx: &Ctx, scope: &Scope) -> i32 {
 
    match scope {
 
        Scope::Definition(definition_id) => {
 
            let definition = &ctx.heap[*definition_id];
 
            match definition {
 
                Definition::Component(definition) => definition.parameters.len() as i32,
 
                Definition::Function(definition) => definition.parameters.len() as i32,
 
                _ => unreachable!("Scope::Definition points to non-procedure type")
 
            }
 
        },
 
        Scope::Synchronous((_, block_id)) | Scope::Regular(block_id) => {
 
            let block = &ctx.heap[*block_id];
 
            block.next_unique_id_in_scope
 
        }
 
    }
 
}
 
\ No newline at end of file
0 comments (0 inline, 0 general)