better organization and try a tex readme

Signed-off-by: Ava Hahn <ava@aidanis.online>
This commit is contained in:
Ava Hahn 2023-03-01 11:21:30 -08:00
parent a489cb85c9
commit 38b941b6cb
Signed by untrusted user who does not match committer: affine
GPG key ID: 3A4645B8CF806069
23 changed files with 273 additions and 0 deletions

View file

@ -0,0 +1,255 @@
/* relish: versatile lisp shell
* Copyright (C) 2021 Aidan Hahn
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
use crate::eval::eval;
use crate::segment::{Seg, Ctr, Type};
use crate::vars::{VAR_TABLE, VTable, LIB_EXPORT};
use std::collections::HashMap;
use std::convert::TryInto;
use lazy_static::lazy_static;
lazy_static! {
pub static ref FUNC_TABLE: FTable<'static> = {
let mut tab = FTable::new();
tab.declare(LIB_EXPORT);
tab
};
}
pub struct FTable<'a> (HashMap<String, &'a Function>);
impl FTable<'_> {
pub fn declare(&mut self, func: Box<Function>) -> Option<String> {
if let Operation::External(ref fun) = &func.function {
if let Args::Lazy(ref i) = func.args {
if fun.arg_syms.len() != i.clone().try_into().unwrap() {
return Some(
"external function must have lazy args equal to declared arg_syms length"
.to_string(),
);
}
} else {
return Some("external function must have lazy args".to_string());
}
}
self.0.insert(func.name, func);
None
}
pub fn get(&self, id: String) -> Option<&Box<Function>> {
self.0.get(&id)
}
pub fn remove(&mut self, id: String) {
self.0.remove(&id);
}
pub fn new() -> FTable<'static> {
FTable{0: HashMap::<String, Box<Function>>::new()}
}
}
// Standardized function signature for stdlib functions
//pub type InternalOperation = impl Fn(&Seg, &mut VTable, &mut FTable) -> Ctr;
#[derive(Debug)]
pub struct ExternalOperation<'a> {
// Un-evaluated abstract syntax tree
// TODO: Intermediate evaluation to simplify branches with no argument in them
// Simplified branches must not have side effects.
// TODO: Apply Memoization?
pub ast: Box<Seg<'a>>,
// list of argument string tokens
pub arg_syms: Vec<String>,
}
/* A stored function may either be a pointer to a function
* or a syntax tree to eval with the arguments
*/
pub enum Operation<'a> {
Internal(Box<dyn Fn(&'a Seg) -> Ctr<'a>>),
External(ExternalOperation<'a>),
}
/* Function Args
* If Lazy, is an integer denoting number of args
* If Strict, is a list of type tags denoting argument type.
*/
pub enum Args {
// signed: -1 denotes infinite args
Lazy(i128),
Strict(Vec<Type>),
}
impl Args {
fn validate_inputs(&self, args: &Seg) -> Result<(), String> {
match self {
Args::Lazy(ref num) => {
let called_arg_count = args.len() as i128;
if *num == 0 {
if let Ctr::None = *args.car {
//pass
} else {
return Err("Expected 0 args. Got one or more.".to_string());
}
} else if *num > -1 && (*num != called_arg_count) {
return Err(format!(
"Expected {} args. Got {}.",
num, called_arg_count
));
}
}
Args::Strict(ref arg_types) => {
let mut idx: usize = 0;
let passes = args.circuit(&mut |c: &Ctr| -> bool {
if idx >= arg_types.len() {
return false;
}
if let Ctr::None = c {
return false;
}
if arg_types[idx] == c.to_type() {
idx += 1;
return true;
}
return false;
});
if passes && idx < (arg_types.len() - 1) {
return Err(format!(
"{} too few arguments",
arg_types.len() - (idx + 1)
));
}
if !passes {
if idx < (arg_types.len() - 1) {
return Err(format!(
"argument {} is of wrong type (expected {})",
idx + 1,
arg_types[idx].to_string()
));
}
if idx == (arg_types.len() - 1) {
return Err("too many arguments".to_string());
}
}
}
}
Ok(())
}
}
pub struct Function {
pub function: Operation,
pub name: String,
pub args: Args,
// dont fail on undefined symbol (passed to eval)
pub loose_syms: bool,
// dont evaluate args at all. leave that to the function
pub eval_lazy: bool,
}
impl<'b, 'c> Function {
/* call
* routine is called by eval when a function call is detected
*/
pub fn func_call(
&self,
args: &'b Seg<'b>,
) -> Result<Box<Ctr<'c>>, String> {
// put args in simplest desired form
let evaluated_args;
match eval(args, self.loose_syms, self.eval_lazy) {
Ok(arg_data) => {
if let Ctr::Seg(ast) = *arg_data {
evaluated_args = &ast;
} else {
return Err("Panicking: eval returned not a list for function args.".to_string());
}
}
Err(s) => {
return Err(format!(
"error evaluating args to {}: {}",
self.name, s
))
}
}
if let Err(msg) = self.args.validate_inputs(evaluated_args) {
return Err(format!("failure to call {}: {}", self.name, msg));
}
/* corecursive with eval.
* essentially calls eval on each body in the function.
* result of the final body is returned.
*/
match &self.function {
Operation::Internal(ref f) => return Ok(Box::new(f(evaluated_args))),
Operation::External(ref f) => {
let mut holding_table = VTable::new();
// Prep var table for function execution
for n in 0..f.arg_syms.len() {
let iter_arg = evaluated_args[n];
if let Some(val) = VAR_TABLE.get(f.arg_syms[n]) {
holding_table.insert(f.arg_syms[n].clone(), val);
}
VAR_TABLE.insert(f.arg_syms[n].clone(), Box::new(iter_arg));
}
// execute function
let mut result: Box<Ctr>;
let iterate = &*(f.ast);
loop {
if let Ctr::Seg(ref data) = *iterate.car {
match eval(data, self.loose_syms, true) {
Ok(ctr) => result = ctr,
Err(e) => return Err(e),
}
} else {
panic!("function body not in standard form!")
}
match *iterate.cdr {
Ctr::Seg(ref next) => iterate = next,
Ctr::None => break,
_ => panic!("function body not in standard form!"),
}
}
// clear local vars and restore previous values
for n in 0..f.arg_syms.len() {
VAR_TABLE.remove(f.arg_syms[n]);
if let Some(val) = holding_table.get(f.arg_syms[n]) {
VAR_TABLE.insert(f.arg_syms[n].clone(), val);
}
}
return Ok(result);
}
}
}
}

View file

@ -0,0 +1,120 @@
/* relish: versatile lisp shell
* Copyright (C) 2021 Aidan Hahn
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
use crate::eval::eval;
use crate::func::{Args, Function, Operation};
use crate::segment::{Seg, Ctr};
use std::collections::HashMap;
use std::env;
use lazy_static::lazy_static;
lazy_static! {
pub static ref VAR_TABLE: VTable<'static> = {
VTable::new()
};
}
/* Mapping between a string token and a tree of Segments
* The string token can be found in any Ctr::Symbol value
* it is expected that the trees stored are already evaluated
*/
pub struct VTable<'a>(HashMap<String, Box<Ctr<'a>>>);
impl<'a, 'b> VTable<'a> {
// WARNING: make sure var_tree is properly evaluated before storing
pub fn insert(&'a mut self, identifier: String, data: Box<Ctr<'a>>) {
if let Some(datum) = self.0.insert(identifier, data) {
drop(datum);
}
}
pub fn get(&'a self, id: String) -> Option<Box<Ctr<'b>>> {
match self.0.get(&id) {
Some(s) => Some(s.clone()),
None => None,
}
}
pub fn remove(&mut self, id: String) {
self.0.remove(&id);
}
pub fn new() -> VTable<'a> {
VTable{0: HashMap::<String, Box<Ctr>>::new()}
}
}
// the stdlib var export function with env_sync on
lazy_static! {
pub static ref LIB_EXPORT_ENV: Function = Function {
name: String::from("export"),
loose_syms: true,
eval_lazy: true,
args: Args::Lazy(2),
function: Operation::Internal(Box::new( move |ast: &Seg| -> Ctr {
_export_callback(ast, true)
},
)),
};
}
// the stdlib var export function with env_sync off
lazy_static! {
pub static ref LIB_EXPORT_NO_ENV: Function = Function {
name: String::from("export"),
loose_syms: true,
eval_lazy: true,
args: Args::Lazy(2),
function: Operation::Internal(Box::new( move |ast: &Seg| -> Ctr {
_export_callback(ast, false)
},
)),
};
}
fn _export_callback<'a> (ast: &Seg, env_cfg: bool) -> Ctr<'a> {
if let Ctr::Symbol(ref identifier) = *ast.car {
match &*ast.cdr {
Ctr::Seg(data_tree) => match eval(&Box::new(data_tree), false, true) {
Ok(seg) => match *seg {
Ctr::Seg(val) => {
SYM_TABLE.declare(Symbol {
value: UserVar(val.car),
name: identifier.clone(),
});
if env_cfg {
env::set_var(identifier.clone(), val.car.to_string())
}
},
_ => eprintln!("impossible args to export"),
},
Err(e) => eprintln!("couldnt eval symbol: {}", e),
},
Ctr::None => {
VAR_TABLE.remove(identifier.to_string());
if env_cfg {
env::remove_var(identifier.to_string());
}
},
_ => eprintln!("args not in standard form"),
}
} else {
eprintln!("first argument to export must be a symbol");
}
return Ctr::None;
}