Skip to main content

wac_parser/
ast.rs

1//! Module for the AST implementation.
2
3use crate::{
4    lexer::{self, Lexer, LexerResult, Token},
5    resolution::{AstResolver, Resolution, ResolutionResult},
6};
7use indexmap::IndexMap;
8use miette::{Diagnostic, SourceSpan};
9use serde::Serialize;
10use std::fmt;
11use wac_graph::types::BorrowedPackageKey;
12
13mod export;
14mod expr;
15mod import;
16mod r#let;
17mod printer;
18mod r#type;
19
20pub use export::*;
21pub use expr::*;
22pub use import::*;
23pub use printer::*;
24pub use r#let::*;
25pub use r#type::*;
26
27struct Found(Option<Token>);
28
29impl fmt::Display for Found {
30    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
31        match self.0 {
32            Some(t) => t.fmt(f),
33            None => write!(f, "end of input"),
34        }
35    }
36}
37
38struct Expected<'a> {
39    expected: &'a [Option<Token>],
40    count: usize,
41}
42
43impl fmt::Display for Expected<'_> {
44    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
45        let mut expected = self.expected.iter().enumerate();
46        while let Some((i, Some(token))) = expected.next() {
47            if i > 0 {
48                write!(f, ", ")?;
49            }
50
51            if i == self.count - 1 {
52                write!(f, "or ")?;
53            }
54
55            token.fmt(f)?;
56        }
57
58        if self.count > self.expected.len() {
59            write!(f, ", or more...")?;
60        }
61
62        Ok(())
63    }
64}
65
66/// Represents a parse error.
67#[derive(thiserror::Error, Diagnostic, Debug)]
68#[diagnostic(code("failed to parse document"))]
69pub enum Error {
70    /// A lexer error occurred.
71    #[error("{error}")]
72    Lexer {
73        /// The lexer error that occurred.
74        error: crate::lexer::Error,
75        /// The span where the error occurred.
76        #[label(primary)]
77        span: SourceSpan,
78    },
79    /// An unexpected token was encountered when a single token was expected.
80    #[error("expected {expected}, found {found}", found = Found(*.found))]
81    Expected {
82        /// The expected token.
83        expected: Token,
84        /// The found token (`None` for end of input).
85        found: Option<Token>,
86        /// The span of the found token.
87        #[label(primary, "unexpected {found}", found = Found(*.found))]
88        span: SourceSpan,
89    },
90    /// An unexpected token was encountered when either one of two tokens was expected.
91    #[error("expected {first} or {second}, found {found}", found = Found(*.found))]
92    ExpectedEither {
93        /// The first expected token.
94        first: Token,
95        /// The second expected token.
96        second: Token,
97        /// The found token.
98        found: Option<Token>,
99        /// The span of the found token.
100        #[label(primary, "unexpected {found}", found = Found(*.found))]
101        span: SourceSpan,
102    },
103    /// An unexpected token was encountered when multiple tokens were expected.
104    #[error("expected either {expected}, found {found}", expected = Expected { expected, count: *.count }, found = Found(*.found))]
105    ExpectedMultiple {
106        /// The tokens that were expected.
107        expected: [Option<Token>; 10],
108        /// The count of expected tokens.
109        count: usize,
110        /// The found token.
111        found: Option<Token>,
112        /// The span of the found token.
113        #[label(primary, "unexpected {found}", found = Found(*.found))]
114        span: SourceSpan,
115    },
116    /// An empty type was encountered.
117    #[error("{ty} must contain at least one {kind}")]
118    EmptyType {
119        /// The type that was empty (e.g. "record", "variant", etc.)
120        ty: &'static str,
121        /// The kind of item that was empty (e.g. "field", "case", etc.)
122        kind: &'static str,
123        /// The span of the empty type.
124        #[label(primary, "empty {ty}")]
125        span: SourceSpan,
126    },
127    /// An invalid semantic version was encountered.
128    #[error("`{version}` is not a valid semantic version")]
129    InvalidVersion {
130        /// The invalid version.
131        version: std::string::String,
132        /// The span of the version.
133        #[label(primary, "invalid version")]
134        span: SourceSpan,
135    },
136}
137
138/// Represents a parse result.
139pub type ParseResult<T> = Result<T, Error>;
140
141impl From<(lexer::Error, SourceSpan)> for Error {
142    fn from((e, s): (lexer::Error, SourceSpan)) -> Self {
143        Self::Lexer { error: e, span: s }
144    }
145}
146
147/// Expects a given token from the lexer.
148pub fn parse_token(lexer: &mut Lexer, expected: Token) -> ParseResult<SourceSpan> {
149    let (result, span) = lexer.next().ok_or_else(|| Error::Expected {
150        expected,
151        found: None,
152        span: lexer.span(),
153    })?;
154
155    match result {
156        Ok(found) if found == expected => Ok(span),
157        Ok(found) => Err(Error::Expected {
158            expected,
159            found: Some(found),
160            span,
161        }),
162        Err(e) => Err((e, span).into()),
163    }
164}
165
166/// Parses an optional tokens from a lexer.
167///
168/// The expected token is removed from the token stream before the callback is invoked.
169pub fn parse_optional<'a, F, R>(
170    lexer: &mut Lexer<'a>,
171    expected: Token,
172    cb: F,
173) -> ParseResult<Option<R>>
174where
175    F: FnOnce(&mut Lexer<'a>) -> ParseResult<R>,
176{
177    match lexer.peek() {
178        Some((Ok(token), _)) => {
179            if token == expected {
180                lexer.next();
181                Ok(Some(cb(lexer)?))
182            } else {
183                Ok(None)
184            }
185        }
186        Some((Err(e), s)) => Err((e, s).into()),
187        None => Ok(None),
188    }
189}
190
191/// Consumes the current keyword token, parses an optional `<T>` payload, then
192/// builds the type using it's constructor.
193pub fn parse_optional_payload<'a>(
194    lexer: &mut Lexer<'a>,
195    ctor: impl FnOnce(Option<Box<Type<'a>>>, SourceSpan) -> Type<'a>,
196) -> ParseResult<Type<'a>> {
197    let span = lexer.next().unwrap().1;
198    let ty = parse_optional(lexer, Token::OpenAngle, |lexer| {
199        let ty = Box::new(Parse::parse(lexer)?);
200        let close = parse_token(lexer, Token::CloseAngle)?;
201        Ok((ty, close))
202    })?;
203    Ok(match ty {
204        Some((ty, close)) => ctor(
205            Some(ty),
206            SourceSpan::new(
207                span.offset().into(),
208                (close.offset() + close.len()) - span.offset(),
209            ),
210        ),
211        None => ctor(None, span),
212    })
213}
214
215/// Used to look ahead one token in the lexer.
216///
217/// The lookahead stores up to 10 attempted tokens.
218pub struct Lookahead {
219    next: Option<(LexerResult<Token>, SourceSpan)>,
220    attempts: [Option<Token>; 10],
221    span: SourceSpan,
222    count: usize,
223}
224
225impl Lookahead {
226    /// Creates a new lookahead from the given lexer.
227    pub fn new(lexer: &Lexer) -> Self {
228        Self {
229            next: lexer.peek(),
230            span: lexer.span(),
231            attempts: Default::default(),
232            count: 0,
233        }
234    }
235
236    /// Peeks to see if the next token matches the given token.
237    pub fn peek(&mut self, expected: Token) -> bool {
238        match &self.next {
239            Some((Ok(t), _)) if *t == expected => true,
240            _ => {
241                if self.count < self.attempts.len() {
242                    self.attempts[self.count] = Some(expected);
243                }
244
245                self.count += 1;
246                false
247            }
248        }
249    }
250
251    /// Returns an error based on the attempted tokens.
252    ///
253    /// Panics if no peeks were attempted.
254    pub fn error(self) -> Error {
255        let (found, span) = match self.next {
256            Some((Ok(token), span)) => (Some(token), span),
257            Some((Err(e), s)) => return (e, s).into(),
258            None => (None, self.span),
259        };
260
261        match self.count {
262            0 => unreachable!("lookahead had no attempts"),
263            1 => Error::Expected {
264                expected: self.attempts[0].unwrap(),
265                found,
266                span,
267            },
268            2 => Error::ExpectedEither {
269                first: self.attempts[0].unwrap(),
270                second: self.attempts[1].unwrap(),
271                found,
272                span,
273            },
274            _ => Error::ExpectedMultiple {
275                expected: self.attempts,
276                count: self.count,
277                found,
278                span,
279            },
280        }
281    }
282}
283
284pub(crate) trait Parse<'a>: Sized {
285    fn parse(lexer: &mut Lexer<'a>) -> ParseResult<Self>;
286}
287
288trait Peek {
289    fn peek(lookahead: &mut Lookahead) -> bool;
290}
291
292fn parse_delimited<'a, T: Parse<'a> + Peek>(
293    lexer: &mut Lexer<'a>,
294    until: Token,
295    with_commas: bool,
296) -> ParseResult<Vec<T>> {
297    let mut items = Vec::new();
298    loop {
299        let mut lookahead = Lookahead::new(lexer);
300        if lookahead.peek(until) {
301            break;
302        }
303
304        if !T::peek(&mut lookahead) {
305            return Err(lookahead.error());
306        }
307
308        items.push(Parse::parse(lexer)?);
309
310        if let Some((Ok(next), _)) = lexer.peek() {
311            if next == until {
312                break;
313            }
314
315            if with_commas {
316                parse_token(lexer, Token::Comma)?;
317            }
318        }
319    }
320
321    Ok(items)
322}
323
324/// Represents a package directive in the AST.
325#[derive(Debug, Clone, Serialize)]
326#[serde(rename_all = "camelCase")]
327pub struct PackageDirective<'a> {
328    /// The name of the package named by the directive.
329    pub package: PackageName<'a>,
330    /// The optional world being targeted by the package.
331    #[serde(skip_serializing_if = "Option::is_none")]
332    pub targets: Option<PackagePath<'a>>,
333}
334
335impl<'a> Parse<'a> for PackageDirective<'a> {
336    fn parse(lexer: &mut Lexer<'a>) -> ParseResult<Self> {
337        parse_token(lexer, Token::PackageKeyword)?;
338        let package = Parse::parse(lexer)?;
339        let targets = parse_optional(lexer, Token::TargetsKeyword, Parse::parse)?;
340        parse_token(lexer, Token::Semicolon)?;
341        Ok(Self { package, targets })
342    }
343}
344
345/// Represents a top-level WAC document.
346#[derive(Debug, Clone, Serialize)]
347#[serde(rename_all = "camelCase")]
348pub struct Document<'a> {
349    /// The doc comments for the package.
350    pub docs: Vec<DocComment<'a>>,
351    /// The package directive of the document.
352    pub directive: PackageDirective<'a>,
353    /// The statements in the document.
354    pub statements: Vec<Statement<'a>>,
355}
356
357impl<'a> Document<'a> {
358    /// Parses the given source string as a document.
359    ///
360    /// The given path is used for error reporting.
361    pub fn parse(source: &'a str) -> ParseResult<Self> {
362        let mut lexer = Lexer::new(source).map_err(Error::from)?;
363
364        let docs = Parse::parse(&mut lexer)?;
365        let directive = Parse::parse(&mut lexer)?;
366
367        let mut statements: Vec<Statement> = Default::default();
368        while lexer.peek().is_some() {
369            statements.push(Parse::parse(&mut lexer)?);
370        }
371
372        assert!(lexer.next().is_none(), "expected all tokens to be consumed");
373        Ok(Self {
374            docs,
375            directive,
376            statements,
377        })
378    }
379
380    /// Resolves the document.
381    ///
382    /// The returned resolution contains an encodable composition graph.
383    pub fn resolve(
384        &self,
385        packages: IndexMap<BorrowedPackageKey<'a>, Vec<u8>>,
386    ) -> ResolutionResult<Resolution<'_>> {
387        AstResolver::new(self).resolve(packages)
388    }
389}
390
391/// Represents a statement in the AST.
392#[derive(Debug, Clone, Serialize)]
393pub enum Statement<'a> {
394    /// An import statement.
395    Import(ImportStatement<'a>),
396    /// A type statement.
397    Type(TypeStatement<'a>),
398    /// A let statement.
399    Let(LetStatement<'a>),
400    /// An export statement.
401    Export(ExportStatement<'a>),
402}
403
404impl<'a> Parse<'a> for Statement<'a> {
405    fn parse(lexer: &mut Lexer<'a>) -> ParseResult<Self> {
406        let mut lookahead = Lookahead::new(lexer);
407        if ImportStatement::peek(&mut lookahead) {
408            Ok(Self::Import(Parse::parse(lexer)?))
409        } else if LetStatement::peek(&mut lookahead) {
410            Ok(Self::Let(Parse::parse(lexer)?))
411        } else if ExportStatement::peek(&mut lookahead) {
412            Ok(Self::Export(Parse::parse(lexer)?))
413        } else if TypeStatement::peek(&mut lookahead) {
414            Ok(Self::Type(Parse::parse(lexer)?))
415        } else {
416            Err(lookahead.error())
417        }
418    }
419}
420
421/// Represents an identifier in the AST.
422#[derive(Debug, Clone, Copy, Serialize)]
423#[serde(rename_all = "camelCase")]
424pub struct Ident<'a> {
425    /// The identifier string.
426    pub string: &'a str,
427    /// The span of the identifier.
428    pub span: SourceSpan,
429}
430
431impl<'a> Parse<'a> for Ident<'a> {
432    fn parse(lexer: &mut Lexer<'a>) -> ParseResult<Self> {
433        let span = parse_token(lexer, Token::Ident)?;
434        let id = lexer.source(span);
435        Ok(Self {
436            string: id.strip_prefix('%').unwrap_or(id),
437            span,
438        })
439    }
440}
441
442impl Peek for Ident<'_> {
443    fn peek(lookahead: &mut Lookahead) -> bool {
444        lookahead.peek(Token::Ident)
445    }
446}
447
448/// Represents a string in the AST.
449#[derive(Debug, Copy, Clone, Serialize)]
450#[serde(rename_all = "camelCase")]
451pub struct String<'a> {
452    /// The value of the string (without quotes).
453    pub value: &'a str,
454    /// The span of the string.
455    pub span: SourceSpan,
456}
457
458impl<'a> Parse<'a> for String<'a> {
459    fn parse(lexer: &mut Lexer<'a>) -> ParseResult<Self> {
460        let span = parse_token(lexer, Token::String)?;
461        let s = lexer.source(span);
462        Ok(Self {
463            value: s.strip_prefix('"').unwrap().strip_suffix('"').unwrap(),
464            span,
465        })
466    }
467}
468
469impl Peek for String<'_> {
470    fn peek(lookahead: &mut Lookahead) -> bool {
471        lookahead.peek(Token::String)
472    }
473}
474
475/// Represents a documentation comment in the AST.
476#[derive(Debug, Clone, Serialize)]
477#[serde(rename_all = "camelCase")]
478pub struct DocComment<'a> {
479    /// The comment string.
480    pub comment: &'a str,
481    /// The span of the comment.
482    pub span: SourceSpan,
483}
484
485impl<'a> Parse<'a> for Vec<DocComment<'a>> {
486    fn parse(lexer: &mut Lexer<'a>) -> ParseResult<Vec<DocComment<'a>>> {
487        Ok(lexer
488            .comments()
489            .map_err(Error::from)?
490            .into_iter()
491            .map(|(comment, span)| DocComment { comment, span })
492            .collect())
493    }
494}
495
496#[cfg(test)]
497mod test {
498    use super::*;
499    use pretty_assertions::assert_eq;
500
501    /// A test function for parsing a string into an AST node,
502    /// converting the AST node into a string, and then comparing
503    /// the result with the given expected string.
504    ///
505    /// Note that we don't expect the input string to be the same
506    /// as the output string, since the input string may contain
507    /// extra whitespace and comments that are not preserved in
508    /// the AST.
509    pub(crate) fn roundtrip(source: &str, expected: &str) -> ParseResult<()> {
510        let doc = Document::parse(source)?;
511        let mut s = std::string::String::new();
512        DocumentPrinter::new(&mut s, source, None)
513            .document(&doc)
514            .unwrap();
515        assert_eq!(s, expected, "unexpected AST output");
516        Ok(())
517    }
518
519    #[test]
520    fn document_roundtrip() {
521        roundtrip(
522            r#"/* ignore me */
523/// Doc comment for the package!
524package test:foo:bar@1.0.0;
525/// Doc comment #1!
526import foo: foo:bar/baz;
527/// Doc comment #2!
528type a = u32;
529/// Doc comment #3!
530record r {
531    x: string
532}
533/// Doc comment #4!
534interface i {
535    /// Doc comment #5!
536    f: func() -> r;
537}
538/// Doc comment #6!
539world w {
540    /// Doc comment #7!
541    import i;
542    /// Doc comment #8!
543    export f: func() -> a;
544}
545/// Doc comment #9!
546let x = new foo:bar { };
547/// Doc comment #10!
548export x as "foo";
549"#,
550            r#"/// Doc comment for the package!
551package test:foo:bar@1.0.0;
552
553/// Doc comment #1!
554import foo: foo:bar/baz;
555
556/// Doc comment #2!
557type a = u32;
558
559/// Doc comment #3!
560record r {
561    x: string,
562}
563
564/// Doc comment #4!
565interface i {
566    /// Doc comment #5!
567    f: func() -> r;
568}
569
570/// Doc comment #6!
571world w {
572    /// Doc comment #7!
573    import i;
574
575    /// Doc comment #8!
576    export f: func() -> a;
577}
578
579/// Doc comment #9!
580let x = new foo:bar {};
581
582/// Doc comment #10!
583export x as "foo";
584"#,
585        )
586        .unwrap()
587    }
588}