Skip to content

Commit

Permalink
add experimental eval function
Browse files Browse the repository at this point in the history
  • Loading branch information
kaikalii committed Nov 26, 2024
1 parent 1eddcad commit a56d089
Show file tree
Hide file tree
Showing 6 changed files with 70 additions and 5 deletions.
1 change: 1 addition & 0 deletions changelog.md
Original file line number Diff line number Diff line change
Expand Up @@ -35,6 +35,7 @@ This version is not yet released. If you are reading this on the website, then t
- Add experimental [inline macros](https://www.uiua.org/docs/experimental#inline-macros)
- Deprecate the experimental `stringify` and `signature` modifiers in favor of inline code macros
- Add experimental [`binary`](https://uiua.org/docs/binary) function, which encodes and decodes arrays into a compact binary representation
- Add experimental [`eval`](https://uiua.org/docs/eval) function, which evaluates a string as code at compile time
- Remove the previously deprecated experimental `coordinate ⟔` function
- Remove the previously deprecated experimental `struct` modifier
- Remove the previously deprecated `setinv` and `setund` modifiers
Expand Down
4 changes: 2 additions & 2 deletions src/compile/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -106,7 +106,7 @@ impl Default for Compiler {
print_diagnostics: false,
comptime: true,
pre_eval_mode: PreEvalMode::default(),
macro_env: Uiua::default(),
macro_env: Uiua::default().comptime(),
}
}
}
Expand Down Expand Up @@ -247,7 +247,7 @@ impl Compiler {
/// Create a new compiler with a custom backend for `comptime` code
pub fn with_backend(backend: impl IntoSysBackend) -> Self {
Self {
macro_env: Uiua::with_backend(backend.into_sys_backend()),
macro_env: Uiua::with_backend(backend.into_sys_backend()).comptime(),
..Self::default()
}
}
Expand Down
23 changes: 23 additions & 0 deletions src/primitive/defs.rs
Original file line number Diff line number Diff line change
Expand Up @@ -3454,6 +3454,29 @@ primitive!(
/// : ⍜⊜□⍚(⊂@,)∈" \n". repr # add commas
/// : &p ⍜▽∵⋅@-=@¯. # replace negate glyphs with minus signs
(1, Repr, Misc, "repr"),
/// Evalute a string as code at compile time
///
/// The first argument is a list of arguments to pass to the code.
/// The second argument is the string to evaluate.
/// If the code returns a single value, it will simply be pushed to the stack.
/// If the code returns multiple values, they will collected into a list of boxes.
///
/// [eval] can only be called in a compile-time context.
/// ex! # Experimental!
/// : eval {1 2} "+"
/// The simplest way to do this is to use [comptime].
/// ex: # Experimental!
/// : comptime(eval {1 2} "+")
/// [eval] is probably more useful in code macros.
/// ex: # Experimental!
/// : F! ←^ eval{1 2}⊢
/// : F!+
/// ex: # Experimental!
/// : F! ←^ eval{}⊢
/// : F!"+1" 5
///
/// [eval] is still very experimental and may be buggy.
(2, Eval, Misc, "eval"),
/// Encode an image into a byte array with the specified format
///
/// The first argument is the format, and the second is the image.
Expand Down
37 changes: 35 additions & 2 deletions src/primitive/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -33,7 +33,7 @@ use crate::{
lex::{AsciiToken, SUBSCRIPT_DIGITS},
sys::*,
value::*,
FunctionId, Ops, Shape, Signature, Uiua, UiuaErrorKind, UiuaResult,
Compiler, FunctionId, Ops, Shape, Signature, Uiua, UiuaErrorKind, UiuaResult,
};

/// Categories of primitives
Expand Down Expand Up @@ -547,7 +547,7 @@ impl Primitive {
self,
(Off | Backward | Above | Around)
| (Tuples | Choose | Permute)
| (Or | Chunks | Base | Fft | Case | Layout | Binary)
| (Or | Chunks | Base | Fft | Case | Layout | Binary | Eval)
| (Astar | Triangle)
| (Derivative | Integral)
| Sys(Ffi | MemCopy | MemFree | TlsListen)
Expand Down Expand Up @@ -837,6 +837,39 @@ impl Primitive {
env.push(val.box_depth(0));
}
Primitive::Repr => env.monadic_ref(Value::representation)?,
Primitive::Eval => {
if !env.rt.is_comptime {
return Err(env.error("eval cannot be called outside a compile-time context"));
}
let args = env.pop(1)?;
let code = env.pop(2)?.as_string(env, "eval expects a string")?;
let stack = env.take_stack();
for arg in args.into_rows().rev() {
env.push(arg.unboxed());
}

let mut comp = Compiler::with_backend(env.rt.backend.clone());
comp.asm.spans = env.asm.spans.clone();
comp.asm.inputs = env.asm.inputs.clone();
let res = (|| -> UiuaResult {
comp.load_str(&code)?;
env.run_compiler(&mut comp)?;
env.asm.spans = comp.asm.spans;
env.asm.inputs = comp.asm.inputs;
Ok(())
})();

let results = env.take_stack();
for val in stack.into_iter().rev() {
env.push(val);
}
env.push(if results.len() == 1 {
results.into_iter().next().unwrap()
} else {
Array::from_iter(results.into_iter().map(Boxed)).into()
});
res?;
}
Primitive::Parse => env.monadic_ref_env(Value::parse_num)?,
Primitive::Utf8 => env.monadic_ref_env(Value::utf8)?,
Primitive::Graphemes => env.monadic_ref_env(Value::graphemes)?,
Expand Down
9 changes: 9 additions & 0 deletions src/run.rs
Original file line number Diff line number Diff line change
Expand Up @@ -59,6 +59,8 @@ pub(crate) struct Runtime {
pub(crate) execution_start: f64,
/// The recursion limit
recursion_limit: usize,
/// Whether this is a compile-time runtime
pub(crate) is_comptime: bool,
/// Whether the program was interrupted
pub(crate) interrupted: Option<Arc<dyn Fn() -> bool + Send + Sync>>,
/// Whether to print the time taken to execute each instruction
Expand Down Expand Up @@ -199,6 +201,7 @@ impl Default for Runtime {
cli_file_path: PathBuf::new(),
execution_limit: None,
execution_start: 0.0,
is_comptime: false,
#[cfg(debug_assertions)]
recursion_limit: 20,
#[cfg(not(debug_assertions))]
Expand Down Expand Up @@ -241,6 +244,11 @@ impl Uiua {
pub fn build(self) -> Assembly {
self.asm
}
/// Make this a compile-time runtime
pub(crate) fn comptime(mut self) -> Self {
self.rt.is_comptime = true;
self
}
/// Get a reference to the system backend
pub fn backend(&self) -> &dyn SysBackend {
&*self.rt.backend
Expand Down Expand Up @@ -1433,6 +1441,7 @@ impl Uiua {
interrupted: self.rt.interrupted.clone(),
output_comments: HashMap::new(),
memo: self.rt.memo.clone(),
is_comptime: self.rt.is_comptime,
unevaluated_constants: HashMap::new(),
test_results: Vec::new(),
reports: Vec::new(),
Expand Down
1 change: 0 additions & 1 deletion todo.md
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,6 @@ The next version of Uiua

- Stabilize `backward`, `case`
- Optimize `group`/`partition` reduction replacement
- Compile-time code string evaluation
- `do` function pack
- Allow for multi-value constant bindings
- Animated WEBP support? (`&webp`)
Expand Down

0 comments on commit a56d089

Please sign in to comment.