From abc5bbbed7db0a68b59f043840f1191316ce184a Mon Sep 17 00:00:00 2001 From: Tim Fennis Date: Sun, 1 Mar 2026 00:26:35 +0100 Subject: [PATCH 1/5] =?UTF-8?q?=F0=9F=9A=A7=20Migrate=20parser=20to=20Expr?= =?UTF-8?q?essionPool/ExpressionRef-based=20AST?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- ndc_lib/src/interpreter/environment.rs | 2 +- ndc_lib/src/interpreter/evaluate/index.rs | 12 +- ndc_lib/src/interpreter/evaluate/mod.rs | 6 +- ndc_lib/src/interpreter/function.rs | 2 +- ndc_lib/src/interpreter/mod.rs | 2 +- ndc_lib/src/interpreter/num.rs | 2 +- ndc_lib/src/interpreter/semantic/analyser.rs | 14 +- ndc_lsp/src/backend.rs | 2 +- ndc_parser/src/expression.rs | 110 +++-- ndc_parser/src/parser.rs | 478 ++++++++++--------- 10 files changed, 348 insertions(+), 282 deletions(-) diff --git a/ndc_lib/src/interpreter/environment.rs b/ndc_lib/src/interpreter/environment.rs index fe2cf08a..fefff509 100644 --- a/ndc_lib/src/interpreter/environment.rs +++ b/ndc_lib/src/interpreter/environment.rs @@ -1,7 +1,7 @@ use crate::interpreter::function::{Function, StaticType}; -use ndc_parser::ResolvedVar; use crate::interpreter::value::Value; +use ndc_parser::ResolvedVar; use std::cell::RefCell; use std::fmt; use std::fmt::Formatter; diff --git a/ndc_lib/src/interpreter/evaluate/index.rs b/ndc_lib/src/interpreter/evaluate/index.rs index 23acdc42..cb31813d 100644 --- a/ndc_lib/src/interpreter/evaluate/index.rs +++ b/ndc_lib/src/interpreter/evaluate/index.rs @@ -9,14 +9,12 @@ //! | Backward index | -10 | -9 | -8 | -7 | -6 | -5 | -4 | -3 | -2 | -1 | //! +----------------+-----+----+----+----+----+----+----+----+----+----+ -use ndc_parser::{Expression, ExpressionLocation}; use super::{EvaluationError, EvaluationResult, IntoEvaluationResult, evaluate_expression}; use crate::interpreter::environment::Environment; -use crate::{ - interpreter::{function::FunctionCarrier, sequence::Sequence, value::Value}, -}; +use crate::interpreter::{function::FunctionCarrier, sequence::Sequence, value::Value}; use itertools::Itertools; use ndc_lexer::Span; +use ndc_parser::{Expression, ExpressionLocation}; use std::cell::RefCell; use std::cmp::min; use std::ops::IndexMut; @@ -300,7 +298,11 @@ pub fn set_at_index( Offset::Range(from_usize, to_usize) => { let tail = list.drain(from_usize..).collect::>(); - list.extend(rhs.try_into_vec().expect("this must succeed, but not sure why").into_iter()); + list.extend( + rhs.try_into_vec() + .expect("this must succeed, but not sure why") + .into_iter(), + ); list.extend_from_slice(&tail[(to_usize - from_usize)..]); } diff --git a/ndc_lib/src/interpreter/evaluate/mod.rs b/ndc_lib/src/interpreter/evaluate/mod.rs index 7fdc973b..9a41c094 100644 --- a/ndc_lib/src/interpreter/evaluate/mod.rs +++ b/ndc_lib/src/interpreter/evaluate/mod.rs @@ -1,6 +1,3 @@ -use ndc_parser::{ - Binding, Expression, ExpressionLocation, ForBody, ForIteration, LogicalOperator, Lvalue, -}; use crate::hash_map::HashMap; use crate::interpreter::environment::Environment; use crate::interpreter::function::{Function, FunctionBody, FunctionCarrier, StaticType}; @@ -12,6 +9,9 @@ use crate::interpreter::value::Value; use index::{Offset, evaluate_as_index, get_at_index, set_at_index}; use itertools::Itertools; use ndc_lexer::Span; +use ndc_parser::{ + Binding, Expression, ExpressionLocation, ForBody, ForIteration, LogicalOperator, Lvalue, +}; use std::cell::RefCell; use std::fmt; use std::rc::Rc; diff --git a/ndc_lib/src/interpreter/function.rs b/ndc_lib/src/interpreter/function.rs index 47aea37e..a8e93a1a 100644 --- a/ndc_lib/src/interpreter/function.rs +++ b/ndc_lib/src/interpreter/function.rs @@ -1,4 +1,3 @@ -use ndc_parser::{ExpressionLocation, ResolvedVar}; use crate::hash_map::{DefaultHasher, HashMap}; use crate::interpreter::environment::Environment; use crate::interpreter::evaluate::{ @@ -9,6 +8,7 @@ use crate::interpreter::sequence::Sequence; use crate::interpreter::value::Value; use derive_builder::Builder; use ndc_lexer::Span; +use ndc_parser::{ExpressionLocation, ResolvedVar}; pub use ndc_parser::{Parameter, StaticType, TypeSignature}; use std::cell::{BorrowError, BorrowMutError, RefCell}; use std::fmt; diff --git a/ndc_lib/src/interpreter/mod.rs b/ndc_lib/src/interpreter/mod.rs index cd479a26..e09c6ffb 100644 --- a/ndc_lib/src/interpreter/mod.rs +++ b/ndc_lib/src/interpreter/mod.rs @@ -1,13 +1,13 @@ use std::cell::RefCell; use std::rc::Rc; -use ndc_parser::ExpressionLocation; use crate::interpreter::environment::{Environment, InterpreterOutput}; use crate::interpreter::evaluate::{EvaluationError, evaluate_expression}; use crate::interpreter::function::FunctionCarrier; use crate::interpreter::semantic::analyser::{Analyser, ScopeTree}; use crate::interpreter::value::Value; use ndc_lexer::{Lexer, TokenLocation}; +use ndc_parser::ExpressionLocation; pub mod environment; pub mod evaluate; pub mod function; diff --git a/ndc_lib/src/interpreter/num.rs b/ndc_lib/src/interpreter/num.rs index ee211a4d..75cae747 100644 --- a/ndc_lib/src/interpreter/num.rs +++ b/ndc_lib/src/interpreter/num.rs @@ -4,11 +4,11 @@ use std::hash::{Hash, Hasher}; use std::num::TryFromIntError; use std::ops::{Add, Div, Mul, Neg, Not, Rem, Sub}; -use ndc_parser::BinaryOperator; use crate::interpreter::evaluate::EvaluationError; use crate::interpreter::function::StaticType; use crate::interpreter::int::Int; use ndc_lexer::Span; +use ndc_parser::BinaryOperator; use num::bigint::TryFromBigIntError; use num::complex::{Complex64, ComplexFloat}; use num::{BigInt, BigRational, Complex, FromPrimitive, Signed, ToPrimitive, Zero}; diff --git a/ndc_lib/src/interpreter/semantic/analyser.rs b/ndc_lib/src/interpreter/semantic/analyser.rs index 68704aa4..8d78ead7 100644 --- a/ndc_lib/src/interpreter/semantic/analyser.rs +++ b/ndc_lib/src/interpreter/semantic/analyser.rs @@ -1,9 +1,9 @@ -use ndc_parser::{ - Binding, Expression, ExpressionLocation, ForBody, ForIteration, Lvalue, ResolvedVar, -}; use crate::interpreter::function::StaticType; use itertools::Itertools; use ndc_lexer::Span; +use ndc_parser::{ + Binding, Expression, ExpressionLocation, ForBody, ForIteration, Lvalue, ResolvedVar, +}; use std::fmt::{Debug, Formatter}; pub struct Analyser { @@ -347,7 +347,13 @@ impl Analyser { self.scope_tree.new_scope(); // TODO: when we give type parameters to all instances of sequence we can correctly infer StaticType::Any in this position - self.resolve_lvalue_declarative(l_value, sequence_type.sequence_element_type().unwrap_or(StaticType::Any), span)?; + self.resolve_lvalue_declarative( + l_value, + sequence_type + .sequence_element_type() + .unwrap_or(StaticType::Any), + span, + )?; do_destroy = true; // TODO: why is this correct } ForIteration::Guard(expr) => { diff --git a/ndc_lsp/src/backend.rs b/ndc_lsp/src/backend.rs index 075023c4..f7abf99f 100644 --- a/ndc_lsp/src/backend.rs +++ b/ndc_lsp/src/backend.rs @@ -1,8 +1,8 @@ use std::collections::HashMap; use ndc_lexer::{Lexer, Span, TokenLocation}; -use ndc_parser::{Expression, ExpressionLocation, ForBody, ForIteration, Lvalue}; use ndc_lib::interpreter::Interpreter; +use ndc_parser::{Expression, ExpressionLocation, ForBody, ForIteration, Lvalue}; use tokio::sync::Mutex; use tower_lsp::jsonrpc::Result as JsonRPCResult; use tower_lsp::lsp_types::{ diff --git a/ndc_parser/src/expression.rs b/ndc_parser/src/expression.rs index 261d646f..58805ca3 100644 --- a/ndc_parser/src/expression.rs +++ b/ndc_parser/src/expression.rs @@ -24,6 +24,9 @@ pub struct ExpressionLocation { pub span: Span, } +#[derive(Debug, Clone, Copy, PartialEq, Eq)] +pub struct ExpressionRef(u32); + #[derive(Debug, PartialEq, Clone)] pub enum Expression { // Literals @@ -37,24 +40,24 @@ pub enum Expression { name: String, resolved: Binding, }, - Statement(Box), + Statement(ExpressionRef), Logical { - left: Box, + left: ExpressionRef, operator: LogicalOperator, - right: Box, + right: ExpressionRef, }, - Grouping(Box), + Grouping(ExpressionRef), VariableDeclaration { l_value: Lvalue, - value: Box, + value: ExpressionRef, }, Assignment { l_value: Lvalue, - r_value: Box, + r_value: ExpressionRef, }, OpAssignment { l_value: Lvalue, - r_value: Box, + r_value: ExpressionRef, operation: String, resolved_assign_operation: Binding, resolved_operation: Binding, @@ -63,8 +66,8 @@ pub enum Expression { name: Option, resolved_name: Option, // TODO: Instead of an ExpressionLocation with a Tuple the parser should just give us something we can actually work with - parameters: Box, - body: Box, + parameters: ExpressionRef, + body: ExpressionRef, return_type: Option, pure: bool, }, @@ -72,13 +75,13 @@ pub enum Expression { statements: Vec, }, If { - condition: Box, - on_true: Box, - on_false: Option>, + condition: ExpressionRef, + on_true: ExpressionRef, + on_false: Option, }, While { - expression: Box, - loop_body: Box, + expression: ExpressionRef, + loop_body: ExpressionRef, }, For { iterations: Vec, @@ -86,12 +89,12 @@ pub enum Expression { }, Call { /// The function to call, could be an identifier, or any expression that produces a function as its value - function: Box, - arguments: Vec, + function: ExpressionRef, + arguments: Vec, }, Index { - value: Box, - index: Box, + value: ExpressionRef, + index: ExpressionRef, }, Tuple { values: Vec, @@ -101,40 +104,42 @@ pub enum Expression { }, Map { values: Vec<(ExpressionLocation, Option)>, - default: Option>, + default: Option, }, Return { - value: Box, + value: ExpressionRef, }, Break, Continue, RangeInclusive { - start: Option>, - end: Option>, + start: Option, + end: Option, }, RangeExclusive { - start: Option>, - end: Option>, + start: Option, + end: Option, }, } +impl Eq for Expression {} + #[derive(Debug, Eq, PartialEq, Clone)] pub enum ForIteration { Iteration { l_value: Lvalue, - sequence: ExpressionLocation, + sequence: ExpressionRef, }, - Guard(ExpressionLocation), + Guard(ExpressionRef), } #[derive(Debug, Eq, PartialEq, Clone)] pub enum ForBody { - Block(ExpressionLocation), - List(ExpressionLocation), + Block(ExpressionRef), + List(ExpressionRef), Map { - key: ExpressionLocation, - value: Option, - default: Option>, + key: ExpressionRef, + value: Option, + default: Option>, }, } @@ -149,15 +154,13 @@ pub enum Lvalue { }, // Example: `foo()[1] = ...` Index { - value: Box, - index: Box, + value: ExpressionRef, + index: ExpressionRef, }, // Example: `let a, b = ...` Sequence(Vec), } -impl Eq for Expression {} - impl Expression { #[must_use] pub fn to_location(self, span: Span) -> ExpressionLocation { @@ -168,15 +171,30 @@ impl Expression { } } -impl ExpressionLocation { - #[must_use] - pub fn to_statement(self) -> Self { - Self { - span: self.span, - expression: Expression::Statement(Box::new(self)), - } +#[derive(Clone)] +pub struct ExpressionPool { + region: Vec, +} + +impl ExpressionPool { + pub fn new() -> Self { + Self { region: Vec::new() } + } + pub fn get(&self, er: ExpressionRef) -> &ExpressionLocation { + &self.region[er.0 as usize] } + pub fn add(&mut self, expr: ExpressionLocation) -> ExpressionRef { + self.region.push(expr); + ExpressionRef((self.region.len() - 1) as u32) + } + + pub fn merged_span(&self, left_id: ExpressionRef, right_id: ExpressionRef) -> Span { + self.region[left_id.0 as usize].span.merge(self.region[right_id.0 as usize].span) + } +} + +impl ExpressionLocation { pub fn as_identifier(&self) -> &str { match &self.expression { Expression::Identifier { name, resolved: _ } => name, @@ -219,13 +237,15 @@ impl Lvalue { } #[must_use] - pub fn can_build_from_expression(expression: &Expression) -> bool { + pub fn can_build_from_expression(expression: &Expression, pool: &ExpressionPool) -> bool { match expression { Expression::Identifier { .. } | Expression::Index { .. } => true, Expression::List { values } | Expression::Tuple { values } => values .iter() - .all(|el| Self::can_build_from_expression(&el.expression)), - Expression::Grouping(inner) => Self::can_build_from_expression(&inner.expression), + .all(|el| Self::can_build_from_expression(&el.expression, pool)), + Expression::Grouping(inner) => { + Self::can_build_from_expression(&pool.get(*inner).expression, pool) + } _ => false, } } diff --git a/ndc_parser/src/parser.rs b/ndc_parser/src/parser.rs index 733566fb..d2bd195e 100644 --- a/ndc_parser/src/parser.rs +++ b/ndc_parser/src/parser.rs @@ -1,25 +1,30 @@ use std::fmt::Write; -use crate::expression::Expression; -use crate::expression::{Binding, ExpressionLocation, ForBody, ForIteration, Lvalue}; +use crate::expression::{Binding, ExpressionLocation, ExpressionRef, ForBody, ForIteration, Lvalue}; +use crate::expression::{Expression, ExpressionPool}; use crate::operator::{BinaryOperator, LogicalOperator, UnaryOperator}; use ndc_lexer::{Span, Token, TokenLocation}; pub struct Parser { tokens: Vec, current: usize, + pool: ExpressionPool, } impl Parser { pub fn from_tokens(tokens: Vec) -> Self { - Self { tokens, current: 0 } + Self { + tokens, + current: 0, + pool: ExpressionPool::new(), + } } /// Parse a Vec of Tokens into a Vec of statements /// # Errors /// If the parsing fails which it could do for many different reasons it will return an [Error]. See the variants of /// this type for more information about the different kinds of errors. - pub fn parse(&mut self) -> Result, Error> { + pub fn parse(&mut self) -> Result { let is_valid_statement = |expr: &Expression| -> bool { matches!( expr, @@ -31,12 +36,12 @@ impl Parser { | Expression::FunctionDeclaration { .. } ) }; - let mut expressions = Vec::new(); + while self.peek_current_token_location().is_some() { let expr_loc = self.expression_or_statement()?; let is_statement = is_valid_statement(&expr_loc.expression); - expressions.push(expr_loc); + self.pool.add(expr_loc); if !is_statement { break; @@ -51,7 +56,7 @@ impl Parser { )); } - Ok(expressions) + Ok(std::mem::replace(&mut self.pool, ExpressionPool::new())) } fn peek_current_token(&self) -> Option<&Token> { @@ -150,11 +155,11 @@ impl Parser { /// it also works for expression like `x not == 5` which is a debatable feature. fn consume_binary_expression_left_associative( &mut self, - next: fn(&mut Self) -> Result, + next: fn(&mut Self) -> Result, valid_tokens: &[Token], augment_not: bool, - ) -> Result { - let mut left = next(self)?; + ) -> Result { + let mut left_id = next(self)?; let mut extended_valid_tokens = valid_tokens.to_vec(); if augment_not { @@ -188,79 +193,85 @@ impl Parser { operator_token_loc.token ) }); - let right = next(self)?; - // IS this new span logic sound? - let new_span = left.span.merge(right.span); + let right_id = next(self)?; + + let new_span = self.pool.get(left_id).span.merge(self.pool.get(right_id).span); // Is this always the same debug_assert_eq!(operator.to_string(), operator_token_loc.token.to_string()); - left = Expression::Call { - function: Box::new( + let function_id = self.pool.add( + Expression::Identifier { + name: operator_token_loc.token.to_string(), + resolved: Binding::None, + } + .to_location(operator_token_loc.span), + ); + + left_id = self.pool.add(Expression::Call { + function: function_id, + arguments: vec![left_id, right_id], + } + .to_location(new_span)); + + if let Some(not_token) = invert { + let not_function_id = self.pool.add( Expression::Identifier { - name: operator_token_loc.token.to_string(), + name: not_token.token.to_string(), resolved: Binding::None, } - .to_location(operator_token_loc.span), - ), - arguments: vec![left, right], - } - .to_location(new_span); + .to_location(not_token.span), + ); - if let Some(not_token) = invert { - left = Expression::Call { - function: Box::new( - Expression::Identifier { - name: not_token.token.to_string(), - resolved: Binding::None, - } - .to_location(not_token.span), - ), - arguments: vec![left], + left_id = self.pool.add(Expression::Call { + function: not_function_id, + arguments: vec![left_id], } - .to_location(new_span.merge(not_token.span)); + .to_location(new_span.merge(not_token.span))); } } - Ok(left) + Ok(left_id) } fn consume_binary_expression_right_associative( &mut self, - next: fn(&mut Self) -> Result, - current: fn(&mut Self) -> Result, + next: fn(&mut Self) -> Result, + current: fn(&mut Self) -> Result, valid_tokens: &[Token], - ) -> Result { - let left = next(self)?; + ) -> Result { + let left_id = next(self)?; if let Some(token_location) = self.consume_token_if(valid_tokens) { let operator_span = token_location.span; let operator = BinaryOperator::try_from(token_location) .expect("COMPILER ERROR: consume_token_if must guarantee the correct token"); - let right = current(self)?; + + let right_id = current(self)?; + let new_span = self.pool.get(left_id).span.merge(self.pool.get(right_id).span); - let new_span = left.span.merge(right.span); - - return Ok(Expression::Call { - function: Box::new( - Expression::Identifier { - name: operator.to_string(), - resolved: Binding::None, - } - .to_location(operator_span), - ), - arguments: vec![left, right], + + let ident_id = self.pool.add(Expression::Identifier { + name: operator.to_string(), + resolved: Binding::None, } - .to_location(new_span)); + .to_location(operator_span)); + + let out_id = self.pool.add(Expression::Call { + function: ident_id, + arguments: vec![left_id, right_id], + } + .to_location(new_span)); + return Ok(out_id); } - Ok(left) + Ok(left_id) } fn consume_logical_expression_left_associative( &mut self, - next: fn(&mut Self) -> Result, + next: fn(&mut Self) -> Result, valid_tokens: &[Token], - ) -> Result { + ) -> Result { let mut left = next(self)?; while let Some(token_location) = self.consume_token_if(valid_tokens) { let operator: LogicalOperator = token_location @@ -268,13 +279,13 @@ impl Parser { .try_into() .expect("consume_operator_if guaranteed us that this is an operator"); let right = next(self)?; - let new_span = left.span.merge(right.span); - left = Expression::Logical { - left: Box::new(left), + let new_span = self.pool.get(left).span.merge(self.pool.get(right).span); + left = self.pool.add(Expression::Logical { + left, operator, - right: Box::new(right), + right, } - .to_location(new_span); + .to_location(new_span)); } Ok(left) } @@ -324,7 +335,7 @@ impl Parser { let end = expression.span; let declaration = Expression::VariableDeclaration { l_value: lvalue, - value: Box::new(expression), + value: self.pool.add(expression), }; if self.peek_current_token().is_some() { @@ -367,7 +378,7 @@ impl Parser { let assignment_expression = Expression::Assignment { l_value: Lvalue::try_from(maybe_lvalue) .expect("guaranteed to produce an lvalue"), - r_value: Box::new(expression), + r_value: self.pool.add(expression), }; Ok(assignment_expression.to_location(start.merge(end))) @@ -381,7 +392,7 @@ impl Parser { let op_assign = Expression::OpAssignment { l_value: Lvalue::try_from(maybe_lvalue) .expect("guaranteed to produce an lvalue"), - r_value: Box::new(expression), + r_value: self.pool.add(expression), operation: operation_identifier, resolved_assign_operation: Binding::None, resolved_operation: Binding::None, @@ -395,10 +406,11 @@ impl Parser { fn tuple_expression( &mut self, - next: fn(&mut Self) -> Result, + next: fn(&mut Self) -> Result, must_be_tuple: bool, ) -> Result { - let mut expressions = vec![next(self)?]; + let first_ref = next(self)?; + let mut refs = vec![first_ref]; let mut must_be_tuple = must_be_tuple; while self.consume_token_if(&[Token::Comma]).is_some() { // Peek at right paren, if that matches we break @@ -407,14 +419,13 @@ impl Parser { must_be_tuple = true; break; } - expressions.push(next(self)?); + refs.push(next(self)?); } - let new_span = expressions - .first() - .expect("first is guaranteed to have a result") - .span - .merge(expressions.last().unwrap().span); + let new_span = self.pool.get(refs[0]).span + .merge(self.pool.get(*refs.last().unwrap()).span); + let expressions: Vec = + refs.iter().map(|&r| self.pool.get(r).clone()).collect(); let tuple_expression = ExpressionLocation { expression: Expression::Tuple { @@ -433,7 +444,7 @@ impl Parser { /// Parses a delimited tuple (enclosed in parentheses) that can be empty fn delimited_tuple( &mut self, - next: fn(&mut Self) -> Result, + next: fn(&mut Self) -> Result, ) -> Result { let start = self.require_current_token_matches(&Token::LeftParentheses)?; if let Some(end) = self.consume_token_if(&[Token::RightParentheses]) { @@ -451,14 +462,14 @@ impl Parser { } } - fn single_expression(&mut self) -> Result { + fn single_expression(&mut self) -> Result { self.logic_or() } - fn logic_or(&mut self) -> Result { + fn logic_or(&mut self) -> Result { self.consume_logical_expression_left_associative(Self::logic_and, &[Token::LogicOr]) } - fn logic_and(&mut self) -> Result { + fn logic_and(&mut self) -> Result { self.consume_logical_expression_left_associative(Self::logic_not, &[Token::LogicAnd]) } @@ -466,7 +477,7 @@ impl Parser { // ```ndc // x := not foo in bar // ``` - fn logic_not(&mut self) -> Result { + fn logic_not(&mut self) -> Result { if let Some(operator_token_loc) = self.consume_token_if(&[Token::LogicNot]) { let operator_span = operator_token_loc.span; let _: UnaryOperator = operator_token_loc @@ -475,53 +486,59 @@ impl Parser { .expect("consume_operator_if guaranteed us that this token can be UnaryOperator"); let right = self.logic_not()?; - let span = right.span; + let span = self.pool.get(right).span; - Ok(Expression::Call { - function: Box::new( - Expression::Identifier { - name: operator_token_loc.token.to_string(), - resolved: Binding::None, - } - .to_location(operator_span), - ), - arguments: vec![right], - } - .to_location(span)) + let function_id = self.pool.add( + Expression::Identifier { + name: operator_token_loc.token.to_string(), + resolved: Binding::None, + } + .to_location(operator_span), + ); + + Ok(self.pool.add( + Expression::Call { + function: function_id, + arguments: vec![right], + } + .to_location(span), + )) } else { self.range() } } - fn range(&mut self) -> Result { + fn range(&mut self) -> Result { let left = self.comparison()?; if let Some(token) = self.consume_token_if(&[Token::DotDot, Token::DotDotEquals]) { + let left_span = self.pool.get(left).span; let (span, right) = if self.peek_range_end() { - (left.span.merge(token.span), None) + (left_span.merge(token.span), None) } else { let right = self.comparison()?; - (left.span.merge(right.span), Some(Box::new(right))) + let right_span = self.pool.get(right).span; + (left_span.merge(right_span), Some(right)) }; let expression = if token.token == Token::DotDot { Expression::RangeExclusive { - start: Some(Box::new(left)), + start: Some(left), end: right, } } else { Expression::RangeInclusive { - start: Some(Box::new(left)), + start: Some(left), end: right, } }; - Ok(ExpressionLocation { expression, span }) + Ok(self.pool.add(ExpressionLocation { expression, span })) } else { Ok(left) } } - fn comparison(&mut self) -> Result { + fn comparison(&mut self) -> Result { self.consume_binary_expression_left_associative( Self::spaceship, &[ @@ -537,7 +554,7 @@ impl Parser { ) } - fn spaceship(&mut self) -> Result { + fn spaceship(&mut self) -> Result { self.consume_binary_expression_left_associative( Self::bit_shift, &[Token::Spaceship, Token::InverseSpaceship], @@ -545,7 +562,7 @@ impl Parser { ) } - fn bit_shift(&mut self) -> Result { + fn bit_shift(&mut self) -> Result { self.consume_binary_expression_left_associative( Self::boolean_or, &[Token::LessLess, Token::GreaterGreater], @@ -553,19 +570,19 @@ impl Parser { ) } - fn boolean_or(&mut self) -> Result { + fn boolean_or(&mut self) -> Result { self.consume_binary_expression_left_associative(Self::boolean_xor, &[Token::Pipe], false) } - fn boolean_xor(&mut self) -> Result { + fn boolean_xor(&mut self) -> Result { self.consume_binary_expression_left_associative(Self::boolean_and, &[Token::Tilde], false) } - fn boolean_and(&mut self) -> Result { + fn boolean_and(&mut self) -> Result { self.consume_binary_expression_left_associative(Self::term, &[Token::Ampersand], false) } - fn term(&mut self) -> Result { + fn term(&mut self) -> Result { self.consume_binary_expression_left_associative( Self::factor, &[Token::Plus, Token::Minus, Token::PlusPlus, Token::Diamond], @@ -573,7 +590,7 @@ impl Parser { ) } - fn factor(&mut self) -> Result { + fn factor(&mut self) -> Result { self.consume_binary_expression_left_associative( Self::exponent, &[ @@ -587,7 +604,7 @@ impl Parser { ) } - fn exponent(&mut self) -> Result { + fn exponent(&mut self) -> Result { self.consume_binary_expression_right_associative( Self::tight_unary, Self::exponent, @@ -595,32 +612,36 @@ impl Parser { ) } - fn tight_unary(&mut self) -> Result { + fn tight_unary(&mut self) -> Result { if let Some(operator_token_loc) = self.consume_token_if(&[Token::Bang, Token::Minus, Token::Tilde]) { let token_span = operator_token_loc.span; let right = self.tight_unary()?; - let span = right.span; + let right_span = self.pool.get(right).span; - Ok(Expression::Call { - function: Box::new( - Expression::Identifier { - name: operator_token_loc.token.to_string(), - resolved: Binding::None, - } - .to_location(token_span), - ), - arguments: vec![right], - } - .to_location(span.merge(token_span))) + let function_id = self.pool.add( + Expression::Identifier { + name: operator_token_loc.token.to_string(), + resolved: Binding::None, + } + .to_location(token_span), + ); + + Ok(self.pool.add( + Expression::Call { + function: function_id, + arguments: vec![right], + } + .to_location(right_span.merge(token_span)), + )) } else { self.operand() } } - fn operand(&mut self) -> Result { + fn operand(&mut self) -> Result { let mut expr = self.primary()?; // This loop handles the following cases: @@ -639,29 +660,31 @@ impl Parser { let Expression::Tuple { values: arguments } = arguments.expression else { unreachable!("self.tuple() must always produce a tuple"); }; + let arguments: Vec = + arguments.into_iter().map(|a| self.pool.add(a)).collect(); - let span = expr.span; + let span = self.pool.get(expr).span; - expr = ExpressionLocation { - expression: Expression::Call { - function: Box::new(expr), + expr = self.pool.add( + Expression::Call { + function: expr, arguments, - }, - span: span.merge(arguments_span), - }; + } + .to_location(span.merge(arguments_span)), + ); } Token::Dot => { self.require_current_token_matches(&Token::Dot)?; // consume matched token - let l_value = self.require_identifier()?; - let identifier_span = l_value.span; - let first_argument_span = expr.span; - let identifier = Lvalue::try_from(l_value)?; - let Lvalue::Identifier { identifier, .. } = identifier else { - unreachable!("Guaranteed to match by previous call to require_identifier") + let identifier_ref = self.require_identifier()?; + let identifier_span = self.pool.get(identifier_ref).span; + let first_argument_span = self.pool.get(expr).span; + let identifier = match &self.pool.get(identifier_ref).expression { + Expression::Identifier { name, .. } => name.clone(), + _ => unreachable!("require_identifier guarantees an identifier"), }; // () is now optional? - let (mut arguments, tuple_span) = + let (extra_arguments, tuple_span) = if self.match_token(&[Token::LeftParentheses]).is_some() { let tuple_expression = self.delimited_tuple(Self::single_expression)?; @@ -676,23 +699,28 @@ impl Parser { (Vec::new(), None) }; - arguments.insert(0, expr); - - expr = ExpressionLocation { - expression: Expression::Call { - function: Box::new( - Expression::Identifier { - name: identifier, - resolved: Binding::None, - } - .to_location(identifier_span), - ), + let mut arguments: Vec = vec![expr]; + arguments.extend(extra_arguments.into_iter().map(|a| self.pool.add(a))); + + let function_id = self.pool.add( + Expression::Identifier { + name: identifier, + resolved: Binding::None, + } + .to_location(identifier_span), + ); + + expr = self.pool.add( + Expression::Call { + function: function_id, arguments, - }, - span: tuple_span - .unwrap_or(identifier_span) - .merge(first_argument_span), - } + } + .to_location( + tuple_span + .unwrap_or(identifier_span) + .merge(first_argument_span), + ), + ); // for now, we require parentheses } @@ -724,15 +752,16 @@ impl Parser { let end_token = self.require_current_token_matches(&Token::RightSquareBracket)?; - let span = expr.span.merge(end_token.span); + let span = self.pool.get(expr).span.merge(end_token.span); + let index_ref = self.pool.add(index_expression); - expr = ExpressionLocation { - expression: Expression::Index { - value: Box::new(expr), - index: Box::new(index_expression), - }, - span, - }; + expr = self.pool.add( + Expression::Index { + value: expr, + index: index_ref, + } + .to_location(span), + ); } _ => unreachable!("guaranteed to match"), } @@ -794,7 +823,7 @@ impl Parser { } // WOAH, this is not a list, it's a list comprehension Some(Token::For) => { - let result = ForBody::List(expr.simplify()); + let result = ForBody::List(self.pool.add(expr.simplify())); self.for_comprehension(left_square_bracket_span, result, &Token::RightSquareBracket) } _ => { @@ -876,22 +905,26 @@ impl Parser { } #[allow(clippy::too_many_lines)] // WERE BUILDING A PROGRAMMING LANGUAGE CLIPPY WHAT DO YOU WANT? - fn primary(&mut self) -> Result { + fn primary(&mut self) -> Result { // matches if expression like `if a < b { } else { }` if self.consume_token_if(&[Token::If]).is_some() { - return self.if_expression(); + let loc = self.if_expression()?; + return Ok(self.pool.add(loc)); } // matches while loops like `while foo < bar { }` else if self.consume_token_if(&[Token::While]).is_some() { - return self.while_expression(); + let loc = self.while_expression()?; + return Ok(self.pool.add(loc)); } // matches for loops like `for x in xs { }` else if self.match_token(&[Token::For]).is_some() { - return self.for_expression(); + let loc = self.for_expression()?; + return Ok(self.pool.add(loc)); } // matches function declarations like `fn function_name(arg1, arg2) { }` else if self.match_token(&[Token::Fn, Token::Pure]).is_some() { - return self.function_declaration(); + let loc = self.function_declaration()?; + return Ok(self.pool.add(loc)); } // matches `return;` and `return (expression);` else if let Some(return_token_location) = self.consume_token_if(&[Token::Return]) { @@ -902,45 +935,46 @@ impl Parser { }; let span = expr_loc.span; + let value = self.pool.add(expr_loc); - let return_expression = Expression::Return { - value: Box::new(expr_loc), - } - .to_location(return_token_location.span.merge(span)); + let return_expression = Expression::Return { value } + .to_location(return_token_location.span.merge(span)); - return Ok(return_expression); + return Ok(self.pool.add(return_expression)); } else if let Some(token_location) = self.consume_token_if(&[Token::Break]) { - let expression = Expression::Break; - return Ok(expression.to_location(token_location.span)); + return Ok(self.pool.add(Expression::Break.to_location(token_location.span))); } else if let Some(token_location) = self.consume_token_if(&[Token::Continue]) { - let expression = Expression::Continue; - return Ok(expression.to_location(token_location.span)); + return Ok(self.pool.add(Expression::Continue.to_location(token_location.span))); } // matches curly bracketed block expression `{ }` else if self.match_token(&[Token::LeftCurlyBracket]).is_some() { - return self.block(); + let loc = self.block()?; + return Ok(self.pool.add(loc)); } // matches map expression %{1,2,3} else if self.match_token(&[Token::MapOpen]).is_some() { - return self.map_expression(); + let loc = self.map_expression()?; + return Ok(self.pool.add(loc)); } // matches list and list comprehensions else if self.match_token(&[Token::LeftSquareBracket]).is_some() { - return self.list(); + let loc = self.list()?; + return Ok(self.pool.add(loc)); } // matches either a grouped expression `(1+1)` or a tuple `(1,1)` else if let Some(start_parentheses) = self.consume_token_if(&[Token::LeftParentheses]) { // If an opening parentheses is immediately followed by a closing parentheses we're dealing with a Unit expression if let Some(end_parentheses) = self.consume_token_if(&[Token::RightParentheses]) { - return Ok(Expression::Tuple { values: vec![] } - .to_location(start_parentheses.span.merge(end_parentheses.span))); + let loc = Expression::Tuple { values: vec![] } + .to_location(start_parentheses.span.merge(end_parentheses.span)); + return Ok(self.pool.add(loc)); } let grouped = self.expression()?; self.require_current_token_matches(&Token::RightParentheses)?; - return Ok(grouped); + return Ok(self.pool.add(grouped)); } let token_location = self.require_current_token()?; @@ -970,7 +1004,7 @@ impl Parser { } }; - Ok(expression.to_location(token_location.span)) + Ok(self.pool.add(expression.to_location(token_location.span))) } /// Parses if expression without the `if` token @@ -986,38 +1020,39 @@ impl Parser { /// ``` fn if_expression(&mut self) -> Result { let expression = self.expression()?; - let on_true = self.block()?; + let span = expression.span; + let condition = self.pool.add(expression); + let on_true_loc = self.block()?; + let on_true = self.pool.add(on_true_loc); let on_false = if self.consume_token_if(&[Token::Else]).is_some() { if self.consume_token_if(&[Token::If]).is_some() { - let expression = self.if_expression()?; - Some(Box::new(expression)) + let loc = self.if_expression()?; + Some(self.pool.add(loc)) } else { - Some(Box::new(self.block()?)) + let loc = self.block()?; + Some(self.pool.add(loc)) } } else { None }; - let span = expression.span; Ok(Expression::If { - condition: Box::new(expression), - on_true: Box::new(on_true), + condition, + on_true, on_false, } .to_location(span)) } fn while_expression(&mut self) -> Result { - let expression = self.expression()?; - let loop_body = self.block()?; + let expression_loc = self.expression()?; + let span = expression_loc.span; + let expression = self.pool.add(expression_loc); + let block = self.block()?; + let loop_body = self.pool.add(block); - let span = expression.span; - Ok(Expression::While { - expression: Box::new(expression), - loop_body: Box::new(loop_body), - } - .to_location(span)) + Ok(Expression::While { expression, loop_body }.to_location(span)) } fn for_expression(&mut self) -> Result { @@ -1042,29 +1077,27 @@ impl Parser { let body = self.block()?; let body_span = body.span; + let body_ref = self.pool.add(body); Ok(Expression::For { iterations, - body: Box::new(ForBody::Block(body)), + body: Box::new(ForBody::Block(body_ref)), } .to_location(for_token_span.merge(body_span))) } - fn require_identifier(&mut self) -> Result { - let identifier_expression = self.primary()?; + fn require_identifier(&mut self) -> Result { + let identifier_ref = self.primary()?; if matches!( - identifier_expression, - ExpressionLocation { - expression: Expression::Identifier { .. }, - .. - } + self.pool.get(identifier_ref).expression, + Expression::Identifier { .. } ) { - Ok(identifier_expression) + Ok(identifier_ref) } else { Err(Error::text( "expected an identifier".to_string(), - identifier_expression.span, + self.pool.get(identifier_ref).span, )) } } @@ -1104,14 +1137,11 @@ impl Parser { let identifier = match self.peek_current_token() { Some(Token::LeftParentheses) => None, Some(Token::Identifier(_)) => { - let Ok(ExpressionLocation { - expression: Expression::Identifier { name, .. }, - .. - }) = self.require_identifier() - else { - unreachable!("guaranteed to to produce identifier") + let identifier_ref = self.require_identifier()?; + let name = match &self.pool.get(identifier_ref).expression { + Expression::Identifier { name, .. } => name.clone(), + _ => unreachable!("require_identifier guarantees an identifier"), }; - Some(name) } // MUST BE IDENT Some(_) | None => { @@ -1129,12 +1159,15 @@ impl Parser { // Next we either expect a body block `{ ... }` or a fat arrow followed by a single expression `=> ...` - let body = match self.peek_current_token() { + let body: ExpressionRef = match self.peek_current_token() { Some(Token::FatArrow) => { self.advance(); self.single_expression()? } - Some(Token::LeftCurlyBracket) => self.block()?, + Some(Token::LeftCurlyBracket) => { + let block = self.block()?; + self.pool.add(block) + } Some(token) => { return Err(Error::with_help( format!("unexpected token: {token}"), @@ -1145,12 +1178,13 @@ impl Parser { None => return Err(Error::end_of_input(argument_list.span)), }; - let span = fn_token.span.merge(body.span); + let span = fn_token.span.merge(self.pool.get(body).span); + let parameters = self.pool.add(argument_list); Ok(ExpressionLocation { expression: Expression::FunctionDeclaration { name: identifier, - parameters: Box::new(argument_list), - body: Box::new(body), + parameters, + body, return_type: None, // At some point in the future we could use type declarations here to insert the type (return type inference is cringe anyway) pure: is_pure, resolved_name: None, @@ -1196,19 +1230,19 @@ impl Parser { let map_open_span = self.require_token(&[Token::MapOpen])?; // Optional default value - let default = if self.consume_token_if(&[Token::Colon]).is_some() { + let default: Option = if self.consume_token_if(&[Token::Colon]).is_some() { let default = self.single_expression()?; // If the list ends without any values we just do nothing and let the loop below handle the rest if self.match_token(&[Token::RightCurlyBracket]).is_none() { // If there isn't a '}' to close the map there must be a comma otherwise we error out self.require_current_token_matches(&Token::Comma)?; } - Some(Box::new(default)) + Some(default) } else { None }; - let mut values = Vec::new(); + let mut values: Vec<(ExpressionLocation, Option)> = Vec::new(); let map_close_span = loop { // End parsing if we see a RightCurlyBracket, this one only happens if the expression is @@ -1217,10 +1251,12 @@ impl Parser { break token_location.span; } - let key = self.single_expression()?; + let key_ref = self.single_expression()?; + let key = self.pool.get(key_ref).clone(); if self.consume_token_if(&[Token::Colon]).is_some() { - let value = self.single_expression()?; + let value_ref = self.single_expression()?; + let value = self.pool.get(value_ref).clone(); values.push((key, Some(value))); } else { values.push((key, None)); @@ -1234,12 +1270,14 @@ impl Parser { let (key_expr, value_expr) = values .pop() .expect("guaranteed by previous call to values.len()"); + let key = self.pool.add(key_expr); + let value = value_expr.map(|v| self.pool.add(v)); return self.for_comprehension( map_open_span, ForBody::Map { - key: key_expr, - value: value_expr, - default, + key, + value, + default: default.map(Box::new), }, &Token::RightCurlyBracket, ); From 079dda318c2f9f795fac72f5f556e0434a7bdd1f Mon Sep 17 00:00:00 2001 From: Tim Fennis Date: Sun, 1 Mar 2026 18:21:06 +0100 Subject: [PATCH 2/5] =?UTF-8?q?=F0=9F=9A=A7=20Migrate=20interpreter=20and?= =?UTF-8?q?=20LSP=20to=20ExpressionPool/PoolWalker-based=20evaluation?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- ndc_lib/src/interpreter/evaluate/flat.rs | 77 +++ ndc_lib/src/interpreter/evaluate/index.rs | 27 +- ndc_lib/src/interpreter/evaluate/mod.rs | 293 ++++++----- ndc_lib/src/interpreter/function.rs | 9 +- ndc_lib/src/interpreter/mod.rs | 68 +-- ndc_lib/src/interpreter/semantic/analyser.rs | 524 +++++++++++-------- ndc_lsp/src/backend.rs | 76 +-- ndc_parser/src/expression.rs | 118 +++-- ndc_parser/src/lib.rs | 3 +- ndc_parser/src/parser.rs | 463 ++++++++-------- 10 files changed, 953 insertions(+), 705 deletions(-) create mode 100644 ndc_lib/src/interpreter/evaluate/flat.rs diff --git a/ndc_lib/src/interpreter/evaluate/flat.rs b/ndc_lib/src/interpreter/evaluate/flat.rs new file mode 100644 index 00000000..dd49ff8f --- /dev/null +++ b/ndc_lib/src/interpreter/evaluate/flat.rs @@ -0,0 +1,77 @@ +use crate::interpreter::InterpreterError; +use crate::interpreter::environment::Environment; +use crate::interpreter::evaluate::{EvaluationError, LiftEvaluationResult, PoolWalker}; +use crate::interpreter::function::FunctionCarrier; +use crate::interpreter::int::Int; +use crate::interpreter::num::Number; +use crate::interpreter::value::Value; +use ndc_parser::{Expression, ExpressionPool}; +use std::cell::RefCell; +use std::rc::Rc; + +fn evaluate_flat( + pool: ExpressionPool, + environment: &Rc>, +) -> Result { + let pool = Rc::new(pool); + + let mut value = Value::unit(); + let mut state: Vec = Vec::with_capacity(pool.len()); + for (idx, expr) in pool.iter().enumerate() { + match &expr.expression { + Expression::BoolLiteral(v) => state[idx] = Value::Bool(*v), + Expression::StringLiteral(s) => state[idx] = Value::string(s), + Expression::Int64Literal(i) => state[idx] = Value::from(*i), + Expression::Float64Literal(f) => state[idx] = Value::from(*f), + Expression::BigIntLiteral(i) => { + state[idx] = Value::Number(Number::Int(Int::BigInt(i.clone()))) + } // TODO: mem take? + Expression::ComplexLiteral(c) => state[idx] = Value::Number(Number::Complex(c.clone())), + Expression::Identifier { .. } => {} + Expression::Statement(_) => {} + Expression::Logical { .. } => {} + Expression::Grouping(_) => {} + Expression::VariableDeclaration { .. } => {} + Expression::Assignment { .. } => {} + Expression::OpAssignment { .. } => {} + Expression::FunctionDeclaration { .. } => {} + Expression::Block { .. } => {} + Expression::If { .. } => {} + Expression::While { .. } => {} + Expression::For { .. } => {} + Expression::Call { + function, + arguments, + } => { + let mut arguments: Vec<_> = arguments + .into_iter() + .map(|arg| state.remove(arg.as_usize())) + .collect(); + + let function = &state[function.as_usize()]; + + if let Value::Function(function) = function { + state[idx] = function + .call(&mut arguments, environment) + .add_span(expr.span)? + } else { + return Err(FunctionCarrier::EvaluationError(EvaluationError::new( + format!("Unable to invoke {} as a function.", function.static_type()), + expr.span, + ))); + } + } + Expression::Index { .. } => {} + Expression::Tuple { .. } => {} + Expression::List { .. } => {} + Expression::Map { .. } => {} + Expression::Return { .. } => {} + Expression::Break => {} + Expression::Continue => {} + Expression::RangeInclusive { .. } => {} + Expression::RangeExclusive { .. } => {} + } + } + + Ok(Value::unit()) +} diff --git a/ndc_lib/src/interpreter/evaluate/index.rs b/ndc_lib/src/interpreter/evaluate/index.rs index cb31813d..8bc64c9c 100644 --- a/ndc_lib/src/interpreter/evaluate/index.rs +++ b/ndc_lib/src/interpreter/evaluate/index.rs @@ -9,12 +9,12 @@ //! | Backward index | -10 | -9 | -8 | -7 | -6 | -5 | -4 | -3 | -2 | -1 | //! +----------------+-----+----+----+----+----+----+----+----+----+----+ -use super::{EvaluationError, EvaluationResult, IntoEvaluationResult, evaluate_expression}; +use super::{EvaluationError, EvaluationResult, IntoEvaluationResult, PoolWalker, evaluate_expression}; use crate::interpreter::environment::Environment; use crate::interpreter::{function::FunctionCarrier, sequence::Sequence, value::Value}; use itertools::Itertools; use ndc_lexer::Span; -use ndc_parser::{Expression, ExpressionLocation}; +use ndc_parser::Expression; use std::cell::RefCell; use std::cmp::min; use std::ops::IndexMut; @@ -61,20 +61,23 @@ impl EvaluatedIndex { } pub(crate) fn evaluate_as_index( - expression_location: &ExpressionLocation, + walker: PoolWalker, environment: &Rc>, ) -> Result { - let (range_start, range_end, inclusive) = match expression_location.expression { + let expression_location = walker.current(); + let span = expression_location.span; + + let (range_start, range_end, inclusive) = match &expression_location.expression { Expression::RangeExclusive { - start: ref range_start, - end: ref range_end, + start: range_start, + end: range_end, } => (range_start, range_end, false), Expression::RangeInclusive { - start: ref range_start, - end: ref range_end, + start: range_start, + end: range_end, } => (range_start, range_end, true), _ => { - let result = evaluate_expression(expression_location, environment)?; + let result = evaluate_expression(walker, environment)?; return Ok(EvaluatedIndex::Index(result)); } }; @@ -82,19 +85,19 @@ pub(crate) fn evaluate_as_index( if inclusive && range_end.is_none() { return Err(EvaluationError::new( "inclusive ranges must have an end".to_string(), - expression_location.span, + span, ) .into()); } let start = if let Some(range_start) = range_start { - Some(evaluate_expression(range_start, environment)?) + Some(evaluate_expression(walker.resolve(*range_start), environment)?) } else { None }; let end = if let Some(range_end) = range_end { - Some(evaluate_expression(range_end, environment)?) + Some(evaluate_expression(walker.resolve(*range_end), environment)?) } else { None }; diff --git a/ndc_lib/src/interpreter/evaluate/mod.rs b/ndc_lib/src/interpreter/evaluate/mod.rs index 9a41c094..813e952a 100644 --- a/ndc_lib/src/interpreter/evaluate/mod.rs +++ b/ndc_lib/src/interpreter/evaluate/mod.rs @@ -10,7 +10,8 @@ use index::{Offset, evaluate_as_index, get_at_index, set_at_index}; use itertools::Itertools; use ndc_lexer::Span; use ndc_parser::{ - Binding, Expression, ExpressionLocation, ForBody, ForIteration, LogicalOperator, Lvalue, + Binding, Expression, ExpressionLocation, ExpressionPool, ExpressionRef, ForBody, ForIteration, + LogicalOperator, Lvalue, }; use std::cell::RefCell; use std::fmt; @@ -18,13 +19,34 @@ use std::rc::Rc; pub type EvaluationResult = Result; +mod flat; mod index; +#[derive(Clone)] +pub(crate) struct PoolWalker { + pub(crate) cur: ExpressionRef, + pub(crate) pool: Rc, +} + +impl PoolWalker { + pub(crate) fn current(&self) -> &ExpressionLocation { + self.pool.get(self.cur) + } + + pub(crate) fn resolve(&self, r: ExpressionRef) -> PoolWalker { + PoolWalker { + cur: r, + pool: Rc::clone(&self.pool), + } + } +} + #[allow(clippy::too_many_lines)] pub(crate) fn evaluate_expression( - expression_location: &ExpressionLocation, + pool_walker: PoolWalker, environment: &Rc>, ) -> EvaluationResult { + let expression_location: &ExpressionLocation = pool_walker.current(); let span = expression_location.span; let literal: Value = match &expression_location.expression { Expression::BoolLiteral(b) => Value::Bool(*b), @@ -33,7 +55,7 @@ pub(crate) fn evaluate_expression( Expression::BigIntLiteral(n) => Value::Number(Number::Int(Int::BigInt(n.clone()))), Expression::Float64Literal(n) => Value::Number(Number::Float(*n)), Expression::ComplexLiteral(n) => Value::Number(Number::Complex(*n)), - Expression::Grouping(expr) => evaluate_expression(expr, environment)?, + Expression::Grouping(expr) => evaluate_expression(pool_walker.resolve(*expr), environment)?, Expression::Identifier { name, resolved } => { if name == "None" { return Ok(Value::none()); @@ -46,8 +68,8 @@ pub(crate) fn evaluate_expression( } } Expression::VariableDeclaration { l_value, value } => { - let value = evaluate_expression(value, environment)?; - declare_or_assign_variable(l_value, value, environment, span)?; + let value = evaluate_expression(pool_walker.resolve(*value), environment)?; + declare_or_assign_variable(l_value, value, environment, span, &pool_walker)?; Value::unit() } Expression::Assignment { @@ -55,21 +77,22 @@ pub(crate) fn evaluate_expression( r_value: value, } => match l_value { l_value @ (Lvalue::Identifier { .. } | Lvalue::Sequence(_)) => { - let value = evaluate_expression(value, environment)?; - declare_or_assign_variable(l_value, value, environment, span)? + let value = evaluate_expression(pool_walker.resolve(*value), environment)?; + declare_or_assign_variable(l_value, value, environment, span, &pool_walker)? } Lvalue::Index { value: lhs_expression, index: index_expression, } => { - let mut lhs = evaluate_expression(lhs_expression, environment)?; + let mut lhs = + evaluate_expression(pool_walker.resolve(*lhs_expression), environment)?; // the computation of this value may need the list that we assign to, // therefore the value needs to be computed before we mutably borrow the list // see: `bug0001_in_place_map.ndct` - let rhs = evaluate_expression(value, environment)?; + let rhs = evaluate_expression(pool_walker.resolve(*value), environment)?; - let index = evaluate_as_index(index_expression, environment)?; + let index = evaluate_as_index(pool_walker.resolve(*index_expression), environment)?; set_at_index(&mut lhs, rhs, index, span)?; @@ -90,7 +113,7 @@ pub(crate) fn evaluate_expression( .. } => { let resolved_l_value = resolved_l_value.expect("lvalue must be resolved"); - let rhs = evaluate_expression(r_value, environment)?; + let rhs = evaluate_expression(pool_walker.resolve(*r_value), environment)?; // TODO: this statement does damage which isn't reverted when for instance we can't find the function let Some(lhs) = environment.borrow_mut().take(resolved_l_value) else { @@ -120,7 +143,6 @@ pub(crate) fn evaluate_expression( "the resolver pass should have guaranteed that the operation points to a function" ); }; - // (&func, &mut arguments, environment, span) let result = match func.call_checked(&mut arguments, environment) { Err(FunctionCarrier::FunctionTypeMismatch) if operations_to_try.peek().is_none() => @@ -163,12 +185,15 @@ pub(crate) fn evaluate_expression( value: lhs_expression, index: index_expression, } => { - let mut lhs_value = evaluate_expression(lhs_expression, environment)?; - let index = evaluate_as_index(index_expression, environment)?; + let mut lhs_value = + evaluate_expression(pool_walker.resolve(*lhs_expression), environment)?; + let index = + evaluate_as_index(pool_walker.resolve(*index_expression), environment)?; let value_at_index = get_at_index(&lhs_value, index.clone(), span, environment)?; - let right_value = evaluate_expression(r_value, environment)?; + let right_value = + evaluate_expression(pool_walker.resolve(*r_value), environment)?; let types = [value_at_index.static_type(), right_value.static_type()]; let mut operations_to_try = [ @@ -241,7 +266,7 @@ pub(crate) fn evaluate_expression( let mut value = Value::unit(); for stm in statements { - value = evaluate_expression(stm, &local_scope)?; + value = evaluate_expression(pool_walker.resolve(*stm), &local_scope)?; } drop(local_scope); @@ -252,11 +277,15 @@ pub(crate) fn evaluate_expression( on_true, on_false, } => { - let result = evaluate_expression(condition, environment)?; + let result = evaluate_expression(pool_walker.resolve(*condition), environment)?; match (result, on_false) { - (Value::Bool(true), _) => evaluate_expression(on_true, environment)?, - (Value::Bool(false), Some(block)) => evaluate_expression(block, environment)?, + (Value::Bool(true), _) => { + evaluate_expression(pool_walker.resolve(*on_true), environment)? + } + (Value::Bool(false), Some(block)) => { + evaluate_expression(pool_walker.resolve(*block), environment)? + } (Value::Bool(false), None) => Value::unit(), (value, _) => { return Err(EvaluationError::new( @@ -272,7 +301,7 @@ pub(crate) fn evaluate_expression( } } Expression::Statement(expression) => { - evaluate_expression(expression, environment)?; + evaluate_expression(pool_walker.resolve(*expression), environment)?; Value::unit() } Expression::Logical { @@ -280,11 +309,11 @@ pub(crate) fn evaluate_expression( left, right, } => { - let left = evaluate_expression(left, environment)?; + let left = evaluate_expression(pool_walker.resolve(*left), environment)?; match (operator, left) { (LogicalOperator::And, Value::Bool(true)) | (LogicalOperator::Or, Value::Bool(false)) => { - evaluate_expression(right, environment)? + evaluate_expression(pool_walker.resolve(*right), environment)? } (LogicalOperator::And, Value::Bool(false)) => Value::Bool(false), (LogicalOperator::Or, Value::Bool(true)) => Value::Bool(true), @@ -305,9 +334,9 @@ pub(crate) fn evaluate_expression( loop_body, } => { loop { - let lit = evaluate_expression(expression, environment)?; + let lit = evaluate_expression(pool_walker.resolve(*expression), environment)?; if lit == Value::Bool(true) { - let result = evaluate_expression(loop_body, environment); + let result = evaluate_expression(pool_walker.resolve(*loop_body), environment); match result { Err(FunctionCarrier::Break(value)) => return Ok(value), Err(FunctionCarrier::Continue) | Ok(_) => {} @@ -332,11 +361,16 @@ pub(crate) fn evaluate_expression( let mut evaluated_args = Vec::new(); for argument in arguments { - let arg = evaluate_expression(argument, environment)?; + let arg = evaluate_expression(pool_walker.resolve(*argument), environment)?; evaluated_args.push(arg); } - resolve_and_call(function, evaluated_args, environment, span)? + resolve_and_call( + &pool_walker.resolve(*function), + evaluated_args, + environment, + span, + )? } Expression::FunctionDeclaration { parameters, @@ -346,13 +380,27 @@ pub(crate) fn evaluate_expression( pure, .. } => { + let params_walker = pool_walker.resolve(*parameters); + let params_loc = params_walker.current(); + let parameter_names: Vec = + if let Expression::Tuple { values } = ¶ms_loc.expression { + values + .iter() + .map(|param_ref| { + pool_walker + .resolve(*param_ref) + .current() + .as_identifier() + .to_string() + }) + .collect() + } else { + panic!("function parameters must be a tuple expression") + }; + let mut user_function = FunctionBody::Closure { - parameter_names: parameters - .as_parameters() - .into_iter() - .map(|x| x.to_string()) - .collect(), - body: *body.clone(), + parameter_names, + body: pool_walker.resolve(*body), return_type: return_type.clone().unwrap_or_else(StaticType::unit), environment: environment.clone(), }; @@ -380,7 +428,10 @@ pub(crate) fn evaluate_expression( Expression::Tuple { values } => { let mut out_values = Vec::with_capacity(values.len()); for value in values { - out_values.push(evaluate_expression(value, environment)?); + out_values.push(evaluate_expression( + pool_walker.resolve(*value), + environment, + )?); } Value::Sequence(Sequence::Tuple(Rc::new(out_values))) @@ -388,7 +439,7 @@ pub(crate) fn evaluate_expression( Expression::List { values } => { let mut values_out = Vec::with_capacity(values.len()); for expression in values { - let v = evaluate_expression(expression, environment)?; + let v = evaluate_expression(pool_walker.resolve(*expression), environment)?; values_out.push(v); } Value::Sequence(Sequence::List(Rc::new(RefCell::new(values_out)))) @@ -396,9 +447,9 @@ pub(crate) fn evaluate_expression( Expression::Map { values, default } => { let mut hashmap = HashMap::with_capacity(values.len()); for (key, value) in values { - let key = evaluate_expression(key, environment)?; + let key = evaluate_expression(pool_walker.resolve(*key), environment)?; let value = if let Some(value) = value { - evaluate_expression(value, environment)? + evaluate_expression(pool_walker.resolve(*value), environment)? } else { Value::unit() }; @@ -407,7 +458,10 @@ pub(crate) fn evaluate_expression( } let default = if let Some(default) = default { - Some(Box::new(evaluate_expression(default, environment)?)) + Some(Box::new(evaluate_expression( + pool_walker.resolve(*default), + environment, + )?)) } else { None }; @@ -416,8 +470,14 @@ pub(crate) fn evaluate_expression( } Expression::For { iterations, body } => { let mut out_values = Vec::new(); - let result = - execute_for_iterations(iterations, body, &mut out_values, environment, span); + let result = execute_for_iterations( + iterations, + body, + &mut out_values, + &pool_walker, + environment, + span, + ); match result { Err(FunctionCarrier::Break(break_value)) => return Ok(break_value), @@ -443,14 +503,17 @@ pub(crate) fn evaluate_expression( )), default .as_ref() - .map(|default| evaluate_expression(default, environment).map(Box::new)) + .map(|default| { + evaluate_expression(pool_walker.resolve(**default), environment) + .map(Box::new) + }) .transpose()?, )), } } Expression::Return { value } => { return Err(FunctionCarrier::Return(evaluate_expression( - value, + pool_walker.resolve(*value), environment, )?)); } @@ -460,14 +523,16 @@ pub(crate) fn evaluate_expression( value: lhs_expr, index: index_expr, } => { - let lhs_value = evaluate_expression(lhs_expr, environment)?; + let lhs_span = pool_walker.resolve(*lhs_expr).current().span; + let index_span = pool_walker.resolve(*index_expr).current().span; + let lhs_value = evaluate_expression(pool_walker.resolve(*lhs_expr), environment)?; match lhs_value { Value::Sequence(Sequence::String(string)) => { let string = string.borrow(); - let index = evaluate_as_index(index_expr, environment)? - .try_into_offset(string.chars().count(), index_expr.span)?; + let index = evaluate_as_index(pool_walker.resolve(*index_expr), environment)? + .try_into_offset(string.chars().count(), index_span)?; let (start, end) = index.into_tuple(); let new = string @@ -481,15 +546,15 @@ pub(crate) fn evaluate_expression( Value::Sequence(Sequence::List(list)) => { let list_length = list.borrow().len(); - let index = evaluate_as_index(index_expr, environment)? - .try_into_offset(list_length, index_expr.span)?; + let index = evaluate_as_index(pool_walker.resolve(*index_expr), environment)? + .try_into_offset(list_length, index_span)?; match index { Offset::Element(usize_index) => { let list = list.borrow(); let Some(value) = list.get(usize_index) else { return Err( - EvaluationError::out_of_bounds(index, index_expr.span).into() + EvaluationError::out_of_bounds(index, index_span).into() ); }; value.clone() @@ -498,7 +563,7 @@ pub(crate) fn evaluate_expression( let list = list.borrow(); let Some(values) = list.get(from_usize..to_usize) else { return Err( - EvaluationError::out_of_bounds(index, index_expr.span).into() + EvaluationError::out_of_bounds(index, index_span).into() ); }; @@ -507,14 +572,14 @@ pub(crate) fn evaluate_expression( } } Value::Sequence(Sequence::Tuple(tuple)) => { - let index = evaluate_as_index(index_expr, environment)? - .try_into_offset(tuple.len(), index_expr.span)?; + let index = evaluate_as_index(pool_walker.resolve(*index_expr), environment)? + .try_into_offset(tuple.len(), index_span)?; match index { Offset::Element(index_usize) => { let Some(value) = tuple.get(index_usize) else { return Err( - EvaluationError::out_of_bounds(index, index_expr.span).into() + EvaluationError::out_of_bounds(index, index_span).into() ); }; @@ -524,7 +589,7 @@ pub(crate) fn evaluate_expression( Offset::Range(from_usize, to_usize) => { let Some(values) = tuple.get(from_usize..to_usize) else { return Err( - EvaluationError::out_of_bounds(index, index_expr.span).into() + EvaluationError::out_of_bounds(index, index_span).into() ); }; @@ -535,15 +600,15 @@ pub(crate) fn evaluate_expression( Value::Sequence(Sequence::Deque(deque)) => { let list_length = deque.borrow().len(); - let index = evaluate_as_index(index_expr, environment)? - .try_into_offset(list_length, index_expr.span)?; + let index = evaluate_as_index(pool_walker.resolve(*index_expr), environment)? + .try_into_offset(list_length, index_span)?; match index { Offset::Element(usize_index) => { let list = deque.borrow(); let Some(value) = list.get(usize_index) else { return Err( - EvaluationError::out_of_bounds(index, index_expr.span).into() + EvaluationError::out_of_bounds(index, index_span).into() ); }; value.clone() @@ -562,7 +627,7 @@ pub(crate) fn evaluate_expression( } } Value::Sequence(Sequence::Map(dict, default)) => { - let key = evaluate_expression(index_expr, environment)?; + let key = evaluate_expression(pool_walker.resolve(*index_expr), environment)?; // let dict = dict.borrow(); let value = { dict.borrow().get(&key).cloned() }; @@ -576,7 +641,7 @@ pub(crate) fn evaluate_expression( // NOTE: this span points at the entire expression instead of the // function that cannot be executed because we don't have that span here // maybe we can check the function signature earlier when we do have the span - lhs_expr.span.merge(index_expr.span), + lhs_span.merge(index_span), )?; // TODO: This borrow_mut can fail, handle it better!! @@ -585,13 +650,13 @@ pub(crate) fn evaluate_expression( Ok(default_value) } else { - Err(EvaluationError::key_not_found(&key, index_expr.span).into()) + Err(EvaluationError::key_not_found(&key, index_span).into()) }; } value => { return Err(EvaluationError::new( format!("cannot index into {}", value.static_type()), - lhs_expr.span, + lhs_span, ) .into()); } @@ -602,7 +667,7 @@ pub(crate) fn evaluate_expression( end: range_end, } => { let range_start = if let Some(range_start) = range_start { - evaluate_expression(range_start, environment)? + evaluate_expression(pool_walker.resolve(*range_start), environment)? } else { return Err(EvaluationError::new( "ranges without a lower bound cannot be evaluated into a value".to_string(), @@ -614,7 +679,7 @@ pub(crate) fn evaluate_expression( let range_start = i64::try_from(range_start).into_evaluation_result(span)?; if let Some(range_end) = range_end { - let range_end = evaluate_expression(range_end, environment)?; + let range_end = evaluate_expression(pool_walker.resolve(*range_end), environment)?; let range_end = i64::try_from(range_end).into_evaluation_result(span)?; Value::from(range_start..=range_end) @@ -627,7 +692,7 @@ pub(crate) fn evaluate_expression( end: range_end, } => { let range_start = if let Some(range_start) = range_start { - evaluate_expression(range_start, environment)? + evaluate_expression(pool_walker.resolve(*range_start), environment)? } else { return Err(EvaluationError::new( "ranges without a lower bound cannot be evaluated into a value".to_string(), @@ -639,7 +704,7 @@ pub(crate) fn evaluate_expression( let range_start = i64::try_from(range_start).into_evaluation_result(span)?; if let Some(range_end) = range_end { - let range_end = evaluate_expression(range_end, environment)?; + let range_end = evaluate_expression(pool_walker.resolve(*range_end), environment)?; let range_end = i64::try_from(range_end).into_evaluation_result(span)?; Value::from(range_start..range_end) @@ -657,6 +722,7 @@ fn declare_or_assign_variable( value: Value, environment: &Rc>, span: Span, + walker: &PoolWalker, ) -> EvaluationResult { match l_value { Lvalue::Identifier { resolved, .. } => { @@ -683,16 +749,16 @@ fn declare_or_assign_variable( let mut iter = l_values.iter().zip(r_values); for (l_value, value) in iter.by_ref() { - declare_or_assign_variable(l_value, value, environment, span)?; + declare_or_assign_variable(l_value, value, environment, span, walker)?; } } Lvalue::Index { value: lhs_expr, index, } => { - let mut lhs = evaluate_expression(lhs_expr, environment)?; + let mut lhs = evaluate_expression(walker.resolve(*lhs_expr), environment)?; - let index = evaluate_as_index(index, environment)?; + let index = evaluate_as_index(walker.resolve(*index), environment)?; set_at_index(&mut lhs, value, index, span)?; } @@ -858,23 +924,24 @@ where fn execute_for_body( body: &ForBody, + pool_walker: &PoolWalker, environment: &Rc>, result: &mut Vec, ) -> EvaluationResult { match body { ForBody::Block(expr) => { - evaluate_expression(expr, environment)?; + evaluate_expression(pool_walker.resolve(*expr), environment)?; } ForBody::List(expr) => { - let value = evaluate_expression(expr, environment)?; + let value = evaluate_expression(pool_walker.resolve(*expr), environment)?; result.push(value); } ForBody::Map { key, value, .. } => { result.push(Value::tuple(vec![ - evaluate_expression(key, environment)?, + evaluate_expression(pool_walker.resolve(*key), environment)?, value .as_ref() - .map(|value| evaluate_expression(value, environment)) + .map(|v| evaluate_expression(pool_walker.resolve(*v), environment)) .transpose()? .unwrap_or(Value::unit()), ])); @@ -891,6 +958,7 @@ fn execute_for_iterations( iterations: &[ForIteration], body: &ForBody, out_values: &mut Vec, + pool_walker: &PoolWalker, environment: &Rc>, span: Span, ) -> Result { @@ -900,7 +968,7 @@ fn execute_for_iterations( match cur { ForIteration::Iteration { l_value, sequence } => { - let mut sequence = evaluate_expression(sequence, environment)?; + let mut sequence = evaluate_expression(pool_walker.resolve(*sequence), environment)?; let iter = mut_value_to_iterator(&mut sequence).into_evaluation_result(span)?; for r_value in iter { @@ -912,75 +980,58 @@ fn execute_for_iterations( // With the current implementation with a new scope declared for every iteration this produces 10 functions // each with their own scope and their own version of `i`, this might potentially be a bit slower though let scope = Rc::new(RefCell::new(Environment::new_scope(environment))); - declare_or_assign_variable(l_value, r_value, &scope, span)?; + declare_or_assign_variable(l_value, r_value, &scope, span, pool_walker)?; if tail.is_empty() { - match execute_for_body(body, &scope, out_values) { + match execute_for_body(body, pool_walker, &scope, out_values) { Err(FunctionCarrier::Continue) => {} Err(error) => return Err(error), Ok(_value) => {} } } else { - execute_for_iterations(tail, body, out_values, &scope, span)?; + execute_for_iterations(tail, body, out_values, pool_walker, &scope, span)?; } } } - ForIteration::Guard(guard) => match evaluate_expression(guard, environment)? { - Value::Bool(true) if tail.is_empty() => { - execute_for_body(body, environment, out_values)?; - } - Value::Bool(true) => { - execute_for_iterations(tail, body, out_values, environment, span)?; - } - Value::Bool(false) => {} - value => { - return Err(EvaluationError::type_error( - format!( - "mismatched types: expected {}, found {}", - StaticType::Bool, - value.static_type(), - ), - span, - ) - .into()); + ForIteration::Guard(guard) => { + match evaluate_expression(pool_walker.resolve(*guard), environment)? { + Value::Bool(true) if tail.is_empty() => { + execute_for_body(body, pool_walker, environment, out_values)?; + } + Value::Bool(true) => { + execute_for_iterations(tail, body, out_values, pool_walker, environment, span)?; + } + Value::Bool(false) => {} + value => { + return Err(EvaluationError::type_error( + format!( + "mismatched types: expected {}, found {}", + StaticType::Bool, + value.static_type(), + ), + span, + ) + .into()); + } } - }, + } } Ok(Value::unit()) } -// fn evaluate_as_function( -// function_expression: &ExpressionLocation, -// arg_types: &[StaticType], -// environment: &Rc>, -// ) -> EvaluationResult { -// let ExpressionLocation { expression, .. } = function_expression; -// -// if let Expression::Identifier { resolved, .. } = expression { -// resolve_dynamic_binding(resolved, arg_types, environment).ok_or_else(|| { -// FunctionCarrier::EvaluationError(EvaluationError::new( -// format!( -// "Failed to find a function that can handle the arguments ({}) at runtime", -// arg_types.iter().join(", ") -// ), -// function_expression.span, -// )) -// }) -// } else { -// evaluate_expression(function_expression, environment) -// } -// } - fn resolve_and_call( - function_expression: &ExpressionLocation, + function_expression: &PoolWalker, mut args: Vec, environment: &Rc>, span: Span, ) -> EvaluationResult { - let ExpressionLocation { expression, .. } = function_expression; + let fn_loc = function_expression.current(); + let fn_span = fn_loc.span; - let function_as_value = if let Expression::Identifier { name, resolved, .. } = expression { + let function_as_value = if let Expression::Identifier { name, resolved, .. } = + &fn_loc.expression + { let arg_types = args.iter().map(|arg| arg.static_type()).collect::>(); let opt = match resolved { @@ -1030,14 +1081,14 @@ fn resolve_and_call( opt.ok_or_else(|| { FunctionCarrier::EvaluationError(EvaluationError::new( format!( - "no function called '{name}' found matches the arguments: ({})", + "no function called '{name}' found that matches the arguments: ({})", arg_types.iter().join(", ") ), - function_expression.span, + fn_span, )) })? } else { - evaluate_expression(function_expression, environment)? + evaluate_expression(function_expression.clone(), environment)? }; if let Value::Function(function) = function_as_value { diff --git a/ndc_lib/src/interpreter/function.rs b/ndc_lib/src/interpreter/function.rs index a8e93a1a..2ae6ef7b 100644 --- a/ndc_lib/src/interpreter/function.rs +++ b/ndc_lib/src/interpreter/function.rs @@ -1,14 +1,14 @@ use crate::hash_map::{DefaultHasher, HashMap}; use crate::interpreter::environment::Environment; use crate::interpreter::evaluate::{ - ErrorConverter, EvaluationError, EvaluationResult, evaluate_expression, + ErrorConverter, EvaluationError, EvaluationResult, PoolWalker, evaluate_expression, }; use crate::interpreter::num::{BinaryOperatorError, Number}; use crate::interpreter::sequence::Sequence; use crate::interpreter::value::Value; use derive_builder::Builder; use ndc_lexer::Span; -use ndc_parser::{ExpressionLocation, ResolvedVar}; +use ndc_parser::ResolvedVar; pub use ndc_parser::{Parameter, StaticType, TypeSignature}; use std::cell::{BorrowError, BorrowMutError, RefCell}; use std::fmt; @@ -156,10 +156,11 @@ impl Function { } #[derive(Clone)] +#[allow(private_interfaces)] pub enum FunctionBody { Closure { parameter_names: Vec, - body: ExpressionLocation, + body: PoolWalker, return_type: StaticType, environment: Rc>, }, @@ -259,7 +260,7 @@ impl FunctionBody { } let local_scope = Rc::new(RefCell::new(local_scope)); - match evaluate_expression(body, &local_scope) { + match evaluate_expression(body.clone(), &local_scope) { Err(FunctionCarrier::Return(v)) => Ok(v), r => r, } diff --git a/ndc_lib/src/interpreter/mod.rs b/ndc_lib/src/interpreter/mod.rs index e09c6ffb..f8430521 100644 --- a/ndc_lib/src/interpreter/mod.rs +++ b/ndc_lib/src/interpreter/mod.rs @@ -1,13 +1,15 @@ -use std::cell::RefCell; -use std::rc::Rc; - use crate::interpreter::environment::{Environment, InterpreterOutput}; -use crate::interpreter::evaluate::{EvaluationError, evaluate_expression}; +use crate::interpreter::evaluate::{EvaluationError, PoolWalker, evaluate_expression}; use crate::interpreter::function::FunctionCarrier; +use crate::interpreter::num::Number; use crate::interpreter::semantic::analyser::{Analyser, ScopeTree}; use crate::interpreter::value::Value; +use itertools::Itertools; use ndc_lexer::{Lexer, TokenLocation}; -use ndc_parser::ExpressionLocation; +use ndc_parser::{Expression, ExpressionLocation, ExpressionPool}; +use std::cell::RefCell; +use std::rc::Rc; + pub mod environment; pub mod evaluate; pub mod function; @@ -44,61 +46,64 @@ impl Interpreter { self.environment } - pub fn analyse_str( - &mut self, - input: &str, - ) -> Result, InterpreterError> { + pub fn analyse_str(&mut self, input: &str) -> Result { self.parse_and_analyse(input) } pub fn run_str(&mut self, input: &str) -> Result { let expressions = self.parse_and_analyse(input)?; - let final_value = self.interpret(expressions.into_iter())?; + let final_value = self.interpret(expressions)?; Ok(format!("{final_value}")) } - fn parse_and_analyse( - &mut self, - input: &str, - ) -> Result, InterpreterError> { + fn parse_and_analyse(&mut self, input: &str) -> Result { let tokens = Lexer::new(input).collect::, _>>()?; - let mut expressions = ndc_parser::Parser::from_tokens(tokens).parse()?; + let expressions = ndc_parser::Parser::from_tokens(tokens).parse()?; let checkpoint = self.analyser.checkpoint(); - for e in &mut expressions { - if let Err(e) = self.analyser.analyse(e) { - self.analyser.restore(checkpoint); - return Err(e.into()); - } - } + + dbg!(&expressions); + + // TODO: add back the analyser + // for e in &mut expressions { + // if let Err(e) = self.analyser.analyse(e) { + // self.analyser.restore(checkpoint); + // return Err(e.into()); + // } + // } Ok(expressions) } - fn interpret( - &mut self, - expressions: impl Iterator, - ) -> Result { - let mut value = Value::unit(); - for expr in expressions { - match evaluate_expression(&expr, &self.environment) { + fn interpret(&mut self, pool: ExpressionPool) -> Result { + let pool = Rc::new(pool); + let mut value: Value = Value::unit(); + + for expr in pool.root_expressions() { + match evaluate_expression( + PoolWalker { + cur: *expr, + pool: pool.clone(), + }, + &self.environment, + ) { Ok(val) => value = val, Err(FunctionCarrier::Return(_)) => { Err(EvaluationError::syntax_error( "unexpected return statement outside of function body".to_string(), - expr.span, + pool.get(*expr).span, ))?; } Err(FunctionCarrier::Break(_)) => { Err(EvaluationError::syntax_error( "unexpected break statement outside of loop body".to_string(), - expr.span, + pool.get(*expr).span, ))?; } Err(FunctionCarrier::Continue) => { Err(EvaluationError::syntax_error( "unexpected continue statement outside of loop body".to_string(), - expr.span, + pool.get(*expr).span, ))?; } Err(FunctionCarrier::EvaluationError(e)) => return Err(InterpreterError::from(e)), @@ -109,6 +114,7 @@ impl Interpreter { } } } + Ok(value) } } diff --git a/ndc_lib/src/interpreter/semantic/analyser.rs b/ndc_lib/src/interpreter/semantic/analyser.rs index 8d78ead7..8a2d5fca 100644 --- a/ndc_lib/src/interpreter/semantic/analyser.rs +++ b/ndc_lib/src/interpreter/semantic/analyser.rs @@ -2,7 +2,8 @@ use crate::interpreter::function::StaticType; use itertools::Itertools; use ndc_lexer::Span; use ndc_parser::{ - Binding, Expression, ExpressionLocation, ForBody, ForIteration, Lvalue, ResolvedVar, + Binding, Expression, ExpressionPool, ExpressionRef, ForBody, ForIteration, + Lvalue, ResolvedVar, }; use std::fmt::{Debug, Formatter}; @@ -25,8 +26,13 @@ impl Analyser { pub fn analyse( &mut self, - ExpressionLocation { expression, span }: &mut ExpressionLocation, + expr_ref: ExpressionRef, + pool: &mut ExpressionPool, ) -> Result { + let span = pool.get(expr_ref).span; + // Clone the expression to avoid holding a borrow on pool during recursive calls + let expression = pool.get(expr_ref).expression.clone(); + match expression { Expression::BoolLiteral(_) => Ok(StaticType::Bool), Expression::StringLiteral(_) => Ok(StaticType::String), @@ -34,81 +40,136 @@ impl Analyser { Expression::Float64Literal(_) => Ok(StaticType::Float), Expression::ComplexLiteral(_) => Ok(StaticType::Complex), Expression::Continue | Expression::Break => Ok(StaticType::unit()), - Expression::Identifier { - name: ident, - resolved, - } => { - if ident == "None" { + Expression::Identifier { name, .. } => { + if name == "None" { // TODO: we're going to need something like HM to infer the type of option here, maybe force type annotations? return Ok(StaticType::Option(Box::new(StaticType::Any))); } - let binding = self.scope_tree.get_binding_any(ident).ok_or_else(|| { - AnalysisError::identifier_not_previously_declared(ident, *span) + let binding = self.scope_tree.get_binding_any(&name).ok_or_else(|| { + AnalysisError::identifier_not_previously_declared(&name, span) })?; - *resolved = Binding::Resolved(binding); + if let Expression::Identifier { resolved, .. } = + &mut pool.get_mut(expr_ref).expression + { + *resolved = Binding::Resolved(binding); + } Ok(self.scope_tree.get_type(binding).clone()) } Expression::Statement(inner) => { - self.analyse(inner)?; + self.analyse(inner, pool)?; Ok(StaticType::unit()) } Expression::Logical { left, right, .. } => { - self.analyse(left)?; // TODO: throw error if type does not match bool? - self.analyse(right)?; // TODO: throw error if type does not match bool? + self.analyse(left, pool)?; // TODO: throw error if type does not match bool? + self.analyse(right, pool)?; // TODO: throw error if type does not match bool? Ok(StaticType::Bool) } - Expression::Grouping(expr) => self.analyse(expr), - Expression::VariableDeclaration { l_value, value } => { - let typ = self.analyse(value)?; - self.resolve_lvalue_declarative(l_value, typ, *span)?; + Expression::Grouping(expr) => self.analyse(expr, pool), + Expression::VariableDeclaration { value, .. } => { + let typ = self.analyse(value, pool)?; + // Clone the l_value for mutation, then write back + let mut l_value = if let Expression::VariableDeclaration { l_value, .. } = + &pool.get(expr_ref).expression + { + l_value.clone() + } else { + unreachable!() + }; + self.resolve_lvalue_declarative(&mut l_value, typ, span, pool)?; + if let Expression::VariableDeclaration { + l_value: pool_l_value, + .. + } = &mut pool.get_mut(expr_ref).expression + { + *pool_l_value = l_value; + } Ok(StaticType::unit()) // TODO: never type here? } - Expression::Assignment { l_value, r_value } => { - self.resolve_lvalue(l_value, *span)?; - self.analyse(r_value)?; + Expression::Assignment { r_value, .. } => { + // Clone the l_value for mutation, then write back + let mut l_value = if let Expression::Assignment { l_value, .. } = + &pool.get(expr_ref).expression + { + l_value.clone() + } else { + unreachable!() + }; + self.resolve_lvalue(&mut l_value, span, pool)?; + if let Expression::Assignment { + l_value: pool_l_value, + .. + } = &mut pool.get_mut(expr_ref).expression + { + *pool_l_value = l_value; + } + self.analyse(r_value, pool)?; Ok(StaticType::unit()) } Expression::OpAssignment { - l_value, r_value, - operation, - resolved_assign_operation, - resolved_operation, + ref operation, + .. } => { - let left_type = self.resolve_single_lvalue(l_value, *span)?; - let right_type = self.analyse(r_value)?; + let operation = operation.clone(); + let mut l_value = if let Expression::OpAssignment { l_value, .. } = + &pool.get(expr_ref).expression + { + l_value.clone() + } else { + unreachable!() + }; + let left_type = self.resolve_single_lvalue(&mut l_value, span, pool)?; + if let Expression::OpAssignment { + l_value: pool_l_value, + .. + } = &mut pool.get_mut(expr_ref).expression + { + *pool_l_value = l_value; + } + let right_type = self.analyse(r_value, pool)?; let arg_types = vec![left_type, right_type]; - *resolved_assign_operation = self + let resolved_assign_operation = self .scope_tree .resolve_function2(&format!("{operation}="), &arg_types); - *resolved_operation = self.scope_tree.resolve_function2(operation, &arg_types); + let resolved_operation = + self.scope_tree.resolve_function2(&operation, &arg_types); if let Binding::None = resolved_operation { return Err(AnalysisError::function_not_found( - operation, &arg_types, *span, + &operation, + &arg_types, + span, )); } + if let Expression::OpAssignment { + resolved_assign_operation: pool_rao, + resolved_operation: pool_ro, + .. + } = &mut pool.get_mut(expr_ref).expression + { + *pool_rao = resolved_assign_operation; + *pool_ro = resolved_operation; + } + Ok(StaticType::unit()) } Expression::FunctionDeclaration { name, - resolved_name, parameters, body, - return_type: return_type_slot, .. } => { // TODO: figuring out the type signature of function declarations is the rest of the owl // Pre-register the function before analysing its body so recursive calls can // resolve the name. The return type is unknown at this point so we use Any. - let pre_slot = if let Some(name) = name { + let pre_slot = if let Some(ref name) = name { let param_types: Vec = - std::iter::repeat_n(StaticType::Any, extract_argument_arity(parameters)) + std::iter::repeat_n(StaticType::Any, extract_argument_arity(parameters, pool)) .collect(); let placeholder = StaticType::Function { @@ -124,27 +185,29 @@ impl Analyser { }; self.scope_tree.new_scope(); - let param_types = self.resolve_parameters_declarative(parameters)?; + let param_types = self.resolve_parameters_declarative(parameters, pool)?; - let return_type = self.analyse(body)?; + let return_type = self.analyse(body, pool)?; self.scope_tree.destroy_scope(); - *return_type_slot = Some(return_type); let function_type = StaticType::Function { parameters: Some(param_types.clone()), - return_type: Box::new( - return_type_slot - .clone() - .expect("must have a value at this point"), - ), + return_type: Box::new(return_type.clone()), }; - if let Some(slot) = pre_slot { - // TODO: is this correct, for now we just always create a new binding, we could - // also produce an error if we are generating a conflicting binding - self.scope_tree - .update_binding_type(slot, function_type.clone()); - *resolved_name = Some(slot); + if let Expression::FunctionDeclaration { + return_type: return_type_slot, + resolved_name, + .. + } = &mut pool.get_mut(expr_ref).expression + { + *return_type_slot = Some(return_type); + + if let Some(slot) = pre_slot { + self.scope_tree + .update_binding_type(slot, function_type.clone()); + *resolved_name = Some(slot); + } } Ok(function_type) @@ -153,7 +216,7 @@ impl Analyser { self.scope_tree.new_scope(); let mut last = None; for s in statements { - last = Some(self.analyse(s)?); + last = Some(self.analyse(s, pool)?); } self.scope_tree.destroy_scope(); @@ -164,10 +227,10 @@ impl Analyser { on_true, on_false, } => { - self.analyse(condition)?; - let true_type = self.analyse(on_true)?; + self.analyse(condition, pool)?; + let true_type = self.analyse(on_true, pool)?; let false_type = if let Some(on_false) = on_false { - self.analyse(on_false)? + self.analyse(on_false, pool)? } else { StaticType::unit() }; @@ -186,12 +249,23 @@ impl Analyser { expression, loop_body, } => { - self.analyse(expression)?; - self.analyse(loop_body)?; + self.analyse(expression, pool)?; + self.analyse(loop_body, pool)?; Ok(StaticType::unit()) } Expression::For { iterations, body } => { - Ok(self.resolve_for_iterations(iterations, body, *span)?) + let mut iterations = iterations; + let mut body = body; + let result = self.resolve_for_iterations(&mut iterations, &mut body, span, pool)?; + if let Expression::For { + iterations: pool_iterations, + body: pool_body, + } = &mut pool.get_mut(expr_ref).expression + { + *pool_iterations = iterations; + *pool_body = body; + } + Ok(result) } Expression::Call { function, @@ -199,11 +273,11 @@ impl Analyser { } => { let mut type_sig = Vec::with_capacity(arguments.len()); for a in arguments { - type_sig.push(self.analyse(a)?); + type_sig.push(self.analyse(a, pool)?); } let StaticType::Function { return_type, .. } = - self.resolve_function_with_argument_types(function, &type_sig, *span)? + self.resolve_function_with_argument_types(function, &type_sig, span, pool)? else { // If we couldn't resolve the identifier to a function we have to just assume that // whatever identifier we did find is a function at runtime and will return Any @@ -213,23 +287,23 @@ impl Analyser { Ok(*return_type) } Expression::Index { index, value } => { - self.analyse(index)?; - let container_type = self.analyse(value)?; + self.analyse(index, pool)?; + let container_type = self.analyse(value, pool)?; container_type .index_element_type() - .ok_or_else(|| AnalysisError::unable_to_index_into(&container_type, *span)) + .ok_or_else(|| AnalysisError::unable_to_index_into(&container_type, span)) } Expression::Tuple { values } => { let mut types = Vec::with_capacity(values.len()); for v in values { - types.push(self.analyse(v)?); + types.push(self.analyse(v, pool)?); } Ok(StaticType::Tuple(types)) } Expression::List { values } => { - let element_type = self.analyse_multiple_expression_with_same_type(values)?; + let element_type = self.analyse_multiple_with_same_type(&values, pool)?; // TODO: for now if we encounter an empty list expression we say the list is generic over Any but this clearly is not a good solution Ok(StaticType::List(Box::new( @@ -240,30 +314,26 @@ impl Analyser { let mut key_type: Option = None; let mut value_type: Option = None; for (key, value) in values { - // let map = %{ - // "key": 1, - // 10: 1, - // } if let Some(key_type) = &mut key_type { - let next_type = self.analyse(key)?; + let next_type = self.analyse(key, pool)?; *key_type = key_type.lub(&next_type); } else { - key_type = Some(self.analyse(key)?); + key_type = Some(self.analyse(key, pool)?); } if let Some(value) = value { if let Some(value_type) = &mut value_type { - let next_type = self.analyse(value)?; + let next_type = self.analyse(value, pool)?; if &next_type != value_type { *value_type = value_type.lub(&next_type); } } else { - value_type = Some(self.analyse(value)?); + value_type = Some(self.analyse(value, pool)?); } } } if let Some(default) = default { - self.analyse(default)?; + self.analyse(default, pool)?; } // TODO: defaulting to Any here is surely going to bite us later @@ -274,44 +344,43 @@ impl Analyser { } // Return evaluates to the type of the expression it returns, which makes type checking easier! // Actually it doesn't seem to make it any easier - Expression::Return { value } => self.analyse(value), - Expression::RangeInclusive { start, end } - | Expression::RangeExclusive { start, end } => { + Expression::Return { value } => self.analyse(value, pool), + Expression::RangeInclusive { start, end } | Expression::RangeExclusive { start, end } => { if let Some(start) = start { - self.analyse(start)?; + self.analyse(start, pool)?; } if let Some(end) = end { - self.analyse(end)?; + self.analyse(end, pool)?; } Ok(StaticType::Iterator(Box::new(StaticType::Int))) } } } + fn resolve_function_with_argument_types( &mut self, - ident: &mut ExpressionLocation, + fn_expr_ref: ExpressionRef, argument_types: &[StaticType], span: Span, + pool: &mut ExpressionPool, ) -> Result { - let ExpressionLocation { - expression: Expression::Identifier { name, resolved }, - .. - } = ident - else { + let expression = pool.get(fn_expr_ref).expression.clone(); + + let Expression::Identifier { name, .. } = expression else { // It's possible that we're not trying to invoke an identifier `foo()` but instead we're // invoking a value like `get_function()()` so in this case we just continue like normal? - return self.analyse(ident); + return self.analyse(fn_expr_ref, pool); }; // println!("resolve fn {name} {}", argument_types.iter().join(", ")); - let binding = self.scope_tree.resolve_function2(name, argument_types); + let binding = self.scope_tree.resolve_function2(&name, argument_types); let out_type = match &binding { Binding::None => { return Err(AnalysisError::function_not_found( - name, + &name, argument_types, span, )); @@ -325,15 +394,20 @@ impl Analyser { }, }; - *resolved = binding; + if let Expression::Identifier { resolved, .. } = &mut pool.get_mut(fn_expr_ref).expression + { + *resolved = binding; + } Ok(out_type) } + fn resolve_for_iterations( &mut self, - iterations: &mut [ForIteration], - body: &mut ForBody, + iterations: &mut Vec, + body: &mut Box, span: Span, + pool: &mut ExpressionPool, ) -> Result { let Some((iteration, tail)) = iterations.split_first_mut() else { unreachable!("because this function is never called with an empty slice"); @@ -342,7 +416,7 @@ impl Analyser { let mut do_destroy = false; match iteration { ForIteration::Iteration { l_value, sequence } => { - let sequence_type = self.analyse(sequence)?; + let sequence_type = self.analyse(*sequence, pool)?; self.scope_tree.new_scope(); @@ -353,37 +427,43 @@ impl Analyser { .sequence_element_type() .unwrap_or(StaticType::Any), span, + pool, )?; do_destroy = true; // TODO: why is this correct } ForIteration::Guard(expr) => { - self.analyse(expr)?; + self.analyse(*expr, pool)?; } } let out_type = if !tail.is_empty() { - self.resolve_for_iterations(tail, body, span)? + self.resolve_for_iterations( + &mut tail.to_vec(), + body, + span, + pool, + )? } else { - match body { + match body.as_mut() { ForBody::Block(block) => { - self.analyse(block)?; + self.analyse(*block, pool)?; StaticType::unit() } - ForBody::List(list) => StaticType::List(Box::new(self.analyse(list)?)), + ForBody::List(list) => StaticType::List(Box::new(self.analyse(*list, pool)?)), ForBody::Map { key, value, default, } => { - let key_type = self.analyse(key)?; + let key_type = self.analyse(*key, pool)?; let value_type = if let Some(value) = value { - self.analyse(value)? + self.analyse(*value, pool)? } else { StaticType::unit() }; if let Some(default) = default { - self.analyse(default)?; + self.analyse(**default, pool)?; } StaticType::Map { @@ -405,6 +485,7 @@ impl Analyser { &mut self, lvalue: &mut Lvalue, span: Span, + pool: &mut ExpressionPool, ) -> Result { match lvalue { Lvalue::Identifier { @@ -423,8 +504,8 @@ impl Analyser { Ok(self.scope_tree.get_type(target).clone()) } Lvalue::Index { index, value } => { - self.analyse(index)?; - let type_of_index_target = self.analyse(value)?; + self.analyse(*index, pool)?; + let type_of_index_target = self.analyse(*value, pool)?; type_of_index_target .index_element_type() @@ -436,7 +517,12 @@ impl Analyser { } } - fn resolve_lvalue(&mut self, lvalue: &mut Lvalue, span: Span) -> Result<(), AnalysisError> { + fn resolve_lvalue( + &mut self, + lvalue: &mut Lvalue, + span: Span, + pool: &mut ExpressionPool, + ) -> Result<(), AnalysisError> { match lvalue { Lvalue::Identifier { identifier, @@ -452,12 +538,12 @@ impl Analyser { *resolved = Some(target); } Lvalue::Index { index, value } => { - self.analyse(index)?; - self.analyse(value)?; + self.analyse(*index, pool)?; + self.analyse(*value, pool)?; } Lvalue::Sequence(seq) => { for sub_lvalue in seq { - self.resolve_lvalue(sub_lvalue, span)? + self.resolve_lvalue(sub_lvalue, span, pool)? } } } @@ -468,25 +554,24 @@ impl Analyser { /// Resolve expressions as arguments to a function and return the function arity fn resolve_parameters_declarative( &mut self, - arguments: &mut ExpressionLocation, + params_ref: ExpressionRef, + pool: &mut ExpressionPool, ) -> Result, AnalysisError> { let mut types: Vec = Vec::new(); - let mut names: Vec<&str> = Vec::new(); + let mut names: Vec = Vec::new(); - let ExpressionLocation { - expression: Expression::Tuple { values }, - .. - } = arguments - else { + let values = if let Expression::Tuple { values } = &pool.get(params_ref).expression { + values.clone() + } else { panic!("expected arguments to be tuple"); }; - for arg in values { - let ExpressionLocation { - expression: Expression::Identifier { name, resolved }, - span, - } = arg - else { + for param_ref in values { + let (name, param_span) = if let Expression::Identifier { name, .. } = + &pool.get(param_ref).expression + { + (name.clone(), pool.get(param_ref).span) + } else { panic!("expected tuple values to be ident"); }; @@ -494,24 +579,31 @@ impl Analyser { // it seems like this is something we need an HM like system for!? let resolved_type = StaticType::Any; types.push(resolved_type.clone()); - if names.contains(&name.as_str()) { - return Err(AnalysisError::parameter_redefined(name, *span)); + if names.contains(&name) { + return Err(AnalysisError::parameter_redefined(&name, param_span)); } - names.push(name); + names.push(name.clone()); - *resolved = Binding::Resolved( - self.scope_tree - .create_local_binding((*name).clone(), resolved_type), - ); + let binding = self + .scope_tree + .create_local_binding(name, resolved_type); + + if let Expression::Identifier { resolved, .. } = + &mut pool.get_mut(param_ref).expression + { + *resolved = Binding::Resolved(binding); + } } Ok(types) } + fn resolve_lvalue_declarative( &mut self, lvalue: &mut Lvalue, typ: StaticType, span: Span, + pool: &mut ExpressionPool, ) -> Result<(), AnalysisError> { match lvalue { Lvalue::Identifier { @@ -527,8 +619,8 @@ impl Analyser { *inferred_type = Some(typ); } Lvalue::Index { index, value } => { - self.analyse(index)?; - self.analyse(value)?; + self.analyse(*index, pool)?; + self.analyse(*value, pool)?; } Lvalue::Sequence(seq) => { let sub_types = typ @@ -540,6 +632,7 @@ impl Analyser { sub_lvalue, sub_lvalue_type.clone(), /* todo: figure out how to narrow this span */ span, + pool, )? } } @@ -547,19 +640,21 @@ impl Analyser { Ok(()) } - fn analyse_multiple_expression_with_same_type( + + fn analyse_multiple_with_same_type( &mut self, - expressions: &mut Vec, + expressions: &[ExpressionRef], + pool: &mut ExpressionPool, ) -> Result, AnalysisError> { let mut element_type: Option = None; - for expression in expressions { + for &expression in expressions { if let Some(element_type) = &mut element_type { - let following_type = self.analyse(expression)?; + let following_type = self.analyse(expression, pool)?; *element_type = element_type.lub(&following_type); } else { - element_type = Some(self.analyse(expression)?); + element_type = Some(self.analyse(expression, pool)?); } } @@ -567,12 +662,8 @@ impl Analyser { } } -fn extract_argument_arity(arguments: &ExpressionLocation) -> usize { - let ExpressionLocation { - expression: Expression::Tuple { values }, - .. - } = arguments - else { +fn extract_argument_arity(params_ref: ExpressionRef, pool: &ExpressionPool) -> usize { + let Expression::Tuple { values } = &pool.get(params_ref).expression else { panic!("expected arguments to be tuple"); }; @@ -747,25 +838,32 @@ impl ScopeTree { } fn create_local_binding(&mut self, ident: String, typ: StaticType) -> ResolvedVar { + let scope = &mut self.scopes[self.current_scope_idx]; + let slot = scope.identifiers.len(); + scope.identifiers.push((ident, typ)); ResolvedVar::Captured { - slot: self.scopes[self.current_scope_idx].allocate(ident, typ), + slot, depth: 0, } } - fn update_binding_type(&mut self, var: ResolvedVar, new_type: StaticType) { - let ResolvedVar::Captured { slot, depth } = var else { - panic!("update_binding_type called with a global binding"); - }; - let mut scope_idx = self.current_scope_idx; - let mut remaining = depth; - while remaining > 0 { - remaining -= 1; - scope_idx = self.scopes[scope_idx] - .parent_idx - .expect("parent_idx was None while traversing the scope tree"); + pub fn update_binding_type(&mut self, target: ResolvedVar, typ: StaticType) { + match target { + ResolvedVar::Captured { slot, depth } => { + let mut scope_idx = self.current_scope_idx; + let mut depth = depth; + while depth > 0 { + depth -= 1; + scope_idx = self.scopes[scope_idx] + .parent_idx + .expect("parent_idx was None"); + } + self.scopes[scope_idx].identifiers[slot].1 = typ; + } + ResolvedVar::Global { slot } => { + self.global_scope.identifiers[slot].1 = typ; + } } - self.scopes[scope_idx].identifiers[slot].1 = new_type; } } @@ -779,131 +877,117 @@ impl Scope { fn new(parent_idx: Option) -> Self { Self { parent_idx, - identifiers: Default::default(), + identifiers: Vec::new(), } } - pub fn find_slot_by_name(&self, find_ident: &str) -> Option { + fn find_slot_by_name(&self, ident: &str) -> Option { self.identifiers .iter() - .rposition(|(ident, _)| ident == find_ident) + .rposition(|(name, _)| name == ident) } - fn find_all_slots_by_name(&self, find_ident: &str) -> Vec { + fn find_all_slots_by_name(&self, ident: &str) -> Vec { self.identifiers .iter() .enumerate() - .filter_map(|(slot, (ident, _))| { - if ident == find_ident { - Some(slot) - } else { - None - } - }) + .filter_map(|(slot, (name, _))| if name == ident { Some(slot) } else { None }) .collect() } - fn find_function_candidates(&self, find_ident: &str, find_types: &[StaticType]) -> Vec { - self.identifiers.iter() + fn find_function(&self, ident: &str, arg_types: &[StaticType]) -> Option { + self.identifiers.iter().rposition(|(name, typ)| { + name == ident && typ.is_fn_and_matches(arg_types) + }) + } + + fn find_function_candidates(&self, ident: &str, sig: &[StaticType]) -> Vec { + self.identifiers + .iter() .enumerate() - .rev() - .filter_map(|(slot, (ident, typ))| { - if ident != find_ident { - return None; + .filter_map(|(slot, (name, typ))| { + if name == ident && typ.is_fn_and_matches(sig) { + Some(slot) + } else { + None } - - // If the thing is not a function we're not interested - let StaticType::Function { parameters, .. } = typ else { - return None; - }; - - let Some(param_types) = parameters else { - // If this branch happens then the function we're matching against is variadic meaning it's always a match - debug_assert!(false, "we should never be calling find_function_candidates if there were variadic matches"); - // TODO: Change to unreachable? - return Some(slot); - }; - - let is_good = param_types.len() == find_types.len() - && param_types.iter().zip(find_types.iter()).all(|(typ_1, typ_2)| !typ_1.is_incompatible_with(typ_2)); - - is_good.then_some(slot) }) .collect() } - fn find_function(&self, find_ident: &str, find_types: &[StaticType]) -> Option { - self.identifiers - .iter() - .rposition(|(ident, typ)| ident == find_ident && typ.is_fn_and_matches(find_types)) - } - - fn allocate(&mut self, name: String, typ: StaticType) -> usize { - self.identifiers.push((name, typ)); - // Slot is just the length of the list minus one - self.identifiers.len() - 1 - } } -#[derive(thiserror::Error, Debug)] -#[error("{text}")] + +#[derive(Debug, Clone)] pub struct AnalysisError { text: String, span: Span, + help_text: Option, } -impl AnalysisError { - pub fn span(&self) -> Span { - self.span +impl std::error::Error for AnalysisError {} + +impl std::fmt::Display for AnalysisError { + fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result { + write!(f, "{}", self.text) } - fn parameter_redefined(param: &str, span: Span) -> Self { +} + +impl AnalysisError { + fn identifier_not_previously_declared(ident: &str, span: Span) -> Self { Self { - text: format!("Illegal redefinition of parameter {param}"), + text: format!("Identifier '{ident}' not previously declared"), span, + help_text: None, } } - fn unable_to_index_into(typ: &StaticType, span: Span) -> Self { + + fn function_not_found(name: &str, arg_types: &[StaticType], span: Span) -> Self { Self { - text: format!("Unable to index into {typ}"), + text: format!( + "No function called '{name}' found that matches the arguments: ({})", + arg_types.iter().join(", ") + ), span, + help_text: None, } } - fn unable_to_unpack_type(typ: &StaticType, span: Span) -> Self { + + fn unable_to_index_into(typ: &StaticType, span: Span) -> Self { Self { - text: format!("Invalid unpacking of {typ}"), + text: format!("Cannot index into type '{typ}'"), span, + help_text: None, } } + fn lvalue_required_to_be_single_identifier(span: Span) -> Self { Self { - text: "This lvalue is required to be a single identifier".to_string(), + text: "Left-hand side of augmented assignment must be a single identifier or index expression".to_string(), span, + help_text: None, } } - fn function_not_found(ident: &str, types: &[StaticType], span: Span) -> Self { + fn unable_to_unpack_type(typ: &StaticType, span: Span) -> Self { Self { - text: format!( - "No function called '{ident}' found that matches the arguments '{}'", - types.iter().join(", ") - ), + text: format!("Cannot unpack type '{typ}' into a pattern"), span, + help_text: None, } } - fn identifier_not_previously_declared(ident: &str, span: Span) -> Self { + fn parameter_redefined(name: &str, span: Span) -> Self { Self { - text: format!("Identifier {ident} has not previously been declared"), + text: format!("Parameter '{name}' is defined more than once"), span, + help_text: None, } } -} -impl Debug for Analyser { - fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result { - writeln!(f)?; - for (id, scope) in self.scope_tree.scopes.iter().enumerate() { - writeln!(f, "{id}: {scope:?}")?; - } + pub fn span(&self) -> Span { + self.span + } - Ok(()) + pub fn help_text(&self) -> Option<&str> { + self.help_text.as_deref() } } diff --git a/ndc_lsp/src/backend.rs b/ndc_lsp/src/backend.rs index f7abf99f..060c3599 100644 --- a/ndc_lsp/src/backend.rs +++ b/ndc_lsp/src/backend.rs @@ -2,7 +2,7 @@ use std::collections::HashMap; use ndc_lexer::{Lexer, Span, TokenLocation}; use ndc_lib::interpreter::Interpreter; -use ndc_parser::{Expression, ExpressionLocation, ForBody, ForIteration, Lvalue}; +use ndc_parser::{Expression, ExpressionLocation, ExpressionPool, ForIteration, Lvalue}; use tokio::sync::Mutex; use tower_lsp::jsonrpc::Result as JsonRPCResult; use tower_lsp::lsp_types::{ @@ -84,8 +84,8 @@ impl Backend { match interpreter.analyse_str(text) { Ok(expressions) => { let mut hints = Vec::new(); - for expr in &expressions { - collect_hints(expr, text, &mut hints); + for expr in expressions.iter() { + collect_hints(expr, &expressions, text, &mut hints); } hints } @@ -199,24 +199,24 @@ impl LanguageServer for Backend { } } -/// Recursively walk an analysed AST node and collect inlay hints from places where -/// the analyser stored type information: `Lvalue::Identifier.inferred_type` (variable -/// and for-loop declarations) and `FunctionDeclaration.return_type`. -fn collect_hints(expr: &ExpressionLocation, text: &str, hints: &mut Vec) { +/// Collect inlay hints from a single AST node. Sub-expressions are visited by the caller +/// iterating over all entries in the pool, so this function does not recurse into +/// pool-stored children. It only recurses into embedded `Lvalue` values, which are not +/// stored as separate pool entries. +fn collect_hints(expr: &ExpressionLocation, pool: &ExpressionPool, text: &str, hints: &mut Vec) { match &expr.expression { - Expression::VariableDeclaration { l_value, value } => { + Expression::VariableDeclaration { l_value, .. } => { collect_hints_from_lvalue(l_value, text, hints); - collect_hints(value, text, hints); } Expression::FunctionDeclaration { return_type, parameters, - body, .. } => { if let Some(rt) = return_type { + let params_span = pool.get(*parameters).span; hints.push(InlayHint { - position: position_from_offset(text, parameters.span.end()), + position: position_from_offset(text, params_span.end()), label: InlayHintLabel::String(format!(" -> {rt}")), kind: Some(InlayHintKind::TYPE), text_edits: None, @@ -226,62 +226,14 @@ fn collect_hints(expr: &ExpressionLocation, text: &str, hints: &mut Vec collect_hints(inner, text, hints), - Expression::Grouping(inner) => collect_hints(inner, text, hints), - Expression::Block { statements } => { - for s in statements { - collect_hints(s, text, hints); - } - } - Expression::If { - condition, - on_true, - on_false, - } => { - collect_hints(condition, text, hints); - collect_hints(on_true, text, hints); - if let Some(f) = on_false { - collect_hints(f, text, hints); - } - } - Expression::While { - expression, - loop_body, - } => { - collect_hints(expression, text, hints); - collect_hints(loop_body, text, hints); - } - Expression::For { iterations, body } => { + Expression::For { iterations, .. } => { for iteration in iterations { - match iteration { - ForIteration::Iteration { l_value, sequence } => { - collect_hints_from_lvalue(l_value, text, hints); - collect_hints(sequence, text, hints); - } - ForIteration::Guard(expr) => collect_hints(expr, text, hints), - } - } - match body.as_ref() { - ForBody::Block(e) | ForBody::List(e) => collect_hints(e, text, hints), - ForBody::Map { - key, - value, - default, - } => { - collect_hints(key, text, hints); - if let Some(v) = value { - collect_hints(v, text, hints); - } - if let Some(d) = default { - collect_hints(d, text, hints); - } + if let ForIteration::Iteration { l_value, .. } = iteration { + collect_hints_from_lvalue(l_value, text, hints); } } } - Expression::Return { value } => collect_hints(value, text, hints), - // Literals, identifiers, ranges, calls etc. contain no declaration sites _ => {} } } diff --git a/ndc_parser/src/expression.rs b/ndc_parser/src/expression.rs index 58805ca3..5a112cb6 100644 --- a/ndc_parser/src/expression.rs +++ b/ndc_parser/src/expression.rs @@ -27,6 +27,12 @@ pub struct ExpressionLocation { #[derive(Debug, Clone, Copy, PartialEq, Eq)] pub struct ExpressionRef(u32); +impl ExpressionRef { + pub fn as_usize(self) -> usize { + self.0 as usize + } +} + #[derive(Debug, PartialEq, Clone)] pub enum Expression { // Literals @@ -72,7 +78,7 @@ pub enum Expression { pure: bool, }, Block { - statements: Vec, + statements: Vec, }, If { condition: ExpressionRef, @@ -97,13 +103,13 @@ pub enum Expression { index: ExpressionRef, }, Tuple { - values: Vec, + values: Vec, }, List { - values: Vec, + values: Vec, }, Map { - values: Vec<(ExpressionLocation, Option)>, + values: Vec<(ExpressionRef, Option)>, default: Option, }, Return { @@ -171,15 +177,36 @@ impl Expression { } } -#[derive(Clone)] +#[derive(Clone, Debug, Default)] pub struct ExpressionPool { region: Vec, + root_refs: Vec, +} + +impl ExpressionPool {} + +impl IntoIterator for ExpressionPool { + type Item = ExpressionLocation; + type IntoIter = std::vec::IntoIter; + + fn into_iter(self) -> Self::IntoIter { + self.region.into_iter() + } } impl ExpressionPool { - pub fn new() -> Self { - Self { region: Vec::new() } + pub fn len(&self) -> usize { + self.region.len() + } + + pub fn is_empty(&self) -> bool { + self.region.is_empty() } + + pub fn add_root_ref(&mut self, root_ref: ExpressionRef) { + self.root_refs.push(root_ref) + } + pub fn get(&self, er: ExpressionRef) -> &ExpressionLocation { &self.region[er.0 as usize] } @@ -189,8 +216,38 @@ impl ExpressionPool { ExpressionRef((self.region.len() - 1) as u32) } + pub fn set_span(&mut self, er: ExpressionRef, span: Span) { + self.region[er.0 as usize].span = span; + } + + pub fn get_mut(&mut self, er: ExpressionRef) -> &mut ExpressionLocation { + &mut self.region[er.0 as usize] + } + + pub fn iter(&self) -> impl DoubleEndedIterator { + self.region.iter() + } + + pub fn root_expressions(&self) -> impl DoubleEndedIterator { + self.root_refs.iter() + } pub fn merged_span(&self, left_id: ExpressionRef, right_id: ExpressionRef) -> Span { - self.region[left_id.0 as usize].span.merge(self.region[right_id.0 as usize].span) + self.region[left_id.0 as usize] + .span + .merge(self.region[right_id.0 as usize].span) + } + + pub fn simplify(&mut self, target: ExpressionRef) { + // NOTE: for now we put a copy of the expression in the target slot but in the future we might want to move it + match self.get(target) { + ExpressionLocation { + expression: Expression::Tuple { values }, + .. + } if values.len() == 1 => { + self.region[target.0 as usize] = self.region[values[0].0 as usize].clone() + } + _ => {} + } } } @@ -201,29 +258,6 @@ impl ExpressionLocation { _ => panic!("the parser should have guaranteed us the right type of expression"), } } - - pub fn as_parameters(&self) -> Vec<&str> { - match &self.expression { - Expression::Tuple { - values: tuple_values, - } => tuple_values.iter().map(|it| it.as_identifier()).collect(), - _ => panic!("the parser should have guaranteed us the right type of expression"), - } - } - - /// If this `ExpressionLocation` is a tuple expression with length one, it returns the - /// `ExpressionLocation` inside the tuple. - #[must_use] - pub fn simplify(self) -> Self { - match self { - Self { - expression: Expression::Tuple { mut values }, - .. - // } if values.len() == 1 => values.remove(0).simplify(), - } if values.len() == 1 => values.remove(0), - tuple @ Self { .. } => tuple, - } - } } impl Lvalue { @@ -242,7 +276,7 @@ impl Lvalue { Expression::Identifier { .. } | Expression::Index { .. } => true, Expression::List { values } | Expression::Tuple { values } => values .iter() - .all(|el| Self::can_build_from_expression(&el.expression, pool)), + .all(|el| Self::can_build_from_expression(&pool.get(*el).expression, pool)), Expression::Grouping(inner) => { Self::can_build_from_expression(&pool.get(*inner).expression, pool) } @@ -260,20 +294,26 @@ impl Lvalue { } } -impl TryFrom for Lvalue { - type Error = ParseError; - - fn try_from(value: ExpressionLocation) -> Result { +impl Lvalue { + pub fn from_expression_location( + value: ExpressionLocation, + pool: &ExpressionPool, + ) -> Result { match value.expression { Expression::Identifier { name, .. } => Ok(Self::new_identifier(name, value.span)), Expression::Index { value, index } => Ok(Self::Index { value, index }), Expression::List { values } | Expression::Tuple { values } => Ok(Self::Sequence( values .into_iter() - .map(Self::try_from) - .collect::, Self::Error>>()?, + .map(|er| Self::from_expression_location(pool.get(er).clone(), pool)) + .collect::, ParseError>>()?, )), - Expression::Grouping(value) => Ok(Self::Sequence(vec![Self::try_from(*value)?])), + Expression::Grouping(inner) => { + Ok(Self::Sequence(vec![Self::from_expression_location( + pool.get(inner).clone(), + pool, + )?])) + } _expr => Err(ParseError::text("invalid l-value".to_string(), value.span)), } } diff --git a/ndc_parser/src/lib.rs b/ndc_parser/src/lib.rs index f6f5e900..09cea750 100644 --- a/ndc_parser/src/lib.rs +++ b/ndc_parser/src/lib.rs @@ -4,7 +4,8 @@ mod parser; mod static_type; pub use expression::{ - Binding, Expression, ExpressionLocation, ForBody, ForIteration, Lvalue, ResolvedVar, + Binding, Expression, ExpressionLocation, ExpressionPool, ExpressionRef, ForBody, ForIteration, + Lvalue, ResolvedVar, }; pub use operator::{BinaryOperator, LogicalOperator, UnaryOperator}; pub use parser::Error; diff --git a/ndc_parser/src/parser.rs b/ndc_parser/src/parser.rs index d2bd195e..776c7d7b 100644 --- a/ndc_parser/src/parser.rs +++ b/ndc_parser/src/parser.rs @@ -1,6 +1,8 @@ use std::fmt::Write; -use crate::expression::{Binding, ExpressionLocation, ExpressionRef, ForBody, ForIteration, Lvalue}; +use crate::expression::{ + Binding, ExpressionLocation, ExpressionRef, ForBody, ForIteration, Lvalue, +}; use crate::expression::{Expression, ExpressionPool}; use crate::operator::{BinaryOperator, LogicalOperator, UnaryOperator}; use ndc_lexer::{Span, Token, TokenLocation}; @@ -16,7 +18,7 @@ impl Parser { Self { tokens, current: 0, - pool: ExpressionPool::new(), + pool: ExpressionPool::default(), } } @@ -38,10 +40,10 @@ impl Parser { }; while self.peek_current_token_location().is_some() { - let expr_loc = self.expression_or_statement()?; - let is_statement = is_valid_statement(&expr_loc.expression); + let expr_ref = self.expression_or_statement()?; + let is_statement = is_valid_statement(&self.pool.get(expr_ref).expression); - self.pool.add(expr_loc); + self.pool.add_root_ref(expr_ref); if !is_statement { break; @@ -56,7 +58,7 @@ impl Parser { )); } - Ok(std::mem::replace(&mut self.pool, ExpressionPool::new())) + Ok(std::mem::replace(&mut self.pool, ExpressionPool::default())) } fn peek_current_token(&self) -> Option<&Token> { @@ -194,8 +196,12 @@ impl Parser { ) }); let right_id = next(self)?; - - let new_span = self.pool.get(left_id).span.merge(self.pool.get(right_id).span); + + let new_span = self + .pool + .get(left_id) + .span + .merge(self.pool.get(right_id).span); // Is this always the same debug_assert_eq!(operator.to_string(), operator_token_loc.token.to_string()); @@ -208,11 +214,13 @@ impl Parser { .to_location(operator_token_loc.span), ); - left_id = self.pool.add(Expression::Call { - function: function_id, - arguments: vec![left_id, right_id], - } - .to_location(new_span)); + left_id = self.pool.add( + Expression::Call { + function: function_id, + arguments: vec![left_id, right_id], + } + .to_location(new_span), + ); if let Some(not_token) = invert { let not_function_id = self.pool.add( @@ -223,11 +231,13 @@ impl Parser { .to_location(not_token.span), ); - left_id = self.pool.add(Expression::Call { - function: not_function_id, - arguments: vec![left_id], - } - .to_location(new_span.merge(not_token.span))); + left_id = self.pool.add( + Expression::Call { + function: not_function_id, + arguments: vec![left_id], + } + .to_location(new_span.merge(not_token.span)), + ); } } Ok(left_id) @@ -245,22 +255,29 @@ impl Parser { let operator_span = token_location.span; let operator = BinaryOperator::try_from(token_location) .expect("COMPILER ERROR: consume_token_if must guarantee the correct token"); - + let right_id = current(self)?; - let new_span = self.pool.get(left_id).span.merge(self.pool.get(right_id).span); + let new_span = self + .pool + .get(left_id) + .span + .merge(self.pool.get(right_id).span); - - let ident_id = self.pool.add(Expression::Identifier { - name: operator.to_string(), - resolved: Binding::None, - } - .to_location(operator_span)); - - let out_id = self.pool.add(Expression::Call { - function: ident_id, - arguments: vec![left_id, right_id], - } - .to_location(new_span)); + let ident_id = self.pool.add( + Expression::Identifier { + name: operator.to_string(), + resolved: Binding::None, + } + .to_location(operator_span), + ); + + let out_id = self.pool.add( + Expression::Call { + function: ident_id, + arguments: vec![left_id, right_id], + } + .to_location(new_span), + ); return Ok(out_id); } @@ -280,12 +297,14 @@ impl Parser { .expect("consume_operator_if guaranteed us that this is an operator"); let right = next(self)?; let new_span = self.pool.get(left).span.merge(self.pool.get(right).span); - left = self.pool.add(Expression::Logical { - left, - operator, - right, - } - .to_location(new_span)); + left = self.pool.add( + Expression::Logical { + left, + operator, + right, + } + .to_location(new_span), + ); } Ok(left) } @@ -298,8 +317,8 @@ impl Parser { // ---------------------------------------- Recursive Descent Parser ---------------------------------------- - fn expression_or_statement(&mut self) -> Result { - let mut expression = if self.match_token(&[Token::Let]).is_some() { + fn expression_or_statement(&mut self) -> Result { + let expression = if self.match_token(&[Token::Let]).is_some() { self.let_statement()? } else { self.expression()? @@ -307,21 +326,25 @@ impl Parser { if self.match_token(&[Token::Semicolon]).is_some() { self.advance(); - expression = expression.to_statement(); + let span = self.pool.get(expression).span; + return Ok(self + .pool + .add(Expression::Statement(expression).to_location(span))); } Ok(expression) } - fn let_statement(&mut self) -> Result { + fn let_statement(&mut self) -> Result { let let_token = self .require_current_token_matches(&Token::Let) .expect("guaranteed to match by caller"); - let maybe_lvalue = self.tuple_expression(Self::single_expression, false)?; - let lvalue_span = maybe_lvalue.span; + let maybe_lvalue_ref = self.tuple_expression(Self::single_expression, false)?; + let lvalue_span = self.pool.get(maybe_lvalue_ref).span; + let maybe_lvalue = self.pool.get(maybe_lvalue_ref).clone(); - let Ok(lvalue) = Lvalue::try_from(maybe_lvalue) else { + let Ok(lvalue) = Lvalue::from_expression_location(maybe_lvalue, &self.pool) else { return Err(Error::with_help( "Invalid assignment target".to_string(), lvalue_span, @@ -331,38 +354,42 @@ impl Parser { self.require_current_token_matches(&Token::EqualsSign)?; - let expression = self.variable_assignment()?; - let end = expression.span; + let value = self.variable_assignment()?; + let end = self.pool.get(value).span; let declaration = Expression::VariableDeclaration { l_value: lvalue, - value: self.pool.add(expression), + value, }; if self.peek_current_token().is_some() { self.require_current_token_matches(&Token::Semicolon)?; } - Ok(declaration - .to_location(let_token.span.merge(end)) - .to_statement()) + let decl_ref = self + .pool + .add(declaration.to_location(let_token.span.merge(end))); + let span = self.pool.get(decl_ref).span; + Ok(self + .pool + .add(Expression::Statement(decl_ref).to_location(span))) } - fn expression(&mut self) -> Result { + fn expression(&mut self) -> Result { self.variable_assignment() } - fn variable_assignment(&mut self) -> Result { + fn variable_assignment(&mut self) -> Result { let maybe_lvalue = self.tuple_expression(Self::single_expression, false)?; - let start = maybe_lvalue.span; + let start = self.pool.get(maybe_lvalue).span; - if !Lvalue::can_build_from_expression(&maybe_lvalue.expression) { + if !Lvalue::can_build_from_expression(&self.pool.get(maybe_lvalue).expression, &self.pool) { // In this case we got some kind of expression that we can't assign to. We can just return the expression as is. // But to improve error handling and stuff it would be nice if we could check if the next token matches one // of the assignment operator and throw an appropriate error. return match self.peek_current_token() { Some(Token::EqualsSign) => Err(Error::with_help( "Invalid assignment target".to_string(), - maybe_lvalue.span, + start, "Assignment target is not a valid lvalue. Only a few expressions can be assigned a value. Check that the left-hand side of the assignment is a valid target.".to_string(), )), _ => Ok(maybe_lvalue), @@ -373,32 +400,36 @@ impl Parser { // NOTE: the parser supports every LValue but some might cause an error when declaring vars Some(Token::EqualsSign) => { self.advance(); - let expression = self.tuple_expression(Self::single_expression, false)?; - let end = expression.span; - let assignment_expression = Expression::Assignment { - l_value: Lvalue::try_from(maybe_lvalue) - .expect("guaranteed to produce an lvalue"), - r_value: self.pool.add(expression), - }; - - Ok(assignment_expression.to_location(start.merge(end))) + let r_value = self.tuple_expression(Self::single_expression, false)?; + let end = self.pool.get(r_value).span; + let l_value = Lvalue::from_expression_location( + self.pool.get(maybe_lvalue).clone(), + &self.pool, + ) + .expect("guaranteed to produce an lvalue"); + let assignment_expression = Expression::Assignment { l_value, r_value }; + Ok(self + .pool + .add(assignment_expression.to_location(start.merge(end)))) } Some(Token::OpAssign(inner)) => { let operation_identifier = inner.token.to_string(); - self.advance(); - let expression = self.tuple_expression(Self::single_expression, false)?; - let end = expression.span; + let r_value = self.tuple_expression(Self::single_expression, false)?; + let end = self.pool.get(r_value).span; + let l_value = Lvalue::from_expression_location( + self.pool.get(maybe_lvalue).clone(), + &self.pool, + ) + .expect("guaranteed to produce an lvalue"); let op_assign = Expression::OpAssignment { - l_value: Lvalue::try_from(maybe_lvalue) - .expect("guaranteed to produce an lvalue"), - r_value: self.pool.add(expression), + l_value, + r_value, operation: operation_identifier, resolved_assign_operation: Binding::None, resolved_operation: Binding::None, }; - - Ok(op_assign.to_location(start.merge(end))) + Ok(self.pool.add(op_assign.to_location(start.merge(end)))) } _ => Ok(maybe_lvalue), } @@ -408,7 +439,7 @@ impl Parser { &mut self, next: fn(&mut Self) -> Result, must_be_tuple: bool, - ) -> Result { + ) -> Result { let first_ref = next(self)?; let mut refs = vec![first_ref]; let mut must_be_tuple = must_be_tuple; @@ -422,43 +453,44 @@ impl Parser { refs.push(next(self)?); } - let new_span = self.pool.get(refs[0]).span + let new_span = self + .pool + .get(refs[0]) + .span .merge(self.pool.get(*refs.last().unwrap()).span); - let expressions: Vec = - refs.iter().map(|&r| self.pool.get(r).clone()).collect(); - let tuple_expression = ExpressionLocation { - expression: Expression::Tuple { - values: expressions, - }, - span: new_span, - }; + let tuple_ref = self + .pool + .add(Expression::Tuple { values: refs }.to_location(new_span)); - if must_be_tuple { - Ok(tuple_expression) - } else { - Ok(tuple_expression.simplify()) + if !must_be_tuple { + self.pool.simplify(tuple_ref); } + + Ok(tuple_ref) } /// Parses a delimited tuple (enclosed in parentheses) that can be empty fn delimited_tuple( &mut self, next: fn(&mut Self) -> Result, - ) -> Result { + ) -> Result { let start = self.require_current_token_matches(&Token::LeftParentheses)?; if let Some(end) = self.consume_token_if(&[Token::RightParentheses]) { - Ok(Expression::Tuple { values: vec![] }.to_location(start.span.merge(end.span))) + Ok(self + .pool + .add(Expression::Tuple { values: vec![] }.to_location(start.span.merge(end.span)))) } else { - let mut tuple_expression = self.tuple_expression(next, true)?; + let tuple_ref = self.tuple_expression(next, true)?; let right_paren_span = self .require_current_token_matches(&Token::RightParentheses)? .span; - // Include the right paretheses in the span - tuple_expression.span = tuple_expression.span.merge(right_paren_span); + // Include the right parentheses in the span + let extended_span = self.pool.get(tuple_ref).span.merge(right_paren_span); + self.pool.set_span(tuple_ref, extended_span); - Ok(tuple_expression) + Ok(tuple_ref) } } @@ -655,13 +687,12 @@ impl Parser { match current.token { // handles: foo() Token::LeftParentheses => { - let arguments = self.delimited_tuple(Self::single_expression)?; - let arguments_span = arguments.span; - let Expression::Tuple { values: arguments } = arguments.expression else { - unreachable!("self.tuple() must always produce a tuple"); + let arguments_ref = self.delimited_tuple(Self::single_expression)?; + let arguments_span = self.pool.get(arguments_ref).span; + let arguments = match &self.pool.get(arguments_ref).expression { + Expression::Tuple { values } => values.clone(), + _ => unreachable!("self.delimited_tuple() must always produce a tuple"), }; - let arguments: Vec = - arguments.into_iter().map(|a| self.pool.add(a)).collect(); let span = self.pool.get(expr).span; @@ -684,23 +715,23 @@ impl Parser { }; // () is now optional? - let (extra_arguments, tuple_span) = - if self.match_token(&[Token::LeftParentheses]).is_some() { - let tuple_expression = self.delimited_tuple(Self::single_expression)?; - - if let Expression::Tuple { values: arguments } = - tuple_expression.expression - { - (arguments, Some(tuple_expression.span)) - } else { - unreachable!("self.tuple() must always produce a tuple"); - } - } else { - (Vec::new(), None) + let (extra_arguments, tuple_span) = if self + .match_token(&[Token::LeftParentheses]) + .is_some() + { + let tuple_ref = self.delimited_tuple(Self::single_expression)?; + let tuple_span = self.pool.get(tuple_ref).span; + let values = match &self.pool.get(tuple_ref).expression { + Expression::Tuple { values } => values.clone(), + _ => unreachable!("self.delimited_tuple() must always produce a tuple"), }; + (values, Some(tuple_span)) + } else { + (Vec::new(), None) + }; let mut arguments: Vec = vec![expr]; - arguments.extend(extra_arguments.into_iter().map(|a| self.pool.add(a))); + arguments.extend(extra_arguments); let function_id = self.pool.add( Expression::Identifier { @@ -753,12 +784,11 @@ impl Parser { self.require_current_token_matches(&Token::RightSquareBracket)?; let span = self.pool.get(expr).span.merge(end_token.span); - let index_ref = self.pool.add(index_expression); expr = self.pool.add( Expression::Index { value: expr, - index: index_ref, + index: index_expression, } .to_location(span), ); @@ -784,15 +814,17 @@ impl Parser { /// ```ndc /// [x + y for x in 0..10, y in 0..10, if x != y] /// ``` - fn list(&mut self) -> Result { + fn list(&mut self) -> Result { // Lists must begin with a `[` and the caller should have checked for this let left_square_bracket_span = self .require_current_token_matches(&Token::LeftSquareBracket)? .span; if let Some(bracket) = self.consume_token_if(&[Token::RightSquareBracket]) { - return Ok(Expression::List { values: vec![] } - .to_location(left_square_bracket_span.merge(bracket.span))); + return Ok(self.pool.add( + Expression::List { values: vec![] } + .to_location(left_square_bracket_span.merge(bracket.span)), + )); } // If this isn't an empty list we parse a tuple expression (without delimiters) consisting of @@ -811,19 +843,20 @@ impl Parser { self.advance(); - let Expression::Tuple { values } = expr.expression else { - unreachable!("tuple_expression must guarantee us a tuple"); + let values = match &self.pool.get(expr).expression { + Expression::Tuple { values } => values.clone(), + _ => unreachable!("tuple_expression must guarantee us a tuple"), }; - // Next we can maybe turn this into a list expression - //let last_value_span = values.last().map_or(left_square_bracket_span, |e| e.span); - - Ok(Expression::List { values } - .to_location(left_square_bracket_span.merge(right_square_bracket_span))) + Ok(self.pool.add( + Expression::List { values } + .to_location(left_square_bracket_span.merge(right_square_bracket_span)), + )) } // WOAH, this is not a list, it's a list comprehension Some(Token::For) => { - let result = ForBody::List(self.pool.add(expr.simplify())); + self.pool.simplify(expr); + let result = ForBody::List(expr); self.for_comprehension(left_square_bracket_span, result, &Token::RightSquareBracket) } _ => { @@ -845,7 +878,7 @@ impl Parser { span: Span, result: ForBody, end_token: &Token, - ) -> Result { + ) -> Result { self.require_current_token_matches(&Token::For) .expect("guaranteed to match"); let mut iterations = Vec::new(); @@ -874,11 +907,13 @@ impl Parser { let end = self.require_current_token_matches(end_token)?; - Ok(Expression::For { - body: Box::new(result), - iterations, - } - .to_location(span.merge(end.span))) + Ok(self.pool.add( + Expression::For { + body: Box::new(result), + iterations, + } + .to_location(span.merge(end.span)), + )) } fn if_guard(&mut self) -> Result { @@ -892,7 +927,9 @@ impl Parser { /// x in xs /// ``` fn for_iteration(&mut self) -> Result { - let l_value = Lvalue::try_from(self.tuple_expression(Self::primary, false)?)?; + let lvalue_ref = self.tuple_expression(Self::primary, false)?; + let lvalue_loc = self.pool.get(lvalue_ref).clone(); + let l_value = Lvalue::from_expression_location(lvalue_loc, &self.pool)?; self.require_current_token_matches(&Token::In)?; @@ -908,73 +945,70 @@ impl Parser { fn primary(&mut self) -> Result { // matches if expression like `if a < b { } else { }` if self.consume_token_if(&[Token::If]).is_some() { - let loc = self.if_expression()?; - return Ok(self.pool.add(loc)); + return self.if_expression(); } // matches while loops like `while foo < bar { }` else if self.consume_token_if(&[Token::While]).is_some() { - let loc = self.while_expression()?; - return Ok(self.pool.add(loc)); + return self.while_expression(); } // matches for loops like `for x in xs { }` else if self.match_token(&[Token::For]).is_some() { - let loc = self.for_expression()?; - return Ok(self.pool.add(loc)); + return self.for_expression(); } // matches function declarations like `fn function_name(arg1, arg2) { }` else if self.match_token(&[Token::Fn, Token::Pure]).is_some() { - let loc = self.function_declaration()?; - return Ok(self.pool.add(loc)); + return self.function_declaration(); } // matches `return;` and `return (expression);` else if let Some(return_token_location) = self.consume_token_if(&[Token::Return]) { - let expr_loc = if self.match_token(&[Token::Semicolon]).is_some() { - Expression::Tuple { values: vec![] }.to_location(return_token_location.span) + let value = if self.match_token(&[Token::Semicolon]).is_some() { + self.pool.add( + Expression::Tuple { values: vec![] }.to_location(return_token_location.span), + ) } else { self.expression()? }; - let span = expr_loc.span; - let value = self.pool.add(expr_loc); - - let return_expression = Expression::Return { value } - .to_location(return_token_location.span.merge(span)); - - return Ok(self.pool.add(return_expression)); + let span = self.pool.get(value).span; + return Ok(self.pool.add( + Expression::Return { value }.to_location(return_token_location.span.merge(span)), + )); } else if let Some(token_location) = self.consume_token_if(&[Token::Break]) { - return Ok(self.pool.add(Expression::Break.to_location(token_location.span))); + return Ok(self + .pool + .add(Expression::Break.to_location(token_location.span))); } else if let Some(token_location) = self.consume_token_if(&[Token::Continue]) { - return Ok(self.pool.add(Expression::Continue.to_location(token_location.span))); + return Ok(self + .pool + .add(Expression::Continue.to_location(token_location.span))); } // matches curly bracketed block expression `{ }` else if self.match_token(&[Token::LeftCurlyBracket]).is_some() { - let loc = self.block()?; - return Ok(self.pool.add(loc)); + return self.block(); } // matches map expression %{1,2,3} else if self.match_token(&[Token::MapOpen]).is_some() { - let loc = self.map_expression()?; - return Ok(self.pool.add(loc)); + return self.map_expression(); } // matches list and list comprehensions else if self.match_token(&[Token::LeftSquareBracket]).is_some() { - let loc = self.list()?; - return Ok(self.pool.add(loc)); + return self.list(); } // matches either a grouped expression `(1+1)` or a tuple `(1,1)` else if let Some(start_parentheses) = self.consume_token_if(&[Token::LeftParentheses]) { // If an opening parentheses is immediately followed by a closing parentheses we're dealing with a Unit expression if let Some(end_parentheses) = self.consume_token_if(&[Token::RightParentheses]) { - let loc = Expression::Tuple { values: vec![] } - .to_location(start_parentheses.span.merge(end_parentheses.span)); - return Ok(self.pool.add(loc)); + return Ok(self.pool.add( + Expression::Tuple { values: vec![] } + .to_location(start_parentheses.span.merge(end_parentheses.span)), + )); } let grouped = self.expression()?; self.require_current_token_matches(&Token::RightParentheses)?; - return Ok(self.pool.add(grouped)); + return Ok(grouped); } let token_location = self.require_current_token()?; @@ -1018,44 +1052,46 @@ impl Parser { /// /// } /// ``` - fn if_expression(&mut self) -> Result { - let expression = self.expression()?; - let span = expression.span; - let condition = self.pool.add(expression); - let on_true_loc = self.block()?; - let on_true = self.pool.add(on_true_loc); + fn if_expression(&mut self) -> Result { + let condition = self.expression()?; + let span = self.pool.get(condition).span; + let on_true = self.block()?; let on_false = if self.consume_token_if(&[Token::Else]).is_some() { if self.consume_token_if(&[Token::If]).is_some() { - let loc = self.if_expression()?; - Some(self.pool.add(loc)) + Some(self.if_expression()?) } else { - let loc = self.block()?; - Some(self.pool.add(loc)) + Some(self.block()?) } } else { None }; - Ok(Expression::If { - condition, - on_true, - on_false, - } - .to_location(span)) + Ok(self.pool.add( + Expression::If { + condition, + on_true, + on_false, + } + .to_location(span), + )) } - fn while_expression(&mut self) -> Result { - let expression_loc = self.expression()?; - let span = expression_loc.span; - let expression = self.pool.add(expression_loc); - let block = self.block()?; - let loop_body = self.pool.add(block); + fn while_expression(&mut self) -> Result { + let expression = self.expression()?; + let span = self.pool.get(expression).span; + let loop_body = self.block()?; - Ok(Expression::While { expression, loop_body }.to_location(span)) + Ok(self.pool.add( + Expression::While { + expression, + loop_body, + } + .to_location(span), + )) } - fn for_expression(&mut self) -> Result { + fn for_expression(&mut self) -> Result { let for_token_span = self .require_current_token_matches(&Token::For) .expect("required to be the correct token") @@ -1076,14 +1112,15 @@ impl Parser { } let body = self.block()?; - let body_span = body.span; - let body_ref = self.pool.add(body); + let body_span = self.pool.get(body).span; - Ok(Expression::For { - iterations, - body: Box::new(ForBody::Block(body_ref)), - } - .to_location(for_token_span.merge(body_span))) + Ok(self.pool.add( + Expression::For { + iterations, + body: Box::new(ForBody::Block(body)), + } + .to_location(for_token_span.merge(body_span)), + )) } fn require_identifier(&mut self) -> Result { @@ -1102,7 +1139,7 @@ impl Parser { } } - fn function_declaration(&mut self) -> Result { + fn function_declaration(&mut self) -> Result { let mut modifiers = Vec::new(); while let Some(token) = self.consume_token_if(&[Token::Fn, Token::Pure]) { @@ -1164,10 +1201,7 @@ impl Parser { self.advance(); self.single_expression()? } - Some(Token::LeftCurlyBracket) => { - let block = self.block()?; - self.pool.add(block) - } + Some(Token::LeftCurlyBracket) => self.block()?, Some(token) => { return Err(Error::with_help( format!("unexpected token: {token}"), @@ -1175,22 +1209,21 @@ impl Parser { "Expected that the argument list is followed by either a body `{}` or a fat arrow `=>`".to_string(), )) } - None => return Err(Error::end_of_input(argument_list.span)), + None => return Err(Error::end_of_input(self.pool.get(argument_list).span)), }; let span = fn_token.span.merge(self.pool.get(body).span); - let parameters = self.pool.add(argument_list); - Ok(ExpressionLocation { + Ok(self.pool.add(ExpressionLocation { expression: Expression::FunctionDeclaration { name: identifier, - parameters, + parameters: argument_list, body, return_type: None, // At some point in the future we could use type declarations here to insert the type (return type inference is cringe anyway) pure: is_pure, resolved_name: None, }, span, - }) + })) } /// Parses a block expression including the block delimiters `{` and `}` @@ -1202,7 +1235,7 @@ impl Parser { /// x /// } /// ``` - fn block(&mut self) -> Result { + fn block(&mut self) -> Result { let left_curly_span = self.require_token(&[Token::LeftCurlyBracket])?; let mut statements = Vec::new(); @@ -1222,10 +1255,12 @@ impl Parser { } }; - Ok(Expression::Block { statements }.to_location(left_curly_span.merge(loop_span))) + Ok(self + .pool + .add(Expression::Block { statements }.to_location(left_curly_span.merge(loop_span)))) } - fn map_expression(&mut self) -> Result { + fn map_expression(&mut self) -> Result { // This should have been checked before this method is called; let map_open_span = self.require_token(&[Token::MapOpen])?; @@ -1242,7 +1277,7 @@ impl Parser { None }; - let mut values: Vec<(ExpressionLocation, Option)> = Vec::new(); + let mut values: Vec<(ExpressionRef, Option)> = Vec::new(); let map_close_span = loop { // End parsing if we see a RightCurlyBracket, this one only happens if the expression is @@ -1251,12 +1286,10 @@ impl Parser { break token_location.span; } - let key_ref = self.single_expression()?; - let key = self.pool.get(key_ref).clone(); + let key = self.single_expression()?; if self.consume_token_if(&[Token::Colon]).is_some() { - let value_ref = self.single_expression()?; - let value = self.pool.get(value_ref).clone(); + let value = self.single_expression()?; values.push((key, Some(value))); } else { values.push((key, None)); @@ -1267,11 +1300,9 @@ impl Parser { } if values.len() == 1 && self.match_token(&[Token::For]).is_some() { - let (key_expr, value_expr) = values + let (key, value) = values .pop() .expect("guaranteed by previous call to values.len()"); - let key = self.pool.add(key_expr); - let value = value_expr.map(|v| self.pool.add(v)); return self.for_comprehension( map_open_span, ForBody::Map { @@ -1285,7 +1316,9 @@ impl Parser { self.require_current_token_matches(&Token::Comma)?; }; - Ok(Expression::Map { values, default }.to_location(map_open_span.merge(map_close_span))) + Ok(self.pool.add( + Expression::Map { values, default }.to_location(map_open_span.merge(map_close_span)), + )) } fn peek_range_end(&self) -> bool { matches!( From da9d68ad409380f0a2e27bc3dbcdfcee5b5cff10 Mon Sep 17 00:00:00 2001 From: Tim Fennis Date: Sun, 1 Mar 2026 18:32:19 +0100 Subject: [PATCH 3/5] =?UTF-8?q?=E2=9C=85=20Fix=20multi-iteration=20for=20l?= =?UTF-8?q?oop=20resolution=20and=20update=20test=20error=20messages?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- ndc_lib/src/interpreter/semantic/analyser.rs | 4 ++-- .../004_basic/005_block_scope_destroys_local_variables.ndc | 2 +- tests/programs/004_basic/026_op_assign_invalid_var.ndc | 2 +- tests/programs/005_functions/003_error_in_function.ndc | 2 +- .../005_functions/024_parameter_redefinition_error.ndc | 2 +- tests/programs/006_lists/030_augmented_assign_to_pattern.ndc | 2 +- tests/programs/013_vector_math/002_vector_error.ndc | 2 +- tests/programs/013_vector_math/003_vector_error2.ndc | 2 +- 8 files changed, 9 insertions(+), 9 deletions(-) diff --git a/ndc_lib/src/interpreter/semantic/analyser.rs b/ndc_lib/src/interpreter/semantic/analyser.rs index 8a2d5fca..dbe5f5af 100644 --- a/ndc_lib/src/interpreter/semantic/analyser.rs +++ b/ndc_lib/src/interpreter/semantic/analyser.rs @@ -404,7 +404,7 @@ impl Analyser { fn resolve_for_iterations( &mut self, - iterations: &mut Vec, + iterations: &mut [ForIteration], body: &mut Box, span: Span, pool: &mut ExpressionPool, @@ -438,7 +438,7 @@ impl Analyser { let out_type = if !tail.is_empty() { self.resolve_for_iterations( - &mut tail.to_vec(), + tail, body, span, pool, diff --git a/tests/programs/004_basic/005_block_scope_destroys_local_variables.ndc b/tests/programs/004_basic/005_block_scope_destroys_local_variables.ndc index c99dadf9..80874d6f 100644 --- a/tests/programs/004_basic/005_block_scope_destroys_local_variables.ndc +++ b/tests/programs/004_basic/005_block_scope_destroys_local_variables.ndc @@ -1,4 +1,4 @@ -// expect-error: Identifier x has not previously been declared +// expect-error: Identifier 'x' not previously declared { let x = 5; } diff --git a/tests/programs/004_basic/026_op_assign_invalid_var.ndc b/tests/programs/004_basic/026_op_assign_invalid_var.ndc index 6a51c391..56a8da6f 100644 --- a/tests/programs/004_basic/026_op_assign_invalid_var.ndc +++ b/tests/programs/004_basic/026_op_assign_invalid_var.ndc @@ -1,3 +1,3 @@ -// expect-error: Identifier x has not previously been declared +// expect-error: Identifier 'x' not previously declared x += 3; print(x); diff --git a/tests/programs/005_functions/003_error_in_function.ndc b/tests/programs/005_functions/003_error_in_function.ndc index 49fb9e7c..6bc93aa2 100644 --- a/tests/programs/005_functions/003_error_in_function.ndc +++ b/tests/programs/005_functions/003_error_in_function.ndc @@ -1,4 +1,4 @@ -// expect-error: Identifier n has not previously been declared +// expect-error: Identifier 'n' not previously declared fn test() { n + 3 // n does not exist } diff --git a/tests/programs/005_functions/024_parameter_redefinition_error.ndc b/tests/programs/005_functions/024_parameter_redefinition_error.ndc index 9c7bed7b..22581911 100644 --- a/tests/programs/005_functions/024_parameter_redefinition_error.ndc +++ b/tests/programs/005_functions/024_parameter_redefinition_error.ndc @@ -1,4 +1,4 @@ -// expect-error: Illegal redefinition of parameter a +// expect-error: Parameter 'a' is defined more than once fn foo(a, a, a, a) { print(a) } \ No newline at end of file diff --git a/tests/programs/006_lists/030_augmented_assign_to_pattern.ndc b/tests/programs/006_lists/030_augmented_assign_to_pattern.ndc index b5fe1a95..dda96968 100644 --- a/tests/programs/006_lists/030_augmented_assign_to_pattern.ndc +++ b/tests/programs/006_lists/030_augmented_assign_to_pattern.ndc @@ -1,3 +1,3 @@ -// expect-error: This lvalue is required to be a single identifier +// expect-error: Left-hand side of augmented assignment must be a single identifier or index expression let x, y = 0, 0; x, y += 1; // ERROR diff --git a/tests/programs/013_vector_math/002_vector_error.ndc b/tests/programs/013_vector_math/002_vector_error.ndc index 8aaf34a0..6288999c 100644 --- a/tests/programs/013_vector_math/002_vector_error.ndc +++ b/tests/programs/013_vector_math/002_vector_error.ndc @@ -1,2 +1,2 @@ -// expect-error: no function called '+' found matches the arguments +// expect-error: no function called '+' found that matches the arguments (1,2) + (5,3,2) diff --git a/tests/programs/013_vector_math/003_vector_error2.ndc b/tests/programs/013_vector_math/003_vector_error2.ndc index 49542e40..a0e3b01e 100644 --- a/tests/programs/013_vector_math/003_vector_error2.ndc +++ b/tests/programs/013_vector_math/003_vector_error2.ndc @@ -1,3 +1,3 @@ -// expect-error: no function called '+' found matches the arguments +// expect-error: no function called '+' found that matches the arguments // This looks valid, but isn't (1,1,(1,)) + (1,1,(1,)) From 45433b7f4f77395fb727fbcf5ff5432c8368c2dc Mon Sep 17 00:00:00 2001 From: Tim Fennis Date: Sun, 1 Mar 2026 18:40:26 +0100 Subject: [PATCH 4/5] =?UTF-8?q?=E2=9C=A8=20Add=20compare.sh=20for=20hyperf?= =?UTF-8?q?ine=20performance=20comparison=20between=20branches?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- compare.sh | 45 +++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 45 insertions(+) create mode 100755 compare.sh diff --git a/compare.sh b/compare.sh new file mode 100755 index 00000000..af1b1b29 --- /dev/null +++ b/compare.sh @@ -0,0 +1,45 @@ +#!/usr/bin/env bash +set -euo pipefail + +if [ $# -lt 3 ]; then + echo "Usage: $0 " >&2 + exit 1 +fi + +BRANCH1="$1" +BRANCH2="$2" +SCRIPT="$(realpath "$3")" +REPO_ROOT="$(git -C "$(dirname "$0")" rev-parse --show-toplevel)" +WORKDIR="$(mktemp -d)" + +cleanup() { + git -C "$REPO_ROOT" worktree remove --force "$WORKDIR/b1" 2>/dev/null || true + git -C "$REPO_ROOT" worktree remove --force "$WORKDIR/b2" 2>/dev/null || true + rm -rf "$WORKDIR" +} +trap cleanup EXIT + +build_branch() { + local branch="$1" + local worktree="$2" + local out="$3" + + echo "==> Building $branch..." + git -C "$REPO_ROOT" worktree add --quiet --detach "$worktree" "$branch" + cargo build --release --quiet --manifest-path "$worktree/Cargo.toml" -p ndc_bin 2>&1 + cp "$worktree/target/release/ndc" "$out" + echo " Built $branch -> $out" +} + +BIN1="$WORKDIR/ndc-$(echo "$BRANCH1" | tr '/' '-')" +BIN2="$WORKDIR/ndc-$(echo "$BRANCH2" | tr '/' '-')" + +build_branch "$BRANCH1" "$WORKDIR/b1" "$BIN1" +build_branch "$BRANCH2" "$WORKDIR/b2" "$BIN2" + +echo "" +hyperfine \ + --warmup 3 \ + --shell none \ + -n "$BRANCH1" "$BIN1 $SCRIPT" \ + -n "$BRANCH2" "$BIN2 $SCRIPT" From 871a3c35bb94138477d0bbfa713406e492895320 Mon Sep 17 00:00:00 2001 From: Tim Fennis Date: Sun, 1 Mar 2026 18:41:27 +0100 Subject: [PATCH 5/5] =?UTF-8?q?=E2=9C=85=20Wire=20analyser=20back=20into?= =?UTF-8?q?=20parse=5Fand=5Fanalyse?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- ndc_lib/src/interpreter/mod.rs | 18 ++++++++---------- 1 file changed, 8 insertions(+), 10 deletions(-) diff --git a/ndc_lib/src/interpreter/mod.rs b/ndc_lib/src/interpreter/mod.rs index f8430521..2998e644 100644 --- a/ndc_lib/src/interpreter/mod.rs +++ b/ndc_lib/src/interpreter/mod.rs @@ -58,19 +58,17 @@ impl Interpreter { fn parse_and_analyse(&mut self, input: &str) -> Result { let tokens = Lexer::new(input).collect::, _>>()?; - let expressions = ndc_parser::Parser::from_tokens(tokens).parse()?; + let mut expressions = ndc_parser::Parser::from_tokens(tokens).parse()?; let checkpoint = self.analyser.checkpoint(); - dbg!(&expressions); - - // TODO: add back the analyser - // for e in &mut expressions { - // if let Err(e) = self.analyser.analyse(e) { - // self.analyser.restore(checkpoint); - // return Err(e.into()); - // } - // } + let root_refs = expressions.root_expressions().copied().collect::>(); + for expr_ref in root_refs { + if let Err(e) = self.analyser.analyse(expr_ref, &mut expressions) { + self.analyser.restore(checkpoint); + return Err(e.into()); + } + } Ok(expressions) }