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
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
//  SPEC.rs
//    by Lut99
//
//  Created:
//    20 Oct 2022, 14:17:30
//  Last edited:
//    07 Nov 2023, 17:15:25
//  Auto updated?
//    Yes
//
//  Description:
//!   Defines (public) interfaces and structs for the `brane-ast` crate.
//

use brane_dsl::data_type::FunctionSignature;
use brane_dsl::{DataType, TextRange};
use strum::{EnumIter, IntoEnumIterator as _};

use crate::state::{ClassState, FunctionState, VarState};


/***** LIBRARY *****/
/// Defines the builtin functions that exist in BraneScript.
#[derive(Clone, Copy, Debug, EnumIter)]
pub enum BuiltinFunctions {
    /// The print-function, which prints some text to stdout.
    Print,
    /// The println-function, which does the same as `Print` but now with a newline appended to the text.
    PrintLn,

    /// The len-function, which returns the length of an array.
    Len,

    /// The commit_builtin-function, which turns an IntermediateResult into a Data.
    CommitResult,
}

impl BuiltinFunctions {
    /// Returns the identifier of this builtin function.
    #[inline]
    pub fn name(&self) -> &'static str {
        use BuiltinFunctions::*;
        match self {
            Print => "print",
            PrintLn => "println",

            Len => "len",

            CommitResult => "commit_result",
        }
    }

    /// Returns the signature of this specific builtin.
    #[inline]
    pub fn signature(&self) -> FunctionSignature {
        use BuiltinFunctions::*;
        match self {
            Print => FunctionSignature::new(vec![DataType::String], DataType::Void),
            PrintLn => FunctionSignature::new(vec![DataType::String], DataType::Void),

            Len => FunctionSignature::new(vec![DataType::Array(Box::new(DataType::Any))], DataType::Integer),

            CommitResult => FunctionSignature::new(
                vec![DataType::String, DataType::Class(BuiltinClasses::IntermediateResult.name().into())],
                DataType::Class(BuiltinClasses::Data.name().into()),
            ),
        }
    }

    /// Returns an array with all the builtin functions in it.
    #[inline]
    pub fn all() -> [Self; 4] { [Self::Print, Self::PrintLn, Self::Len, Self::CommitResult] }

    /// Returns an Array with all of the builtin functions but already casted to FunctionStates.
    #[inline]
    pub fn all_into_state() -> [FunctionState; 4] { [Self::Print.into(), Self::PrintLn.into(), Self::Len.into(), Self::CommitResult.into()] }

    /// Checks if the given string is a builtin.
    #[inline]
    pub fn is_builtin(name: impl AsRef<str>) -> bool {
        // Note that the order in which we match (i.e., on self instead of name) is a little awkward but guarantees Rust will warns us if we change the set.
        let name: &str = name.as_ref();
        for builtin in Self::iter() {
            if name == builtin.name() {
                return true;
            }
        }
        false
    }
}

impl From<BuiltinFunctions> for FunctionState {
    #[inline]
    fn from(value: BuiltinFunctions) -> Self {
        Self {
            name:      value.name().into(),
            signature: value.signature(),

            class_name: None,

            range: TextRange::none(),
        }
    }
}



/// Defines the builtin classes that exist in BraneScript.
#[derive(Clone, Copy, Debug)]
pub enum BuiltinClasses {
    /// The data-class.
    Data,
    /// The intermediate-result-class.
    IntermediateResult,
}

impl BuiltinClasses {
    /// Returns the identifier of this builtin class.
    #[inline]
    pub fn name(&self) -> &'static str {
        use BuiltinClasses::*;
        match self {
            Data => "Data",
            IntermediateResult => "IntermediateResult",
        }
    }

    /// Returns a list of all properties (as `VarState`s) in this builtin class.
    #[inline]
    pub fn props(&self) -> Vec<VarState> {
        use BuiltinClasses::*;
        match self {
            Data => vec![VarState {
                name: "name".into(),
                data_type: DataType::String,
                function_name: None,
                class_name: Some(self.name().into()),
                range: TextRange::none(),
            }],
            IntermediateResult => vec![VarState {
                name: "path".into(),
                data_type: DataType::String,
                function_name: None,
                class_name: Some(self.name().into()),
                range: TextRange::none(),
            }],
        }
    }

    /// Returns a list of all methods (as `FunctioNState`s) in this builtin class.
    #[inline]
    pub fn methods(&self) -> Vec<FunctionState> {
        use BuiltinClasses::*;
        match self {
            Data => vec![],
            IntermediateResult => vec![],
        }
    }

    /// Returns an array with all the builtin classes in it.
    #[inline]
    pub fn all() -> [Self; 2] { [Self::Data, Self::IntermediateResult] }

    /// Returns an Array with all of the builtin functions but already casted to FunctionStates.
    ///
    /// # Arguments
    /// - `funcs`: The list of function states to use for declaring new methods, if any.
    #[inline]
    pub fn all_into_state(funcs: &mut Vec<FunctionState>) -> [ClassState; 2] {
        [Self::Data.into_state(funcs), Self::IntermediateResult.into_state(funcs)]
    }

    /// Creates a new ClassState for this BuiltinClasses, where we define the functions in the given TableList of functions.
    ///
    /// # Arguments
    /// - `funcs`: The TableList of functions where to declare the new ones.
    ///
    /// # Returns
    /// A new ClassState instance.
    #[inline]
    pub fn into_state(&self, funcs: &mut Vec<FunctionState>) -> ClassState {
        ClassState {
            name:    self.name().into(),
            props:   self.props(),
            methods: self
                .methods()
                .into_iter()
                .enumerate()
                .map(|(i, state)| {
                    funcs.push(state);
                    i
                })
                .collect(),

            package_name:    None,
            package_version: None,

            range: TextRange::none(),
        }
    }
}