aboutsummaryrefslogtreecommitdiff
path: root/lib/xixanta
diff options
context:
space:
mode:
authorMiquel Sabaté Solà <mikisabate@gmail.com>2025-01-09 16:26:20 +0100
committerMiquel Sabaté Solà <mikisabate@gmail.com>2025-01-09 16:30:01 +0100
commit92092ccc245269e0c3a864d06f7e382a5bb9d8b0 (patch)
tree6670969fdb03d6288fbc500b2792c833c5345b35 /lib/xixanta
parent0f6e6d0b2f6887e025a08a862b55288f871e62ca (diff)
downloadtools.nes-92092ccc245269e0c3a864d06f7e382a5bb9d8b0.tar.gz
tools.nes-92092ccc245269e0c3a864d06f7e382a5bb9d8b0.zip
Merge all error types into a single one
They started with good intentions, but in the end they were all pretty much alike. Hence, it makes sense to simplify everything and provide a single struct. Signed-off-by: Miquel Sabaté Solà <mikisabate@gmail.com>
Diffstat (limited to 'lib/xixanta')
-rw-r--r--lib/xixanta/src/assembler.rs266
-rw-r--r--lib/xixanta/src/errors.rs143
-rw-r--r--lib/xixanta/src/lib.rs52
-rw-r--r--lib/xixanta/src/parser.rs91
4 files changed, 234 insertions, 318 deletions
diff --git a/lib/xixanta/src/assembler.rs b/lib/xixanta/src/assembler.rs
index e472323..99f76d9 100644
--- a/lib/xixanta/src/assembler.rs
+++ b/lib/xixanta/src/assembler.rs
@@ -1,9 +1,9 @@
-use crate::errors::{ContextError, Error, EvalError};
use crate::mapping::{get_mapping_configuration, Mapping};
use crate::node::{ControlType, NodeType, OperationType, PNode};
use crate::object::{Bundle, Context, Object, ObjectType};
use crate::opcodes::{AddressingMode, INSTRUCTIONS};
use crate::parser::Parser;
+use crate::Error;
use crate::SourceInfo;
use std::cmp::Ordering;
use std::collections::HashMap;
@@ -101,12 +101,12 @@ pub fn assemble(reader: impl Read, mapping: &str, source: SourceInfo) -> Assembl
Err(e) => {
return AssemblerResult {
bundles: vec![],
- errors: vec![Error::Eval(EvalError {
+ errors: vec![Error {
global: true,
line: 0,
message: e,
source,
- })],
+ }],
warnings: vec![],
};
}
@@ -134,7 +134,7 @@ pub fn assemble_with_mapping(
if let Err(errors) = parser.parse(reader, source) {
return AssemblerResult {
bundles: vec![],
- errors: errors.iter().map(|e| Error::Parse(e.clone())).collect(),
+ errors,
warnings: asm.warnings,
};
}
@@ -212,7 +212,7 @@ impl<'a> Assembler<'a> {
// Define a new variable by taking the given `id`. This variable will only
// be created if `id` is not empty. The function will error out if the given
// name is already taken.
- fn define_variable(&mut self, node: &PNode) -> Result<(), ContextError> {
+ fn define_variable(&mut self, node: &PNode) -> Result<(), Error> {
if node.value.is_empty() {
return Ok(());
}
@@ -226,7 +226,7 @@ impl<'a> Assembler<'a> {
),
false,
) {
- return Err(ContextError {
+ return Err(Error {
message,
line: node.value.line,
global: false,
@@ -250,7 +250,7 @@ impl<'a> Assembler<'a> {
// There's no good reason to declare a named label inside of
// a macro. If that's the case, just error out.
if (self.macros_seen > 0 || self.repeats_seen > 0) && !node.value.is_empty() {
- errors.push(Error::Eval(EvalError {
+ errors.push(Error {
line: node.value.line,
message: format!(
"using a named label ('{}') inside of a macro/repeat definition",
@@ -258,22 +258,22 @@ impl<'a> Assembler<'a> {
),
source: self.source_for(node),
global: false,
- }));
+ });
continue;
}
if let Err(err) = self.define_variable(node) {
- errors.push(Error::Context(err));
+ errors.push(err);
}
}
NodeType::Assignment => {
if self.macros_seen > 0 || self.repeats_seen > 0 {
- errors.push(Error::Eval(EvalError {
+ errors.push(Error {
message: "cannot have assignments inside of macro/repeat definitions"
.to_string(),
line: node.value.line,
source: self.source_for(node),
global: false,
- }));
+ });
continue;
}
self.literal_mode = None;
@@ -289,25 +289,25 @@ impl<'a> Assembler<'a> {
},
false,
) {
- errors.push(Error::Context(ContextError {
+ errors.push(Error {
message: err,
line: node.value.line,
global: false,
source: self.source_for(node),
- }));
+ });
}
}
- Err(e) => errors.push(Error::Eval(e)),
+ Err(e) => errors.push(e),
}
}
NodeType::Control(control_type) => {
if !self.context.is_global() && control_type.must_be_global() {
- errors.push(Error::Context(ContextError {
+ errors.push(Error {
message: format!("{} must be on the global scope", control_type),
line: node.value.line,
global: false,
source: self.source_for(node),
- }));
+ });
continue;
}
@@ -336,19 +336,19 @@ impl<'a> Assembler<'a> {
ControlType::StartProc => {
if self.macros_seen > 0 || self.procs_seen > 0 || self.repeats_seen > 0
{
- errors.push(Error::Context(ContextError {
+ errors.push(Error {
message: "you cannot call '.proc' in this context".to_string(),
line: node.value.line,
global: false,
source: self.source_for(node),
- }));
+ });
continue;
}
self.procs_seen += 1;
let proc_name = &node.left.as_ref().unwrap();
if let Err(err) = self.define_variable(proc_name) {
- errors.push(Error::Context(err));
+ errors.push(err);
}
}
ControlType::EndProc => {
@@ -360,12 +360,12 @@ impl<'a> Assembler<'a> {
ControlType::StartScope => {
if self.macros_seen > 0 || self.procs_seen > 0 || self.repeats_seen > 0
{
- errors.push(Error::Context(ContextError {
+ errors.push(Error {
message: "you cannot call '.scope' in this context".to_string(),
line: node.value.line,
source: self.source_for(node),
global: false,
- }));
+ });
continue;
}
}
@@ -383,12 +383,12 @@ impl<'a> Assembler<'a> {
// If this control statement implies a context change, do it
// now.
if let Err(message) = self.context.change_context(node) {
- errors.push(Error::Context(ContextError {
+ errors.push(Error {
message,
line: node.value.line,
global: false,
source: self.source_for(node),
- }));
+ });
}
// If this control statement actually has a body, go inside
@@ -413,7 +413,7 @@ impl<'a> Assembler<'a> {
// it's empty (i.e. anonymous label). In either case, the computed label
// will be pushed into the context's list of known labels with the current
// segment offset.
- fn apply_segment_offset_to_label(&mut self, node: &PNode) -> Result<(), ContextError> {
+ fn apply_segment_offset_to_label(&mut self, node: &PNode) -> Result<(), Error> {
let segment = &self.mappings[self.current_mapping].segments[self.current_segment];
let value = segment.offset.to_le_bytes();
let object = Object {
@@ -433,7 +433,7 @@ impl<'a> Assembler<'a> {
if !node.value.is_empty() {
if let Err(message) = self.context.set_variable(&node.value, &object, true) {
- return Err(ContextError {
+ return Err(Error {
message,
line: node.value.line,
global: false,
@@ -459,7 +459,7 @@ impl<'a> Assembler<'a> {
// after calling `Context::get_variable`
NodeType::Label => {
if let Err(e) = self.apply_segment_offset_to_label(node) {
- errors.push(Error::Context(e));
+ errors.push(e);
}
}
// Same as with labels but with the addition that ".proc"
@@ -468,15 +468,15 @@ impl<'a> Assembler<'a> {
NodeType::Control(ControlType::StartProc) => {
let proc_name = &node.left.as_ref().unwrap();
if let Err(e) = self.apply_segment_offset_to_label(proc_name) {
- errors.push(Error::Context(e));
+ errors.push(e);
}
if let Err(message) = self.context.change_context(node) {
- errors.push(Error::Context(ContextError {
+ errors.push(Error {
message,
line: node.value.line,
global: false,
source: self.source_for(node),
- }));
+ });
}
// And now go inside of its body if it exists (note that its
@@ -485,12 +485,12 @@ impl<'a> Assembler<'a> {
// if node.right.as_ref().is_some() {
let args = &node.right.as_ref().unwrap().args.as_ref().unwrap();
if args.is_empty() {
- self.warnings.push(Error::Eval(EvalError {
+ self.warnings.push(Error {
line: node.value.line,
message: format!("empty .proc '{}'", proc_name.value.value),
source: self.source_for(node),
global: false,
- }));
+ });
} else {
self.bundle(args)?;
}
@@ -500,15 +500,15 @@ impl<'a> Assembler<'a> {
match self.evaluate_node(node) {
Ok(bundle) => {
if let Err(e) = self.push_bundle(bundle, node) {
- errors.push(Error::Eval(e));
+ errors.push(e);
}
}
- Err(e) => errors.push(Error::Eval(e)),
+ Err(e) => errors.push(e),
}
}
NodeType::Control(control_type) => {
if let Err(e) = self.evaluate_control_statement(node) {
- errors.push(Error::Eval(e));
+ errors.push(e);
}
// On control statements which modify the context, there are
@@ -529,12 +529,12 @@ impl<'a> Assembler<'a> {
let scope_name = &node.left.as_ref().unwrap().value;
let args = &node.right.as_ref().unwrap().args.as_ref().unwrap();
if args.is_empty() {
- self.warnings.push(Error::Eval(EvalError {
+ self.warnings.push(Error {
line: node.value.line,
message: format!("empty .scope '{}'", scope_name.value),
source: self.source_for(node),
global: false,
- }));
+ });
} else {
self.bundle(args)?;
}
@@ -578,14 +578,14 @@ impl<'a> Assembler<'a> {
if pn.node.is_branch() {
bundle.resolved = true;
if let Err(e) = self.to_relative_address(&pn.node, &mut bundle) {
- errors.push(Error::Eval(e));
+ errors.push(e);
}
}
let current_mut = &mut self.mappings[pn.mapping].segments[pn.segment];
current_mut.bundles[pn.bundle_index].bytes = bundle.bytes;
}
- Err(e) => errors.push(Error::Eval(e)),
+ Err(e) => errors.push(e),
}
self.context.force_context_pop();
@@ -604,12 +604,12 @@ impl<'a> Assembler<'a> {
// Validate the mappings that have been evaluated before spitting it
// out.
if let Err(e) = crate::mapping::validate(&self.mappings) {
- return Err(vec![Error::Eval(EvalError {
+ return Err(vec![Error {
line: 0,
global: true,
message: e,
source: self.sources[0].clone(),
- })]);
+ }]);
}
let mut res = vec![];
@@ -617,19 +617,19 @@ impl<'a> Assembler<'a> {
for mapping in &mut self.mappings {
for segment in mapping.segments.iter_mut() {
if segment.is_empty() {
- self.warnings.push(Error::Eval(EvalError {
+ self.warnings.push(Error {
line: 0,
message: format!("segment '{}' is empty", segment.name),
source: self.sources[0].clone(),
global: true,
- }));
+ });
}
res.append(&mut segment.bundles);
}
let mut diff = mapping.size as isize - mapping.offset as isize;
if diff < 0 {
- errors.push(Error::Eval(EvalError{
+ errors.push(Error{
line: 0,
message: format!(
"exceeding segment size for '{}'; expecting {} bytes and {} bytes have already been seen",
@@ -637,7 +637,7 @@ impl<'a> Assembler<'a> {
),
source: self.sources[0].clone(),
global: false,
- }));
+ });
}
if let Some(fill) = mapping.fill {
@@ -658,7 +658,7 @@ impl<'a> Assembler<'a> {
// Consume a node which contains a macro call by pushing its bundles now.
fn bundle_call(&mut self, node: &PNode) -> Result<(), Vec<Error>> {
// Get the macro we are trying to reproduce.
- let mcr = *self.macros.get(&node.value.value).ok_or(EvalError {
+ let mcr = *self.macros.get(&node.value.value).ok_or(Error {
line: node.value.line,
message: format!(
"could not find a macro with the name '{}'",
@@ -673,7 +673,7 @@ impl<'a> Assembler<'a> {
let given_args = node.args.as_ref().unwrap_or(&vec![]).len();
let macro_args = mcr.args.as_ref().unwrap_or(&vec![]).len();
if macro_args != given_args {
- return Err(vec![Error::Eval(EvalError {
+ return Err(Error {
line: node.value.line,
message: format!(
"wrong number of arguments for '{}': {} required but {} given",
@@ -681,7 +681,8 @@ impl<'a> Assembler<'a> {
),
source: self.source_for(node),
global: false,
- })]);
+ }
+ .into());
}
// If there are arguments defined by the macro, set their values now.
@@ -703,12 +704,13 @@ impl<'a> Assembler<'a> {
self.context
.set_variable(&margs.next().unwrap().value, &obj, true)
{
- return Err(vec![Error::Context(ContextError {
+ return Err(Error {
line: node.value.line,
message,
source: self.source_for(node),
global: false,
- })]);
+ }
+ .into());
}
}
}
@@ -721,19 +723,19 @@ impl<'a> Assembler<'a> {
// altogether. Just issue a warning on the latter case.
let inner = &mcr.right.as_ref().unwrap().args.as_ref().unwrap();
if inner.is_empty() {
- self.warnings.push(Error::Eval(EvalError {
+ self.warnings.push(Error {
line: node.value.line,
message: format!("trying to apply empty macro '{}'", node.value.value),
source: self.source_for(node),
global: false,
- }));
+ });
} else {
self.bundle(inner)?;
}
Ok(())
}
- fn push_bundle(&mut self, mut bundle: Bundle, node: &PNode) -> Result<(), EvalError> {
+ fn push_bundle(&mut self, mut bundle: Bundle, node: &PNode) -> Result<(), Error> {
let current = &mut self.mappings[self.current_mapping];
bundle.address = current.start as usize + current.offset;
current.offset += bundle.size as usize;
@@ -754,7 +756,7 @@ impl<'a> Assembler<'a> {
Ok(())
}
- fn evaluate_node(&mut self, node: &PNode) -> Result<Bundle, EvalError> {
+ fn evaluate_node(&mut self, node: &PNode) -> Result<Bundle, Error> {
match &node.node_type {
NodeType::Instruction => Ok(self.evaluate_instruction(node)?),
NodeType::Literal => Ok(self.evaluate_literal(node)?),
@@ -774,7 +776,7 @@ impl<'a> Assembler<'a> {
Ok(self.evaluate_anonymous_relative_reference(node)?)
} else if node.value.is_valid_identifier(true).is_err() {
// If this is not a valid identifier, just error out.
- Err(EvalError {
+ Err(Error {
message: "no prefix was given to operand".to_string(),
line: node.value.line,
source: self.source_for(node),
@@ -788,7 +790,7 @@ impl<'a> Assembler<'a> {
self.literal_mode = Some(LiteralMode::Hexadecimal);
Ok(v)
}
- Err(err) => Err(EvalError {
+ Err(err) => Err(Error {
message: format!(
"no prefix was given to operand and {} either",
err.message
@@ -801,7 +803,7 @@ impl<'a> Assembler<'a> {
}
}
},
- _ => Err(EvalError {
+ _ => Err(Error {
message: format!("unexpected '{}' expression type", node.node_type),
line: node.value.line,
source: self.source_for(node),
@@ -816,7 +818,7 @@ impl<'a> Assembler<'a> {
&mut self,
node: &PNode,
operation_type: &OperationType,
- ) -> Result<Bundle, EvalError> {
+ ) -> Result<Bundle, Error> {
let mut right = self.evaluate_node(node.right.as_ref().unwrap())?;
let rval = right.value();
@@ -858,7 +860,7 @@ impl<'a> Assembler<'a> {
}
OperationType::Div => {
if rval == 0 {
- return Err(EvalError {
+ return Err(Error {
line: node.value.line,
global: false,
source: self.source_for(node),
@@ -882,7 +884,7 @@ impl<'a> Assembler<'a> {
}
OperationType::Lshift => {
if rval > 16 {
- return Err(EvalError {
+ return Err(Error {
line: node.value.line,
global: false,
source: self.source_for(node),
@@ -895,7 +897,7 @@ impl<'a> Assembler<'a> {
}
OperationType::Rshift => {
if rval > 16 {
- return Err(EvalError {
+ return Err(Error {
line: node.value.line,
global: false,
source: self.source_for(node),
@@ -910,7 +912,7 @@ impl<'a> Assembler<'a> {
// Prevent overflows.
if res > i16::MAX.into() || res < i16::MIN.into() {
- return Err(EvalError {
+ return Err(Error {
line: node.value.line,
global: false,
source: self.source_for(node),
@@ -927,7 +929,7 @@ impl<'a> Assembler<'a> {
Ok(right)
}
- fn evaluate_anonymous_relative_reference(&mut self, node: &PNode) -> Result<Bundle, EvalError> {
+ fn evaluate_anonymous_relative_reference(&mut self, node: &PNode) -> Result<Bundle, Error> {
self.literal_mode = Some(LiteralMode::Plain);
match &self.stage {
@@ -947,7 +949,7 @@ impl<'a> Assembler<'a> {
&self.mappings,
) {
Ok(object) => Ok(object.bundle),
- Err(message) => Err(EvalError {
+ Err(message) => Err(Error {
line: node.value.line,
message,
source: self.source_for(node),
@@ -959,7 +961,7 @@ impl<'a> Assembler<'a> {
}
}
- fn evaluate_hexadecimal(&mut self, node: &PNode) -> Result<Bundle, EvalError> {
+ fn evaluate_hexadecimal(&mut self, node: &PNode) -> Result<Bundle, Error> {
let mut chars = node.value.value.chars();
let mut bytes = [0, 0, 0];
let size: u8;
@@ -989,7 +991,7 @@ impl<'a> Assembler<'a> {
}
_ => {
if self.evaluate_variable(node).is_ok() {
- return Err(EvalError {
+ return Err(Error {
message: format!(
"you cannot use variables like '{}' in hexadecimal literals",
node.value.value
@@ -999,7 +1001,7 @@ impl<'a> Assembler<'a> {
global: false,
});
}
- return Err(EvalError {
+ return Err(Error {
message: "expecting a number of 1 to 4 hexadecimal digits".to_string(),
line: node.value.line,
source: self.source_for(node),
@@ -1019,7 +1021,7 @@ impl<'a> Assembler<'a> {
})
}
- fn evaluate_binary(&mut self, node: &PNode) -> Result<Bundle, EvalError> {
+ fn evaluate_binary(&mut self, node: &PNode) -> Result<Bundle, Error> {
let string = node.value.value.as_str();
let mut value = 0;
@@ -1028,7 +1030,7 @@ impl<'a> Assembler<'a> {
// case, do not even try evaluating it.
match string.len().cmp(&8) {
Ordering::Less => {
- return Err(EvalError {
+ return Err(Error {
message: "missing binary digits to get a full byte".to_string(),
line: node.value.line,
source: self.source_for(node),
@@ -1036,7 +1038,7 @@ impl<'a> Assembler<'a> {
})
}
Ordering::Greater => {
- return Err(EvalError {
+ return Err(Error {
message: "too many binary digits for a single byte".to_string(),
line: node.value.line,
source: self.source_for(node),
@@ -1052,7 +1054,7 @@ impl<'a> Assembler<'a> {
value += val;
} else if c != '0' {
if self.evaluate_variable(node).is_ok() {
- return Err(EvalError {
+ return Err(Error {
message: format!(
"you cannot use variables like '{}' in binary literals",
string
@@ -1062,7 +1064,7 @@ impl<'a> Assembler<'a> {
global: false,
});
}
- return Err(EvalError {
+ return Err(Error {
message: format!("bad binary format for '{}'", string),
line: node.value.line,
global: false,
@@ -1082,10 +1084,10 @@ impl<'a> Assembler<'a> {
})
}
- fn evaluate_decimal(&mut self, node: &PNode) -> Result<Bundle, EvalError> {
+ fn evaluate_decimal(&mut self, node: &PNode) -> Result<Bundle, Error> {
let string = node.value.value.as_str();
if string.is_empty() {
- return Err(EvalError {
+ return Err(Error {
message: "empty decimal literal".to_string(),
line: node.value.line,
source: self.source_for(node),
@@ -1098,7 +1100,7 @@ impl<'a> Assembler<'a> {
for c in string.chars().rev() {
if shift > 100 {
- return Err(EvalError {
+ return Err(Error {
message: "decimal value is too big".to_string(),
line: node.value.line,
source: self.source_for(node),
@@ -1112,7 +1114,7 @@ impl<'a> Assembler<'a> {
}
None => {
if self.stage == Stage::Context {
- return Err(EvalError {
+ return Err(Error {
message: format!(
"variables must come from a constant expression, \
you cannot use other variables such as '{}' \
@@ -1127,7 +1129,7 @@ impl<'a> Assembler<'a> {
match self.evaluate_variable(node) {
Ok(v) => return Ok(v),
Err(err) => {
- return Err(EvalError {
+ return Err(Error {
message: format!(
"'{}' is not a decimal value and {} either",
c, err.message
@@ -1145,7 +1147,7 @@ impl<'a> Assembler<'a> {
shift *= 10;
}
if value > 255 {
- return Err(EvalError {
+ return Err(Error {
message: "decimal value is too big".to_string(),
line: node.value.line,
source: self.source_for(node),
@@ -1164,7 +1166,7 @@ impl<'a> Assembler<'a> {
})
}
- fn evaluate_literal(&mut self, node: &PNode) -> Result<Bundle, EvalError> {
+ fn evaluate_literal(&mut self, node: &PNode) -> Result<Bundle, Error> {
// The value of the literal is guaranteed to not be empty by the parser.
// If that's not the case, then it's a bug.
let val = node.value.value.as_str();
@@ -1178,7 +1180,7 @@ impl<'a> Assembler<'a> {
if val.starts_with('$') {
lm = Some(LiteralMode::Hexadecimal);
if left.node_type == NodeType::Literal {
- return Err(EvalError {
+ return Err(Error {
message: "literal cannot embed another literal".to_string(),
line: node.value.line,
source: self.source_for(node),
@@ -1188,7 +1190,7 @@ impl<'a> Assembler<'a> {
} else if val.starts_with('%') {
lm = Some(LiteralMode::Binary);
if left.node_type == NodeType::Literal {
- return Err(EvalError {
+ return Err(Error {
message: "literal cannot embed another literal".to_string(),
line: node.value.line,
source: self.source_for(node),
@@ -1207,13 +1209,13 @@ impl<'a> Assembler<'a> {
Ok(expr)
}
- fn char_to_hex(&mut self, oc: Option<char>, source: &PNode) -> Result<u8, EvalError> {
+ fn char_to_hex(&mut self, oc: Option<char>, source: &PNode) -> Result<u8, Error> {
match oc {
Some(c) => match c.to_digit(16) {
Some(c) => Ok(c as u8),
None => {
if (c.is_alphabetic() || c == '_') && self.evaluate_variable(source).is_ok() {
- return Err(EvalError {
+ return Err(Error {
message: format!(
"you cannot use variables like '{}' in hexadecimal literals",
source.value.value
@@ -1223,7 +1225,7 @@ impl<'a> Assembler<'a> {
global: false,
});
}
- Err(EvalError {
+ Err(Error {
message: "could not convert digit to hexadecimal".to_string(),
line: source.value.line,
source: self.source_for(source),
@@ -1231,7 +1233,7 @@ impl<'a> Assembler<'a> {
})
}
},
- None => Err(EvalError {
+ None => Err(Error {
message: "digit out of bounds".to_string(),
line: source.value.line,
source: self.source_for(source),
@@ -1240,7 +1242,7 @@ impl<'a> Assembler<'a> {
}
}
- fn evaluate_control_statement(&mut self, node: &PNode) -> Result<(), EvalError> {
+ fn evaluate_control_statement(&mut self, node: &PNode) -> Result<(), Error> {
// This might just be a statement that changes the context (e.g.
// ".macro", ".proc", etc.). In this case change the context and leave
// early.
@@ -1251,7 +1253,7 @@ impl<'a> Assembler<'a> {
}
}
Err(message) => {
- return Err(EvalError {
+ return Err(Error {
message,
line: node.value.line,
source: self.source_for(node),
@@ -1272,7 +1274,7 @@ impl<'a> Assembler<'a> {
self.incbin(node.args.as_ref().unwrap().first().unwrap())
}
NodeType::Control(ControlType::IncludeSource) => Ok(()),
- _ => Err(EvalError {
+ _ => Err(Error {
line: node.value.line,
message: format!(
"cannot handle control statement '{}' in this context",
@@ -1287,12 +1289,12 @@ impl<'a> Assembler<'a> {
// Push as many bundles as bytes are in the given file path. If there is any
// issue with reading the given file, or the parameter is given in a weird
// format, it will error out.
- fn incbin(&mut self, node: &PNode) -> Result<(), EvalError> {
+ fn incbin(&mut self, node: &PNode) -> Result<(), Error> {
let value = &node.value.value;
// Validate the path literal.
if value.len() < 3 || !value.starts_with('"') || !value.ends_with('"') {
- return Err(EvalError {
+ return Err(Error {
line: node.value.line,
message: format!(
"path has to be written inside of double quotes ('{}' given instead)",
@@ -1309,7 +1311,7 @@ impl<'a> Assembler<'a> {
match &self.sources.get(node.source) {
Some(source) => {
if let Err(e) = std::env::set_current_dir(&source.working_directory) {
- return Err(EvalError {
+ return Err(Error {
line: node.value.line,
message: format!("could not move to the directory of '{}': {}", value, e),
source: self.source_for(node),
@@ -1325,7 +1327,7 @@ impl<'a> Assembler<'a> {
let file = match File::open(path) {
Ok(f) => f,
Err(e) => {
- return Err(EvalError {
+ return Err(Error {
global: false,
line: node.value.line,
source: self.source_for(node),
@@ -1344,14 +1346,14 @@ impl<'a> Assembler<'a> {
// limit on where it's included, then it's going to show up at a
// later check.
if metadata.len() > 512 * 1024 {
- return Err(EvalError {
+ return Err(Error {
global: false,
line: node.value.line,
source: self.source_for(node),
message: format!("file '{}' is too big", path),
});
} else if metadata.len() == 0 {
- return Err(EvalError {
+ return Err(Error {
global: false,
line: node.value.line,
source: self.source_for(node),
@@ -1360,7 +1362,7 @@ impl<'a> Assembler<'a> {
}
}
Err(e) => {
- return Err(EvalError {
+ return Err(Error {
global: false,
line: node.value.line,
source: self.source_for(node),
@@ -1388,7 +1390,7 @@ impl<'a> Assembler<'a> {
let repeats = match first.parse::<usize>() {
Ok(n) => {
if n < 2 {
- return Err(EvalError {
+ return Err(Error {
global: false,
line: node.value.line,
source: self.source_for(node),
@@ -1396,7 +1398,7 @@ impl<'a> Assembler<'a> {
}
.into());
} else if n > 255 {
- return Err(EvalError {
+ return Err(Error {
global: false,
line: node.value.line,
source: self.source_for(node),
@@ -1407,7 +1409,7 @@ impl<'a> Assembler<'a> {
n
}
Err(_) => {
- return Err(EvalError {
+ return Err(Error {
global: false,
line: node.value.line,
message: format!(
@@ -1423,12 +1425,12 @@ impl<'a> Assembler<'a> {
// The code that is to be repeated.
let code = &node.right.as_ref().unwrap().args.as_ref().unwrap();
if code.is_empty() {
- self.warnings.push(Error::Eval(EvalError {
+ self.warnings.push(Error {
line: node.value.line,
message: "empty .repeat statement".to_string(),
source: self.source_for(node),
global: false,
- }));
+ });
return Ok(());
}
@@ -1447,7 +1449,7 @@ impl<'a> Assembler<'a> {
},
true,
) {
- return Err(EvalError {
+ return Err(Error {
line: node.value.line,
message: e,
source: self.source_for(node),
@@ -1464,11 +1466,11 @@ impl<'a> Assembler<'a> {
Ok(())
}
- fn evaluate_control_expression(&mut self, node: &PNode) -> Result<Bundle, EvalError> {
+ fn evaluate_control_expression(&mut self, node: &PNode) -> Result<Bundle, Error> {
match node.node_type {
NodeType::Control(ControlType::Hibyte) => self.evaluate_byte(node, true),
NodeType::Control(ControlType::Lobyte) => self.evaluate_byte(node, false),
- _ => Err(EvalError {
+ _ => Err(Error {
line: node.value.line,
message: format!(
"cannot handle control statement '{}' as an expression in this context",
@@ -1480,7 +1482,7 @@ impl<'a> Assembler<'a> {
}
}
- fn evaluate_byte(&mut self, node: &PNode, high: bool) -> Result<Bundle, EvalError> {
+ fn evaluate_byte(&mut self, node: &PNode, high: bool) -> Result<Bundle, Error> {
// The parser actually guarantees that the ".hibyte" and ".lobyte"
// functions have exactly one argument. Hence, if this is not the case,
// it's fine to let "unwrap" panic: it's a sign that's something is
@@ -1503,7 +1505,7 @@ impl<'a> Assembler<'a> {
Ok(bundle)
}
- fn push_evaluated_arguments(&mut self, node: &PNode, nbytes: u8) -> Result<(), EvalError> {
+ fn push_evaluated_arguments(&mut self, node: &PNode, nbytes: u8) -> Result<(), Error> {
match &node.args {
Some(args) => {
for arg in args {
@@ -1520,7 +1522,7 @@ impl<'a> Assembler<'a> {
if bundle.size != nbytes {
match nbytes {
1 => {
- return Err(EvalError {
+ return Err(Error {
line: arg.value.line,
message: "expecting an argument that fits into a byte"
.to_string(),
@@ -1540,7 +1542,7 @@ impl<'a> Assembler<'a> {
}
}
None => {
- return Err(EvalError {
+ return Err(Error {
line: node.value.line,
message: format!(
"expecting at least one argument for '{}'",
@@ -1555,7 +1557,7 @@ impl<'a> Assembler<'a> {
Ok(())
}
- fn switch_to_segment(&mut self, node: &PNode) -> Result<(), EvalError> {
+ fn switch_to_segment(&mut self, node: &PNode) -> Result<(), Error> {
// First of all, fetch the argument for the ".segment" statement and
// validate that it has some basic format. Note that the existence of
// exactly one argument is guaranteed by the parser and, thus,
@@ -1563,7 +1565,7 @@ impl<'a> Assembler<'a> {
let arg = &node.args.as_ref().unwrap().first().unwrap();
let val = &arg.value.value;
if val.len() < 3 || !val.starts_with('"') || !val.ends_with('"') {
- return Err(EvalError {
+ return Err(Error {
line: node.value.line,
message: format!(
"segment declaration has to be written inside of double quotes ('{}' given instead)",
@@ -1580,7 +1582,7 @@ impl<'a> Assembler<'a> {
.chars()
.any(|ch| !(ch.is_ascii_alphanumeric() || ch == '_' || ch == '-'))
{
- return Err(EvalError {
+ return Err(Error {
line: node.value.line,
message: "segment name contains bad characters".to_string(),
source: self.source_for(node),
@@ -1602,7 +1604,7 @@ impl<'a> Assembler<'a> {
}
}
if !found {
- return Err(EvalError {
+ return Err(Error {
line: node.value.line,
message: format!("unknown segment '{}'", name),
source: self.source_for(node),
@@ -1612,10 +1614,10 @@ impl<'a> Assembler<'a> {
Ok(())
}
- fn evaluate_variable(&mut self, node: &PNode) -> Result<Bundle, EvalError> {
+ fn evaluate_variable(&mut self, node: &PNode) -> Result<Bundle, Error> {
match self.context.get_variable(&node.value, &self.mappings) {
Ok(value) => Ok(value.bundle),
- Err(e) => Err(EvalError {
+ Err(e) => Err(Error {
message: e,
line: node.value.line,
source: self.source_for(node),
@@ -1624,7 +1626,7 @@ impl<'a> Assembler<'a> {
}
}
- fn evaluate_instruction(&mut self, node: &PNode) -> Result<Bundle, EvalError> {
+ fn evaluate_instruction(&mut self, node: &PNode) -> Result<Bundle, Error> {
self.literal_mode = None;
let (mode, mut bundle) = match &node.left {
@@ -1644,7 +1646,7 @@ impl<'a> Assembler<'a> {
bundle.bytes[0] = values.opcode.to_le_bytes()[0];
}
None => {
- return Err(EvalError {
+ return Err(Error {
message: format!(
"cannot use {} addressing mode for the instruction '{}'",
mode, mnemonic
@@ -1656,7 +1658,7 @@ impl<'a> Assembler<'a> {
}
},
None => {
- return Err(EvalError {
+ return Err(Error {
message: format!("unknown instruction {}", mnemonic),
line: node.value.line,
source: self.source_for(node),
@@ -1670,7 +1672,7 @@ impl<'a> Assembler<'a> {
fn get_addressing_mode_and_bytes(
&mut self,
node: &PNode,
- ) -> Result<(AddressingMode, Bundle), EvalError> {
+ ) -> Result<(AddressingMode, Bundle), Error> {
let left = &node.left;
if left.as_ref().unwrap().node_type == NodeType::Indirection {
@@ -1682,14 +1684,14 @@ impl<'a> Assembler<'a> {
}
}
- fn get_from_indirect(&mut self, node: &PNode) -> Result<(AddressingMode, Bundle), EvalError> {
+ fn get_from_indirect(&mut self, node: &PNode) -> Result<(AddressingMode, Bundle), Error> {
let left = node.left.as_ref().unwrap();
match node.right.as_ref() {
Some(right) => {
if right.value.value.trim().to_lowercase() == "y" {
if left.right.is_some() {
- return Err(EvalError {
+ return Err(Error {
message:
"it has to be either X addressing or Y addressing, not all at once"
.to_string(),
@@ -1701,7 +1703,7 @@ impl<'a> Assembler<'a> {
let val = self.evaluate_node(left.left.as_ref().unwrap())?;
if val.size != 1 {
- return Err(EvalError {
+ return Err(Error {
message: "address can only be one byte long on indirect Y addressing"
.to_string(),
line: node.value.line,
@@ -1711,7 +1713,7 @@ impl<'a> Assembler<'a> {
}
return Ok((AddressingMode::IndirectY, val));
}
- Err(EvalError {
+ Err(Error {
message: "only the Y index is allowed on indirect Y addressing".to_string(),
line: node.value.line,
source: self.source_for(node),
@@ -1723,7 +1725,7 @@ impl<'a> Assembler<'a> {
if right.value.value.trim().to_lowercase() == "x" {
let val = self.evaluate_node(left.left.as_ref().unwrap())?;
if val.size != 1 {
- return Err(EvalError {
+ return Err(Error {
message:
"address can only be one byte long on indirect X addressing"
.to_string(),
@@ -1734,7 +1736,7 @@ impl<'a> Assembler<'a> {
}
return Ok((AddressingMode::IndirectX, val));
}
- Err(EvalError {
+ Err(Error {
message: "only the X index is allowed on indirect X addressing".to_string(),
line: node.value.line,
source: self.source_for(node),
@@ -1744,7 +1746,7 @@ impl<'a> Assembler<'a> {
None => {
let val = self.evaluate_node(left.left.as_ref().unwrap())?;
if val.size != 2 {
- return Err(EvalError {
+ return Err(Error {
message: "expecting a full 16-bit address".to_string(),
line: node.value.line,
source: self.source_for(node),
@@ -1757,7 +1759,7 @@ impl<'a> Assembler<'a> {
}
}
- fn get_from_indexed(&mut self, node: &PNode) -> Result<(AddressingMode, Bundle), EvalError> {
+ fn get_from_indexed(&mut self, node: &PNode) -> Result<(AddressingMode, Bundle), Error> {
// Evaluate the left arm of the instruction.
let left = node.left.as_ref().unwrap();
let mut val = self.evaluate_node(left)?;
@@ -1766,7 +1768,7 @@ impl<'a> Assembler<'a> {
// instead of some bogus number.
if let Some(lm) = &self.literal_mode {
if *lm != LiteralMode::Hexadecimal {
- return Err(EvalError {
+ return Err(Error {
message: "indexed addressing only works with addresses".to_string(),
line: node.value.line,
source: self.source_for(node),
@@ -1829,7 +1831,7 @@ impl<'a> Assembler<'a> {
Ok((AddressingMode::IndexedY, val))
}
}
- _ => Err(EvalError {
+ _ => Err(Error {
message: "can only use X and Y as indices".to_string(),
line: node.value.line,
source: self.source_for(node),
@@ -1842,7 +1844,7 @@ impl<'a> Assembler<'a> {
&mut self,
base: &PNode,
left_arm: &PNode,
- ) -> Result<(AddressingMode, Bundle), EvalError> {
+ ) -> Result<(AddressingMode, Bundle), Error> {
if left_arm.value.value.to_lowercase().trim() == "a" {
return Ok((AddressingMode::Implied, Bundle::new(true)));
}
@@ -1868,7 +1870,7 @@ impl<'a> Assembler<'a> {
} else if val.size > 1 {
match base.value.value.as_str() {
"jmp" | "jsr" => Ok((AddressingMode::Absolute, val)),
- _ => Err(EvalError {
+ _ => Err(Error {
message: "immediate is too big".to_string(),
line: left_arm.value.line,
source: self.source_for(base),
@@ -1879,7 +1881,7 @@ impl<'a> Assembler<'a> {
Ok((AddressingMode::Immediate, val))
}
}
- _ => Err(EvalError {
+ _ => Err(Error {
message: "left arm of instruction is neither an address nor an immediate"
.to_string(),
line: left_arm.value.line,
@@ -1889,7 +1891,7 @@ impl<'a> Assembler<'a> {
}
}
- fn to_relative_address(&self, node: &PNode, bundle: &mut Bundle) -> Result<(), EvalError> {
+ fn to_relative_address(&self, node: &PNode, bundle: &mut Bundle) -> Result<(), Error> {
if !bundle.resolved {
return Ok(());
}
@@ -1900,7 +1902,7 @@ impl<'a> Assembler<'a> {
let byte = if target < next {
let diff = target as i16 - next as i16;
if diff < -128 {
- return Err(EvalError {
+ return Err(Error {
line: node.value.line,
message: "you cannot branch to this location: it's too far away".to_string(),
source: self.source_for(node),
@@ -1911,7 +1913,7 @@ impl<'a> Assembler<'a> {
} else {
let diff = target - next;
if diff > 127 {
- return Err(EvalError {
+ return Err(Error {
line: node.value.line,
message: "you cannot branch to this location: it's too far away".to_string(),
source: self.source_for(node),
diff --git a/lib/xixanta/src/errors.rs b/lib/xixanta/src/errors.rs
deleted file mode 100644
index 544b9b4..0000000
--- a/lib/xixanta/src/errors.rs
+++ /dev/null
@@ -1,143 +0,0 @@
-use crate::SourceInfo;
-use std::fmt;
-
-#[derive(Debug, Clone, PartialEq)]
-pub enum Error {
- Parse(ParseError),
- Context(ContextError),
- Eval(EvalError),
-}
-
-impl From<ParseError> for Vec<Error> {
- fn from(err: ParseError) -> Self {
- vec![Error::Parse(err)]
- }
-}
-
-impl From<ParseError> for Vec<ParseError> {
- fn from(err: ParseError) -> Self {
- vec![err]
- }
-}
-
-impl From<ContextError> for Vec<Error> {
- fn from(err: ContextError) -> Self {
- vec![Error::Context(err)]
- }
-}
-
-impl From<EvalError> for Vec<Error> {
- fn from(err: EvalError) -> Self {
- vec![Error::Eval(err)]
- }
-}
-
-impl fmt::Display for Error {
- fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
- match self {
- Error::Parse(parse_error) => write!(f, "{}", parse_error),
- Error::Context(context_error) => write!(f, "{}", context_error),
- Error::Eval(eval_error) => write!(f, "{}", eval_error),
- }
- }
-}
-
-#[derive(Debug, Clone, PartialEq)]
-pub struct ParseError {
- pub line: usize,
- pub message: String,
- pub source: SourceInfo,
-}
-
-impl std::error::Error for ParseError {}
-
-impl fmt::Display for ParseError {
- fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
- if self.source.name.is_empty() {
- write!(f, "{} (line {})", self.message, self.line + 1)
- } else {
- write!(
- f,
- "{} ({}: line {})",
- self.message,
- self.source.name,
- self.line + 1
- )
- }
- }
-}
-
-#[derive(Debug, Clone, PartialEq)]
-pub struct ContextError {
- pub line: usize,
- pub message: String,
- pub global: bool,
- pub source: SourceInfo,
-}
-
-impl std::error::Error for ContextError {}
-
-impl fmt::Display for ContextError {
- fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
- if self.global {
- if self.source.name.is_empty() {
- write!(f, "{}", self.message)
- } else {
- write!(f, "{} ({})", self.message, self.source.name)
- }
- } else if self.source.name.is_empty() {
- write!(f, "{} (line {})", self.message, self.line + 1)
- } else {
- write!(
- f,
- "{} ({}: line {})",
- self.message,
- self.source.name,
- self.line + 1
- )
- }
- }
-}
-
-#[derive(Debug, Clone, PartialEq)]
-pub struct EvalError {
- pub line: usize,
- pub message: String,
- pub global: bool,
- pub source: SourceInfo,
-}
-
-impl std::error::Error for EvalError {}
-
-impl From<ContextError> for EvalError {
- fn from(err: ContextError) -> Self {
- EvalError {
- line: err.line,
- message: err.message,
- global: false,
- source: SourceInfo::default(), // TODO
- }
- }
-}
-
-impl fmt::Display for EvalError {
- fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
- if self.global {
- if self.source.name.is_empty() {
- write!(f, "{}", self.message)
- } else {
- write!(f, "{} ({})", self.message, self.source.name)
- }
- } else if self.source.name.is_empty() {
- write!(f, "{} (line {})", self.message, self.line + 1)
- } else {
- write!(
- f,
- "{} ({}: line {})",
- self.message,
- self.source.name,
- self.line + 1
- )
- }
- }
-}
diff --git a/lib/xixanta/src/lib.rs b/lib/xixanta/src/lib.rs
index 95ead9c..2da8fd0 100644
--- a/lib/xixanta/src/lib.rs
+++ b/lib/xixanta/src/lib.rs
@@ -16,8 +16,58 @@ impl Default for SourceInfo {
}
}
+/// 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.
+#[derive(Debug, Clone, PartialEq)]
+pub struct Error {
+ /// Line number where the error was found relative to the `source`. You can
+ /// ignore this field if `global` is true.
+ pub line: usize,
+
+ /// Whether the error affects the whole file or not.
+ pub global: bool,
+
+ /// Information on the file where the error was found.
+ pub source: SourceInfo,
+
+ /// 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.
+ pub message: String,
+}
+
+impl From<Error> for Vec<Error> {
+ fn from(err: Error) -> Self {
+ vec![err]
+ }
+}
+
+impl std::error::Error for Error {}
+
+impl std::fmt::Display for Error {
+ fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result {
+ if self.global {
+ if self.source.name.is_empty() {
+ write!(f, "{}", self.message)
+ } else {
+ write!(f, "{} ({})", self.message, self.source.name)
+ }
+ } else if self.source.name.is_empty() {
+ write!(f, "{} (line {})", self.message, self.line + 1)
+ } else {
+ write!(
+ f,
+ "{} ({}: line {})",
+ self.message,
+ self.source.name,
+ self.line + 1
+ )
+ }
+ }
+}
+
pub mod assembler;
-pub mod errors;
pub mod node;
pub mod object;
pub mod opcodes;
diff --git a/lib/xixanta/src/parser.rs b/lib/xixanta/src/parser.rs
index fc53b08..6ea23f4 100644
--- a/lib/xixanta/src/parser.rs
+++ b/lib/xixanta/src/parser.rs
@@ -1,7 +1,6 @@
-use crate::errors::ParseError;
use crate::node::{ControlType, NodeBodyType, NodeType, OperationType, PNode, PString};
use crate::opcodes::{CONTROL_FUNCTIONS, INSTRUCTIONS};
-use crate::SourceInfo;
+use crate::{Error, SourceInfo};
use rand::distributions::{Alphanumeric, DistString};
use std::cmp::Ordering;
use std::io::{self, BufRead, Read};
@@ -51,9 +50,9 @@ pub struct Parser {
impl Parser {
/// Parse the input from the given `reader`. You can then access the results
- /// from the `nodes` field. Otherwise, a vector of ParseError's might be
+ /// from the `nodes` field. Otherwise, a vector of Error's might be
/// returned.
- pub fn parse(&mut self, reader: impl Read, source: SourceInfo) -> Result<(), Vec<ParseError>> {
+ pub fn parse(&mut self, reader: impl Read, source: SourceInfo) -> Result<(), Vec<Error>> {
let mut errors = Vec::new();
// Push the sources for the parsing session (note that this list might
@@ -117,7 +116,7 @@ impl Parser {
}
// Parse a single `line` and push the parsed nodes into `self.nodes`.
- fn parse_line(&mut self, line: &str) -> Result<(), Vec<ParseError>> {
+ fn parse_line(&mut self, line: &str) -> Result<(), Vec<Error>> {
self.column = 0;
self.offset = 0;
@@ -190,7 +189,7 @@ impl Parser {
// string which can be used to determine which kind of expression we are
// dealing with. Returns a PString representing this identifier on success,
// plus a hint on whether the identifier belongs to a label or not.
- fn parse_identifier(&mut self, line: &str) -> Result<(PString, NodeType), ParseError> {
+ fn parse_identifier(&mut self, line: &str) -> Result<(PString, NodeType), Error> {
let start = self.column;
let base_offset = self.offset;
let mut nt = NodeType::Value;
@@ -238,8 +237,9 @@ impl Parser {
// Hence, if there was something behind it,
// there is something wrong (e.g. "Bad:+").
if base_offset != self.offset {
- return Err(ParseError {
+ return Err(Error {
line: self.line,
+ global: false,
source: self.sources[self.current_source].clone(),
message: "you cannot have a relative label inside of an identifier".to_string(),
});
@@ -261,11 +261,12 @@ impl Parser {
// spaghetti code.
size += 1;
if size > 4 {
- return Err(ParseError {
- line: self.line,
- source: self.sources[self.current_source].clone(),
- message: "you can only jump to a maximum of four relative labels".to_string(),
- });
+ return Err(Error {
+ line: self.line,
+ global: false,
+ source: self.sources[self.current_source].clone(),
+ message: "you can only jump to a maximum of four relative labels".to_string(),
+ });
}
// You cannot mix '+'/'-' characters
@@ -273,8 +274,9 @@ impl Parser {
if cc != next {
let msg =
if next == '+' { "forward" } else { "backward" };
- return Err(ParseError {
+ return Err(Error {
line: self.line,
+ global: false,
source: self.sources[self.current_source].clone(),
message: format!(
"{} relative label can only have '{}' characters",
@@ -344,7 +346,7 @@ impl Parser {
// Parse the top-level statement as found on the given `line` which has a
// leading `id` positioned-string which may be an identifier.
- fn parse_statement(&mut self, line: &str, id: PString) -> Result<(), Vec<ParseError>> {
+ fn parse_statement(&mut self, line: &str, id: PString) -> Result<(), Vec<Error>> {
// There are only two top-level statements: instructions and
// assignments. Other kinds of expressions can also be used in the
// middle of assignments or instructions, and so they have to be handled
@@ -363,7 +365,7 @@ impl Parser {
}
// Parse the given `line` as an instruction being identified by `id`.
- fn parse_instruction(&mut self, line: &str, id: PString) -> Result<(), ParseError> {
+ fn parse_instruction(&mut self, line: &str, id: PString) -> Result<(), Error> {
let mut paren = 0;
// After the initial instruction identifier (e.g. `lda`), there might be
@@ -495,7 +497,7 @@ impl Parser {
// Parse the given `line` as an assignment statement which declares a
// variable at `id`.
- fn parse_assignment(&mut self, line: &str, id: PString) -> Result<(), ParseError> {
+ fn parse_assignment(&mut self, line: &str, id: PString) -> Result<(), Error> {
// Notice that `parse_identifier` pretty much swallows any kind of
// identifier without doing any sanity checks. Now it's the time to do
// so.
@@ -536,7 +538,7 @@ impl Parser {
// Parse statements which are neither an instruction nor an assignment. This
// includes stuff like control statements.
- fn parse_other(&mut self, line: &str, id: PString) -> Result<(), Vec<ParseError>> {
+ fn parse_other(&mut self, line: &str, id: PString) -> Result<(), Vec<Error>> {
// The main job of this function is to parse the expression and
// afterwards deal with corner cases. So, let's first just parse this.
let node = self.parse_expression_with_identifier(id, line)?;
@@ -550,8 +552,9 @@ impl Parser {
) {
// Validate that it's a top layer statement.
if self.nodes.len() > 1 {
- return Err(ParseError {
+ return Err(Error {
line: node.value.line,
+ global: false,
message: ".include statement cannot be inside of a code block".to_string(),
source: self.sources[self.current_source].clone(),
}
@@ -620,7 +623,7 @@ impl Parser {
// Consume the given `node` by assuming it's an `.include` statement. This
// will in turn produce a new parsing session for the given file if possible
// and push the parsed nodes from it to our current list.
- fn include_source(&mut self, node: &PNode) -> Result<(), Vec<ParseError>> {
+ fn include_source(&mut self, node: &PNode) -> Result<(), Vec<Error>> {
// First of all, parse the string as it was given and join it with our
// working directory. This way we construct the absolute path for the
// given file.
@@ -633,8 +636,9 @@ impl Parser {
// Validate that this is really a path that points to a file.
let path = std::path::Path::new(&abs_file);
if !path.is_file() {
- return Err(ParseError {
+ return Err(Error {
line: node.value.line,
+ global: false,
source: current_source.clone(),
message: "expecting a file ({})".to_string(),
}
@@ -648,8 +652,9 @@ impl Parser {
let file = match std::fs::File::open(path) {
Ok(f) => f,
Err(e) => {
- return Err(ParseError {
+ return Err(Error {
line: node.value.line,
+ global: false,
source: current_source.clone(),
message: format!("could not open source file: {}", e),
}
@@ -657,8 +662,9 @@ impl Parser {
}
};
let Some(parent) = path.parent() else {
- return Err(ParseError {
+ return Err(Error {
line: node.value.line,
+ global: false,
source: current_source.clone(),
message: "could not find out the parent directory for file".to_string(),
}
@@ -691,13 +697,14 @@ impl Parser {
// Returns a string containing the path being referenced in the given node.
// The path is assumed to be available directly inside of the value of the
// PNode.
- fn fetch_path_from<'a>(&mut self, node: &'a PNode) -> Result<&'a str, ParseError> {
+ fn fetch_path_from<'a>(&mut self, node: &'a PNode) -> Result<&'a str, Error> {
let value = &node.value.value;
// Validate the path literal.
if value.len() < 3 || !value.starts_with('"') || !value.ends_with('"') {
- return Err(ParseError {
+ return Err(Error {
line: node.value.line,
+ global: false,
source: self.sources[self.current_source].clone(),
message: format!(
"path has to be written inside of double quotes ('{}' given instead)",
@@ -712,7 +719,7 @@ impl Parser {
// Parse any possible arguments for the given `line`. The offset is supposed
// to be at a point where arguments might appear, either between parens or
// not.
- fn parse_arguments(&mut self, line: &str) -> Result<Vec<PNode>, ParseError> {
+ fn parse_arguments(&mut self, line: &str) -> Result<Vec<PNode>, Error> {
// Skip any possible whitespace before the optional opening paren.
self.skip_whitespace(line);
@@ -782,7 +789,7 @@ impl Parser {
// Parse the left arm from an instruction and leave `offset` and `column`
// past the end of it.
- fn parse_left_arm(&mut self, line: &str) -> Result<PNode, ParseError> {
+ fn parse_left_arm(&mut self, line: &str) -> Result<PNode, Error> {
let start_column = self.column;
// We track the start value of the offset and we will keep track of the
@@ -816,7 +823,7 @@ impl Parser {
// might be opening/closing parenthesis which need to be balanced. On
// success it returns the index from within the given `line`, and a boolean
// which is set to true/false on whether a comma was found.
- fn find_left_end(&self, line: &str) -> Result<(usize, bool), ParseError> {
+ fn find_left_end(&self, line: &str) -> Result<(usize, bool), Error> {
let mut idx = self.offset;
let mut parens = 0;
let mut comma = false;
@@ -849,7 +856,7 @@ impl Parser {
// Finds the matching parenthesis which closes the parenthesis that was just
// opened. The `init` index point to the next character after the opening
// paren from the given `line`.
- fn find_matching_paren(&self, line: &str, init: usize) -> Result<usize, ParseError> {
+ fn find_matching_paren(&self, line: &str, init: usize) -> Result<usize, Error> {
let mut idx = init;
let mut parens = 1;
@@ -880,7 +887,7 @@ impl Parser {
// `line` (e.g. the line might not be a full line but rather a limited range
// and the offset has been set accordingly). Returns a new node for the
// expression at hand.
- fn parse_expression(&mut self, line: &str) -> Result<PNode, ParseError> {
+ fn parse_expression(&mut self, line: &str) -> Result<PNode, Error> {
// Cases where fetching an "identifier" is really not needed.
let first = line.chars().next().unwrap_or_default();
if first == '(' {
@@ -956,10 +963,7 @@ impl Parser {
// there's no operator, then None is returned for the first element of the
// Ok tuple. Moreover, the second item on the tuple contains the amount of
// characters that made up the operator.
- fn get_operation_from_line(
- &mut self,
- line: &str,
- ) -> Result<(Option<NodeType>, usize), ParseError> {
+ fn get_operation_from_line(&mut self, line: &str) -> Result<(Option<NodeType>, usize), Error> {
let first = line.chars().nth(0).unwrap_or_default();
let second = line.chars().nth(1).unwrap_or_default();
@@ -991,7 +995,7 @@ impl Parser {
&mut self,
id: PString,
line: &str,
- ) -> Result<PNode, ParseError> {
+ ) -> Result<PNode, Error> {
// Reaching this condition is usually a bad sign, but there is so many
// ways in which it could go wrong, that an `assert!` wouldn't be fair
// either. Hence, just error out.
@@ -1011,8 +1015,9 @@ impl Parser {
// to prevent crashes.
if let Some(next) = line.chars().nth(1) {
if next == '#' || (start != '#' && (next == '$' || next == '%')) {
- return Err(ParseError {
+ return Err(Error {
line: id.line,
+ global: false,
source: self.sources[self.current_source].clone(),
message: "bad literal syntax".to_string(),
});
@@ -1106,7 +1111,7 @@ impl Parser {
// Returns a NodeType::Control node with whatever could be parsed
// considering the given `id` and rest of the `line`.
- fn parse_control(&mut self, id: PString, line: &str) -> Result<PNode, ParseError> {
+ fn parse_control(&mut self, id: PString, line: &str) -> Result<PNode, Error> {
let mut left = None;
// Ensure that this is a function that we know of. In the past this was
@@ -1168,7 +1173,7 @@ impl Parser {
// Returns a NodeType::Literal node with whatever could be parsed
// considering the given `id` and rest of the `line`.
- fn parse_literal(&mut self, id: PString, line: &str) -> Result<PNode, ParseError> {
+ fn parse_literal(&mut self, id: PString, line: &str) -> Result<PNode, Error> {
// Force the column to point to the literal character just in case
// of expressions like '#.hibyte'. Then skip whitespaces for super
// ugly statements such as '# 20'. This is ugly but we should permit
@@ -1184,8 +1189,9 @@ impl Parser {
let inner = line.get(self.offset..).unwrap_or("");
if let Some(c) = inner.chars().nth(0) {
if c.is_whitespace() {
- return Err(ParseError {
+ return Err(Error {
line: id.line,
+ global: false,
source: self.sources[self.current_source].clone(),
message: "numeric literals cannot have white spaces".to_string(),
});
@@ -1209,7 +1215,7 @@ impl Parser {
// Returns a NodeType::Literal node with the given `id` parsed as a
// character literal. This literal will have on the left node a value which
// is the character transformed into a decimal value.
- fn parse_char(&mut self, id: PString) -> Result<PNode, ParseError> {
+ fn parse_char(&mut self, id: PString) -> Result<PNode, Error> {
if id.value.len() != 3 {
return Err(self.parser_error("bad character literal"));
}
@@ -1249,10 +1255,11 @@ impl Parser {
})
}
- // Returns a new ParseError by using the current line.
- fn parser_error(&self, msg: &str) -> ParseError {
- ParseError {
+ // Returns a new Error by using the current line.
+ fn parser_error(&self, msg: &str) -> Error {
+ Error {
message: String::from(msg),
+ global: false,
source: self.sources[self.current_source].clone(),
line: self.line,
}