better mapping of urls to functions

This commit is contained in:
Shautvast 2025-11-03 19:08:49 +01:00
parent 11da3ddced
commit b397c4680e
6 changed files with 124 additions and 132 deletions

View file

@ -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)

View file

@ -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)

View file

@ -71,6 +71,8 @@ pub enum RuntimeError {
Something, Something,
#[error("Expected {0}, got {1}")] #[error("Expected {0}, got {1}")]
Expected(&'static str, &'static str), Expected(&'static str, &'static str),
#[error("Function {0} not found")]
FunctionNotFound(String),
} }
#[derive(Error, Debug, PartialEq)] #[derive(Error, Debug, PartialEq)]

View file

@ -2,7 +2,11 @@ use crate::scanner::scan;
use crate::value::Value; use crate::value::Value;
use crate::vm::interpret; use crate::vm::interpret;
use std::collections::HashMap; use std::collections::HashMap;
use std::fs;
use walkdir::WalkDir;
use crate::chunk::Chunk;
use crate::errors::Error; use crate::errors::Error;
use crate::errors::Error::Platform;
pub mod ast_compiler; pub mod ast_compiler;
pub mod bytecode_compiler; pub mod bytecode_compiler;
@ -15,6 +19,40 @@ mod value;
pub mod vm; pub mod vm;
pub mod errors; pub mod errors;
pub fn compile_sourcedir(source_dir:&str)-> Result<(Vec<String>, HashMap<String,Chunk>), 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<HashMap<String, chunk::Chunk>, Error> { pub fn compile(src: &str) -> Result<HashMap<String, chunk::Chunk>, Error> {
let tokens = scan(src)?; let tokens = scan(src)?;
let mut registry = HashMap::new(); let mut registry = HashMap::new();

View file

@ -1,125 +1,72 @@
use axum::extract::{Request, State}; use axum::extract::{Request, State};
use axum::http::StatusCode; use axum::http::StatusCode;
use axum::routing::{any, get}; use axum::routing::any;
use axum::{Json, Router}; use axum::{Json, Router};
use crudlang::ast_compiler;
use crudlang::bytecode_compiler::compile;
use crudlang::chunk::Chunk; 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::errors::Error::Platform;
use crudlang::vm::interpret_async;
use crudlang::{compile_sourcedir, map_underlying};
use std::collections::HashMap;
use std::sync::Arc;
#[tokio::main] #[tokio::main]
async fn main() -> Result<(), crudlang::errors::Error> { async fn main() -> Result<(), crudlang::errors::Error> {
tracing_subscriber::fmt::init(); tracing_subscriber::fmt::init();
let mut paths = HashMap::new(); let (paths,registry) = compile_sourcedir("source")?;
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 registry = Arc::new(registry); let registry = Arc::new(registry);
// if !paths.is_empty() { if !paths.is_empty() {
// let mut app = Router::new(); let state = Arc::new(AppState {
// for (path, code) in paths.iter() { registry: registry.clone(),
// 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())?;
// }
let app = Router::new() let app = Router::new()
.route("/", any(handle_any)) .route("/", any(handle_any).with_state(state.clone()))
.route("/{*path}", any(handle_any)); .route("/{*path}", any(handle_any).with_state(state.clone()));
let listener = tokio::net::TcpListener::bind("0.0.0.0:3000") let listener = tokio::net::TcpListener::bind("0.0.0.0:3000")
.await .await
.map_err(map_underlying())?; .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())?; axum::serve(listener, app).await.map_err(map_underlying())?;
Ok(()) Ok(())
} else {
Err(Platform("No source files found".to_string()))
} }
fn map_underlying() -> fn(std::io::Error) -> Error {
|e| Platform(e.to_string())
} }
#[derive(Clone)] #[derive(Clone)]
struct AppState { struct AppState {
name: String,
registry: Arc<HashMap<String, Chunk>>, registry: Arc<HashMap<String, Chunk>>,
} }
async fn handle_get(State(state): State<Arc<AppState>>) -> Result<Json<String>, StatusCode> { async fn handle_any(
Ok(Json( State(state): State<Arc<AppState>>,
interpret_async(&state.registry, &state.name) req: Request,
.await ) -> Result<Json<String>, StatusCode> {
.unwrap() let method = req.method().to_string().to_ascii_lowercase();
.to_string(),
))
}
async fn handle_any(req: Request) -> impl IntoResponse {
let method = req.method().clone();
let uri = req.uri(); let uri = req.uri();
// Parse path segments
let path_segments: Vec<&str> = uri.path()
.split('/')
.filter(|s| !s.is_empty())
.collect();
// Parse query parameters // Parse query parameters
let query_params: HashMap<String, String> = uri.query() let query_params: HashMap<String, String> = uri
.query()
.map(|q| { .map(|q| {
url::form_urlencoded::parse(q.as_bytes()) url::form_urlencoded::parse(q.as_bytes())
.into_owned() .into_owned()
.collect() .collect()
}) })
.unwrap_or_default(); .unwrap_or_default();
let component = format!("{}/web.{}", &uri.path()[1..], method);
format!( Ok(Json(
"Method: {}\nPath: {}\nSegments: {:?}\nQuery: {:?}", interpret_async(&state.registry, &component)
method, uri.path(), path_segments, query_params .await
) .unwrap()
.to_string(),
))
} }

View file

@ -1,10 +1,10 @@
use crate::chunk::Chunk; use crate::chunk::Chunk;
use crate::errors::RuntimeError::{Something}; use crate::errors::RuntimeError::Something;
use crate::errors::{RuntimeError, ValueError}; use crate::errors::{RuntimeError, ValueError};
use crate::tokens::TokenType;
use crate::value::Value; use crate::value::Value;
use std::collections::HashMap; use std::collections::HashMap;
use tracing::debug; use tracing::debug;
use crate::tokens::TokenType;
pub struct Vm<'a> { pub struct Vm<'a> {
ip: usize, ip: usize,
@ -30,8 +30,12 @@ pub fn interpret(registry: &HashMap<String, Chunk>, function: &str) -> Result<Va
vm.run(&chunk) vm.run(&chunk)
} }
pub async fn interpret_async(registry: &HashMap<String, Chunk>, function: &str) -> Result<Value, RuntimeError> { pub async fn interpret_async(
let chunk = registry.get(function).unwrap().clone(); registry: &HashMap<String, Chunk>,
function: &str,
) -> Result<Value, RuntimeError> {
let chunk = registry.get(function);
if let Some(chunk) = chunk {
let mut vm = Vm { let mut vm = Vm {
ip: 0, ip: 0,
stack: vec![], stack: vec![],
@ -40,6 +44,9 @@ pub async fn interpret_async(registry: &HashMap<String, Chunk>, function: &str)
registry, registry,
}; };
vm.run(&chunk) vm.run(&chunk)
} else {
Err(RuntimeError::FunctionNotFound(function.to_string()))
}
} }
pub fn interpret_function(chunk: &Chunk, args: Vec<Value>) -> Result<Value, RuntimeError> { pub fn interpret_function(chunk: &Chunk, args: Vec<Value>) -> Result<Value, RuntimeError> {
@ -173,7 +180,7 @@ impl<'a> Vm<'a> {
let arg = self.pop(); let arg = self.pop();
args.push(arg); args.push(arg);
} }
// 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.registry.get(&function_name).unwrap(); let function_chunk = self.registry.get(&function_name).unwrap();