added global function calls, starting now()

This commit is contained in:
Shautvast 2025-11-21 17:02:36 +01:00
parent 3d0aad3de1
commit d766ec775a
8 changed files with 121 additions and 71 deletions

20
src/builtins/globals.rs Normal file
View file

@ -0,0 +1,20 @@
use crate::builtins::{FunctionMap, Signature, add};
use crate::compiler::tokens::TokenType::DateTime;
use crate::errors::RuntimeError;
use crate::value::Value;
use std::collections::HashMap;
use std::sync::LazyLock;
pub(crate) static GLOBAL_FUNCTIONS: LazyLock<FunctionMap> = LazyLock::new(|| {
let mut global_functions: FunctionMap = HashMap::new();
let functions = &mut global_functions;
add(functions, "now", Signature::new(vec![], DateTime, now));
global_functions
});
fn now(_self_val: Value, _args: Vec<Value>) -> Result<Value, RuntimeError> {
Ok(Value::DateTime(Box::new(chrono::DateTime::from(
chrono::Utc::now(),
))))
}

View file

@ -1,5 +1,6 @@
mod string; mod string;
mod list; mod list;
pub(crate) mod globals;
use crate::builtins::string::string_functions; use crate::builtins::string::string_functions;
use crate::errors::{CompilerError, RuntimeError}; use crate::errors::{CompilerError, RuntimeError};
@ -35,10 +36,12 @@ impl Signature {
} }
pub(crate) type FunctionFn = fn(Value, Vec<Value>) -> Result<Value, RuntimeError>; pub(crate) type FunctionFn = fn(Value, Vec<Value>) -> Result<Value, RuntimeError>;
/// maps function names to the signature
pub(crate) type FunctionMap = HashMap<String, Signature>; pub(crate) type FunctionMap = HashMap<String, Signature>;
/// maps receiver type name to a function map
pub(crate) type FunctionTable = HashMap<String, FunctionMap>; pub(crate) type FunctionTable = HashMap<String, FunctionMap>;
static METHODS: LazyLock<FunctionTable> = LazyLock::new(|| { static FUNCTIONS: LazyLock<FunctionTable> = LazyLock::new(|| {
let mut table: FunctionTable = HashMap::new(); let mut table: FunctionTable = HashMap::new();
table.insert("string".to_string(), string_functions()); table.insert("string".to_string(), string_functions());
table.insert("list".to_string(), list_functions()); table.insert("list".to_string(), list_functions());
@ -51,7 +54,7 @@ pub(crate) fn add(m: &mut FunctionMap, name: &str, method: Signature) {
} }
pub(crate) fn lookup(type_name: &str, method_name: &str) -> Result<&'static Signature, CompilerError> { pub(crate) fn lookup(type_name: &str, method_name: &str) -> Result<&'static Signature, CompilerError> {
METHODS FUNCTIONS
.get(type_name) .get(type_name)
.and_then(|methods| methods.get(method_name)) .and_then(|methods| methods.get(method_name))
.ok_or_else(|| CompilerError::FunctionNotFound(format!("{}.{}", type_name, method_name))) .ok_or_else(|| CompilerError::FunctionNotFound(format!("{}.{}", type_name, method_name)))

View file

@ -1,5 +1,10 @@
use crate::builtins::globals::GLOBAL_FUNCTIONS;
use crate::builtins::lookup; use crate::builtins::lookup;
use crate::compiler::assembly_pass::Op::{Add, And, Assign, BitAnd, BitOr, BitXor, Call, CallBuiltin, Constant, DefList, DefMap, Divide, Dup, Equal, Get, Goto, GotoIf, GotoIfNot, Greater, GreaterEqual, Less, LessEqual, ListGet, Multiply, Negate, Not, NotEqual, Or, Pop, Print, Return, Shr, Subtract}; use crate::compiler::assembly_pass::Op::{
Add, And, Assign, BitAnd, BitOr, BitXor, Call, CallBuiltin, Constant, DefList, DefMap, Divide,
Dup, Equal, Get, Goto, GotoIf, GotoIfNot, Greater, GreaterEqual, Less, LessEqual, ListGet,
Multiply, Negate, Not, NotEqual, Or, Pop, Print, Return, Shr, Subtract,
};
use crate::compiler::ast_pass::Expression::NamedParameter; use crate::compiler::ast_pass::Expression::NamedParameter;
use crate::compiler::ast_pass::{Expression, Function, Parameter, Statement}; use crate::compiler::ast_pass::{Expression, Function, Parameter, Statement};
use crate::compiler::tokens::TokenType; use crate::compiler::tokens::TokenType;
@ -300,8 +305,15 @@ impl AsmPass {
)?; )?;
self.emit(Call(name_index, arguments.len())); self.emit(Call(name_index, arguments.len()));
} }
// maybe global function
_ => { _ => {
return Err(self.raise(CompilerError::FunctionNotFound(name.to_string()))); if let Some(fun) = GLOBAL_FUNCTIONS.get(name) {
self.emit(Call(name_index, fun.arity()));
} else {
return Err(
self.raise(CompilerError::FunctionNotFound(name.to_string()))
);
}
} }
} }
} }
@ -338,11 +350,7 @@ impl AsmPass {
arguments, arguments,
&signature.parameters, &signature.parameters,
)?; )?;
self.emit(CallBuiltin( self.emit(CallBuiltin(name_index, type_index, arguments.len()));
name_index,
type_index,
arguments.len(),
));
} }
Expression::Variable { name, .. } => { Expression::Variable { name, .. } => {
let name_index = self.vars.get(name); let name_index = self.vars.get(name);

View file

@ -1,11 +1,6 @@
use crate::compiler::ast_pass::Expression::{ use crate::compiler::ast_pass::Expression::{
Assignment, FieldGet, FunctionCall, ListGet, MapGet, MethodCall, NamedParameter, Stop, Variable, Assignment, FieldGet, FunctionCall, ListGet, MapGet, MethodCall, NamedParameter, Stop, Variable,
}; };
use crate::errors::CompilerError::{
self, Expected, ParseError, TooManyParameters, UnexpectedIndent, UninitializedVariable,
};
use crate::errors::CompilerErrorAtLine;
use crate::symbol_builder::{Symbol, calculate_type, infer_type};
use crate::compiler::tokens::TokenType::{ use crate::compiler::tokens::TokenType::{
Bang, Bool, Char, Colon, DateTime, Dot, Else, Eof, Eol, Equal, False, FloatingPoint, Fn, For, Bang, Bool, Char, Colon, DateTime, Dot, Else, Eof, Eol, Equal, False, FloatingPoint, Fn, For,
Greater, GreaterEqual, GreaterGreater, Identifier, If, In, Indent, Integer, LeftBrace, Greater, GreaterEqual, GreaterGreater, Identifier, If, In, Indent, Integer, LeftBrace,
@ -14,8 +9,13 @@ use crate::compiler::tokens::TokenType::{
True, U32, U64, Unknown, True, U32, U64, Unknown,
}; };
use crate::compiler::tokens::{Token, TokenType}; use crate::compiler::tokens::{Token, TokenType};
use crate::errors::CompilerError::{
self, Expected, ParseError, TooManyParameters, UnexpectedIndent, UninitializedVariable,
};
use crate::errors::CompilerErrorAtLine;
use crate::symbol_builder::{Symbol, calculate_type, infer_type};
use crate::value::Value; use crate::value::Value;
use crate::{Expr, Stmt, SymbolTable}; use crate::{DATE_FORMAT_TIMEZONE, Expr, Stmt, SymbolTable};
use log::debug; use log::debug;
use std::collections::HashMap; use std::collections::HashMap;
@ -663,10 +663,7 @@ impl AstCompiler {
line: self.peek().line, line: self.peek().line,
literaltype: DateTime, literaltype: DateTime,
value: Value::DateTime(Box::new( value: Value::DateTime(Box::new(
chrono::DateTime::parse_from_str( chrono::DateTime::parse_from_str(&self.previous().lexeme, DATE_FORMAT_TIMEZONE)
&self.previous().lexeme,
"%Y-%m-%d %H:%M:%S%.3f %z",
)
.map_err(|_| self.raise(ParseError(self.previous().lexeme.clone())))? .map_err(|_| self.raise(ParseError(self.previous().lexeme.clone())))?
.into(), .into(),
)), )),

View file

@ -1,12 +1,13 @@
#[cfg(test)] #[cfg(test)]
mod tests { mod tests {
use crate::compiler::{compile, run};
use crate::errors::CompilerError::IllegalArgumentsException; use crate::errors::CompilerError::IllegalArgumentsException;
use crate::errors::CompilerErrorAtLine; use crate::errors::CompilerErrorAtLine;
use crate::errors::TipiLangError::{Compiler, Runtime};
use crate::errors::RuntimeError::{IllegalArgumentException, IndexOutOfBounds}; use crate::errors::RuntimeError::{IllegalArgumentException, IndexOutOfBounds};
use crate::errors::TipiLangError::{Compiler, Runtime};
use crate::value::{Value, string}; use crate::value::{Value, string};
use chrono::DateTime; use chrono::DateTime;
use crate::compiler::{compile, run}; use crate::DATE_FORMAT_TIMEZONE;
#[test] #[test]
fn literal_int() { fn literal_int() {
@ -241,8 +242,7 @@ m["name"]"#);
date"#), date"#),
Ok(Value::DateTime(Box::new( Ok(Value::DateTime(Box::new(
DateTime::parse_from_str( DateTime::parse_from_str(
"2025-11-09 16:44:28.000 +0100", "2025-11-09 16:44:28.000 +0100", DATE_FORMAT_TIMEZONE
"%Y-%m-%d %H:%M:%S%.3f %z"
) )
.unwrap() .unwrap()
.into() .into()
@ -408,6 +408,15 @@ sum
); );
} }
#[test]
fn global_function_call() {
let value = run(r#"now()"#);
assert!(value.is_ok());
let value = value.unwrap();
let date_time_string = value.to_string();
assert!(DateTime::parse_from_str(&date_time_string, DATE_FORMAT_TIMEZONE).is_ok());
}
// #[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

@ -20,3 +20,4 @@ pub(crate) type Stmt = Result<Statement, CompilerErrorAtLine>;
pub(crate) type AsmRegistry = HashMap<String, AsmChunk>; pub(crate) type AsmRegistry = HashMap<String, AsmChunk>;
pub const TIPI_EXT: &str = ".tp"; pub const TIPI_EXT: &str = ".tp";
pub const DATE_FORMAT_TIMEZONE: &str = "%Y-%m-%d %H:%M:%S%.3f %z";

View file

@ -5,6 +5,7 @@ use std::collections::HashMap;
use std::fmt::{Display, Formatter}; use std::fmt::{Display, Formatter};
use std::hash::{Hash, Hasher}; use std::hash::{Hash, Hasher};
use std::ops::{Add, BitAnd, BitOr, BitXor, Div, Mul, Neg, Not, Shl, Shr, Sub}; use std::ops::{Add, BitAnd, BitOr, BitXor, Div, Mul, Neg, Not, Shl, Shr, Sub};
use crate::DATE_FORMAT_TIMEZONE;
#[derive(Debug, Clone)] #[derive(Debug, Clone)]
pub struct Object { pub struct Object {
@ -186,7 +187,7 @@ impl Display for Value {
Value::F32(v) => write!(f, "{}", v), Value::F32(v) => write!(f, "{}", v),
Value::F64(v) => write!(f, "{}", v), Value::F64(v) => write!(f, "{}", v),
Value::Char(v) => write!(f, "{}", v), Value::Char(v) => write!(f, "{}", v),
Value::DateTime(v) => write!(f, "{}", v), Value::DateTime(v) => write!(f, "{}", v.format(DATE_FORMAT_TIMEZONE)),
Value::Enum => write!(f, "enum"), Value::Enum => write!(f, "enum"),
Value::ObjectType(o) => write!(f, "{}: {:?}", o.definition, o.fields), Value::ObjectType(o) => write!(f, "{}: {:?}", o.definition, o.fields),
Value::List(v) => write!(f, "{:?}", v), Value::List(v) => write!(f, "{:?}", v),

View file

@ -1,12 +1,13 @@
use crate::AsmRegistry;
use crate::builtins::globals::GLOBAL_FUNCTIONS;
use crate::compiler::assembly_pass::{AsmChunk, Op}; use crate::compiler::assembly_pass::{AsmChunk, Op};
use crate::compiler::tokens::TokenType;
use crate::errors::{RuntimeError, ValueError}; use crate::errors::{RuntimeError, ValueError};
use crate::value::{Object, Value}; use crate::value::{Object, Value};
use crate::{AsmRegistry};
use arc_swap::Guard; use arc_swap::Guard;
use std::collections::HashMap; use std::collections::HashMap;
use std::sync::Arc; use std::sync::Arc;
use tracing::debug; use tracing::debug;
use crate::compiler::tokens::TokenType;
pub async fn interpret_async( pub async fn interpret_async(
registry: Guard<Arc<HashMap<String, AsmChunk>>>, registry: Guard<Arc<HashMap<String, AsmChunk>>>,
@ -60,7 +61,11 @@ impl Vm {
} }
} }
fn run_function(&mut self, chunk: &AsmChunk, mut args: Vec<Value>) -> Result<Value, RuntimeError> { fn run_function(
&mut self,
chunk: &AsmChunk,
mut args: Vec<Value>,
) -> Result<Value, RuntimeError> {
// arguments -> locals // arguments -> locals
for (_, name) in chunk.vars.iter() { for (_, name) in chunk.vars.iter() {
self.local_vars.insert(name.clone(), args.remove(0)); self.local_vars.insert(name.clone(), args.remove(0));
@ -172,7 +177,9 @@ impl Vm {
crate::builtins::call(&receiver_type_name, &function_name, receiver, args)?; crate::builtins::call(&receiver_type_name, &function_name, receiver, args)?;
self.push(return_value); self.push(return_value);
} }
Op::Pop => {self.pop();} Op::Pop => {
self.pop();
}
Op::Call(function_name_index, num_args) => { Op::Call(function_name_index, num_args) => {
let mut args = vec![]; let mut args = vec![];
for _ in 0..*num_args { for _ in 0..*num_args {
@ -182,10 +189,13 @@ impl Vm {
args.reverse(); args.reverse();
let function_name = chunk.constants[*function_name_index].to_string(); let function_name = chunk.constants[*function_name_index].to_string();
let function_chunk = self if let Some(fun) = GLOBAL_FUNCTIONS.get(&function_name) {
.registry let return_value = (fun.function)(Value::Void, args)?;
.get(&function_name) self.push(return_value);
.or_else(|| self.registry.get(&format!("{}/{}", context, function_name))); } else {
let function_chunk = self.registry.get(&function_name).or_else(|| {
self.registry.get(&format!("{}/{}", context, function_name))
});
if function_chunk.is_none() { if function_chunk.is_none() {
let constructor = chunk.object_defs.get(&function_name); let constructor = chunk.object_defs.get(&function_name);
@ -216,6 +226,7 @@ impl Vm {
self.push(result); self.push(result);
} }
} }
}
Op::GotoIfNot(goto_addr) => { Op::GotoIfNot(goto_addr) => {
let b = self.pop(); let b = self.pop();
if b == Value::Bool(false) { if b == Value::Bool(false) {