diff --git a/examples/api/customer/web.crud b/examples/api/customer/web.crud index 532ee84..f85634e 100644 --- a/examples/api/customer/web.crud +++ b/examples/api/customer/web.crud @@ -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: - service.get_by_firstname(fname)? - else if lastname: - service.get_by_lastname(lname)? - else: - 404 + | path == "/" && query.firstname -> Customer?: + service.get_by_firstname(fname)? + | path == "/" && query.last_name -> Customer? + service.get_by_lastname(lname)? + | 404 fn post(customer: Customer): service.add(customer) diff --git a/source/hello/web.crud b/source/hello/web.crud index 4d05061..580afa7 100644 --- a/source/hello/web.crud +++ b/source/hello/web.crud @@ -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 diff --git a/src/ast_compiler.rs b/src/ast_compiler.rs index 8b370d1..c330a80 100644 --- a/src/ast_compiler.rs +++ b/src/ast_compiler.rs @@ -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) -> Result, CompilerErrorAtLine> { - let mut compiler = AstCompiler::new(tokens); +pub fn compile( + path: Option<&str>, + tokens: Vec, +) -> Result, 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) -> Self { + fn new(_name: &str, tokens: Vec) -> 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, diff --git a/src/bytecode_compiler.rs b/src/bytecode_compiler.rs index 9b2db7a..92146ec 100644 --- a/src/bytecode_compiler.rs +++ b/src/bytecode_compiler.rs @@ -12,11 +12,11 @@ use crate::vm::{ use std::collections::HashMap; pub fn compile( - namespace: Option<&str>, + qualified_name: Option<&str>, ast: &Vec, registry: &mut HashMap, ) -> 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, namespace: &str, ) -> Result { - 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, 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), diff --git a/src/lib.rs b/src/lib.rs index 24df1ea..99e758e 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -1,25 +1,25 @@ +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, Error>{ +pub fn compile_sourcedir(source_dir: &str) -> Result, Error> { let mut registry = HashMap::new(); for entry in WalkDir::new(source_dir).into_iter().filter_map(|e| e.ok()) { @@ -28,12 +28,9 @@ pub fn compile_sourcedir(source_dir:&str)-> Result, 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, Error> { +pub fn compile(src: &str) -> Result, 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, Error> { fn run(src: &str) -> Result { 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(®istry, "main").map_err(Error::from) } diff --git a/src/main.rs b/src/main.rs index 3111ee5..94b044f 100644 --- a/src/main.rs +++ b/src/main.rs @@ -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 - // let query_params: HashMap = uri - // .query() - // .map(|q| { - // url::form_urlencoded::parse(q.as_bytes()) - // .into_owned() - // .collect() - // }) - // .unwrap_or_default(); + let query_params: HashMap = 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(), diff --git a/src/vm.rs b/src/vm.rs index 52da9b8..01a0cb6 100644 --- a/src/vm.rs +++ b/src/vm.rs @@ -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, function: &str) -> Result, function: &str, - _request: Request, + uri: &str, + query_params: HashMap, + headers: HashMap, ) -> Result { //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) -> HashMap { + 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) -> Result { let mut vm = Vm { ip: 0,