diff --git a/Readme.org b/Readme.org index 1e46b2b..1596c93 100644 --- a/Readme.org +++ b/Readme.org @@ -101,16 +101,16 @@ This contains any executable target of this project. Notably the main shell file * Current Status / TODO list Note: this section will not show the status of each item unless you are viewing it with a proper orgmode viewer *** TODO Rudimentary Control Flow -**** TODO if clause +**** DONE if clause **** TODO loop clause **** TODO while clause **** TODO circuit clause *** TODO Configuration -**** TODO get_stdlibphase1 -> configuration -> get_stdlibphase2 -**** TODO Function to load configuration into Variable and Function tables -**** TODO Configure in main shell -**** TODO manual verification of config settings -**** TODO manual verification of config defaults +**** DONE get_stdlibphase1 -> configuration -> get_stdlibphase2 +**** DONE Function to load configuration into Variable and Function tables +**** DONE Configure in main shell +**** DONE manual verification of config settings +**** DONE manual verification of config defaults *** TODO Help function *** TODO Env function *** TODO User variable declaration tests @@ -122,26 +122,30 @@ Will need a concatenate function for func tables *** TODO Main shell calls Load function on arg and exits *** TODO Custom error printing *** TODO Custom ast pretty print -*** TODO STDLIB *** TODO Shell module **** TODO Process launching with environment variables **** TODO Foreground process TTY **** TODO Background processes -**** TODO append -**** TODO string operations -***** TODO concatenate -***** TODO substr by index -***** TODO tokenize by delimiter -***** TODO sprintf / string build -**** TODO arithmetic operations -***** TODO -**** TODO Serialize/Deserialize basic data types -**** TODO file opterations -***** TODO -**** TODO Network library -***** TODO HTTP Client -***** TODO TCP Stream client -***** TODO UDP Client -***** TODO TCP Listener -***** TODO HTTP Listener -***** TODO UDP Listener +**** DONE append +*** TODO string operations +**** TODO concatenate +**** TODO substr by index +**** TODO tokenize by delimiter +**** TODO sprintf / string build +*** TODO arithmetic operations +**** TODO add +**** TODO sub +**** TODO div +**** TODO mul +**** TODO exp +**** TODO mod +*** TODO file operations +**** TODO read-to-string +**** TODO write-to-file +*** TODO Network library +**** TODO HTTP Client +**** TODO TCP Stream client +**** TODO UDP Client +**** TODO TCP Listener +**** TODO HTTP Listener +**** TODO UDP Listener diff --git a/src/bin/main.rs b/src/bin/main.rs new file mode 100644 index 0000000..e16bdf8 --- /dev/null +++ b/src/bin/main.rs @@ -0,0 +1,90 @@ +/* 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 . + */ + +use dirs::home_dir; +use relish::ast::{eval, lex, Ctr, Seg, SymTable}; +use relish::stdlib::{static_stdlib, dynamic_stdlib}; +use relish::aux::configure; +use rustyline::error::ReadlineError; +use rustyline::Editor; +use std::env; + +fn main() { + let mut rl = Editor::<()>::new(); + + const HIST_FILE: &str = "/.relish_hist"; + const CONFIG_FILE_DEFAULT: &str = "/.relishrc"; + + let home_dir = home_dir().unwrap().to_str().unwrap().to_owned(); + let hist_file_name = home_dir.clone() + HIST_FILE; + let cfg_file_name = home_dir + CONFIG_FILE_DEFAULT; + + if !hist_file_name.is_empty() { + rl.load_history(&hist_file_name) + .unwrap_or_else(|err: ReadlineError| eprintln!("{}", err)); + } + + let mut syms = SymTable::new(); + static_stdlib(&mut syms) + .unwrap_or_else(|err: String| eprintln!("{}", err)); + dynamic_stdlib(&mut syms) + .unwrap_or_else(|err: String| eprintln!("{}", err)); + { // scope the below borrow of syms + let cfg_file = env::var("RELISH_CFG_FILE").unwrap_or(cfg_file_name); + configure( + cfg_file.clone(), + &mut syms, + ).unwrap_or_else(|err: String| eprintln!("failed to load script {}\n{}", + cfg_file, err)); + } + dynamic_stdlib(&mut syms) + .unwrap_or_else(|err: String| eprintln!("{}", err)); + + loop { + let s = *syms.call_symbol(&"CFG_RELISH_PROMPT".to_string(), &Seg::new(), true) + .unwrap_or_else(|err: String| { + eprintln!("{}", err); + Box::new(Ctr::String("".to_string())) + }); + let readline_prompt = s.to_string(); + + let user_doc = rl.readline(&readline_prompt); + match user_doc { + Ok(line) => { + rl.add_history_entry(line.as_str()); + let l = line.as_str().to_owned(); + match lex(&l) { + Ok(a) => match eval(&a, &mut syms) { + Ok(a) => println!("{}", a), + Err(s) => println!("{}", s), + }, + Err(s) => println!("{}", s), + } + } + + Err(ReadlineError::Interrupted) => break, + Err(ReadlineError::Eof) => return, + Err(err) => { + eprintln!("Prompt error: {:?}", err); + break; + } + } + } + if !hist_file_name.is_empty() { + rl.save_history(&hist_file_name).unwrap(); + } +} diff --git a/src/config.rs b/src/config.rs index b0af11d..02fc6aa 100644 --- a/src/config.rs +++ b/src/config.rs @@ -16,78 +16,44 @@ */ use crate::eval::eval; -use crate::func::{func_declare, Args, FTable, Function, Operation}; use crate::lex::lex; -use crate::segment::{Ast, Ctr}; -use crate::stl::get_stdlib; -use crate::vars::{define, VTable}; -use std::cell::RefCell; +use crate::segment::{Seg, Ctr}; +use crate::sym::{SymTable, ValueType, Args, Symbol}; use std::fs; -use std::io::{self, Write}; +use std::io; use std::rc::Rc; -fn prompt_default_callback(_: Ast, _: Rc>, _: Rc>) -> Ctr { - return Ctr::String("λ ".to_string()); +fn prompt_default_callback(_: &Seg, _: &mut SymTable) -> Result { + Ok(Ctr::String("λ ".to_string())) } -pub fn configure(filename: String, vars: Rc>) -> Result>, String> { - let funcs; +/* loads defaults, evaluates config script */ +pub fn configure(filename: String, syms: &mut SymTable) -> Result<(), String> { + syms.insert("CFG_RELISH_POSIX".to_string(), Symbol { + name: String::from("CFG_RELISH_POSIX"), + args: Args::None, + conditional_branches: false, + value: ValueType::VarForm(Box::new(Ctr::String("0".to_string()))), + }); - define( - vars.clone(), - String::from("CFG_RELISH_POSIX"), - Rc::new(Ctr::String(String::from("0"))), - ); - define( - vars.clone(), - String::from("CFG_RELISH_ENV"), - Rc::new(Ctr::String(String::from("1"))), - ); + syms.insert("CFG_RELISH_ENV".to_string(), Symbol { + name: String::from("CFG_RELISH_ENV"), + args: Args::None, + conditional_branches: false, + value: ValueType::VarForm(Box::new(Ctr::String("1".to_string()))), + }); - match get_stdlib(vars.clone()) { - Ok(f) => funcs = f, - Err(s) => { - funcs = Rc::new(RefCell::new(FTable::new())); - println!("Couldnt get stdlib: {}", s) - }, - } + syms.insert("CFG_RELISH_PROMPT".to_string(), Symbol { + name: String::from("default relish prompt"), + args: Args::None, + conditional_branches: false, + value: ValueType::Internal(Rc::new(prompt_default_callback)), + }); - match func_declare( - funcs.clone(), - Rc::new(RefCell::new(Function { - name: String::from("CFG_RELISH_PROMPT"), - loose_syms: false, - eval_lazy: false, - args: Args::Lazy(0), - function: Operation::Internal(Box::new(prompt_default_callback)), - })), - ) { - Some(e) => return Err(e), - None => {}, - } + let config_document = fs::read_to_string(filename).unwrap_or_else(|err: io::Error| err.to_string()); + let config_tree = lex(&config_document)?; + let config_result = eval(&config_tree, syms)?; - match fs::read_to_string(filename.clone()) { - Err(s) => { - return Err(format!("Couldnt open configuration file: {}", s)); - } - - Ok(raw_config) => { - let mut l = raw_config; - l = "(".to_owned() + &l + ")"; - - match lex(l) { - Err(s) => { - return Err(format!("Error in configuration: {}", s)); - } - - Ok(config) => { - if let Err(errst) = eval(config, vars, funcs.clone(), false) { - return Err(format!("Error loading {}: {}", filename.clone(), errst)); - } - } - } - }, - } - - return Ok(funcs); + println!("config result: {config_result}"); + Ok(()) } diff --git a/src/lex.rs b/src/lex.rs index a8d4bf7..af2f7ea 100644 --- a/src/lex.rs +++ b/src/lex.rs @@ -200,26 +200,24 @@ fn process(document: &String) -> Result, String> { if is_str { Err(UNMATCHED_STR_DELIM.to_string()) - } else { - if ref_stack.is_empty() { - let obj; - if token == "true" { - obj = Box::from(Ctr::Bool(true)); - } else if token == "false" { - obj = Box::from(Ctr::Bool(false)); - } else if let Ok(i) = token.parse::() { - obj = Box::from(Ctr::Integer(i)); - } else if let Ok(f) = token.parse::() { - obj = Box::from(Ctr::Float(f)); - } else if let Some(s) = tok_is_symbol(&token) { - obj = Box::from(Ctr::Symbol(s)); - } else { - return Err(format!("Unparsable token: {}", token)); - } - Ok(Box::new(Seg::from_mono(obj))) + } else if ref_stack.is_empty() { + let obj; + if token == "true" { + obj = Box::from(Ctr::Bool(true)); + } else if token == "false" { + obj = Box::from(Ctr::Bool(false)); + } else if let Ok(i) = token.parse::() { + obj = Box::from(Ctr::Integer(i)); + } else if let Ok(f) = token.parse::() { + obj = Box::from(Ctr::Float(f)); + } else if let Some(s) = tok_is_symbol(&token) { + obj = Box::from(Ctr::Symbol(s)); } else { - Err(UNMATCHED_LIST_DELIM.to_string()) + return Err(format!("Unparsable token: {}", token)); } + Ok(Box::new(Seg::from_mono(obj))) + } else { + Err(UNMATCHED_LIST_DELIM.to_string()) } } diff --git a/src/lib.rs b/src/lib.rs index 4dfaebe..4047f99 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -16,7 +16,7 @@ */ -//mod config; +mod config; mod eval; mod lex; mod segment; @@ -37,6 +37,6 @@ pub mod stdlib { pub use crate::stl::{static_stdlib, dynamic_stdlib}; } -/*pub mod aux { +pub mod aux { pub use crate::config::configure; -}*/ +} diff --git a/src/stl.rs b/src/stl.rs index 7f3290e..09930ba 100644 --- a/src/stl.rs +++ b/src/stl.rs @@ -56,13 +56,20 @@ pub fn static_stdlib(syms: &mut SymTable) -> Result<(), String> { /// dynamic_stdlib /// takes configuration data and uses it to insert dynamic /// callbacks with configuration into a symtable -pub fn dynamic_stdlib(env: bool, syms: &mut SymTable) -> Result<(), String> { +pub fn dynamic_stdlib(syms: &mut SymTable) -> Result<(), String> { + //get CFG_RELISH_ENV from syms + let env_cfg_user_form = syms.call_symbol( + &"CFG_RELISH_ENV".to_string(), &Seg::new(), true) + .unwrap_or_else(|_: String| Box::new(Ctr::None)) + .to_string() + .ne(""); + syms.insert("def".to_string(), Symbol { name: String::from("define"), args: Args::Infinite, conditional_branches: true, value: ValueType::Internal(Rc::new( move |ast: &Seg, syms: &mut SymTable| -> Result { - _store_callback(ast, syms, env) + _store_callback(ast, syms, env_cfg_user_form) }, )), }); diff --git a/tests/test_lib_append.rs b/tests/test_lib_append.rs index 24f7591..5260fb0 100644 --- a/tests/test_lib_append.rs +++ b/tests/test_lib_append.rs @@ -9,7 +9,7 @@ mod append_lib_tests { let mut syms = SymTable::new(); static_stdlib(&mut syms).unwrap(); - dynamic_stdlib(false, &mut syms).unwrap(); + dynamic_stdlib(&mut syms).unwrap(); if let Ok(tree) = lex(&document.to_string()) { if let Ctr::Seg(ref s) = *eval(&tree, &mut syms).unwrap() { @@ -27,7 +27,7 @@ mod append_lib_tests { let mut syms = SymTable::new(); static_stdlib(&mut syms).unwrap(); - dynamic_stdlib(false, &mut syms).unwrap(); + dynamic_stdlib(&mut syms).unwrap(); if let Ok(tree) = lex(&document.to_string()) { if let Ctr::Seg(ref s) = *eval(&tree, &mut syms).unwrap() { @@ -45,7 +45,7 @@ mod append_lib_tests { let mut syms = SymTable::new(); static_stdlib(&mut syms).unwrap(); - dynamic_stdlib(false, &mut syms).unwrap(); + dynamic_stdlib(&mut syms).unwrap(); if let Ok(tree) = lex(&document.to_string()) { if let Ctr::Seg(ref s) = *eval(&tree, &mut syms).unwrap() { @@ -63,7 +63,7 @@ mod append_lib_tests { let mut syms = SymTable::new(); static_stdlib(&mut syms).unwrap(); - dynamic_stdlib(false, &mut syms).unwrap(); + dynamic_stdlib(&mut syms).unwrap(); if let Ok(tree) = lex(&document.to_string()) { if let Ctr::Seg(ref s) = *eval(&tree, &mut syms).unwrap() { @@ -81,7 +81,7 @@ mod append_lib_tests { let mut syms = SymTable::new(); static_stdlib(&mut syms).unwrap(); - dynamic_stdlib(false, &mut syms).unwrap(); + dynamic_stdlib(&mut syms).unwrap(); if let Ok(tree) = lex(&document.to_string()) { if let Ctr::Seg(ref s) = *eval(&tree, &mut syms).unwrap() { diff --git a/tests/test_lib_control.rs b/tests/test_lib_control.rs index 7a1a4c1..9212b91 100644 --- a/tests/test_lib_control.rs +++ b/tests/test_lib_control.rs @@ -9,7 +9,7 @@ mod control_lib_tests { let mut syms = SymTable::new(); static_stdlib(&mut syms).unwrap(); - dynamic_stdlib(false, &mut syms).unwrap(); + dynamic_stdlib(&mut syms).unwrap(); if let Ok(tree) = lex(&document.to_string()) { if let Ctr::Integer(i) = *eval(&tree, &mut syms).unwrap() { @@ -29,7 +29,7 @@ mod control_lib_tests { let mut syms = SymTable::new(); static_stdlib(&mut syms).unwrap(); - dynamic_stdlib(false, &mut syms).unwrap(); + dynamic_stdlib(&mut syms).unwrap(); if let Ok(tree) = lex(&document.to_string()) { if let Ctr::Integer(i) = *eval(&tree, &mut syms).unwrap() { @@ -50,7 +50,7 @@ mod control_lib_tests { let mut syms = SymTable::new(); static_stdlib(&mut syms).unwrap(); - dynamic_stdlib(false, &mut syms).unwrap(); + dynamic_stdlib(&mut syms).unwrap(); if let Ok(tree) = lex(&document.to_string()) { if let Ctr::Seg(ref i) = *eval(&tree, &mut syms).unwrap() { diff --git a/tests/test_vars.rs b/tests/test_vars.rs index ce4041f..7d2f95f 100644 --- a/tests/test_vars.rs +++ b/tests/test_vars.rs @@ -10,7 +10,7 @@ mod var_lib_tests { let mut syms = SymTable::new(); static_stdlib(&mut syms).unwrap(); - dynamic_stdlib(false, &mut syms).unwrap(); + dynamic_stdlib(&mut syms).unwrap(); if let Ok(tree) = lex(&doc1.to_string()) { let eval_result = *eval(&tree, &mut syms).unwrap(); @@ -49,7 +49,7 @@ mod var_lib_tests { let mut syms = SymTable::new(); static_stdlib(&mut syms).unwrap(); - dynamic_stdlib(false, &mut syms).unwrap(); + dynamic_stdlib(&mut syms).unwrap(); if let Ok(tree) = lex(&doc1.to_string()) { let eval_result = *eval(&tree, &mut syms).unwrap(); @@ -101,7 +101,7 @@ mod var_lib_tests { let mut syms = SymTable::new(); static_stdlib(&mut syms).unwrap(); - dynamic_stdlib(false, &mut syms).unwrap(); + dynamic_stdlib(&mut syms).unwrap(); if let Ok(tree) = lex(&doc1.to_string()) { let eval_result = *eval(&tree, &mut syms).unwrap();