pink_runtime/parser/
mod.rs1mod resolvers;
2mod standalone;
3#[cfg(test)]
4mod test;
5
6pub use standalone::expression;
7
8use std::{collections::BTreeMap, error::Error, fmt::Display, io, path::PathBuf};
9
10use regex_macro::regex;
12
13use crate::engine::{Runtime, Structure, StructureError};
14
15use self::standalone::{definition, domain, get_domain, get_reserved, parse_use, reserve};
16
17fn strip_comments(input: String) -> String {
21 input.replace(regex!("#.*"), "")
22}
23
24pub fn parse(name: &str, resolver: impl Fn(&str) -> Option<String>) -> Result<Runtime, ParseError> {
25 let mut partial_runtime =
26 BTreeMap::from([("intrinsic".to_string(), Some(Structure::intrinsic()))]);
27
28 let input = resolver(name).unwrap();
29
30 parse_into_runtime(&input, name, &resolver, &mut partial_runtime)?;
31
32 let runtime = partial_runtime
33 .into_iter()
34 .filter_map(|(name, structure)| structure.map(|structure| (name, structure)))
35 .collect();
36
37 Ok(Runtime::new(runtime))
38}
39
40type PartialRuntime = BTreeMap<String, Option<Structure>>;
42
43fn parse_into_runtime(
44 input: &str,
45 name: &str,
46 resolver: &impl Fn(&str) -> Option<String>,
47 runtime: &mut PartialRuntime,
48) -> Result<(), ParseError> {
49 let input = strip_comments(input.to_string());
50
51 let (input, domain) = domain(&input)?;
52 let (input, reserved) = reserve(input)?;
53 let (input, dependencies) = parse_use(input)?;
54
55 for dependency in dependencies {
56 match runtime.get(&dependency) {
57 Some(Some(_)) => continue,
59
60 Some(None) => {
62 return Err(ParseError::CircularDependency {
63 cycle: vec![dependency.clone(), name.to_string()],
64 })
65 }
66
67 None => (),
69 }
70
71 runtime.insert(dependency.clone(), None);
72
73 let dependecy_program = resolver(&dependency)
74 .expect("Hande failures of resolver (basically not finding files)");
75
76 match parse_into_runtime(&dependecy_program, &dependency, resolver, runtime) {
77 Ok(()) => (),
78
79 Err(ParseError::CircularDependency { mut cycle }) => {
81 cycle.push(dependency);
82 return Err(ParseError::CircularDependency { cycle });
83 }
84
85 Err(err) => return Err(err),
86 };
87 }
88
89 let full_domain = domain.iter().chain(get_domain(runtime)).collect();
90 let full_reserved = reserved.iter().chain(get_reserved(runtime)).collect();
91
92 let mut definitions = Vec::new();
93 let mut input = input;
94 while let Ok((rest, mut parsed_definitions)) = definition(input, &full_domain, &full_reserved) {
95 input = rest;
96 definitions.append(&mut parsed_definitions);
97 }
98
99 let structure = match Structure::create(domain, reserved, definitions) {
100 Ok(structure) => structure,
101 Err(StructureError::DomainAndReservedOverlap { culprit }) => {
102 return Err(ParseError::DomainAndReservedOverlap { culprit })
103 }
104 };
105
106 runtime.insert(name.to_string(), Some(structure));
107
108 Ok(())
109}
110
111pub fn parse_file(path: PathBuf) -> Result<Runtime, ParseError> {
112 let (root, name) = resolvers::get_root_and_name(path).unwrap();
113 let resolver = resolvers::file_resolver(root);
114
115 parse(name.as_str(), resolver)
116}
117
118#[derive(Debug)]
119pub enum ParseError {
120 Expected { expected: String, found: String },
121 DomainAndReservedOverlap { culprit: String },
122 CircularDependency { cycle: Vec<String> },
123 UknownToken(String),
124
125 Io(io::Error),
127}
128
129impl Display for ParseError {
130 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
131 let message = match self {
132 ParseError::Expected { expected, found } => {
133 format!("Expected {}, found {}", expected, found)
134 }
135 ParseError::DomainAndReservedOverlap { culprit } => {
136 format!("Domain and reserved overlap: {}", culprit)
137 }
138 ParseError::CircularDependency { cycle } => {
139 format!("Found circular dependency: {}", cycle.join(" -> "))
140 }
141 ParseError::UknownToken(token) => format!("Unknown token in: \"{}\"", token),
142 ParseError::Io(e) => format!("IO error: {}", e),
143 };
144
145 write!(f, "{}", message)
146 }
147}
148
149impl Error for ParseError {}