support for injected request args (path, query, headers)

This commit is contained in:
Shautvast 2025-11-03 22:07:00 +01:00
parent 42c431d5c7
commit 3142e59450
7 changed files with 76 additions and 47 deletions

View file

@ -1,17 +1,13 @@
fn get(): fn get():
#path(/) -> list: | path == "/" -> list:
service.get_all() service.get_all()
| path == "/{uuid}" -> Customer?:
#path(/id/{uuid}) -> Customer?:
service.get(uuid)? service.get(uuid)?
| path == "/" && query.firstname -> Customer?:
#path(?firstname={fname} | lastname={lname}) -> Customer?:
if name:
service.get_by_firstname(fname)? service.get_by_firstname(fname)?
else if lastname: | path == "/" && query.last_name -> Customer?
service.get_by_lastname(lname)? service.get_by_lastname(lname)?
else: | 404
404
fn post(customer: Customer): fn post(customer: Customer):
service.add(customer) service.add(customer)

View file

@ -1,5 +1,5 @@
fn get() -> string: fn get(path: string) -> string:
add("hello", "world") add("hello", path)
fn add(a: string, b: string) -> string: fn add(a: string, b: string) -> string:
a + " " + b a + " " + b

View file

@ -1,3 +1,4 @@
use crate::ast_compiler::Expression::Variable;
use crate::errors::CompilerError::{ use crate::errors::CompilerError::{
self, Expected, IncompatibleTypes, ParseError, TooManyParameters, TypeError, UnexpectedIndent, self, Expected, IncompatibleTypes, ParseError, TooManyParameters, TypeError, UnexpectedIndent,
UninitializedVariable, UninitializedVariable,
@ -15,8 +16,11 @@ use crate::value::Value;
use log::debug; use log::debug;
use std::collections::HashMap; use std::collections::HashMap;
pub fn compile(tokens: Vec<Token>) -> Result<Vec<Statement>, CompilerErrorAtLine> { pub fn compile(
let mut compiler = AstCompiler::new(tokens); path: Option<&str>,
tokens: Vec<Token>,
) -> Result<Vec<Statement>, CompilerErrorAtLine> {
let mut compiler = AstCompiler::new(path.unwrap_or(""), tokens);
compiler.compile_tokens() compiler.compile_tokens()
} }
@ -38,7 +42,7 @@ struct AstCompiler {
} }
impl AstCompiler { impl AstCompiler {
fn new(tokens: Vec<Token>) -> Self { fn new(_name: &str, tokens: Vec<Token>) -> Self {
Self { Self {
tokens, tokens,
current: 0, current: 0,
@ -514,7 +518,7 @@ impl AstCompiler {
.vars .vars
.iter() .iter()
.filter_map(|e| { .filter_map(|e| {
if let Expression::Variable { name, var_type, .. } = e { if let Variable { name, var_type, .. } = e {
Some((name, var_type)) Some((name, var_type))
} else { } else {
None None
@ -522,7 +526,7 @@ impl AstCompiler {
}) })
.find(|e| e.0 == &token.lexeme) .find(|e| e.0 == &token.lexeme)
.ok_or_else(|| return self.raise(CompilerError::UndeclaredVariable(token.clone())))?; .ok_or_else(|| return self.raise(CompilerError::UndeclaredVariable(token.clone())))?;
Ok(Expression::Variable { Ok(Variable {
name: var_name.to_string(), name: var_name.to_string(),
var_type: var_type.clone(), var_type: var_type.clone(),
line: token.line, line: token.line,

View file

@ -12,11 +12,11 @@ use crate::vm::{
use std::collections::HashMap; use std::collections::HashMap;
pub fn compile( pub fn compile(
namespace: Option<&str>, qualified_name: Option<&str>,
ast: &Vec<Statement>, ast: &Vec<Statement>,
registry: &mut HashMap<String, Chunk>, registry: &mut HashMap<String, Chunk>,
) -> Result<(), CompilerErrorAtLine> { ) -> Result<(), CompilerErrorAtLine> {
compile_in_namespace(ast, namespace, registry) compile_in_namespace(ast, qualified_name, registry)
} }
pub(crate) fn compile_function( pub(crate) fn compile_function(
@ -24,7 +24,16 @@ pub(crate) fn compile_function(
registry: &mut HashMap<String, Chunk>, registry: &mut HashMap<String, Chunk>,
namespace: &str, namespace: &str,
) -> Result<Chunk, CompilerErrorAtLine> { ) -> Result<Chunk, CompilerErrorAtLine> {
let mut compiler = Compiler::new(&function.name.lexeme); let fn_name = &function.name.lexeme;
let mut compiler = Compiler::new(fn_name);
if is_http_method(fn_name) {
compiler.chunk.add_var(&TokenType::StringType, "path");
compiler.chunk.add_var(&TokenType::MapType, "query");
compiler.chunk.add_var(&TokenType::MapType, "headers");
compiler.vars.insert("path".to_string(), 0);
compiler.vars.insert("query".to_string(), 1);
compiler.vars.insert("headers".to_string(), 2);
}
for parm in &function.parameters { for parm in &function.parameters {
let name = parm.name.lexeme.clone(); let name = parm.name.lexeme.clone();
let var_index = compiler.chunk.add_var(&parm.var_type, &parm.name.lexeme); let var_index = compiler.chunk.add_var(&parm.var_type, &parm.name.lexeme);
@ -35,6 +44,10 @@ pub(crate) fn compile_function(
Ok(compiler.compile(&function.body, registry, namespace)?) Ok(compiler.compile(&function.body, registry, namespace)?)
} }
fn is_http_method(name: &str) -> bool {
vec!["get", "post", "put", "delete", "patch"].contains(&name)
}
pub(crate) fn compile_in_namespace( pub(crate) fn compile_in_namespace(
ast: &Vec<Statement>, ast: &Vec<Statement>,
namespace: Option<&str>, namespace: Option<&str>,
@ -110,7 +123,6 @@ impl Compiler {
} }
Statement::FunctionStmt { function } => { Statement::FunctionStmt { function } => {
let function_name = function.name.lexeme.clone(); let function_name = function.name.lexeme.clone();
// self.emit_constant(Value::String(function_name.clone()));
let compiled_function = compile_function(function, registry, namespace)?; let compiled_function = compile_function(function, registry, namespace)?;
registry.insert( registry.insert(
format!("{}.{}", self.chunk.name, function_name), format!("{}.{}", self.chunk.name, function_name),

View file

@ -1,23 +1,23 @@
use crate::chunk::Chunk;
use crate::errors::Error;
use crate::errors::Error::Platform;
use crate::scanner::scan; 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 std::fs;
use walkdir::WalkDir; use walkdir::WalkDir;
use crate::chunk::Chunk;
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;
pub mod chunk; pub mod chunk;
mod compiler_tests; mod compiler_tests;
pub mod errors;
mod keywords; mod keywords;
pub mod scanner; pub mod scanner;
mod tokens; mod tokens;
mod value; mod value;
pub mod vm; pub mod vm;
pub mod errors;
pub fn compile_sourcedir(source_dir: &str) -> Result<HashMap<String, Chunk>, Error> { pub fn compile_sourcedir(source_dir: &str) -> Result<HashMap<String, Chunk>, Error> {
let mut registry = HashMap::new(); let mut registry = HashMap::new();
@ -28,12 +28,9 @@ pub fn compile_sourcedir(source_dir:&str)-> Result<HashMap<String,Chunk>, Error>
print!("compiling {:?}: ", path); print!("compiling {:?}: ", path);
let source = fs::read_to_string(path).map_err(map_underlying())?; let source = fs::read_to_string(path).map_err(map_underlying())?;
let tokens = scan(&source)?; let tokens = scan(&source)?;
match ast_compiler::compile(tokens) { match ast_compiler::compile(Some(&path), tokens) {
Ok(statements) => { Ok(statements) => {
let path = path let path = path.strip_prefix("source/").unwrap().replace(".crud", "");
.strip_prefix("source/")
.unwrap()
.replace(".crud", "");
bytecode_compiler::compile(Some(&path), &statements, &mut registry)?; bytecode_compiler::compile(Some(&path), &statements, &mut registry)?;
} }
Err(e) => { Err(e) => {
@ -51,10 +48,10 @@ pub fn map_underlying() -> fn(std::io::Error) -> Error {
|e| Platform(e.to_string()) |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>, Error> {
let tokens = scan(src)?; let tokens = scan(src)?;
let mut registry = HashMap::new(); let mut registry = HashMap::new();
let ast = ast_compiler::compile(tokens)?; let ast = ast_compiler::compile(None, tokens)?;
bytecode_compiler::compile(None, &ast, &mut registry)?; bytecode_compiler::compile(None, &ast, &mut registry)?;
Ok(registry) Ok(registry)
} }
@ -62,7 +59,7 @@ pub fn compile(src: &str) -> Result<HashMap<String, chunk::Chunk>, Error> {
fn run(src: &str) -> Result<Value, Error> { fn run(src: &str) -> Result<Value, Error> {
let tokens = scan(src)?; let tokens = scan(src)?;
let mut registry = HashMap::new(); let mut registry = HashMap::new();
let ast = ast_compiler::compile(tokens)?; let ast = ast_compiler::compile(None, tokens)?;
bytecode_compiler::compile(None, &ast, &mut registry)?; bytecode_compiler::compile(None, &ast, &mut registry)?;
interpret(&registry, "main").map_err(Error::from) interpret(&registry, "main").map_err(Error::from)
} }

View file

@ -37,7 +37,7 @@ async fn main() -> Result<(), crudlang::errors::Error> {
axum::serve(listener, app).await.map_err(map_underlying())?; axum::serve(listener, app).await.map_err(map_underlying())?;
Ok(()) Ok(())
} else { } else {
Err(Platform("No source files found".to_string())) Err(Platform("No source files found or compilation error".to_string()))
} }
} }
@ -54,17 +54,22 @@ async fn handle_any(
let uri = req.uri(); let uri = req.uri();
// // todo value = Vec<String> // // todo value = Vec<String>
// let query_params: HashMap<String, String> = uri let query_params: HashMap<String, String> = uri
// .query() .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); let component = format!("{}/web.{}", &uri.path()[1..], method);
let mut headers = HashMap::new();
for (k,v) in req.headers().iter(){
headers.insert(k.to_string(), v.to_str().unwrap().to_string());
}
Ok(Json( Ok(Json(
interpret_async(&state.registry, &component, req) interpret_async(&state.registry, &component, &req.uri().to_string(), query_params, headers)
.await .await
.unwrap() .unwrap()
.to_string(), .to_string(),

View file

@ -3,8 +3,8 @@ use crate::errors::RuntimeError::Something;
use crate::errors::{RuntimeError, ValueError}; use crate::errors::{RuntimeError, ValueError};
use crate::tokens::TokenType; use crate::tokens::TokenType;
use crate::value::Value; use crate::value::Value;
use axum::http::{Uri};
use std::collections::HashMap; use std::collections::HashMap;
use axum::extract::Request;
use tracing::debug; use tracing::debug;
pub struct Vm<'a> { pub struct Vm<'a> {
@ -34,7 +34,9 @@ pub fn interpret(registry: &HashMap<String, Chunk>, function: &str) -> Result<Va
pub async fn interpret_async( pub async fn interpret_async(
registry: &HashMap<String, Chunk>, registry: &HashMap<String, Chunk>,
function: &str, function: &str,
_request: Request, uri: &str,
query_params: HashMap<String, String>,
headers: HashMap<String, String>,
) -> Result<Value, RuntimeError> { ) -> Result<Value, RuntimeError> {
//TODO convert request to arguments //TODO convert request to arguments
let chunk = registry.get(function); let chunk = registry.get(function);
@ -46,12 +48,25 @@ pub async fn interpret_async(
error_occurred: false, error_occurred: false,
registry, registry,
}; };
vm.local_vars
.insert("path".to_string(), Value::String(uri.into()));
vm.local_vars
.insert("query".to_string(), Value::Map(value_map(query_params)));
vm.local_vars
.insert("headers".to_string(), Value::Map(value_map(headers)));
vm.run(&chunk) vm.run(&chunk)
} else { } else {
Err(RuntimeError::FunctionNotFound(function.to_string())) Err(RuntimeError::FunctionNotFound(function.to_string()))
} }
} }
fn value_map(strings: HashMap<String, String>) -> HashMap<Value, Value> {
strings
.into_iter()
.map(|(k, v)| (Value::String(k.to_string()), Value::String(v.to_string())))
.collect()
}
pub fn interpret_function(chunk: &Chunk, args: Vec<Value>) -> Result<Value, RuntimeError> { pub fn interpret_function(chunk: &Chunk, args: Vec<Value>) -> Result<Value, RuntimeError> {
let mut vm = Vm { let mut vm = Vm {
ip: 0, ip: 0,