customasm/asm/parser/
mod.rs

1use crate::*;
2
3
4mod directive;
5
6mod directive_addr;
7pub use directive_addr::AstDirectiveAddr;
8
9mod directive_align;
10pub use directive_align::AstDirectiveAlign;
11
12mod directive_assert;
13pub use directive_assert::AstDirectiveAssert;
14
15mod directive_bank;
16pub use directive_bank::AstDirectiveBank;
17
18mod directive_bankdef;
19pub use directive_bankdef::AstDirectiveBankdef;
20
21mod directive_bits;
22pub use directive_bits::AstDirectiveBits;
23
24mod directive_const;
25
26mod directive_data;
27pub use directive_data::AstDirectiveData;
28
29mod directive_fn;
30pub use directive_fn::{
31    AstDirectiveFn,
32    AstFnParameter,
33};
34
35mod directive_if;
36pub use directive_if::AstDirectiveIf;
37
38mod directive_include;
39pub use directive_include::AstDirectiveInclude;
40
41mod directive_labelalign;
42pub use directive_labelalign::AstDirectiveLabelAlign;
43
44mod directive_noemit;
45pub use directive_noemit::AstDirectiveNoEmit;
46
47mod directive_once;
48pub use directive_once::AstDirectiveOnce;
49
50mod directive_res;
51pub use directive_res::AstDirectiveRes;
52
53mod directive_ruledef;
54pub use directive_ruledef::{
55    AstDirectiveRuledef,
56    AstRule,
57    AstRulePatternPart,
58    AstRuleParameter,
59    AstRuleParameterType,
60};
61
62mod fields;
63pub use fields::{
64    AstFields,
65    AstField,
66};
67
68mod instruction;
69pub use instruction::AstInstruction;
70
71mod symbol;
72pub use symbol::{
73    AstSymbol,
74    AstSymbolKind,
75    AstSymbolConstant,
76};
77
78
79#[derive(Clone, Debug)]
80pub enum AstAny
81{
82    DirectiveAddr(AstDirectiveAddr),
83    DirectiveAlign(AstDirectiveAlign),
84    DirectiveAssert(AstDirectiveAssert),
85    DirectiveBank(AstDirectiveBank),
86    DirectiveBankdef(AstDirectiveBankdef),
87    DirectiveBits(AstDirectiveBits),
88    DirectiveData(AstDirectiveData),
89    DirectiveFn(AstDirectiveFn),
90    DirectiveIf(AstDirectiveIf),
91    DirectiveInclude(AstDirectiveInclude),
92    DirectiveLabelAlign(AstDirectiveLabelAlign),
93    DirectiveNoEmit(AstDirectiveNoEmit),
94    DirectiveOnce(AstDirectiveOnce),
95    DirectiveRes(AstDirectiveRes),
96    DirectiveRuledef(AstDirectiveRuledef),
97    Instruction(AstInstruction),
98    Symbol(AstSymbol),
99}
100
101
102#[derive(Clone, Debug)]
103pub struct AstTopLevel
104{
105    pub nodes: Vec<AstAny>,
106}
107
108
109pub fn parse_many_and_resolve_includes<S>(
110    report: &mut diagn::Report,
111    fileserver: &mut dyn util::FileServer,
112    root_filenames: &[S])
113    -> Result<AstTopLevel, ()>
114    where S: std::borrow::Borrow<str>
115{
116    let mut result = AstTopLevel {
117        nodes: Vec::new(),
118    };
119
120    let mut once_filenames = std::collections::HashSet::new();
121
122    for file in root_filenames
123    {
124        let ast = parse_and_resolve_includes(
125            report,
126            None,
127            fileserver,
128            file.borrow(),
129            &mut Vec::new(),
130            &mut once_filenames)?;
131
132        result.nodes.extend(ast.nodes);
133    }
134
135    Ok(result)
136}
137
138
139pub fn parse_and_resolve_includes<S>(
140    report: &mut diagn::Report,
141    span: Option<diagn::Span>,
142    fileserver: &mut dyn util::FileServer,
143    root_filename: S,
144    seen_filenames: &mut Vec<String>,
145    once_filenames: &mut std::collections::HashSet<String>)
146    -> Result<AstTopLevel, ()>
147    where S: std::borrow::Borrow<str>
148{
149    if once_filenames.contains(root_filename.borrow())
150    {
151        return Ok(AstTopLevel {
152            nodes: Vec::new(),
153        });
154    }
155
156    let file_handle = fileserver.get_handle(
157        report,
158        span,
159        root_filename.borrow())?;
160
161    let src = fileserver.get_str(
162        report,
163        span,
164        file_handle)?;
165
166    let mut walker = syntax::Walker::new(
167        &src,
168        file_handle,
169        0);
170
171    let mut root_ast = parse(report, &mut walker)?;
172
173    // Check presence of an #once directive
174    if root_ast.nodes.iter().any(|n| matches!(n, AstAny::DirectiveOnce(_)))
175    {
176        once_filenames.insert(root_filename.borrow().to_owned());
177    }
178
179    // Recursively find and replace our `#include` AST nodes
180    // with the full ASTs of the included files
181    let mut node_index = 0;
182    while node_index < root_ast.nodes.len()
183    {
184        let node = &root_ast.nodes[node_index];
185
186        if let AstAny::DirectiveInclude(ast_include) = node
187        {
188            let included_filename = util::filename_navigate(
189                report,
190                ast_include.filename_span,
191                root_filename.borrow(),
192                &ast_include.filename)?;
193
194
195            if seen_filenames.contains(&included_filename)
196            {
197                report.error_span(
198                    "recursive file inclusion",
199                    ast_include.filename_span);
200
201                return Err(());
202            }
203
204    
205            seen_filenames.push(included_filename.clone());
206
207            let inner_ast = parse_and_resolve_includes(
208                report,
209                Some(ast_include.filename_span),
210                fileserver,
211                included_filename.as_ref(),
212                seen_filenames,
213                once_filenames)?;
214
215            let inner_ast_len = inner_ast.nodes.len();
216
217            root_ast.nodes.splice(
218                node_index..(node_index + 1),
219                inner_ast.nodes);
220
221            // Skip over the included AST since it already
222            // had its own `#include` nodes handled and replaced
223            node_index += inner_ast_len;
224            
225            seen_filenames.pop();
226        }
227        else
228        {
229            node_index += 1;
230        }
231    }
232
233    Ok(root_ast)
234}
235
236
237pub fn parse(
238    report: &mut diagn::Report,
239    walker: &mut syntax::Walker)
240    -> Result<AstTopLevel, ()>
241{
242    let mut nodes = Vec::new();
243    
244    while !walker.is_over()
245    {
246        if let Some(node) = parse_line(report, walker)?
247        {
248            nodes.push(node);
249        }
250    }
251
252    Ok(AstTopLevel {
253        nodes
254    })
255}
256
257
258pub fn parse_nested_toplevel(
259    report: &mut diagn::Report,
260    walker: &mut syntax::Walker)
261    -> Result<AstTopLevel, ()>
262{
263    let mut nodes = Vec::new();
264    
265    while !walker.is_over() &&
266        !walker.next_useful_is(0, syntax::TokenKind::BraceClose)
267    {
268        if let Some(node) = parse_line(report, walker)?
269        {
270            nodes.push(node);
271        }
272    }
273
274    Ok(AstTopLevel {
275        nodes
276    })
277}
278
279
280fn parse_line(
281    report: &mut diagn::Report,
282    walker: &mut syntax::Walker)
283    -> Result<Option<AstAny>, ()>
284{
285    // Directives (starting with a hash sign)
286    if walker.next_useful_is(0, syntax::TokenKind::Hash)
287    {
288        Ok(Some(directive::parse(report, walker)?))
289    }
290
291    // Global labels (identifiers followed by colons)
292    else if walker.next_useful_is(0, syntax::TokenKind::Identifier) &&
293        walker.next_useful_is(1, syntax::TokenKind::Colon)
294    {
295        Ok(Some(symbol::parse(report, walker)?))
296    }
297
298    // Global constants (identifiers followed by equal signs)
299    else if walker.next_useful_is(0, syntax::TokenKind::Identifier) &&
300        walker.next_useful_is(1, syntax::TokenKind::Equal)
301    {
302        Ok(Some(symbol::parse(report, walker)?))
303    }
304
305    // Local labels or constants (starting with a dot)
306    else if walker.next_useful_is(0, syntax::TokenKind::Dot)
307    {
308        Ok(Some(symbol::parse(report, walker)?))
309    }
310
311    // Empty lines
312    else if walker.maybe_expect_linebreak().is_some()
313    {
314        Ok(None)
315    }
316
317    // Everything else is regarded as an instruction
318    else
319    {
320        Ok(Some(AstAny::Instruction(
321            instruction::parse(report, walker)?)))
322    }
323}
324
325
326impl AstAny
327{
328    pub fn span(&self) -> diagn::Span
329    {
330        match self
331        {
332            AstAny::DirectiveAddr(node) => node.header_span,
333            AstAny::DirectiveAlign(node) => node.header_span,
334            AstAny::DirectiveAssert(node) => node.header_span,
335            AstAny::DirectiveBank(node) => node.header_span,
336            AstAny::DirectiveBankdef(node) => node.header_span,
337            AstAny::DirectiveBits(node) => node.header_span,
338            AstAny::DirectiveData(node) => node.header_span,
339            AstAny::DirectiveFn(node) => node.header_span,
340            AstAny::DirectiveIf(node) => node.header_span,
341            AstAny::DirectiveInclude(node) => node.header_span,
342            AstAny::DirectiveLabelAlign(node) => node.header_span,
343            AstAny::DirectiveNoEmit(node) => node.header_span,
344            AstAny::DirectiveOnce(node) => node.header_span,
345            AstAny::DirectiveRes(node) => node.header_span,
346            AstAny::DirectiveRuledef(node) => node.header_span,
347            AstAny::Instruction(node) => node.span,
348            AstAny::Symbol(node) => node.decl_span,
349        }
350    }
351}