cl_embed: Goodbye, cl_embed. You will be missed.
This commit is contained in:
		| @@ -2,7 +2,6 @@ | ||||
| members = [ | ||||
|     "compiler/cl-repl", | ||||
|     "compiler/cl-typeck", | ||||
|     "compiler/cl-embed", | ||||
|     "compiler/cl-interpret", | ||||
|     "compiler/cl-structures", | ||||
|     "compiler/cl-token", | ||||
| @@ -14,7 +13,7 @@ resolver = "2" | ||||
|  | ||||
| [workspace.package] | ||||
| repository = "https://git.soft.fish/j/Conlang" | ||||
| version = "0.0.10" | ||||
| version = "0.0.11" | ||||
| authors = ["John Breaux <j@soft.fish>"] | ||||
| edition = "2024" | ||||
| license = "MIT" | ||||
|   | ||||
| @@ -1,18 +0,0 @@ | ||||
| [package] | ||||
| name = "cl-embed" | ||||
| version = "0.1.0" | ||||
| repository.workspace = true | ||||
| authors.workspace = true | ||||
| edition.workspace = true | ||||
| license.workspace = true | ||||
| publish.workspace = true | ||||
|  | ||||
| [dependencies] | ||||
| cl-interpret = { path = "../cl-interpret" } | ||||
| cl-ast = { path = "../cl-ast" } | ||||
| cl-structures = { path = "../cl-structures" } | ||||
| cl-lexer = { path = "../cl-lexer" } | ||||
| cl-parser = { path = "../cl-parser" } | ||||
|  | ||||
| [dev-dependencies] | ||||
| repline = { version = "*", registry = "soft-fish" } | ||||
| @@ -1,27 +0,0 @@ | ||||
| //! Demonstrates the cl_embed library | ||||
|  | ||||
| use cl_embed::*; | ||||
| use repline::{Response, prebaked}; | ||||
|  | ||||
| fn main() -> Result<(), repline::Error> { | ||||
|     let mut env = Environment::new(); | ||||
|  | ||||
|     if let Err(e) = conlang_include!("calculator/expression.cl")(&mut env) { | ||||
|         panic!("{e}") | ||||
|     } | ||||
|  | ||||
|     prebaked::read_and("", "calc >", "   ? >", |line| { | ||||
|         env.bind("line", line); | ||||
|  | ||||
|         let res = conlang! { | ||||
|  | ||||
|             let (expr, rest) = parse(line.chars(), Power::None); | ||||
|             execute(expr) | ||||
|  | ||||
|         }(&mut env)?; | ||||
|  | ||||
|         println!("{res}"); | ||||
|  | ||||
|         Ok(Response::Accept) | ||||
|     }) | ||||
| } | ||||
| @@ -1 +0,0 @@ | ||||
| ../../../../sample-code/calculator.cl | ||||
| @@ -1,182 +0,0 @@ | ||||
| //! Embed Conlang code into your Rust project! | ||||
| //! | ||||
| //! # This crate is experimental, and has no guarantees of stability. | ||||
| #![feature(decl_macro)] | ||||
| #![cfg_attr(test, feature(assert_matches))] | ||||
| #![allow(unused_imports)] | ||||
|  | ||||
| pub use cl_interpret::{convalue::ConValue as Value, env::Environment}; | ||||
|  | ||||
| use cl_ast::{Block, File, Module, ast_visitor::Fold}; | ||||
| use cl_interpret::{convalue::ConValue, interpret::Interpret}; | ||||
| use cl_lexer::Lexer; | ||||
| use cl_parser::{Parser, error::Error as ParseError, inliner::ModuleInliner}; | ||||
| use std::{path::Path, sync::OnceLock}; | ||||
|  | ||||
| /// Constructs a function which evaluates a Conlang Block | ||||
| /// | ||||
| /// # Examples | ||||
| /// | ||||
| /// Bind and use a variable | ||||
| /// ```rust | ||||
| /// # fn main() -> Result<(), Box<dyn std::error::Error>> { | ||||
| /// use cl_embed::{conlang, Environment, Value}; | ||||
| /// | ||||
| /// let mut env = Environment::new(); | ||||
| /// | ||||
| /// // Bind a variable named `message` to "Hello, world!" | ||||
| /// env.bind("message", "Hello, World!"); | ||||
| /// | ||||
| /// let print_hello = conlang!{ | ||||
| ///     println(message); | ||||
| /// }; | ||||
| /// | ||||
| /// // Run the function | ||||
| /// let ret = print_hello(&mut env)?; | ||||
| /// | ||||
| /// // `println` returns Empty | ||||
| /// assert!(matches!(ret, Value::Empty)); | ||||
| /// | ||||
| /// # Ok(()) | ||||
| /// # } | ||||
| /// ``` | ||||
| pub macro conlang( | ||||
|     $($t:tt)* | ||||
| ) {{ | ||||
|     // Parse once | ||||
|     static FN: OnceLock<Result<Block, ParseError>> = OnceLock::new(); | ||||
|  | ||||
|     |env: &mut Environment| -> Result<ConValue, EvalError> { | ||||
|         FN.get_or_init(|| { | ||||
|             // TODO: embed the full module tree at compile time | ||||
|             let path = | ||||
|                 AsRef::<Path>::as_ref(&concat!(env!("CARGO_MANIFEST_DIR"), "/../../", file!())) | ||||
|                     .with_extension(""); | ||||
|             let mut mi = ModuleInliner::new(path); | ||||
|             let code = mi.fold_block( | ||||
|                 Parser::new( | ||||
|                     concat!(file!(), ":", line!(), ":", column!()), | ||||
|                     Lexer::new(stringify!({ $($t)* })), | ||||
|                 ) | ||||
|                 .parse::<Block>()?, | ||||
|             ); | ||||
|             if let Some((ie, pe)) = mi.into_errs() { | ||||
|                 for (file, err) in ie { | ||||
|                     eprintln!("{}: {err}", file.display()); | ||||
|                 } | ||||
|                 for (file, err) in pe { | ||||
|                     eprintln!("{}: {err}", file.display()); | ||||
|                 } | ||||
|             } | ||||
|             Ok(code) | ||||
|         }) | ||||
|         .as_ref() | ||||
|         .map_err(Clone::clone)? | ||||
|         .interpret(env) | ||||
|         .map_err(Into::into) | ||||
|     } | ||||
| }} | ||||
|  | ||||
| pub macro conlang_include{ | ||||
|     ($path:literal, $name:ident) => { | ||||
|         |env: &mut Environment| -> Result<ConValue, EvalError> { | ||||
|             // TODO: embed the full module tree at compile time | ||||
|             let path = AsRef::<Path>::as_ref(&concat!(env!("CARGO_MANIFEST_DIR"), "/../../", file!())) | ||||
|                 .with_file_name(concat!($path)); | ||||
|             let mut mi = ModuleInliner::new(path); | ||||
|             let code = mi.fold_module(Module { | ||||
|                 name: stringify!($name).into(), | ||||
|                 file: Some( | ||||
|                     Parser::new( | ||||
|                         concat!(file!(), ":", line!(), ":", column!()), | ||||
|                         Lexer::new(include_str!($path)), | ||||
|                     ) | ||||
|                     .parse()?, | ||||
|                 ), | ||||
|             }); | ||||
|             if let Some((ie, pe)) = mi.into_errs() { | ||||
|                 for (file, err) in ie { | ||||
|                     eprintln!("{}: {err}", file.display()); | ||||
|                 } | ||||
|                 for (file, err) in pe { | ||||
|                     eprintln!("{}: {err}", file.display()); | ||||
|                 } | ||||
|             } | ||||
|             code.interpret(env).map_err(Into::into) | ||||
|         } | ||||
|     }, | ||||
|     ($path:literal) => { | ||||
|         |env: &mut Environment| -> Result<ConValue, EvalError> { | ||||
|             // TODO: embed the full module tree at compile time | ||||
|             let path = AsRef::<Path>::as_ref(&concat!(env!("CARGO_MANIFEST_DIR"), "/../../", file!())) | ||||
|                 .with_file_name(concat!($path)); | ||||
|             let mut mi = ModuleInliner::new(path); | ||||
|             let code = mi.fold_file( | ||||
|                 Parser::new( | ||||
|                     concat!(file!(), ":", line!(), ":", column!()), | ||||
|                     Lexer::new(include_str!($path)), | ||||
|                 ) | ||||
|                 .parse()?, | ||||
|             ); | ||||
|             if let Some((ie, pe)) = mi.into_errs() { | ||||
|                 for (file, err) in ie { | ||||
|                     eprintln!("{}: {err}", file.display()); | ||||
|                 } | ||||
|                 for (file, err) in pe { | ||||
|                     eprintln!("{}: {err}", file.display()); | ||||
|                 } | ||||
|             } | ||||
|             code.interpret(env).map_err(Into::into) | ||||
|         } | ||||
|     } | ||||
| } | ||||
|  | ||||
| #[derive(Clone, Debug)] | ||||
| pub enum EvalError { | ||||
|     Parse(cl_parser::error::Error), | ||||
|     Interpret(cl_interpret::error::Error), | ||||
| } | ||||
|  | ||||
| impl From<cl_parser::error::Error> for EvalError { | ||||
|     fn from(value: cl_parser::error::Error) -> Self { | ||||
|         Self::Parse(value) | ||||
|     } | ||||
| } | ||||
| impl From<cl_interpret::error::Error> for EvalError { | ||||
|     fn from(value: cl_interpret::error::Error) -> Self { | ||||
|         Self::Interpret(value) | ||||
|     } | ||||
| } | ||||
| impl std::error::Error for EvalError {} | ||||
| impl std::fmt::Display for EvalError { | ||||
|     fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { | ||||
|         match self { | ||||
|             EvalError::Parse(error) => error.fmt(f), | ||||
|             EvalError::Interpret(error) => error.fmt(f), | ||||
|         } | ||||
|     } | ||||
| } | ||||
|  | ||||
| #[cfg(test)] | ||||
| mod tests { | ||||
|     use std::assert_matches::assert_matches; | ||||
|  | ||||
|     use super::*; | ||||
|  | ||||
|     #[test] | ||||
|     fn it_works() -> Result<(), EvalError> { | ||||
|         let mut env = Environment::new(); | ||||
|  | ||||
|         let result = conlang! { | ||||
|             fn add(left, right) -> isize { | ||||
|                 left + right | ||||
|             } | ||||
|  | ||||
|             add(2, 2) | ||||
|         }(&mut env); | ||||
|  | ||||
|         assert_matches!(result, Ok(Value::Int(4))); | ||||
|  | ||||
|         Ok(()) | ||||
|     } | ||||
| } | ||||
		Reference in New Issue
	
	Block a user