use crate::ast::{ArgSep, ExprType, Value};
use crate::compiler::{
ArgSepSyntax, RepeatedSyntax, RepeatedTypeSyntax, RequiredRefSyntax, RequiredValueSyntax,
SingularArgSyntax,
};
use crate::exec::{Machine, Scope, ValueTag};
use crate::syms::{
Array, CallError, CallResult, Callable, CallableMetadata, CallableMetadataBuilder, Symbol,
SymbolKey, Symbols,
};
use crate::value;
use async_trait::async_trait;
use std::borrow::Cow;
use std::cell::RefCell;
use std::collections::HashMap;
use std::io;
use std::rc::Rc;
pub struct ArglessFunction {
metadata: CallableMetadata,
value: Value,
}
impl ArglessFunction {
pub fn new(value: Value) -> Rc<Self> {
Rc::from(Self {
metadata: CallableMetadataBuilder::new("ARGLESS")
.with_syntax(&[(&[], None)])
.with_return_type(value.as_exprtype())
.test_build(),
value,
})
}
}
#[async_trait(?Send)]
impl Callable for ArglessFunction {
fn metadata(&self) -> &CallableMetadata {
&self.metadata
}
async fn exec(&self, scope: Scope<'_>, _machine: &mut Machine) -> CallResult {
assert_eq!(0, scope.nargs());
scope.return_any(self.value.clone())
}
}
pub(crate) struct ClearCommand {
metadata: CallableMetadata,
}
impl ClearCommand {
pub(crate) fn new() -> Rc<Self> {
Rc::from(Self {
metadata: CallableMetadataBuilder::new("CLEAR")
.with_syntax(&[(&[], None)])
.test_build(),
})
}
}
#[async_trait(?Send)]
impl Callable for ClearCommand {
fn metadata(&self) -> &CallableMetadata {
&self.metadata
}
async fn exec(&self, scope: Scope<'_>, machine: &mut Machine) -> CallResult {
assert_eq!(0, scope.nargs());
machine.clear();
Ok(())
}
}
pub(crate) struct CountFunction {
metadata: CallableMetadata,
counter: Rc<RefCell<i32>>,
}
impl CountFunction {
pub(crate) fn new() -> Rc<Self> {
Rc::from(Self {
metadata: CallableMetadataBuilder::new("COUNT")
.with_return_type(ExprType::Integer)
.with_syntax(&[(&[], None)])
.test_build(),
counter: Rc::from(RefCell::from(0)),
})
}
}
#[async_trait(?Send)]
impl Callable for CountFunction {
fn metadata(&self) -> &CallableMetadata {
&self.metadata
}
async fn exec(&self, scope: Scope<'_>, _machine: &mut Machine) -> CallResult {
assert_eq!(0, scope.nargs());
let mut counter = self.counter.borrow_mut();
*counter += 1;
debug_assert!(*counter >= 0);
scope.return_integer(*counter)
}
}
pub struct RaisefFunction {
metadata: CallableMetadata,
}
impl RaisefFunction {
pub fn new() -> Rc<Self> {
Rc::from(Self {
metadata: CallableMetadataBuilder::new("RAISEF")
.with_return_type(ExprType::Boolean)
.with_syntax(&[(
&[SingularArgSyntax::RequiredValue(
RequiredValueSyntax { name: Cow::Borrowed("arg"), vtype: ExprType::Text },
ArgSepSyntax::End,
)],
None,
)])
.test_build(),
})
}
}
#[async_trait(?Send)]
impl Callable for RaisefFunction {
fn metadata(&self) -> &CallableMetadata {
&self.metadata
}
async fn exec(&self, mut scope: Scope<'_>, _machine: &mut Machine) -> CallResult {
assert_eq!(1, scope.nargs());
let (arg, pos) = scope.pop_string_with_pos();
match arg.as_str() {
"argument" => Err(CallError::ArgumentError(pos, "Bad argument".to_owned())),
"eval" => Err(CallError::EvalError(pos, "Some eval error".to_owned())),
"internal" => Err(CallError::InternalError(pos, "Some internal error".to_owned())),
"io" => Err(io::Error::new(io::ErrorKind::Other, "Some I/O error".to_owned()).into()),
"syntax" => Err(CallError::SyntaxError),
_ => panic!("Invalid arguments"),
}
}
}
pub struct RaiseCommand {
metadata: CallableMetadata,
}
impl RaiseCommand {
pub fn new() -> Rc<Self> {
Rc::from(Self {
metadata: CallableMetadataBuilder::new("RAISE")
.with_syntax(&[(
&[SingularArgSyntax::RequiredValue(
RequiredValueSyntax { name: Cow::Borrowed("arg"), vtype: ExprType::Text },
ArgSepSyntax::End,
)],
None,
)])
.test_build(),
})
}
}
#[async_trait(?Send)]
impl Callable for RaiseCommand {
fn metadata(&self) -> &CallableMetadata {
&self.metadata
}
async fn exec(&self, mut scope: Scope<'_>, _machine: &mut Machine) -> CallResult {
assert_eq!(1, scope.nargs());
let (arg, pos) = scope.pop_string_with_pos();
match arg.as_str() {
"argument" => Err(CallError::ArgumentError(pos, "Bad argument".to_owned())),
"eval" => Err(CallError::EvalError(pos, "Some eval error".to_owned())),
"internal" => Err(CallError::InternalError(pos, "Some internal error".to_owned())),
"io" => Err(io::Error::new(io::ErrorKind::Other, "Some I/O error".to_owned()).into()),
"syntax" => Err(CallError::SyntaxError),
_ => panic!("Invalid arguments"),
}
}
}
pub(crate) struct LastErrorFunction {
metadata: CallableMetadata,
}
impl LastErrorFunction {
pub(crate) fn new() -> Rc<Self> {
Rc::from(Self {
metadata: CallableMetadataBuilder::new("LAST_ERROR")
.with_return_type(ExprType::Text)
.test_build(),
})
}
}
#[async_trait(?Send)]
impl Callable for LastErrorFunction {
fn metadata(&self) -> &CallableMetadata {
&self.metadata
}
async fn exec(&self, scope: Scope<'_>, machine: &mut Machine) -> CallResult {
assert_eq!(0, scope.nargs());
match machine.last_error() {
Some(message) => scope.return_string(message),
None => scope.return_string("".to_owned()),
}
}
}
pub(crate) struct GetDataCommand {
metadata: CallableMetadata,
data: Rc<RefCell<Vec<Option<Value>>>>,
}
impl GetDataCommand {
pub(crate) fn new(data: Rc<RefCell<Vec<Option<Value>>>>) -> Rc<Self> {
Rc::from(Self {
metadata: CallableMetadataBuilder::new("GETDATA")
.with_syntax(&[(&[], None)])
.test_build(),
data,
})
}
}
#[async_trait(?Send)]
impl Callable for GetDataCommand {
fn metadata(&self) -> &CallableMetadata {
&self.metadata
}
async fn exec(&self, scope: Scope<'_>, machine: &mut Machine) -> CallResult {
assert_eq!(0, scope.nargs());
*self.data.borrow_mut() = machine.get_data().to_vec();
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")
.with_syntax(&[(
&[SingularArgSyntax::RequiredRef(
RequiredRefSyntax {
name: Cow::Borrowed("vref"),
require_array: false,
define_undefined: true,
},
ArgSepSyntax::End,
)],
None,
)])
.test_build(),
data,
})
}
}
#[async_trait(?Send)]
impl Callable for InCommand {
fn metadata(&self) -> &CallableMetadata {
&self.metadata
}
async fn exec(&self, mut scope: Scope<'_>, machine: &mut Machine) -> CallResult {
debug_assert_eq!(1, scope.nargs());
let (vname, vtype) = scope.pop_varref();
let mut data = self.data.borrow_mut();
let raw_value = data.next().unwrap();
let value = match vtype {
ExprType::Double => Value::Double(raw_value.parse::<f64>().unwrap()),
ExprType::Integer => Value::Integer(raw_value.parse::<i32>().unwrap()),
ExprType::Text => Value::Text(raw_value.to_string()),
_ => unreachable!("Unsupported target type"),
};
machine.get_mut_symbols().assign(&vname, 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")
.with_syntax(&[(
&[],
Some(&RepeatedSyntax {
name: Cow::Borrowed("arg"),
type_syn: RepeatedTypeSyntax::AnyValue,
sep: ArgSepSyntax::Exactly(ArgSep::Short),
require_one: false,
allow_missing: false,
}),
)])
.test_build(),
data,
})
}
}
#[async_trait(?Send)]
impl Callable for OutCommand {
fn metadata(&self) -> &CallableMetadata {
&self.metadata
}
async fn exec(&self, mut scope: Scope<'_>, _machine: &mut Machine) -> CallResult {
let mut first = true;
let mut text = String::new();
while scope.nargs() > 0 {
if !first {
text += " ";
}
first = false;
match scope.pop_value_tag() {
ValueTag::Boolean => {
let b = scope.pop_boolean();
if b {
text.push_str("TRUE");
} else {
text.push_str("FALSE");
}
}
ValueTag::Double => {
let d = scope.pop_double();
text.push_str(&d.to_string());
}
ValueTag::Integer => {
let i = scope.pop_integer();
text.push_str(&i.to_string());
}
ValueTag::Text => {
let s = scope.pop_string();
text.push_str(&s);
}
ValueTag::Missing => {
unreachable!("Missing expressions aren't allowed in function calls");
}
}
}
self.data.borrow_mut().push(text);
Ok(())
}
}
pub struct OutfFunction {
metadata: CallableMetadata,
data: Rc<RefCell<Vec<String>>>,
}
impl OutfFunction {
pub fn new(data: Rc<RefCell<Vec<String>>>) -> Rc<Self> {
Rc::from(Self {
metadata: CallableMetadataBuilder::new("OUTF")
.with_return_type(ExprType::Integer)
.with_syntax(&[(
&[],
Some(&RepeatedSyntax {
name: Cow::Borrowed("expr"),
type_syn: RepeatedTypeSyntax::TypedValue(ExprType::Integer),
sep: ArgSepSyntax::Exactly(ArgSep::Long),
require_one: false,
allow_missing: false,
}),
)])
.test_build(),
data,
})
}
}
#[async_trait(?Send)]
impl Callable for OutfFunction {
fn metadata(&self) -> &CallableMetadata {
&self.metadata
}
async fn exec(&self, mut scope: Scope<'_>, _machine: &mut Machine) -> CallResult {
assert_ne!(0, scope.nargs());
let result = scope.pop_integer();
let mut text = String::new();
let mut first = true;
while scope.nargs() > 0 {
let arg = scope.pop_integer();
if !first {
text += " ";
}
first = false;
text.push_str(&arg.to_string());
}
self.data.borrow_mut().push(text);
scope.return_integer(result)
}
}
pub struct SumFunction {
metadata: CallableMetadata,
}
impl SumFunction {
pub fn new() -> Rc<Self> {
Rc::from(Self {
metadata: CallableMetadataBuilder::new("SUM")
.with_return_type(ExprType::Integer)
.with_syntax(&[(
&[],
Some(&RepeatedSyntax {
name: Cow::Borrowed("n"),
type_syn: RepeatedTypeSyntax::TypedValue(ExprType::Integer),
sep: ArgSepSyntax::Exactly(ArgSep::Long),
require_one: false,
allow_missing: false,
}),
)])
.test_build(),
})
}
}
#[async_trait(?Send)]
impl Callable for SumFunction {
fn metadata(&self) -> &CallableMetadata {
&self.metadata
}
async fn exec(&self, mut scope: Scope<'_>, _machine: &mut Machine) -> CallResult {
let mut result = 0;
while scope.nargs() > 0 {
let (i, pos) = scope.pop_integer_with_pos();
result =
value::add_integer(result, i).map_err(|e| CallError::EvalError(pos, e.message))?;
}
scope.return_integer(result)
}
}
#[derive(Default)]
pub struct SymbolsBuilder {
globals: HashMap<SymbolKey, Symbol>,
scope: HashMap<SymbolKey, Symbol>,
}
impl SymbolsBuilder {
pub fn add_array<S: AsRef<str>>(mut self, name: S, subtype: ExprType) -> Self {
let key = SymbolKey::from(name);
let array = Array::new(subtype, vec![10]);
self.scope.insert(key, Symbol::Array(array));
self
}
pub fn add_callable(mut self, callable: Rc<dyn Callable>) -> Self {
let name = callable.metadata().name();
self.globals.insert(SymbolKey::from(name), Symbol::Callable(callable));
self
}
pub fn add_var<S: AsRef<str>>(mut self, name: S, value: Value) -> Self {
let key = SymbolKey::from(name);
self.scope.insert(key, Symbol::Variable(value));
self
}
pub(crate) fn add_global_var<S: AsRef<str>>(mut self, name: S, value: Value) -> Self {
let key = SymbolKey::from(name);
self.globals.insert(key, Symbol::Variable(value));
self
}
pub fn build(self) -> Symbols {
Symbols::from(self.globals, self.scope)
}
}
pub struct TypeCheckFunction {
metadata: CallableMetadata,
value: Value,
}
impl TypeCheckFunction {
pub fn new(value: Value) -> Rc<Self> {
Rc::from(Self {
metadata: CallableMetadataBuilder::new("TYPE_CHECK")
.with_return_type(ExprType::Boolean)
.with_syntax(&[(&[], None)])
.test_build(),
value,
})
}
}
#[async_trait(?Send)]
impl Callable for TypeCheckFunction {
fn metadata(&self) -> &CallableMetadata {
&self.metadata
}
async fn exec(&self, scope: Scope<'_>, _machine: &mut Machine) -> CallResult {
assert_eq!(0, scope.nargs());
scope.return_any(self.value.clone())
}
}