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}