1#![feature(decl_macro)]
5#![cfg_attr(test, feature(assert_matches))]
6#![allow(unused_imports)]
7
8pub use cl_interpret::{convalue::ConValue as Value, env::Environment};
9
10use cl_ast::{Block, File, Module, ast_visitor::Fold};
11use cl_interpret::{convalue::ConValue, interpret::Interpret};
12use cl_lexer::Lexer;
13use cl_parser::{Parser, error::Error as ParseError, inliner::ModuleInliner};
14use std::{path::Path, sync::OnceLock};
15
16pub macro conlang(
44 $($t:tt)*
45) {{
46 static FN: OnceLock<Result<Block, ParseError>> = OnceLock::new();
48
49 |env: &mut Environment| -> Result<ConValue, EvalError> {
50 FN.get_or_init(|| {
51 let path =
53 AsRef::<Path>::as_ref(&concat!(env!("CARGO_MANIFEST_DIR"), "/../../", file!()))
54 .with_extension("");
55 let mut mi = ModuleInliner::new(path);
56 let code = mi.fold_block(
57 Parser::new(
58 concat!(file!(), ":", line!(), ":", column!()),
59 Lexer::new(stringify!({ $($t)* })),
60 )
61 .parse::<Block>()?,
62 );
63 if let Some((ie, pe)) = mi.into_errs() {
64 for (file, err) in ie {
65 eprintln!("{}: {err}", file.display());
66 }
67 for (file, err) in pe {
68 eprintln!("{}: {err}", file.display());
69 }
70 }
71 Ok(code)
72 })
73 .as_ref()
74 .map_err(Clone::clone)?
75 .interpret(env)
76 .map_err(Into::into)
77 }
78}}
79
80pub macro conlang_include{
81 ($path:literal, $name:ident) => {
82 |env: &mut Environment| -> Result<ConValue, EvalError> {
83 let path = AsRef::<Path>::as_ref(&concat!(env!("CARGO_MANIFEST_DIR"), "/../../", file!()))
85 .with_file_name(concat!($path));
86 let mut mi = ModuleInliner::new(path);
87 let code = mi.fold_module(Module {
88 name: stringify!($name).into(),
89 file: Some(
90 Parser::new(
91 concat!(file!(), ":", line!(), ":", column!()),
92 Lexer::new(include_str!($path)),
93 )
94 .parse()?,
95 ),
96 });
97 if let Some((ie, pe)) = mi.into_errs() {
98 for (file, err) in ie {
99 eprintln!("{}: {err}", file.display());
100 }
101 for (file, err) in pe {
102 eprintln!("{}: {err}", file.display());
103 }
104 }
105 code.interpret(env).map_err(Into::into)
106 }
107 },
108 ($path:literal) => {
109 |env: &mut Environment| -> Result<ConValue, EvalError> {
110 let path = AsRef::<Path>::as_ref(&concat!(env!("CARGO_MANIFEST_DIR"), "/../../", file!()))
112 .with_file_name(concat!($path));
113 let mut mi = ModuleInliner::new(path);
114 let code = mi.fold_file(
115 Parser::new(
116 concat!(file!(), ":", line!(), ":", column!()),
117 Lexer::new(include_str!($path)),
118 )
119 .parse()?,
120 );
121 if let Some((ie, pe)) = mi.into_errs() {
122 for (file, err) in ie {
123 eprintln!("{}: {err}", file.display());
124 }
125 for (file, err) in pe {
126 eprintln!("{}: {err}", file.display());
127 }
128 }
129 code.interpret(env).map_err(Into::into)
130 }
131 }
132}
133
134#[derive(Clone, Debug)]
135pub enum EvalError {
136 Parse(cl_parser::error::Error),
137 Interpret(cl_interpret::error::Error),
138}
139
140impl From<cl_parser::error::Error> for EvalError {
141 fn from(value: cl_parser::error::Error) -> Self {
142 Self::Parse(value)
143 }
144}
145impl From<cl_interpret::error::Error> for EvalError {
146 fn from(value: cl_interpret::error::Error) -> Self {
147 Self::Interpret(value)
148 }
149}
150impl std::error::Error for EvalError {}
151impl std::fmt::Display for EvalError {
152 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
153 match self {
154 EvalError::Parse(error) => error.fmt(f),
155 EvalError::Interpret(error) => error.fmt(f),
156 }
157 }
158}
159
160#[cfg(test)]
161mod tests {
162 use std::assert_matches::assert_matches;
163
164 use super::*;
165
166 #[test]
167 fn it_works() -> Result<(), EvalError> {
168 let mut env = Environment::new();
169
170 let result = conlang! {
171 fn add(left, right) -> isize {
172 left + right
173 }
174
175 add(2, 2)
176 }(&mut env);
177
178 assert_matches!(result, Ok(Value::Int(4)));
179
180 Ok(())
181 }
182}