diff options
Diffstat (limited to 'lib/xixanta')
| -rw-r--r-- | lib/xixanta/src/assembler.rs | 119 | ||||
| -rw-r--r-- | lib/xixanta/src/lib.rs | 33 | ||||
| -rw-r--r-- | lib/xixanta/src/parser.rs | 15 |
3 files changed, 163 insertions, 4 deletions
diff --git a/lib/xixanta/src/assembler.rs b/lib/xixanta/src/assembler.rs index 57202bf..0d4e132 100644 --- a/lib/xixanta/src/assembler.rs +++ b/lib/xixanta/src/assembler.rs @@ -6,8 +6,8 @@ use crate::node::{ use crate::object::{Bundle, Context, Object, ObjectType}; use crate::opcodes::{AddressingMode, INSTRUCTIONS}; use crate::parser::Parser; -use crate::Error; use crate::SourceInfo; +use crate::{Error, ExpandedFrom}; use std::cmp::Ordering; use std::collections::HashMap; use std::fs::File; @@ -57,6 +57,7 @@ struct PendingNode { bundle_index: usize, node: PNode, labels_seen: usize, + macro_context: Vec<ExpandedFrom>, } /// Memory range that can be identified by a name. @@ -135,6 +136,13 @@ struct Assembler<'a> { // The amount of bytes to reserve for the next variable assignment. asan_next_reserve: usize, + + // Stack of macro expansions. That is, every time a macro is expanded + // (i.e. via 'bundle_call'), the node that performed the expansion is + // stacked here. This is then picked up via structs like 'PendingNode' and + // 'Error' so the programmer gets information on all the macro expansions + // that happened before reaching a given error. + macro_context: Vec<ExpandedFrom>, } /// The result to be given at the end of `assembler::assemble` and @@ -205,6 +213,7 @@ pub fn assemble( line: 0, message: e, source: source.clone(), + expanded_from: vec![], }], warnings: vec![], mappings: vec![], @@ -372,6 +381,7 @@ impl<'a> Assembler<'a> { asan_enabled: false, asan_next_ignore: false, asan_next_reserve: 1, + macro_context: vec![], } } @@ -382,6 +392,7 @@ impl<'a> Assembler<'a> { global: true, line: 0, source: self.sources.first().unwrap().clone(), + expanded_from: self.macro_context.clone(), message: "empty variable name".to_string(), }); } @@ -408,6 +419,7 @@ impl<'a> Assembler<'a> { global: true, line: 0, source: self.sources.first().unwrap().clone(), + expanded_from: self.macro_context.clone(), message: err, }) } else { @@ -436,6 +448,7 @@ impl<'a> Assembler<'a> { message, line: node.value.line, global: false, + expanded_from: self.macro_context.clone(), source: self.source_for(node), }); } @@ -469,6 +482,7 @@ impl<'a> Assembler<'a> { node.value.value ), source: self.source_for(node), + expanded_from: self.macro_context.clone(), global: false, }); continue; @@ -491,6 +505,7 @@ impl<'a> Assembler<'a> { id.value, ), source: self.source_for(node), + expanded_from: self.macro_context.clone(), global: false, }); } @@ -506,6 +521,7 @@ impl<'a> Assembler<'a> { .to_string(), line: node.value.line, source: self.source_for(node), + expanded_from: self.macro_context.clone(), global: false, }); continue; @@ -541,6 +557,7 @@ impl<'a> Assembler<'a> { message: err, line: node.value.line, global: false, + expanded_from: self.macro_context.clone(), source: self.source_for(node), }); } @@ -556,6 +573,7 @@ impl<'a> Assembler<'a> { message: format!("{control_type} must be on the global scope"), line: node.value.line, global: false, + expanded_from: self.macro_context.clone(), source: self.source_for(node), }); continue; @@ -590,6 +608,7 @@ impl<'a> Assembler<'a> { message: "you cannot call '.proc' in this context".to_string(), line: node.value.line, global: false, + expanded_from: self.macro_context.clone(), source: self.source_for(node), }); continue; @@ -614,6 +633,7 @@ impl<'a> Assembler<'a> { message: "you cannot call '.scope' in this context".to_string(), line: node.value.line, source: self.source_for(node), + expanded_from: self.macro_context.clone(), global: false, }); continue; @@ -637,6 +657,7 @@ impl<'a> Assembler<'a> { message, line: node.value.line, global: false, + expanded_from: self.macro_context.clone(), source: self.source_for(node), }); } @@ -713,6 +734,7 @@ impl<'a> Assembler<'a> { message, line: node.value.line, global: false, + expanded_from: self.macro_context.clone(), source: self.source_for(node), }); } @@ -752,6 +774,7 @@ impl<'a> Assembler<'a> { message, line: node.value.line, global: false, + expanded_from: self.macro_context.clone(), source: self.source_for(node), }); } @@ -767,6 +790,7 @@ impl<'a> Assembler<'a> { message: format!("empty .proc '{}'", proc_name.value.value), source: self.source_for(node), global: false, + expanded_from: self.macro_context.clone(), }); } else { self.bundle(args)?; @@ -816,6 +840,7 @@ impl<'a> Assembler<'a> { message: format!("empty .scope '{}'", scope_name.value), source: self.source_for(node), global: false, + expanded_from: self.macro_context.clone(), }); } else { self.bundle(args)?; @@ -918,6 +943,7 @@ impl<'a> Assembler<'a> { line: 0, message: format!("variable {range} is unused"), source: self.sources[0].clone(), + expanded_from: self.macro_context.clone(), global: true, }); continue; @@ -936,6 +962,7 @@ impl<'a> Assembler<'a> { line: 0, global: true, message: "out of internal RAM".to_string(), + expanded_from: self.macro_context.clone(), source: self.sources[0].clone(), }); } @@ -948,6 +975,7 @@ impl<'a> Assembler<'a> { line: 0, global: true, message: "out of working RAM".to_string(), + expanded_from: self.macro_context.clone(), source: self.sources[0].clone(), }); } @@ -979,6 +1007,7 @@ impl<'a> Assembler<'a> { line: 0, global: true, message: format!("The variable {range} conflicts with {existing}",), + expanded_from: self.macro_context.clone(), source: self.sources[0].clone(), }); } @@ -1003,6 +1032,7 @@ impl<'a> Assembler<'a> { line: 0, global: true, message: e, + expanded_from: self.macro_context.clone(), source: self.sources[0].clone(), }]); } @@ -1016,6 +1046,7 @@ impl<'a> Assembler<'a> { line: 0, message: format!("segment '{}' is empty", segment.name), source: self.sources[0].clone(), + expanded_from: self.macro_context.clone(), global: true, }); } @@ -1031,6 +1062,7 @@ impl<'a> Assembler<'a> { mapping.name, mapping.size, mapping.offset, ), source: self.sources[0].clone(), + expanded_from: self.macro_context.clone(), global: false, }); } @@ -1061,6 +1093,7 @@ impl<'a> Assembler<'a> { global: false, line: node.value.line, source: self.source_for(node), + expanded_from: self.macro_context.clone(), message: val.to_string(), }), EchoKind::Error => { @@ -1068,6 +1101,7 @@ impl<'a> Assembler<'a> { global: false, line: node.value.line, source: self.source_for(node), + expanded_from: self.macro_context.clone(), message: val.to_string(), } .into()); @@ -1086,6 +1120,7 @@ impl<'a> Assembler<'a> { "could not find a macro with the name '{}'", node.value.value ), + expanded_from: self.macro_context.clone(), source: self.source_for(node), global: false, })?; @@ -1101,6 +1136,7 @@ impl<'a> Assembler<'a> { "wrong number of arguments for '{}': {} required but {} given", node.value.value, macro_args, given_args, ), + expanded_from: self.macro_context.clone(), source: self.source_for(node), global: false, } @@ -1135,6 +1171,7 @@ impl<'a> Assembler<'a> { line: node.value.line, message, source: self.source_for(node), + expanded_from: self.macro_context.clone(), global: false, } .into()); @@ -1154,10 +1191,16 @@ impl<'a> Assembler<'a> { line: node.value.line, message: format!("trying to apply empty macro '{}'", node.value.value), source: self.source_for(node), + expanded_from: self.macro_context.clone(), global: false, }); } else { + self.macro_context.push(ExpandedFrom { + line: node.value.line, + source: self.source_for(node), + }); self.bundle(inner)?; + let _ = self.macro_context.pop(); } Ok(()) } @@ -1176,6 +1219,7 @@ impl<'a> Assembler<'a> { bundle_index: current.segments[self.current_segment].bundles.len(), node: node.to_owned(), labels_seen: self.context.labels_seen(), + macro_context: self.macro_context.clone(), }); } current.segments[self.current_segment].bundles.push(bundle); @@ -1206,6 +1250,7 @@ impl<'a> Assembler<'a> { message: "invalid identifier".to_string(), line: node.value.line, source: self.source_for(node), + expanded_from: self.macro_context.clone(), global: false, }) } else { @@ -1220,6 +1265,7 @@ impl<'a> Assembler<'a> { message: err.message, line: node.value.line, source: self.source_for(node), + expanded_from: self.macro_context.clone(), global: false, }), } @@ -1230,6 +1276,7 @@ impl<'a> Assembler<'a> { message: format!("unexpected '{}' expression type", node.node_type), line: node.value.line, source: self.source_for(node), + expanded_from: self.macro_context.clone(), global: false, }), } @@ -1293,6 +1340,7 @@ impl<'a> Assembler<'a> { line: node.value.line, global: false, source: self.source_for(node), + expanded_from: self.macro_context.clone(), message: "attempting to divide by zero".to_string(), }); } @@ -1325,6 +1373,7 @@ impl<'a> Assembler<'a> { line: node.value.line, global: false, source: self.source_for(node), + expanded_from: self.macro_context.clone(), message: "shift operator too big".to_string(), }); } @@ -1338,6 +1387,7 @@ impl<'a> Assembler<'a> { line: node.value.line, global: false, source: self.source_for(node), + expanded_from: self.macro_context.clone(), message: "shift operator too big".to_string(), }); } @@ -1377,6 +1427,7 @@ impl<'a> Assembler<'a> { line: node.value.line, global: false, source: self.source_for(node), + expanded_from: self.macro_context.clone(), message: "performing the operation would overflow a 16-bit integer".to_string(), }); } @@ -1422,6 +1473,7 @@ impl<'a> Assembler<'a> { line: node.value.line, message, source: self.source_for(node), + expanded_from: self.macro_context.clone(), global: false, }), } @@ -1430,6 +1482,7 @@ impl<'a> Assembler<'a> { line: node.value.line, source: self.source_for(node), global: false, + expanded_from: self.macro_context.clone(), message: "trying to evaluate a relative reference as a bare value".to_string(), }), } @@ -1472,6 +1525,7 @@ impl<'a> Assembler<'a> { ), source: self.source_for(node), line: node.value.line, + expanded_from: self.macro_context.clone(), global: false, }); } @@ -1479,6 +1533,7 @@ impl<'a> Assembler<'a> { message: "expecting a number of 1 to 4 hexadecimal digits".to_string(), line: node.value.line, source: self.source_for(node), + expanded_from: self.macro_context.clone(), global: false, }); } @@ -1508,6 +1563,7 @@ impl<'a> Assembler<'a> { message: "missing binary digits to get a full byte".to_string(), line: node.value.line, source: self.source_for(node), + expanded_from: self.macro_context.clone(), global: false, }) } @@ -1516,6 +1572,7 @@ impl<'a> Assembler<'a> { message: "too many binary digits for a single byte".to_string(), line: node.value.line, source: self.source_for(node), + expanded_from: self.macro_context.clone(), global: false, }) } @@ -1534,6 +1591,7 @@ impl<'a> Assembler<'a> { ), line: node.value.line, source: self.source_for(node), + expanded_from: self.macro_context.clone(), global: false, }); } @@ -1541,6 +1599,7 @@ impl<'a> Assembler<'a> { message: format!("bad binary format for '{string}'"), line: node.value.line, global: false, + expanded_from: self.macro_context.clone(), source: self.source_for(node), }); } @@ -1564,6 +1623,7 @@ impl<'a> Assembler<'a> { message: "empty decimal literal".to_string(), line: node.value.line, source: self.source_for(node), + expanded_from: self.macro_context.clone(), global: false, }); } @@ -1577,6 +1637,7 @@ impl<'a> Assembler<'a> { message: "decimal value is too big".to_string(), line: node.value.line, source: self.source_for(node), + expanded_from: self.macro_context.clone(), global: false, }); } @@ -1595,6 +1656,7 @@ impl<'a> Assembler<'a> { ), source: self.source_for(node), line: node.value.line, + expanded_from: self.macro_context.clone(), global: false, }); } @@ -1608,6 +1670,7 @@ impl<'a> Assembler<'a> { ), source: self.source_for(node), line: node.value.line, + expanded_from: self.macro_context.clone(), global: false, }); } @@ -1623,6 +1686,7 @@ impl<'a> Assembler<'a> { message: "decimal value is too big".to_string(), line: node.value.line, source: self.source_for(node), + expanded_from: self.macro_context.clone(), global: false, }); } @@ -1658,6 +1722,7 @@ impl<'a> Assembler<'a> { line: node.value.line, source: self.source_for(node), global: false, + expanded_from: self.macro_context.clone(), }); } } else if val.starts_with('%') { @@ -1668,6 +1733,7 @@ impl<'a> Assembler<'a> { line: node.value.line, source: self.source_for(node), global: false, + expanded_from: self.macro_context.clone(), }); } } else { @@ -1696,6 +1762,7 @@ impl<'a> Assembler<'a> { line: source.value.line, source: self.source_for(source), global: false, + expanded_from: self.macro_context.clone(), }); } Err(Error { @@ -1703,6 +1770,7 @@ impl<'a> Assembler<'a> { line: source.value.line, source: self.source_for(source), global: false, + expanded_from: self.macro_context.clone(), }) } }, @@ -1711,6 +1779,7 @@ impl<'a> Assembler<'a> { line: source.value.line, source: self.source_for(source), global: false, + expanded_from: self.macro_context.clone(), }), } } @@ -1731,6 +1800,7 @@ impl<'a> Assembler<'a> { line: node.value.line, source: self.source_for(node), global: false, + expanded_from: self.macro_context.clone(), } .into()); } @@ -1763,6 +1833,7 @@ impl<'a> Assembler<'a> { node.value.value ), source: self.source_for(node), + expanded_from: self.macro_context.clone(), global: false, } .into()), @@ -1783,6 +1854,7 @@ impl<'a> Assembler<'a> { "path has to be written inside of double quotes ('{value}' given instead)", ), source: self.source_for(node), + expanded_from: self.macro_context.clone(), global: false, }); } @@ -1797,6 +1869,7 @@ impl<'a> Assembler<'a> { line: node.value.line, message: format!("could not move to the directory of '{value}': {e}"), source: self.source_for(node), + expanded_from: self.macro_context.clone(), global: false, }); } @@ -1813,6 +1886,7 @@ impl<'a> Assembler<'a> { global: false, line: node.value.line, source: self.source_for(node), + expanded_from: self.macro_context.clone(), message: format!("could not include binary data: {e}"), }) } @@ -1832,6 +1906,7 @@ impl<'a> Assembler<'a> { global: false, line: node.value.line, source: self.source_for(node), + expanded_from: self.macro_context.clone(), message: format!("file '{path}' is too big"), }); } else if metadata.len() == 0 { @@ -1839,6 +1914,7 @@ impl<'a> Assembler<'a> { global: false, line: node.value.line, source: self.source_for(node), + expanded_from: self.macro_context.clone(), message: format!("trying to include an empty file ('{path}')"), }); } @@ -1848,6 +1924,7 @@ impl<'a> Assembler<'a> { global: false, line: node.value.line, source: self.source_for(node), + expanded_from: self.macro_context.clone(), message: format!("could not include binary data: {e}"), }) } @@ -1877,6 +1954,7 @@ impl<'a> Assembler<'a> { global: false, line: node.value.line, source: self.source_for(node), + expanded_from: self.macro_context.clone(), message: "pointless .repeat statement".to_string(), } .into()); @@ -1885,6 +1963,7 @@ impl<'a> Assembler<'a> { global: false, line: node.value.line, source: self.source_for(node), + expanded_from: self.macro_context.clone(), message: "the number of iterations has to fit in a single byte".to_string(), } .into()); @@ -1896,6 +1975,7 @@ impl<'a> Assembler<'a> { global: false, line: node.value.line, message: format!("first argument must be an integer, '{first}' found instead"), + expanded_from: self.macro_context.clone(), source: self.source_for(node), } .into()) @@ -1909,6 +1989,7 @@ impl<'a> Assembler<'a> { line: node.value.line, message: "empty .repeat statement".to_string(), source: self.source_for(node), + expanded_from: self.macro_context.clone(), global: false, }); return Ok(()); @@ -1937,6 +2018,7 @@ impl<'a> Assembler<'a> { line: node.value.line, message: e, source: self.source_for(node), + expanded_from: self.macro_context.clone(), global: false, } .into()); @@ -1998,6 +2080,7 @@ impl<'a> Assembler<'a> { line: node.value.line, message: "empty body".to_string(), source: self.source_for(node), + expanded_from: self.macro_context.clone(), global: false, }); } else if self.stage == Stage::Context { @@ -2020,6 +2103,7 @@ impl<'a> Assembler<'a> { "cannot handle control statement '{}' as an expression in this context", node.value.value ), + expanded_from: self.macro_context.clone(), source: self.source_for(node), global: false, }), @@ -2089,6 +2173,7 @@ impl<'a> Assembler<'a> { message: "expecting an argument that fits into a byte" .to_string(), source: self.source_for(node), + expanded_from: self.macro_context.clone(), global: false, }) } @@ -2111,6 +2196,7 @@ impl<'a> Assembler<'a> { node.value.value.as_str(), ), source: self.source_for(node), + expanded_from: self.macro_context.clone(), global: false, }) } @@ -2131,6 +2217,7 @@ impl<'a> Assembler<'a> { global: false, line: node.value.line, source: self.source_for(node), + expanded_from: self.macro_context.clone(), message: "you are trying to reserve too much memory".to_string(), }); } @@ -2140,6 +2227,7 @@ impl<'a> Assembler<'a> { global: false, line: node.value.line, source: self.source_for(node), + expanded_from: self.macro_context.clone(), message: "empty .res statement".to_string(), }); } else if iterations == 1 { @@ -2147,6 +2235,7 @@ impl<'a> Assembler<'a> { global: false, line: node.value.line, source: self.source_for(node), + expanded_from: self.macro_context.clone(), message: "pointless .res statement, prefer using .byte".to_string(), }); } @@ -2161,6 +2250,7 @@ impl<'a> Assembler<'a> { global: false, line: node.value.line, source: self.source_for(node), + expanded_from: self.macro_context.clone(), message: "fill value must fit into a single byte".to_string(), }); } @@ -2193,6 +2283,7 @@ impl<'a> Assembler<'a> { line: node.value.line, message: "unpermitted empty argument".to_string(), source: self.source_for(node), + expanded_from: self.macro_context.clone(), global: false, }); } @@ -2203,6 +2294,7 @@ impl<'a> Assembler<'a> { "declaration has to be written inside of double quotes ('{val}' given instead)", ), source: self.source_for(node), + expanded_from: self.macro_context.clone(), global: false, }); } @@ -2236,6 +2328,7 @@ impl<'a> Assembler<'a> { line: node.value.line, message: "segment name contains bad characters".to_string(), source: self.source_for(node), + expanded_from: self.macro_context.clone(), global: false, }); } @@ -2258,6 +2351,7 @@ impl<'a> Assembler<'a> { line: node.value.line, message: format!("unknown segment '{name}'"), source: self.source_for(node), + expanded_from: self.macro_context.clone(), global: false, }); } @@ -2296,6 +2390,7 @@ impl<'a> Assembler<'a> { line: node.value.line, source: self.source_for(node), global: false, + expanded_from: self.macro_context.clone(), }), } } @@ -2336,6 +2431,7 @@ impl<'a> Assembler<'a> { source: self.source_for(node), line: node.value.line, global: false, + expanded_from: self.macro_context.clone(), }); } }, @@ -2345,6 +2441,7 @@ impl<'a> Assembler<'a> { line: node.value.line, source: self.source_for(node), global: false, + expanded_from: self.macro_context.clone(), }); } } @@ -2380,6 +2477,7 @@ impl<'a> Assembler<'a> { line: node.value.line, source: self.source_for(node), global: false, + expanded_from: self.macro_context.clone(), }); } @@ -2392,6 +2490,7 @@ impl<'a> Assembler<'a> { line: node.value.line, source: self.source_for(node), global: false, + expanded_from: self.macro_context.clone(), }); } self.asan_check_arm(evaluated_node, &val)?; @@ -2402,6 +2501,7 @@ impl<'a> Assembler<'a> { line: node.value.line, source: self.source_for(node), global: false, + expanded_from: self.macro_context.clone(), }) } None => match left.right.as_ref() { @@ -2417,6 +2517,7 @@ impl<'a> Assembler<'a> { line: node.value.line, source: self.source_for(node), global: false, + expanded_from: self.macro_context.clone(), }); } self.asan_check_arm(evaluated_node, &val)?; @@ -2427,6 +2528,7 @@ impl<'a> Assembler<'a> { line: node.value.line, source: self.source_for(node), global: false, + expanded_from: self.macro_context.clone(), }) } None => { @@ -2438,6 +2540,7 @@ impl<'a> Assembler<'a> { line: node.value.line, source: self.source_for(node), global: false, + expanded_from: self.macro_context.clone(), }); } if !matches!(node.value.value.as_str(), "jmp" | "jsr") { @@ -2463,6 +2566,7 @@ impl<'a> Assembler<'a> { line: node.value.line, source: self.source_for(node), global: false, + expanded_from: self.macro_context.clone(), }); } } @@ -2530,6 +2634,7 @@ impl<'a> Assembler<'a> { line: node.value.line, source: self.source_for(node), global: false, + expanded_from: self.macro_context.clone(), }), } } @@ -2575,6 +2680,7 @@ impl<'a> Assembler<'a> { line: left_arm.value.line, source: self.source_for(base), global: false, + expanded_from: self.macro_context.clone(), }), } } else { @@ -2587,6 +2693,7 @@ impl<'a> Assembler<'a> { line: left_arm.value.line, source: self.source_for(base), global: false, + expanded_from: self.macro_context.clone(), }), } } @@ -2628,6 +2735,7 @@ impl<'a> Assembler<'a> { ), source: self.source_for(node), global: false, + expanded_from: self.macro_context.clone(), }); } Ok(()) @@ -2646,6 +2754,7 @@ impl<'a> Assembler<'a> { message: "accessing a memory region without a proper name".to_string(), source: self.source_for(node), global: false, + expanded_from: self.macro_context.clone(), }); return Ok(()); }; @@ -2671,6 +2780,7 @@ impl<'a> Assembler<'a> { message: format!("out of bounds memory access for {range}"), source: self.source_for(node), global: false, + expanded_from: self.macro_context.clone(), }); } } @@ -2685,6 +2795,7 @@ impl<'a> Assembler<'a> { message: "accessing a memory region without using a variable".to_string(), source: self.source_for(node), global: false, + expanded_from: self.macro_context.clone(), }); Ok(()) @@ -2708,6 +2819,7 @@ impl<'a> Assembler<'a> { message: "you cannot use a negative value for a memory variable".to_string(), source: self.source_for(node), global: false, + expanded_from: self.macro_context.clone(), }); } @@ -2727,6 +2839,7 @@ impl<'a> Assembler<'a> { message: format!("you are assigning the zero-page variable {range} to something outside of zero page"), source: self.source_for(node), global: false, + expanded_from: self.macro_context.clone(), }); } } else if bundle.size != 2 { @@ -2742,6 +2855,7 @@ impl<'a> Assembler<'a> { ), source: self.source_for(node), global: false, + expanded_from: self.macro_context.clone(), }); } else if actual_name.starts_with("wr_") { let value = bundle.value() as usize; @@ -2757,6 +2871,7 @@ impl<'a> Assembler<'a> { ), source: self.source_for(node), global: false, + expanded_from: self.macro_context.clone(), }); } } @@ -2780,6 +2895,7 @@ impl<'a> Assembler<'a> { message: "you cannot branch to this location: it's too far away".to_string(), source: self.source_for(node), global: false, + expanded_from: self.macro_context.clone(), }); } diff.to_le_bytes()[0] @@ -2791,6 +2907,7 @@ impl<'a> Assembler<'a> { message: "you cannot branch to this location: it's too far away".to_string(), source: self.source_for(node), global: false, + expanded_from: self.macro_context.clone(), }); } diff.to_le_bytes()[0] diff --git a/lib/xixanta/src/lib.rs b/lib/xixanta/src/lib.rs index 8515e23..a45868f 100644 --- a/lib/xixanta/src/lib.rs +++ b/lib/xixanta/src/lib.rs @@ -17,6 +17,20 @@ impl Default for SourceInfo { } } +/// Contains all the information relevant for a macro expansion. That is, +/// whenever expanding a macro call, the assembler is expected to create a new +/// 'ExpandedFrom' object with all the relevant information so a node underneath +/// can refer to it whenever an error occurred (e.g. 'I detected an error here +/// but I was coming from an expansion from "there"'). +#[derive(Clone, Debug, PartialEq)] +pub struct ExpandedFrom { + /// The source from which the expansion happened. + pub source: SourceInfo, + + /// The line in the 'source' where the macro expansion happened. + pub line: usize, +} + /// Error provides an interface used throughout this crate in which an error /// message of type String is located through a line and an associated /// SourceInfo. If global is set to true, then `line` does not matter. @@ -32,6 +46,11 @@ pub struct Error { /// Information on the file where the error was found. pub source: SourceInfo, + /// Stack of macro expansions before reaching to the error. This is used for + /// the 'Display' trait, so the expansion context is rewinded and shown to + /// the user. + pub expanded_from: Vec<ExpandedFrom>, + /// Human-readable representation of the error. Don't use this field /// directly, prefer its `std::fmt::Display` implementation. Hence, calling /// `.to_string()` on it is most probably the way to go. @@ -57,13 +76,21 @@ impl std::fmt::Display for Error { } else if self.source.name.is_empty() { write!(f, "{} (line {})", self.message, self.line + 1) } else { - write!( - f, + let mut msg = vec![format!( "{} ({}: line {})", self.message, self.source.name, self.line + 1 - ) + )]; + for ef in &self.expanded_from { + msg.push(format!( + " >> expanded from {}: line {}.", + ef.source.name, + ef.line + 1 + )); + } + + write!(f, "{}", msg.join("\n")) } } } diff --git a/lib/xixanta/src/parser.rs b/lib/xixanta/src/parser.rs index cf01133..7a7e3f0 100644 --- a/lib/xixanta/src/parser.rs +++ b/lib/xixanta/src/parser.rs @@ -242,6 +242,7 @@ impl Parser { global: false, source: self.sources[self.current_source].clone(), message: "expecting a number formatted with a leading '$' sign".to_string(), + expanded_from: vec![], }) } } @@ -257,6 +258,7 @@ impl Parser { global: false, source: self.sources[self.current_source].clone(), message: "expecting a number formatted with a leading '$' sign".to_string(), + expanded_from: vec![], }); } let Ok(val) = usize::from_str_radix(arg.get(1..).unwrap_or("0000"), 16) else { @@ -264,6 +266,7 @@ impl Parser { line: self.line, global: false, source: self.sources[self.current_source].clone(), + expanded_from: vec![], message: "could not parse asan:reserve number".to_string(), }); }; @@ -272,6 +275,7 @@ impl Parser { line: self.line, global: false, source: self.sources[self.current_source].clone(), + expanded_from: vec![], message: "bad asan:reserve number, should be higher than $01".to_string(), }); } @@ -460,6 +464,7 @@ impl Parser { line: self.line, global: false, source: self.sources[self.current_source].clone(), + expanded_from: vec![], message: "you cannot have a relative label inside of an identifier".to_string(), }); } @@ -484,6 +489,7 @@ impl Parser { line: self.line, global: false, source: self.sources[self.current_source].clone(), + expanded_from: vec![], message: "you can only jump to a maximum of four relative labels".to_string(), }); } @@ -497,6 +503,7 @@ impl Parser { line: self.line, global: false, source: self.sources[self.current_source].clone(), + expanded_from: vec![], message: format!("{msg} relative label can only have '{next}' characters"), }); } @@ -801,6 +808,7 @@ impl Parser { line: node.value.line, global: false, message: ".include statement cannot be inside of a code block".to_string(), + expanded_from: vec![], source: self.sources[self.current_source].clone(), } .into()); @@ -963,6 +971,7 @@ impl Parser { line: node.value.line, global: false, source: current_source.clone(), + expanded_from: vec![], message: ".include statements expect a file as the argument".to_string(), } .into()); @@ -980,6 +989,7 @@ impl Parser { line: node.value.line, global: false, source: current_source.clone(), + expanded_from: vec![], message: format!("could not open source file '{file_path}': {e}"), } .into()) @@ -990,6 +1000,7 @@ impl Parser { line: node.value.line, global: false, source: current_source.clone(), + expanded_from: vec![], message: format!("could not find out the parent directory for file '{file_path}'"), } .into()); @@ -1030,6 +1041,7 @@ impl Parser { line: node.value.line, global: false, source: self.sources[self.current_source].clone(), + expanded_from: vec![], message: format!( "path has to be written inside of double quotes ('{value}' given instead)", ), @@ -1443,6 +1455,7 @@ impl Parser { line: self.line, global: false, source: self.sources[self.current_source].clone(), + expanded_from: vec![], message: "bad literal syntax".to_string(), }); } @@ -1645,6 +1658,7 @@ impl Parser { line: self.line, global: false, source: self.sources[self.current_source].clone(), + expanded_from: vec![], message: "numeric literals cannot have white spaces".to_string(), }); } @@ -1744,6 +1758,7 @@ impl Parser { message: String::from(msg), global: false, source: self.sources[self.current_source].clone(), + expanded_from: vec![], line: self.line, } } |
