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,
#[error("Expected {0}, got {1}")]
Expected(&'static str, &'static str),
#[error("Function {0} not found")]
FunctionNotFound(String),
}
#[derive(Error, Debug, PartialEq)]

View file

@ -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<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> {
let tokens = scan(src)?;
let mut registry = HashMap::new();

View file

@ -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<HashMap<String, Chunk>>,
}
async fn handle_get(State(state): State<Arc<AppState>>) -> Result<Json<String>, 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<Arc<AppState>>,
req: Request,
) -> Result<Json<String>, 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<String, String> = uri.query()
let query_params: HashMap<String, String> = 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(),
))
}

View file

@ -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<String, Chunk>, function: &str) -> Result<Va
vm.run(&chunk)
}
pub async fn interpret_async(registry: &HashMap<String, Chunk>, function: &str) -> Result<Value, RuntimeError> {
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<String, Chunk>,
function: &str,
) -> Result<Value, RuntimeError> {
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<Value>) -> Result<Value, RuntimeError> {
@ -57,7 +64,7 @@ pub fn interpret_function(chunk: &Chunk, args: Vec<Value>) -> Result<Value, Runt
impl<'a> Vm<'a> {
fn run_function(&mut self, chunk: &Chunk, mut args: Vec<Value>) -> Result<Value, RuntimeError> {
// 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();