Skip to main content

orrery_parser/
lib.rs

1//! # Orrery Parser
2//!
3//! Parser for the Orrery diagram language. This crate provides the
4//! parsing pipeline from source text to semantic diagram representation.
5//!
6//! ## Usage
7//!
8//! ```
9//! # use std::path::Path;
10//! # use bumpalo::Bump;
11//! # use orrery_parser::{parse, ElaborateConfig, InMemorySourceProvider};
12//! # fn main() -> Result<(), Box<dyn std::error::Error>> {
13//! let arena = Bump::new();
14//! let source = r#"
15//!     diagram component;
16//!     user: Rectangle;
17//!     server: Rectangle;
18//!     user -> server: "Request";
19//! "#;
20//!
21//! let mut provider = InMemorySourceProvider::new();
22//! provider.add_file("main.orr", source);
23//!
24//! let diagram = parse(&arena, Path::new("main.orr"), provider, ElaborateConfig::default())
25//!     .map_err(|e| e.to_string())?;
26//! # Ok(())
27//! # }
28//! ```
29
30pub mod error;
31pub mod source_map;
32pub mod source_provider;
33
34mod builtin_types;
35mod desugar;
36mod elaborate;
37mod elaborate_utils;
38mod file_id;
39mod lexer;
40mod parser;
41#[cfg(test)]
42mod parser_tests;
43mod parser_types;
44mod resolver;
45mod span;
46mod tokens;
47mod validate;
48
49pub use elaborate::ElaborateConfig;
50pub use source_provider::{InMemorySourceProvider, SourceProvider};
51pub use span::Span;
52
53use std::path::Path;
54
55use bumpalo::Bump;
56
57use orrery_core::semantic::Diagram;
58
59use elaborate::Builder;
60use error::ParseError;
61use resolver::Resolver;
62
63/// Parse an Orrery file into a semantic diagram.
64///
65/// This is the main entry point for parsing Orrery diagram source code.
66/// It orchestrates the complete parsing pipeline:
67///
68/// 1. **Resolve** — Recursively load the root file and all its imports via
69///    the [`SourceProvider`], building a virtual address space in the
70///    `SourceMap` and populating the import tree. For each file:
71///    - **Tokenize** — Convert source text to tokens
72///    - **Parse** — Build an AST from tokens
73/// 2. **Desugar** — Normalize syntax sugar and flatten imported types
74/// 3. **Validate** — Check semantic validity
75/// 4. **Elaborate** — Transform to semantic model
76///
77/// # Arguments
78///
79/// * `arena` — A [`Bump`] arena that owns all source text. The arena must
80///   outlive the returned error (if any), since [`ParseError`] borrows
81///   source data from it.
82/// * `root_path` — Path to the root/entry Orrery file.
83/// * `provider` — A [`SourceProvider`] implementation that resolves import
84///   paths and reads source text.
85/// * `config` — Configuration for the elaboration phase.
86///
87/// # Returns
88///
89/// Returns the parsed [`orrery_core::semantic::Diagram`] on success,
90/// or a [`ParseError`] with location information on failure.
91///
92/// # Example
93///
94/// ```
95/// # use std::path::Path;
96/// # use bumpalo::Bump;
97/// # use orrery_parser::{parse, ElaborateConfig, InMemorySourceProvider};
98/// # fn main() -> Result<(), Box<dyn std::error::Error>> {
99/// let arena = Bump::new();
100/// let mut provider = InMemorySourceProvider::new();
101/// provider.add_file("main.orr", "diagram component; box: Rectangle;");
102///
103/// let diagram = parse(&arena, Path::new("main.orr"), provider, ElaborateConfig::default())
104///     .map_err(|e| e.to_string())?;
105/// # Ok(())
106/// # }
107/// ```
108pub fn parse<'a, P: SourceProvider>(
109    arena: &'a Bump,
110    root_path: &Path,
111    provider: P,
112    config: ElaborateConfig,
113) -> Result<Diagram, ParseError<'a>> {
114    // Step 1: Resolve — load all files recursively via the provider
115    let resolver = Resolver::new(arena, provider);
116    let resolved = resolver.resolve(root_path)?;
117    let (file_ast, source_map) = resolved.into_parts();
118
119    // Step 2: Desugar — normalize syntax sugar, flatten imported types
120    let desugared = desugar::desugar(file_ast);
121
122    // Step 3: Validate — check semantic validity
123    if let Err(diags) = validate::validate(&desugared) {
124        return Err(ParseError::new(diags, source_map));
125    }
126
127    // Step 4: Elaborate — transform to semantic model
128    let builder = Builder::new(config);
129    builder
130        .build(&desugared)
131        .map_err(|diag| ParseError::from_diagnostic(diag, source_map))
132}