1use std::fmt;
4
5use crate::ast::*;
6
7#[derive(Debug, Clone, PartialEq)]
8pub struct ParseError {
9 pub line: usize,
10 pub column: usize,
11 pub message: String,
12}
13
14impl fmt::Display for ParseError {
15 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
16 write!(f, "{}:{}: {}", self.line, self.column, self.message)
17 }
18}
19
20impl std::error::Error for ParseError {}
21
22pub fn parse(source: &str) -> Result<Idl, ParseError> {
23 let mut p = Parser { src: source, pos: 0 };
24 p.parse_file()
25}
26
27struct Parser<'a> {
28 src: &'a str,
29 pos: usize,
30}
31
32fn is_ident_char(c: char) -> bool {
33 c.is_ascii_alphanumeric() || c == '_'
34}
35
36impl<'a> Parser<'a> {
37 fn rest(&self) -> &'a str {
38 &self.src[self.pos..]
39 }
40
41 fn peek(&self) -> Option<char> {
42 self.rest().chars().next()
43 }
44
45 fn eof(&self) -> bool {
46 self.pos >= self.src.len()
47 }
48
49 fn error<T>(&self, message: impl Into<String>) -> Result<T, ParseError> {
50 let before = &self.src[..self.pos.min(self.src.len())];
51 let line = before.matches('\n').count() + 1;
52 let column = before.rfind('\n').map_or(self.pos, |i| self.pos - i - 1) + 1;
53 Err(ParseError { line, column, message: message.into() })
54 }
55
56 fn skip_trivia(&mut self) -> Result<(), ParseError> {
58 loop {
59 let rest = self.rest();
60 if let Some(c) = rest.chars().next() {
61 if c.is_whitespace() {
62 self.pos += c.len_utf8();
63 continue;
64 }
65 }
66 if rest.starts_with("//") {
67 self.pos += rest.find('\n').unwrap_or(rest.len());
68 continue;
69 }
70 if rest.starts_with("/*") {
71 match rest.find("*/") {
72 Some(end) => self.pos += end + 2,
73 None => return self.error("unterminated comment"),
74 }
75 continue;
76 }
77 return Ok(());
78 }
79 }
80
81 fn eat(&mut self, c: char) -> Result<bool, ParseError> {
82 self.skip_trivia()?;
83 if self.peek() == Some(c) {
84 self.pos += c.len_utf8();
85 Ok(true)
86 } else {
87 Ok(false)
88 }
89 }
90
91 fn expect(&mut self, c: char) -> Result<(), ParseError> {
92 if self.eat(c)? {
93 Ok(())
94 } else {
95 let found = self.peek().map_or("end of file".to_string(), |c| format!("'{c}'"));
96 self.error(format!("expected '{c}', found {found}"))
97 }
98 }
99
100 fn try_ident(&mut self) -> Result<Option<String>, ParseError> {
101 self.skip_trivia()?;
102 let rest = self.rest();
103 let len = rest.find(|c| !is_ident_char(c)).unwrap_or(rest.len());
104 if len == 0 {
105 return Ok(None);
106 }
107 self.pos += len;
108 Ok(Some(rest[..len].to_string()))
109 }
110
111 fn ident(&mut self, what: &str) -> Result<String, ParseError> {
112 match self.try_ident()? {
113 Some(i) => Ok(i),
114 None => self.error(format!("expected {what}")),
115 }
116 }
117
118 fn parse_file(&mut self) -> Result<Idl, ParseError> {
119 let mut idl = Idl::default();
120 loop {
121 self.skip_trivia()?;
122 if self.eof() {
123 return Ok(idl);
124 }
125 if self.peek() == Some('@') {
126 idl.directives.push(self.parse_directive()?);
127 continue;
128 }
129 let attributes = self.parse_attributes()?;
130 let keyword = self.ident("'enum', 'struct' or 'interface'")?;
131 match keyword.as_str() {
132 "enum" => idl.enums.push(self.parse_enum(attributes)?),
133 "struct" => idl.structs.push(self.parse_struct(attributes)?),
134 "interface" => idl.interfaces.push(self.parse_interface(attributes)?),
135 other => return self.error(format!("unexpected '{other}'")),
136 }
137 }
138 }
139
140 fn parse_directive(&mut self) -> Result<Directive, ParseError> {
142 self.pos += 1; let rest = self.rest();
144 let name_len = rest.find(|c: char| c.is_whitespace()).unwrap_or(rest.len());
145 let name = rest[..name_len].to_string();
146 if name.is_empty() {
147 return self.error("expected directive name after '@'");
148 }
149 self.pos += name_len;
150 let line_end = self.rest().find('\n').unwrap_or(self.rest().len());
151 let line = &self.rest()[..line_end];
152 if let Some(start) = line.find("@@") {
153 self.pos += start + 2;
154 let Some(end) = self.rest().find("@@") else {
155 return self.error("unterminated '@@' block");
156 };
157 let value = self.rest()[..end].trim().replace("\r\n", "\n");
158 self.pos += end + 2;
159 Ok(Directive { name, value })
160 } else {
161 let value = line.trim().to_string();
162 self.pos += line_end;
163 Ok(Directive { name, value })
164 }
165 }
166
167 fn parse_attributes(&mut self) -> Result<Vec<Attribute>, ParseError> {
169 let mut out = Vec::new();
170 while self.eat('[')? {
171 let rest = self.rest();
172 let Some(end) = rest.find(']') else {
173 return self.error("unterminated attribute list");
174 };
175 let body = &rest[..end];
176 self.pos += end + 1;
177 let mut depth = 0;
179 let mut start = 0;
180 let mut parts = Vec::new();
181 for (i, c) in body.char_indices() {
182 match c {
183 '(' => depth += 1,
184 ')' => depth -= 1,
185 ',' if depth == 0 => {
186 parts.push(&body[start..i]);
187 start = i + 1;
188 }
189 _ => {}
190 }
191 }
192 parts.push(&body[start..]);
193 for part in parts {
194 let part = part.trim();
195 if part.is_empty() {
196 continue;
197 }
198 match part.find('(') {
199 Some(open) if part.ends_with(')') => out.push(Attribute {
200 name: part[..open].trim().to_string(),
201 value: Some(part[open + 1..part.len() - 1].trim().to_string()),
202 }),
203 _ => out.push(Attribute { name: part.to_string(), value: None }),
204 }
205 }
206 }
207 Ok(out)
208 }
209
210 fn parse_enum(&mut self, attributes: Vec<Attribute>) -> Result<Enum, ParseError> {
211 let name = self.ident("enum name")?;
212 self.expect('{')?;
213 let mut members = Vec::new();
214 loop {
215 if self.eat('}')? {
216 break;
217 }
218 let member = self.ident("enum member name")?;
219 let value = if self.eat('=')? {
220 self.skip_trivia()?;
221 let rest = self.rest();
222 let Some(end) = rest.find([',', '}']) else {
223 return self.error("unterminated enum");
224 };
225 let text = rest[..end].trim();
226 if text.is_empty() {
227 return self.error("expected enum member value");
228 }
229 self.pos += end;
230 Some(text.to_string())
231 } else {
232 None
233 };
234 members.push(EnumMember { name: member, value });
235 if !self.eat(',')? {
236 self.expect('}')?;
237 break;
238 }
239 }
240 self.eat(';')?;
241 Ok(Enum { attributes, name, members })
242 }
243
244 fn parse_struct(&mut self, attributes: Vec<Attribute>) -> Result<Struct, ParseError> {
245 let name = self.ident("struct name")?;
246 self.expect('{')?;
247 let mut fields = Vec::new();
248 while !self.eat('}')? {
249 let (ty, first) = self.parse_type_and_name("field")?;
251 fields.push(Field { ty: ty.clone(), name: first });
252 while self.eat(',')? {
253 let n = self.ident("field name")?;
254 fields.push(Field { ty: ty.clone(), name: n });
255 }
256 self.expect(';')?;
257 }
258 self.eat(';')?;
259 Ok(Struct { attributes, name, fields })
260 }
261
262 fn parse_type_and_name(&mut self, what: &str) -> Result<(TypeRef, String), ParseError> {
265 let mut words = vec![self.ident(&format!("{what} type"))?];
266 let mut pointers = 0;
267 let mut reference = false;
268 loop {
269 if self.eat('*')? {
270 if reference {
271 return self.error("pointer to reference is not supported");
272 }
273 pointers += 1;
274 } else if self.eat('&')? {
275 reference = true;
276 } else if let Some(word) = self.try_ident()? {
277 if pointers > 0 || reference {
278 let ty = TypeRef { name: words.join(" "), pointers, reference };
280 return Ok((ty, word));
281 }
282 words.push(word);
283 } else {
284 break;
285 }
286 }
287 if pointers > 0 || reference || words.len() < 2 {
288 return self.error(format!("expected {what} name"));
289 }
290 let name = words.pop().unwrap();
291 Ok((TypeRef { name: words.join(" "), pointers, reference }, name))
292 }
293
294 fn parse_interface(&mut self, attributes: Vec<Attribute>) -> Result<Interface, ParseError> {
295 let name = self.ident("interface name")?;
296 let base = if self.eat(':')? { Some(self.ident("base interface name")?) } else { None };
297 self.expect('{')?;
298 let mut methods = Vec::new();
299 while !self.eat('}')? {
300 let method_attrs = self.parse_attributes()?;
301 let (return_type, method_name) = self.parse_type_and_name("method")?;
302 self.expect('(')?;
303 let mut params = Vec::new();
304 if !self.eat(')')? {
305 loop {
306 let param_attrs = self.parse_attributes()?;
307 let (ty, pname) = self.parse_type_and_name("parameter")?;
308 params.push(Param { attributes: param_attrs, ty, name: pname });
309 if self.eat(',')? {
310 continue;
311 }
312 self.expect(')')?;
313 break;
314 }
315 }
316 self.expect(';')?;
317 methods.push(Method { attributes: method_attrs, return_type, name: method_name, params });
318 }
319 self.eat(';')?;
320 Ok(Interface { attributes, name, base, methods })
321 }
322}
323
324#[cfg(test)]
325mod tests {
326 use super::*;
327
328 const SAMPLE: &str = r#"
329@clr-namespace Foo.Bar
330@clr-map bool int
331@clr-map long IntPtr
332@cpp-preamble @@
333#pragma once
334#include "com.h"
335@@
336
337enum E { A, B = 5, C = A | B, }
338[class-enum]
339enum F { X = -1, Y }
340
341struct S { double X, Y; void* Data; byte B; }
342
343// line comment
344[uuid(809c652e-7396-11d2-9771-00a0c9b4d50c)]
345interface IRoot : IUnknown
346{
347 /** doc
348 * comment */
349 HRESULT Get(IRoot**ppv);
350 [intptr]void* Handle([intptr] void* a, [const] char* text, [const] S& s);
351 bool Flag();
352}
353
354[uuid(e5aca675-02b7-4129-aa79-d6e417210bda), cpp-virtual-inherits]
355interface IChild : IRoot { }
356
357[uuid(722aad20-a87b-4ce5-b50f-f05cfa4cda39)]
358interface INoBase { unsigned int Count(); }
359"#;
360
361 #[test]
362 fn parses_sample() {
363 let idl = parse(SAMPLE).unwrap();
364 assert_eq!(idl.directive("clr-namespace"), Some("Foo.Bar"));
365 assert_eq!(idl.directive_values("clr-map").collect::<Vec<_>>(), ["bool int", "long IntPtr"]);
366 assert_eq!(idl.directive("cpp-preamble"), Some("#pragma once\n#include \"com.h\""));
367
368 assert_eq!(idl.enums.len(), 2);
369 assert_eq!(idl.enums[0].members[1].value.as_deref(), Some("5"));
370 assert_eq!(idl.enums[0].members[2].value.as_deref(), Some("A | B"));
371 assert!(idl.enums[1].is_class_enum());
372 assert_eq!(idl.enums[1].members[0].value.as_deref(), Some("-1"));
373
374 let s = &idl.structs[0];
375 assert_eq!(s.fields.iter().map(|f| f.name.as_str()).collect::<Vec<_>>(), ["X", "Y", "Data", "B"]);
376 assert_eq!(s.fields[2].ty, TypeRef { name: "void".into(), pointers: 1, reference: false });
377
378 let root = &idl.interfaces[0];
379 assert_eq!(root.uuid(), Some("809c652e-7396-11d2-9771-00a0c9b4d50c"));
380 assert_eq!(root.methods.len(), 3);
381 assert_eq!(root.methods[0].params[0].ty.pointers, 2);
382 assert_eq!(root.methods[0].params[0].name, "ppv");
383 let handle = &root.methods[1];
384 assert!(has_attr(&handle.attributes, "intptr"));
385 assert_eq!(handle.return_type.pointers, 1);
386 assert!(handle.params[1].is_const());
387 assert!(handle.params[2].ty.reference);
388
389 assert!(idl.interfaces[1].cpp_virtual_inherits());
390 assert_eq!(idl.interfaces[1].base.as_deref(), Some("IRoot"));
391 assert_eq!(idl.interfaces[2].base, None);
392 assert_eq!(idl.interfaces[2].methods[0].return_type.name, "unsigned int");
393 }
394
395 #[test]
396 fn reports_position() {
397 let err = parse("enum E {\n A = ,\n}").unwrap_err();
398 assert_eq!(err.line, 2);
399 }
400}