sizzle_parser/
pipeline.rs1use std::{collections::HashMap, path::PathBuf};
4use thiserror::Error;
5
6use crate::{
7 SszSchema,
8 ast::{self, ModuleManager, ParseError},
9 schema::{self, SchemaError},
10 token::{self, TokenError},
11 token_tree::{self, ToktrError},
12 ty_resolver::{CrossModuleTypeMap, ModuleTypeMap, ResolverError},
13};
14
15#[derive(Debug, Error)]
17pub enum SszError {
18 #[error("tokenizer: {0}")]
20 Token(#[from] TokenError),
21
22 #[error("treeizer: {0}")]
24 TokenTree(#[from] ToktrError),
25
26 #[error("parser: {0}")]
28 Parser(#[from] ParseError),
29
30 #[error("type resolution: {0}")]
32 TyResolver(#[from] ResolverError),
33
34 #[error("schema generation: {0}")]
36 SchemaGen(#[from] SchemaError),
37}
38
39pub fn parse_str_schema(
41 files: &HashMap<PathBuf, String>,
42 external_modules: &[&str],
43) -> Result<(Vec<PathBuf>, HashMap<PathBuf, SszSchema>), SszError> {
44 let mut module_manager = ModuleManager::new(external_modules);
45
46 for (path, content) in files {
47 let chars = content.chars().collect::<Vec<_>>();
48 let tokens = token::parse_char_array_to_tokens(&chars)?;
49 let toktrs = token_tree::parse_tokens_to_toktrs(&tokens)?;
50
51 module_manager.add_module_to_front(path.clone());
54 ast::parse_module_from_toktrs(&toktrs, path, &mut module_manager)?;
55 }
56
57 let mut schema_map = HashMap::new();
58 let mut cross_module_types = CrossModuleTypeMap::new();
59 let mut parsing_order = Vec::new();
60 while let Some((path, module)) = module_manager.pop_module() {
61 if module.is_external() {
62 cross_module_types.insert(path.clone(), ModuleTypeMap::External);
63 continue;
64 }
65 let (schema, idents) = schema::conv_module_to_schema(&module, &cross_module_types)?;
66 parsing_order.push(path.clone());
67 cross_module_types.insert(path.clone(), ModuleTypeMap::Internal(idents));
68 schema_map.insert(path, schema);
69 }
70
71 Ok((parsing_order, schema_map))
72}
73
74#[cfg(test)]
75mod tests {
76 use std::{collections::HashMap, path::Path};
77
78 use crate::pipeline::parse_str_schema;
79
80 #[test]
85 fn test_pipeline_simple() {
86 const SCHEMA: &str = r"
87class Point2d(Container):
88 x_coord: uint32
89 y_coord: uint32
90";
91
92 let files = HashMap::from([(Path::new("").to_path_buf(), SCHEMA.to_string())]);
93 let schema = parse_str_schema(&files, &[]).expect("test: parse schema");
94
95 eprintln!("{schema:#?}");
96 }
97
98 #[test]
99 fn test_pipeline_beacon_deposit_request() {
100 const SCHEMA: &str = r"
102BLSPubkey = List[byte, 96]
103BLSSignature = List[byte, 96]
104Gwei = uint256
105
106class DepositRequest(Container):
107 pubkey: BLSPubkey
108 withdrawal_credentials: Bytes32
109 amount: Gwei
110 signature: BLSSignature
111 index: uint64
112";
113
114 let files = HashMap::from([(Path::new("").to_path_buf(), SCHEMA.to_string())]);
115 let schema = parse_str_schema(&files, &[]).expect("test: parse schema");
116
117 eprintln!("{schema:#?}");
118 }
119
120 #[test]
121 fn test_pipeline_aliases() {
122 const SCHEMA: &str = r"
123OMG = 3
124Epoch = uint32
125SomeVec = List[Epoch, 1337]
126
127class Header(Container):
128 slot: uint64
129 epoch: Epoch
130";
131
132 let files = HashMap::from([(Path::new("").to_path_buf(), SCHEMA.to_string())]);
133 let schema = parse_str_schema(&files, &[]).expect("test: parse schema");
134
135 eprintln!("{schema:#?}");
136 }
137
138 #[test]
139 fn test_pipeline_parent_aliases() {
140 const SCHEMA: &str = r"
142MagicStable = StableContainer[32]
143
144class MagicFoo(MagicStable):
145 foo: Optional[uint32]
146 bar: Optional[uint64]
147";
148
149 let files = HashMap::from([(Path::new("").to_path_buf(), SCHEMA.to_string())]);
150 let schema = parse_str_schema(&files, &[]).expect("test: parse schema");
151
152 eprintln!("{schema:#?}");
153 }
154
155 #[test]
156 fn test_pipeline_imports() {
157 const SCHEMA_AS: &str = r"
158import import_test as test
159import ssz_external as external
160
161TestA = test.A
162TestB = test.B
163TestC = test.C
164TestD = external.D
165
166VAL_A = 12
167VAL_B = VAL_A
168TEST_CONST = test.D
169
170class Header(test.A):
171 a: Union[null, test.B]
172 b: test.B
173 c: test.C
174 d: uint8
175
176f = List[test.A, TEST_CONST]
177";
178
179 const SCHEMA: &str = r"
180import import_test
181import ssz_external.module_a
182
183TestA = import_test.A
184TestB = import_test.B
185TestC = import_test.C
186TestD = module_a.D
187
188VAL_A = 12
189VAL_B = VAL_A
190TEST_CONST = import_test.D
191
192class Header(import_test.A):
193 a: Union[null, import_test.B]
194 b: import_test.B
195 c: import_test.C
196 d: uint8
197
198f = List[import_test.A, TEST_CONST]
199";
200
201 let files = HashMap::from([(
202 Path::new("tests/non_existent").to_path_buf(),
203 SCHEMA_AS.to_string(),
204 )]);
205 let schema = parse_str_schema(&files, &["ssz_external"]).expect("test: parse schema");
206
207 eprintln!("{schema:#?}");
208
209 let files = HashMap::from([(
210 Path::new("tests/non_existent").to_path_buf(),
211 SCHEMA.to_string(),
212 )]);
213 let schema = parse_str_schema(&files, &["ssz_external"]).expect("test: parse schema");
214
215 eprintln!("{schema:#?}");
216 }
217}