Indexing into lists/maps.
First test with literal list completed. When it's assigned to a var if fails
This commit is contained in:
parent
27fbf4b828
commit
61b9d86aa2
7 changed files with 182 additions and 50 deletions
|
|
@ -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,
|
||||||
|
// }
|
||||||
|
// }
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -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;
|
||||||
|
|
||||||
|
|
@ -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(())
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -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)));
|
||||||
|
|
|
||||||
|
|
@ -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)]
|
||||||
|
|
|
||||||
|
|
@ -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,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -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, "()"),
|
||||||
|
|
|
||||||
10
src/vm.rs
10
src/vm.rs
|
|
@ -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;
|
||||||
|
|
|
||||||
Loading…
Add table
Reference in a new issue