1use pest::{Parser, Position, Span};
16
17use super::parser::{consume_token, MojomParser, Pairs, Rule};
18
19#[derive(Debug, Clone, PartialEq)]
20pub struct Range {
21 pub start: usize,
22 pub end: usize,
23}
24
25impl<'a> From<Span<'a>> for Range {
26 fn from(span: Span<'a>) -> Range {
27 Range {
28 start: span.start(),
29 end: span.end(),
30 }
31 }
32}
33
34fn skip_attribute_list(pairs: &mut Pairs) {
36 match pairs.peek().unwrap().as_rule() {
37 Rule::attribute_section => {
38 pairs.next();
39 }
40 _ => (),
41 }
42}
43
44fn consume_semicolon(pairs: &mut Pairs) {
45 match pairs.next().unwrap().as_rule() {
46 Rule::t_semicolon => (),
47 _ => unreachable!(),
48 };
49}
50
51fn consume_as_range(pairs: &mut Pairs) -> Range {
52 pairs.next().unwrap().as_span().into()
53}
54
55#[derive(Debug, Clone, PartialEq)]
56pub struct Module {
57 pub name: Range,
58}
59
60fn into_module(mut pairs: Pairs) -> Module {
61 skip_attribute_list(&mut pairs);
62 consume_token(Rule::t_module, &mut pairs);
63 let name = consume_as_range(&mut pairs);
64 consume_semicolon(&mut pairs);
65 Module { name: name }
66}
67
68#[derive(Debug, PartialEq)]
69pub struct Import {
70 pub path: Range,
71}
72
73fn into_import(mut pairs: Pairs) -> Import {
74 skip_attribute_list(&mut pairs);
75 consume_token(Rule::t_import, &mut pairs);
76 let path = consume_as_range(&mut pairs);
77 consume_semicolon(&mut pairs);
78 Import { path: path }
79}
80
81#[derive(Debug, PartialEq)]
82pub struct Const {
83 pub typ: Range,
84 pub name: Range,
85 pub value: Range,
86}
87
88fn into_const(mut pairs: Pairs) -> Const {
89 skip_attribute_list(&mut pairs);
90 consume_token(Rule::t_const, &mut pairs);
91 let pair = pairs.next().unwrap();
92 let typ = pair.as_span().into();
93 let name = consume_as_range(&mut pairs);
94 consume_token(Rule::t_equal, &mut pairs);
95 let value = consume_as_range(&mut pairs);
96 consume_semicolon(&mut pairs);
97 Const {
98 typ: typ,
99 name: name,
100 value: value,
101 }
102}
103
104#[derive(Debug, PartialEq)]
105pub struct EnumValue {
106 pub name: Range,
107 pub value: Option<Range>,
108}
109
110fn into_enum_value(mut pairs: Pairs) -> EnumValue {
111 skip_attribute_list(&mut pairs);
112 let name = consume_as_range(&mut pairs);
113 if let Some(item) = pairs.next() {
115 assert!(item.as_rule() == Rule::t_equal);
116 }
117 let value = pairs.next().map(|item| item.as_span().into());
118 EnumValue {
119 name: name,
120 value: value,
121 }
122}
123
124#[derive(Debug, PartialEq)]
125pub struct Enum {
126 pub name: Range,
127 pub values: Vec<EnumValue>,
128}
129
130fn into_enum(mut pairs: Pairs) -> Enum {
131 skip_attribute_list(&mut pairs);
132 let name = consume_as_range(&mut pairs);
133 let mut values = Vec::new();
134 for item in pairs {
135 match item.as_rule() {
136 Rule::enum_block => {
137 let mut pairs = item.into_inner();
138 consume_token(Rule::t_lbrace, &mut pairs);
139 for item in pairs {
140 let value = match item.as_rule() {
141 Rule::enum_value => into_enum_value(item.into_inner()),
142 Rule::t_comma => continue,
143 Rule::t_rbrace => break,
144 _ => unreachable!(),
145 };
146 values.push(value);
147 }
148 }
149 Rule::t_semicolon => break,
150 _ => unreachable!(),
151 }
152 }
153 Enum {
154 name: name,
155 values: values,
156 }
157}
158
159#[derive(Debug, PartialEq)]
160pub struct StructField {
161 pub typ: Range,
162 pub name: Range,
163 pub ordinal: Option<Range>,
164 pub default: Option<Range>,
165}
166
167fn into_struct_field(mut pairs: Pairs) -> StructField {
168 skip_attribute_list(&mut pairs);
169 let typ = consume_as_range(&mut pairs);
170 let name = consume_as_range(&mut pairs);
171 let mut res = StructField {
172 typ: typ,
173 name: name,
174 ordinal: None,
175 default: None,
176 };
177 for item in pairs {
178 match item.as_rule() {
179 Rule::ordinal_value => res.ordinal = Some(item.as_span().into()),
180 Rule::default => {
181 let mut pairs = item.into_inner();
182 consume_token(Rule::t_equal, &mut pairs);
183 res.default = Some(pairs.next().unwrap().as_span().into());
184 }
185 Rule::t_semicolon => break,
186 _ => unreachable!(),
187 }
188 }
189 res
190}
191
192#[derive(Debug, PartialEq)]
193pub enum StructBody {
194 Const(Const),
195 Enum(Enum),
196 Field(StructField),
197}
198
199#[derive(Debug, PartialEq)]
200pub struct Struct {
201 pub name: Range,
202 pub members: Vec<StructBody>,
203}
204
205fn into_struct_members(mut pairs: Pairs) -> Vec<StructBody> {
206 consume_token(Rule::t_lbrace, &mut pairs);
207 let mut members = Vec::new();
208 for item in pairs {
209 if item.as_rule() == Rule::t_rbrace {
210 break;
211 }
212 let struct_item = item.into_inner().next().unwrap();
214 let member = match struct_item.as_rule() {
215 Rule::const_stmt => StructBody::Const(into_const(struct_item.into_inner())),
216 Rule::enum_stmt => StructBody::Enum(into_enum(struct_item.into_inner())),
217 Rule::struct_field => StructBody::Field(into_struct_field(struct_item.into_inner())),
218 _ => unreachable!(),
219 };
220 members.push(member);
221 }
222 members
223}
224
225fn into_struct(mut pairs: Pairs) -> Struct {
226 skip_attribute_list(&mut pairs);
227 consume_token(Rule::t_struct, &mut pairs);
228 let name = consume_as_range(&mut pairs);
229 let item = pairs.next().unwrap();
230 match item.as_rule() {
231 Rule::t_semicolon => {
232 return Struct {
233 name: name,
234 members: Vec::new(),
235 };
236 }
237 Rule::struct_body => {
238 let members = into_struct_members(item.into_inner());
239 consume_semicolon(&mut pairs);
240 return Struct {
241 name: name,
242 members: members,
243 };
244 }
245 _ => unreachable!(),
246 }
247}
248
249#[derive(Debug, PartialEq)]
250pub struct UnionField {
251 pub typ: Range,
252 pub name: Range,
253 pub ordinal: Option<Range>,
254}
255
256fn into_union_field(mut pairs: Pairs) -> UnionField {
257 skip_attribute_list(&mut pairs);
258 let typ = consume_as_range(&mut pairs);
259 let name = consume_as_range(&mut pairs);
260 let mut ordinal = None;
261 for item in pairs {
262 match item.as_rule() {
263 Rule::ordinal_value => ordinal = Some(item.as_span().into()),
264 Rule::t_semicolon => break,
265 _ => unreachable!(),
266 }
267 }
268 UnionField {
269 typ: typ,
270 name: name,
271 ordinal: ordinal,
272 }
273}
274
275#[derive(Debug, PartialEq)]
276pub struct Union {
277 pub name: Range,
278 pub fields: Vec<UnionField>,
279}
280
281fn into_union(mut pairs: Pairs) -> Union {
282 skip_attribute_list(&mut pairs);
283 consume_token(Rule::t_union, &mut pairs);
284 let name = consume_as_range(&mut pairs);
285 consume_token(Rule::t_lbrace, &mut pairs);
286 let mut fields = Vec::new();
287 loop {
288 let item = pairs.next().unwrap();
289 let item = match item.as_rule() {
290 Rule::union_field => into_union_field(item.into_inner()),
291 Rule::t_rbrace => break,
292 _ => unreachable!(),
293 };
294 fields.push(item);
295 }
296 consume_semicolon(&mut pairs);
297 Union {
298 name: name,
299 fields: fields,
300 }
301}
302
303#[derive(Debug, PartialEq)]
304pub struct Parameter {
305 pub typ: Range,
306 pub name: Range,
307 pub ordinal: Option<Range>,
308}
309
310fn into_parameter(mut pairs: Pairs) -> Parameter {
311 let typ = consume_as_range(&mut pairs);
312 let name = consume_as_range(&mut pairs);
313 let ordinal = pairs.next().map(|ord| ord.as_span().into());
314 Parameter {
315 typ: typ,
316 name: name,
317 ordinal: ordinal,
318 }
319}
320
321fn parameter_list(mut pairs: Pairs) -> Vec<Parameter> {
322 consume_token(Rule::t_lparen, &mut pairs);
323 let mut params = Vec::new();
324 for item in pairs {
325 let param = match item.as_rule() {
326 Rule::parameter => into_parameter(item.into_inner()),
327 Rule::t_comma => continue,
328 Rule::t_rparen => break,
329 _ => unreachable!(),
330 };
331 params.push(param);
332 }
333 params
334}
335
336#[derive(Debug, PartialEq)]
337pub struct Response {
338 pub params: Vec<Parameter>,
339}
340
341fn into_response(mut pairs: Pairs) -> Response {
342 consume_token(Rule::t_arrow, &mut pairs);
343 let params = parameter_list(pairs.next().unwrap().into_inner());
344 Response { params: params }
345}
346
347#[derive(Debug, PartialEq)]
348pub struct Method {
349 pub name: Range,
350 pub ordinal: Option<Range>,
351 pub params: Vec<Parameter>,
352 pub response: Option<Response>,
353}
354
355fn into_method(mut pairs: Pairs) -> Method {
356 skip_attribute_list(&mut pairs);
357 let name = consume_as_range(&mut pairs);
358 let ordinal = match pairs.peek().unwrap().as_rule() {
359 Rule::ordinal_value => pairs.next().map(|ord| ord.as_span().into()),
360 _ => None,
361 };
362 let params = parameter_list(pairs.next().unwrap().into_inner());
363 let mut response = None;
364 for item in pairs {
365 match item.as_rule() {
366 Rule::response => response = Some(into_response(item.into_inner())),
367 Rule::t_semicolon => break,
368 _ => unreachable!(),
369 }
370 }
371 Method {
372 name: name,
373 ordinal: ordinal,
374 params: params,
375 response: response,
376 }
377}
378
379#[derive(Debug, PartialEq)]
380pub enum InterfaceMember {
381 Const(Const),
382 Enum(Enum),
383 Method(Method),
384}
385
386fn into_interface_member(mut pairs: Pairs) -> InterfaceMember {
387 let member = pairs.next().unwrap();
388 match member.as_rule() {
389 Rule::const_stmt => InterfaceMember::Const(into_const(member.into_inner())),
390 Rule::enum_stmt => InterfaceMember::Enum(into_enum(member.into_inner())),
391 Rule::method_stmt => InterfaceMember::Method(into_method(member.into_inner())),
392 _ => unreachable!(),
393 }
394}
395
396#[derive(Debug, PartialEq)]
397pub struct Interface {
398 pub name: Range,
399 pub members: Vec<InterfaceMember>,
400}
401
402fn into_interface(mut pairs: Pairs) -> Interface {
403 skip_attribute_list(&mut pairs);
404 consume_token(Rule::t_interface, &mut pairs);
405 let name = consume_as_range(&mut pairs);
406 consume_token(Rule::t_lbrace, &mut pairs);
407 let mut members = Vec::new();
408 loop {
410 let item = pairs.next().unwrap(); match item.as_rule() {
412 Rule::interface_body => {
413 let member = into_interface_member(item.into_inner());
414 members.push(member);
415 }
416 Rule::t_rbrace => break,
417 _ => unreachable!(),
418 }
419 }
420 consume_semicolon(&mut pairs);
421 Interface {
422 name: name,
423 members: members,
424 }
425}
426
427#[derive(Debug, PartialEq)]
428pub enum Statement {
429 Module(Module),
430 Import(Import),
431 Interface(Interface),
432 Struct(Struct),
433 Union(Union),
434 Enum(Enum),
435 Const(Const),
436}
437
438fn into_statement(mut pairs: Pairs) -> Statement {
439 let stmt = pairs.next().unwrap();
440 match stmt.as_rule() {
441 Rule::module_stmt => Statement::Module(into_module(stmt.into_inner())),
442 Rule::import_stmt => Statement::Import(into_import(stmt.into_inner())),
443 Rule::interface => Statement::Interface(into_interface(stmt.into_inner())),
444 Rule::struct_stmt => Statement::Struct(into_struct(stmt.into_inner())),
445 Rule::union_stmt => Statement::Union(into_union(stmt.into_inner())),
446 Rule::enum_stmt => Statement::Enum(into_enum(stmt.into_inner())),
447 Rule::const_stmt => Statement::Const(into_const(stmt.into_inner())),
448 _ => unreachable!(),
449 }
450}
451
452#[derive(Debug, PartialEq)]
453pub struct MojomFile {
454 pub stmts: Vec<Statement>,
455}
456
457fn into_mojom_file(pairs: Pairs) -> MojomFile {
458 let mut stmts = Vec::new();
459 for stmt in pairs {
460 let stmt = match stmt.as_rule() {
461 Rule::statement => into_statement(stmt.into_inner()),
462 Rule::EOI => break,
463 _ => unreachable!(),
464 };
465 stmts.push(stmt);
466 }
467 MojomFile { stmts: stmts }
468}
469
470#[derive(Debug)]
472pub struct LineCol {
473 pub line: usize,
474 pub col: usize,
475}
476
477type PestError = pest::error::Error<Rule>;
478
479#[derive(Debug)]
481pub struct SyntaxError<'a> {
482 input: &'a str,
483 pest_err: PestError,
484 span: (usize, usize),
485}
486
487impl<'a> SyntaxError<'a> {
488 pub fn range(&self) -> (LineCol, LineCol) {
490 let (start, end) = self.span;
491 let start = line_col(&self.input, start).unwrap();
492 let end = line_col(&self.input, end).unwrap();
493 (start, end)
494 }
495}
496
497impl<'a> std::fmt::Display for SyntaxError<'a> {
498 fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result {
499 write!(f, "{}", self.pest_err)
500 }
501}
502
503fn find_token_end_position(input: &str, start: usize) -> usize {
504 let mut end = start;
505 for ch in input[start..].chars() {
506 if ch == ' ' || ch == '\r' || ch == '\n' {
507 break;
508 }
509 end += 1;
510 }
511
512 if end > input.len() {
513 end = input.len();
514 }
515 end
516}
517
518impl<'a> SyntaxError<'a> {
519 fn new(input: &str, err: PestError) -> SyntaxError {
520 let span = match &err.location {
521 pest::error::InputLocation::Pos(start) => {
522 let end = find_token_end_position(&input, *start);
523 (*start, end)
524 }
525 pest::error::InputLocation::Span((start, end)) => (*start, *end),
526 };
527 SyntaxError {
528 input: input,
529 pest_err: err,
530 span: span,
531 }
532 }
533}
534
535fn parse_input(input: &str) -> Result<Pairs, PestError> {
536 MojomParser::parse(Rule::mojom_file, input).map_err(|err| {
537 err.renamed_rules(|rule| match rule {
538 Rule::EOI => "'End of File'".to_owned(),
539 Rule::mojom_file => "statement".to_owned(),
540 Rule::t_array => "array".to_owned(),
541 Rule::t_associated => "associated".to_owned(),
542 Rule::t_const => "const".to_owned(),
543 Rule::t_handle => "handle".to_owned(),
544 Rule::t_import => "import".to_owned(),
545 Rule::t_interface => "interface".to_owned(),
546 Rule::t_map => "map".to_owned(),
547 Rule::t_module => "module".to_owned(),
548 Rule::t_struct => "struct".to_owned(),
549 Rule::t_union => "union".to_owned(),
550 Rule::t_amp => "'&'".to_owned(),
551 Rule::t_arrow => "'=>'".to_owned(),
552 Rule::t_comma => "','".to_owned(),
553 Rule::t_equal => "'='".to_owned(),
554 Rule::t_langlebracket => "'<'".to_owned(),
555 Rule::t_lbrace => "'{'".to_owned(),
556 Rule::t_lbracket => "'['".to_owned(),
557 Rule::t_lparen => "'('".to_owned(),
558 Rule::t_nullable => "'?'".to_owned(),
559 Rule::t_ranglebracket => "'>'".to_owned(),
560 Rule::t_rbrace => "'}'".to_owned(),
561 Rule::t_rbracket => "']'".to_owned(),
562 Rule::t_rparen => "')'".to_owned(),
563 Rule::t_semicolon => "';'".to_owned(),
564 _ => format!("{:?}", rule),
565 })
566 })
567}
568
569fn build_syntax_tree(mut pairs: Pairs) -> MojomFile {
570 let inner = pairs.next().unwrap().into_inner();
571 into_mojom_file(inner)
572}
573
574pub fn parse(input: &str) -> Result<MojomFile, SyntaxError> {
576 let pairs = parse_input(input).map_err(|err| SyntaxError::new(input, err))?;
577 let mojom = build_syntax_tree(pairs);
578 Ok(mojom)
579}
580
581pub fn line_col(text: &str, offset: usize) -> Option<LineCol> {
583 Position::new(text, offset)
584 .map(|p| p.line_col())
585 .map(|(line, col)| LineCol {
586 line: line - 1,
587 col: col - 1,
588 })
589}
590
591#[cfg(test)]
592mod tests {
593 use super::*;
594
595 fn partial_text<'t>(text: &'t str, range: &Range) -> &'t str {
596 &text[range.start..range.end]
597 }
598
599 #[test]
600 fn test_comment() {
601 let input = "/* block comment */";
602 let parsed = MojomParser::parse(Rule::mojom_file, &input);
603 assert!(parsed.is_ok());
604
605 let input = "// line comment";
606 let parsed = MojomParser::parse(Rule::mojom_file, &input);
607 assert!(parsed.is_ok());
608 }
609
610 fn parse_part(r: Rule, i: &str) -> &str {
611 MojomParser::parse(r, i).unwrap().as_str()
612 }
613
614 #[test]
615 fn test_integer() {
616 assert_eq!("0", parse_part(Rule::integer, "0"));
617 assert_eq!("123", parse_part(Rule::integer, "123"));
618 assert_eq!("-42", parse_part(Rule::integer, "-42"));
619 assert_eq!("0xdeadbeef", parse_part(Rule::integer, "0xdeadbeef"));
620 assert_eq!("+0X1AB4", parse_part(Rule::integer, "+0X1AB4"));
621 }
622
623 #[test]
624 fn test_float() {
625 assert_eq!("0.0", parse_part(Rule::float, "0.0"));
626 assert_eq!("1.0", parse_part(Rule::float, "1.0"));
627 assert_eq!("3.141", parse_part(Rule::float, "3.141"));
628 assert_eq!("+0.123", parse_part(Rule::float, "+0.123"));
629 assert_eq!("-5.67", parse_part(Rule::float, "-5.67"));
630 assert_eq!("4e5", parse_part(Rule::float, "4e5"));
631 assert_eq!("-7e+15", parse_part(Rule::float, "-7e+15"));
632 assert_eq!("+9e-2", parse_part(Rule::float, "+9e-2"));
633 }
634
635 #[test]
636 fn test_number() {
637 assert_eq!("0", parse_part(Rule::number, "0"));
638 assert_eq!("123", parse_part(Rule::number, "123"));
639 assert_eq!("-42", parse_part(Rule::number, "-42"));
640 assert_eq!("0xdeadbeef", parse_part(Rule::number, "0xdeadbeef"));
641 assert_eq!("+0X1AB4", parse_part(Rule::number, "+0X1AB4"));
642
643 assert_eq!("0.0", parse_part(Rule::number, "0.0"));
644 assert_eq!("1.0", parse_part(Rule::number, "1.0"));
645 assert_eq!("3.141", parse_part(Rule::number, "3.141"));
646 assert_eq!("+0.123", parse_part(Rule::number, "+0.123"));
647 assert_eq!("-5.67", parse_part(Rule::number, "-5.67"));
648 assert_eq!("4e5", parse_part(Rule::number, "4e5"));
649 assert_eq!("-7e+15", parse_part(Rule::number, "-7e+15"));
650 assert_eq!("+9e-2", parse_part(Rule::number, "+9e-2"));
651 }
652
653 #[test]
654 fn test_string_literal() {
655 assert_eq!(r#""hello""#, parse_part(Rule::string_literal, r#""hello""#));
656 assert_eq!(
657 r#""hell\"o""#,
658 parse_part(Rule::string_literal, r#""hell\"o""#)
659 );
660 }
661
662 #[test]
663 fn test_literal() {
664 assert_eq!("true", parse_part(Rule::literal, "true"));
665 assert_eq!("false", parse_part(Rule::literal, "false"));
666 assert_eq!("default", parse_part(Rule::literal, "default"));
667 assert_eq!("0x12ab", parse_part(Rule::literal, "0x12ab"));
668 assert_eq!(
669 r#""string literal \"with\" quote""#,
670 parse_part(Rule::literal, r#""string literal \"with\" quote""#)
671 );
672 }
673
674 #[test]
675 fn test_attribute() {
676 assert_eq!("[]", parse_part(Rule::attribute_section, "[]"));
677 assert_eq!(
678 "[Attr1, Attr2=NameVal, Attr3=123]",
679 parse_part(Rule::attribute_section, "[Attr1, Attr2=NameVal, Attr3=123]")
680 );
681 assert_eq!(
682 "[Attr=foo.bar.baz]",
683 parse_part(Rule::attribute_section, "[Attr=foo.bar.baz]")
684 );
685 }
686
687 #[test]
688 fn test_types() {
689 macro_rules! parse_type {
690 ($tok:expr) => {{
691 assert_eq!($tok, parse_part(Rule::type_spec, $tok));
692 }};
693 }
694
695 parse_type!("bool");
696 parse_type!("int8");
697 parse_type!("uint8");
698 parse_type!("int16");
699 parse_type!("uint16");
700 parse_type!("int32");
701 parse_type!("uint32");
702 parse_type!("int64");
703 parse_type!("uint64");
704 parse_type!("float");
705 parse_type!("double");
706 parse_type!("handle");
707 parse_type!("handle<message_pipe>");
708 parse_type!("pending_receiver<MyInterface>");
709 parse_type!("pending_remote<mymodule.MyInterface>");
710 parse_type!("pending_associated_remote<FooInterface>?");
711 parse_type!("pending_associated_receiver<FooInterface>");
712 parse_type!("handle<message_pipe>");
713 parse_type!("string");
714 parse_type!("array<uint8>");
715 parse_type!("array<uint8, 16>");
716 parse_type!("map<int32, MyInterface>");
717 parse_type!("MyInterface");
718 parse_type!("MyInerface&");
719 parse_type!("associated MyInterface");
720 parse_type!("associated MyInterface&");
721 parse_type!("bool?");
722 }
723
724 #[test]
725 fn test_module_stmt() {
726 let input = "module my.mod;";
727 let parsed = MojomParser::parse(Rule::module_stmt, &input)
728 .unwrap()
729 .next()
730 .unwrap();
731 let stmt = into_module(parsed.into_inner());
732 assert_eq!("my.mod", partial_text(&input, &stmt.name));
733 }
734
735 #[test]
736 fn test_import_stmt() {
737 let input = r#"import "my.mod";"#;
738 let parsed = MojomParser::parse(Rule::import_stmt, &input)
739 .unwrap()
740 .next()
741 .unwrap();
742 let stmt = into_import(parsed.into_inner());
743 assert_eq!(r#""my.mod""#, partial_text(&input, &stmt.path));
744
745 let input = r#"[Attr] import "my.mod";"#;
746 let parsed = MojomParser::parse(Rule::import_stmt, &input)
747 .unwrap()
748 .next()
749 .unwrap();
750 let stmt = into_import(parsed.into_inner());
751 assert_eq!(r#""my.mod""#, partial_text(&input, &stmt.path));
752 }
753
754 #[test]
755 fn test_const_stmt() {
756 let input = "const uint32 kTheAnswer = 42;";
757 let parsed = MojomParser::parse(Rule::const_stmt, &input)
758 .unwrap()
759 .next()
760 .unwrap();
761 let stmt = into_const(parsed.into_inner());
762 assert_eq!("uint32", partial_text(&input, &stmt.typ));
763 assert_eq!("kTheAnswer", partial_text(&input, &stmt.name));
764 assert_eq!("42", partial_text(&input, &stmt.value));
765 }
766
767 #[test]
768 fn test_enum_stmt() {
769 let input = "enum MyEnum { kOne, kTwo=2, kThree=IdentValue, };";
770 let parsed = MojomParser::parse(Rule::enum_stmt, &input)
771 .unwrap()
772 .next()
773 .unwrap();
774 let stmt = into_enum(parsed.into_inner());
775 assert_eq!("MyEnum", partial_text(&input, &stmt.name));
776 let values = &stmt.values;
777 assert_eq!(3, values.len());
778 assert_eq!("kOne", partial_text(&input, &values[0].name));
779 assert_eq!("kTwo", partial_text(&input, &values[1].name));
780 assert_eq!("2", partial_text(&input, values[1].value.as_ref().unwrap()));
781 assert_eq!("kThree", partial_text(&input, &values[2].name));
782 assert_eq!(
783 "IdentValue",
784 partial_text(&input, values[2].value.as_ref().unwrap())
785 );
786
787 let input = "enum MyEnum {};";
788 let parsed = MojomParser::parse(Rule::enum_stmt, &input)
789 .unwrap()
790 .next()
791 .unwrap();
792 let stmt = into_enum(parsed.into_inner());
793 assert_eq!("MyEnum", partial_text(&input, &stmt.name));
794 assert_eq!(0, stmt.values.len());
795
796 let input = "[Native] enum MyEnum;";
797 let parsed = MojomParser::parse(Rule::enum_stmt, &input)
798 .unwrap()
799 .next()
800 .unwrap();
801 let stmt = into_enum(parsed.into_inner());
802 assert_eq!("MyEnum", partial_text(&input, &stmt.name));
803 assert_eq!(0, stmt.values.len());
804 }
805
806 #[test]
807 fn test_method_stmt() {
808 let input = "MyMethod(string str_arg, int8 int8_arg) => (uint32 result);";
809 let parsed = MojomParser::parse(Rule::method_stmt, &input)
810 .unwrap()
811 .next()
812 .unwrap();
813 let stmt = into_method(parsed.into_inner());
814 assert_eq!("MyMethod", partial_text(&input, &stmt.name));
815 let params = &stmt.params;
816 assert_eq!(2, params.len());
817 assert_eq!("string", partial_text(&input, ¶ms[0].typ));
818 assert_eq!("str_arg", partial_text(&input, ¶ms[0].name));
819 assert_eq!("int8", partial_text(&input, ¶ms[1].typ));
820 assert_eq!("int8_arg", partial_text(&input, ¶ms[1].name));
821 let response = stmt.response.as_ref().unwrap();
822 assert_eq!(1, response.params.len());
823 assert_eq!("uint32", partial_text(&input, &response.params[0].typ));
824 assert_eq!("result", partial_text(&input, &response.params[0].name));
825
826 let input = "MyMethod2();";
827 let parsed = MojomParser::parse(Rule::method_stmt, &input)
828 .unwrap()
829 .next()
830 .unwrap();
831 let stmt = into_method(parsed.into_inner());
832 assert_eq!("MyMethod2", partial_text(&input, &stmt.name));
833 assert_eq!(0, stmt.params.len());
834 assert!(stmt.response.is_none());
835
836 let input = "MyMethod3(int8 default_int8_arg) => ();";
837 let parsed = MojomParser::parse(Rule::method_stmt, &input)
838 .unwrap()
839 .next()
840 .unwrap();
841 let stmt = into_method(parsed.into_inner());
842 assert_eq!("MyMethod3", partial_text(&input, &stmt.name));
843 assert_eq!(1, stmt.params.len());
844 let params = &stmt.params;
845 assert_eq!("int8", partial_text(&input, ¶ms[0].typ));
846 assert_eq!("default_int8_arg", partial_text(&input, ¶ms[0].name));
847 let response = stmt.response.as_ref().unwrap();
848 assert_eq!(0, response.params.len());
849 }
850
851 #[test]
852 fn test_struct_stmt() {
853 let input = "struct MyStruct {
854 const int64 kInvalidId = -1;
855 int64 my_id;
856 MyInterface? my_interface;
857 float my_float_value = 0.1;
858 };";
859 let parsed = MojomParser::parse(Rule::struct_stmt, &input)
860 .unwrap()
861 .next()
862 .unwrap();
863 let stmt = into_struct(parsed.into_inner());
864 assert_eq!("MyStruct", partial_text(&input, &stmt.name));
865 let members = &stmt.members;
866 assert_eq!(4, members.len());
867
868 let item = match &members[0] {
869 StructBody::Const(item) => item,
870 _ => unreachable!(),
871 };
872 assert_eq!("kInvalidId", partial_text(&input, &item.name));
873
874 let item = match &members[1] {
875 StructBody::Field(item) => item,
876 _ => unreachable!(),
877 };
878 assert_eq!("my_id", partial_text(&input, &item.name));
879
880 let item = match &members[2] {
881 StructBody::Field(item) => item,
882 _ => unreachable!(),
883 };
884 assert_eq!("my_interface", partial_text(&input, &item.name));
885
886 let item = match &members[3] {
887 StructBody::Field(item) => item,
888 _ => unreachable!(),
889 };
890 assert_eq!("my_float_value", partial_text(&input, &item.name));
891
892 let input = "[Native] struct MyStruct;";
893 let parsed = MojomParser::parse(Rule::struct_stmt, &input)
894 .unwrap()
895 .next()
896 .unwrap();
897 let stmt = into_struct(parsed.into_inner());
898 assert_eq!("MyStruct", partial_text(&input, &stmt.name));
899 assert_eq!(0, stmt.members.len());
900 }
901
902 #[test]
903 fn test_interface() {
904 let input = "interface MyInterface {
905 MyMethod();
906 enum MyEnum { kMyEnumVal1, kMyEnumVal2 };
907 };";
908 let parsed = MojomParser::parse(Rule::interface, &input)
909 .unwrap()
910 .next()
911 .unwrap();
912 let intr = into_interface(parsed.into_inner());
913 assert_eq!("MyInterface", partial_text(&input, &intr.name));
914 let members = &intr.members;
915 assert_eq!(2, members.len());
916
917 let member = match &members[0] {
918 InterfaceMember::Method(member) => member,
919 _ => unreachable!(),
920 };
921 assert_eq!("MyMethod", partial_text(&input, &member.name));
922
923 let member = match &members[1] {
924 InterfaceMember::Enum(member) => member,
925 _ => unreachable!(),
926 };
927 assert_eq!("MyEnum", partial_text(&input, &member.name));
928 }
929
930 #[test]
931 fn test_union_stmt() {
932 let input = "union MyUnion {
933 string str_field;
934 StringPair pair_field;
935 int64 int64_field;
936 };";
937 let parsed = MojomParser::parse(Rule::union_stmt, &input)
938 .unwrap()
939 .next()
940 .unwrap();
941 let stmt = into_union(parsed.into_inner());
942 assert_eq!("MyUnion", partial_text(&input, &stmt.name));
943 let fields = &stmt.fields;
944 assert_eq!(3, fields.len());
945 assert_eq!("str_field", partial_text(&input, &fields[0].name));
946 assert_eq!("pair_field", partial_text(&input, &fields[1].name));
947 assert_eq!("int64_field", partial_text(&input, &fields[2].name));
948 }
949
950 #[test]
951 fn test_parse() {
952 let input = r#"
953 module test.mod;
954 import "a.b.c";
955 import "a.c.d";
956
957 enum MyEnum;
958 enum MyEnum2 { kFoo, kBar, kBaz };
959
960 const string kMyConst = "const_value";
961 const int32 kMyConst2 = -1;
962
963 struct MyStruct {};
964 struct MyStruct2 {
965 uint8 my_uint8_value;
966 float my_float_value = 0.1;
967 };
968
969 union MyUnion {
970 string str_field;
971 uint8 uint8_field;
972 };
973 union MyUnion2 {
974 MyInterface myinterface_field;
975 uint32 uint32_field;
976 };
977
978 // This is MyInterface
979 interface MyInterface {
980 MyMethod() => (/* empty */);
981 };
982
983 interface InterfaceA {};
984
985 // This is comment.
986 interface InterfaceB {};
987
988 [Attr]
989 interface InterfaceC {};
990
991 interface InterfaceD {
992 const string kMessage = "message";
993 enum SomeEnum { Foo, Bar, Baz, };
994 MethodA(string message, string? optional_message) => ();
995 MethodB() => (int32 result, MyStruct? optional_result);
996 [Attr2] MethodC(associated InterfaceA assoc) => (map<string, int8> result);
997 };
998 "#;
999 let res = parse(input).unwrap();
1000 assert_eq!(16, res.stmts.len());
1001 }
1002}