correct naming resolution for called functions, including namespaces

This commit is contained in:
Shautvast 2025-10-29 22:41:20 +01:00
parent c77a602545
commit ddd7bd4472
6 changed files with 168 additions and 83 deletions

View file

@ -2,4 +2,4 @@ fn get() -> string:
add("hello", "world") add("hello", "world")
fn add(a: string, b: string) -> string: fn add(a: string, b: string) -> string:
a + b a + " " + b

View file

@ -4,18 +4,27 @@ use crate::tokens::TokenType;
use crate::value::Value; use crate::value::Value;
use crate::vm::{ use crate::vm::{
OP_ADD, OP_AND, OP_BITAND, OP_BITOR, OP_BITXOR, OP_CALL, OP_CONSTANT, OP_DEF_BOOL, OP_DEF_CHAR, OP_ADD, OP_AND, OP_BITAND, OP_BITOR, OP_BITXOR, OP_CALL, OP_CONSTANT, OP_DEF_BOOL, OP_DEF_CHAR,
OP_DEF_DATE, OP_DEF_F32, OP_DEF_F64, OP_DEF_I32, OP_DEF_I64, OP_DEF_LIST, OP_DEF_DATE, OP_DEF_F32, OP_DEF_F64, OP_DEF_I32, OP_DEF_I64, OP_DEF_LIST, OP_DEF_MAP,
OP_DEF_MAP, OP_DEF_STRING, OP_DEF_STRUCT, OP_DEFINE, OP_DIVIDE, OP_EQUAL, OP_GET, OP_GREATER, OP_DEF_STRING, OP_DEF_STRUCT, OP_DEFINE, 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_GREATER_EQUAL, OP_LESS, OP_LESS_EQUAL, OP_MULTIPLY, OP_NEGATE, OP_NOT, OP_OR, OP_PRINT,
OP_PRINT, OP_RETURN, OP_SHL, OP_SHR, OP_SUBTRACT, OP_RETURN, OP_SHL, OP_SHR, OP_SUBTRACT,
}; };
use anyhow::anyhow;
use std::collections::HashMap; use std::collections::HashMap;
pub fn compile(ast: &Vec<Statement>) -> anyhow::Result<Chunk> { pub fn compile(
compile_name(ast, "/") namespace: &str,
ast: &Vec<Statement>,
registry: &mut HashMap<String, Chunk>,
) -> anyhow::Result<Chunk> {
compile_name(ast, namespace, registry)
} }
pub(crate) fn compile_function(function: &Function) -> anyhow::Result<Chunk> { pub(crate) fn compile_function(
function: &Function,
registry: &mut HashMap<String, Chunk>,
namespace: &str,
) -> anyhow::Result<Chunk> {
let mut compiler = Compiler::new(&function.name.lexeme); let mut compiler = Compiler::new(&function.name.lexeme);
for parm in &function.parameters { for parm in &function.parameters {
let name = parm.name.lexeme.clone(); let name = parm.name.lexeme.clone();
@ -24,12 +33,16 @@ pub(crate) fn compile_function(function: &Function) -> anyhow::Result<Chunk> {
compiler.emit_bytes(OP_DEFINE, name_index as u16); compiler.emit_bytes(OP_DEFINE, name_index as u16);
} }
Ok(compiler.compile(&function.body)?) Ok(compiler.compile(&function.body, registry, namespace)?)
} }
pub(crate) fn compile_name(ast: &Vec<Statement>, name: &str) -> anyhow::Result<Chunk> { pub(crate) fn compile_name(
let compiler = Compiler::new(name); ast: &Vec<Statement>,
Ok(compiler.compile(ast)?) namespace: &str,
registry: &mut HashMap<String, Chunk>,
) -> anyhow::Result<Chunk> {
let compiler = Compiler::new(namespace);
Ok(compiler.compile(ast, registry, namespace)?)
} }
struct Compiler { struct Compiler {
@ -37,7 +50,6 @@ struct Compiler {
had_error: bool, had_error: bool,
current_line: usize, current_line: usize,
vars: HashMap<String, usize>, vars: HashMap<String, usize>,
functions: HashMap<String, usize>,
} }
impl Compiler { impl Compiler {
@ -47,20 +59,39 @@ impl Compiler {
had_error: false, had_error: false,
current_line: 0, current_line: 0,
vars: HashMap::new(), vars: HashMap::new(),
functions: HashMap::new(),
} }
} }
fn compile(mut self, ast: &Vec<Statement>) -> anyhow::Result<Chunk> { fn compile(
mut self,
ast: &Vec<Statement>,
registry: &mut HashMap<String, Chunk>,
namespace: &str,
) -> anyhow::Result<Chunk> {
for statement in ast { for statement in ast {
self.compile_statement(statement)? if let Statement::FunctionStmt { function } = statement {
self.emit_constant(Value::String(format!(
"{}.{}",
namespace,
function.name.lexeme.clone()
)));
}
}
for statement in ast {
self.compile_statement(statement, registry, namespace)?;
} }
self.emit_byte(OP_RETURN); self.emit_byte(OP_RETURN);
Ok(self.chunk) Ok(self.chunk)
} }
fn compile_statement(&mut self, statement: &Statement) -> anyhow::Result<()> { fn compile_statement(
&mut self,
statement: &Statement,
registry: &mut HashMap<String, Chunk>,
namespace: &str,
) -> anyhow::Result<()> {
self.current_line = statement.line(); self.current_line = statement.line();
match statement { match statement {
Statement::VarStmt { Statement::VarStmt {
@ -70,39 +101,55 @@ impl Compiler {
} => { } => {
let name_index = self.chunk.add_constant(Value::String(name.lexeme.clone())); let name_index = self.chunk.add_constant(Value::String(name.lexeme.clone()));
self.vars.insert(name.lexeme.clone(), name_index); self.vars.insert(name.lexeme.clone(), name_index);
self.compile_expression(initializer)?; self.compile_expression(namespace, initializer, registry)?;
self.define_variable(var_type, name_index)?; self.define_variable(var_type, name_index)?;
if let Expression::List {values, .. } = initializer { if let Expression::List { values, .. } = initializer {
self.emit_byte(values.len() as u16); self.emit_byte(values.len() as u16);
} }
} }
Statement::PrintStmt { value } => { Statement::PrintStmt { value } => {
self.compile_expression(value)?; self.compile_expression(namespace, value, registry)?;
self.emit_byte(OP_PRINT); self.emit_byte(OP_PRINT);
} }
Statement::ExpressionStmt { expression } => { Statement::ExpressionStmt { expression } => {
self.compile_expression(expression)?; self.compile_expression(namespace, expression, registry)?;
} }
Statement::FunctionStmt { function } => { Statement::FunctionStmt { function } => {
let function_name = function.name.lexeme.clone(); let function_name = function.name.lexeme.clone();
let name_index = self.chunk.add_constant(Value::String(function_name.clone())); self.emit_constant(Value::String(function_name.clone()));
self.functions.insert(function_name, name_index); let compiled_function = compile_function(function, registry, namespace)?;
let compiled_function = compile_function(function)?; registry.insert(
self.chunk.add_function(compiled_function); format!("{}.{}", self.chunk.name, function_name),
compiled_function,
);
} }
} }
Ok(()) Ok(())
} }
fn compile_expression(&mut self, expression: &Expression) -> anyhow::Result<()> { fn compile_expression(
&mut self,
namespace: &str,
expression: &Expression,
registry: &mut HashMap<String, Chunk>,
) -> anyhow::Result<()> {
match expression { match expression {
Expression::FunctionCall { Expression::FunctionCall {
name, arguments, .. name, arguments, ..
} => { } => {
println!("call {}",name); let name = if let None = self.chunk.find_constant(&name) {
let name_index = *self.functions.get(name).unwrap(); format!("{}.{}", namespace, name)
} else {
name.clone()
};
println!("call {}", name);
let name_index = self
.chunk
.find_constant(&name)
.unwrap_or_else(|| self.emit_constant(name.into()) as usize);
for argument in arguments { for argument in arguments {
self.compile_expression(argument)?; self.compile_expression(namespace, argument, registry)?;
} }
self.emit_bytes(OP_CALL, name_index as u16); self.emit_bytes(OP_CALL, name_index as u16);
self.emit_byte(arguments.len() as u16); self.emit_byte(arguments.len() as u16);
@ -111,18 +158,22 @@ impl Compiler {
let name_index = self.vars.get(name).unwrap(); let name_index = self.vars.get(name).unwrap();
self.emit_bytes(OP_GET, *name_index as u16); self.emit_bytes(OP_GET, *name_index as u16);
} }
Expression::Literal { value, .. } => self.emit_constant(value), Expression::Literal { value, .. } => {
self.emit_constant(value.clone());
}
Expression::List { values, .. } => { Expression::List { values, .. } => {
for expr in values { for expr in values {
self.compile_expression(expr)?; self.compile_expression(namespace, expr, registry)?;
} }
// self.emit_bytes(OP_NEW_LIST, values.len() as u16); // self.emit_bytes(OP_NEW_LIST, values.len() as u16);
} }
Expression::Grouping { expression, .. } => self.compile_expression(expression)?, Expression::Grouping { expression, .. } => {
self.compile_expression(namespace, expression, registry)?
}
Expression::Unary { Expression::Unary {
operator, right, .. operator, right, ..
} => { } => {
self.compile_expression(right)?; self.compile_expression(namespace, right, registry)?;
match operator.token_type { match operator.token_type {
TokenType::Minus => { TokenType::Minus => {
self.emit_byte(OP_NEGATE); self.emit_byte(OP_NEGATE);
@ -139,8 +190,8 @@ impl Compiler {
right, right,
.. ..
} => { } => {
self.compile_expression(left)?; self.compile_expression(namespace, left, registry)?;
self.compile_expression(right)?; self.compile_expression(namespace, right, registry)?;
match operator.token_type { match operator.token_type {
TokenType::Plus => self.emit_byte(OP_ADD), TokenType::Plus => self.emit_byte(OP_ADD),
TokenType::Minus => self.emit_byte(OP_SUBTRACT), TokenType::Minus => self.emit_byte(OP_SUBTRACT),
@ -196,8 +247,9 @@ impl Compiler {
self.emit_byte(b2); self.emit_byte(b2);
} }
fn emit_constant(&mut self, value: &Value) { fn emit_constant(&mut self, value: Value) -> u16 {
let index = self.chunk.add_constant(value.clone()); let index = self.chunk.add_constant(value) as u16;
self.emit_bytes(OP_CONSTANT, index as u16); self.emit_bytes(OP_CONSTANT, index);
index
} }
} }

View file

@ -1,20 +1,30 @@
use std::collections::HashMap;
use crate::value::Value; use crate::value::Value;
use crate::vm::{ use crate::vm::{
OP_ADD, OP_BITAND, OP_BITOR, OP_BITXOR, OP_CALL, OP_CONSTANT, OP_DEF_BOOL, OP_DEF_F32, OP_ADD, OP_BITAND, OP_BITOR, OP_BITXOR, OP_CALL, OP_CONSTANT, OP_DEF_BOOL, OP_DEF_F32,
OP_DEF_F64, OP_DEF_I32, OP_DEF_I64, OP_DEF_LIST, OP_DEF_STRING, OP_DEFINE, OP_DIVIDE, OP_EQUAL, OP_DEF_F64, OP_DEF_I32, OP_DEF_I64, OP_DEF_LIST, OP_DEF_STRING, OP_DEFINE, OP_DIVIDE, OP_EQUAL,
OP_GET, OP_GREATER, OP_GREATER_EQUAL, OP_LESS, OP_LESS_EQUAL, OP_MULTIPLY, OP_NEGATE, OP_GET, OP_GREATER, OP_GREATER_EQUAL, OP_LESS, OP_LESS_EQUAL, OP_MULTIPLY, OP_NEGATE, OP_NOT,
OP_NOT, OP_POP, OP_PRINT, OP_RETURN, OP_SHL, OP_SHR, OP_SUBTRACT, OP_POP, OP_PRINT, OP_RETURN, OP_SHL, OP_SHR, OP_SUBTRACT,
}; };
#[derive(Debug, Clone)] #[derive(Debug, Clone)]
pub struct Chunk { pub struct Chunk {
name: String, pub(crate) name: String,
pub code: Vec<u16>, pub code: Vec<u16>,
pub constants: Vec<Value>, pub constants: Vec<Value>,
lines: Vec<usize>, lines: Vec<usize>,
pub functions: HashMap<String, Chunk>, }
// pub(crate) functions_by_index: Vec<Chunk>,
impl Chunk {
pub(crate) fn find_constant(&self, p0: &String) -> Option<usize> {
for (i, constant) in self.constants.iter().enumerate() {
if let Value::String(s) = constant {
if s == p0 {
return Some(i);
}
}
}
None
}
} }
impl Chunk { impl Chunk {
@ -24,8 +34,6 @@ impl Chunk {
code: Vec::new(), code: Vec::new(),
constants: vec![], constants: vec![],
lines: vec![], lines: vec![],
functions: HashMap::new(),
// functions_by_index: Vec::new(),
} }
} }
@ -39,15 +47,7 @@ impl Chunk {
self.constants.len() - 1 self.constants.len() - 1
} }
pub fn add_function(&mut self, function: Chunk) {
// self.functions_by_index.push(function.clone());
self.functions.insert(function.name.to_string(), function);
}
pub fn disassemble(&self) { pub fn disassemble(&self) {
for f in self.functions.values() {
f.disassemble();
}
println!("== {} ==", self.name); println!("== {} ==", self.name);
let mut offset = 0; let mut offset = 0;
while offset < self.code.len() { while offset < self.code.len() {
@ -110,7 +110,10 @@ impl Chunk {
fn call_inst(&self, op: &str, offset: usize) -> usize { fn call_inst(&self, op: &str, offset: usize) -> usize {
let constant = self.code[offset + 1]; let constant = self.code[offset + 1];
let num_args = self.code[offset + 2]; let num_args = self.code[offset + 2];
println!("{} {}:{}({}):", op, constant, &self.constants[constant as usize], num_args); println!(
"{} {}:{}({}):",
op, constant, &self.constants[constant as usize], num_args
);
offset + 3 offset + 3
} }

View file

@ -9,6 +9,7 @@ use crudlang::scanner::scan;
use crudlang::vm::interpret; use crudlang::vm::interpret;
use std::collections::HashMap; use std::collections::HashMap;
use std::fs; use std::fs;
use std::hash::Hash;
use std::sync::Arc; use std::sync::Arc;
use walkdir::WalkDir; use walkdir::WalkDir;
@ -17,6 +18,7 @@ async fn main() -> anyhow::Result<()> {
tracing_subscriber::fmt::init(); tracing_subscriber::fmt::init();
let mut paths = HashMap::new(); let mut paths = HashMap::new();
let mut registry = HashMap::new();
for entry in WalkDir::new("source").into_iter().filter_map(|e| e.ok()) { for entry in WalkDir::new("source").into_iter().filter_map(|e| e.ok()) {
let path = entry.path(); let path = entry.path();
if path.is_file() && path.ends_with("web.crud") { if path.is_file() && path.ends_with("web.crud") {
@ -25,10 +27,13 @@ async fn main() -> anyhow::Result<()> {
let tokens = scan(&source); let tokens = scan(&source);
match ast_compiler::compile(tokens) { match ast_compiler::compile(tokens) {
Ok(statements) => { Ok(statements) => {
let chunk = compile(&statements)?; let path = path
let path = path.strip_prefix("source")?.to_str().unwrap(); .strip_prefix("source")?
let path = path.replace("/web.crud", ""); .to_str()
paths.insert(format!("/{}", path), chunk); .unwrap()
.replace(".crud", "");
let chunk = compile(&path, &statements, &mut registry)?;
paths.insert(path, chunk);
} }
Err(e) => { Err(e) => {
println!("{}", e); println!("{}", e);
@ -38,16 +43,18 @@ async fn main() -> anyhow::Result<()> {
println!(); println!();
} }
} }
let registry = Arc::new(registry);
if !paths.is_empty() { if !paths.is_empty() {
let mut app = Router::new(); let mut app = Router::new();
for (path, code) in paths.iter() { for (path, code) in paths.iter() {
let code = code.functions.get("get").unwrap(); let state = Arc::new(AppState {
let state = Arc::new(AppState { code: code.clone() }); name: format!("{}.get", path),
registry: registry.clone(),
});
println!("adding {}", path); println!("adding {}", path);
app = app.route(path, get(handle_get).with_state(state.clone())); app = app.route(&format!("/{}",path.replace("/web", "")), get(handle_get).with_state(state.clone()));
// .with_state(state);
} }
// run our app with hyper, listening globally on port 3000
let listener = tokio::net::TcpListener::bind("0.0.0.0:3000").await?; let listener = tokio::net::TcpListener::bind("0.0.0.0:3000").await?;
println!("listening on {}", listener.local_addr()?); println!("listening on {}", listener.local_addr()?);
axum::serve(listener, app).await?; axum::serve(listener, app).await?;
@ -57,11 +64,17 @@ async fn main() -> anyhow::Result<()> {
#[derive(Clone)] #[derive(Clone)]
struct AppState { struct AppState {
code: Chunk, name: String,
registry: Arc<HashMap<String, Chunk>>,
} }
async fn handle_get(State(state): State<Arc<AppState>>) -> Result<Json<String>, StatusCode> { async fn handle_get(State(state): State<Arc<AppState>>) -> Result<Json<String>, StatusCode> {
Ok(Json(interpret(&state.code).await.unwrap().to_string())) Ok(Json(
interpret(&state.registry, &state.name)
.await
.unwrap()
.to_string(),
))
} }
// //
@ -78,18 +91,19 @@ fn hello(name: string) -> string:
"Hello "+name "Hello "+name
hello("sander")"#, hello("sander")"#,
); );
let mut registry = HashMap::new();
match ast_compiler::compile(tokens) { match ast_compiler::compile(tokens) {
Ok(statements) => { Ok(statements) => {
println!("{:?}", statements); println!("{:?}", statements);
let chunk = compile(&statements)?; let chunk = compile("", &statements, &mut registry)?;
chunk.disassemble(); chunk.disassemble();
println!("{}", interpret(&chunk).await?); // println!("{}", interpret(&chunk).await?);
} }
Err(e) => { Err(e) => {
println!("{}", e) println!("{}", e)
} }
} }
println!("{:?}", registry);
Ok(()) Ok(())
} }
} }

View file

@ -96,6 +96,19 @@ impl Into<Value> for &str {
Value::String(self.to_string()) Value::String(self.to_string())
} }
} }
impl From<String> for Value {
fn from(s: String) -> Self {
Value::String(s.clone())
}
}
impl From<&String> for Value {
fn from(s: &String) -> Self {
Value::String(s.clone())
}
}
impl Into<Value> for char { impl Into<Value> for char {
fn into(self) -> Value { fn into(self) -> Value {
Value::Char(self) Value::Char(self)
@ -171,8 +184,8 @@ impl Add<&Value> for &Value {
(Value::U64(a), Value::U64(b)) => Ok(Value::U64(a + b)), (Value::U64(a), Value::U64(b)) => Ok(Value::U64(a + b)),
(Value::F32(a), Value::F32(b)) => Ok(Value::F32(a + b)), (Value::F32(a), Value::F32(b)) => Ok(Value::F32(a + b)),
(Value::F64(a), Value::F64(b)) => Ok(Value::F64(a + b)), (Value::F64(a), Value::F64(b)) => Ok(Value::F64(a + b)),
(Value::String(s), Value::I32(i)) => Ok(Value::String(format!("{}{}", s, i))), (Value::String(s), Value::I32(i)) => Ok(format!("{}{}", s, i).into()),
(Value::String(s), Value::I64(i)) => Ok(Value::String(format!("{}{}", s, i))), (Value::String(s), Value::I64(i)) => Ok(format!("{}{}", s, i).into()),
(Value::String(s), Value::U32(u)) => Ok(Value::String(format!("{}{}", s, u))), (Value::String(s), Value::U32(u)) => Ok(Value::String(format!("{}{}", s, u))),
(Value::String(s), Value::U64(u)) => Ok(Value::String(format!("{}{}", s, u))), (Value::String(s), Value::U64(u)) => Ok(Value::String(format!("{}{}", s, u))),
(Value::String(s), Value::F32(f)) => Ok(Value::String(format!("{}{}", s, f))), (Value::String(s), Value::F32(f)) => Ok(Value::String(format!("{}{}", s, f))),

View file

@ -20,23 +20,24 @@ macro_rules! define_var {
}}; }};
} }
pub struct Vm { pub struct Vm<'a> {
ip: usize, ip: usize,
stack: Vec<Value>, stack: Vec<Value>,
local_vars: HashMap<String, Value>, local_vars: HashMap<String, Value>,
error_occurred: bool, error_occurred: bool,
arena: Bump, registry: &'a HashMap<String, Chunk>,
} }
pub async fn interpret(chunk: &Chunk) -> anyhow::Result<Value> { pub async fn interpret(registry: &HashMap<String, Chunk>, function: &str) -> anyhow::Result<Value> {
let chunk = registry.get(function).unwrap().clone();
let mut vm = Vm { let mut vm = Vm {
ip: 0, ip: 0,
stack: vec![], stack: vec![],
local_vars: HashMap::new(), local_vars: HashMap::new(),
error_occurred: false, error_occurred: false,
arena: Bump::new(), registry,
}; };
vm.run(chunk, vec![]) vm.run(&chunk, vec![])
} }
pub fn interpret_function(chunk: &Chunk, args: Vec<Value>) -> anyhow::Result<Value> { pub fn interpret_function(chunk: &Chunk, args: Vec<Value>) -> anyhow::Result<Value> {
@ -45,12 +46,12 @@ pub fn interpret_function(chunk: &Chunk, args: Vec<Value>) -> anyhow::Result<Val
stack: vec![], stack: vec![],
local_vars: HashMap::new(), local_vars: HashMap::new(),
error_occurred: false, error_occurred: false,
arena: Bump::new(), registry: &HashMap::new(),
}; };
vm.run(chunk, args) vm.run(chunk, args)
} }
impl Vm { impl <'a> Vm<'a> {
fn run(&mut self, chunk: &Chunk, args: Vec<Value>) -> anyhow::Result<Value> { fn run(&mut self, chunk: &Chunk, args: Vec<Value>) -> anyhow::Result<Value> {
for arg in args { for arg in args {
self.push(arg); self.push(arg);
@ -145,16 +146,18 @@ impl Vm {
} }
OP_CALL => { OP_CALL => {
let function_name_index = self.read(chunk); let function_name_index = self.read(chunk);
let function_name = chunk.constants[function_name_index].to_string();
let function = chunk.functions.get(&function_name).unwrap();
let mut args = vec![];
let num_args = self.read(chunk); let num_args = self.read(chunk);
let mut args = vec![];
for _ in 0..num_args { for _ in 0..num_args {
let arg = self.pop(); let arg = self.pop();
args.push(arg); args.push(arg);
} }
args.reverse(); // args.reverse();
let result = interpret_function(function, args)?;
let function_name = chunk.constants[function_name_index].to_string();
let function_chunk = self.registry.get(&function_name).unwrap();
let result = interpret_function(function_chunk, args)?;
self.push(result); self.push(result);
} }
_ => {} _ => {}