1use crate::conv::Context;
2use crate::conv::utils::check_compatibility;
3use crate::ir::assign_table::{AssignContext, AssignTable};
4use crate::ir::{
5 AssignDestination, Comptime, Expression, FfTable, IrResult, Shape, Signature, Statement,
6 ValueVariant, VarId, VarIndex, VarPath, VarPathSelect, VarSelect,
7};
8use crate::symbol::{Direction, Symbol, SymbolId, SymbolKind};
9use crate::value::{Value, ValueBigUint};
10use crate::{AnalyzerError, HashMap, ir_error};
11use indent::indent_all_by;
12use std::fmt;
13use veryl_parser::resource_table::StrId;
14use veryl_parser::token_range::TokenRange;
15
16#[derive(Clone, Debug, Hash, PartialEq, Eq, PartialOrd, Ord)]
17pub struct FuncPath {
18 pub path: VarPath,
19 pub sig: Signature,
20}
21
22impl FuncPath {
23 pub fn new(id: SymbolId) -> Self {
24 Self {
25 path: VarPath::default(),
26 sig: Signature::new(id),
27 }
28 }
29
30 pub fn add_prelude(&mut self, x: &[StrId]) {
31 self.path.add_prelude(x)
32 }
33
34 pub fn base(&self) -> FuncPath {
35 let mut ret = self.clone();
36 ret.sig.parameters.clear();
37 ret.sig.generic_parameters.clear();
38 ret
39 }
40}
41
42impl fmt::Display for FuncPath {
43 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
44 let mut ret = String::new();
45
46 if !self.path.0.is_empty() {
47 ret.push_str(&format!("{}.", self.path));
48 }
49 ret.push_str(&format!("{}", self.sig));
50
51 ret.fmt(f)
52 }
53}
54
55#[derive(Clone)]
56pub struct FuncArg {
57 pub name: StrId,
58 pub comptime: Comptime,
59 pub members: Vec<(VarPath, Comptime, Direction)>,
60}
61
62#[derive(Clone)]
63pub struct Function {
64 pub name: StrId,
65 pub id: VarId,
66 pub path: FuncPath,
67 pub r#type: Comptime,
68 pub array: Shape,
69 pub arity: usize,
70 pub args: Vec<FuncArg>,
71 pub is_const: bool,
72 pub functions: Vec<FunctionBody>,
73 pub token: TokenRange,
74}
75
76impl Function {
77 pub fn eval_assign(&self, context: &mut Context, assign_table: &mut AssignTable) {
78 for x in &self.functions {
79 x.eval_assign(context, assign_table);
80 }
81 }
82
83 pub fn set_index(&mut self, index: &VarIndex) {
84 for x in &mut self.functions {
85 x.set_index(index);
86 }
87 }
88
89 pub fn get_function(&self, index: &[usize]) -> Option<FunctionBody> {
90 let index = self.array.calc_index(index)?;
91 self.functions.get(index).cloned()
92 }
93
94 pub fn to_proto(&self) -> FuncProto {
95 FuncProto {
96 name: self.name,
97 id: self.id,
98 r#type: self.r#type.clone(),
99 arity: self.arity,
100 args: self.args.clone(),
101 token: self.token,
102 }
103 }
104}
105
106#[derive(Clone)]
107pub struct FuncProto {
108 pub name: StrId,
109 pub id: VarId,
110 pub r#type: Comptime,
111 pub arity: usize,
112 pub args: Vec<FuncArg>,
113 pub token: TokenRange,
114}
115
116#[derive(Clone)]
117pub struct FunctionBody {
118 pub ret: Option<VarId>,
119 pub arg_map: HashMap<VarPath, VarId>,
120 pub statements: Vec<Statement>,
121}
122
123impl FunctionBody {
124 pub fn eval_assign(&self, context: &mut Context, assign_table: &mut AssignTable) {
125 for x in &self.statements {
126 x.eval_assign(context, assign_table, AssignContext::Function, &[]);
127 }
128 }
129
130 pub fn set_index(&mut self, index: &VarIndex) {
131 for x in &mut self.statements {
132 x.set_index(index);
133 }
134 }
135}
136
137impl fmt::Display for Function {
138 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
139 let mut ret = String::new();
140
141 for (i, f) in self.functions.iter().enumerate() {
142 if self.functions.len() == 1 {
143 ret.push_str(&format!("func {}({})", self.id, self.path));
144 } else {
145 ret.push_str(&format!("func {}[{}]({})", self.id, i, self.path));
146 }
147
148 if let Some(x) = f.ret {
149 ret.push_str(&format!(" -> {x}"));
150 }
151 ret.push_str(" {\n");
152
153 for s in &f.statements {
154 let text = format!("{}\n", s);
155 ret.push_str(&indent_all_by(2, text));
156 }
157
158 ret.push_str("}\n");
159 }
160
161 ret.trim_end().fmt(f)
162 }
163}
164
165#[derive(Clone, Debug)]
166pub struct FunctionCall {
167 pub id: VarId,
168 pub index: Option<Vec<usize>>,
169 pub comptime: Comptime,
170 pub inputs: HashMap<VarPath, Expression>,
171 pub outputs: HashMap<VarPath, Vec<AssignDestination>>,
172}
173
174impl FunctionCall {
175 pub fn eval_type(&mut self, context: &mut Context) {
176 self.comptime.is_const = self.eval_comptime_flag(context);
177 }
178
179 pub fn eval_value(&self, context: &mut Context) -> Option<Value> {
180 let func = context.functions.get(&self.id)?;
181 let func = if let Some(x) = &self.index {
182 func.get_function(x)
183 } else {
184 func.get_function(&[])
185 }?;
186
187 for (path, expr) in &self.inputs {
189 let id = func.arg_map.get(path)?;
190 let value = expr.eval_value(context)?;
191 let var = context.variables.get_mut(id)?;
192 var.set_value(&[], value, None);
193 }
194
195 let disable_const_opt = context.disalbe_const_opt;
196 context.disalbe_const_opt = true;
197 for x in &func.statements {
198 x.eval_value(context);
199 }
200 context.disalbe_const_opt = disable_const_opt;
201
202 if let Some(x) = &func.ret {
205 let variable = context.variables.get(x)?;
206 variable.get_value(&[]).cloned()
207 } else {
208 None
209 }
210 }
211
212 pub fn eval_comptime(&mut self, context: &mut Context) -> Comptime {
213 let value = self.eval_value(context);
214 let value = if let Some(x) = value {
215 ValueVariant::Numeric(x)
216 } else {
217 ValueVariant::Unknown
218 };
219
220 let mut ret = self.comptime.clone();
221 ret.value = value;
222
223 ret.is_const = self.eval_comptime_flag(context);
224 ret
225 }
226
227 pub fn eval_assign(
228 &self,
229 context: &mut Context,
230 assign_table: &mut AssignTable,
231 assign_context: AssignContext,
232 ) {
233 for output in self.outputs.values() {
234 for dst in output {
235 if let Some(index) = dst.index.eval_value(context) {
236 let variable = context.get_variable_info(dst.id).unwrap();
237 if let Some((beg, end)) =
238 dst.select.eval_value(context, &variable.r#type, false)
239 {
240 let mask = ValueBigUint::gen_mask_range(beg, end);
241 let (success, tokens) = assign_table.insert_assign(
242 &variable,
243 index,
244 mask,
245 false,
246 self.comptime.token,
247 );
248 if !success & assign_context.is_ff() {
249 context.insert_error(AnalyzerError::multiple_assignment(
250 &variable.path.to_string(),
251 &self.comptime.token,
252 &tokens,
253 ));
254 }
255 }
256 }
257 }
258 }
259 }
260
261 pub fn gather_ff(
262 &self,
263 context: &mut Context,
264 table: &mut FfTable,
265 decl: usize,
266 assign_target: Option<(VarId, Option<usize>)>,
267 from_ff: bool,
268 ) {
269 for input in self.inputs.values() {
270 input.gather_ff(context, table, decl, assign_target, from_ff);
271 }
272 for dsts in self.outputs.values() {
273 for dst in dsts {
274 dst.gather_ff(context, table, decl);
275 }
276 }
277 }
278
279 pub fn gather_ff_comb_assign(&self, context: &mut Context, table: &mut FfTable, decl: usize) {
280 for dsts in self.outputs.values() {
281 for dst in dsts {
282 dst.gather_ff_comb_assign(context, table, decl);
283 }
284 }
285 }
286
287 pub fn set_index(&mut self, index: &VarIndex) {
288 for x in self.inputs.values_mut() {
289 x.set_index(index);
290 }
291 for x in self.outputs.values_mut() {
292 for x in x {
293 x.set_index(index);
294 }
295 }
296 }
297
298 fn eval_comptime_flag(&mut self, context: &mut Context) -> bool {
299 let mut is_const = context
300 .functions
301 .get(&self.id)
302 .map(|func| func.is_const)
303 .unwrap_or(true);
304 for expr in self.inputs.values_mut() {
305 is_const &= expr.eval_comptime(context, None).is_const;
306 }
307
308 if !self.outputs.is_empty() {
310 is_const = false;
311 }
312
313 is_const
314 }
315}
316
317impl fmt::Display for FunctionCall {
318 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
319 let mut args = String::new();
320
321 let mut inputs: Vec<_> = self.inputs.iter().collect();
322 let mut outputs: Vec<_> = self.outputs.iter().collect();
323 inputs.sort_by_key(|x| x.0);
324 outputs.sort_by_key(|x| x.0);
325
326 for (id, val) in &inputs {
327 args.push_str(&format!("{id}: {val}, "));
328 }
329 for (id, val) in &outputs {
330 if val.len() == 1 {
331 args.push_str(&format!("{id}: {}, ", val[0]));
332 } else {
333 args.push_str(&format!("{id}: {{{}", val[0]));
334 for x in &val[1..] {
335 args.push_str(&format!(", {x}"));
336 }
337 args.push_str("}}, ");
338 }
339 }
340 let args = if args.is_empty() {
341 &args
342 } else {
343 &args[0..args.len() - 2]
344 };
345
346 let mut index = String::new();
347 if let Some(x) = &self.index {
348 for x in x {
349 index.push_str(&format!("[{}]", x));
350 }
351 }
352
353 format!("{}{}({})", self.id, index, args).fmt(f)
354 }
355}
356
357pub type PositionalArgs = Vec<(Expression, Vec<VarPathSelect>, TokenRange)>;
358pub type NamedArgs = Vec<(StrId, (Expression, Vec<VarPathSelect>, TokenRange))>;
359pub type FunctionArgs = (
360 HashMap<VarPath, Expression>,
361 HashMap<VarPath, Vec<AssignDestination>>,
362);
363
364#[derive(Clone)]
365pub enum Arguments {
366 Positional(PositionalArgs),
367 Named(NamedArgs),
368 Mixed(PositionalArgs, NamedArgs),
369 Null,
370}
371
372impl Arguments {
373 pub fn is_empty(&self) -> bool {
374 self.len() == 0
375 }
376
377 pub fn len(&self) -> usize {
378 match self {
379 Arguments::Positional(x) => x.len(),
380 Arguments::Named(x) => x.len(),
381 Arguments::Mixed(x, y) => x.len() + y.len(),
382 Arguments::Null => 0,
383 }
384 }
385
386 pub fn to_system_function_args(
387 self,
388 _context: &mut Context,
389 symbol: &Symbol,
390 ) -> Vec<(Expression, Vec<VarPathSelect>, TokenRange)> {
391 let ret = match self {
392 Arguments::Positional(x) => x,
393 Arguments::Named(_) => {
394 return vec![];
396 }
397 Arguments::Mixed(_, _) => vec![],
398 Arguments::Null => vec![],
399 };
400
401 if let SymbolKind::SystemFunction(x) = &symbol.kind {
402 let arity = x.ports.len();
403
404 if arity != ret.len() {
405 }
414 }
415
416 ret
417 }
418
419 pub fn to_function_args(
420 self,
421 context: &mut Context,
422 func: &FuncProto,
423 token: TokenRange,
424 ) -> IrResult<FunctionArgs> {
425 let mut inputs = HashMap::default();
426 let mut outputs = HashMap::default();
427
428 if func.arity != self.len() {
429 context.insert_error(AnalyzerError::mismatch_function_arity(
430 &func.name.to_string(),
431 func.arity,
432 self.len(),
433 &token,
434 ));
435 return Err(ir_error!(func.token));
436 }
437
438 let mut arg_map_by_name = HashMap::default();
439 let mut arg_map_by_index = HashMap::default();
440 for (i, arg) in func.args.iter().enumerate() {
441 arg_map_by_name.insert(arg.name, arg.clone());
442 arg_map_by_index.insert(i, arg.clone());
443 }
444
445 let mut connections = vec![];
446 match self {
447 Arguments::Positional(x) => {
448 for (i, (expr, dst, _)) in x.into_iter().enumerate() {
449 if let Some(arg) = arg_map_by_index.get(&i) {
450 connections.push((arg, expr, dst));
451 }
452 }
453 }
454 Arguments::Named(x) => {
455 for (name, (expr, dst, _)) in x {
456 if let Some(arg) = arg_map_by_name.get(&name) {
457 connections.push((arg, expr, dst));
458 }
459 }
460 }
461 Arguments::Mixed(_, _) => (),
462 Arguments::Null => (),
463 };
464
465 for (arg, mut expr, dst) in connections {
466 if arg.members.len() == 1 {
467 let (path, _, direction) = &arg.members[0];
468 match direction {
469 Direction::Input => {
470 inputs.insert(path.clone(), expr);
471 }
472 Direction::Output => {
473 let dst = dst
474 .into_iter()
475 .filter_map(|x| x.to_assign_destination(context, false))
476 .collect();
477 outputs.insert(path.clone(), dst);
478 }
479 _ => (),
480 }
481 } else {
482 let expr_comptime = expr.eval_comptime(context, None);
483 let expr_token = expr_comptime.token;
484 let expr_members = expr_comptime
485 .r#type
486 .expand_interface(context, &dst[0].0, expr_token)?;
487
488 check_compatibility(context, &arg.comptime.r#type, expr_comptime, &expr_token);
489
490 for (x, y) in arg.members.iter().zip(expr_members.iter()) {
491 let arg_path = x.0.clone();
492 let direction = x.2;
493 let expr_path = y.0.clone();
494
495 match direction {
496 Direction::Input => {
497 let expr = VarPathSelect(expr_path, VarSelect::default(), expr_token);
498 let expr = expr.to_expression(context);
499 if let Some(expr) = expr {
500 inputs.insert(arg_path, expr);
501 }
502 }
503 Direction::Output => {
504 let dst = VarPathSelect(expr_path, VarSelect::default(), expr_token);
505 let dst = dst.to_assign_destination(context, false);
506 if let Some(dst) = dst {
507 outputs.insert(arg_path, vec![dst]);
508 }
509 }
510 _ => (),
511 }
512 }
513 }
514 }
515
516 Ok((inputs, outputs))
517 }
518}