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():
#path(/) -> list:
| path == "/" -> list:
service.get_all()
#path(/id/{uuid}) -> Customer?:
| path == "/{uuid}" -> Customer?:
service.get(uuid)?
#path(?firstname={fname} | lastname={lname}) -> Customer?:
if name:
| path == "/" && query.firstname -> Customer?:
service.get_by_firstname(fname)?
else if lastname:
| path == "/" && query.last_name -> Customer?
service.get_by_lastname(lname)?
else:
404
| 404
fn post(customer: Customer):
service.add(customer)

View file

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

View file

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

View file

@ -12,11 +12,11 @@ use crate::vm::{
use std::collections::HashMap;
pub fn compile(
namespace: Option<&str>,
qualified_name: Option<&str>,
ast: &Vec<Statement>,
registry: &mut HashMap<String, Chunk>,
) -> Result<(), CompilerErrorAtLine> {
compile_in_namespace(ast, namespace, registry)
compile_in_namespace(ast, qualified_name, registry)
}
pub(crate) fn compile_function(
@ -24,7 +24,16 @@ pub(crate) fn compile_function(
registry: &mut HashMap<String, Chunk>,
namespace: &str,
) -> 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 {
let name = parm.name.lexeme.clone();
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)?)
}
fn is_http_method(name: &str) -> bool {
vec!["get", "post", "put", "delete", "patch"].contains(&name)
}
pub(crate) fn compile_in_namespace(
ast: &Vec<Statement>,
namespace: Option<&str>,
@ -110,7 +123,6 @@ impl Compiler {
}
Statement::FunctionStmt { function } => {
let function_name = function.name.lexeme.clone();
// self.emit_constant(Value::String(function_name.clone()));
let compiled_function = compile_function(function, registry, namespace)?;
registry.insert(
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::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;
pub mod chunk;
mod compiler_tests;
pub mod errors;
mod keywords;
pub mod scanner;
mod tokens;
mod value;
pub mod vm;
pub mod errors;
pub fn compile_sourcedir(source_dir: &str) -> Result<HashMap<String, Chunk>, Error> {
let mut registry = HashMap::new();
@ -28,12 +28,9 @@ pub fn compile_sourcedir(source_dir:&str)-> Result<HashMap<String,Chunk>, Error>
print!("compiling {:?}: ", path);
let source = fs::read_to_string(path).map_err(map_underlying())?;
let tokens = scan(&source)?;
match ast_compiler::compile(tokens) {
match ast_compiler::compile(Some(&path), tokens) {
Ok(statements) => {
let path = path
.strip_prefix("source/")
.unwrap()
.replace(".crud", "");
let path = path.strip_prefix("source/").unwrap().replace(".crud", "");
bytecode_compiler::compile(Some(&path), &statements, &mut registry)?;
}
Err(e) => {
@ -51,10 +48,10 @@ 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>, Error> {
let tokens = scan(src)?;
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)?;
Ok(registry)
}
@ -62,7 +59,7 @@ pub fn compile(src: &str) -> Result<HashMap<String, chunk::Chunk>, Error> {
fn run(src: &str) -> Result<Value, Error> {
let tokens = scan(src)?;
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)?;
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())?;
Ok(())
} 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();
// // todo value = Vec<String>
// let query_params: HashMap<String, String> = uri
// .query()
// .map(|q| {
// url::form_urlencoded::parse(q.as_bytes())
// .into_owned()
// .collect()
// })
// .unwrap_or_default();
let query_params: HashMap<String, String> = uri
.query()
.map(|q| {
url::form_urlencoded::parse(q.as_bytes())
.into_owned()
.collect()
})
.unwrap_or_default();
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(
interpret_async(&state.registry, &component, req)
interpret_async(&state.registry, &component, &req.uri().to_string(), query_params, headers)
.await
.unwrap()
.to_string(),

View file

@ -3,8 +3,8 @@ use crate::errors::RuntimeError::Something;
use crate::errors::{RuntimeError, ValueError};
use crate::tokens::TokenType;
use crate::value::Value;
use axum::http::{Uri};
use std::collections::HashMap;
use axum::extract::Request;
use tracing::debug;
pub struct Vm<'a> {
@ -34,7 +34,9 @@ pub fn interpret(registry: &HashMap<String, Chunk>, function: &str) -> Result<Va
pub async fn interpret_async(
registry: &HashMap<String, Chunk>,
function: &str,
_request: Request,
uri: &str,
query_params: HashMap<String, String>,
headers: HashMap<String, String>,
) -> Result<Value, RuntimeError> {
//TODO convert request to arguments
let chunk = registry.get(function);
@ -46,12 +48,25 @@ pub async fn interpret_async(
error_occurred: false,
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)
} else {
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> {
let mut vm = Vm {
ip: 0,