552 lines
18 KiB
Rust
552 lines
18 KiB
Rust
use std::fs::File;
|
|
use std::io::{self, Read, StdoutLock, Write, stdout};
|
|
use std::rc::Rc;
|
|
use std::{env, f64};
|
|
|
|
use libopenbirch::environment::{Environment, EnvironmentInternalSymbolKey};
|
|
use libopenbirch::node::call::Call;
|
|
use libopenbirch::node::constant::Constant;
|
|
use libopenbirch::node::empty::Empty;
|
|
use libopenbirch::node::set::Set;
|
|
use libopenbirch::node::string_node::StringNode;
|
|
use libopenbirch::node::{Error, Node, NodeEnum};
|
|
use libopenbirch::parser::{Lexer, LexerError, Parser, ParserError};
|
|
use termion::color;
|
|
use termion::event::Key;
|
|
use termion::input::TermReadEventsAndRaw;
|
|
use termion::raw::{IntoRawMode, RawTerminal};
|
|
|
|
pub struct Input {
|
|
#[cfg(not(feature = "async"))]
|
|
stdin: std::io::Stdin,
|
|
|
|
stdout: RawTerminal<StdoutLock<'static>>,
|
|
buffer: String,
|
|
current_char: usize,
|
|
|
|
history: Vec<String>,
|
|
history_idx: Option<usize>,
|
|
}
|
|
impl Input {
|
|
pub fn new() -> Self {
|
|
#[cfg(not(feature = "async"))]
|
|
let stdin = std::io::stdin();
|
|
let stdout = stdout().lock().into_raw_mode().unwrap();
|
|
|
|
Self {
|
|
stdin,
|
|
stdout,
|
|
buffer: "".into(),
|
|
current_char: 0,
|
|
|
|
history: vec![],
|
|
history_idx: None,
|
|
}
|
|
}
|
|
|
|
#[inline]
|
|
fn draw_line(
|
|
buffer: &String,
|
|
stdout: &mut RawTerminal<StdoutLock<'static>>,
|
|
current_char: usize,
|
|
) {
|
|
print!("\r{}> {}", termion::clear::CurrentLine, buffer,);
|
|
let left_diff = buffer.len() - current_char;
|
|
if left_diff > 0 {
|
|
print!("{}", termion::cursor::Left(left_diff.try_into().unwrap()));
|
|
}
|
|
let _ = stdout.flush();
|
|
}
|
|
|
|
/// Gets input from `io::stdin`.
|
|
/// Returns `None` when the user presses <C-c> or types `:quit`
|
|
pub fn get(&mut self) -> Result<Option<String>, io::Error> {
|
|
Self::draw_line(&self.buffer, &mut self.stdout, self.current_char);
|
|
loop {
|
|
for b in (&mut self.stdin).events_and_raw() {
|
|
let (event, _slice) = b?;
|
|
|
|
match event {
|
|
termion::event::Event::Key(key) => match key {
|
|
Key::Char(char) if char == '\n' => {
|
|
if &self.buffer == ":quit" {
|
|
println!("\r");
|
|
return Err(io::Error::from(io::ErrorKind::Interrupted));
|
|
}
|
|
|
|
if self.buffer.trim().len() > 0 {
|
|
self.history.insert(0, self.buffer.clone());
|
|
}
|
|
let r = self.buffer.clone();
|
|
self.buffer.clear();
|
|
self.current_char = 0;
|
|
self.history_idx = None;
|
|
print!("\n\r");
|
|
return Ok(Some(r));
|
|
}
|
|
Key::Backspace => {
|
|
if self.current_char == 0 {
|
|
continue;
|
|
}
|
|
self.current_char -= 1;
|
|
self.buffer.remove(self.current_char);
|
|
Self::draw_line(&self.buffer, &mut self.stdout, self.current_char);
|
|
let _ = self.stdout.flush();
|
|
}
|
|
Key::Delete => {
|
|
if self.current_char == self.buffer.len() {
|
|
continue;
|
|
}
|
|
self.buffer.remove(self.current_char);
|
|
Self::draw_line(&self.buffer, &mut self.stdout, self.current_char);
|
|
let _ = self.stdout.flush();
|
|
}
|
|
Key::Left => {
|
|
if self.current_char == 0 {
|
|
continue;
|
|
}
|
|
self.current_char -= 1;
|
|
print!("{}", termion::cursor::Left(1));
|
|
let _ = self.stdout.flush();
|
|
}
|
|
Key::Right => {
|
|
if self.current_char == self.buffer.len() {
|
|
continue;
|
|
}
|
|
self.current_char += 1;
|
|
print!("{}", termion::cursor::Right(1));
|
|
let _ = self.stdout.flush();
|
|
}
|
|
Key::Up => {
|
|
match self.history_idx {
|
|
Some(i) => self.history_idx = Some((i + 1) % self.history.len()),
|
|
None => {
|
|
if self.history.len() > 0 {
|
|
self.history_idx = Some(0)
|
|
} else {
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
|
|
self.buffer =
|
|
self.history.get(self.history_idx.unwrap()).unwrap().clone();
|
|
self.current_char = self.buffer.len();
|
|
Self::draw_line(&self.buffer, &mut self.stdout, self.current_char);
|
|
}
|
|
Key::Down => {
|
|
match self.history_idx {
|
|
Some(i) => {
|
|
let i = if i == 0 { self.history.len() } else { i };
|
|
self.history_idx = Some((i - 1) % self.history.len())
|
|
}
|
|
None => {
|
|
if self.history.len() > 0 {
|
|
self.history_idx = Some(self.history.len() - 1);
|
|
} else {
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
|
|
self.buffer =
|
|
self.history.get(self.history_idx.unwrap()).unwrap().clone();
|
|
self.current_char = self.buffer.len();
|
|
Self::draw_line(&self.buffer, &mut self.stdout, self.current_char);
|
|
}
|
|
Key::Home => self.current_char = 0,
|
|
Key::End => self.current_char = self.buffer.len(),
|
|
Key::Char(char) => {
|
|
self.buffer.insert(self.current_char, char);
|
|
self.current_char += 1;
|
|
Self::draw_line(&self.buffer, &mut self.stdout, self.current_char);
|
|
}
|
|
Key::Ctrl(c) if c == 'c' => {
|
|
println!("\r");
|
|
return Err(io::Error::from(io::ErrorKind::Interrupted));
|
|
}
|
|
_ => {
|
|
#[cfg(debug_assertions)]
|
|
return Err(io::Error::other(format!(
|
|
"Key {key:?} is not implemented"
|
|
)));
|
|
}
|
|
},
|
|
termion::event::Event::Mouse(_mouse_event) => {}
|
|
termion::event::Event::Unsupported(_items) => {}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
pub fn disable_raw(&mut self) -> io::Result<()> {
|
|
self.stdout.suspend_raw_mode()
|
|
}
|
|
|
|
pub fn enable_raw(&mut self) -> io::Result<()> {
|
|
self.stdout.activate_raw_mode()
|
|
}
|
|
}
|
|
|
|
fn print_err(i: usize, len: usize, exp: String) {
|
|
println!(
|
|
"\r{}{}{}",
|
|
" ".repeat(i + 3 - len),
|
|
color::Fg(color::Yellow),
|
|
"^".repeat(len)
|
|
);
|
|
println!(
|
|
"\r{}{}{}",
|
|
color::Fg(color::Red),
|
|
exp,
|
|
color::Fg(color::Reset)
|
|
);
|
|
}
|
|
|
|
fn print(args: &Vec<Rc<NodeEnum>>, env: &mut Environment) -> Result<Rc<NodeEnum>, Error> {
|
|
for expr in args {
|
|
println!("\r{}", expr.as_string(Some(env)));
|
|
}
|
|
|
|
Ok(Empty::new(""))
|
|
}
|
|
|
|
fn unset(args: &Vec<Rc<NodeEnum>>, env: &mut Environment) -> Result<Rc<NodeEnum>, Error> {
|
|
for arg in args {
|
|
if let NodeEnum::StringNode(string) = arg.as_ref() {
|
|
if let Some(id) = env.str_to_id(string.get_value()) {
|
|
env.undefine(*id);
|
|
}
|
|
} else {
|
|
return Err(Error::MismatchedType(format!(
|
|
"Expected strings, but one of the arguments was a {}",
|
|
arg.type_str()
|
|
)));
|
|
}
|
|
}
|
|
|
|
Ok(Empty::new(format!("Undefined {} symbols", args.len())))
|
|
}
|
|
|
|
fn get_float_precision(
|
|
args: &Vec<Rc<NodeEnum>>,
|
|
env: &mut Environment,
|
|
) -> Result<Rc<NodeEnum>, Error> {
|
|
match args.len() {
|
|
0 => Ok(Rc::new(
|
|
Constant::new_from_float(env.get_float_precision() as f64, &env).into(),
|
|
)),
|
|
1 => {
|
|
let arg = args.first().unwrap();
|
|
if let NodeEnum::Constant(constant) = arg.as_ref() {
|
|
let prec = constant.get_value().prec_64() as f64;
|
|
|
|
Ok(Rc::new(Constant::new_from_float(prec, &env).into()))
|
|
} else {
|
|
Err(Error::MismatchedType(format!(
|
|
"Expected argument of type Constant, but got {}",
|
|
arg.type_str()
|
|
)))
|
|
}
|
|
}
|
|
_ => Err(Error::Other(format!(
|
|
"Expected 0 or 1 arguments, got {}",
|
|
args.len()
|
|
)))?,
|
|
}
|
|
}
|
|
|
|
fn set_float_precision(
|
|
args: &Vec<Rc<NodeEnum>>,
|
|
env: &mut Environment,
|
|
) -> Result<Rc<NodeEnum>, Error> {
|
|
if args.len() != 1 {
|
|
Err(Error::ArgumentCount(1, args.len()))?
|
|
}
|
|
|
|
let arg = args.first().unwrap();
|
|
|
|
let precision: u64 = if let NodeEnum::Constant(value) = arg.as_ref() {
|
|
value.get_value().to_u32_saturating().unwrap().into()
|
|
} else {
|
|
Err(Error::MismatchedType(format!(
|
|
"Expected argument of type Constant, but got {}",
|
|
arg.type_str()
|
|
)))?
|
|
};
|
|
|
|
env.set_float_precision(precision);
|
|
|
|
Ok(Empty::new(format!("Set float precision to {precision}")))
|
|
}
|
|
|
|
fn map(args: &Vec<Rc<NodeEnum>>, env: &mut Environment) -> Result<Rc<NodeEnum>, Error> {
|
|
if args.len() != 2 {
|
|
Err(Error::ArgumentCount(2, args.len()))?
|
|
}
|
|
|
|
let arg = args.first().unwrap();
|
|
let func = match arg.as_ref() {
|
|
NodeEnum::Function(_) | NodeEnum::Closure(_) => arg,
|
|
_ => Err(Error::MismatchedType(format!(
|
|
"Argument 1 expected a Function but got {}",
|
|
arg.type_str()
|
|
)))?,
|
|
};
|
|
|
|
let arg = args.get(1).unwrap().as_ref();
|
|
let set = if let NodeEnum::Set(set) = arg {
|
|
set.get_values()
|
|
} else {
|
|
return Err(Error::MismatchedType(format!(
|
|
"Argument 1 expected a Set but got {}",
|
|
arg.type_str()
|
|
)))?;
|
|
};
|
|
|
|
let mut out = Vec::with_capacity(set.len());
|
|
|
|
for expr in set.iter() {
|
|
out.push(Call::new(func.clone(), vec![expr.clone()]).evaluate(env)?);
|
|
}
|
|
|
|
Ok(Set::new(out))
|
|
}
|
|
|
|
fn get(args: &Vec<Rc<NodeEnum>>, _env: &mut Environment) -> Result<Rc<NodeEnum>, Error> {
|
|
if args.len() != 2 {
|
|
Err(Error::ArgumentCount(2, args.len()))?
|
|
}
|
|
|
|
let set = if let NodeEnum::Set(set) = args.first().unwrap().as_ref() {
|
|
set.get_values()
|
|
} else {
|
|
return Err(Error::MismatchedType(format!(
|
|
"Argument 1 expected Set but got {}",
|
|
args.first().unwrap().type_str()
|
|
)));
|
|
};
|
|
|
|
let idx = if let NodeEnum::Constant(c) = args.get(1).unwrap().as_ref() {
|
|
if let Some(u) = c.get_value().to_u32_saturating() {
|
|
u as usize
|
|
} else {
|
|
return Err(Error::ConversionError(
|
|
"Error occured while trying to convert second argument to integer".to_owned(),
|
|
));
|
|
}
|
|
} else {
|
|
return Err(Error::MismatchedType(
|
|
"Argument 1 is expected to be a Set".to_owned(),
|
|
));
|
|
};
|
|
|
|
if let Some(v) = set.get(idx) {
|
|
return Ok(v.clone());
|
|
}
|
|
|
|
return Err(Error::IndexOutOfBounds(set.len(), idx));
|
|
}
|
|
|
|
fn length(args: &Vec<Rc<NodeEnum>>, env: &mut Environment) -> Result<Rc<NodeEnum>, Error> {
|
|
if args.len() != 1 {
|
|
Err(Error::ArgumentCount(1, args.len()))?
|
|
}
|
|
|
|
let set = if let NodeEnum::Set(set) = args.first().unwrap().as_ref() {
|
|
set.get_values()
|
|
} else {
|
|
return Err(Error::MismatchedType(
|
|
"Argument 1 is expected to be a Set".to_owned(),
|
|
));
|
|
};
|
|
|
|
Ok(Rc::new(
|
|
Constant::new_from_float(set.len() as f32, &env).into(),
|
|
))
|
|
}
|
|
|
|
fn benchmark(args: &Vec<Rc<NodeEnum>>, env: &mut Environment) -> Result<Rc<NodeEnum>, Error> {
|
|
if args.len() != 1 {
|
|
Err(Error::ArgumentCount(1, args.len()))?
|
|
}
|
|
|
|
use std::time::Instant;
|
|
let now = Instant::now();
|
|
|
|
let result = Call::new(args.first().unwrap().clone(), vec![]).evaluate(env)?;
|
|
|
|
let elapsed = now.elapsed();
|
|
|
|
let time_str = StringNode::new(elapsed.as_secs_f64().to_string()).into();
|
|
let time_const = Rc::new(Constant::new_from_float(elapsed.as_secs_f64(), &env).into());
|
|
|
|
Ok(Set::new(vec![time_str, time_const, result]))
|
|
}
|
|
|
|
fn head(args: &Vec<Rc<NodeEnum>>, _env: &mut Environment) -> Result<Rc<NodeEnum>, Error> {
|
|
if args.len() != 1 {
|
|
Err(Error::ArgumentCount(1, args.len()))?
|
|
}
|
|
|
|
let set = if let NodeEnum::Set(set) = args.first().unwrap().as_ref() {
|
|
set.get_values()
|
|
} else {
|
|
return Err(Error::MismatchedType(format!(
|
|
"Expected a Set but got a {}",
|
|
args.first().unwrap().type_str()
|
|
)));
|
|
};
|
|
|
|
let head = set.first().cloned().unwrap_or(Set::new(vec![]));
|
|
let len = set.len();
|
|
let tail = if len > 1 {
|
|
set[1..len].to_vec()
|
|
} else {
|
|
vec![]
|
|
};
|
|
|
|
Ok(Set::new(vec![head, Set::new(tail)]))
|
|
}
|
|
|
|
fn mult_equal(args: &Vec<Rc<NodeEnum>>, env: &mut Environment) -> Result<Rc<NodeEnum>, Error> {
|
|
let a1 = if let NodeEnum::Multiply(m) = args.get(0).ok_or(Error::ArgumentCount(1, 0))?.as_ref()
|
|
{
|
|
m
|
|
} else {
|
|
return Err(Error::MismatchedType(
|
|
"Argument 1 is not a multiply".to_owned(),
|
|
));
|
|
};
|
|
let a2 = if let NodeEnum::Multiply(m) = args.get(1).ok_or(Error::ArgumentCount(2, 1))?.as_ref()
|
|
{
|
|
m
|
|
} else {
|
|
return Err(Error::MismatchedType(
|
|
"Argument 2 is not a multiply".to_owned(),
|
|
));
|
|
};
|
|
|
|
Ok(Rc::new(NodeEnum::Bool(a1.symbols_match(a2).into())))
|
|
}
|
|
|
|
fn diff(args: &Vec<Rc<NodeEnum>>, _env: &mut Environment) -> Result<Rc<NodeEnum>, Error> {
|
|
if args.len() != 2 {
|
|
return Err(Error::ArgumentCount(2, args.len()));
|
|
}
|
|
|
|
let expr = args.first().unwrap().clone();
|
|
|
|
let var = if let NodeEnum::Symbol(symbol) = args.get(1).unwrap().as_ref() {
|
|
symbol.get_value()
|
|
} else {
|
|
return Err(Error::MismatchedType(format!(
|
|
"Argument 2 is expected to be a Symbol but got a {}",
|
|
args.get(1).unwrap().type_str()
|
|
)));
|
|
};
|
|
|
|
todo!()
|
|
}
|
|
|
|
fn diff_internally(
|
|
expr: Rc<NodeEnum>,
|
|
var: EnvironmentInternalSymbolKey,
|
|
env: &mut Environment,
|
|
) -> Result<Rc<NodeEnum>, String> {
|
|
todo!()
|
|
}
|
|
|
|
fn evaluate(source: &String, mut env: &mut Environment, verbose: bool) {
|
|
let mut lexer = Lexer::new(&source);
|
|
let tokens_result = lexer.lex();
|
|
|
|
if tokens_result.is_err() {
|
|
match tokens_result.err().unwrap() {
|
|
LexerError::UnexpectedChar(i, exp) => print_err(i, 1, exp),
|
|
}
|
|
return;
|
|
}
|
|
|
|
let tokens = tokens_result.unwrap();
|
|
let mut parser = Parser::new(tokens, &mut env);
|
|
|
|
let nodes = match parser.parse() {
|
|
Ok(nodes) => nodes,
|
|
Err(err) => {
|
|
match err {
|
|
ParserError::UnexpectedEndOfTokens(exp) => print_err(source.len(), 1, exp),
|
|
ParserError::UnexpectedToken(i, len, exp)
|
|
| ParserError::Unimplemented(i, len, exp) => print_err(i, len, exp),
|
|
ParserError::UnexpectedNode(i, exp) | ParserError::NumberParse(i, exp) => {
|
|
print_err(i, 1, exp)
|
|
}
|
|
}
|
|
return;
|
|
}
|
|
};
|
|
|
|
print!("{}", color::Fg(color::Blue));
|
|
|
|
for node in nodes {
|
|
let evaluated = node.evaluate(&mut env);
|
|
match evaluated {
|
|
Ok(result) if verbose => println!("\r\t{}", result.as_string(Some(&env))),
|
|
Err(exp) => print_err(0, 1, exp.to_string()),
|
|
_ => {}
|
|
}
|
|
}
|
|
}
|
|
|
|
fn main() -> Result<(), io::Error> {
|
|
let mut env = Environment::new();
|
|
|
|
env.set_float_precision(128);
|
|
|
|
env.define_native_function("print", print);
|
|
env.define_native_function("unset", unset);
|
|
env.define_native_function("get_float_precision", get_float_precision);
|
|
env.define_native_function("set_float_precision", set_float_precision);
|
|
env.define_native_function("map", map);
|
|
env.define_native_function("head", head);
|
|
env.define_native_function("get", get);
|
|
env.define_native_function("length", length);
|
|
env.define_native_function("benchmark", benchmark);
|
|
env.define_native_function("mult_equals", mult_equal);
|
|
|
|
env.define(
|
|
"pi",
|
|
Rc::new(Constant::new_from_float(std::f64::consts::PI, &env).into()),
|
|
);
|
|
|
|
let args: Vec<String> = std::env::args().collect();
|
|
|
|
if args.len() > 1 {
|
|
let file = args.get(1).unwrap();
|
|
|
|
let mut file = File::open(file)?;
|
|
let mut src = String::new();
|
|
let _ = file.read_to_string(&mut src);
|
|
|
|
evaluate(&src, &mut env, false);
|
|
|
|
return Ok(());
|
|
}
|
|
|
|
let mut input = Input::new();
|
|
|
|
while let Some(source) = input.get()? {
|
|
input.disable_raw()?;
|
|
|
|
evaluate(&source, &mut env, true);
|
|
|
|
input.enable_raw()?;
|
|
|
|
print!("{}", color::Fg(color::Reset));
|
|
|
|
// println!("\t{}{source}{}", termion::color::Fg(termion::color::Blue), termion::color::Fg(termion::color::Reset));
|
|
}
|
|
|
|
Ok(())
|
|
}
|