use crate::ast::{ArgSep, Expr, Value, VarType};
use crate::eval::{eval_all, Error};
use crate::exec::Machine;
use crate::syms::{
Array, CallError, CallableMetadata, CallableMetadataBuilder, Command, CommandResult, Function,
FunctionResult, Symbol, Symbols,
};
use async_trait::async_trait;
use std::cell::RefCell;
use std::collections::HashMap;
use std::rc::Rc;
pub struct ErrorFunction {
metadata: CallableMetadata,
}
impl ErrorFunction {
pub fn new() -> Rc<Self> {
Rc::from(Self {
metadata: CallableMetadataBuilder::new("ERROR", VarType::Boolean)
.with_syntax("arg1$")
.test_build(),
})
}
}
#[async_trait(?Send)]
impl Function for ErrorFunction {
fn metadata(&self) -> &CallableMetadata {
&self.metadata
}
async fn exec(&self, args: &[Expr], symbols: &mut Symbols) -> FunctionResult {
let args = eval_all(args, symbols).await?;
match args.as_slice() {
[Value::Text(s)] => {
if s == "argument" {
Err(CallError::ArgumentError("Bad argument".to_owned()))
} else if s == "eval" {
Err(Error::new("Some eval error").into())
} else if s == "internal" {
Err(CallError::InternalError("Some internal error".to_owned()))
} else if s == "syntax" {
Err(CallError::SyntaxError)
} else {
panic!("Unknown argument");
}
}
_ => panic!("Invalid arguments"),
}
}
}
pub struct ExitCommand {
metadata: CallableMetadata,
}
impl ExitCommand {
pub fn new() -> Rc<Self> {
Rc::from(Self {
metadata: CallableMetadataBuilder::new("EXIT", VarType::Void).test_build(),
})
}
}
#[async_trait(?Send)]
impl Command for ExitCommand {
fn metadata(&self) -> &CallableMetadata {
&self.metadata
}
async fn exec(&self, args: &[(Option<Expr>, ArgSep)], machine: &mut Machine) -> CommandResult {
let arg = match args {
[(Some(expr), ArgSep::End)] => match expr.eval(machine.get_mut_symbols()).await? {
Value::Integer(n) => {
assert!((0..128).contains(&n), "Exit code out of range");
n as u8
}
_ => panic!("Exit code must be a positive integer"),
},
_ => panic!("EXIT takes one argument"),
};
machine.exit(arg);
Ok(())
}
}
pub struct InCommand {
metadata: CallableMetadata,
data: Box<RefCell<dyn Iterator<Item = &'static &'static str>>>,
}
impl InCommand {
pub fn new(data: Box<RefCell<dyn Iterator<Item = &'static &'static str>>>) -> Rc<Self> {
Rc::from(Self {
metadata: CallableMetadataBuilder::new("IN", VarType::Void).test_build(),
data,
})
}
}
#[async_trait(?Send)]
impl Command for InCommand {
fn metadata(&self) -> &CallableMetadata {
&self.metadata
}
async fn exec(&self, args: &[(Option<Expr>, ArgSep)], machine: &mut Machine) -> CommandResult {
if args.len() != 1 {
return Err(CallError::SyntaxError);
}
if args[0].1 != ArgSep::End {
return Err(CallError::SyntaxError);
}
let vref = match &args[0].0 {
Some(Expr::Symbol(vref)) => vref,
_ => return Err(CallError::SyntaxError),
};
let mut data = self.data.borrow_mut();
let raw_value = data.next().unwrap().to_owned();
let value = Value::parse_as(vref.ref_type(), raw_value)?;
machine.get_mut_symbols().set_var(vref, value)?;
Ok(())
}
}
pub struct OutCommand {
metadata: CallableMetadata,
data: Rc<RefCell<Vec<String>>>,
}
impl OutCommand {
pub fn new(data: Rc<RefCell<Vec<String>>>) -> Rc<Self> {
Rc::from(Self {
metadata: CallableMetadataBuilder::new("OUT", VarType::Void).test_build(),
data,
})
}
}
#[async_trait(?Send)]
impl Command for OutCommand {
fn metadata(&self) -> &CallableMetadata {
&self.metadata
}
async fn exec(&self, args: &[(Option<Expr>, ArgSep)], machine: &mut Machine) -> CommandResult {
let mut text = String::new();
for arg in args.iter() {
if let Some(expr) = arg.0.as_ref() {
text += &expr.eval(machine.get_mut_symbols()).await?.to_string();
}
match arg.1 {
ArgSep::End => break,
ArgSep::Short => text += " ",
ArgSep::Long => return Err(CallError::SyntaxError),
}
}
self.data.borrow_mut().push(text);
Ok(())
}
}
pub struct SumFunction {
metadata: CallableMetadata,
}
impl SumFunction {
pub fn new() -> Rc<Self> {
Rc::from(Self {
metadata: CallableMetadataBuilder::new("SUM", VarType::Integer).test_build(),
})
}
}
#[async_trait(?Send)]
impl Function for SumFunction {
fn metadata(&self) -> &CallableMetadata {
&self.metadata
}
async fn exec(&self, args: &[Expr], symbols: &mut Symbols) -> FunctionResult {
let mut result = Value::Integer(0);
for a in args {
let value = a.eval(symbols).await?;
result = result.add(&value)?;
}
Ok(result)
}
}
#[derive(Default)]
pub struct SymbolsBuilder {
by_name: HashMap<String, Symbol>,
}
impl SymbolsBuilder {
pub fn add_array<S: Into<String>>(mut self, name: S, subtype: VarType) -> Self {
let name = name.into();
assert!(name == name.to_ascii_uppercase());
let array = Array::new(subtype, vec![10]);
self.by_name.insert(name, Symbol::Array(array));
self
}
pub fn add_command(mut self, cmd: Rc<dyn Command>) -> Self {
let name = cmd.metadata().name();
assert!(name == name.to_ascii_uppercase());
self.by_name.insert(name.to_owned(), Symbol::Command(cmd));
self
}
pub fn add_function(mut self, func: Rc<dyn Function>) -> Self {
let name = func.metadata().name();
assert!(name == name.to_ascii_uppercase());
self.by_name.insert(name.to_owned(), Symbol::Function(func));
self
}
pub fn add_var<S: Into<String>>(mut self, name: S, value: Value) -> Self {
let name = name.into();
assert!(name == name.to_ascii_uppercase());
self.by_name.insert(name, Symbol::Variable(value));
self
}
pub fn build(self) -> Symbols {
Symbols::from(self.by_name)
}
}
pub struct TypeCheckFunction {
metadata: CallableMetadata,
value: Value,
}
impl TypeCheckFunction {
pub fn new(value: Value) -> Rc<Self> {
Rc::from(Self {
metadata: CallableMetadataBuilder::new("TYPE_CHECK", VarType::Boolean).test_build(),
value,
})
}
}
#[async_trait(?Send)]
impl Function for TypeCheckFunction {
fn metadata(&self) -> &CallableMetadata {
&self.metadata
}
async fn exec(&self, args: &[Expr], _symbols: &mut Symbols) -> FunctionResult {
assert!(args.is_empty());
Ok(self.value.clone())
}
}