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,
|
Something,
|
||||||
#[error("Expected {0}, got {1}")]
|
#[error("Expected {0}, got {1}")]
|
||||||
Expected(&'static str, &'static str),
|
Expected(&'static str, &'static str),
|
||||||
|
#[error("Function {0} not found")]
|
||||||
|
FunctionNotFound(String),
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Error, Debug, PartialEq)]
|
#[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::value::Value;
|
||||||
use crate::vm::interpret;
|
use crate::vm::interpret;
|
||||||
use std::collections::HashMap;
|
use std::collections::HashMap;
|
||||||
|
use std::fs;
|
||||||
|
use walkdir::WalkDir;
|
||||||
|
use crate::chunk::Chunk;
|
||||||
use crate::errors::Error;
|
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;
|
||||||
|
|
@ -15,6 +19,40 @@ mod value;
|
||||||
pub mod vm;
|
pub mod vm;
|
||||||
pub mod errors;
|
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> {
|
pub fn compile(src: &str) -> Result<HashMap<String, chunk::Chunk>, Error> {
|
||||||
let tokens = scan(src)?;
|
let tokens = scan(src)?;
|
||||||
let mut registry = HashMap::new();
|
let mut registry = HashMap::new();
|
||||||
|
|
|
||||||
131
src/main.rs
131
src/main.rs
|
|
@ -1,125 +1,72 @@
|
||||||
use axum::extract::{Request, State};
|
use axum::extract::{Request, State};
|
||||||
use axum::http::StatusCode;
|
use axum::http::StatusCode;
|
||||||
use axum::routing::{any, get};
|
use axum::routing::any;
|
||||||
use axum::{Json, Router};
|
use axum::{Json, Router};
|
||||||
use crudlang::ast_compiler;
|
|
||||||
use crudlang::bytecode_compiler::compile;
|
|
||||||
use crudlang::chunk::Chunk;
|
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::errors::Error::Platform;
|
||||||
|
use crudlang::vm::interpret_async;
|
||||||
|
use crudlang::{compile_sourcedir, map_underlying};
|
||||||
|
use std::collections::HashMap;
|
||||||
|
use std::sync::Arc;
|
||||||
|
|
||||||
#[tokio::main]
|
#[tokio::main]
|
||||||
async fn main() -> Result<(), crudlang::errors::Error> {
|
async fn main() -> Result<(), crudlang::errors::Error> {
|
||||||
tracing_subscriber::fmt::init();
|
tracing_subscriber::fmt::init();
|
||||||
|
|
||||||
let mut paths = HashMap::new();
|
let (paths,registry) = compile_sourcedir("source")?;
|
||||||
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 registry = Arc::new(registry);
|
let registry = Arc::new(registry);
|
||||||
// if !paths.is_empty() {
|
if !paths.is_empty() {
|
||||||
// let mut app = Router::new();
|
let state = Arc::new(AppState {
|
||||||
// for (path, code) in paths.iter() {
|
registry: registry.clone(),
|
||||||
// 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())?;
|
|
||||||
// }
|
|
||||||
|
|
||||||
let app = Router::new()
|
let app = Router::new()
|
||||||
.route("/", any(handle_any))
|
.route("/", any(handle_any).with_state(state.clone()))
|
||||||
.route("/{*path}", any(handle_any));
|
.route("/{*path}", any(handle_any).with_state(state.clone()));
|
||||||
|
|
||||||
let listener = tokio::net::TcpListener::bind("0.0.0.0:3000")
|
let listener = tokio::net::TcpListener::bind("0.0.0.0:3000")
|
||||||
.await
|
.await
|
||||||
.map_err(map_underlying())?;
|
.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())?;
|
axum::serve(listener, app).await.map_err(map_underlying())?;
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
} else {
|
||||||
|
Err(Platform("No source files found".to_string()))
|
||||||
fn map_underlying() -> fn(std::io::Error) -> Error {
|
}
|
||||||
|e| Platform(e.to_string())
|
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Clone)]
|
#[derive(Clone)]
|
||||||
struct AppState {
|
struct AppState {
|
||||||
name: String,
|
|
||||||
registry: Arc<HashMap<String, Chunk>>,
|
registry: Arc<HashMap<String, Chunk>>,
|
||||||
}
|
}
|
||||||
|
|
||||||
async fn handle_get(State(state): State<Arc<AppState>>) -> Result<Json<String>, StatusCode> {
|
async fn handle_any(
|
||||||
Ok(Json(
|
State(state): State<Arc<AppState>>,
|
||||||
interpret_async(&state.registry, &state.name)
|
req: Request,
|
||||||
.await
|
) -> Result<Json<String>, StatusCode> {
|
||||||
.unwrap()
|
let method = req.method().to_string().to_ascii_lowercase();
|
||||||
.to_string(),
|
|
||||||
))
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
async fn handle_any(req: Request) -> impl IntoResponse {
|
|
||||||
let method = req.method().clone();
|
|
||||||
let uri = req.uri();
|
let uri = req.uri();
|
||||||
|
|
||||||
// Parse path segments
|
|
||||||
let path_segments: Vec<&str> = uri.path()
|
|
||||||
.split('/')
|
|
||||||
.filter(|s| !s.is_empty())
|
|
||||||
.collect();
|
|
||||||
|
|
||||||
// Parse query parameters
|
// Parse query parameters
|
||||||
let query_params: HashMap<String, String> = uri.query()
|
let query_params: HashMap<String, String> = uri
|
||||||
|
.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);
|
||||||
format!(
|
Ok(Json(
|
||||||
"Method: {}\nPath: {}\nSegments: {:?}\nQuery: {:?}",
|
interpret_async(&state.registry, &component)
|
||||||
method, uri.path(), path_segments, query_params
|
.await
|
||||||
)
|
.unwrap()
|
||||||
|
.to_string(),
|
||||||
|
))
|
||||||
}
|
}
|
||||||
|
|
|
||||||
41
src/vm.rs
41
src/vm.rs
|
|
@ -1,10 +1,10 @@
|
||||||
use crate::chunk::Chunk;
|
use crate::chunk::Chunk;
|
||||||
use crate::errors::RuntimeError::{Something};
|
use crate::errors::RuntimeError::Something;
|
||||||
use crate::errors::{RuntimeError, ValueError};
|
use crate::errors::{RuntimeError, ValueError};
|
||||||
|
use crate::tokens::TokenType;
|
||||||
use crate::value::Value;
|
use crate::value::Value;
|
||||||
use std::collections::HashMap;
|
use std::collections::HashMap;
|
||||||
use tracing::debug;
|
use tracing::debug;
|
||||||
use crate::tokens::TokenType;
|
|
||||||
|
|
||||||
pub struct Vm<'a> {
|
pub struct Vm<'a> {
|
||||||
ip: usize,
|
ip: usize,
|
||||||
|
|
@ -30,16 +30,23 @@ pub fn interpret(registry: &HashMap<String, Chunk>, function: &str) -> Result<Va
|
||||||
vm.run(&chunk)
|
vm.run(&chunk)
|
||||||
}
|
}
|
||||||
|
|
||||||
pub async fn interpret_async(registry: &HashMap<String, Chunk>, function: &str) -> Result<Value, RuntimeError> {
|
pub async fn interpret_async(
|
||||||
let chunk = registry.get(function).unwrap().clone();
|
registry: &HashMap<String, Chunk>,
|
||||||
let mut vm = Vm {
|
function: &str,
|
||||||
ip: 0,
|
) -> Result<Value, RuntimeError> {
|
||||||
stack: vec![],
|
let chunk = registry.get(function);
|
||||||
local_vars: HashMap::new(),
|
if let Some(chunk) = chunk {
|
||||||
error_occurred: false,
|
let mut vm = Vm {
|
||||||
registry,
|
ip: 0,
|
||||||
};
|
stack: vec![],
|
||||||
vm.run(&chunk)
|
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> {
|
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> {
|
impl<'a> Vm<'a> {
|
||||||
fn run_function(&mut self, chunk: &Chunk, mut args: Vec<Value>) -> Result<Value, RuntimeError> {
|
fn run_function(&mut self, chunk: &Chunk, mut args: Vec<Value>) -> Result<Value, RuntimeError> {
|
||||||
// arguments -> locals
|
// arguments -> locals
|
||||||
for (_,name) in chunk.vars.iter() {
|
for (_, name) in chunk.vars.iter() {
|
||||||
self.local_vars.insert(name.clone(), args.remove(0));
|
self.local_vars.insert(name.clone(), args.remove(0));
|
||||||
}
|
}
|
||||||
self.run(&chunk)
|
self.run(&chunk)
|
||||||
|
|
@ -134,11 +141,11 @@ impl<'a> Vm<'a> {
|
||||||
list.reverse();
|
list.reverse();
|
||||||
self.push(Value::List(list));
|
self.push(Value::List(list));
|
||||||
}
|
}
|
||||||
OP_ASSIGN=>{
|
OP_ASSIGN => {
|
||||||
let index = self.read(chunk);
|
let index = self.read(chunk);
|
||||||
let (var_type, name) = chunk.vars.get(index).unwrap();
|
let (var_type, name) = chunk.vars.get(index).unwrap();
|
||||||
let value = self.pop();
|
let value = self.pop();
|
||||||
let value = match var_type{
|
let value = match var_type {
|
||||||
TokenType::U32 => value.cast_u32()?,
|
TokenType::U32 => value.cast_u32()?,
|
||||||
TokenType::U64 => value.cast_u64()?,
|
TokenType::U64 => value.cast_u64()?,
|
||||||
TokenType::F32 => value.cast_f32()?,
|
TokenType::F32 => value.cast_f32()?,
|
||||||
|
|
@ -159,7 +166,7 @@ impl<'a> Vm<'a> {
|
||||||
}
|
}
|
||||||
OP_GET => {
|
OP_GET => {
|
||||||
let var_index = self.read(chunk);
|
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();
|
let value = self.local_vars.get(name_index).unwrap();
|
||||||
self.push(value.clone()); // not happy , take ownership, no clone
|
self.push(value.clone()); // not happy , take ownership, no clone
|
||||||
debug!("after get {:?}", self.stack);
|
debug!("after get {:?}", self.stack);
|
||||||
|
|
@ -173,7 +180,7 @@ impl<'a> Vm<'a> {
|
||||||
let arg = self.pop();
|
let arg = self.pop();
|
||||||
args.push(arg);
|
args.push(arg);
|
||||||
}
|
}
|
||||||
// args.reverse();
|
args.reverse();
|
||||||
|
|
||||||
let function_name = chunk.constants[function_name_index].to_string();
|
let function_name = chunk.constants[function_name_index].to_string();
|
||||||
let function_chunk = self.registry.get(&function_name).unwrap();
|
let function_chunk = self.registry.get(&function_name).unwrap();
|
||||||
|
|
|
||||||
Loading…
Add table
Reference in a new issue