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::{
|
||||
self, Expected, IncompatibleTypes, ParseError, TooManyParameters, TypeError,
|
||||
UndeclaredVariable, UnexpectedIndent, UninitializedVariable,
|
||||
};
|
||||
use crate::errors::CompilerErrorAtLine;
|
||||
use crate::tokens::TokenType::{
|
||||
Bang, Bool, Char, Colon, DateTime, Dot, Eof, Eol, Equal, F32, F64, False, FloatingPoint,
|
||||
Fn, Greater, GreaterEqual, GreaterGreater, I32, I64, Identifier, Indent, Integer, LeftBrace,
|
||||
Bang, Bool, Char, Colon, DateTime, Dot, Eof, Eol, Equal, F32, F64, False, FloatingPoint, Fn,
|
||||
Greater, GreaterEqual, GreaterGreater, I32, I64, Identifier, Indent, Integer, LeftBrace,
|
||||
LeftBracket, LeftParen, Less, LessEqual, LessLess, Let, ListType, MapType, Minus, Object, Plus,
|
||||
Print, RightBrace, RightBracket, RightParen, SignedInteger, SingleRightArrow, Slash, Star,
|
||||
StringType, True, U32, U64, UnsignedInteger,
|
||||
|
|
@ -14,6 +16,7 @@ use crate::tokens::TokenType::{
|
|||
use crate::tokens::{Token, TokenType};
|
||||
use crate::value::Value;
|
||||
use log::debug;
|
||||
use tokio_postgres::fallible_iterator::FallibleIterator;
|
||||
|
||||
pub fn compile(
|
||||
path: Option<&str>,
|
||||
|
|
@ -401,10 +404,63 @@ impl AstCompiler {
|
|||
right: Box::new(right),
|
||||
})
|
||||
} 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> {
|
||||
debug!("primary {:?}", self.peek());
|
||||
Ok(if self.match_token(vec![LeftBracket]) {
|
||||
|
|
@ -500,34 +556,35 @@ impl AstCompiler {
|
|||
debug!("{:?}", token);
|
||||
// function call?
|
||||
if self.match_token(vec![LeftParen]) {
|
||||
self.function_call(token.lexeme)?
|
||||
self.function_call(token.clone())?
|
||||
} else if self.match_token(vec![Colon]) {
|
||||
self.named_parameter(&token)?
|
||||
} else if self.check(Dot) {
|
||||
} else {
|
||||
// } else if self.check(Dot) {
|
||||
// chain of variable or function lookups?
|
||||
let mut name = "/".to_string();
|
||||
name.push_str(&self.previous().lexeme);
|
||||
while self.match_token(vec![Dot]) {
|
||||
name.push_str("/");
|
||||
name.push_str(&self.peek().lexeme);
|
||||
self.advance();
|
||||
}
|
||||
// let mut name = "/".to_string();
|
||||
// name.push_str(&self.previous().lexeme);
|
||||
// while self.match_token(vec![Dot]) {
|
||||
// name.push_str("/");
|
||||
// name.push_str(&self.peek().lexeme);
|
||||
// self.advance();
|
||||
// }
|
||||
// chained function call?
|
||||
if self.match_token(vec![LeftParen]) {
|
||||
self.function_call(name)?
|
||||
} else {
|
||||
// if self.match_token(vec![LeftParen]) {
|
||||
// self.function_call(name())?
|
||||
// } else {
|
||||
// empty line
|
||||
return if self.match_token(vec![Eol, Eof]) {
|
||||
Ok(Expression::Literal {
|
||||
value: Value::Void,
|
||||
literaltype: Object,
|
||||
line: token.line,
|
||||
})
|
||||
} else {
|
||||
Err(self.raise(UndeclaredVariable(token.lexeme.clone())))
|
||||
};
|
||||
}
|
||||
} else {
|
||||
// return if self.match_token(vec![Eol, Eof]) {
|
||||
// Ok(Expression::Literal {
|
||||
// value: Value::Void,
|
||||
// literaltype: Object,
|
||||
// line: token.line,
|
||||
// })
|
||||
// } else {
|
||||
// Err(self.raise(UndeclaredVariable(token.lexeme.clone())))
|
||||
// };
|
||||
// }
|
||||
// } else {
|
||||
// none of the above, must be a variable lookup
|
||||
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![];
|
||||
while !self.match_token(vec![RightParen]) {
|
||||
if arguments.len() >= 25 {
|
||||
|
|
@ -608,7 +665,7 @@ impl AstCompiler {
|
|||
}
|
||||
Ok(FunctionCall {
|
||||
line: self.peek().line,
|
||||
name,
|
||||
name: name.lexeme.to_string(),
|
||||
arguments,
|
||||
})
|
||||
}
|
||||
|
|
@ -759,15 +816,25 @@ pub enum Expression {
|
|||
Stop {
|
||||
line: usize,
|
||||
},
|
||||
PathMatch {
|
||||
line: usize,
|
||||
condition: Box<Expression>,
|
||||
},
|
||||
// PathMatch {
|
||||
// line: usize,
|
||||
// condition: Box<Expression>,
|
||||
// },
|
||||
NamedParameter {
|
||||
line: usize,
|
||||
name: Token,
|
||||
value: Box<Expression>,
|
||||
},
|
||||
MapGet {
|
||||
key: String,
|
||||
},
|
||||
ListGet {
|
||||
list: Box<Expression>,
|
||||
index: usize,
|
||||
},
|
||||
FieldGet {
|
||||
field: String,
|
||||
},
|
||||
}
|
||||
|
||||
impl Expression {
|
||||
|
|
@ -782,8 +849,28 @@ impl Expression {
|
|||
Variable { line, .. } => *line,
|
||||
FunctionCall { line, .. } => *line,
|
||||
Stop { line } => *line,
|
||||
Expression::PathMatch { line, .. } => *line,
|
||||
// Expression::PathMatch { 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, Function, Parameter, Statement};
|
||||
use crate::ast_compiler::{Expression, Function, Statement};
|
||||
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::tokens::TokenType;
|
||||
use crate::tokens::TokenType::Unknown;
|
||||
|
|
@ -9,7 +9,8 @@ use crate::value::Value;
|
|||
use crate::vm::{
|
||||
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_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;
|
||||
|
||||
|
|
@ -292,9 +293,16 @@ impl Compiler {
|
|||
_ => unimplemented!("binary other than plus, minus, star, slash"),
|
||||
}
|
||||
}
|
||||
Expression::Stop { line } => {}
|
||||
Expression::PathMatch { line, .. } => {}
|
||||
Expression::NamedParameter { line, .. } => {}
|
||||
Expression::Stop { .. } => {}
|
||||
// Expression::PathMatch { 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(())
|
||||
}
|
||||
|
|
|
|||
|
|
@ -2,7 +2,7 @@
|
|||
mod tests {
|
||||
use crate::value::Value;
|
||||
use crate::{compile, run};
|
||||
use chrono::{DateTime, FixedOffset, NaiveDate, TimeZone};
|
||||
use chrono::{DateTime};
|
||||
|
||||
#[test]
|
||||
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]
|
||||
fn infer_type() {
|
||||
assert_eq!(
|
||||
|
|
@ -209,11 +220,21 @@ m"#);
|
|||
run(r#"let date:datetime = d"2025-11-09 16:44:28.000 +0100"
|
||||
date"#),
|
||||
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]
|
||||
// fn package() {
|
||||
// assert_eq!(run(r#"a.b.c()"#), Ok(Value::U32(48)));
|
||||
|
|
|
|||
|
|
@ -1,5 +1,5 @@
|
|||
use crate::tokens::TokenType;
|
||||
use std::fmt::Display;
|
||||
use crate::tokens::{Token, TokenType};
|
||||
use thiserror::Error;
|
||||
|
||||
#[derive(Error, Debug, PartialEq)]
|
||||
|
|
@ -64,7 +64,11 @@ pub enum CompilerError {
|
|||
#[error("Crud does not support numbers above 2^64")]
|
||||
Overflow,
|
||||
#[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)]
|
||||
|
|
|
|||
|
|
@ -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,
|
||||
ast: &[Statement],
|
||||
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::PathMatch { .. } => TokenType::Unknown,
|
||||
// Expression::PathMatch { .. } => 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::Slash => write!(f, "/"),
|
||||
TokenType::Star => write!(f, "*"),
|
||||
TokenType::DateTime => write!(f, "t\""),
|
||||
TokenType::True => write!(f, "true"),
|
||||
TokenType::Unknown => 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();
|
||||
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 => {
|
||||
let function_name_index = 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_F64: u16 = 40;
|
||||
pub const OP_ASSIGN: u16 = 41;
|
||||
pub const OP_LIST_GET: u16 = 42;
|
||||
|
|
|
|||
Loading…
Add table
Reference in a new issue