Skip to main content

mojom_lsp/syntax/
syntax.rs

1// Copyright 2020 Google LLC
2//
3// Licensed under the Apache License, Version 2.0 (the "License");
4// you may not use this file except in compliance with the License.
5// You may obtain a copy of the License at
6//
7//      http://www.apache.org/licenses/LICENSE-2.0
8//
9// Unless required by applicable law or agreed to in writing, software
10// distributed under the License is distributed on an "AS IS" BASIS,
11// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12// See the License for the specific language governing permissions and
13// limitations under the License.
14
15use 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
34// Skips attribute list if exists. This is tentative.
35fn 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    // The next item should be t_equal when it's Some(item).
114    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        // At this point `item` should have only one inner and it should be struct_item.
213        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    // `for` takes the ownership of `pairs`. Use `loop`.
409    loop {
410        let item = pairs.next().unwrap(); // Should not be None.
411        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/// Zero-based line/column in a text.
471#[derive(Debug)]
472pub struct LineCol {
473    pub line: usize,
474    pub col: usize,
475}
476
477type PestError = pest::error::Error<Rule>;
478
479/// Represents a syntax error.
480#[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    /// Returns `start` and `end` positions of the error.
489    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
574/// Parses `input` into a syntax tree.
575pub 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
581/// Converts `offset` to LineCol in `text`.
582pub 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, &params[0].typ));
818        assert_eq!("str_arg", partial_text(&input, &params[0].name));
819        assert_eq!("int8", partial_text(&input, &params[1].typ));
820        assert_eq!("int8_arg", partial_text(&input, &params[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, &params[0].typ));
846        assert_eq!("default_int8_arg", partial_text(&input, &params[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}