object instantiation

This commit is contained in:
Shautvast 2025-11-10 15:10:36 +01:00
parent 647ba186b1
commit 0945d385d0
10 changed files with 127 additions and 109 deletions

View file

@ -2,17 +2,15 @@ use crate::ast_compiler::Expression::{
FieldGet, FunctionCall, ListGet, MapGet, NamedParameter, Stop, Variable,
};
use crate::errors::CompilerError::{
self, Expected, ParseError, TooManyParameters, UndeclaredVariable, UnexpectedIndent,
UninitializedVariable,
self, Expected, ParseError, TooManyParameters, UnexpectedIndent, UninitializedVariable,
};
use crate::errors::CompilerErrorAtLine;
use crate::symbol_builder::{Symbol, calculate_type, infer_type};
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,
LeftBracket, LeftParen, Less, LessEqual, LessLess, Let, ListType, MapType, Minus, Object, Plus,
Print, RightBrace, RightBracket, RightParen, SignedInteger, SingleRightArrow, Slash, Star,
StringType, True, U32, U64, Unknown, UnsignedInteger,
Bang, Bool, Char, Colon, DateTime, Dot, Eof, Eol, Equal, False, FloatingPoint, Fn, Greater,
GreaterEqual, GreaterGreater, Identifier, Indent, Integer, LeftBrace, LeftBracket, LeftParen,
Less, LessEqual, LessLess, Let, ListType, MapType, Minus, Object, Plus, Print, RightBrace,
RightBracket, RightParen, SingleRightArrow, Slash, Star, StringType, True, U32, U64, Unknown,
};
use crate::tokens::{Token, TokenType};
use crate::value::Value;
@ -125,7 +123,7 @@ impl AstCompiler {
} else if self.match_token(vec![Let]) {
self.let_declaration(symbol_table)
} else if self.match_token(vec![Object]) {
self.object_declaration()
self.object_declaration(symbol_table)
} else if self.match_token(vec![TokenType::Pipe]) {
self.guard_declaration(symbol_table)
} else {
@ -194,7 +192,10 @@ impl AstCompiler {
Err(self.raise(Expected("unimplemented")))
}
fn object_declaration(&mut self) -> Result<Statement, CompilerErrorAtLine> {
fn object_declaration(
&mut self,
symbol_table: &mut HashMap<String, Symbol>,
) -> Result<Statement, CompilerErrorAtLine> {
let type_name = self.consume(Identifier, Expected("object name."))?;
self.consume(Colon, Expected("':' after object name."))?;
self.consume(Eol, Expected("end of line."))?;
@ -202,7 +203,6 @@ impl AstCompiler {
let mut fields = vec![];
let expected_indent = self.indent.last().unwrap() + 1;
// self.indent.push(expected_indent);
let mut done = false;
while !done && !self.match_token(vec![Eof]) {
for _ in 0..expected_indent {
@ -228,6 +228,16 @@ impl AstCompiler {
}
}
self.consume(Eol, Expected("end of line."))?;
let type_name_as_str = type_name.lexeme.clone();
symbol_table.insert(
type_name_as_str.clone(),
Symbol::Object {
name: type_name_as_str.clone(),
fields: fields.clone(),
},
); // name name name
Ok(Statement::ObjectStmt {
name: type_name,
fields,
@ -518,7 +528,7 @@ impl AstCompiler {
operand: Expression,
index: Expression,
) -> Result<Expression, CompilerErrorAtLine> {
let get = (match &operand {
let get = match &operand {
Expression::Map { .. } => MapGet {
map: Box::new(operand),
key: Box::new(index),
@ -541,7 +551,7 @@ impl AstCompiler {
}
},
_ => return Err(self.raise(CompilerError::IllegalTypeToIndex("Unknown".to_string()))),
});
};
self.consume(RightBracket, Expected("']' after index."))?;
Ok(get)
}
@ -634,14 +644,14 @@ impl AstCompiler {
Expression::Literal {
line: self.peek().line,
literaltype: DateTime,
value: Value::DateTime(
value: Value::DateTime(Box::new(
chrono::DateTime::parse_from_str(
&self.previous().lexeme,
"%Y-%m-%d %H:%M:%S%.3f %z",
)
.map_err(|_| self.raise(ParseError(self.previous().lexeme.clone())))?
.into(),
),
)),
}
} else if self.match_token(vec![LeftParen]) {
let expr = self.expression(symbol_table)?;
@ -653,38 +663,11 @@ impl AstCompiler {
} else {
let token = self.advance().clone();
debug!("{:?}", token);
// function call?
if self.match_token(vec![LeftParen]) {
self.function_call(token.clone(), symbol_table)?
} else if self.match_token(vec![Colon]) {
self.named_parameter(&token, symbol_table)?
} 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();
// }
// chained function call?
// 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 {
// none of the above, must be a variable lookup
self.variable_lookup(&token, symbol_table)?
}
})
@ -939,10 +922,6 @@ pub enum Expression {
Stop {
line: usize,
},
// PathMatch {
// line: usize,
// condition: Box<Expression>,
// },
NamedParameter {
line: usize,
name: Token,
@ -973,7 +952,6 @@ impl Expression {
Variable { line, .. } => *line,
FunctionCall { line, .. } => *line,
Stop { line } => *line,
// Expression::PathMatch { line, .. } => *line,
NamedParameter { line, .. } => *line,
MapGet { .. } => 0,
ListGet { .. } => 0,

View file

@ -1,5 +1,5 @@
use crate::ast_compiler::Expression::NamedParameter;
use crate::ast_compiler::{Expression, Function, Statement};
use crate::ast_compiler::{Expression, Function, Parameter, Statement};
use crate::chunk::Chunk;
use crate::errors::{CompilerError, CompilerErrorAtLine};
use crate::symbol_builder::{Symbol, calculate_type, infer_type};
@ -183,29 +183,29 @@ impl Compiler {
.find_constant(&name)
.unwrap_or_else(|| self.chunk.add_constant(Value::String(name.to_string())));
let function = symbols.get(name);
if let Some(Symbol::Function { parameters, .. }) = function {
for parameter in parameters {
for argument in arguments {
if let NamedParameter { name, .. } = argument {
if name.lexeme == parameter.name.lexeme {
self.compile_expression(
namespace, argument, symbols, registry,
)?;
break;
}
} else {
self.compile_expression(namespace, argument, symbols, registry)?;
break;
}
}
match function {
Some(Symbol::Function { parameters, .. }) => {
self.get_arguments_in_order(
namespace, symbols, registry, arguments, parameters,
)?;
self.emit_bytes(OP_CALL, name_index as u16);
self.emit_byte(arguments.len() as u16);
}
// constructor function
Some(Symbol::Object { fields, .. }) => {
self.get_arguments_in_order(
namespace, symbols, registry, arguments, fields,
)?;
self.emit_bytes(OP_CALL, name_index as u16);
self.emit_byte(arguments.len() as u16);
}
_ => {
return Err(CompilerErrorAtLine::raise(
CompilerError::FunctionNotFound(name.to_string()),
0,
));
}
self.emit_bytes(OP_CALL, name_index as u16);
self.emit_byte(arguments.len() as u16);
} else {
return Err(CompilerErrorAtLine::raise(
CompilerError::FunctionNotFound(name.to_string()),
0,
));
}
}
Expression::Variable { name, line, .. } => {
@ -281,8 +281,7 @@ impl Compiler {
}
}
Expression::Stop { .. } => {}
// Expression::PathMatch { line, .. } => {}
NamedParameter { .. } => {}
NamedParameter { value,.. } => {self.compile_expression(namespace, value, symbols, registry)?}
Expression::ListGet { index, list } => {
self.compile_expression(namespace, list, symbols, registry)?;
self.compile_expression(namespace, index, symbols, registry)?;
@ -294,6 +293,32 @@ impl Compiler {
Ok(())
}
// any unnamed parameters must be passed in order
// named parameters do not have to be passed in order, but they do need to be evaluated in the order of the called function/constructor
fn get_arguments_in_order(
&mut self,
namespace: &str,
symbols: &HashMap<String, Symbol>,
registry: &mut HashMap<String, Chunk>,
arguments: &Vec<Expression>,
parameters: &Vec<Parameter>,
) -> Result<(), CompilerErrorAtLine> {
for parameter in parameters {
for argument in arguments {
if let NamedParameter { name, .. } = argument {
if name.lexeme == parameter.name.lexeme {
self.compile_expression(namespace, argument, symbols, registry)?;
break;
}
} else {
self.compile_expression(namespace, argument, symbols, registry)?;
break;
}
}
}
Ok(())
}
fn emit_byte(&mut self, byte: u16) {
self.chunk.add(byte, self.current_line);
}

View file

@ -15,7 +15,7 @@ pub struct Chunk {
pub code: Vec<u16>,
pub constants: Vec<Value>,
lines: Vec<usize>,
object_defs: HashMap<String, Vec<Parameter>>,
pub(crate) object_defs: HashMap<String, Vec<Parameter>>,
pub(crate) function_parameters: Vec<Parameter>,
pub vars: Vec<(TokenType, String)>
}

View file

@ -115,10 +115,9 @@ a"#),
#[test]
fn call_fn_with_args_returns_value() {
assert_eq!(
run(r#"
fn add_hello(name: string) -> string:
run(r#"fn add_hello(name: string) -> string:
"Hello " + name
add_hello("world")"#,),
add_hello("world")"#),
Ok(Value::String("Hello world".to_string()))
);
}
@ -133,20 +132,20 @@ object Person:
assert!(r.is_ok()); // does nothing runtime
}
// #[test]
// fn object_() {
// let r = run(
// r#"
// object Person:
// name: string
//
// let p = Person(name: "Sander")
// print p
// "#,
// );
// println!("{:?}", r);
// assert!(r.is_ok());
// }
#[test]
fn declare_and_instantiate_object() {
let r = run(r#"
object Person:
name: string
let p = Person(name: "Sander")
p"#);
assert!(r.is_ok());
assert_eq!(
r#"Person: [("name", String("Sander"))]"#,
format!("{}", r.unwrap().to_string())
);
}
#[test]
fn literal_map() {
@ -233,14 +232,14 @@ m["name"]"#);
assert_eq!(
run(r#"let date:datetime = d"2025-11-09 16:44:28.000 +0100"
date"#),
Ok(Value::DateTime(
Ok(Value::DateTime(Box::new(
DateTime::parse_from_str(
"2025-11-09 16:44:28.000 +0100",
"%Y-%m-%d %H:%M:%S%.3f %z"
)
.unwrap()
.into()
))
)))
);
}

View file

@ -81,6 +81,8 @@ pub enum RuntimeError {
Expected(&'static str, &'static str),
#[error("Function {0} not found")]
FunctionNotFound(String),
#[error("The number of of arguments for {0} is not correct. Should be {1}, got {2}")]
IllegalArgumentsException(String,usize,usize),
}
#[derive(Error, Debug, PartialEq)]

View file

@ -1,9 +1,7 @@
use crate::chunk::Chunk;
use crate::compile_sourcedir;
use log4rs::append::Append;
use notify::{RecursiveMode, Watcher};
use std::collections::HashMap;
use std::hash::Hash;
use std::path::Path;
use std::sync::Arc;
use std::sync::mpsc::channel;

View file

@ -80,6 +80,7 @@ pub fn compile(src: &str) -> Result<HashMap<String, Chunk>, CrudLangError> {
Ok(registry)
}
#[cfg(test)]
pub(crate) fn run(src: &str) -> Result<Value, CrudLangError> {
let tokens = scan(src)?;
let mut symbol_table = HashMap::new();

View file

@ -1,10 +1,10 @@
use crate::ast_compiler::{Expression, Parameter, Statement};
use crate::errors::CompilerError;
use crate::errors::CompilerError::{IncompatibleTypes, TypeError};
use crate::errors::CompilerError::IncompatibleTypes;
use crate::tokens::TokenType::{
Bool, DateTime, F32, F64, FloatingPoint, Greater, GreaterEqual, I32, I64, Integer, Less,
LessEqual, ListType, MapType, Minus, Object, Plus, SignedInteger, StringType, U32, U64,
Unknown, UnsignedInteger,
LessEqual, ListType, MapType, Minus, ObjectType, Plus, SignedInteger, StringType, U32,
U64, Unknown, UnsignedInteger,
};
use crate::tokens::{Token, TokenType};
use log::debug;
@ -138,7 +138,7 @@ pub fn calculate_type(
DateTime => DateTime,
ListType => ListType,
MapType => MapType,
Object => Object,
ObjectType(p) => ObjectType(p.clone()),
_ => return Err(CompilerError::UnexpectedType(inferred_type.clone())),
}
})
@ -219,10 +219,10 @@ pub fn infer_type(expr: &Expression, symbols: &HashMap<String, Symbol>) -> Token
Expression::Variable { var_type, .. } => var_type.clone(),
Expression::FunctionCall { name, .. } => {
let symbol = symbols.get(name);
if let Some(Symbol::Function { return_type, .. }) = symbol {
return_type.clone()
} else {
Unknown
match symbol {
Some(Symbol::Function { return_type, .. }) => return_type.clone(),
Some(Symbol::Object { name, .. }) => ObjectType(name.clone()),
_ => Unknown,
}
}
Expression::Stop { .. } => TokenType::Unknown,

View file

@ -8,8 +8,8 @@ use std::ops::{Add, BitAnd, BitOr, BitXor, Div, Mul, Neg, Not, Shl, Shr, Sub};
#[derive(Debug, Clone)]
pub struct Object {
definition: String,
fields: Vec<Value>,
pub(crate) definition: String,
pub(crate) fields: Vec<(String,Value)>,
}
#[derive(Debug, Clone)]
@ -23,7 +23,7 @@ pub enum Value {
String(String),
Char(char),
Bool(bool),
DateTime(DateTime<Utc>),
DateTime(Box<DateTime<Utc>>),
Enum,
List(Vec<Value>),
Map(HashMap<Value, Value>),
@ -156,7 +156,7 @@ impl Into<Value> for bool {
impl Into<Value> for DateTime<Utc> {
fn into(self) -> Value {
Value::DateTime(self)
Value::DateTime(Box::new(self))
}
}

View file

@ -2,10 +2,8 @@ use crate::chunk::Chunk;
use crate::errors::RuntimeError::Something;
use crate::errors::{RuntimeError, ValueError};
use crate::tokens::TokenType;
use crate::value::Value;
use crate::value::{Object, Value};
use arc_swap::Guard;
use axum::http::Uri;
use reqwest::get;
use std::collections::HashMap;
use std::sync::Arc;
use tracing::debug;
@ -216,8 +214,25 @@ impl Vm {
.registry
.get(&function_name)
.or_else(|| self.registry.get(&format!("{}/{}", context, function_name)));
if function_chunk.is_none() {
return Err(RuntimeError::FunctionNotFound(function_name));
let constructor = chunk.object_defs.get(&function_name);
if let Some(params) = constructor {
if params.len() != args.len() {
return Err(RuntimeError::IllegalArgumentsException(function_name, params.len(), args.len()));
}
let mut fields = vec![];
params.iter().zip(args.into_iter()).for_each(|(param, arg)| {fields.push((param.name.lexeme.clone(), arg))});
let new_instance = Value::ObjectType(Box::new(Object {
definition: function_name,
fields,
}));
self.push(new_instance);
} else {
return Err(RuntimeError::FunctionNotFound(function_name));
}
} else {
let result = interpret_function(function_chunk.unwrap(), args)?;
self.push(result);