cl_embed: Goodbye, cl_embed. You will be missed.
This commit is contained in:
@@ -2,7 +2,6 @@
|
|||||||
members = [
|
members = [
|
||||||
"compiler/cl-repl",
|
"compiler/cl-repl",
|
||||||
"compiler/cl-typeck",
|
"compiler/cl-typeck",
|
||||||
"compiler/cl-embed",
|
|
||||||
"compiler/cl-interpret",
|
"compiler/cl-interpret",
|
||||||
"compiler/cl-structures",
|
"compiler/cl-structures",
|
||||||
"compiler/cl-token",
|
"compiler/cl-token",
|
||||||
@@ -14,7 +13,7 @@ resolver = "2"
|
|||||||
|
|
||||||
[workspace.package]
|
[workspace.package]
|
||||||
repository = "https://git.soft.fish/j/Conlang"
|
repository = "https://git.soft.fish/j/Conlang"
|
||||||
version = "0.0.10"
|
version = "0.0.11"
|
||||||
authors = ["John Breaux <j@soft.fish>"]
|
authors = ["John Breaux <j@soft.fish>"]
|
||||||
edition = "2024"
|
edition = "2024"
|
||||||
license = "MIT"
|
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