1use alloc::{
2 boxed::Box,
3 string::{String, ToString},
4 vec::Vec,
5};
6use hashbrown::HashSet;
7use thiserror::Error;
8
9use crate::{
10 grammar::ProgramParser,
11 lexer::{Lexer, LexicalError},
12};
13
14pub mod visitor;
15
16#[derive(Error, Debug, PartialEq)]
17pub enum ParseError {
18 #[error(transparent)]
19 Lexical(#[from] LexicalError),
20 #[error("duplicate type `{0}`")]
21 DuplicateType(String),
22 #[error("duplicate ctor `{0}`")]
23 DuplicateCtor(String),
24 #[error("duplicate service `{0}`")]
25 DuplicateService(String),
26 #[error("duplicate service method `{method}` in service `{service}`")]
27 DuplicateServiceMethod { method: String, service: String },
28 #[error("duplicate variant `{0}`")]
29 DuplicateEnumVariant(String),
30 #[error("duplicate field `{0}`")]
31 DuplicateStructField(String),
32 #[error("struct has mixed named and unnamed fields")]
33 StructMixedFields,
34 #[error("parse error: `{0}`")]
35 Other(String),
36}
37
38pub fn parse_idl(idl: &str) -> Result<Program, ParseError> {
39 let lexer = Lexer::new(idl);
40 let parser = ProgramParser::new();
41
42 let program = parser.parse(lexer).map_err(|e| match e {
43 lalrpop_util::ParseError::User { error } => error,
44 _ => ParseError::Other(e.to_string()),
45 })?;
46
47 Ok(program)
48}
49
50type ParseResult<T> = Result<T, ParseError>;
51
52#[derive(Debug, PartialEq, Clone)]
54pub struct Program {
55 ctor: Option<Ctor>,
56 services: Vec<Service>,
57 types: Vec<Type>,
58}
59
60impl Program {
61 pub(crate) fn new(
62 ctor: Option<Ctor>,
63 services: Vec<Service>,
64 types: Vec<Type>,
65 ) -> ParseResult<Self> {
66 let mut seen_types = HashSet::new();
67 for t in &types {
68 if !seen_types.insert(t.name().to_lowercase()) {
69 return Err(ParseError::DuplicateType(t.name().to_string()));
70 }
71 }
72
73 let mut seen_services = HashSet::new();
74 for s in &services {
75 if !seen_services.insert(s.name().to_lowercase()) {
76 return Err(ParseError::DuplicateService(s.name().to_string()));
77 }
78 }
79
80 Ok(Self {
81 ctor,
82 services,
83 types,
84 })
85 }
86
87 pub fn ctor(&self) -> Option<&Ctor> {
88 self.ctor.as_ref()
89 }
90
91 pub fn services(&self) -> &[Service] {
92 &self.services
93 }
94
95 pub fn types(&self) -> &[Type] {
96 &self.types
97 }
98}
99
100#[derive(Debug, PartialEq, Clone)]
102pub struct Ctor {
103 funcs: Vec<CtorFunc>,
104}
105
106impl Ctor {
107 pub(crate) fn new(funcs: Vec<CtorFunc>) -> ParseResult<Self> {
108 let mut seen = HashSet::new();
109 for f in &funcs {
110 if !seen.insert(f.name().to_lowercase()) {
111 return Err(ParseError::DuplicateCtor(f.name().to_string()));
112 }
113 }
114 Ok(Self { funcs })
115 }
116
117 pub fn funcs(&self) -> &[CtorFunc] {
118 &self.funcs
119 }
120}
121
122#[derive(Debug, PartialEq, Clone)]
124pub struct CtorFunc {
125 name: String,
126 params: Vec<FuncParam>,
127 docs: Vec<String>,
128}
129
130impl CtorFunc {
131 pub(crate) fn new(name: String, params: Vec<FuncParam>, docs: Vec<String>) -> Self {
132 Self { name, params, docs }
133 }
134
135 pub fn name(&self) -> &str {
136 &self.name
137 }
138
139 pub fn params(&self) -> &[FuncParam] {
140 &self.params
141 }
142
143 pub fn docs(&self) -> &Vec<String> {
144 &self.docs
145 }
146}
147
148#[derive(Debug, PartialEq, Clone)]
150pub struct Service {
151 name: String,
152 funcs: Vec<ServiceFunc>,
153 events: Vec<ServiceEvent>,
154}
155
156impl Service {
157 pub(crate) fn new(
158 name: String,
159 funcs: Vec<ServiceFunc>,
160 events: Vec<ServiceEvent>,
161 ) -> ParseResult<Self> {
162 let mut seen = HashSet::new();
163 for f in &funcs {
164 if !seen.insert(f.name().to_lowercase()) {
165 return Err(ParseError::DuplicateServiceMethod {
166 method: f.name().to_string(),
167 service: name.to_string(),
168 });
169 }
170 }
171
172 Ok(Self {
173 name,
174 funcs,
175 events,
176 })
177 }
178
179 pub fn name(&self) -> &str {
180 self.name.as_str()
181 }
182
183 pub fn funcs(&self) -> &[ServiceFunc] {
184 &self.funcs
185 }
186
187 pub fn events(&self) -> &[ServiceEvent] {
188 &self.events
189 }
190}
191
192#[derive(Debug, PartialEq, Clone)]
194pub struct ServiceFunc {
195 name: String,
196 params: Vec<FuncParam>,
197 output: TypeDecl,
198 is_query: bool,
199 docs: Vec<String>,
200}
201
202impl ServiceFunc {
203 pub(crate) fn new(
204 name: String,
205 params: Vec<FuncParam>,
206 output: TypeDecl,
207 is_query: bool,
208 docs: Vec<String>,
209 ) -> Self {
210 Self {
211 name,
212 params,
213 output,
214 is_query,
215 docs,
216 }
217 }
218
219 pub fn name(&self) -> &str {
220 &self.name
221 }
222
223 pub fn params(&self) -> &[FuncParam] {
224 &self.params
225 }
226
227 pub fn output(&self) -> &TypeDecl {
228 &self.output
229 }
230
231 pub fn is_query(&self) -> bool {
232 self.is_query
233 }
234
235 pub fn docs(&self) -> &Vec<String> {
236 &self.docs
237 }
238}
239
240pub type ServiceEvent = EnumVariant;
242
243#[derive(Debug, PartialEq, Clone)]
244pub struct FuncParam {
245 name: String,
246 type_decl: TypeDecl,
247}
248
249impl FuncParam {
250 pub(crate) fn new(name: String, type_decl: TypeDecl) -> Self {
251 Self { name, type_decl }
252 }
253
254 pub fn name(&self) -> &str {
255 &self.name
256 }
257
258 pub fn type_decl(&self) -> &TypeDecl {
259 &self.type_decl
260 }
261}
262
263#[derive(Debug, PartialEq, Clone)]
264pub struct Type {
265 name: String,
266 def: TypeDef,
267 docs: Vec<String>,
268}
269
270impl Type {
271 pub(crate) fn new(name: String, def: TypeDef, docs: Vec<String>) -> Self {
272 Self { name, def, docs }
273 }
274
275 pub fn name(&self) -> &str {
276 &self.name
277 }
278
279 pub fn def(&self) -> &TypeDef {
280 &self.def
281 }
282
283 pub fn docs(&self) -> &Vec<String> {
284 &self.docs
285 }
286}
287
288#[derive(Debug, PartialEq, Clone)]
289pub enum TypeDecl {
290 Vector(Box<TypeDecl>),
291 Array {
292 item: Box<TypeDecl>,
293 len: u32,
294 },
295 Map {
296 key: Box<TypeDecl>,
297 value: Box<TypeDecl>,
298 },
299 Optional(Box<TypeDecl>),
300 Result {
301 ok: Box<TypeDecl>,
302 err: Box<TypeDecl>,
303 },
304 Id(TypeId),
305 Def(TypeDef),
306}
307
308#[derive(Debug, PartialEq, Clone)]
309pub enum TypeId {
310 Primitive(PrimitiveType),
311 UserDefined(String),
312}
313
314#[derive(Debug, PartialEq, Clone, Copy)]
315#[repr(u8)]
316pub enum PrimitiveType {
317 Null,
318 Bool,
319 Char,
320 Str,
321 U8,
322 U16,
323 U32,
324 U64,
325 U128,
326 I8,
327 I16,
328 I32,
329 I64,
330 I128,
331 ActorId,
332 CodeId,
333 MessageId,
334 H256,
335 U256,
336 H160,
337 NonZeroU8,
338 NonZeroU16,
339 NonZeroU32,
340 NonZeroU64,
341 NonZeroU128,
342 NonZeroU256,
343}
344
345impl PrimitiveType {
346 pub(crate) fn str_to_enum(str: &str) -> Option<Self> {
347 match str {
348 "bool" => Some(PrimitiveType::Bool),
349 "char" => Some(PrimitiveType::Char),
350 "str" => Some(PrimitiveType::Str),
351 "u8" => Some(PrimitiveType::U8),
352 "u16" => Some(PrimitiveType::U16),
353 "u32" => Some(PrimitiveType::U32),
354 "u64" => Some(PrimitiveType::U64),
355 "u128" => Some(PrimitiveType::U128),
356 "i8" => Some(PrimitiveType::I8),
357 "i16" => Some(PrimitiveType::I16),
358 "i32" => Some(PrimitiveType::I32),
359 "i64" => Some(PrimitiveType::I64),
360 "i128" => Some(PrimitiveType::I128),
361 "h160" => Some(PrimitiveType::H160),
362 "h256" => Some(PrimitiveType::H256),
363 "u256" => Some(PrimitiveType::U256),
364 "nat8" => Some(PrimitiveType::NonZeroU8),
365 "nat16" => Some(PrimitiveType::NonZeroU16),
366 "nat32" => Some(PrimitiveType::NonZeroU32),
367 "nat64" => Some(PrimitiveType::NonZeroU64),
368 "nat128" => Some(PrimitiveType::NonZeroU128),
369 "nat256" => Some(PrimitiveType::NonZeroU256),
370 "actor_id" => Some(PrimitiveType::ActorId),
371 "code_id" => Some(PrimitiveType::CodeId),
372 "message_id" => Some(PrimitiveType::MessageId),
373 _ => None,
374 }
375 }
376}
377
378#[derive(Debug, PartialEq, Clone)]
379pub enum TypeDef {
380 Struct(StructDef),
381 Enum(EnumDef),
382}
383
384#[derive(Debug, PartialEq, Clone)]
385pub struct StructDef {
386 fields: Vec<StructField>,
387}
388
389impl StructDef {
390 pub(crate) fn new(fields: Vec<StructField>) -> ParseResult<Self> {
391 let all_unnamed = fields.iter().all(|f| f.name().is_none());
393 let all_named = fields.iter().all(|f| f.name().is_some());
394
395 if !all_unnamed && !all_named {
396 return Err(ParseError::StructMixedFields);
397 }
398
399 let mut seen = HashSet::new();
400
401 if all_named {
402 for f in &fields {
403 let name = f.name().unwrap();
404 if !seen.insert(name.to_lowercase()) {
405 return Err(ParseError::DuplicateStructField(name.to_string()));
406 }
407 }
408 }
409
410 Ok(Self { fields })
411 }
412
413 pub fn fields(&self) -> &[StructField] {
414 &self.fields
415 }
416}
417
418#[derive(Debug, PartialEq, Clone)]
419pub struct StructField {
420 name: Option<String>,
421 type_decl: TypeDecl,
422 docs: Vec<String>,
423}
424
425impl StructField {
426 pub(crate) fn new(name: Option<String>, type_decl: TypeDecl, docs: Vec<String>) -> Self {
427 Self {
428 name,
429 type_decl,
430 docs,
431 }
432 }
433
434 pub fn name(&self) -> Option<&str> {
435 self.name.as_deref()
436 }
437
438 pub fn type_decl(&self) -> &TypeDecl {
439 &self.type_decl
440 }
441
442 pub fn docs(&self) -> &Vec<String> {
443 &self.docs
444 }
445}
446
447#[derive(Debug, PartialEq, Clone)]
448pub struct EnumDef {
449 variants: Vec<EnumVariant>,
450}
451
452impl EnumDef {
453 pub(crate) fn new(variants: Vec<EnumVariant>) -> ParseResult<Self> {
454 let mut seen = HashSet::new();
455 for v in &variants {
456 if !seen.insert(v.name().to_lowercase()) {
457 return Err(ParseError::DuplicateEnumVariant(v.name().to_string()));
458 }
459 }
460 Ok(Self { variants })
461 }
462
463 pub fn variants(&self) -> &[EnumVariant] {
464 &self.variants
465 }
466}
467
468#[derive(Debug, PartialEq, Clone)]
469pub struct EnumVariant {
470 name: String,
471 type_decl: Option<TypeDecl>,
472 docs: Vec<String>,
473}
474
475impl EnumVariant {
476 pub(crate) fn new(name: String, type_decl: Option<TypeDecl>, docs: Vec<String>) -> Self {
477 Self {
478 name,
479 type_decl,
480 docs,
481 }
482 }
483
484 pub fn name(&self) -> &str {
485 &self.name
486 }
487
488 pub fn type_decl(&self) -> Option<&TypeDecl> {
489 self.type_decl.as_ref()
490 }
491
492 pub fn docs(&self) -> &Vec<String> {
493 &self.docs
494 }
495}
496
497#[cfg(test)]
498mod tests {
499 extern crate alloc;
500
501 use super::*;
502 use alloc::borrow::ToOwned;
503
504 #[test]
505 fn parser_works() {
506 let program_idl = r"
507 type ThisThatSvcAppTupleStruct = struct {
508 bool,
509 };
510
511 type ThisThatSvcAppDoThatParam = struct {
512 p1: u32,
513 p2: str,
514 p3: ThisThatSvcAppManyVariants,
515 };
516
517 type ThisThatSvcAppManyVariants = enum {
518 One,
519 Two: u32,
520 Three: opt u256,
521 Four: struct { a: u32, b: opt u16 },
522 Five: struct { str, h256 },
523 Six: struct { u32 },
524 Seven: [map (u32, str), 10],
525 Eight: actor_id,
526 };
527
528 constructor {
529 New : (p1: u32);
530 };
531
532 service {
533 DoThis : (p1: u32, p2: str, p3: struct { opt str, u8 }, p4: ThisThatSvcAppTupleStruct) -> struct { str, u32 };
534 DoThat : (param: ThisThatSvcAppDoThatParam) -> result (struct { str, u32 }, struct { str });
535 query This : (v1: vec u16) -> u32;
536 query That : (v1: null) -> result (str, str);
537
538 events {
539 ThisDone;
540 ThatDone: u32;
541 SomethingHappened: struct { str, u32 };
542 SomethingDone: ThisThatSvcAppManyVariants;
543 }
544 };
545 ";
546
547 let program = parse_idl(program_idl).unwrap();
548
549 assert_eq!(program.types().len(), 3);
550 assert_eq!(program.ctor().unwrap().funcs().len(), 1);
551 assert_eq!(program.services().len(), 1);
552 assert_eq!(program.services()[0].funcs().len(), 4);
553 assert_eq!(program.services()[0].events().len(), 4);
554
555 }
557
558 #[test]
559 fn parser_accepts_types_service() {
560 let program_idl = r"
561 type T = enum { One };
562 service {}
563 ";
564
565 let program = parse_idl(program_idl).unwrap();
566
567 assert_eq!(program.types().len(), 1);
568 assert_eq!(program.services().len(), 1);
569 assert_eq!(program.services()[0].funcs().len(), 0);
570 }
571
572 #[test]
573 fn parser_accepts_ctor_service() {
574 let program_idl = r"
575 constructor {};
576 service {}
577 ";
578
579 let program = parse_idl(program_idl).unwrap();
580
581 assert_eq!(program.ctor().unwrap().funcs().len(), 0);
582 assert_eq!(program.services().len(), 1);
583 assert_eq!(program.services()[0].funcs().len(), 0);
584 }
585
586 #[test]
587 fn parser_accepts_multiple_services() {
588 let program_idl = r"
589 service {};
590 service SomeService {};
591 ";
592
593 let program = parse_idl(program_idl).unwrap();
594
595 assert_eq!(program.services().len(), 2);
596 assert_eq!(program.services()[0].name(), "");
597 assert_eq!(program.services()[1].name(), "SomeService");
598 }
599
600 #[test]
601 fn parser_accepts_types_ctor_service() {
602 let program_idl = r"
603 type T = enum { One };
604 constructor {};
605 service {}
606 ";
607
608 let program = parse_idl(program_idl).unwrap();
609
610 assert_eq!(program.types().len(), 1);
611 assert_eq!(program.ctor().unwrap().funcs().len(), 0);
612 assert_eq!(program.services().len(), 1);
613 assert_eq!(program.services()[0].funcs().len(), 0);
614 }
615
616 #[test]
617 fn parser_requires_semicolon_between_types_and_service() {
618 let program_idl = r"
619 type T = enum { One }
620 service {}
621 ";
622
623 let program = parse_idl(program_idl);
624
625 assert!(program.is_err());
626 }
627
628 #[test]
629 fn parser_recognizes_builtin_types_as_primitives() {
630 let program_idl = r"
631 service {
632 DoThis : (p1: actor_id, p2: code_id, p3: message_id, p4: h256, p5: u256, p6: h160) -> null;
633 }
634 ";
635
636 let program = parse_idl(program_idl).unwrap();
637
638 assert_eq!(program.services().len(), 1);
639 program.services()[0]
640 .funcs()
641 .first()
642 .unwrap()
643 .params()
644 .iter()
645 .for_each(|p| match p.type_decl() {
646 TypeDecl::Id(TypeId::Primitive(PrimitiveType::ActorId)) => {
647 assert_eq!(p.name(), "p1");
648 }
649 TypeDecl::Id(TypeId::Primitive(PrimitiveType::CodeId)) => {
650 assert_eq!(p.name(), "p2");
651 }
652 TypeDecl::Id(TypeId::Primitive(PrimitiveType::MessageId)) => {
653 assert_eq!(p.name(), "p3");
654 }
655 TypeDecl::Id(TypeId::Primitive(PrimitiveType::H256)) => {
656 assert_eq!(p.name(), "p4");
657 }
658 TypeDecl::Id(TypeId::Primitive(PrimitiveType::U256)) => {
659 assert_eq!(p.name(), "p5");
660 }
661 TypeDecl::Id(TypeId::Primitive(PrimitiveType::H160)) => {
662 assert_eq!(p.name(), "p6");
663 }
664 _ => panic!("unexpected type"),
665 });
666 }
667
668 #[test]
669 fn parser_rejects_duplicate_names() {
670 let program_idl = r"
671 type A = enum { One };
672 type A = enum { Two };
673 service {};
674 ";
675
676 let err = parse_idl(program_idl).unwrap_err();
677
678 assert_eq!(err, ParseError::DuplicateType("A".to_owned()));
679 }
680
681 #[test]
682 fn parser_rejects_duplicate_unnamed_services() {
683 let program_idl = r"
684 service {};
685 service {};
686 ";
687
688 let err = parse_idl(program_idl).unwrap_err();
689
690 assert_eq!(err, ParseError::DuplicateService("".to_owned()));
691 }
692
693 #[test]
694 fn parser_rejects_duplicate_named_services() {
695 let program_idl = r"
696 service A {};
697 service B {};
698 service A {};
699 service {};
700 ";
701
702 let err = parse_idl(program_idl).unwrap_err();
703
704 assert_eq!(err, ParseError::DuplicateService("A".to_owned()));
705 }
706
707 #[test]
708 fn parser_rejects_duplicate_service_methods() {
709 let program_idl = r"
710 service {
711 DoTHIS : () -> null;
712 DoThis : () -> null;
713 };
714 ";
715
716 let err = parse_idl(program_idl).unwrap_err();
717
718 assert_eq!(
719 err,
720 ParseError::DuplicateServiceMethod {
721 method: "DoThis".to_owned(),
722 service: "".to_owned()
723 }
724 );
725 }
726
727 #[test]
728 fn parser_rejects_duplicate_ctor_funcs() {
729 let program_idl = r"
730 constructor {
731 New : ();
732 new : ();
733 };
734 ";
735
736 let err = parse_idl(program_idl).unwrap_err();
737
738 assert_eq!(err, ParseError::DuplicateCtor("new".to_owned()));
739 }
740
741 #[test]
742 fn parser_rejects_duplicate_enum_variants() {
743 let program_idl = r"
744 type T = enum { One, One };
745 ";
746
747 let err = parse_idl(program_idl).unwrap_err();
748
749 assert_eq!(err, ParseError::DuplicateEnumVariant("One".to_owned()));
750 }
751
752 #[test]
753 fn parser_rejects_duplicate_struct_fields() {
754 let program_idl = r"
755 type T = struct {
756 a: u32,
757 a: u32,
758 };
759 ";
760
761 let err = parse_idl(program_idl).unwrap_err();
762
763 assert_eq!(err, ParseError::DuplicateStructField("a".to_owned()));
764 }
765
766 #[test]
767 fn parser_rejects_mixed_named_unnamed_struct_fields() {
768 let program_idl = r"
769 type T = struct {
770 a: u32,
771 u32,
772 };
773 ";
774
775 let err = parse_idl(program_idl).unwrap_err();
776
777 assert_eq!(err, ParseError::StructMixedFields);
778 }
779
780 #[test]
781 fn parser_accepts_struct_field_reserved_keywords() {
782 const IDL: &str = r#"
783 type MyStruct = struct {
784 query: u8,
785 result: u8,
786 };
787 "#;
788
789 let expected = TypeDef::Struct(
790 StructDef::new(vec![
791 StructField::new(
792 Some("query".to_owned()),
793 TypeDecl::Id(TypeId::Primitive(PrimitiveType::U8)),
794 vec![],
795 ),
796 StructField::new(
797 Some("result".to_owned()),
798 TypeDecl::Id(TypeId::Primitive(PrimitiveType::U8)),
799 vec![],
800 ),
801 ])
802 .unwrap(),
803 );
804
805 let program = parse_idl(IDL).unwrap();
807
808 let my_struct = program
810 .types()
811 .iter()
812 .find(|t| t.name() == "MyStruct")
813 .unwrap();
814 assert_eq!(&expected, my_struct.def());
815 }
816
817 #[test]
818 fn parser_accepts_func_param_reserved_keywords() {
819 const IDL: &str = r#"
820 service {
821 /// DoThis comment
822 DoThis : (constructor: u8, service: u8, events: vec u8) -> null;
823 }
824 "#;
825
826 let expected = Service::new(
827 "".to_owned(),
828 vec![ServiceFunc::new(
829 "DoThis".to_owned(),
830 vec![
831 FuncParam::new(
832 "constructor".to_owned(),
833 TypeDecl::Id(TypeId::Primitive(PrimitiveType::U8)),
834 ),
835 FuncParam::new(
836 "service".to_owned(),
837 TypeDecl::Id(TypeId::Primitive(PrimitiveType::U8)),
838 ),
839 FuncParam::new(
840 "events".to_owned(),
841 TypeDecl::Vector(Box::new(TypeDecl::Id(TypeId::Primitive(
842 PrimitiveType::U8,
843 )))),
844 ),
845 ],
846 TypeDecl::Id(TypeId::Primitive(PrimitiveType::Null)),
847 false,
848 vec!["DoThis comment".to_owned()],
849 )],
850 vec![],
851 )
852 .unwrap();
853
854 let program = parse_idl(IDL).unwrap();
856
857 let my_service = program.services().iter().find(|t| t.name() == "").unwrap();
859 assert_eq!(&expected, my_service);
860 }
861
862 #[test]
863 fn parser_accepts_nonzero_primitives() {
864 const IDL: &str = r#"
865 type MyStruct = struct {
866 /// field `query`
867 query: nat32,
868 data: nat256,
869 /// field `result`
870 /// second line
871 result: nat8
872 };
873 "#;
874
875 let expected = TypeDef::Struct(
876 StructDef::new(vec![
877 StructField::new(
878 Some("query".to_owned()),
879 TypeDecl::Id(TypeId::Primitive(PrimitiveType::NonZeroU32)),
880 vec!["field `query`".into()],
881 ),
882 StructField::new(
883 Some("data".to_owned()),
884 TypeDecl::Id(TypeId::Primitive(PrimitiveType::NonZeroU256)),
885 vec![],
886 ),
887 StructField::new(
888 Some("result".to_owned()),
889 TypeDecl::Id(TypeId::Primitive(PrimitiveType::NonZeroU8)),
890 vec!["field `result`".into(), "second line".into()],
891 ),
892 ])
893 .unwrap(),
894 );
895
896 let program = parse_idl(IDL).unwrap();
898
899 let my_struct = program
901 .types()
902 .iter()
903 .find(|t| t.name() == "MyStruct")
904 .unwrap();
905 assert_eq!(&expected, my_struct.def());
906 }
907}