From 38de69dc93d46757273d8fce7c5e6a849900d32f Mon Sep 17 00:00:00 2001 From: Shautvast Date: Tue, 9 Dec 2025 21:03:49 +0100 Subject: [PATCH] non-constant range expressions --- examples/fibonacci/fib.tp | 6 +++-- src/compiler/assembly_pass.rs | 25 +++++++++++++-------- src/compiler/ast_pass.rs | 15 ++++++++++--- src/compiler/compiler_tests.rs | 15 +++++++++++++ src/compiler/scan_pass.rs | 41 +++++++++++++++++++++++++++------- src/value.rs | 9 ++++++-- 6 files changed, 87 insertions(+), 24 deletions(-) diff --git a/examples/fibonacci/fib.tp b/examples/fibonacci/fib.tp index 669f738..64cac89 100644 --- a/examples/fibonacci/fib.tp +++ b/examples/fibonacci/fib.tp @@ -1,6 +1,8 @@ println("Fibonacci sequence:") let fib = [1,1] -for i in 2..10: +let r=10 + +for i in 2..r: fib.push(fib[i-2] + fib[i-1]) - println(fib) \ No newline at end of file + println(fib) diff --git a/src/compiler/assembly_pass.rs b/src/compiler/assembly_pass.rs index ff722a7..63db826 100644 --- a/src/compiler/assembly_pass.rs +++ b/src/compiler/assembly_pass.rs @@ -212,7 +212,7 @@ impl AsmPass { self.emit(Goto(0)); let goto_addr2 = self.chunk.code.len() - 1; // placeholder self.chunk.code[goto_addr1] = GotoIfNot(self.chunk.code.len()); - self.chunk.code[goto_addr2] = Op::Goto(self.chunk.code.len()); + self.chunk.code[goto_addr2] = Goto(self.chunk.code.len()); } IfElseExpression { condition, @@ -435,8 +435,8 @@ impl AsmPass { Expression::FieldGet { .. } => {} Expression::Range { lower, upper, .. } => { // opposite order, because we have to assign last one first to the loop variable - self.compile_expression(namespace, upper, symbols, registry)?; - self.compile_expression(namespace, lower, symbols, registry)?; + // self.compile_expression(namespace, upper, symbols, registry)?; + // self.compile_expression(namespace, lower, symbols, registry)?; } Expression::ForStatement { loop_var, @@ -446,17 +446,24 @@ impl AsmPass { // 1. step var index let step_const_index = self.emit_constant(Value::I64(1)); // 2. range expression - self.compile_expression(namespace, range, symbols, registry)?; - //save the constants for lower and upper bounds of the range - let start_index = self.chunk.constants.len() - 1; - let end_index = self.chunk.constants.len() - 2; + // self.compile_expression(namespace, range, symbols, registry)?; + // //save the constants for lower and upper bounds of the range + // let start_index = self.chunk.constants.len() - 1; + // let end_index = self.chunk.constants.len() - 2; let name = loop_var.lexeme.as_str(); let loop_var_name_index = self.chunk.add_var(&loop_var.token_type, name); self.vars.insert(name.to_string(), loop_var_name_index); // 3. start index - self.emit(Constant(start_index)); + let end= + if let Expression::Range { lower, upper, .. } = range.deref() { + self.compile_expression(namespace, lower, symbols, registry)?; + upper.clone() + } else { + unreachable!("range expression should be a range expression") + }; + self.emit(Assign(loop_var_name_index)); let return_addr = self.chunk.code.len(); @@ -465,7 +472,7 @@ impl AsmPass { self.emit(Constant(step_const_index)); self.emit(Add); self.emit(Assign(loop_var_name_index)); - self.emit(Constant(end_index)); + self.compile_expression(namespace, &end, symbols, registry)?; self.emit(Get(loop_var_name_index)); self.emit(GreaterEqual); self.emit(GotoIf(return_addr)); diff --git a/src/compiler/ast_pass.rs b/src/compiler/ast_pass.rs index 7243962..015fd15 100644 --- a/src/compiler/ast_pass.rs +++ b/src/compiler/ast_pass.rs @@ -7,8 +7,8 @@ use crate::compiler::tokens::TokenType::{ Bang, Bool, Char, Colon, DateTime, Dot, Else, Eof, Eol, Equal, False, FloatingPoint, Fn, For, Greater, GreaterEqual, GreaterGreater, Identifier, If, In, Indent, Integer, LeftBrace, LeftBracket, LeftParen, Less, LessEqual, LessLess, Let, ListType, MapType, Minus, Object, Plus, - Range, RightBrace, RightBracket, RightParen, SingleRightArrow, Slash, Star, StringType, - True, U32, U64, Unknown, + Range, RightBrace, RightBracket, RightParen, SingleRightArrow, Slash, Star, StringType, True, + U32, U64, Unknown, }; use crate::compiler::tokens::{Token, TokenType}; use crate::errors::CompilerError::{ @@ -686,7 +686,8 @@ impl AstCompiler { debug!("{:?}", token); if self.match_token(&[LeftParen]) { self.function_call(token.clone(), symbol_table)? - } else if self.match_token(&[Colon]) { + } else if self.peek().token_type == Colon && *self.peek_next() != Eol { + self.advance(); self.named_parameter(&token, symbol_table)? } else { self.variable_lookup(&token, symbol_table)? @@ -823,6 +824,14 @@ impl AstCompiler { &self.tokens[self.current] } + fn peek_next(&self) -> &TokenType { + if self.current + 1 >= self.tokens.len() { + &Unknown + } else { + &self.tokens[self.current + 1].token_type + } + } + fn previous(&self) -> &Token { &self.tokens[self.current - 1] } diff --git a/src/compiler/compiler_tests.rs b/src/compiler/compiler_tests.rs index 1819408..d6e7357 100644 --- a/src/compiler/compiler_tests.rs +++ b/src/compiler/compiler_tests.rs @@ -406,6 +406,21 @@ sum ); } + #[test] + fn non_constant_range_loop() { + assert_eq!( + run(r#" +let sum=0 +let s = 1 +let f = 5 +for a in s..f: + sum = sum + a +sum +"#), + Ok(Value::I64(15)) + ); + } + #[test] fn global_function_call() { let value = run(r#"now()"#); diff --git a/src/compiler/scan_pass.rs b/src/compiler/scan_pass.rs index fea70ea..b51c594 100644 --- a/src/compiler/scan_pass.rs +++ b/src/compiler/scan_pass.rs @@ -1,11 +1,12 @@ -use crate::errors::CompilerError::{IllegalCharLength, UnexpectedIdentifier, Unterminated}; -use crate::errors::{CompilerError, CompilerErrorAtLine}; -use crate::compiler::tokens::TokenType::{BitXor, FloatingPoint, Integer, Question, U32, U64}; -use crate::keywords; +use crate::compiler::tokens::TokenType::{BitXor, FloatingPoint, In, Integer, Question, U32, U64}; use crate::compiler::tokens::{ Token, TokenType::{self}, }; +use crate::errors::CompilerError::{IllegalCharLength, UnexpectedIdentifier, Unterminated}; +use crate::errors::{CompilerError, CompilerErrorAtLine}; +use crate::keywords; +use axum::http::header::ToStrError; pub fn scan(source: &str) -> Result, CompilerErrorAtLine> { let scanner = Scanner { @@ -47,7 +48,14 @@ impl Scanner { '[' => self.add_token(TokenType::LeftBracket), ']' => self.add_token(TokenType::RightBracket), ',' => self.add_token(TokenType::Comma), - '.' => self.add_token(TokenType::Dot), + '.' => { + let t = if self.match_next('.') { + TokenType::Range + } else { + TokenType::Dot + }; + self.add_token(t); + } '-' => { let t = if self.match_next('>') { TokenType::SingleRightArrow @@ -203,14 +211,31 @@ impl Scanner { let lower: String = self.chars[self.start..self.current].iter().collect(); self.match_next('.'); self.match_next('.'); - self.add_token_with_value(Integer, lower); + let tokentype = if lower.parse::().is_err() { + TokenType::Identifier + } else { + Integer + }; + self.add_token_with_value(tokentype, lower); self.add_token(TokenType::Range); self.start = self.current; - while self.peek().is_ascii_digit() { + let tokentype = if self.peek().is_ascii_digit() { + Integer + } else { + TokenType::Identifier + }; + if self.peek().is_ascii_digit() { + while self.peek().is_ascii_digit() { + self.advance(); + } + } else { self.advance(); + while self.peek().is_alphanumeric() { + self.advance(); + } } let upper: String = self.chars[self.start..self.current].iter().collect(); - self.add_token_with_value(Integer, upper); + self.add_token_with_value(tokentype, upper); } fn char(&mut self) -> Result<(), CompilerErrorAtLine> { diff --git a/src/value.rs b/src/value.rs index fa4c8eb..0fac67f 100644 --- a/src/value.rs +++ b/src/value.rs @@ -1,3 +1,4 @@ +use crate::DATE_FORMAT_TIMEZONE; use crate::errors::ValueError; use chrono::{DateTime, Utc}; use std::cmp::Ordering; @@ -5,7 +6,6 @@ use std::collections::HashMap; use std::fmt::{Display, Formatter}; use std::hash::{Hash, Hasher}; use std::ops::{Add, BitAnd, BitOr, BitXor, Div, Mul, Neg, Not, Shl, Shr, Sub}; -use crate::DATE_FORMAT_TIMEZONE; #[derive(Debug, Clone)] pub struct Object { @@ -190,7 +190,12 @@ impl Display for Value { Value::DateTime(v) => write!(f, "{}", v.format(DATE_FORMAT_TIMEZONE)), Value::Enum => write!(f, "enum"), Value::ObjectType(o) => write!(f, "{}: {:?}", o.definition, o.fields), - Value::List(v) => write!(f, "{:?}", v), + Value::List(v) => { + for i in &v[0..v.len() - 1] { + write!(f, "{}, ", i)?; + } + write!(f, "{}", v[v.len() - 1]) + } Value::Map(map) => to_string(f, map), Value::Error(v) => write!(f, "{}", v), Value::Void => write!(f, "()"),