Indexing into lists/maps.

First test with literal list completed. When it's assigned to a var if fails
This commit is contained in:
Shautvast 2025-11-09 21:56:59 +01:00
parent 27fbf4b828
commit 61b9d86aa2
7 changed files with 182 additions and 50 deletions

View file

@ -1,12 +1,14 @@
use crate::ast_compiler::Expression::{FunctionCall, NamedParameter, Stop, Variable}; use crate::ast_compiler::Expression::{
FieldGet, FunctionCall, ListGet, MapGet, NamedParameter, Stop, Variable,
};
use crate::errors::CompilerError::{ use crate::errors::CompilerError::{
self, Expected, IncompatibleTypes, ParseError, TooManyParameters, TypeError, self, Expected, IncompatibleTypes, ParseError, TooManyParameters, TypeError,
UndeclaredVariable, UnexpectedIndent, UninitializedVariable, UndeclaredVariable, UnexpectedIndent, UninitializedVariable,
}; };
use crate::errors::CompilerErrorAtLine; use crate::errors::CompilerErrorAtLine;
use crate::tokens::TokenType::{ use crate::tokens::TokenType::{
Bang, Bool, Char, Colon, DateTime, Dot, Eof, Eol, Equal, F32, F64, False, FloatingPoint, Bang, Bool, Char, Colon, DateTime, Dot, Eof, Eol, Equal, F32, F64, False, FloatingPoint, Fn,
Fn, Greater, GreaterEqual, GreaterGreater, I32, I64, Identifier, Indent, Integer, LeftBrace, Greater, GreaterEqual, GreaterGreater, I32, I64, Identifier, Indent, Integer, LeftBrace,
LeftBracket, LeftParen, Less, LessEqual, LessLess, Let, ListType, MapType, Minus, Object, Plus, LeftBracket, LeftParen, Less, LessEqual, LessLess, Let, ListType, MapType, Minus, Object, Plus,
Print, RightBrace, RightBracket, RightParen, SignedInteger, SingleRightArrow, Slash, Star, Print, RightBrace, RightBracket, RightParen, SignedInteger, SingleRightArrow, Slash, Star,
StringType, True, U32, U64, UnsignedInteger, StringType, True, U32, U64, UnsignedInteger,
@ -14,6 +16,7 @@ use crate::tokens::TokenType::{
use crate::tokens::{Token, TokenType}; use crate::tokens::{Token, TokenType};
use crate::value::Value; use crate::value::Value;
use log::debug; use log::debug;
use tokio_postgres::fallible_iterator::FallibleIterator;
pub fn compile( pub fn compile(
path: Option<&str>, path: Option<&str>,
@ -401,10 +404,63 @@ impl AstCompiler {
right: Box::new(right), right: Box::new(right),
}) })
} else { } else {
self.primary() let expr = self.get();
expr
} }
} }
fn get(&mut self) -> Result<Expression, CompilerErrorAtLine> {
let expr = self.primary()?;
if self.match_token(vec![LeftParen]) {
let name = self.peek().clone();
self.advance();
self.function_call(name)
} else if self.match_token(vec![LeftBracket]) {
let name = self.peek().clone();
self.advance();
self.index(expr, name)
} else if self.match_token(vec![Dot]){
let name = self.peek().clone();
self.advance();
self.field(expr, name)
} else {
Ok(expr)
}
}
fn index(
&mut self,
operand: Expression,
index: Token,
) -> Result<Expression, CompilerErrorAtLine> {
let get = (match operand {
Expression::Map { .. } => MapGet {
key: index.lexeme.clone(),
},
Expression::List { .. } => ListGet {
list: Box::new(operand),
index: index.lexeme.clone().parse().map_err(|_| {
self.raise(CompilerError::IllegalTypeToIndex(index.lexeme.clone()))
})?,
},
_ => return Err(self.raise(CompilerError::IllegalTypeToIndex("".to_string()))),
});
self.consume(RightBracket, Expected("']' after index."))?;
Ok(get)
}
fn field(
&mut self,
operand: Expression,
index: Token,
) -> Result<Expression, CompilerErrorAtLine> {
//TODO?
Ok(Expression::FieldGet {
field: index.lexeme.clone(),
})
}
fn primary(&mut self) -> Result<Expression, CompilerErrorAtLine> { fn primary(&mut self) -> Result<Expression, CompilerErrorAtLine> {
debug!("primary {:?}", self.peek()); debug!("primary {:?}", self.peek());
Ok(if self.match_token(vec![LeftBracket]) { Ok(if self.match_token(vec![LeftBracket]) {
@ -500,34 +556,35 @@ impl AstCompiler {
debug!("{:?}", token); debug!("{:?}", token);
// function call? // function call?
if self.match_token(vec![LeftParen]) { if self.match_token(vec![LeftParen]) {
self.function_call(token.lexeme)? self.function_call(token.clone())?
} else if self.match_token(vec![Colon]) { } else if self.match_token(vec![Colon]) {
self.named_parameter(&token)? self.named_parameter(&token)?
} else if self.check(Dot) { } else {
// } else if self.check(Dot) {
// chain of variable or function lookups? // chain of variable or function lookups?
let mut name = "/".to_string(); // let mut name = "/".to_string();
name.push_str(&self.previous().lexeme); // name.push_str(&self.previous().lexeme);
while self.match_token(vec![Dot]) { // while self.match_token(vec![Dot]) {
name.push_str("/"); // name.push_str("/");
name.push_str(&self.peek().lexeme); // name.push_str(&self.peek().lexeme);
self.advance(); // self.advance();
} // }
// chained function call? // chained function call?
if self.match_token(vec![LeftParen]) { // if self.match_token(vec![LeftParen]) {
self.function_call(name)? // self.function_call(name())?
} else { // } else {
// empty line // empty line
return if self.match_token(vec![Eol, Eof]) { // return if self.match_token(vec![Eol, Eof]) {
Ok(Expression::Literal { // Ok(Expression::Literal {
value: Value::Void, // value: Value::Void,
literaltype: Object, // literaltype: Object,
line: token.line, // line: token.line,
}) // })
} else { // } else {
Err(self.raise(UndeclaredVariable(token.lexeme.clone()))) // Err(self.raise(UndeclaredVariable(token.lexeme.clone())))
}; // };
} // }
} else { // } else {
// none of the above, must be a variable lookup // none of the above, must be a variable lookup
self.variable_lookup(&token)? self.variable_lookup(&token)?
} }
@ -591,7 +648,7 @@ impl AstCompiler {
}) })
} }
fn function_call(&mut self, name: String) -> Result<Expression, CompilerErrorAtLine> { fn function_call(&mut self, name: Token) -> Result<Expression, CompilerErrorAtLine> {
let mut arguments = vec![]; let mut arguments = vec![];
while !self.match_token(vec![RightParen]) { while !self.match_token(vec![RightParen]) {
if arguments.len() >= 25 { if arguments.len() >= 25 {
@ -608,7 +665,7 @@ impl AstCompiler {
} }
Ok(FunctionCall { Ok(FunctionCall {
line: self.peek().line, line: self.peek().line,
name, name: name.lexeme.to_string(),
arguments, arguments,
}) })
} }
@ -759,15 +816,25 @@ pub enum Expression {
Stop { Stop {
line: usize, line: usize,
}, },
PathMatch { // PathMatch {
line: usize, // line: usize,
condition: Box<Expression>, // condition: Box<Expression>,
}, // },
NamedParameter { NamedParameter {
line: usize, line: usize,
name: Token, name: Token,
value: Box<Expression>, value: Box<Expression>,
}, },
MapGet {
key: String,
},
ListGet {
list: Box<Expression>,
index: usize,
},
FieldGet {
field: String,
},
} }
impl Expression { impl Expression {
@ -782,8 +849,28 @@ impl Expression {
Variable { line, .. } => *line, Variable { line, .. } => *line,
FunctionCall { line, .. } => *line, FunctionCall { line, .. } => *line,
Stop { line } => *line, Stop { line } => *line,
Expression::PathMatch { line, .. } => *line, // Expression::PathMatch { line, .. } => *line,
NamedParameter { line, .. } => *line, NamedParameter { line, .. } => *line,
MapGet { .. } => 0,
ListGet { .. } => 0,
FieldGet { .. } => 0,
} }
} }
// pub fn get_type(&self) -> &str {
// match self {
// Expression::Binary { .. } => "binary",
// Expression::Unary { .. } => TokenType::Unknown,
// Expression::Grouping { .. } => TokenType::Unknown,
// Expression::Literal { literaltype, .. } => literaltype.clone(),
// Expression::List { literaltype, .. } => literaltype.clone(),
// Expression::Map { literaltype, .. } => literaltype.clone(),
// Expression::Variable { var_type, .. } => var_type.clone(),
// Expression::FunctionCall { .. } => TokenType::Unknown,
// Expression::Stop { .. } => TokenType::Unknown,
// Expression::NamedParameter { .. } => TokenType::Unknown,
// Expression::MapGet { .. } => TokenType::Unknown,
// Expression::ListGet { .. } => TokenType::Unknown,
// }
// }
} }

View file

@ -1,7 +1,7 @@
use crate::ast_compiler::Expression::NamedParameter; use crate::ast_compiler::Expression::NamedParameter;
use crate::ast_compiler::{Expression, Function, Parameter, Statement}; use crate::ast_compiler::{Expression, Function, Statement};
use crate::chunk::Chunk; use crate::chunk::Chunk;
use crate::errors::{CompilerError, CompilerErrorAtLine, RuntimeError}; use crate::errors::{CompilerError, CompilerErrorAtLine};
use crate::symbol_builder::{Symbol, calculate_type, infer_type}; use crate::symbol_builder::{Symbol, calculate_type, infer_type};
use crate::tokens::TokenType; use crate::tokens::TokenType;
use crate::tokens::TokenType::Unknown; use crate::tokens::TokenType::Unknown;
@ -9,7 +9,8 @@ use crate::value::Value;
use crate::vm::{ use crate::vm::{
OP_ADD, OP_AND, OP_ASSIGN, OP_BITAND, OP_BITOR, OP_BITXOR, OP_CALL, OP_CONSTANT, OP_DEF_LIST, OP_ADD, OP_AND, OP_ASSIGN, OP_BITAND, OP_BITOR, OP_BITXOR, OP_CALL, OP_CONSTANT, OP_DEF_LIST,
OP_DEF_MAP, OP_DIVIDE, OP_EQUAL, OP_GET, OP_GREATER, OP_GREATER_EQUAL, OP_LESS, OP_LESS_EQUAL, OP_DEF_MAP, OP_DIVIDE, OP_EQUAL, OP_GET, OP_GREATER, OP_GREATER_EQUAL, OP_LESS, OP_LESS_EQUAL,
OP_MULTIPLY, OP_NEGATE, OP_NOT, OP_OR, OP_PRINT, OP_RETURN, OP_SHL, OP_SHR, OP_SUBTRACT, OP_LIST_GET, OP_MULTIPLY, OP_NEGATE, OP_NOT, OP_OR, OP_PRINT, OP_RETURN, OP_SHL, OP_SHR,
OP_SUBTRACT,
}; };
use std::collections::HashMap; use std::collections::HashMap;
@ -220,7 +221,7 @@ impl Compiler {
)); ));
} }
} }
Expression::Variable { name, line,.. } => { Expression::Variable { name, line, .. } => {
let name_index = self.vars.get(name); let name_index = self.vars.get(name);
if let Some(name_index) = name_index { if let Some(name_index) = name_index {
self.emit_bytes(OP_GET, *name_index as u16); self.emit_bytes(OP_GET, *name_index as u16);
@ -292,9 +293,16 @@ impl Compiler {
_ => unimplemented!("binary other than plus, minus, star, slash"), _ => unimplemented!("binary other than plus, minus, star, slash"),
} }
} }
Expression::Stop { line } => {} Expression::Stop { .. } => {}
Expression::PathMatch { line, .. } => {} // Expression::PathMatch { line, .. } => {}
Expression::NamedParameter { line, .. } => {} NamedParameter { .. } => {}
Expression::ListGet { index, list} => {
self.compile_expression(namespace, list, symbols, registry)?;
self.emit_byte(OP_LIST_GET);
self.emit_bytes((index >> 16) as u16, *index as u16);
}
Expression::MapGet { .. } => {}
Expression::FieldGet { .. } => {}
} }
Ok(()) Ok(())
} }

View file

@ -2,7 +2,7 @@
mod tests { mod tests {
use crate::value::Value; use crate::value::Value;
use crate::{compile, run}; use crate::{compile, run};
use chrono::{DateTime, FixedOffset, NaiveDate, TimeZone}; use chrono::{DateTime};
#[test] #[test]
fn literal_int() { fn literal_int() {
@ -35,6 +35,17 @@ mod tests {
); );
} }
#[test]
fn index_in_list_literal() {
assert_eq!(run(r#"["abc","def"][0]"#), Ok(Value::String("abc".into())))
}
#[test]
fn index_in_list_as_var() {
assert_eq!(run(r#"let a:list = ["abc","def"]
a[1]"#), Ok(Value::String("def".into())))
}
#[test] #[test]
fn infer_type() { fn infer_type() {
assert_eq!( assert_eq!(
@ -209,11 +220,21 @@ m"#);
run(r#"let date:datetime = d"2025-11-09 16:44:28.000 +0100" run(r#"let date:datetime = d"2025-11-09 16:44:28.000 +0100"
date"#), date"#),
Ok(Value::DateTime( Ok(Value::DateTime(
DateTime::parse_from_str("2025-11-09 16:44:28.000 +0100", "%Y-%m-%d %H:%M:%S%.3f %z").unwrap().into() DateTime::parse_from_str(
"2025-11-09 16:44:28.000 +0100",
"%Y-%m-%d %H:%M:%S%.3f %z"
)
.unwrap()
.into()
)) ))
); );
} }
// #[test]
// fn string_reverse(){
// assert_eq!(run(r#""abc".reverse()"#), Ok(Value::String("cba".into())));
// }
// #[test] // #[test]
// fn package() { // fn package() {
// assert_eq!(run(r#"a.b.c()"#), Ok(Value::U32(48))); // assert_eq!(run(r#"a.b.c()"#), Ok(Value::U32(48)));

View file

@ -1,5 +1,5 @@
use crate::tokens::TokenType;
use std::fmt::Display; use std::fmt::Display;
use crate::tokens::{Token, TokenType};
use thiserror::Error; use thiserror::Error;
#[derive(Error, Debug, PartialEq)] #[derive(Error, Debug, PartialEq)]
@ -64,7 +64,11 @@ pub enum CompilerError {
#[error("Crud does not support numbers above 2^64")] #[error("Crud does not support numbers above 2^64")]
Overflow, Overflow,
#[error("Undeclared function: '{0}'")] #[error("Undeclared function: '{0}'")]
FunctionNotFound(String) FunctionNotFound(String),
#[error("Illegal argument: '{0}' cannot be used as an index into a list")]
IllegalIndexArgument(TokenType),
#[error("Illegal argument: '{0}' cannot be indexed")]
IllegalTypeToIndex(String),
} }
#[derive(Error, Debug, PartialEq)] #[derive(Error, Debug, PartialEq)]

View file

@ -75,7 +75,7 @@ pub fn build(path: &str, ast: &[Statement], symbols: &mut HashMap<String, Symbol
} }
} }
pub fn add_types( pub fn _add_types(
path: &str, path: &str,
ast: &[Statement], ast: &[Statement],
symbols: &mut HashMap<String, Symbol>, symbols: &mut HashMap<String, Symbol>,
@ -225,7 +225,10 @@ pub fn infer_type(expr: &Expression, symbols: &HashMap<String, Symbol>) -> Token
} }
} }
Expression::Stop { .. } => TokenType::Unknown, Expression::Stop { .. } => TokenType::Unknown,
Expression::PathMatch { .. } => TokenType::Unknown, // Expression::PathMatch { .. } => TokenType::Unknown,
Expression::NamedParameter { .. } => TokenType::Unknown, Expression::NamedParameter { .. } => TokenType::Unknown,
Expression::ListGet { .. } => TokenType::Unknown,
Expression::MapGet { .. } => TokenType::Unknown,
Expression::FieldGet { .. } => TokenType::Unknown,
} }
} }

View file

@ -156,7 +156,6 @@ impl fmt::Display for TokenType {
TokenType::SingleRightArrow => write!(f, "->"), TokenType::SingleRightArrow => write!(f, "->"),
TokenType::Slash => write!(f, "/"), TokenType::Slash => write!(f, "/"),
TokenType::Star => write!(f, "*"), TokenType::Star => write!(f, "*"),
TokenType::DateTime => write!(f, "t\""),
TokenType::True => write!(f, "true"), TokenType::True => write!(f, "true"),
TokenType::Unknown => write!(f, "?"), TokenType::Unknown => write!(f, "?"),
TokenType::Void => write!(f, "()"), TokenType::Void => write!(f, "()"),

View file

@ -190,6 +190,15 @@ impl Vm {
let value = self.local_vars.get(name_index).unwrap(); let value = self.local_vars.get(name_index).unwrap();
self.push(value.clone()); // not happy , take ownership, no clone self.push(value.clone()); // not happy , take ownership, no clone
} }
OP_LIST_GET => {
let index_high = self.read(chunk);
let index_low = self.read(chunk);
let index = index_high <<16 + index_low;
let list = self.pop();
if let Value::List(list) = list {
self.push(list.get(index).cloned().unwrap())
}
}
OP_CALL => { OP_CALL => {
let function_name_index = self.read(chunk); let function_name_index = self.read(chunk);
let num_args = self.read(chunk); let num_args = self.read(chunk);
@ -309,3 +318,4 @@ pub const OP_DEF_STRUCT: u16 = 38;
pub const OP_DEF_F32: u16 = 39; pub const OP_DEF_F32: u16 = 39;
pub const OP_DEF_F64: u16 = 40; pub const OP_DEF_F64: u16 = 40;
pub const OP_ASSIGN: u16 = 41; pub const OP_ASSIGN: u16 = 41;
pub const OP_LIST_GET: u16 = 42;