rukalang/meta/
runtime_eval.rs

1use super::core::{
2    expand_runtime_block, expected_builder_type, match_cannot_succeed,
3    match_requires_code_but_cannot_have_code, resolved_name_kind, staged_builtin_arg_mismatch,
4};
5use super::meta_types_builder::{
6    builder_bindings_to_locals, coerce_meta_value_to_staged_meta_type, coerce_meta_value_to_type,
7    eval_meta_expr_arg, eval_meta_int_arg, eval_meta_string_arg, eval_meta_type_arg,
8    eval_meta_typed_expr_arg, expand_builder_expr, expr_builder_locals,
9};
10use super::quote_expand::{
11    coerce_value_to_code_for_match, expand_quoted_expr, match_quote_code, promote_splice_value,
12    runtime_splice_value_to_expr,
13};
14use super::quote_match::match_quote_pattern_type;
15use super::*;
16
17pub(super) fn expand_runtime_expr(
18    program: &Program,
19    expr: &Expr,
20    function_table: &BTreeMap<String, &MetaFunctionDecl>,
21    runtime_scope: &RuntimeScope,
22    template_env: &BTreeMap<String, MetaValue>,
23    mut builder_env_cursor: Option<&mut BuilderEnvCursor>,
24) -> Result<Expr, MetaEvalError> {
25    match expr {
26        Expr::Ident { .. } | Expr::Int(_) | Expr::Number(_) | Expr::String(_) | Expr::Bool(_) => {
27            Ok(expr.clone())
28        }
29        Expr::Call { callee, args, span } => Ok(Expr::Call {
30            callee: Box::new(expand_runtime_expr(
31                program,
32                callee,
33                function_table,
34                runtime_scope,
35                template_env,
36                builder_env_cursor.as_deref_mut(),
37            )?),
38            args: args
39                .iter()
40                .map(|arg| {
41                    expand_runtime_call_arg(
42                        program,
43                        arg,
44                        function_table,
45                        runtime_scope,
46                        template_env,
47                        builder_env_cursor.as_deref_mut(),
48                    )
49                })
50                .collect::<Result<Vec<_>, _>>()?,
51            span: *span,
52        }),
53        Expr::IntrinsicCall { name, args, span } => Ok(Expr::IntrinsicCall {
54            name: name.clone(),
55            args: args
56                .iter()
57                .map(|arg| {
58                    expand_runtime_call_arg(
59                        program,
60                        arg,
61                        function_table,
62                        runtime_scope,
63                        template_env,
64                        builder_env_cursor.as_deref_mut(),
65                    )
66                })
67                .collect::<Result<Vec<_>, _>>()?,
68            span: *span,
69        }),
70        Expr::Field { base, field } => Ok(Expr::Field {
71            base: Box::new(expand_runtime_expr(
72                program,
73                base,
74                function_table,
75                runtime_scope,
76                template_env,
77                builder_env_cursor.as_deref_mut(),
78            )?),
79            field: field.clone(),
80        }),
81        Expr::Index { base, index } => Ok(Expr::Index {
82            base: Box::new(expand_runtime_expr(
83                program,
84                base,
85                function_table,
86                runtime_scope,
87                template_env,
88                builder_env_cursor.as_deref_mut(),
89            )?),
90            index: Box::new(expand_runtime_expr(
91                program,
92                index,
93                function_table,
94                runtime_scope,
95                template_env,
96                builder_env_cursor.as_deref_mut(),
97            )?),
98        }),
99        Expr::SliceRange { base, start, end } => Ok(Expr::SliceRange {
100            base: Box::new(expand_runtime_expr(
101                program,
102                base,
103                function_table,
104                runtime_scope,
105                template_env,
106                builder_env_cursor.as_deref_mut(),
107            )?),
108            start: start
109                .as_ref()
110                .map(|expr| {
111                    expand_runtime_expr(
112                        program,
113                        expr,
114                        function_table,
115                        runtime_scope,
116                        template_env,
117                        builder_env_cursor.as_deref_mut(),
118                    )
119                    .map(Box::new)
120                })
121                .transpose()?,
122            end: end
123                .as_ref()
124                .map(|expr| {
125                    expand_runtime_expr(
126                        program,
127                        expr,
128                        function_table,
129                        runtime_scope,
130                        template_env,
131                        builder_env_cursor.as_deref_mut(),
132                    )
133                    .map(Box::new)
134                })
135                .transpose()?,
136        }),
137        Expr::Prefix { op, value } => Ok(Expr::Prefix {
138            op: *op,
139            value: Box::new(expand_runtime_expr(
140                program,
141                value,
142                function_table,
143                runtime_scope,
144                template_env,
145                builder_env_cursor.as_deref_mut(),
146            )?),
147        }),
148        Expr::Binary { op, lhs, rhs } => Ok(Expr::Binary {
149            op: *op,
150            lhs: Box::new(expand_runtime_expr(
151                program,
152                lhs,
153                function_table,
154                runtime_scope,
155                template_env,
156                builder_env_cursor.as_deref_mut(),
157            )?),
158            rhs: Box::new(expand_runtime_expr(
159                program,
160                rhs,
161                function_table,
162                runtime_scope,
163                template_env,
164                builder_env_cursor.as_deref_mut(),
165            )?),
166        }),
167        Expr::Relational { op, lhs, rhs } => Ok(Expr::Relational {
168            op: *op,
169            lhs: Box::new(expand_runtime_expr(
170                program,
171                lhs,
172                function_table,
173                runtime_scope,
174                template_env,
175                builder_env_cursor.as_deref_mut(),
176            )?),
177            rhs: Box::new(expand_runtime_expr(
178                program,
179                rhs,
180                function_table,
181                runtime_scope,
182                template_env,
183                builder_env_cursor.as_deref_mut(),
184            )?),
185        }),
186        Expr::Array { items, span } => Ok(Expr::Array {
187            items: items
188                .iter()
189                .map(|item| {
190                    expand_runtime_expr(
191                        program,
192                        item,
193                        function_table,
194                        runtime_scope,
195                        template_env,
196                        builder_env_cursor.as_deref_mut(),
197                    )
198                })
199                .collect::<Result<Vec<_>, _>>()?,
200            span: *span,
201        }),
202        Expr::Tuple { items, span } => Ok(Expr::Tuple {
203            items: items
204                .iter()
205                .map(|item| {
206                    expand_runtime_expr(
207                        program,
208                        item,
209                        function_table,
210                        runtime_scope,
211                        template_env,
212                        builder_env_cursor.as_deref_mut(),
213                    )
214                })
215                .collect::<Result<Vec<_>, _>>()?,
216            span: *span,
217        }),
218        Expr::StructLit { name, fields, span } => Ok(Expr::StructLit {
219            name: name.clone(),
220            fields: fields
221                .iter()
222                .map(|field| {
223                    Ok(NamedExprField {
224                        name: field.name.clone(),
225                        value: expand_runtime_expr(
226                            program,
227                            &field.value,
228                            function_table,
229                            runtime_scope,
230                            template_env,
231                            builder_env_cursor.as_deref_mut(),
232                        )?,
233                    })
234                })
235                .collect::<Result<Vec<_>, MetaEvalError>>()?,
236            span: *span,
237        }),
238        Expr::Block(block) => Ok(Expr::Block(expand_runtime_block(
239            program,
240            block,
241            function_table,
242            runtime_scope,
243            template_env,
244            builder_env_cursor.as_deref_mut(),
245        )?)),
246        Expr::Splice(meta_expr) => {
247            let mut env = template_env.clone();
248            let value = eval_meta_expr(
249                program,
250                meta_expr,
251                &mut env,
252                function_table,
253                runtime_scope,
254                builder_env_cursor.as_deref_mut(),
255            )?;
256            runtime_splice_value_to_expr(value)
257        }
258    }
259}
260
261pub(super) fn expand_runtime_call_arg(
262    program: &Program,
263    arg: &CallArg,
264    function_table: &BTreeMap<String, &MetaFunctionDecl>,
265    runtime_scope: &RuntimeScope,
266    template_env: &BTreeMap<String, MetaValue>,
267    builder_env_cursor: Option<&mut BuilderEnvCursor>,
268) -> Result<CallArg, MetaEvalError> {
269    match arg {
270        CallArg::Expr(expr) => Ok(CallArg::Expr(expand_runtime_expr(
271            program,
272            expr,
273            function_table,
274            runtime_scope,
275            template_env,
276            builder_env_cursor,
277        )?)),
278        CallArg::Spread(expr) => Ok(CallArg::Spread(expand_runtime_expr(
279            program,
280            expr,
281            function_table,
282            runtime_scope,
283            template_env,
284            builder_env_cursor,
285        )?)),
286        CallArg::Type(ty) => Ok(CallArg::Type(ty.clone())),
287    }
288}
289
290#[derive(Debug)]
291pub(super) struct MetaBlockOutcome {
292    pub(super) last_value: Option<MetaValue>,
293    pub(super) emitted_runtime_stmts: Vec<Stmt>,
294}
295
296pub(super) fn eval_meta_block(
297    program: &Program,
298    block: &MetaBlock,
299    env: &mut BTreeMap<String, MetaValue>,
300    function_table: &BTreeMap<String, &MetaFunctionDecl>,
301    emit_runtime_stmts: bool,
302    runtime_scope: &RuntimeScope,
303    mut builder_env_cursor: Option<&mut BuilderEnvCursor>,
304) -> Result<MetaBlockOutcome, MetaEvalError> {
305    let mut last_value = None;
306    let mut emitted_runtime_stmts = Vec::new();
307
308    for statement in &block.statements {
309        let step = eval_meta_stmt(
310            program,
311            statement,
312            env,
313            function_table,
314            emit_runtime_stmts,
315            runtime_scope,
316            builder_env_cursor.as_deref_mut(),
317        )?;
318        if let Some(value) = step.last_value {
319            last_value = Some(value);
320        }
321        emitted_runtime_stmts.extend(step.emitted_runtime_stmts);
322    }
323
324    Ok(MetaBlockOutcome {
325        last_value,
326        emitted_runtime_stmts,
327    })
328}
329
330pub(super) fn eval_meta_stmt(
331    program: &Program,
332    statement: &MetaStmt,
333    env: &mut BTreeMap<String, MetaValue>,
334    function_table: &BTreeMap<String, &MetaFunctionDecl>,
335    emit_runtime_stmts: bool,
336    runtime_scope: &RuntimeScope,
337    mut builder_env_cursor: Option<&mut BuilderEnvCursor>,
338) -> Result<MetaBlockOutcome, MetaEvalError> {
339    let semantic_stmt_entry = builder_env_cursor
340        .as_deref_mut()
341        .and_then(|cursor| cursor.next_stmt_entry().cloned());
342    match statement {
343        MetaStmt::Let { name, value } => {
344            let value = eval_meta_expr(
345                program,
346                value,
347                env,
348                function_table,
349                runtime_scope,
350                builder_env_cursor.as_deref_mut(),
351            )?;
352            env.insert(name.clone(), value);
353            Ok(MetaBlockOutcome {
354                last_value: None,
355                emitted_runtime_stmts: Vec::new(),
356            })
357        }
358        MetaStmt::Expr { expr } => {
359            let value = eval_meta_expr(
360                program,
361                expr,
362                env,
363                function_table,
364                runtime_scope,
365                builder_env_cursor.as_deref_mut(),
366            )?;
367            let emitted_runtime_stmts = if emit_runtime_stmts {
368                runtime_stmts_from_meta_value(&value)
369            } else {
370                Vec::new()
371            };
372            Ok(MetaBlockOutcome {
373                last_value: Some(value),
374                emitted_runtime_stmts,
375            })
376        }
377        MetaStmt::Match { value, arms } => {
378            let matched = eval_meta_expr(
379                program,
380                value,
381                env,
382                function_table,
383                runtime_scope,
384                builder_env_cursor.as_deref_mut(),
385            )?;
386
387            if semantic_stmt_entry
388                .as_ref()
389                .and_then(|entry| entry.match_info.as_ref())
390                .is_some_and(match_requires_code_but_cannot_have_code)
391            {
392                return Err(MetaEvalError::MatchExpectedCode);
393            }
394            if semantic_stmt_entry
395                .as_ref()
396                .and_then(|entry| entry.match_info.as_ref())
397                .is_some_and(match_cannot_succeed)
398            {
399                return Err(MetaEvalError::NoMatchArm);
400            }
401
402            for arm in arms {
403                let mut captures = BTreeMap::new();
404                let arm_matches = match &arm.pattern {
405                    MetaMatchPattern::Wildcard => true,
406                    MetaMatchPattern::Int(value) => {
407                        matches!(matched, MetaValue::Int(actual) if actual == *value)
408                    }
409                    MetaMatchPattern::Bool(value) => {
410                        matches!(matched, MetaValue::Bool(actual) if actual == *value)
411                    }
412                    MetaMatchPattern::String(value) => {
413                        matches!(&matched, MetaValue::String(actual) if actual == value)
414                    }
415                    MetaMatchPattern::Quote(pattern) => {
416                        let code = coerce_value_to_code_for_match(matched.clone())
417                            .ok_or(MetaEvalError::MatchExpectedCode)?;
418                        match_quote_code(pattern, &code, &mut captures)
419                    }
420                    MetaMatchPattern::Type(pattern_ty) => match &matched {
421                        MetaValue::Type(target_ty) => {
422                            match_quote_pattern_type(pattern_ty, target_ty)
423                        }
424                        _ => {
425                            let code = coerce_value_to_code_for_match(matched.clone())
426                                .ok_or(MetaEvalError::MatchExpectedCode)?;
427                            matches!(&code, CodeValue::Type(target_ty) if match_quote_pattern_type(pattern_ty, target_ty))
428                        }
429                    },
430                };
431                if !arm_matches {
432                    continue;
433                }
434
435                let mut arm_env = env.clone();
436                arm_env.extend(captures);
437                let arm_value = eval_meta_expr(
438                    program,
439                    &arm.result,
440                    &mut arm_env,
441                    function_table,
442                    runtime_scope,
443                    builder_env_cursor.as_deref_mut(),
444                )?;
445                let arm_value = coerce_meta_value_to_staged_meta_type(
446                    arm_value,
447                    semantic_stmt_entry
448                        .as_ref()
449                        .and_then(|entry| entry.match_info.as_ref())
450                        .and_then(|info| info.converged_arm_type.as_ref()),
451                )?;
452                let emitted_runtime_stmts = if emit_runtime_stmts {
453                    runtime_stmts_from_meta_value(&arm_value)
454                } else {
455                    Vec::new()
456                };
457                return Ok(MetaBlockOutcome {
458                    last_value: Some(arm_value),
459                    emitted_runtime_stmts,
460                });
461            }
462
463            Err(MetaEvalError::NoMatchArm)
464        }
465        MetaStmt::Meta { body } => eval_meta_block(
466            program,
467            body,
468            env,
469            function_table,
470            emit_runtime_stmts,
471            runtime_scope,
472            builder_env_cursor,
473        ),
474    }
475}
476
477pub(super) fn runtime_stmts_from_meta_value(value: &MetaValue) -> Vec<Stmt> {
478    match value {
479        MetaValue::Expr { expr, .. } => vec![Stmt::Expr {
480            expr: expr.clone(),
481            has_semi: true,
482        }],
483        _ => Vec::new(),
484    }
485}
486
487pub(super) fn eval_meta_expr(
488    program: &Program,
489    expr: &MetaExpr,
490    env: &mut BTreeMap<String, MetaValue>,
491    function_table: &BTreeMap<String, &MetaFunctionDecl>,
492    runtime_scope: &RuntimeScope,
493    mut builder_env_cursor: Option<&mut BuilderEnvCursor>,
494) -> Result<MetaValue, MetaEvalError> {
495    let semantic_entry = builder_env_cursor
496        .as_deref_mut()
497        .and_then(|cursor| cursor.next_meta_entry().cloned());
498    match expr {
499        MetaExpr::Ident(name) => {
500            if let Some(value) = env.get(name) {
501                return Ok(value.clone());
502            }
503            if runtime_scope.contains_key(name) {
504                return Err(MetaEvalError::RuntimeBindingUnavailable { name: name.clone() });
505            }
506            if let Some(kind) = semantic_entry
507                .as_ref()
508                .and_then(|entry| entry.resolved_name.as_ref())
509                .and_then(resolved_name_kind)
510            {
511                return Err(MetaEvalError::KnownNameNotMetaValue {
512                    name: name.clone(),
513                    kind,
514                });
515            }
516            Err(MetaEvalError::UnknownBinding { name: name.clone() })
517        }
518        MetaExpr::Int(value) => Ok(MetaValue::Int(*value)),
519        MetaExpr::Bool(value) => Ok(MetaValue::Bool(*value)),
520        MetaExpr::String(value) => Ok(MetaValue::String(value.clone())),
521        MetaExpr::Type(ty) => Ok(MetaValue::Type(ty.clone())),
522        MetaExpr::Call { callee, args } => {
523            let staged_call_info = semantic_entry
524                .as_ref()
525                .and_then(|entry| entry.call_info.as_ref());
526            let staged_builtin = staged_call_info.and_then(|info| info.builtin);
527            let resolved_callee = staged_builtin
528                .map(builtin_meta_call_name)
529                .or_else(|| staged_call_info.map(|info| info.callee.as_str()))
530                .unwrap_or(callee.as_str());
531            if let Some(builtin) = staged_builtin {
532                if let Some((expected, actual)) =
533                    staged_call_info.and_then(staged_builtin_arg_mismatch)
534                {
535                    return Err(MetaEvalError::TypeMismatch {
536                        expected: expected.to_owned(),
537                        actual,
538                    });
539                }
540                if let Some(value) = eval_builtin_meta_call(
541                    program,
542                    builtin_meta_call_name(builtin),
543                    args,
544                    env,
545                    function_table,
546                    runtime_scope,
547                    builder_env_cursor.as_deref_mut(),
548                )? {
549                    return coerce_meta_value_to_staged_meta_type(
550                        value,
551                        staged_call_info.and_then(|info| info.result_type.as_ref()),
552                    );
553                }
554                return Err(MetaEvalError::ArityMismatch {
555                    name: builtin_meta_call_name(builtin).to_owned(),
556                    expected: builtin_meta_call_arity(builtin),
557                    actual: args.len(),
558                });
559            }
560            if let Some(value) = eval_builtin_meta_call(
561                program,
562                resolved_callee,
563                args,
564                env,
565                function_table,
566                runtime_scope,
567                builder_env_cursor.as_deref_mut(),
568            )? {
569                return coerce_meta_value_to_staged_meta_type(
570                    value,
571                    staged_call_info.and_then(|info| info.result_type.as_ref()),
572                );
573            }
574            let function = function_table
575                .get(resolved_callee)
576                .copied()
577                .ok_or_else(|| MetaEvalError::UnknownMetaFunction {
578                    name: resolved_callee.to_owned(),
579                })?;
580
581            if args.len() != function.params.len() {
582                return Err(MetaEvalError::ArityMismatch {
583                    name: resolved_callee.to_owned(),
584                    expected: function.params.len(),
585                    actual: args.len(),
586                });
587            }
588
589            let mut call_env = BTreeMap::new();
590            for (param, arg) in function.params.iter().zip(args.iter()) {
591                let arg_value = eval_meta_expr(
592                    program,
593                    arg,
594                    env,
595                    function_table,
596                    runtime_scope,
597                    builder_env_cursor.as_deref_mut(),
598                )?;
599                let arg_value = coerce_meta_value_to_type(program, arg_value, &param.ty)?;
600                call_env.insert(param.name.clone(), arg_value);
601            }
602
603            let outcome = eval_meta_block(
604                program,
605                &function.body,
606                &mut call_env,
607                function_table,
608                false,
609                runtime_scope,
610                None,
611            )?;
612            let value = outcome
613                .last_value
614                .ok_or_else(|| MetaEvalError::MissingReturnValue {
615                    name: function.name.clone(),
616                })?;
617            let value = coerce_meta_value_to_staged_meta_type(
618                value,
619                staged_call_info.and_then(|info| info.result_type.as_ref()),
620            )?;
621            coerce_meta_value_to_type(program, value, &function.return_type)
622        }
623        MetaExpr::BuildExpr(expr) => {
624            let semantic_locals = builder_env_cursor
625                .as_deref_mut()
626                .and_then(|cursor| cursor.next_env().map(builder_bindings_to_locals));
627            let expr = expand_builder_expr(
628                program,
629                expr,
630                env,
631                function_table,
632                runtime_scope,
633                builder_env_cursor.as_deref_mut(),
634            )?;
635            let locals = expr_builder_locals(env, runtime_scope, semantic_locals.as_deref());
636            let (expr, ty) =
637                elaborate_meta_generated_expr(program, &expr, &locals).map_err(|error| {
638                    MetaEvalError::Elab {
639                        message: semantic_entry
640                            .as_ref()
641                            .and_then(|entry| entry.meta_type.as_ref())
642                            .and_then(expected_builder_type)
643                            .map(|expected| format!("while building `Expr[{expected}]`: {}", error))
644                            .unwrap_or_else(|| error.to_string()),
645                    }
646                })?;
647            Ok(MetaValue::Expr { expr, ty })
648        }
649        MetaExpr::Quote(quoted_expr) => Ok(MetaValue::Code(match quoted_expr.as_ref() {
650            QuotedCode::Expr(expr) => CodeValue::Expr(expand_quoted_expr(
651                program,
652                expr,
653                env,
654                function_table,
655                runtime_scope,
656                builder_env_cursor.as_deref_mut(),
657            )?),
658            QuotedCode::Type(ty) => CodeValue::Type(ty.clone()),
659        })),
660        MetaExpr::Splice(value) => {
661            let value = eval_meta_expr(
662                program,
663                value,
664                env,
665                function_table,
666                runtime_scope,
667                builder_env_cursor,
668            )?;
669            promote_splice_value(value)
670        }
671    }
672}
673
674pub(super) fn eval_builtin_meta_call(
675    program: &Program,
676    callee: &str,
677    args: &[MetaExpr],
678    env: &mut BTreeMap<String, MetaValue>,
679    function_table: &BTreeMap<String, &MetaFunctionDecl>,
680    runtime_scope: &RuntimeScope,
681    mut builder_env_cursor: Option<&mut BuilderEnvCursor>,
682) -> Result<Option<MetaValue>, MetaEvalError> {
683    let Some(spec) = builtin_spec_by_name(callee) else {
684        return Ok(None);
685    };
686    let builtin_name = spec.name;
687    match spec.kind {
688        crate::semantic::BuiltinMetaCallKind::StringConcat => {
689            let [left_expr, right_expr] = args else {
690                return Ok(None);
691            };
692            let left = eval_meta_string_arg(
693                program,
694                left_expr,
695                env,
696                function_table,
697                runtime_scope,
698                builder_env_cursor.as_deref_mut(),
699            )?;
700            let right = eval_meta_string_arg(
701                program,
702                right_expr,
703                env,
704                function_table,
705                runtime_scope,
706                builder_env_cursor.as_deref_mut(),
707            )?;
708            Ok(Some(MetaValue::String(string_concat(&left, &right))))
709        }
710        crate::semantic::BuiltinMetaCallKind::StringStartsWith => {
711            let [value_expr, prefix_expr] = args else {
712                return Ok(None);
713            };
714            let value = eval_meta_string_arg(
715                program,
716                value_expr,
717                env,
718                function_table,
719                runtime_scope,
720                builder_env_cursor.as_deref_mut(),
721            )?;
722            let prefix = eval_meta_string_arg(
723                program,
724                prefix_expr,
725                env,
726                function_table,
727                runtime_scope,
728                builder_env_cursor.as_deref_mut(),
729            )?;
730            Ok(Some(MetaValue::Bool(string_starts_with(&value, &prefix))))
731        }
732        crate::semantic::BuiltinMetaCallKind::StringDrop => {
733            let [value_expr, count_expr] = args else {
734                return Ok(None);
735            };
736            let value = eval_meta_string_arg(
737                program,
738                value_expr,
739                env,
740                function_table,
741                runtime_scope,
742                builder_env_cursor.as_deref_mut(),
743            )?;
744            let count = eval_meta_int_arg(
745                program,
746                count_expr,
747                env,
748                function_table,
749                runtime_scope,
750                builder_env_cursor.as_deref_mut(),
751            )?;
752            let count =
753                usize::try_from(count).expect("meta string drop count must be non-negative");
754            Ok(Some(MetaValue::String(string_drop(&value, count))))
755        }
756        crate::semantic::BuiltinMetaCallKind::StringTake => {
757            let [value_expr, count_expr] = args else {
758                return Ok(None);
759            };
760            let value = eval_meta_string_arg(
761                program,
762                value_expr,
763                env,
764                function_table,
765                runtime_scope,
766                builder_env_cursor.as_deref_mut(),
767            )?;
768            let count = eval_meta_int_arg(
769                program,
770                count_expr,
771                env,
772                function_table,
773                runtime_scope,
774                builder_env_cursor.as_deref_mut(),
775            )?;
776            let count =
777                usize::try_from(count).expect("meta string take count must be non-negative");
778            Ok(Some(MetaValue::String(string_take(&value, count))))
779        }
780        crate::semantic::BuiltinMetaCallKind::TupleIsEmpty => {
781            let [tuple_expr] = args else {
782                return Ok(None);
783            };
784            let tuple = eval_meta_expr_arg(
785                program,
786                tuple_expr,
787                env,
788                function_table,
789                runtime_scope,
790                builder_env_cursor.as_deref_mut(),
791            )?;
792            let Some(is_empty) = tuple_expr_is_empty(&tuple) else {
793                return Err(MetaEvalError::ExpectedTupleCode {
794                    builtin: builtin_name,
795                });
796            };
797            Ok(Some(MetaValue::Bool(is_empty)))
798        }
799        crate::semantic::BuiltinMetaCallKind::TupleHead => {
800            let [tuple_expr] = args else {
801                return Ok(None);
802            };
803            let (tuple, tuple_ty) = eval_meta_typed_expr_arg(
804                program,
805                tuple_expr,
806                env,
807                function_table,
808                runtime_scope,
809                builder_env_cursor.as_deref_mut(),
810            )?;
811            let (head, ty) = match tuple_expr_head(tuple, &tuple_ty) {
812                Ok(value) => value,
813                Err(TupleExprBuiltinError::NotTupleExpr) => {
814                    return Err(MetaEvalError::ExpectedTupleCode {
815                        builtin: builtin_name,
816                    });
817                }
818                Err(TupleExprBuiltinError::EmptyTuple) => return Err(MetaEvalError::NoMatchArm),
819            };
820            Ok(Some(MetaValue::Expr { expr: head, ty }))
821        }
822        crate::semantic::BuiltinMetaCallKind::TupleTail => {
823            let [tuple_expr] = args else {
824                return Ok(None);
825            };
826            let (tuple, tuple_ty) = eval_meta_typed_expr_arg(
827                program,
828                tuple_expr,
829                env,
830                function_table,
831                runtime_scope,
832                builder_env_cursor.as_deref_mut(),
833            )?;
834            let Some((tail, ty)) = tuple_expr_tail(tuple, &tuple_ty) else {
835                return Err(MetaEvalError::ExpectedTupleCode {
836                    builtin: builtin_name,
837                });
838            };
839            Ok(Some(MetaValue::Expr { expr: tail, ty }))
840        }
841        crate::semantic::BuiltinMetaCallKind::TupleTypeHead => {
842            let [tuple_expr] = args else {
843                return Ok(None);
844            };
845            let tuple = eval_meta_type_arg(
846                program,
847                tuple_expr,
848                env,
849                function_table,
850                runtime_scope,
851                builder_env_cursor.as_deref_mut(),
852            )?;
853            let head = match tuple_type_head_builtin(&tuple) {
854                Ok(value) => value,
855                Err(TupleTypeBuiltinError::EmptyTuple) => return Err(MetaEvalError::NoMatchArm),
856                Err(TupleTypeBuiltinError::NotTupleType) => {
857                    return Err(MetaEvalError::ExpectedTupleType {
858                        builtin: builtin_name,
859                    });
860                }
861            };
862            Ok(Some(MetaValue::Type(head)))
863        }
864        crate::semantic::BuiltinMetaCallKind::TupleTypeTail => {
865            let [tuple_expr] = args else {
866                return Ok(None);
867            };
868            let tuple = eval_meta_type_arg(
869                program,
870                tuple_expr,
871                env,
872                function_table,
873                runtime_scope,
874                builder_env_cursor.as_deref_mut(),
875            )?;
876            let tail = match tuple_type_tail_builtin(&tuple) {
877                Ok(value) => value,
878                Err(TupleTypeBuiltinError::NotTupleType) => {
879                    return Err(MetaEvalError::ExpectedTupleType {
880                        builtin: builtin_name,
881                    });
882                }
883                Err(TupleTypeBuiltinError::EmptyTuple) => unreachable!(),
884            };
885            Ok(Some(MetaValue::Type(tail)))
886        }
887    }
888}