better mapping of urls to functions
This commit is contained in:
parent
11da3ddced
commit
b397c4680e
6 changed files with 124 additions and 132 deletions
|
|
@ -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)
|
||||
21
examples/api/customer/web.crud
Normal file
21
examples/api/customer/web.crud
Normal 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)
|
||||
|
|
@ -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)]
|
||||
|
|
|
|||
38
src/lib.rs
38
src/lib.rs
|
|
@ -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();
|
||||
|
|
|
|||
119
src/main.rs
119
src/main.rs
|
|
@ -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));
|
||||
.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())?;
|
||||
|
||||
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())
|
||||
} 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(),
|
||||
))
|
||||
}
|
||||
|
|
|
|||
25
src/vm.rs
25
src/vm.rs
|
|
@ -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,8 +30,12 @@ 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();
|
||||
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![],
|
||||
|
|
@ -40,6 +44,9 @@ pub async fn interpret_async(registry: &HashMap<String, Chunk>, function: &str)
|
|||
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();
|
||||
|
|
|
|||
Loading…
Add table
Reference in a new issue