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 {}