1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
//! Provides the built-in functions and traits to create custom ones.

/// Contains all the built-in function parsers.
#[allow(clippy::missing_docs_in_private_items)]
pub mod parsers {
    mod begin;
    mod dateformat;
    mod r#else;
    mod end;
    mod excerpt;
    mod r#for;
    mod ifdefined;
    mod import;
    mod insert;
    mod timetoread;

    pub use begin::BeginParser as Begin;
    pub use dateformat::DateFormatParser as DateFormat;
    pub use end::EndParser as End;
    pub use excerpt::ExcerptParser as Excerpt;
    pub use ifdefined::IfDefinedParser as IfDefined;
    pub use import::ImportParser as Import;
    pub use insert::InsertParser as Insert;
    pub use r#else::ElseParser as Else;
    pub use r#for::ForParser as For;
    pub use timetoread::TimeToReadParser as TimeToRead;

    #[macro_use]
    mod r#if;

    if_parsers![
        ifeq, IfEq, ==;
        ifne, IfNe, !=;
        ifgt, IfGt, >;
        ifge, IfGe, >=;
        iflt, IfLt, <;
        ifle, IfLe, <;
    ];
}

use crate::error::ProcessError;
use crate::parse::{ParseError, RawFunction};
use crate::process::Scope;
use crate::TracebackError;

use humphrey_json::Value;

use std::fmt::Debug;

/// Represents a function parser.
///
/// A function parser is an object which is capable of parsing a raw function into an executable function object,
///   the inner workings of which are hidden from Stuart through the [`Function`] trait. The parser should also
///   define a name, which is used to identify the function when parsing a file. The name of the function parser
///   **must** be the same as that of the returned function.
pub trait FunctionParser: Send + Sync {
    /// Returns the name of the function which the parser can parse.
    ///
    /// This **must** return the same value as the `name` method of the returned function.
    fn name(&self) -> &'static str;

    /// Attempts to parse the raw function into an executable function object.
    fn parse(&self, raw: RawFunction) -> Result<Box<dyn Function>, ParseError>;

    /// Returns `true` if the raw function can be parsed by this function parser.
    fn can_parse(&self, raw: &RawFunction) -> bool {
        raw.name == self.name()
    }
}

/// Represents an executable function.
///
/// When the function is executed, it is given a [`Scope`] object, which contains information about the current state
///   of the program, including variables, stack frames and more.
pub trait Function: Debug + Send + Sync {
    /// Returns the name of the function.
    fn name(&self) -> &'static str;

    /// Executes the function in the given scope.
    fn execute(&self, scope: &mut Scope) -> Result<(), TracebackError<ProcessError>>;
}

/// Represents an input into a function.
#[derive(Debug, Clone)]
enum Input {
    /// A variable name.
    Variable(String),
    /// A string literal.
    String(String),
    /// An integer literal.
    Integer(i32),
}

impl Input {
    /// If the input is a variable, converts it to its value in the given scope.
    ///
    /// If the input is not a variable, returns the input unchanged.
    fn evaluate_variable(&self, scope: &mut Scope) -> Option<Self> {
        match self {
            Input::Variable(name) => match scope.get_variable(name) {
                Some(Value::String(s)) => Some(Input::String(s)),
                Some(Value::Number(i)) => Some(Input::Integer(i as i32)),
                _ => None,
            },
            x => Some(x.clone()),
        }
    }
}

impl PartialEq for Input {
    fn eq(&self, other: &Self) -> bool {
        match (self, other) {
            (Self::Variable(l0), Self::Variable(r0)) => l0 == r0,
            (Self::String(l0), Self::String(r0)) => l0 == r0,
            (Self::Integer(l0), Self::Integer(r0)) => l0 == r0,
            _ => false,
        }
    }
}

impl PartialOrd for Input {
    fn partial_cmp(&self, other: &Self) -> Option<std::cmp::Ordering> {
        match (self, other) {
            (Self::Variable(_), Self::Variable(_)) => None,
            (Self::String(_), Self::String(_)) => None,
            (Self::Integer(i), Self::Integer(j)) => i.partial_cmp(j),
            _ => None,
        }
    }
}

impl ToString for Input {
    fn to_string(&self) -> String {
        match self {
            Input::Variable(v) => v.clone(),
            Input::String(s) => s.clone(),
            Input::Integer(i) => i.to_string(),
        }
    }
}

/// A macro which counts its arguments.
macro_rules! count {
    () => { 0_usize };
    ($head:tt $($tail:tt)*) => { 1_usize + count!($($tail)*) };
}

/// Defines the functions available in the program by way of a global variable.
macro_rules! define_functions {
    ($($name:expr,)*) => {
        const FUNCTION_COUNT: usize = count!($($name)*);

        ::lazy_static::lazy_static! {
            static ref FUNCTION_PARSERS: [Box<dyn $crate::functions::FunctionParser>; FUNCTION_COUNT] = [
                $(Box::new($name)),*
            ];
        }
    }
}

/// Quietly asserts that the given condition is true.
///
/// If the condition is false, this macro will not panic, and will instead return an error.
#[macro_export]
macro_rules! quiet_assert {
    ($cond:expr) => {
        match $cond {
            true => Ok(()),
            false => Err($crate::parse::ParseError::AssertionError(
                stringify!($cond).to_string(),
            )),
        }
    };
}

/// Returns true if the string is an identifier of a function.
#[inline]
pub fn is_ident(s: &str) -> bool {
    crate::FUNCTION_PARSERS.iter().any(|f| f.name() == s)
}