kdl_script/
parse.rs

1//! Parsing and AST types.
2//!
3//! The entrypoint is to invoke [`parse_kdl_script`][] which is implicitly
4//! handled by [`Compiler::compile_path`][] or [`Compiler::compile_string`][]
5//! and will produce a [`ParsedProgram`][].
6//!
7//! Things like name resolution are handled by the [type checker](`crate::types`).
8
9use std::sync::Arc;
10
11use kdl::{KdlDocument, KdlEntry, KdlNode};
12use miette::{Diagnostic, NamedSource, SourceSpan};
13use nom::branch::alt;
14use nom::bytes::complete::tag;
15use nom::character::complete::{alpha1, alphanumeric1};
16use nom::combinator::{all_consuming, cut, opt, recognize};
17use nom::error::{context, VerboseError};
18use nom::multi::{many0, many0_count, separated_list1};
19use nom::sequence::{delimited, pair, preceded, separated_pair};
20use nom::{Finish, IResult};
21use thiserror::Error;
22use tracing::trace;
23
24use crate::spanned::Spanned;
25use crate::types::{PrimitiveTy, PRIMITIVES};
26use crate::{Compiler, Result};
27
28pub type StableMap<K, V> = linked_hash_map::LinkedHashMap<K, V>;
29
30/// A string that may refer to another item like a type of function
31#[derive(Debug, Clone)]
32pub struct Ident {
33    /// true if the ident was actually "_" and we made up a name.
34    ///
35    /// Languages which have anonymous/positional fields may choose
36    /// to emit this field as such, instead of using `val`.
37    pub was_blank: bool,
38    /// the string to use for the ident
39    pub val: Spanned<String>,
40}
41
42impl std::cmp::PartialEq for Ident {
43    fn eq(&self, other: &Self) -> bool {
44        self.val == other.val
45    }
46}
47impl std::cmp::PartialEq<String> for Ident {
48    fn eq(&self, other: &String) -> bool {
49        self.val.as_str() == other
50    }
51}
52impl std::cmp::PartialEq<str> for Ident {
53    fn eq(&self, other: &str) -> bool {
54        self.val.as_str() == other
55    }
56}
57impl std::cmp::Eq for Ident {}
58impl std::cmp::PartialOrd for Ident {
59    fn partial_cmp(&self, other: &Self) -> Option<std::cmp::Ordering> {
60        Some(self.cmp(other))
61    }
62}
63impl std::cmp::Ord for Ident {
64    fn cmp(&self, other: &Self) -> std::cmp::Ordering {
65        self.val.cmp(&other.val)
66    }
67}
68impl std::hash::Hash for Ident {
69    fn hash<H: std::hash::Hasher>(&self, state: &mut H) {
70        self.val.hash(state);
71    }
72}
73impl std::borrow::Borrow<str> for Ident {
74    fn borrow(&self) -> &str {
75        &self.val
76    }
77}
78
79impl std::ops::Deref for Ident {
80    type Target = Spanned<String>;
81    fn deref(&self) -> &Self::Target {
82        &self.val
83    }
84}
85impl From<String> for Ident {
86    fn from(val: String) -> Self {
87        Self::new(Spanned::from(val))
88    }
89}
90
91impl Ident {
92    fn new(val: Spanned<String>) -> Self {
93        Self {
94            val,
95            was_blank: false,
96        }
97    }
98    fn with_span(val: String, span: SourceSpan) -> Self {
99        Self {
100            val: Spanned::new(val, span),
101            was_blank: false,
102        }
103    }
104}
105
106impl std::fmt::Display for Ident {
107    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
108        self.val.fmt(f)
109    }
110}
111
112/// An error that occured during parsing
113#[derive(Debug, Error, Diagnostic)]
114#[error("{message}")]
115pub struct KdlScriptParseError {
116    pub message: String,
117    #[source_code]
118    pub src: Arc<NamedSource>,
119    #[label]
120    pub span: SourceSpan,
121    #[help]
122    pub help: Option<String>,
123}
124
125/// A Parsed KDLScript program
126#[derive(Debug, Clone)]
127pub struct ParsedProgram {
128    /// The type definitions
129    pub tys: StableMap<Ident, TyDecl>,
130    /// The function definitions
131    pub funcs: StableMap<Ident, FuncDecl>,
132    /// Where in funcs builtins like `+` start (if at all).
133    pub builtin_funcs_start: usize,
134}
135
136/// A Type declaration
137#[derive(Debug, Clone)]
138pub enum TyDecl {
139    /// A Struct
140    Struct(StructDecl),
141    /// An untagged union
142    Union(UnionDecl),
143    /// A c-style enum
144    Enum(EnumDecl),
145    /// A tagged union (rust-style enum)
146    Tagged(TaggedDecl),
147    /// A transparent type alias
148    Alias(AliasDecl),
149    /// A type pun
150    Pun(PunDecl),
151}
152
153/// A type "name" (which may be structural like `[u32; 4]`).
154///
155/// It's like an ident but, for types -- a tydent!
156#[derive(Debug, Clone)]
157pub enum Tydent {
158    /// A named type (the type checker will resolve this)
159    Name(Ident),
160    /// A fixed length array
161    Array(Box<Spanned<Tydent>>, u64),
162    /// A by-reference type
163    Ref(Box<Spanned<Tydent>>),
164    /// The empty tuple -- `()`
165    Empty,
166}
167
168/// An attribute that can be hung off a function or type.
169///
170/// Currently stubbed out, not really used. Potential uses:
171///
172/// * setting the backing type on an Enum's tag
173/// * packed(N)
174/// * align(N)
175/// * passthrough attrs to underlying language
176///
177/// Probably this should be broken up so that you can't "pack"
178/// a function or other such nonsense...
179#[derive(Debug, Clone, PartialEq, Eq, Hash)]
180pub enum Attr {
181    /// The type should be packed
182    Packed(AttrPacked),
183    /// The type should be aligned
184    Align(AttrAligned),
185    /// The type should use this repr
186    Repr(AttrRepr),
187    /// Pass this attribute through to the target language
188    Passthrough(AttrPassthrough),
189}
190
191/// An attribute declaring this type should be packed (remove padding/align).
192///
193/// FIXME? add support for an integer argument for the max align of a
194/// field? Without one the default is 1. I never see packed(2) or whatever
195/// so I've never seriously thought through the implications...
196///
197/// @packed (N?)
198#[derive(Debug, Clone, PartialEq, Eq, Hash)]
199pub struct AttrPacked {}
200
201/// An attribute to passthrough to the target language.
202///
203/// @ "whatever you want buddy"
204#[derive(Debug, Clone, PartialEq, Eq, Hash)]
205pub struct AttrAligned {
206    pub align: IntExpr,
207}
208
209#[derive(Debug, Clone, PartialEq, Eq, Hash)]
210pub struct AttrRepr {
211    pub reprs: Vec<Repr>,
212}
213
214#[derive(Debug, Clone, PartialEq, Eq, Hash)]
215pub enum Repr {
216    Primitive(PrimitiveTy),
217    Lang(LangRepr),
218    Transparent,
219}
220
221#[derive(Debug, Copy, Clone, PartialEq, Eq, PartialOrd, Ord, Hash, serde::Serialize)]
222pub enum LangRepr {
223    Rust,
224    C,
225}
226impl std::fmt::Display for LangRepr {
227    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
228        let s = match self {
229            LangRepr::Rust => "rust",
230            LangRepr::C => "c",
231        };
232        s.fmt(f)
233    }
234}
235impl std::str::FromStr for LangRepr {
236    type Err = String;
237    fn from_str(s: &str) -> std::result::Result<Self, Self::Err> {
238        match s {
239            "rust" => Ok(Self::Rust),
240            "c" => Ok(Self::C),
241            _ => Err(format!("unknown lang repr {s}")),
242        }
243    }
244}
245
246/// An attribute to passthrough to the target language.
247///
248/// @ "whatever you want buddy"
249#[derive(Debug, Clone, PartialEq, Eq, Hash)]
250pub struct AttrPassthrough(pub Spanned<String>);
251
252/// A struct decl.
253///
254/// Field names may be positional by naming them underscore (`_`).
255/// For languages like Rust, if all fields are declared like this
256/// it should be lowered to a tuple-struct. Otherwise positional
257/// fields will be autonamed something like field0, field1, ...
258#[derive(Debug, Clone)]
259pub struct StructDecl {
260    /// Name of the struct
261    pub name: Ident,
262    /// Fields
263    pub fields: Vec<TypedVar>,
264    /// Attributes
265    pub attrs: Vec<Attr>,
266}
267
268/// An untagged union decl.
269///
270/// Variant names may be positional by naming them underscore (`_`).
271/// Positional variants will be autonamed something like Case0, Case1, ...
272#[derive(Debug, Clone)]
273pub struct UnionDecl {
274    /// Name of the union
275    pub name: Ident,
276    /// Fields (variants)
277    pub fields: Vec<TypedVar>,
278    pub attrs: Vec<Attr>,
279}
280
281/// A c-like enum decl.
282///
283/// Variant names may be positional by naming them underscore (`_`).
284/// Positional variants will be autonamed something like Case0, Case1, ...
285#[derive(Debug, Clone)]
286pub struct EnumDecl {
287    pub name: Ident,
288    pub variants: Vec<EnumVariant>,
289    pub attrs: Vec<Attr>,
290}
291
292/// A variant of an [`EnumDecl`].
293#[derive(Debug, Clone)]
294pub struct EnumVariant {
295    pub name: Ident,
296    /// Optional value this case is required to have
297    /// in its underlying integer representation.
298    pub val: Option<IntExpr>,
299}
300
301/// A tagged union (rust-like enum).
302///
303/// Variant names may be positional by naming them underscore (`_`).
304/// Positional variants will be autonamed something like Case0, Case1, ...
305#[derive(Debug, Clone)]
306pub struct TaggedDecl {
307    pub name: Ident,
308    pub variants: Vec<TaggedVariant>,
309    pub attrs: Vec<Attr>,
310}
311
312/// A variant of a [`TaggedDecl`][].
313#[derive(Debug, Clone)]
314pub struct TaggedVariant {
315    pub name: Ident,
316    pub fields: Option<Vec<TypedVar>>,
317}
318
319/// A type pun between different languages.
320#[derive(Debug, Clone)]
321pub struct PunDecl {
322    pub name: Ident,
323    pub blocks: Vec<PunBlock>,
324    pub attrs: Vec<Attr>,
325}
326
327/// A block for a [`PunDecl`][].
328#[derive(Debug, Clone)]
329pub struct PunBlock {
330    pub selector: PunSelector,
331    pub decl: TyDecl,
332}
333
334/// A selector for a [`PunBlock`][]
335#[derive(Debug, Clone, PartialEq, Eq, Hash)]
336pub enum PunSelector {
337    /// Selector applies if any of the following apply.
338    Any(Vec<PunSelector>),
339    /// Selector applies if all of the following apply.
340    All(Vec<PunSelector>),
341    /// Selector applies if the [`PunEnv::lang`] is the following.
342    Lang(Spanned<String>),
343    /// Selector always applies (default fallback).
344    Default,
345}
346
347/// The environment required to resolve a [`PunSelector`][].
348#[derive(Debug, Clone)]
349pub struct PunEnv {
350    /// The target language
351    pub lang: String,
352    // compiler: String,
353    // os: String,
354    // cpu: String,
355}
356
357impl PunSelector {
358    /// Check if this [`PunSelector`][] matches the given [`PunEnv`][].
359    pub fn matches(&self, env: &PunEnv) -> bool {
360        use PunSelector::*;
361        match self {
362            Any(args) => args.iter().any(|s| s.matches(env)),
363            All(args) => args.iter().all(|s| s.matches(env)),
364            Lang(lang) => env.lang == **lang,
365            Default => true,
366        }
367    }
368}
369
370/// A transparent type alias.
371#[derive(Debug, Clone)]
372pub struct AliasDecl {
373    pub name: Ident,
374    pub alias: Spanned<Tydent>,
375    pub attrs: Vec<Attr>,
376}
377
378/// A (name, type) pair that occurs in many places like field/arg decls.
379#[derive(Debug, Clone)]
380pub struct TypedVar {
381    pub name: Option<Ident>,
382    pub ty: Spanned<Tydent>,
383}
384
385/// A function declaration
386#[derive(Debug, Clone)]
387pub struct FuncDecl {
388    pub name: Ident,
389    pub inputs: Vec<TypedVar>,
390    pub outputs: Vec<TypedVar>,
391    pub attrs: Vec<Attr>,
392    #[cfg(feature = "eval")]
393    pub body: Vec<Stmt>,
394}
395
396/// The parser, used to hold onto some global state for things like diagnostic.
397struct Parser<'a> {
398    // comp: &'a mut Compiler,
399    src: Arc<NamedSource>,
400    ast: &'a KdlDocument,
401}
402
403/// Parse a KdlScript program!
404pub fn parse_kdl_script(
405    _comp: &mut Compiler,
406    src: Arc<NamedSource>,
407    ast: &KdlDocument,
408) -> Result<ParsedProgram> {
409    let mut parser = Parser { src, ast };
410    parser.parse()
411}
412
413impl Parser<'_> {
414    /// Parse a KdlScript program!
415    fn parse(&mut self) -> Result<ParsedProgram> {
416        trace!("parsing");
417
418        let mut program = self.parse_module(self.ast)?;
419        #[cfg(feature = "eval")]
420        program.add_builtin_funcs()?;
421
422        Ok(program)
423    }
424
425    /// Parse a "module" which is either the entire program or the contents of a [`PunBlock`][].
426    fn parse_module(&mut self, doc: &KdlDocument) -> Result<ParsedProgram> {
427        let mut funcs = StableMap::new();
428        let mut tys = StableMap::new();
429
430        let mut cur_attrs = vec![];
431        for node in doc.nodes() {
432            let name = node.name().value();
433
434            // If it's an attribute, gather it up to be given to a "real" item
435            if name.starts_with('@') {
436                cur_attrs.push(self.attr(node)?);
437                continue;
438            }
439
440            // Ok it's a real item, grab all the attributes, they belong to it
441            let attrs = std::mem::take(&mut cur_attrs);
442
443            // Now parse the various kinds of top-level items
444            match name {
445                "fn" => {
446                    let func = self.func_decl(node, attrs)?;
447                    funcs.insert(func.name.clone(), func);
448                }
449                "struct" => {
450                    let ty = self.struct_decl(node, attrs)?;
451                    let old = tys.insert(ty.name.clone(), TyDecl::Struct(ty));
452                    assert!(old.is_none(), "duplicate type def");
453                }
454                "union" => {
455                    let ty = self.union_decl(node, attrs)?;
456                    let old = tys.insert(ty.name.clone(), TyDecl::Union(ty));
457                    assert!(old.is_none(), "duplicate type def");
458                }
459                "enum" => {
460                    let ty = self.enum_decl(node, attrs)?;
461                    let old = tys.insert(ty.name.clone(), TyDecl::Enum(ty));
462                    assert!(old.is_none(), "duplicate type def");
463                }
464                "tagged" => {
465                    let ty = self.tagged_decl(node, attrs)?;
466                    let old = tys.insert(ty.name.clone(), TyDecl::Tagged(ty));
467                    assert!(old.is_none(), "duplicate type def");
468                }
469                "alias" => {
470                    let ty = self.alias_decl(node, attrs)?;
471                    let old = tys.insert(ty.name.clone(), TyDecl::Alias(ty));
472                    assert!(old.is_none(), "duplicate type def");
473                }
474                "pun" => {
475                    let ty = self.pun_decl(node, attrs)?;
476                    let old = tys.insert(ty.name.clone(), TyDecl::Pun(ty));
477                    assert!(old.is_none(), "duplicate type def");
478                }
479                x => {
480                    return Err(KdlScriptParseError {
481                        message: format!("I don't know what a '{x}' is"),
482                        src: self.src.clone(),
483                        span: *node.name().span(),
484                        help: None,
485                    })?;
486                }
487            }
488        }
489
490        // Anything added after this point is a builtin!
491        let builtin_funcs_start = funcs.len();
492        Ok(ParsedProgram {
493            tys,
494            funcs,
495            builtin_funcs_start,
496        })
497    }
498
499    /// Parse a `struct` node.
500    fn struct_decl(&mut self, node: &KdlNode, attrs: Vec<Attr>) -> Result<StructDecl> {
501        trace!("struct decl");
502        let name = self.one_string(node, "type name")?;
503        let name = self.ident(name)?;
504        let fields = self.typed_var_children(node)?;
505
506        Ok(StructDecl {
507            name,
508            fields,
509            attrs,
510        })
511    }
512
513    /// Parse a `union` node.
514    fn union_decl(&mut self, node: &KdlNode, attrs: Vec<Attr>) -> Result<UnionDecl> {
515        trace!("union decl");
516        let name = self.one_string(node, "type name")?;
517        let name = self.ident(name)?;
518        let fields = self.typed_var_children(node)?;
519
520        Ok(UnionDecl {
521            name,
522            fields,
523            attrs,
524        })
525    }
526
527    /// Parse an `enum` node.
528    fn enum_decl(&mut self, node: &KdlNode, attrs: Vec<Attr>) -> Result<EnumDecl> {
529        trace!("enum decl");
530        let name = self.one_string(node, "type name")?;
531        let name = self.ident(name)?;
532        let variants = self.enum_variant_children(node)?;
533
534        Ok(EnumDecl {
535            name,
536            variants,
537            attrs,
538        })
539    }
540
541    /// Parse a `tagged` node.
542    fn tagged_decl(&mut self, node: &KdlNode, attrs: Vec<Attr>) -> Result<TaggedDecl> {
543        trace!("enum decl");
544        let name = self.one_string(node, "type name")?;
545        let name = self.ident(name)?;
546        let variants = self.tagged_variant_children(node)?;
547
548        Ok(TaggedDecl {
549            name,
550            variants,
551            attrs,
552        })
553    }
554
555    /// Parse a `pun` node.
556    fn pun_decl(&mut self, node: &KdlNode, attrs: Vec<Attr>) -> Result<PunDecl> {
557        let name = self.one_string(node, "type name")?;
558        let name = self.ident(name)?;
559
560        // Parse the pun blocks
561        let mut blocks = vec![];
562        for item in node.children().into_iter().flat_map(|d| d.nodes()) {
563            let item_name = item.name().value();
564            match item_name {
565                "lang" => {
566                    let langs = self.string_list(item.entries())?;
567                    if langs.is_empty() {
568                        let node_ident = item.name().span();
569                        let after_ident = node_ident.offset() + node_ident.len();
570                        return Err(KdlScriptParseError {
571                            message: "Hey I need a lang name (string) here!".to_string(),
572                            src: self.src.clone(),
573                            span: (after_ident..after_ident).into(),
574                            help: None,
575                        })?;
576                    }
577                    let final_ty = self.pun_block(item, &name)?;
578                    blocks.push(PunBlock {
579                        selector: PunSelector::Any(
580                            langs.into_iter().map(PunSelector::Lang).collect(),
581                        ),
582                        decl: final_ty,
583                    });
584                }
585                "default" => {
586                    self.no_args(item)?;
587                    let final_ty = self.pun_block(item, &name)?;
588                    blocks.push(PunBlock {
589                        selector: PunSelector::Default,
590                        decl: final_ty,
591                    });
592                }
593                x => {
594                    return Err(KdlScriptParseError {
595                        message: format!("I don't know what a '{x}' is here"),
596                        src: self.src.clone(),
597                        span: *item.name().span(),
598                        help: None,
599                    })?;
600                }
601            }
602        }
603
604        Ok(PunDecl {
605            name,
606            blocks,
607            attrs,
608        })
609    }
610
611    /// Parse a pun block, expecting only a single type with the pun's name.
612    ///
613    /// In the future we may allow [`PunBlock`][]s to declare other "private" types
614    /// that are only in scope for the block but required to define the final type.
615    /// For now we punt on this to avoid thinking through the name resolution implications
616    /// for things like name shadowing.
617    fn pun_block(&mut self, block: &KdlNode, final_ty_name: &Ident) -> Result<TyDecl> {
618        if let Some(doc) = block.children() {
619            // Recursively parse this block as an entire KdlScript program
620            let defs = self.parse_module(doc)?;
621
622            // Don't want any functions
623            if let Some((_name, func)) = defs.funcs.iter().next() {
624                return Err(KdlScriptParseError {
625                    message: "puns can't contain function decls".to_string(),
626                    src: self.src.clone(),
627                    span: Spanned::span(&func.name),
628                    help: None,
629                })?;
630            }
631
632            let mut final_ty = None;
633
634            // Only want one type declared (might loosen this later)
635            for (ty_name, ty) in defs.tys {
636                if &ty_name == final_ty_name {
637                    // this is the type
638                    final_ty = Some(ty);
639                } else {
640                    return Err(KdlScriptParseError {
641                        message: "pun declared a type other than what it should have".to_string(),
642                        src: self.src.clone(),
643                        span: Spanned::span(&ty_name),
644                        help: None,
645                    })?;
646                }
647            }
648
649            // Check that we defined the type
650            if let Some(ty) = final_ty {
651                Ok(ty)
652            } else {
653                Err(KdlScriptParseError {
654                    message: "pun block failed to define the type it puns!".to_string(),
655                    src: self.src.clone(),
656                    span: *block.span(),
657                    help: None,
658                })?
659            }
660        } else {
661            Err(KdlScriptParseError {
662                message: "pun blocks need bodies".to_string(),
663                src: self.src.clone(),
664                span: *block.span(),
665                help: None,
666            })?
667        }
668    }
669
670    /// Parse an `alias` node.
671    fn alias_decl(&mut self, node: &KdlNode, attrs: Vec<Attr>) -> Result<AliasDecl> {
672        let name = self.string_at(node, "type name", 0)?;
673        let name = self.ident(name)?;
674        let alias_str = self.string_at(node, "type name", 1)?;
675        let alias = self.tydent(&alias_str)?;
676
677        Ok(AliasDecl { name, alias, attrs })
678    }
679
680    /// Parse a `fn` node.
681    fn func_decl(&mut self, node: &KdlNode, attrs: Vec<Attr>) -> Result<FuncDecl> {
682        trace!("fn");
683        let name = self.one_string(node, "function name")?;
684        let name = self.ident(name)?;
685        let mut inputs = vec![];
686        let mut outputs = vec![];
687        #[cfg(feature = "eval")]
688        let mut body = vec![];
689
690        let mut reached_body = false;
691        let mut input_span = None;
692        let mut output_span = None;
693        for stmt in node.children().into_iter().flat_map(|d| d.nodes()) {
694            match stmt.name().value() {
695                "inputs" => {
696                    trace!("fn input");
697                    if reached_body {
698                        return Err(KdlScriptParseError {
699                            message: "input declaration must come before the body".to_string(),
700                            src: self.src.clone(),
701                            span: *stmt.name().span(),
702                            help: None,
703                        })?;
704                    }
705                    if let Some(_old_input) = input_span {
706                        return Err(KdlScriptParseError {
707                            message: "duplicate input block".to_string(),
708                            src: self.src.clone(),
709                            span: *stmt.name().span(),
710                            help: None,
711                        })?;
712                    }
713                    if let Some(_old_output) = output_span {
714                        return Err(KdlScriptParseError {
715                            message: "It's confusing to declare inputs after outputs".to_string(),
716                            src: self.src.clone(),
717                            span: *stmt.name().span(),
718                            help: Some("Move this before the output block".to_string()),
719                        })?;
720                    }
721                    self.no_args(stmt)?;
722                    inputs = self.typed_var_children(stmt)?;
723                    input_span = Some(*stmt.name().span());
724                    continue;
725                }
726                "outputs" => {
727                    trace!("fn output");
728                    if reached_body {
729                        return Err(KdlScriptParseError {
730                            message: "output declaration must come before the body".to_string(),
731                            src: self.src.clone(),
732                            span: *stmt.name().span(),
733                            help: None,
734                        })?;
735                    }
736                    if let Some(_old_output) = output_span {
737                        return Err(KdlScriptParseError {
738                            message: "duplicate output block".to_string(),
739                            src: self.src.clone(),
740                            span: *stmt.name().span(),
741                            help: None,
742                        })?;
743                    }
744                    self.no_args(stmt)?;
745                    outputs = self.typed_var_children(stmt)?;
746                    output_span = Some(*stmt.name().span());
747                    continue;
748                }
749                x => {
750                    #[cfg(feature = "eval")]
751                    match x {
752                        "let" => {
753                            trace!("let stmt");
754                            let name = self.string_at(stmt, "variable name", 0)?;
755                            let name = self.ident(name)?;
756                            let name = if &*name == "_" { None } else { Some(name) };
757                            let expr = self.expr_rhs(stmt, 1)?;
758                            body.push(Stmt::Let(LetStmt { var: name, expr }));
759                        }
760                        "return" => {
761                            trace!("return stmt");
762                            let expr = self.expr_rhs(stmt, 0)?;
763                            body.push(Stmt::Return(ReturnStmt { expr }));
764                        }
765                        "print" => {
766                            trace!("print stmt");
767                            let expr = self.expr_rhs(stmt, 0)?;
768                            body.push(Stmt::Print(PrintStmt { expr }));
769                        }
770                        x => {
771                            return Err(KdlScriptParseError {
772                                message: format!("I don't know what a '{x}' statement is"),
773                                src: self.src.clone(),
774                                span: *stmt.name().span(),
775                                help: None,
776                            })?;
777                        }
778                    }
779                    #[cfg(not(feature = "eval"))]
780                    return Err(KdlScriptParseError {
781                        message: format!("I don't know what a '{x}' statement is"),
782                        src: self.src.clone(),
783                        span: *stmt.name().span(),
784                        help: None,
785                    })?;
786                }
787            }
788
789            reached_body = true;
790        }
791
792        Ok(FuncDecl {
793            name,
794            inputs,
795            outputs,
796            #[cfg(feature = "eval")]
797            body,
798            attrs,
799        })
800    }
801
802    /// Parse an `@attribute` node.
803    fn attr(&mut self, attr: &KdlNode) -> Result<Attr> {
804        let attr = match attr.name().value() {
805            "@packed" => {
806                trace!("packed attr");
807                self.no_children(attr)?;
808                Attr::Packed(AttrPacked {})
809            }
810            "@align" => {
811                trace!("align attr");
812                let Some(e) = attr.entries().first() else {
813                    return Err(KdlScriptParseError {
814                        message: "align attr needs an integer argument".to_owned(),
815                        src: self.src.clone(),
816                        span: *attr.name().span(),
817                        help: None,
818                    })?;
819                };
820                let align = self.int_expr(e)?;
821                Attr::Align(AttrAligned { align })
822            }
823            "@repr" => {
824                trace!("repr attr");
825                let raw_reprs = self.string_list(attr.entries())?;
826                let mut reprs = vec![];
827                for raw_repr in raw_reprs {
828                    let repr = match &**raw_repr {
829                        "rust" => Repr::Lang(LangRepr::Rust),
830                        "c" => Repr::Lang(LangRepr::C),
831                        "transparent" => Repr::Transparent,
832                        name => {
833                            let mut prim_repr = None;
834                            for &(prim_name, prim) in PRIMITIVES {
835                                if prim_name == name {
836                                    prim_repr = Some(prim);
837                                }
838                            }
839                            let Some(prim_repr) = prim_repr else {
840                                return Err(KdlScriptParseError {
841                                    message: "repr attr has unknown kind".to_owned(),
842                                    src: self.src.clone(),
843                                    span: *attr.name().span(),
844                                    help: None,
845                                })?;
846                            };
847                            Repr::Primitive(prim_repr)
848                        }
849                    };
850                    reprs.push(repr);
851                }
852                Attr::Repr(AttrRepr { reprs })
853            }
854            "@" => {
855                trace!("passthrough attr");
856                let val = self.one_string(attr, "attribute to pass through to target language")?;
857                Attr::Passthrough(AttrPassthrough(val))
858            }
859            x => {
860                return Err(KdlScriptParseError {
861                    message: format!("I don't know what a '{x}' attribute is"),
862                    src: self.src.clone(),
863                    span: *attr.name().span(),
864                    help: None,
865                })?;
866            }
867        };
868        Ok(attr)
869    }
870
871    /// This node's entries should only be a positional list of strings.
872    fn string_list(&mut self, entries: &[KdlEntry]) -> Result<Vec<Spanned<String>>> {
873        entries
874            .iter()
875            .map(|e| -> Result<Spanned<String>> {
876                if e.name().is_some() {
877                    return Err(KdlScriptParseError {
878                        message: "Named values don't belong here, only strings".to_string(),
879                        src: self.src.clone(),
880                        span: *e.span(),
881                        help: Some("try removing the name".to_owned()),
882                    })?;
883                }
884                match e.value() {
885                    kdl::KdlValue::RawString(s) | kdl::KdlValue::String(s) => {
886                        Ok(Spanned::new(s.clone(), *e.span()))
887                    }
888                    _ => Err(KdlScriptParseError {
889                        message: "This should be a string".to_string(),
890                        src: self.src.clone(),
891                        span: *e.span(),
892                        help: Some("try adding quotes?".to_owned()),
893                    })?,
894                }
895            })
896            .collect()
897    }
898
899    /// This node's entries should be only one string.
900    fn one_string(&mut self, node: &KdlNode, desc: &str) -> Result<Spanned<String>> {
901        let res = self.string_at(node, desc, 0)?;
902        let entries = node.entries();
903        if let Some(e) = entries.get(1) {
904            return Err(KdlScriptParseError {
905                message: format!("You have something extra after your {desc}"),
906                src: self.src.clone(),
907                span: *e.span(),
908                help: Some("remove this?".to_owned()),
909            })?;
910        }
911        Ok(res)
912    }
913
914    /// This node should have a string at this entry offset.
915    fn string_at(&mut self, node: &KdlNode, desc: &str, offset: usize) -> Result<Spanned<String>> {
916        let entries = node.entries();
917        if let Some(e) = entries.get(offset) {
918            if e.name().is_some() {
919                return Err(KdlScriptParseError {
920                    message: "Named values don't belong here, only strings".to_string(),
921                    src: self.src.clone(),
922                    span: *e.span(),
923                    help: Some("try removing the name".to_owned()),
924                })?;
925            }
926
927            match e.value() {
928                kdl::KdlValue::RawString(s) | kdl::KdlValue::String(s) => {
929                    Ok(Spanned::new(s.clone(), *e.span()))
930                }
931                _ => Err(KdlScriptParseError {
932                    message: format!("This should be a {desc} (string)"),
933                    src: self.src.clone(),
934                    span: *e.span(),
935                    help: Some("try adding quotes".to_owned()),
936                })?,
937            }
938        } else {
939            let node_ident = node.name().span();
940            let after_ident = node_ident.offset() + node_ident.len();
941            Err(KdlScriptParseError {
942                message: format!("Hey I need a {desc} (string) here!"),
943                src: self.src.clone(),
944                span: (after_ident..after_ident).into(),
945                help: None,
946            })?
947        }
948    }
949
950    /// This node should have no entries.
951    fn no_args(&mut self, node: &KdlNode) -> Result<()> {
952        if let Some(entry) = node.entries().first() {
953            return Err(KdlScriptParseError {
954                message: "This shouldn't have arguments".to_string(),
955                src: self.src.clone(),
956                span: *entry.span(),
957                help: Some("delete them?".to_string()),
958            })?;
959        }
960        Ok(())
961    }
962
963    /// This node should have no children.
964    fn no_children(&mut self, node: &KdlNode) -> Result<()> {
965        if let Some(children) = node.children() {
966            return Err(KdlScriptParseError {
967                message: "These children should never have been born".to_string(),
968                src: self.src.clone(),
969                span: *children.span(),
970                help: Some("delete this block?".to_string()),
971            })?;
972        }
973        Ok(())
974    }
975
976    /// This node's children should be TypedVars
977    fn typed_var_children(&mut self, node: &KdlNode) -> Result<Vec<TypedVar>> {
978        node.children()
979            .into_iter()
980            .flat_map(|d| d.nodes())
981            .map(|var| {
982                let name = self.var_name_decl(var)?;
983                let ty_str = self.one_string(var, "type")?;
984                let ty = self.tydent(&ty_str)?;
985                self.no_children(var)?;
986                Ok(TypedVar { name, ty })
987            })
988            .collect()
989    }
990
991    /// This node's children should be enum variants
992    fn enum_variant_children(&mut self, node: &KdlNode) -> Result<Vec<EnumVariant>> {
993        node.children()
994            .into_iter()
995            .flat_map(|d| d.nodes())
996            .map(|var| {
997                let name = var.name();
998                let name = Spanned::new(name.value().to_owned(), *name.span());
999                let name = self.ident(name)?;
1000                let entries = var.entries();
1001                let val = if let Some(e) = entries.first() {
1002                    Some(self.int_expr(e)?)
1003                } else {
1004                    None
1005                };
1006                if let Some(e) = entries.get(1) {
1007                    return Err(KdlScriptParseError {
1008                        message: "You have something extra after your enum case".to_string(),
1009                        src: self.src.clone(),
1010                        span: *e.span(),
1011                        help: Some("remove this?".to_owned()),
1012                    })?;
1013                }
1014                self.no_children(var)?;
1015                Ok(EnumVariant { name, val })
1016            })
1017            .collect()
1018    }
1019
1020    /// This node's children should be tagged variants
1021    fn tagged_variant_children(&mut self, node: &KdlNode) -> Result<Vec<TaggedVariant>> {
1022        node.children()
1023            .into_iter()
1024            .flat_map(|d| d.nodes())
1025            .map(|var| {
1026                self.no_args(var)?;
1027                let name = var.name();
1028                let name = Spanned::new(name.value().to_owned(), *name.span());
1029                let name = self.ident(name)?;
1030                let fields = if var.children().is_some() {
1031                    Some(self.typed_var_children(var)?)
1032                } else {
1033                    None
1034                };
1035                Ok(TaggedVariant { name, fields })
1036            })
1037            .collect()
1038    }
1039
1040    /// Parse this node's name as a possibly-positional variable name
1041    fn var_name_decl(&mut self, var: &KdlNode) -> Result<Option<Ident>> {
1042        let name = var.name();
1043        let name = if name.value() == "_" {
1044            None
1045        } else {
1046            let name = self.ident(Spanned::new(name.value().to_owned(), *name.span()))?;
1047            Some(name)
1048        };
1049        Ok(name)
1050    }
1051
1052    /// Parse a [`Tydent`][] from this String.
1053    fn tydent(&mut self, input: &Spanned<String>) -> Result<Spanned<Tydent>> {
1054        let (_, mut ty_ref) = all_consuming(context("a type", tydent))(input)
1055            .finish()
1056            .map_err(|_e| KdlScriptParseError {
1057                message: String::from("couldn't parse type"),
1058                src: self.src.clone(),
1059                span: Spanned::span(input),
1060                help: None,
1061            })?;
1062
1063        // Inherit spans
1064        inherit_spans(&mut ty_ref, input);
1065        Ok(ty_ref)
1066    }
1067
1068    /// Parse an [`Ident`][] from this String.
1069    fn ident(&mut self, input: Spanned<String>) -> Result<Ident> {
1070        let (_, _) =
1071            all_consuming(context("a type", tydent))(&input).map_err(|_e| KdlScriptParseError {
1072                message: String::from("invalid identifier"),
1073                src: self.src.clone(),
1074                span: Spanned::span(&input),
1075                help: None,
1076            })?;
1077        Ok(Ident {
1078            val: input,
1079            was_blank: false,
1080        })
1081    }
1082
1083    /// Parse an [`IntExpr`][] (literal) from this entry.
1084    fn int_expr(&mut self, entry: &KdlEntry) -> Result<IntExpr> {
1085        if entry.name().is_some() {
1086            return Err(KdlScriptParseError {
1087                message: "Named values don't belong here, only literals".to_string(),
1088                src: self.src.clone(),
1089                span: *entry.span(),
1090                help: Some("try removing the name".to_owned()),
1091            })?;
1092        }
1093        let val = match entry.value() {
1094            kdl::KdlValue::Base2(int)
1095            | kdl::KdlValue::Base8(int)
1096            | kdl::KdlValue::Base10(int)
1097            | kdl::KdlValue::Base16(int) => *int,
1098            _ => {
1099                return Err(KdlScriptParseError {
1100                    message: String::from("must be an integer"),
1101                    src: self.src.clone(),
1102                    span: *entry.span(),
1103                    help: None,
1104                })?;
1105            }
1106        };
1107        Ok(IntExpr {
1108            span: *entry.span(),
1109            val,
1110        })
1111    }
1112}
1113
1114fn inherit_spans(tydent: &mut Spanned<Tydent>, input: &Spanned<String>) {
1115    Spanned::clone_span_from(tydent, input);
1116    match &mut **tydent {
1117        Tydent::Name(ident) => {
1118            Spanned::clone_span_from(&mut ident.val, input);
1119        }
1120        Tydent::Array(elem_tydent, _) => {
1121            inherit_spans(elem_tydent, input);
1122        }
1123        Tydent::Ref(pointee_tydent) => {
1124            inherit_spans(pointee_tydent, input);
1125        }
1126        Tydent::Empty => {
1127            // noop
1128        }
1129    }
1130}
1131
1132// A fuckton of nom parsing for sub-syntax like Tydents.
1133
1134type NomResult<I, O> = IResult<I, O, VerboseError<I>>;
1135
1136/// Matches the syntax for tydent ("identifier, but for types") incl structural types like arrays/references.
1137fn tydent(input: &str) -> NomResult<&str, Spanned<Tydent>> {
1138    alt((tydent_ref, tydent_array, tydent_empty_tuple, tydent_named))(input)
1139}
1140
1141/// Matches a reference type (&T)
1142fn tydent_ref(input: &str) -> NomResult<&str, Spanned<Tydent>> {
1143    let (input, pointee_ty) = preceded(
1144        tag("&"),
1145        context("pointee type", cut(preceded(many0(unicode_space), tydent))),
1146    )(input)?;
1147    Ok((input, Spanned::from(Tydent::Ref(Box::new(pointee_ty)))))
1148}
1149
1150/// Matches an array type ([T; N])
1151fn tydent_array(input: &str) -> NomResult<&str, Spanned<Tydent>> {
1152    let (input, (elem_ty, array_len)) = delimited(
1153        tag("["),
1154        cut(separated_pair(
1155            context(
1156                "an element type",
1157                delimited(many0(unicode_space), tydent, many0(unicode_space)),
1158            ),
1159            tag(";"),
1160            context(
1161                "an array length (integer)",
1162                delimited(many0(unicode_space), array_len, many0(unicode_space)),
1163            ),
1164        )),
1165        tag("]"),
1166    )(input)?;
1167    Ok((
1168        input,
1169        Spanned::from(Tydent::Array(Box::new(elem_ty), array_len)),
1170    ))
1171}
1172
1173/// Matches an array length (u64)
1174fn array_len(input: &str) -> NomResult<&str, u64> {
1175    nom::character::complete::u64(input)
1176}
1177
1178/// Matches the empty tuple
1179fn tydent_empty_tuple(input: &str) -> NomResult<&str, Spanned<Tydent>> {
1180    let (input, _tup) = tag("()")(input)?;
1181    Ok((input, Spanned::from(Tydent::Empty)))
1182}
1183
1184/// Matches a named type
1185fn tydent_named(input: &str) -> NomResult<&str, Spanned<Tydent>> {
1186    let (input, (ty_name, generics)) = pair(
1187        ident,
1188        opt(delimited(
1189            pair(unicode_space, tag("<")),
1190            cut(separated_list1(
1191                tag(","),
1192                delimited(unicode_space, tydent_ref, unicode_space),
1193            )),
1194            tag(">"),
1195        )),
1196    )(input)?;
1197
1198    if let Some(_generics) = generics {
1199        panic!("generics aren't yet implemented!");
1200    }
1201    Ok((
1202        input,
1203        Spanned::from(Tydent::Name(Ident {
1204            val: Spanned::from(ty_name.to_owned()),
1205            was_blank: false,
1206        })),
1207    ))
1208}
1209
1210/// Matches an identifier
1211fn ident(input: &str) -> NomResult<&str, &str> {
1212    recognize(pair(
1213        alt((alpha1, tag("_"))),
1214        many0_count(alt((alphanumeric1, tag("_")))),
1215    ))(input)
1216}
1217
1218/// Matches various kinds of whitespace we allow
1219fn unicode_space(input: &str) -> NomResult<&str, &str> {
1220    alt((
1221        tag(" "),
1222        tag("\t"),
1223        tag("\u{FEFF}"), // BOM
1224        tag("\u{00A0}"),
1225        tag("\u{1680}"),
1226        tag("\u{2000}"),
1227        tag("\u{2001}"),
1228        tag("\u{2002}"),
1229        tag("\u{2003}"),
1230        tag("\u{2004}"),
1231        tag("\u{2005}"),
1232        tag("\u{2006}"),
1233        tag("\u{2007}"),
1234        tag("\u{2008}"),
1235        tag("\u{2009}"),
1236        tag("\u{200A}"),
1237        tag("\u{202F}"),
1238        tag("\u{205F}"),
1239        tag("\u{3000}"),
1240    ))(input)
1241}
1242
1243/// An integer expression (literal)
1244///
1245/// FIXME: this should actually defer deserializing into an integer
1246/// so that it can be some huge type like `u256`. Not sure who/where
1247/// would be responsible for validating that the value fits in the
1248/// expected range for where it's placed!
1249///
1250/// Possibly the type checker, but it also kinda needs to be deferred
1251/// to the target language backends as a language may not be able to
1252/// handle a `u256` or whatever.
1253#[derive(Debug, Clone, PartialEq, Eq, Hash)]
1254pub struct IntExpr {
1255    pub span: SourceSpan,
1256    pub val: i64,
1257}
1258
1259/// All of this gunk is only used for function bodies, which only
1260/// exist in the meme `feature=eval` mode.
1261#[cfg(feature = "eval")]
1262pub use runnable::*;
1263#[cfg(feature = "eval")]
1264mod runnable {
1265    use super::*;
1266
1267    #[derive(Debug, Clone)]
1268    pub enum Stmt {
1269        Let(LetStmt),
1270        Return(ReturnStmt),
1271        Print(PrintStmt),
1272    }
1273
1274    #[derive(Debug, Clone)]
1275    pub struct LetStmt {
1276        pub var: Option<Ident>,
1277        pub expr: Spanned<Expr>,
1278    }
1279
1280    #[derive(Debug, Clone)]
1281    pub struct ReturnStmt {
1282        pub expr: Spanned<Expr>,
1283    }
1284
1285    #[derive(Debug, Clone)]
1286    pub struct PrintStmt {
1287        pub expr: Spanned<Expr>,
1288    }
1289
1290    #[derive(Debug, Clone)]
1291    pub enum Expr {
1292        Call(CallExpr),
1293        Path(PathExpr),
1294        Ctor(CtorExpr),
1295        Literal(LiteralExpr),
1296    }
1297
1298    #[derive(Debug, Clone)]
1299    pub struct CallExpr {
1300        pub func: Ident,
1301        pub args: Vec<Spanned<Expr>>,
1302    }
1303
1304    #[derive(Debug, Clone)]
1305    pub struct PathExpr {
1306        pub var: Ident,
1307        pub path: Vec<Ident>,
1308    }
1309
1310    #[derive(Debug, Clone)]
1311    pub struct CtorExpr {
1312        pub ty: Ident,
1313        pub vals: Vec<Spanned<LetStmt>>,
1314    }
1315
1316    #[derive(Debug, Clone)]
1317    pub struct LiteralExpr {
1318        pub span: SourceSpan,
1319        pub val: Literal,
1320    }
1321
1322    #[derive(Debug, Clone)]
1323    pub enum Literal {
1324        Float(f64),
1325        Int(i64),
1326        Bool(bool),
1327    }
1328
1329    impl Parser<'_> {
1330        pub(crate) fn func_args(
1331            &mut self,
1332            node: &KdlNode,
1333            expr_start: usize,
1334        ) -> Result<Vec<Spanned<Expr>>> {
1335            node.entries()[expr_start..]
1336                .iter()
1337                .enumerate()
1338                .map(|(idx, _e)| self.smol_expr(node, expr_start + idx))
1339                .collect()
1340        }
1341
1342        pub(crate) fn literal_expr(&mut self, entry: &KdlEntry) -> Result<LiteralExpr> {
1343            if entry.name().is_some() {
1344                return Err(KdlScriptParseError {
1345                    message: "Named values don't belong here, only literals".to_string(),
1346                    src: self.src.clone(),
1347                    span: *entry.span(),
1348                    help: Some("try removing the name".to_owned()),
1349                })?;
1350            }
1351
1352            let val = match entry.value() {
1353                kdl::KdlValue::RawString(_) | kdl::KdlValue::String(_) => {
1354                    return Err(KdlScriptParseError {
1355                        message: "strings aren't supported literals".to_string(),
1356                        src: self.src.clone(),
1357                        span: *entry.span(),
1358                        help: None,
1359                    })?;
1360                }
1361                kdl::KdlValue::Null => {
1362                    return Err(KdlScriptParseError {
1363                        message: "nulls aren't supported literals".to_string(),
1364                        src: self.src.clone(),
1365                        span: *entry.span(),
1366                        help: None,
1367                    })?;
1368                }
1369                kdl::KdlValue::Base2(int)
1370                | kdl::KdlValue::Base8(int)
1371                | kdl::KdlValue::Base10(int)
1372                | kdl::KdlValue::Base16(int) => Literal::Int(*int),
1373                kdl::KdlValue::Base10Float(val) => Literal::Float(*val),
1374                kdl::KdlValue::Bool(val) => Literal::Bool(*val),
1375            };
1376
1377            Ok(LiteralExpr {
1378                span: *entry.span(),
1379                val,
1380            })
1381        }
1382
1383        pub(crate) fn expr_rhs(
1384            &mut self,
1385            node: &KdlNode,
1386            expr_start: usize,
1387        ) -> Result<Spanned<Expr>> {
1388            trace!("expr rhs");
1389            let expr = if let Ok(string) = self.string_at(node, "", expr_start) {
1390                if let Some((func, "")) = string.rsplit_once(':') {
1391                    trace!("  call expr");
1392                    let func = Ident::with_span(func.to_owned(), Spanned::span(&string));
1393                    let args = self.func_args(node, expr_start + 1)?;
1394                    Expr::Call(CallExpr { func, args })
1395                } else if node.children().is_some() {
1396                    trace!("  ctor expr");
1397                    let ty = Ident::new(string);
1398                    let vals = self.let_stmt_children(node)?;
1399                    Expr::Ctor(CtorExpr { ty, vals })
1400                } else {
1401                    trace!("  path expr");
1402                    let mut parts = string.split('.');
1403                    let var =
1404                        Ident::with_span(parts.next().unwrap().to_owned(), Spanned::span(&string));
1405                    let path = parts
1406                        .map(|s| Ident::with_span(s.to_owned(), Spanned::span(&string)))
1407                        .collect();
1408                    Expr::Path(PathExpr { var, path })
1409                }
1410            } else if let Some(val) = node.entries().get(expr_start) {
1411                trace!("  literal expr");
1412                Expr::Literal(self.literal_expr(val)?)
1413            } else {
1414                return Err(KdlScriptParseError {
1415                    message: "I thought there was supposed to be an expression after here?"
1416                        .to_string(),
1417                    src: self.src.clone(),
1418                    span: *node.span(),
1419                    help: None,
1420                })?;
1421            };
1422
1423            Ok(Spanned::new(expr, *node.span()))
1424        }
1425
1426        pub(crate) fn smol_expr(
1427            &mut self,
1428            node: &KdlNode,
1429            expr_at: usize,
1430        ) -> Result<Spanned<Expr>> {
1431            trace!("smol expr");
1432            let expr = if let Ok(string) = self.string_at(node, "", expr_at) {
1433                if let Some((_func, "")) = string.rsplit_once(':') {
1434                    return Err(KdlScriptParseError {
1435                        message:
1436                            "Nested function calls aren't supported because this is a shitpost"
1437                                .to_string(),
1438                        src: self.src.clone(),
1439                        span: *node.span(),
1440                        help: None,
1441                    })?;
1442                } else if node.children().is_some() {
1443                    return Err(KdlScriptParseError {
1444                        message: "Ctors exprs can't be nested in function calls because this is a shitpost".to_string(),
1445                        src: self.src.clone(),
1446                        span: *node.span(),
1447                        help: None,
1448                    })?;
1449                } else {
1450                    trace!("  path expr");
1451                    let mut parts = string.split('.');
1452                    let var =
1453                        Ident::with_span(parts.next().unwrap().to_owned(), Spanned::span(&string));
1454                    let path = parts
1455                        .map(|s| Ident::with_span(s.to_owned(), Spanned::span(&string)))
1456                        .collect();
1457                    Expr::Path(PathExpr { var, path })
1458                }
1459            } else if let Some(val) = node.entries().get(expr_at) {
1460                trace!("  literal expr");
1461                Expr::Literal(self.literal_expr(val)?)
1462            } else {
1463                return Err(KdlScriptParseError {
1464                    message: "I thought there was supposed to be an expression after here?"
1465                        .to_string(),
1466                    src: self.src.clone(),
1467                    span: *node.span(),
1468                    help: None,
1469                })?;
1470            };
1471
1472            Ok(Spanned::new(expr, *node.span()))
1473        }
1474
1475        pub(crate) fn let_stmt_children(
1476            &mut self,
1477            node: &KdlNode,
1478        ) -> Result<Vec<Spanned<LetStmt>>> {
1479            node.children()
1480                .into_iter()
1481                .flat_map(|d| d.nodes())
1482                .map(|var| {
1483                    let name = self.var_name_decl(var)?;
1484                    let expr = self.expr_rhs(var, 0)?;
1485                    Ok(Spanned::new(LetStmt { var: name, expr }, *var.span()))
1486                })
1487                .collect()
1488        }
1489    }
1490
1491    impl ParsedProgram {
1492        pub(crate) fn add_builtin_funcs(&mut self) -> Result<()> {
1493            // Add builtins jankily
1494            self.funcs.insert(
1495                Ident::from(String::from("+")),
1496                FuncDecl {
1497                    name: Ident::from(String::from("+")),
1498                    inputs: vec![
1499                        TypedVar {
1500                            name: Some(Ident::from(String::from("lhs"))),
1501                            ty: Spanned::from(Tydent::Name(Ident::from(String::from("i64")))),
1502                        },
1503                        TypedVar {
1504                            name: Some(Ident::from(String::from("rhs"))),
1505                            ty: Spanned::from(Tydent::Name(Ident::from(String::from("i64")))),
1506                        },
1507                    ],
1508                    outputs: vec![TypedVar {
1509                        name: Some(Ident::from(String::from("out"))),
1510                        ty: Spanned::from(Tydent::Name(Ident::from(String::from("i64")))),
1511                    }],
1512                    attrs: vec![],
1513
1514                    body: vec![],
1515                },
1516            );
1517
1518            Ok(())
1519        }
1520    }
1521}