diff --git a/Cargo.lock b/Cargo.lock index 358fa27..dfb8f37 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -11,6 +11,56 @@ dependencies = [ "libc", ] +[[package]] +name = "anstream" +version = "0.6.21" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "43d5b281e737544384e969a5ccad3f1cdd24b48086a0fc1b2a5262a26b8f4f4a" +dependencies = [ + "anstyle", + "anstyle-parse", + "anstyle-query", + "anstyle-wincon", + "colorchoice", + "is_terminal_polyfill", + "utf8parse", +] + +[[package]] +name = "anstyle" +version = "1.0.13" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5192cca8006f1fd4f7237516f40fa183bb07f8fbdfedaa0036de5ea9b0b45e78" + +[[package]] +name = "anstyle-parse" +version = "0.2.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4e7644824f0aa2c7b9384579234ef10eb7efb6a0deb83f9630a49594dd9c15c2" +dependencies = [ + "utf8parse", +] + +[[package]] +name = "anstyle-query" +version = "1.1.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9e231f6134f61b71076a3eab506c379d4f36122f2af15a9ff04415ea4c3339e2" +dependencies = [ + "windows-sys 0.60.2", +] + +[[package]] +name = "anstyle-wincon" +version = "3.0.10" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3e0633414522a32ffaac8ac6cc8f748e090c5717661fddeea04219e2344f5f2a" +dependencies = [ + "anstyle", + "once_cell_polyfill", + "windows-sys 0.60.2", +] + [[package]] name = "anyhow" version = "1.0.100" @@ -166,6 +216,52 @@ dependencies = [ "windows-link 0.2.1", ] +[[package]] +name = "clap" +version = "4.5.51" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4c26d721170e0295f191a69bd9a1f93efcdb0aff38684b61ab5750468972e5f5" +dependencies = [ + "clap_builder", + "clap_derive", +] + +[[package]] +name = "clap_builder" +version = "4.5.51" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "75835f0c7bf681bfd05abe44e965760fea999a5286c6eb2d59883634fd02011a" +dependencies = [ + "anstream", + "anstyle", + "clap_lex", + "strsim", +] + +[[package]] +name = "clap_derive" +version = "4.5.49" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2a0b5487afeab2deb2ff4e03a807ad1a03ac532ff5a2cee5d86884440c7f7671" +dependencies = [ + "heck", + "proc-macro2", + "quote", + "syn", +] + +[[package]] +name = "clap_lex" +version = "0.7.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a1d728cc89cf3aee9ff92b05e62b19ee65a02b5702cff7d5a377e32c6ae29d8d" + +[[package]] +name = "colorchoice" +version = "1.0.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b05b61dc5112cbb17e4b6cd61790d9845d13888356391624cbe7e41efeac1e75" + [[package]] name = "core-foundation" version = "0.9.4" @@ -197,6 +293,7 @@ version = "0.1.0" dependencies = [ "axum", "chrono", + "clap", "dotenv", "log", "log4rs", @@ -451,6 +548,12 @@ version = "0.16.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "5419bdc4f6a9207fbeba6d11b604d481addf78ecd10c11ad51e76c2f6482748d" +[[package]] +name = "heck" +version = "0.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2304e00983f87ffb38b55b444b5e3b60a884b5d30c0fca7d82fe33449bbe55ea" + [[package]] name = "hmac" version = "0.12.1" @@ -756,6 +859,12 @@ dependencies = [ "serde", ] +[[package]] +name = "is_terminal_polyfill" +version = "1.70.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a6cb138bb79a146c1bd460005623e142ef0181e3d0219cb493e02f7d08a35695" + [[package]] name = "itoa" version = "1.0.15" @@ -956,6 +1065,12 @@ version = "1.21.3" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "42f5e15c9953c5e4ccceeb2e7382a716482c34515315f7b03532b8b4e8393d2d" +[[package]] +name = "once_cell_polyfill" +version = "1.70.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "384b8ab6d37215f3c5301a95a4accb5d64aa607f1fcb26a11b5303878451b4fe" + [[package]] name = "openssl" version = "0.10.73" @@ -1514,6 +1629,12 @@ dependencies = [ "unicode-properties", ] +[[package]] +name = "strsim" +version = "0.11.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7da8b5736845d9f2fcb837ea5d9e2628564b3b043a70948a3f0b778838c5fb4f" + [[package]] name = "subtle" version = "2.6.1" @@ -1969,6 +2090,12 @@ version = "1.0.4" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "b6c140620e7ffbb22c2dee59cafe6084a59b5ffc27a8859a5f0d494b5d52b6be" +[[package]] +name = "utf8parse" +version = "0.2.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "06abde3611657adf66d383f00b093d7faecc7fa57071cce2578660c9f1010821" + [[package]] name = "valuable" version = "0.1.1" diff --git a/Cargo.toml b/Cargo.toml index 5d751e4..a3d3d0c 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -22,3 +22,4 @@ log = "0.4.28" walkdir = "2.5.0" thiserror = "2.0.17" url = "2.5.7" +clap = { version = "4.5.51", features = ["derive"] } diff --git a/README.md b/README.md index 36b9fab..003b465 100644 --- a/README.md +++ b/README.md @@ -158,3 +158,11 @@ fn add(a: string, b: string) -> string: a + " " + b ``` * get() is the entry point for http GET method calls, likewise for POST, PUT, DELETE, etc. + +teveel ideeen +* een repl die ook een test client is + * :le list endpoints -> tree + * :lf list functions -> tree + * met ... debugger FLW! + * ingebouwde editor (vi) reload on save +* genereren van openapi spec diff --git a/src/errors.rs b/src/errors.rs index aaf2ec2..3270495 100644 --- a/src/errors.rs +++ b/src/errors.rs @@ -3,7 +3,7 @@ use crate::tokens::{Token, TokenType}; use thiserror::Error; #[derive(Error, Debug, PartialEq)] -pub enum Error { +pub enum CrudLangError { #[error("Compilation failed: {0}")] Compiler(#[from] CompilerErrorAtLine), diff --git a/src/lib.rs b/src/lib.rs index 99e758e..a6dca5a 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -1,6 +1,6 @@ use crate::chunk::Chunk; -use crate::errors::Error; -use crate::errors::Error::Platform; +use crate::errors::CrudLangError; +use crate::errors::CrudLangError::Platform; use crate::scanner::scan; use crate::value::Value; use crate::vm::interpret; @@ -18,8 +18,9 @@ pub mod scanner; mod tokens; mod value; pub mod vm; +pub mod repl; -pub fn compile_sourcedir(source_dir: &str) -> Result, Error> { +pub fn compile_sourcedir(source_dir: &str) -> Result, CrudLangError> { let mut registry = HashMap::new(); for entry in WalkDir::new(source_dir).into_iter().filter_map(|e| e.ok()) { @@ -44,11 +45,11 @@ pub fn compile_sourcedir(source_dir: &str) -> Result, Err Ok(registry) } -pub fn map_underlying() -> fn(std::io::Error) -> Error { +pub fn map_underlying() -> fn(std::io::Error) -> CrudLangError { |e| Platform(e.to_string()) } -pub fn compile(src: &str) -> Result, Error> { +pub fn compile(src: &str) -> Result, CrudLangError> { let tokens = scan(src)?; let mut registry = HashMap::new(); let ast = ast_compiler::compile(None, tokens)?; @@ -56,10 +57,10 @@ pub fn compile(src: &str) -> Result, Error> { Ok(registry) } -fn run(src: &str) -> Result { +fn run(src: &str) -> Result { let tokens = scan(src)?; let mut registry = HashMap::new(); let ast = ast_compiler::compile(None, tokens)?; bytecode_compiler::compile(None, &ast, &mut registry)?; - interpret(®istry, "main").map_err(Error::from) + interpret(®istry, "main").map_err(CrudLangError::from) } diff --git a/src/main.rs b/src/main.rs index a52c66e..f2308f3 100644 --- a/src/main.rs +++ b/src/main.rs @@ -2,21 +2,37 @@ use axum::extract::{Request, State}; use axum::http::StatusCode; use axum::routing::any; use axum::{Json, Router}; +use clap::Parser; use crudlang::chunk::Chunk; -use crudlang::errors::Error::Platform; +use crudlang::errors::CrudLangError; +use crudlang::errors::CrudLangError::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(); +/// A simple CLI tool to greet users +#[derive(Parser, Debug)] +struct Args { + #[arg(short, long)] + repl: bool, - let registry = compile_sourcedir("source")?; + #[arg(short, long)] + source: Option, +} + +#[tokio::main] +async fn main() -> Result<(), CrudLangError> { + println!("-- Crudlang --"); + tracing_subscriber::fmt::init(); + let args = Args::parse(); + let source = args.source.unwrap_or("source".to_string()); + let registry = compile_sourcedir(&source)?; let registry = Arc::new(registry); + if !registry.is_empty() { + println!("-- Compilation successful -- Starting server --"); let state = Arc::new(AppState { registry: registry.clone(), }); @@ -30,10 +46,14 @@ async fn main() -> Result<(), crudlang::errors::Error> { .map_err(map_underlying())?; println!( - "listening on {}", + "listening on {}\n", listener.local_addr().map_err(map_underlying())? ); + if args.repl { + std::thread::spawn(move || crudlang::repl::start(registry).unwrap()); + } + axum::serve(listener, app).await.map_err(map_underlying())?; Ok(()) } else { @@ -84,7 +104,7 @@ async fn handle_any( Ok(value) => Ok(Json(value.to_string())), Err(e) => { // url checks out but function for method not found - if state.registry.get(&format!("{}.main",component)).is_some() { + if state.registry.get(&format!("{}.main", component)).is_some() { Err(StatusCode::METHOD_NOT_ALLOWED) } else { Err(StatusCode::NOT_FOUND) diff --git a/src/repl.rs b/src/repl.rs new file mode 100644 index 0000000..9bc2ae2 --- /dev/null +++ b/src/repl.rs @@ -0,0 +1,55 @@ +use crate::chunk::Chunk; +use crate::errors::CrudLangError; +use crate::map_underlying; +use std::collections::HashMap; +use std::io; +use std::io::Write; +use std::sync::Arc; + +pub fn start(registry: Arc>) -> Result<(), CrudLangError> { + println!("REPL started -- Type ctrl-c to exit (both the repl and the server)"); + println!(":h for help"); + loop { + print!(">"); + io::stdout().flush().map_err(map_underlying())?; + let mut input = String::new(); + io::stdin() + .read_line(&mut input) + .map_err(map_underlying())?; + let input = input.trim(); + match input { + ":h" => help(), + ":le" => list_endpoints(registry.clone()), + ":lf" => list_functions(registry.clone()), + _ => {} + } + // println!("[{}]",input); + // if input == ":q" { + // break; + // } + } + // println!("-- Crudlang -- REPL exited"); + Ok(()) +} + +fn list_endpoints(registry: Arc>) { + registry + .iter() + .filter(|(k, _)| k.contains("get")) + .for_each(|(k, _)| { + println!("{}", k);//number + }); +} + +fn list_functions(registry: Arc>) { + registry + .iter() + .for_each(|(k, _)| { + println!("{}", k);//number + }); +} + +fn help() { + println!(":le\t lists all registered endpoints"); + println!(":lf\t lists all registered functions"); +}