object instantiation
This commit is contained in:
parent
647ba186b1
commit
0945d385d0
10 changed files with 127 additions and 109 deletions
|
|
@ -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,
|
||||
|
|
|
|||
|
|
@ -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,31 +183,31 @@ 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,
|
||||
match function {
|
||||
Some(Symbol::Function { parameters, .. }) => {
|
||||
self.get_arguments_in_order(
|
||||
namespace, symbols, registry, arguments, parameters,
|
||||
)?;
|
||||
break;
|
||||
}
|
||||
} else {
|
||||
self.compile_expression(namespace, argument, symbols, registry)?;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
self.emit_bytes(OP_CALL, name_index as u16);
|
||||
self.emit_byte(arguments.len() as u16);
|
||||
} else {
|
||||
}
|
||||
// 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,
|
||||
));
|
||||
}
|
||||
}
|
||||
}
|
||||
Expression::Variable { name, line, .. } => {
|
||||
let name_index = self.vars.get(name);
|
||||
if let Some(name_index) = name_index {
|
||||
|
|
@ -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);
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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)>
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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()
|
||||
))
|
||||
)))
|
||||
);
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -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)]
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
|
|
|
|||
|
|
@ -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();
|
||||
|
|
|
|||
|
|
@ -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,
|
||||
|
|
|
|||
|
|
@ -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))
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
|||
21
src/vm.rs
21
src/vm.rs
|
|
@ -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() {
|
||||
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);
|
||||
|
|
|
|||
Loading…
Add table
Reference in a new issue