red: add eval/set/unset commands
This commit is contained in:
@@ -6,7 +6,7 @@ use crate::{
|
||||
vm::{
|
||||
Value,
|
||||
env::Environment,
|
||||
value::{IdentifierValue, NativeObject, convert::TryFromValue},
|
||||
value::{IdentifierValue, NativeObject, StringValue, convert::TryFromValue},
|
||||
},
|
||||
};
|
||||
|
||||
@@ -60,6 +60,30 @@ pub fn load(env: &Rc<Environment>) {
|
||||
};
|
||||
Ok(outcome)
|
||||
});
|
||||
env.defun_native(
|
||||
"read-eval",
|
||||
"Evaluates the given expression, passed as a string",
|
||||
|vm, env, args| {
|
||||
let (env, input) = match args {
|
||||
[input] => (env.clone(), input),
|
||||
[env, input] => (env.as_native()?, input),
|
||||
_ => return Err(MachineError::InvalidArgumentCount),
|
||||
};
|
||||
let input = StringValue::try_from_value(input)?;
|
||||
let mut input = String::from(&*input);
|
||||
input.push('\n');
|
||||
let outcome =
|
||||
match vm.evaluate_str(Default::default(), Some("eval".into()), &env, &input) {
|
||||
Ok(result) => Value::list_or_nil([Value::Identifier("ok".into()), result]),
|
||||
Err(error) => Value::list_or_nil([
|
||||
Value::Identifier("err".into()),
|
||||
Value::String(format!("{error}").into()),
|
||||
]),
|
||||
};
|
||||
|
||||
Ok(outcome)
|
||||
},
|
||||
);
|
||||
env.defun_native("env/create", "Create a new environment", |_, env, args| {
|
||||
let parent = match args {
|
||||
[] => Some(env.clone()),
|
||||
|
||||
@@ -133,6 +133,12 @@ impl From<IdentifierValue> for Value {
|
||||
}
|
||||
}
|
||||
|
||||
impl From<StringValue> for Value {
|
||||
fn from(value: StringValue) -> Self {
|
||||
Value::String(value)
|
||||
}
|
||||
}
|
||||
|
||||
impl TryFromValue<'_> for IdentifierValue {
|
||||
fn try_from_value(value: &'_ Value) -> Result<Self, ValueConversionError> {
|
||||
match value {
|
||||
|
||||
@@ -1,3 +1,17 @@
|
||||
;; TODO prelude
|
||||
(defun string/join (xs)
|
||||
(let (accumulator "")
|
||||
(while (cons? xs)
|
||||
(if accumulator
|
||||
(setq accumulator (+ accumulator " " (car xs)))
|
||||
(setq accumulator (car xs))
|
||||
)
|
||||
(setq xs (cdr xs))
|
||||
)
|
||||
accumulator
|
||||
)
|
||||
)
|
||||
|
||||
(declare-command "q" () (red/quit))
|
||||
(declare-command "q!" () (red/quit #t))
|
||||
(declare-command
|
||||
@@ -28,3 +42,37 @@
|
||||
"source" (filename)
|
||||
(import filename)
|
||||
)
|
||||
|
||||
;; TODO only allow some keys to be set
|
||||
;; TODO at least check that the key is defined
|
||||
(declare-command
|
||||
"set" (key &rest value)
|
||||
(let* (
|
||||
key-symbol (symbol (+ "red/" key))
|
||||
expression (string/join value)
|
||||
eval-result (if (nil? value) '(ok #t) (read-eval expression))
|
||||
)
|
||||
(cond
|
||||
((result/ok? eval-result) (set key-symbol (cadr eval-result)))
|
||||
(&otherwise (red/message (+ "error: " (cadr eval-result))))
|
||||
)
|
||||
)
|
||||
)
|
||||
(declare-command
|
||||
"unset" (key)
|
||||
(let (key-symbol (symbol (+ "red/" key)))
|
||||
(set key-symbol nil)
|
||||
)
|
||||
)
|
||||
(declare-command
|
||||
"eval" (&rest expressions)
|
||||
(let* (
|
||||
expression (string/join expressions)
|
||||
eval-result (read-eval expression)
|
||||
)
|
||||
(cond
|
||||
((result/ok? eval-result) (red/status (+ "ok: " (cadr eval-result))))
|
||||
(&otherwise (red/message (+ "error: " (cadr eval-result))))
|
||||
)
|
||||
)
|
||||
)
|
||||
|
||||
@@ -9,16 +9,39 @@
|
||||
)
|
||||
|
||||
(defmacro declare-command
|
||||
(command args body-head &rest body-tail)
|
||||
;; TODO auto-rest?
|
||||
`(setq
|
||||
_red/command-table
|
||||
(cons
|
||||
(list ,command (lambda (,@args &rest _) ,body-head ,@body-tail))
|
||||
_red/command-table
|
||||
)
|
||||
(command args &rest body)
|
||||
(unless (cons? body)
|
||||
(error "No body provided in declare-command"))
|
||||
(unless (list? args)
|
||||
(error "Argument list must either be a NIL or a list"))
|
||||
(let*
|
||||
(
|
||||
args-has-rest (find (lambda (x) (= x '&rest)) args)
|
||||
lambda-args (if args-has-rest
|
||||
`(,@args)
|
||||
`(,@args &rest _)
|
||||
)
|
||||
)
|
||||
`(setq
|
||||
_red/command-table
|
||||
(cons
|
||||
(list ,command (lambda ,lambda-args ,@body))
|
||||
_red/command-table
|
||||
)
|
||||
)
|
||||
)
|
||||
)
|
||||
;; (defmacro declare-command
|
||||
;; (command args body-head &rest body-tail)
|
||||
;; ;; TODO auto-rest?
|
||||
;; `(setq
|
||||
;; _red/command-table
|
||||
;; (cons
|
||||
;; (list ,command (lambda (,@args &rest _) ,body-head ,@body-tail))
|
||||
;; _red/command-table
|
||||
;; )
|
||||
;; )
|
||||
;; )
|
||||
|
||||
(defun try-import (path)
|
||||
(when (fs/file? path) (import path) #t))
|
||||
|
||||
@@ -6,7 +6,7 @@ use std::{
|
||||
cell::RefCell,
|
||||
env,
|
||||
io::{self, IsTerminal},
|
||||
path::Path,
|
||||
path::{Path, PathBuf},
|
||||
rc::Rc,
|
||||
};
|
||||
|
||||
@@ -20,7 +20,7 @@ use lysp::{
|
||||
Value,
|
||||
env::{Environment, EnvironmentAccessHook},
|
||||
machine::Machine,
|
||||
value::{IdentifierValue, convert::AnyFunction},
|
||||
value::{IdentifierValue, StringValue, convert::AnyFunction},
|
||||
},
|
||||
};
|
||||
|
||||
@@ -148,15 +148,22 @@ impl Editor {
|
||||
pub fn run(&mut self) -> Result<(), Error> {
|
||||
script::setup_env(self);
|
||||
|
||||
#[cfg(any(feature = "runtime", rust_analyzer))]
|
||||
let runtime_directory: PathBuf;
|
||||
#[cfg(feature = "runtime")]
|
||||
{
|
||||
self.evaluate_file("runtime/core.lysp");
|
||||
runtime_directory = PathBuf::from("runtime").canonicalize().unwrap();
|
||||
}
|
||||
#[cfg(any(not(feature = "runtime"), rust_analyzer))]
|
||||
{
|
||||
self.evaluate_file("/usr/share/red/runtime/core.lysp");
|
||||
runtime_directory = "/usr/share/red/runtime".into();
|
||||
}
|
||||
|
||||
self.env.set_global_value(
|
||||
"red/runtime-directory",
|
||||
StringValue::from(format!("{}", runtime_directory.display())),
|
||||
);
|
||||
self.evaluate_file(runtime_directory.join("core.lysp"));
|
||||
|
||||
loop {
|
||||
let exited = self.state.borrow().exited();
|
||||
|
||||
|
||||
@@ -80,6 +80,17 @@ fn editor_api(editor: &mut Editor) {
|
||||
state.borrow_mut().set_message(message);
|
||||
Ok(Value::Nil)
|
||||
});
|
||||
editor.defun("red/status", |_, _, state, args| {
|
||||
let mut message = String::new();
|
||||
for (i, arg) in args.iter().enumerate() {
|
||||
if i != 0 {
|
||||
message.push(' ');
|
||||
}
|
||||
message.push_str(&format!("{arg}"));
|
||||
}
|
||||
state.borrow_mut().set_status(message);
|
||||
Ok(Value::Nil)
|
||||
});
|
||||
}
|
||||
|
||||
fn syntax_api(editor: &mut Editor) {
|
||||
|
||||
Reference in New Issue
Block a user