cas_compiler/
item.rs

1use cas_compute::numerical::builtin::{Builtin, ParamKind};
2use cas_error::Error;
3use cas_parser::parser::ast::{Call, Param as ParserParam};
4use crate::{error::{MissingArgument, TooManyArguments}, sym_table::ScopeId};
5use std::cmp::Ordering;
6
7enum Signature<'a> {
8    Builtin(&'static dyn Builtin),
9    Parser(&'a [ParserParam]),
10}
11
12impl Signature<'_> {
13    /// Returns the indices of the required parameters that were not provided by the user in the
14    /// call.
15    ///
16    /// This indicates an error in the user's code.
17    pub fn missing_args(&self, num_given: usize) -> Vec<usize> {
18        match self {
19            Self::Builtin(builtin) => {
20                // default arguments must be at the end of the signature, so we can safely assume
21                // that all required arguments are at the beginning
22                let idx_of_first_default = builtin.sig()
23                    .iter()
24                    .position(|param| param.kind == ParamKind::Optional);
25                // the missing arguments are the required arguments before the first default
26                // argument, and after the number of arguments given
27                (num_given..idx_of_first_default.unwrap_or(builtin.sig().len()))
28                    .collect()
29            },
30            Self::Parser(params) => {
31                let idx_of_first_default = params.iter()
32                    .position(|param| matches!(param, ParserParam::Default(..)));
33                (num_given..idx_of_first_default.unwrap_or(params.len()))
34                    .collect()
35            },
36        }
37    }
38
39    fn signature(&self) -> String {
40        match self {
41            Self::Builtin(builtin) => builtin.sig_str().to_owned(),
42            Self::Parser(params) => params.iter()
43                .map(|param| match param {
44                    ParserParam::Symbol(name) => format!("{}", name),
45                    ParserParam::Default(name, value) => format!("{} = {}", name, value),
46                })
47                .collect::<Vec<_>>()
48                .join(", "),
49        }
50    }
51}
52
53/// Given a function call to a function with the given signature, checks if the call matches the
54/// signature in argument count.
55///
56/// Returns [`Ok`] if the call matches the signature, or [`Err`] otherwise.
57fn check_call(sig: Signature<'_>, sig_len: usize, call: &Call) -> Result<(), Error> {
58    match call.args.len().cmp(&sig_len) {
59        Ordering::Greater => {
60            // add span of extraneous arguments
61            let mut spans = call.outer_span().to_vec();
62            spans.push(call.arg_span(sig_len..call.args.len() - 1));
63            Err(Error::new(
64                spans,
65                TooManyArguments {
66                    name: call.name.name.to_string(),
67                    expected: sig_len,
68                    given: call.args.len(),
69                    signature: sig.signature(),
70                },
71            ))
72        },
73        Ordering::Equal => Ok(()),
74        Ordering::Less => {
75            let indices = sig.missing_args(call.args.len());
76            if indices.is_empty() {
77                Ok(())
78            } else {
79                Err(Error::new(
80                    call.outer_span().to_vec(),
81                    MissingArgument {
82                        name: call.name.name.to_string(),
83                        indices,
84                        expected: sig_len,
85                        given: call.args.len(),
86                        signature: sig.signature(),
87                    },
88                ))
89            }
90        },
91    }
92}
93
94/// An item declaration in the program.
95#[derive(Clone, Debug)]
96pub enum Item {
97    /// A symbol declaration.
98    Symbol(SymbolDecl),
99
100    /// A function declaration. Function bodies introduce new scopes where symbols can be declared.
101    Func(FuncDecl),
102}
103
104impl Item {
105    /// Returns the unique item ID for the item.
106    pub fn id(&self) -> usize {
107        match self {
108            Self::Symbol(symbol) => symbol.id,
109            Self::Func(func) => func.id,
110        }
111    }
112}
113
114/// A symbol declaration.
115#[derive(Clone, Copy, Debug)]
116pub struct SymbolDecl {
117    /// The unique identifier for the symbol.
118    pub id: usize,
119}
120
121/// A function declaration. Function bodies introduce new scopes where symbols can be declared.
122#[derive(Clone, Debug)]
123pub struct FuncDecl {
124    /// The unique identifier for the function.
125    pub id: usize,
126
127    /// The identifier of the scope containing the function's symbol table.
128    pub scope_id: ScopeId,
129
130    /// The index of the chunk containing the function body.
131    pub chunk: usize,
132
133    /// The function signature.
134    pub signature: Vec<ParserParam>,
135}
136
137impl FuncDecl {
138    /// Returns a function declaration with the initial given id, scope id, chunk index, and
139    /// signature.
140    pub fn new(
141        id: usize,
142        scope_id: ScopeId,
143        chunk: usize,
144        signature: Vec<ParserParam>,
145    ) -> Self {
146        Self {
147            id,
148            scope_id,
149            chunk,
150            signature,
151        }
152    }
153
154    /// Checks if this function call matches the signature of its target function in argmuent
155    /// count.
156    ///
157    /// Returns [`Ok`] if the call matches the signature, or [`Err`] otherwise.
158    pub fn check_call(&self, call: &Call) -> Result<(), Error> {
159        check_call(
160            Signature::Parser(&self.signature),
161            self.signature.len(),
162            call
163        )
164    }
165}
166
167/// An identifier for a symbol, user-defined or builtin.
168#[derive(Clone, Copy, Debug, Hash, PartialEq, Eq)]
169pub enum Symbol {
170    /// A user-defined symbol. The inner value represents the index of the symbol in the symbol
171    /// table.
172    User(usize),
173
174    /// A builtin symbol. The inner value is the name of the symbol.
175    Builtin(&'static str),
176}
177
178impl Symbol {
179    /// Returns [`Ok`] with the index of the symbol if it is a user-defined symbol, or [`Err`] with
180    /// the name of the symbol if it is a builtin symbol.
181    pub fn index(&self) -> Result<usize, &'static str> {
182        match self {
183            Self::User(index) => Ok(*index),
184            Self::Builtin(name) => Err(name),
185        }
186    }
187}
188
189/// An identifier for a function call to a [`UserCall`] or [`BuiltinCall`].
190#[derive(Clone, Debug, PartialEq, Eq)]
191pub enum Func {
192    /// A call to a user-defined function.
193    User(UserCall),
194
195    /// A call to a builtin function.
196    Builtin(BuiltinCall),
197}
198
199impl Func {
200    /// Returns the number of arguments in the function signature.
201    pub fn arity(&self) -> usize {
202        match self {
203            Self::User(user) => user.signature.len(),
204            Self::Builtin(builtin) => builtin.builtin.sig().len(),
205        }
206    }
207
208    /// Checks if this function call matches the signature of its target function in argmuent
209    /// count.
210    ///
211    /// Returns [`Ok`] if the call matches the signature, or [`Err`] otherwise.
212    pub fn check_call(&self, call: &Call) -> Result<(), Error> {
213        match self {
214            Self::User(user) => check_call(
215                Signature::Parser(&user.signature),
216                user.signature.len(),
217                call,
218            ),
219            Self::Builtin(builtin) => check_call(
220                Signature::Builtin(builtin.builtin),
221                builtin.builtin.sig().len(),
222                call,
223            ),
224        }
225    }
226
227    /// Returns the number of default arguments being used in the call.
228    ///
229    /// Returns [`None`] if the function call has more arguments than the signature, which shuold
230    /// cause a compilation error.
231    pub fn num_defaults_used(&self) -> Option<usize> {
232        match self {
233            Self::User(call) => call.signature.len().checked_sub(call.num_given),
234            Self::Builtin(call) => call.builtin.sig().len().checked_sub(call.num_given),
235        }
236    }
237}
238
239/// An identifier to a call to a user-defined function.
240#[derive(Clone, Debug, PartialEq, Eq)]
241pub struct UserCall {
242    /// The index of the chunk containing the function body.
243    pub chunk: usize,
244
245    /// The function signature.
246    pub signature: Vec<ParserParam>,
247
248    /// The number of arguments passed to the function in the call.
249    pub num_given: usize,
250}
251
252/// An identifier to a call to a builtin function.
253#[derive(Clone, Debug)]
254pub struct BuiltinCall {
255    /// The builtin function.
256    pub builtin: &'static dyn Builtin,
257
258    /// The number of arguments passed to the function in the call.
259    pub num_given: usize,
260}
261
262/// Manual implementation of [`PartialEq`] to support `dyn Builtin` by comparing pointers.
263impl PartialEq for BuiltinCall {
264    fn eq(&self, other: &Self) -> bool {
265        std::ptr::eq(self.builtin, other.builtin) && self.num_given == other.num_given
266    }
267}
268
269/// Manual implementation of [`Eq`] to support `dyn Builtin` by comparing pointers.
270impl Eq for BuiltinCall {}