From b397c4680ef514abc01e29fd6beea7a6318fbdb3 Mon Sep 17 00:00:00 2001 From: Shautvast Date: Mon, 3 Nov 2025 19:08:49 +0100 Subject: [PATCH] better mapping of urls to functions --- examples/api/customer/controller.crud | 23 ----- examples/api/customer/web.crud | 21 +++++ src/errors.rs | 2 + src/lib.rs | 38 ++++++++ src/main.rs | 131 ++++++++------------------ src/vm.rs | 41 ++++---- 6 files changed, 124 insertions(+), 132 deletions(-) delete mode 100644 examples/api/customer/controller.crud create mode 100644 examples/api/customer/web.crud diff --git a/examples/api/customer/controller.crud b/examples/api/customer/controller.crud deleted file mode 100644 index 21b2fa8..0000000 --- a/examples/api/customer/controller.crud +++ /dev/null @@ -1,23 +0,0 @@ -#path(/id/{uuid}) -fn get(uuid: uuid) -> Customer: - service.get(uuid)?or(404) - -#path(?firstname={name}) -fn get(name: string) -> Customer: - if name: - service.get(id) - else: - 404 - -#path(/) -fn get() -> list: - service.get_all() - -#path(/) -fn post(customer: Customer): - service.add(customer) - - -#path() -fn put(customer: Customer): - service.update(customer) diff --git a/examples/api/customer/web.crud b/examples/api/customer/web.crud new file mode 100644 index 0000000..532ee84 --- /dev/null +++ b/examples/api/customer/web.crud @@ -0,0 +1,21 @@ +fn get(): + #path(/) -> list: + service.get_all() + + #path(/id/{uuid}) -> Customer?: + service.get(uuid)? + + #path(?firstname={fname} | lastname={lname}) -> Customer?: + if name: + service.get_by_firstname(fname)? + else if lastname: + service.get_by_lastname(lname)? + else: + 404 + +fn post(customer: Customer): + service.add(customer) + + +fn put(customer: Customer): + service.update(customer) diff --git a/src/errors.rs b/src/errors.rs index 0c57725..fd3cf9c 100644 --- a/src/errors.rs +++ b/src/errors.rs @@ -71,6 +71,8 @@ pub enum RuntimeError { Something, #[error("Expected {0}, got {1}")] Expected(&'static str, &'static str), + #[error("Function {0} not found")] + FunctionNotFound(String), } #[derive(Error, Debug, PartialEq)] diff --git a/src/lib.rs b/src/lib.rs index 00446c7..2841fae 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -2,7 +2,11 @@ use crate::scanner::scan; use crate::value::Value; use crate::vm::interpret; use std::collections::HashMap; +use std::fs; +use walkdir::WalkDir; +use crate::chunk::Chunk; use crate::errors::Error; +use crate::errors::Error::Platform; pub mod ast_compiler; pub mod bytecode_compiler; @@ -15,6 +19,40 @@ mod value; pub mod vm; pub mod errors; +pub fn compile_sourcedir(source_dir:&str)-> Result<(Vec, HashMap), Error>{ + let mut paths = vec![]; + let mut registry = HashMap::new(); + + for entry in WalkDir::new(source_dir).into_iter().filter_map(|e| e.ok()) { + let path = entry.path().to_str().unwrap(); + if path.ends_with(".crud") { + print!("compiling {:?}: ", path); + let source = fs::read_to_string(path).map_err(map_underlying())?; + let tokens = scan(&source)?; + match ast_compiler::compile(tokens) { + Ok(statements) => { + let path = path + .strip_prefix("source/") + .unwrap() + .replace(".crud", ""); + bytecode_compiler::compile(Some(&path), &statements, &mut registry)?; + paths.push(path); + } + Err(e) => { + println!("{}", e); + break; + } + } + println!(); + } + } + Ok((paths,registry)) +} + +pub fn map_underlying() -> fn(std::io::Error) -> Error { + |e| Platform(e.to_string()) +} + pub fn compile(src: &str) -> Result, Error> { let tokens = scan(src)?; let mut registry = HashMap::new(); diff --git a/src/main.rs b/src/main.rs index 3f43633..e70edd7 100644 --- a/src/main.rs +++ b/src/main.rs @@ -1,125 +1,72 @@ use axum::extract::{Request, State}; use axum::http::StatusCode; -use axum::routing::{any, get}; +use axum::routing::any; use axum::{Json, Router}; -use crudlang::ast_compiler; -use crudlang::bytecode_compiler::compile; use crudlang::chunk::Chunk; -use crudlang::scanner::scan; -use crudlang::vm::{interpret_async}; -use std::collections::HashMap; -use std::fs; -use std::sync::Arc; -use axum::response::IntoResponse; -use walkdir::WalkDir; -use crudlang::errors::Error; use crudlang::errors::Error::Platform; +use crudlang::vm::interpret_async; +use crudlang::{compile_sourcedir, map_underlying}; +use std::collections::HashMap; +use std::sync::Arc; #[tokio::main] async fn main() -> Result<(), crudlang::errors::Error> { tracing_subscriber::fmt::init(); - let mut paths = HashMap::new(); - let mut registry = HashMap::new(); - for entry in WalkDir::new("source").into_iter().filter_map(|e| e.ok()) { - let path = entry.path(); - if path.is_file() && path.ends_with("web.crud") { - print!("compiling {:?}: ", path); - let source = fs::read_to_string(path).map_err(map_underlying())?; - let tokens = scan(&source)?; - match ast_compiler::compile(tokens) { - Ok(statements) => { - let path = path - .strip_prefix("source").map_err(|e|Platform(e.to_string()))? - .to_str() - .unwrap() - .replace(".crud", ""); - let chunk = compile(Some(&path), &statements, &mut registry)?; - paths.insert(path, chunk); - } - Err(e) => { - println!("{}", e); - break; - } - } - println!(); - } - } + let (paths,registry) = compile_sourcedir("source")?; let registry = Arc::new(registry); - // if !paths.is_empty() { - // let mut app = Router::new(); - // for (path, code) in paths.iter() { - // let state = Arc::new(AppState { - // name: format!("{}.get", path), - // registry: registry.clone(), - // }); - // println!("adding {}", path); - // app = app.route( - // &format!("/{}", path.replace("/web", "")), - // get(handle_get).with_state(state.clone()), - // ); - // } - // let listener = tokio::net::TcpListener::bind("0.0.0.0:3000").await.map_err(map_underlying())?; - // println!("listening on {}", listener.local_addr().map_err(map_underlying())?); - // axum::serve(listener, app).await.map_err(map_underlying())?; - // } + if !paths.is_empty() { + let state = Arc::new(AppState { + registry: registry.clone(), + }); - let app = Router::new() - .route("/", any(handle_any)) - .route("/{*path}", any(handle_any)); + let app = Router::new() + .route("/", any(handle_any).with_state(state.clone())) + .route("/{*path}", any(handle_any).with_state(state.clone())); - let listener = tokio::net::TcpListener::bind("0.0.0.0:3000") - .await - .map_err(map_underlying())?; + let listener = tokio::net::TcpListener::bind("0.0.0.0:3000") + .await + .map_err(map_underlying())?; - println!("listening on {}", listener.local_addr().map_err(map_underlying())?); + println!( + "listening on {}", + listener.local_addr().map_err(map_underlying())? + ); - axum::serve(listener, app).await.map_err(map_underlying())?; - Ok(()) -} - -fn map_underlying() -> fn(std::io::Error) -> Error { - |e| Platform(e.to_string()) + axum::serve(listener, app).await.map_err(map_underlying())?; + Ok(()) + } else { + Err(Platform("No source files found".to_string())) + } } #[derive(Clone)] struct AppState { - name: String, registry: Arc>, } -async fn handle_get(State(state): State>) -> Result, StatusCode> { - Ok(Json( - interpret_async(&state.registry, &state.name) - .await - .unwrap() - .to_string(), - )) -} - - -async fn handle_any(req: Request) -> impl IntoResponse { - let method = req.method().clone(); +async fn handle_any( + State(state): State>, + req: Request, +) -> Result, StatusCode> { + let method = req.method().to_string().to_ascii_lowercase(); let uri = req.uri(); - // Parse path segments - let path_segments: Vec<&str> = uri.path() - .split('/') - .filter(|s| !s.is_empty()) - .collect(); - // Parse query parameters - let query_params: HashMap = uri.query() + let query_params: HashMap = uri + .query() .map(|q| { url::form_urlencoded::parse(q.as_bytes()) .into_owned() .collect() }) .unwrap_or_default(); - - format!( - "Method: {}\nPath: {}\nSegments: {:?}\nQuery: {:?}", - method, uri.path(), path_segments, query_params - ) + let component = format!("{}/web.{}", &uri.path()[1..], method); + Ok(Json( + interpret_async(&state.registry, &component) + .await + .unwrap() + .to_string(), + )) } diff --git a/src/vm.rs b/src/vm.rs index 26a9b27..2293a4c 100644 --- a/src/vm.rs +++ b/src/vm.rs @@ -1,10 +1,10 @@ use crate::chunk::Chunk; -use crate::errors::RuntimeError::{Something}; +use crate::errors::RuntimeError::Something; use crate::errors::{RuntimeError, ValueError}; +use crate::tokens::TokenType; use crate::value::Value; use std::collections::HashMap; use tracing::debug; -use crate::tokens::TokenType; pub struct Vm<'a> { ip: usize, @@ -30,16 +30,23 @@ pub fn interpret(registry: &HashMap, function: &str) -> Result, function: &str) -> Result { - let chunk = registry.get(function).unwrap().clone(); - let mut vm = Vm { - ip: 0, - stack: vec![], - local_vars: HashMap::new(), - error_occurred: false, - registry, - }; - vm.run(&chunk) +pub async fn interpret_async( + registry: &HashMap, + function: &str, +) -> Result { + let chunk = registry.get(function); + if let Some(chunk) = chunk { + let mut vm = Vm { + ip: 0, + stack: vec![], + local_vars: HashMap::new(), + error_occurred: false, + registry, + }; + vm.run(&chunk) + } else { + Err(RuntimeError::FunctionNotFound(function.to_string())) + } } pub fn interpret_function(chunk: &Chunk, args: Vec) -> Result { @@ -57,7 +64,7 @@ pub fn interpret_function(chunk: &Chunk, args: Vec) -> Result Vm<'a> { fn run_function(&mut self, chunk: &Chunk, mut args: Vec) -> Result { // arguments -> locals - for (_,name) in chunk.vars.iter() { + for (_, name) in chunk.vars.iter() { self.local_vars.insert(name.clone(), args.remove(0)); } self.run(&chunk) @@ -134,11 +141,11 @@ impl<'a> Vm<'a> { list.reverse(); self.push(Value::List(list)); } - OP_ASSIGN=>{ + OP_ASSIGN => { let index = self.read(chunk); let (var_type, name) = chunk.vars.get(index).unwrap(); let value = self.pop(); - let value = match var_type{ + let value = match var_type { TokenType::U32 => value.cast_u32()?, TokenType::U64 => value.cast_u64()?, TokenType::F32 => value.cast_f32()?, @@ -159,7 +166,7 @@ impl<'a> Vm<'a> { } OP_GET => { let var_index = self.read(chunk); - let (_,name_index)= chunk.vars.get(var_index).unwrap(); + let (_, name_index) = chunk.vars.get(var_index).unwrap(); let value = self.local_vars.get(name_index).unwrap(); self.push(value.clone()); // not happy , take ownership, no clone debug!("after get {:?}", self.stack); @@ -173,7 +180,7 @@ impl<'a> Vm<'a> { let arg = self.pop(); args.push(arg); } - // args.reverse(); + args.reverse(); let function_name = chunk.constants[function_name_index].to_string(); let function_chunk = self.registry.get(&function_name).unwrap();