cadi_core/
parser.rs

1use pest::Parser;
2use pest::iterators::Pair;
3use pest_derive::Parser;
4use std::collections::HashMap;
5use crate::ast::*;
6
7#[derive(Parser)]
8#[grammar = "src/grammar.pest"] 
9// Note: path is relative to Cargo.toml of the crate usually
10pub struct CadlParser;
11
12pub fn parse_file(input: &str) -> Result<CadlDocument, String> {
13    let mut pairs = CadlParser::parse(Rule::file, input)
14        .map_err(|e| format!("{}", e))?;
15
16    let mut doc = CadlDocument {
17        interfaces: Vec::new(),
18        implementations: Vec::new(),
19        constraints: Vec::new(),
20        top_level_annotations: Vec::new(),
21    };
22
23    for pair in pairs.next().unwrap().into_inner() {
24        match pair.as_rule() {
25            Rule::interface_def => {
26                doc.interfaces.push(parse_interface(pair));
27            }
28            Rule::impl_def => {
29                doc.implementations.push(parse_impl(pair));
30            }
31            Rule::annotation => {
32                let ann = parse_annotation(pair);
33                // Check if it's strictly a constraint block (special case in AST usually, 
34                // but here we might treat @constraints as just another annotation or promote it)
35                // For the spec, @constraints { ... } is a block.
36                if let Annotation::Unknown(name, map) = &ann {
37                    if name == "constraints" {
38                        doc.constraints.push(ConstraintDef {
39                            name: None,
40                            rules: Vec::new(), // TODO: parse internals better
41                            other: map.clone(),
42                        });
43                        continue;
44                    }
45                }
46                doc.top_level_annotations.push(ann);
47            }
48            Rule::EOI => (),
49            _ => (),
50        }
51    }
52
53    Ok(doc)
54}
55
56fn parse_interface(pair: Pair<Rule>) -> InterfaceDef {
57    let mut inner = pair.into_inner();
58    // input string: @interface Name { ... }
59    // first token is "interface" keyword (implicit), second is identifier
60    // Wait, grammar: "@interface" ~ identifier ~ "{" ~ interface_content ~ "}"
61    
62    let name = inner.next().unwrap().as_str().to_string();
63    let content = inner.next().unwrap();
64
65    let mut methods = Vec::new();
66    let mut annotations = Vec::new();
67
68    for item in content.into_inner() {
69        match item.as_rule() {
70            Rule::method_def => {
71                methods.push(parse_method(item));
72            }
73            Rule::annotation => {
74                annotations.push(parse_annotation(item));
75            }
76            _ => {}
77        }
78    }
79
80    InterfaceDef {
81        name,
82        methods,
83        annotations,
84    }
85}
86
87fn parse_method(pair: Pair<Rule>) -> MethodDef {
88    let mut inner = pair.into_inner();
89    let name = inner.next().unwrap().as_str().to_string();
90    
91    // Check if params exist
92    let next = inner.next().unwrap();
93    let (params, return_type) = if next.as_rule() == Rule::params {
94        let params = parse_params(next);
95        (params, inner.next().unwrap().as_str().trim().to_string())
96    } else {
97        (Vec::new(), next.as_str().trim().to_string())
98    };
99
100    MethodDef {
101        name,
102        params,
103        return_type,
104    }
105}
106
107fn parse_params(pair: Pair<Rule>) -> Vec<ParamDef> {
108    let mut params = Vec::new();
109    for p in pair.into_inner() {
110        let mut inner = p.into_inner();
111        let name = inner.next().unwrap().as_str().to_string();
112        let type_name = inner.next().unwrap().as_str().to_string();
113        params.push(ParamDef { name, type_name });
114    }
115    params
116}
117
118fn parse_annotation(pair: Pair<Rule>) -> Annotation {
119    let mut inner = pair.into_inner();
120    let name = inner.next().unwrap().as_str().to_string();
121    let content = inner.next().unwrap(); // object or block_body
122
123    let map = parse_object_like(content);
124
125    match name.as_str() {
126        "contract" => Annotation::Contract(from_map_contract(map)),
127        "effects" => Annotation::Effects(from_map_effects(map)),
128        "abi" => Annotation::Abi(from_map_abi(map)),
129        "data_format" => Annotation::DataFormat(from_map_data_format(map)),
130        "resources" => Annotation::Resources(from_map_resources(map)),
131        "protocol" => Annotation::Protocol(from_map_protocol(map)),
132        "numerical" => Annotation::Numerical(from_map_numerical(map)),
133        "observability" => Annotation::Observability(from_map_observability(map)),
134        "permissions" => Annotation::Permissions(from_map_permissions(map)),
135        _ => Annotation::Unknown(name, map),
136    }
137}
138
139fn parse_object_like(pair: Pair<Rule>) -> HashMap<String, Value> {
140    let mut map = HashMap::new();
141    for item in pair.into_inner() {
142        match item.as_rule() {
143            Rule::pair => {
144                let mut inner = item.into_inner();
145                let key = inner.next().unwrap().as_str().to_string();
146                let val = parse_value(inner.next().unwrap());
147                map.insert(key, val);
148            }
149            Rule::named_block => {
150                let mut inner = item.into_inner();
151                let key = inner.next().unwrap().as_str().to_string();
152                let content = inner.next().unwrap();
153                let val = Value::Object(parse_object_like(content));
154                map.insert(key, val);
155            }
156            // TODO: handle nested annotations in blocks if necessary
157            _ => {}
158        }
159    }
160    map
161}
162
163fn parse_value(pair: Pair<Rule>) -> Value {
164    let inner = pair.into_inner().next().unwrap();
165    match inner.as_rule() {
166        Rule::string => {
167             // Remove quotes
168             let s = inner.as_str();
169             Value::String(s[1..s.len()-1].to_string())
170        },
171        Rule::number => {
172            let n = inner.as_str().parse::<f64>().unwrap_or(0.0);
173            Value::Number(n)
174        },
175        Rule::boolean => {
176            let b = inner.as_str() == "true";
177            Value::Bool(b)
178        },
179        Rule::identifier => Value::String(inner.as_str().to_string()),
180        Rule::array => {
181             let vals = inner.into_inner().map(parse_value).collect();
182             Value::Array(vals)
183        },
184        Rule::object => {
185             Value::Object(parse_object_like(inner))
186        },
187        Rule::null_val => Value::String("null".to_string()),
188        _ => Value::String(inner.as_str().to_string()),
189    }
190}
191
192fn parse_impl(pair: Pair<Rule>) -> ImplDef {
193    let mut inner = pair.into_inner();
194    let name = inner.next().unwrap().as_str().to_string();
195    let content = inner.next().unwrap();
196
197    let mut attributes = HashMap::new();
198    let mut annotations = Vec::new();
199
200    for item in content.into_inner() {
201        match item.as_rule() {
202            Rule::pair => {
203                let mut p_inner = item.into_inner();
204                let key = p_inner.next().unwrap().as_str().to_string();
205                let val = parse_value(p_inner.next().unwrap());
206                attributes.insert(key, val);
207            }
208            Rule::annotation => {
209                annotations.push(parse_annotation(item));
210            }
211            _ => {}
212        }
213    }
214
215    ImplDef {
216        name,
217        attributes,
218        annotations,
219    }
220}
221
222// Helpers to convert HashMap<String, Value> to specific structs
223fn from_map_contract(mut map: HashMap<String, Value>) -> ContractDef {
224    ContractDef {
225        codec: extract_string(&mut map, "codec"),
226        profile: extract_string(&mut map, "profile"),
227        container: extract_string(&mut map, "container"),
228        ensures: extract_string_list(&mut map, "ensures"),
229        complexity: extract_string_map(&mut map, "complexity"),
230        other: map,
231    }
232}
233
234fn from_map_effects(mut map: HashMap<String, Value>) -> EffectsDef {
235    EffectsDef {
236        concurrency: extract_string(&mut map, "concurrency"),
237        io: extract_string_list(&mut map, "io"),
238        memory: extract_string(&mut map, "memory"),
239        mutates: extract_string_list(&mut map, "mutates"),
240        reads: extract_string_list(&mut map, "reads"),
241        blocking: extract_string(&mut map, "blocking"),
242        async_exec: extract_string(&mut map, "async"),
243        other: map,
244    }
245}
246
247// Minimal stubs for others to allow compilation for now
248fn from_map_abi(mut map: HashMap<String, Value>) -> AbiDef {
249    AbiDef {
250        string_encoding: extract_string(&mut map, "string_encoding"),
251        memory_ownership: extract_string_map(&mut map, "memory_ownership"),
252        error_model: extract_string(&mut map, "error_model"),
253        other: map,
254    }
255}
256
257fn from_map_data_format(mut map: HashMap<String, Value>) -> DataFormatDef {
258    DataFormatDef {
259        input: extract_data_format_spec_map(&mut map, "input"),
260        output: extract_data_format_spec_map(&mut map, "output"),
261    }
262}
263
264fn extract_data_format_spec_map(map: &mut HashMap<String, Value>, key: &str) -> HashMap<String, DataFormatSpec> {
265    match map.remove(key) {
266        Some(Value::Object(obj)) => obj.into_iter().map(|(k, v)| {
267            let spec = match v {
268                Value::Object(mut inner_map) => DataFormatSpec {
269                    format: extract_string(&mut inner_map, "format"),
270                    schema: extract_string(&mut inner_map, "schema"),
271                    value_range: extract_number_list(&mut inner_map, "value_range"),
272                    other: inner_map,
273                },
274                _ => DataFormatSpec {
275                    format: None,
276                    schema: None,
277                    value_range: None,
278                    other: HashMap::new(),
279                }
280            };
281            (k, spec)
282        }).collect(),
283        _ => HashMap::new(),
284    }
285}
286
287fn from_map_resources(mut map: HashMap<String, Value>) -> ResourcesDef {
288    ResourcesDef {
289        requires: extract_string_list(&mut map, "requires"),
290        memory: extract_string_map(&mut map, "memory"),
291        cpu_time: extract_string(&mut map, "cpu_time"),
292        gpu: extract_string_map(&mut map, "gpu"),
293        other: map,
294    }
295}
296
297fn from_map_protocol(mut map: HashMap<String, Value>) -> ProtocolDef {
298    ProtocolDef {
299        states: extract_string_list(&mut map, "states"),
300        initial: extract_string(&mut map, "initial"),
301        transitions: extract_string_list(&mut map, "transitions"),
302        other: map,
303    }
304}
305
306fn from_map_numerical(mut map: HashMap<String, Value>) -> NumericalDef {
307    NumericalDef {
308        precision: extract_string(&mut map, "precision"),
309        error_bounds: extract_string_map(&mut map, "error_bounds"),
310        other: map,
311    }
312}
313
314fn from_map_observability(mut map: HashMap<String, Value>) -> ObservabilityDef {
315    // Just putting raw values for now
316    ObservabilityDef {
317        logging: extract_map(&mut map, "logging"),
318        metrics: extract_map(&mut map, "metrics"),
319        tracing: extract_map(&mut map, "tracing"),
320    }
321}
322
323fn from_map_permissions(mut map: HashMap<String, Value>) -> PermissionsDef {
324    PermissionsDef {
325        allow: extract_string_list(&mut map, "allow"),
326        deny: extract_string_list(&mut map, "deny"),
327        sandbox: extract_string_map(&mut map, "sandbox"),
328    }
329}
330
331// Extraction helpers
332fn extract_string(map: &mut HashMap<String, Value>, key: &str) -> Option<String> {
333    match map.remove(key) {
334        Some(Value::String(s)) => Some(s),
335        Some(v) => Some(format!("{:?}", v)), // Fallback
336        None => None,
337    }
338}
339
340fn extract_string_list(map: &mut HashMap<String, Value>, key: &str) -> Vec<String> {
341    match map.remove(key) {
342        Some(Value::Array(arr)) => arr.into_iter().filter_map(|v| match v {
343            Value::String(s) => Some(s),
344            _ => None,
345        }).collect(),
346        _ => Vec::new(),
347    }
348}
349
350fn extract_string_map(map: &mut HashMap<String, Value>, key: &str) -> HashMap<String, String> {
351    match map.remove(key) {
352        Some(Value::Object(obj)) => obj.into_iter().map(|(k, v)| (k, match v {
353            Value::String(s) => s,
354            _ => format!("{:?}", v),
355        })).collect(),
356        _ => HashMap::new(),
357    }
358}
359
360fn extract_map(map: &mut HashMap<String, Value>, key: &str) -> HashMap<String, Value> {
361    match map.remove(key) {
362        Some(Value::Object(obj)) => obj,
363        _ => HashMap::new(),
364    }
365}
366
367fn extract_number_list(map: &mut HashMap<String, Value>, key: &str) -> Option<Vec<f64>> {
368    match map.remove(key) {
369        Some(Value::Array(arr)) => Some(arr.into_iter().filter_map(|v| match v {
370            Value::Number(n) => Some(n),
371            _ => None,
372        }).collect()),
373        _ => None,
374    }
375}