aboutsummaryrefslogtreecommitdiff
path: root/lib/xixanta/src/assembler.rs
diff options
context:
space:
mode:
Diffstat (limited to 'lib/xixanta/src/assembler.rs')
-rw-r--r--lib/xixanta/src/assembler.rs61
1 files changed, 40 insertions, 21 deletions
diff --git a/lib/xixanta/src/assembler.rs b/lib/xixanta/src/assembler.rs
index d24a555..42842a7 100644
--- a/lib/xixanta/src/assembler.rs
+++ b/lib/xixanta/src/assembler.rs
@@ -1,4 +1,4 @@
-use crate::errors::{ContextError, Error, EvalError};
+use crate::errors::{ContextError, ContextErrorReason, Error, EvalError};
use crate::mapping::Mapping;
use crate::node::{ControlType, NodeType, OperationType, PNode, PString};
use crate::object::{Bundle, Context, Object, ObjectType};
@@ -44,7 +44,7 @@ pub enum Stage {
}
#[derive(Clone, Debug)]
-pub struct Macro {
+pub struct CodeBlock {
nodes: Range<usize>,
args: Vec<PString>,
}
@@ -63,7 +63,7 @@ pub struct Assembler {
context: Context,
literal_mode: Option<LiteralMode>,
stage: Stage,
- macros: HashMap<String, Macro>,
+ macros: HashMap<String, CodeBlock>,
can_bundle: bool,
mappings: Vec<Mapping>,
current_mapping: usize,
@@ -207,21 +207,29 @@ impl Assembler {
}
}
NodeType::Control(control_type) => {
+ if !self.context.is_global() && control_type.must_be_global() {
+ errors.push(Error::Context(ContextError {
+ message: format!("{} must be on the global scope", control_type),
+ line: node.value.line,
+ global: false,
+ reason: ContextErrorReason::BadScope,
+ }));
+ continue;
+ }
+
// TODO: prevent nesting of control statements depending on
// a definition (e.g. .macro's cannot be nested inside of
// another control statement, but .if yes).
match control_type {
ControlType::StartMacro => {
- // TODO: macros are only on the global scope.
- //
// TODO: boy this is ugly. In fact, this stupid shit if
// current_macro might not be relevant anymore.
current_macro = Some(&node.left.as_ref().unwrap().value);
// TODO: watch out for weird shit on the name of arguments.
self.macros
.entry(node.left.as_ref().unwrap().value.value.clone())
- .or_insert(Macro {
+ .or_insert(CodeBlock {
nodes: Range {
start: idx + 1,
end: idx + 1,
@@ -1239,19 +1247,6 @@ impl Assembler {
});
}
- // You cannot change the segment if you are not in the global context.
- if !self.context.is_global() {
- return Err(EvalError {
- line: node.value.line,
- message: format!(
- "cannot switch to segment '{}' if we are still inside of a scope ('{}')",
- name,
- self.context.name()
- ),
- global: false,
- });
- }
-
// Find the segment being referenced and update the
// `self.current_segment` accordingly.
let mut found = false;
@@ -2917,6 +2912,31 @@ WRITE_PPU_DATA $20B9, $04
assert_eq!(res[6].bytes[2], 0x20);
}
+ #[test]
+ fn start_macro_inside_of_scope() {
+ let mut asm = Assembler::new(empty());
+ asm.mappings[0].segments[0].bundles = minimal_header();
+ asm.mappings[0].offset = 6;
+ asm.current_mapping = 1;
+ let res = &asm
+ .assemble(
+ std::env::current_dir().unwrap().to_path_buf(),
+ r#".scope Some
+.macro WRITE_PPU_DATA
+ bit $2002
+.endmacro
+.endscope
+"#
+ .as_bytes(),
+ )
+ .unwrap_err();
+
+ assert_eq!(
+ res.first().unwrap().to_string(),
+ ".macro must be on the global scope (line 2)"
+ );
+ }
+
// Segments
#[test]
@@ -3153,8 +3173,7 @@ code:
assert_eq!(
res.first().unwrap().to_string(),
- "cannot switch to segment 'CODE' \
- if we are still inside of a scope ('Vars') (line 3)"
+ ".segment must be on the global scope (line 3)"
);
}