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