1#![allow(clippy::cast_lossless)]
2
3use core::str;
4use std::borrow::Cow;
5use std::cell::RefCell;
6use std::fmt::Debug;
7use std::ops::DerefMut;
8use std::rc::Rc;
9use std::str::FromStr;
10use std::sync::{Arc, LazyLock};
11
12use choice_nocase::choice_nocase;
13use cpclib_common::itertools::Itertools;
14#[cfg(all(not(target_arch = "wasm32"), feature = "rayon"))]
15use cpclib_common::rayon::iter::{IntoParallelRefIterator, ParallelIterator};
16use cpclib_common::smallvec::SmallVec;
17use cpclib_common::smol_str::SmolStr;
18use cpclib_common::winnow::ascii::{Caseless, alpha1, alphanumeric1, line_ending, space0};
19use cpclib_common::winnow::combinator::{
20 alt, cut_err, delimited, eof, not, opt, peek, preceded, repeat, separated, terminated
21};
22#[allow(deprecated)]
23use cpclib_common::winnow::error::ErrorKind;
24use cpclib_common::winnow::error::{AddContext, ErrMode, ParserError, StrContext};
25use cpclib_common::winnow::stream::{
26 Accumulate, AsBStr, AsBytes, AsChar, Checkpoint, LocatingSlice, Offset, Stream, UpdateSlice
27};
28use cpclib_common::winnow::token::{none_of, one_of, take, take_till, take_until, take_while};
29use cpclib_common::winnow::{self, BStr, ModalResult, Parser, Stateful};
30use cpclib_sna::parse::parse_flag;
31use cpclib_sna::{
32 FlagValue, RemuBreakPointAccessMode, RemuBreakPointRunMode, RemuBreakPointType, SnapshotVersion
33};
34use cpclib_tokens::ListingElement;
35use obtained::LocatedTokenInner;
37
38use super::context::*;
39use super::obtained::*;
40use super::orgams::*;
41use super::*;
42use crate::parser::parser::winnow::combinator::repeat_till;
43use crate::preamble::*;
44
45#[derive(Clone, Debug, PartialEq, Eq)]
48pub enum Z80ParserErrorKind {
49 Context(StrContext),
51 Char(char),
53 Winnow,
55 Inner {
57 listing: std::sync::Arc<LocatedListing>,
58 error: Box<Z80ParserError>
59 }
60}
61
62#[derive(Debug, Clone, PartialEq, Eq)]
63pub struct Z80ParserError(Vec<(InnerZ80Span, Z80ParserErrorKind)>);
64
65impl Z80ParserError {
66 pub fn errors(&self) -> Vec<(&InnerZ80Span, &Z80ParserErrorKind)> {
67 let mut res = Vec::new();
68
69 for e in self.0.iter() {
70 if let Z80ParserErrorKind::Inner { listing: _, error } = &e.1 {
71 res.extend(error.errors())
72 }
73 else {
74 res.push((&e.0, &e.1));
75 }
76 }
77 res
78 }
79}
80
81impl From<char> for Z80ParserErrorKind {
82 fn from(other: char) -> Self {
83 Self::Char(other)
84 }
85}
86
87impl Z80ParserError {
88 pub fn from_inner_error(
89 input: &InnerZ80Span,
90 listing: std::sync::Arc<LocatedListing>,
91 error: Box<Z80ParserError>
92 ) -> Self {
93 Self(vec![(*input, Z80ParserErrorKind::Inner { listing, error })])
94 }
95}
96
97impl ParserError<InnerZ80Span> for Z80ParserError {
98 #[allow(deprecated)]
99 fn from_error_kind(input: &InnerZ80Span, kind: ErrorKind) -> Self {
100 Self(vec![(*input, Z80ParserErrorKind::Winnow)])
101 }
102
103 #[allow(deprecated)]
104 fn append(
105 mut self,
106 input: &InnerZ80Span,
107 token_start: &<InnerZ80Span as Stream>::Checkpoint,
108 kind: ErrorKind
109 ) -> Self {
110 self.0.push((*input, Z80ParserErrorKind::Winnow));
111 self
112 }
113
114 fn assert(input: &InnerZ80Span, _message: &'static str) -> Self {
115 #[cfg(debug_assertions)]
116 panic!("assert `{}` failed at {:#?}", _message, input);
117 #[cfg(not(debug_assertions))]
118 #[allow(deprecated)]
119 Self::from_error_kind(input, ErrorKind::Assert)
120 }
121
122 fn or(self, other: Self) -> Self {
123 other
124 }
125}
126
127impl AddContext<InnerZ80Span> for Z80ParserError {
128 fn add_context(
129 mut self,
130 input: &InnerZ80Span,
131 start: &<InnerZ80Span as Stream>::Checkpoint,
132 ctx: &'static str
133 ) -> Self {
134 self.0
135 .push((*input, Z80ParserErrorKind::Context(StrContext::Label(ctx))));
136 self
137 }
138}
139
140impl AddContext<InnerZ80Span, StrContext> for Z80ParserError {
141 fn add_context(
142 mut self,
143 input: &InnerZ80Span,
144 start: &<InnerZ80Span as Stream>::Checkpoint,
145 ctx: StrContext
146 ) -> Self {
147 self.0.push((*input, Z80ParserErrorKind::Context(ctx)));
148 self
149 }
150}
151
152pub mod error_code {
154 pub const ASSERT_MUST_BE_FOLLOWED_BY_AN_EXPRESSION: u32 = 128;
156 pub const INVALID_ARGUMENT: u32 = 129;
158 pub const UNABLE_TO_PARSE_INNER_CONTENT: u32 = 130;
160}
161
162trait AccumulateSeveral<O>: Accumulate<O> {
163 fn accumulate_several(&mut self, items: &mut Vec<O>);
164}
165
166impl<O> AccumulateSeveral<O> for Vec<O> {
167 fn accumulate_several(&mut self, items: &mut Vec<O>) {
168 self.append(items);
169 }
170}
171
172const REGISTERS: &[&[u8]] = &[b"AF", b"HL", b"DE", b"BC", b"IX", b"IY", b"IXL", b"IXH"];
174
175const INSTRUCTIONS: &[&[u8]] = &[
176 b"ADC", b"ADD", b"AND", b"BIT", b"CALL", b"CCF", b"CP", b"CPD", b"CPDR", b"CPI", b"CPIR",
177 b"CPL", b"DAA", b"DEC", b"DI", b"DJNZ", b"EI", b"EX", b"EXX", b"HALT", b"IM", b"IN", b"INC",
178 b"IND", b"INDR", b"INI", b"INIR", b"JP", b"JR", b"LD", b"LDD", b"LDDR", b"LDI", b"LDIR",
179 b"NEG", b"NOP", b"OR", b"OTDR", b"OTIR", b"OUT", b"OUTD", b"OUTI", b"POP", b"PUSH", b"RES",
180 b"RET", b"RETI", b"RETN", b"RL", b"RLA", b"RLC", b"RLCA", b"RLD", b"RR", b"RRA", b"RRC",
181 b"RRCA", b"RRD", b"RST", b"SBC", b"SCF", b"SET", b"SLA", b"SRA", b"SRL", b"SUB", b"XOR",
182 b"SL1", b"SLL", b"EXA", b"EXD"
183];
184
185const STAND_ALONE_DIRECTIVE: &[&[u8]] = &[
186 b"#",
187 b"ABYTE",
188 b"ALIGN",
189 b"ASMCONTROL",
190 b"ASSERT",
191 b"BANK",
192 b"BANKSET",
193 b"BINCLUDE",
194 b"BREAK",
195 b"BREAKPOINT",
196 b"BUILDCPR",
197 b"BUILDSNA",
198 b"BYTE",
199 b"CASE",
200 b"CHARSET",
201 b"DB",
202 b"DEFAULT",
203 b"DEFB",
204 b"DEFM",
205 b"DEFS",
206 b"DEFW",
207 b"DEFSECTION",
208 b"DM",
209 b"DS",
210 b"FOR",
211 b"DW",
212 b"ELSE",
213 b"ELSEIF",
214 b"ELSEIFDEF",
215 b"ELSEIFEXIST",
216 b"ELSEIFNDEF",
217 b"ELSEIFNOT",
218 b"ELSEIFUSED",
219 b"ENT",
221 b"EQU",
222 b"EXPORT",
223 b"FAIL",
224 b"INCBIN",
225 b"INCLUDE",
226 b"INCLZ4",
227 b"INCEXO",
228 b"INCL48",
229 b"INCL49",
230 b"INCLZSA1",
231 b"INCLZSA2",
232 b"INCAPU",
233 b"INCZX0",
234 b"INCSHRINKLER",
235 b"INCUPKR",
236 b"LET",
237 b"LIMIT",
238 b"LIST",
239 b"LZEXO",
240 b"LZSA1",
241 b"LZSA2",
242 b"LZUPKR",
243 b"MAP",
244 b"MODULE",
245 b"NOEXPORT",
246 b"NOLIST",
247 b"NOP",
248 b"ORG",
249 b"PAUSE",
250 b"PRINT",
251 b"PROTECT",
252 b"RANGE",
253 b"READ",
254 b"REND",
255 b"REPEAT",
256 b"REP",
257 b"REPT",
258 b"RORG",
259 b"RETURN",
260 b"RUN",
261 b"SAVE",
262 b"SECTION",
263 b"SNAINIT",
264 b"SNAPINIT",
265 b"SNASET",
266 b"STARTINGINDEX",
267 b"STR",
268 b"TEXT",
269 b"TICKER",
270 b"UNDEF",
271 b"UNTIL",
272 b"WAITNOPS",
273 b"WORD",
274 b"WRITE DIRECT",
275 b"WRITE"
276];
277
278const START_DIRECTIVE: &[&[u8]] = &[
279 b"ASMCONTROLENV",
280 b"CONFINED",
281 b"FUNCTION",
282 b"FOR",
283 b"IF",
284 b"IFDEF",
285 b"IFEXIST",
286 b"IFNDEF",
287 b"IFNOT",
288 b"IFUSED",
289 b"IFNUSED",
290 b"ITER",
291 b"ITERATE",
292 b"LZ4",
293 b"LZ48",
294 b"LZ49",
295 b"LZ48",
296 b"LZAPU",
297 b"LZX0",
298 b"LZEXO",
299 b"LZ4",
300 b"LZX7",
301 b"LZSHRINKLER",
302 b"LOCOMOTIVE",
303 b"MACRO",
304 b"MODULE",
305 b"PHASE",
306 b"REPEAT",
307 b"REPT",
308 b"STRUCT",
309 b"SWITCH",
310 b"WHILE"
311];
312
313const END_DIRECTIVE: &[&[u8]] = &[
315 b"END", b"ENDASMCONTROLENV",
317 b"ENDA",
318 b"BREAK",
319 b"CASE",
320 b"CEND",
321 b"DEFAULT",
322 b"DEPHASE",
323 b"ELSE",
324 b"ELSEIF",
325 b"ELSEIFDEF",
326 b"ELSEIFEXIST",
327 b"ELSEIFNDEF",
328 b"ELSEIFNOT",
329 b"ELSEIFUSED",
330 b"ENDC",
331 b"ENDCONFINED",
332 b"ENDF",
333 b"ENDFOR",
334 b"ENDFUNCTION",
335 b"ENDI",
336 b"ENDIF", b"ENDITER",
338 b"ENDITERATE",
339 b"ENDM",
340 b"ENDMACRO",
341 b"ENDMODULE",
342 b"ENDR",
343 b"ENDREP", b"ENDREPEAT",
345 b"ENDS",
346 b"ENDSWITCH",
347 b"ENDW",
348 b"FEND",
349 b"IEND",
350 b"LZCLOSE",
351 b"REND", b"UNTIL",
353 b"WEND"
354];
355
356static _DOTTED_END_DIRECTIVE: LazyLock<Vec<String>> = LazyLock::new(|| {
357 END_DIRECTIVE
358 .iter()
359 .map(|d| format!(".{}", { unsafe { std::str::from_utf8_unchecked(d) } }))
360 .collect_vec()
361});
362
363static _DOTTED_STAND_ALONE_DIRECTIVE: LazyLock<Vec<String>> = LazyLock::new(|| {
365 STAND_ALONE_DIRECTIVE
366 .iter()
367 .map(|d| format!(".{}", unsafe { std::str::from_utf8_unchecked(d) }))
368 .collect_vec()
369});
370
371static _DOTTED_START_DIRECTIVE: LazyLock<Vec<String>> = LazyLock::new(|| {
372 START_DIRECTIVE
373 .iter()
374 .map(|d| format!(".{}", { unsafe { std::str::from_utf8_unchecked(d) } }))
375 .collect_vec()
376});
377
378static DOTTED_STAND_ALONE_DIRECTIVE: LazyLock<Vec<&'static [u8]>> = LazyLock::new(|| {
379 _DOTTED_STAND_ALONE_DIRECTIVE
380 .iter()
381 .map(String::as_str)
382 .map(str::as_bytes)
383 .collect_vec()
384});
385static DOTTED_START_DIRECTIVE: LazyLock<Vec<&'static [u8]>> = LazyLock::new(|| {
386 _DOTTED_START_DIRECTIVE
387 .iter()
388 .map(String::as_str)
389 .map(str::as_bytes)
390 .collect_vec()
391});
392static DOTTED_END_DIRECTIVE: LazyLock<Vec<&'static [u8]>> = LazyLock::new(|| {
393 _DOTTED_END_DIRECTIVE
394 .iter()
395 .map(String::as_str)
396 .map(str::as_bytes)
397 .collect_vec()
398});
399
400static DOTTED_IMPOSSIBLE_NAMES: LazyLock<Vec<&'static [u8]>> = LazyLock::new(|| {
401 REGISTERS
402 .iter()
403 .chain(INSTRUCTIONS)
404 .chain(DOTTED_STAND_ALONE_DIRECTIVE.iter())
405 .chain(DOTTED_START_DIRECTIVE.iter())
406 .chain(DOTTED_END_DIRECTIVE.iter())
407 .cloned()
408 .collect()
409});
410
411static IMPOSSIBLE_NAMES: LazyLock<Vec<&'static [u8]>> = LazyLock::new(|| {
412 REGISTERS
413 .iter()
414 .chain(INSTRUCTIONS)
415 .chain(STAND_ALONE_DIRECTIVE)
416 .chain(START_DIRECTIVE)
417 .chain(END_DIRECTIVE)
418 .cloned()
419 .collect()
420});
421
422static IMPOSSIBLE_NAMES_ORGAMS: LazyLock<Vec<&'static [u8]>> = LazyLock::new(|| {
423 REGISTERS
424 .iter()
425 .chain(INSTRUCTIONS)
426 .chain(STAND_ALONE_DIRECTIVE_ORGAMS)
427 .chain(START_DIRECTIVE_ORGAMS)
428 .chain(END_DIRECTIVE_ORGAMS)
429 .cloned()
430 .collect()
431});
432
433static MIN_MAX_LABEL_SIZE: LazyLock<(usize, usize)> = LazyLock::new(|| {
434 DOTTED_IMPOSSIBLE_NAMES
435 .iter()
436 .map(|l| l.len())
437 .minmax()
438 .into_option()
439 .unwrap()
440});
441static DOTTED_MIN_MAX_LABEL_SIZE: LazyLock<(usize, usize)> = LazyLock::new(|| {
442 DOTTED_IMPOSSIBLE_NAMES
443 .iter()
444 .map(|l| l.len())
445 .minmax()
446 .into_option()
447 .unwrap()
448});
449
450pub fn parse_z80_with_context_builder<S: Into<String>>(
453 str: S,
454 builder: ParserContextBuilder
455) -> Result<LocatedListing, AssemblerError> {
456 LocatedListing::new_complete_source(str, builder)
457 .map_err(|l| AssemblerError::LocatedListingError(std::sync::Arc::new(l)))
458}
459
460#[cfg_attr(not(target_arch = "wasm32"), inline)]
461#[cfg_attr(target_arch = "wasm32", inline(never))]
462pub(crate) fn build_span(
463 start_eof_offset: usize,
464 start: &<InnerZ80Span as Stream>::Checkpoint,
465 mut input: InnerZ80Span
466) -> InnerZ80Span {
467 let span_len: usize = start_eof_offset - input.eof_offset();
468 input.reset(start);
469 let bytes: &'static [u8] = unsafe { std::mem::transmute(&input.as_bstr()[..span_len]) }; input.update_slice(bytes)
471}
472
473#[cfg_attr(not(target_arch = "wasm32"), inline)]
480#[cfg_attr(target_arch = "wasm32", inline(never))]
481pub fn parse_z80<S: Into<String>>(code: S) -> Result<LocatedListing, AssemblerError> {
482 parse_z80_str(code)
483}
484
485#[cfg_attr(not(target_arch = "wasm32"), inline)]
487#[cfg_attr(target_arch = "wasm32", inline(never))]
488pub fn parse_z80_str<S: Into<String>>(code: S) -> Result<LocatedListing, AssemblerError> {
489 parse_z80_with_context_builder(code, ParserContextBuilder::default())
490}
491
492#[cfg_attr(not(target_arch = "wasm32"), inline)]
493#[cfg_attr(target_arch = "wasm32", inline(never))]
494pub fn my_many0_nocollect<O, E, F>(mut f: F) -> impl FnMut(&mut InnerZ80Span) -> ModalResult<(), E>
495where
496 F: Parser<InnerZ80Span, O, E>,
497 E: ParserError<InnerZ80Span>
498{
499 #[cfg_attr(not(target_arch = "wasm32"), inline)]
500 #[cfg_attr(target_arch = "wasm32", inline(never))]
501 move |i: &mut InnerZ80Span| {
502 loop {
503 let start = i.checkpoint();
504 let len = i.eof_offset();
505
506 match f.parse_next(i) {
507 Err(ErrMode::Backtrack(_)) => {
508 i.reset(&start);
509 return Ok(());
510 },
511 Err(e) => return Err(e),
512 Ok(_) => {
513 if len == i.eof_offset() {
514 return Ok(()); }
516 }
517 }
518 }
519 }
520}
521
522#[cfg_attr(not(target_arch = "wasm32"), inline)]
523#[cfg_attr(target_arch = "wasm32", inline(never))]
524pub fn my_many_till_nocollect<O, P, E, F, G>(
525 mut f: F,
526 mut g: G
527) -> impl FnMut(&mut InnerZ80Span) -> ModalResult<((), P), E>
528where
529 F: Parser<InnerZ80Span, O, E>,
530 G: Parser<InnerZ80Span, P, E>,
531 E: ParserError<InnerZ80Span>
532{
533 #[cfg_attr(not(target_arch = "wasm32"), inline)]
534 #[cfg_attr(target_arch = "wasm32", inline(never))]
535 move |i: &mut InnerZ80Span| {
536 loop {
537 let start_i = i.checkpoint();
538 let len = i.eof_offset();
539 match g.parse_next(i) {
540 Ok(o) => return Ok(((), o)),
541 Err(ErrMode::Backtrack(e)) => {
542 match f.parse_next(i) {
543 Err(ErrMode::Backtrack(_err)) => {
544 i.reset(&start_i);
545 #[allow(deprecated)]
546 return Err(ErrMode::Backtrack(e.append(i, &start_i, ErrorKind::Many)));
547 },
548 Err(e) => return Err(e),
549 Ok(_o) => {
550 if i.eof_offset() == len {
552 return Err(ErrMode::Backtrack(E::from_input(i)));
553 }
554 }
555 }
556 },
557 Err(e) => return Err(e)
558 }
559 }
560 }
561}
562
563#[cfg_attr(not(target_arch = "wasm32"), inline)]
564#[cfg_attr(target_arch = "wasm32", inline(never))]
565pub fn inner_code(input: &mut InnerZ80Span) -> ModalResult<LocatedListing, Z80ParserError> {
566 inner_code_with_state(input.state.state, false).parse_next(input)
567}
568#[cfg_attr(not(target_arch = "wasm32"), inline)]
569#[cfg_attr(target_arch = "wasm32", inline(never))]
570pub fn one_instruction_inner_code(
571 input: &mut InnerZ80Span
572) -> ModalResult<LocatedListing, Z80ParserError> {
573 inner_code_with_state(input.state.state, true).parse_next(input)
574}
575
576#[cfg_attr(not(target_arch = "wasm32"), inline)]
579#[cfg_attr(target_arch = "wasm32", inline(never))]
580pub fn inner_code_with_state(
581 new_state: ParsingState,
582 only_one_instruction: bool
583) -> impl Fn(&mut InnerZ80Span) -> ModalResult<LocatedListing, Z80ParserError> {
584 #[cfg_attr(not(target_arch = "wasm32"), inline)]
585 #[cfg_attr(target_arch = "wasm32", inline(never))]
586 move |input: &mut InnerZ80Span| {
587 LocatedListing::parse_inner(input, new_state, only_one_instruction)
589 .map(|l| (Arc::<LocatedListing>::try_unwrap(l).unwrap()))
590 }
591}
592
593pub fn parse_rorg(input: &mut InnerZ80Span) -> ModalResult<LocatedToken, Z80ParserError> {
595 let _ = my_space0.parse_next(input)?;
596 let rorg_start = input.checkpoint();
597 let _ = alt((Caseless("PHASE"), Caseless("RORG"))).parse_next(input)?;
598
599 let exp = cut_err(
600 delimited(my_space1, located_expr, my_space0)
601 .context(StrContext::Label("RORG: error in the expression"))
602 )
603 .parse_next(input)?;
604
605 let _ = my_line_ending.parse_next(input)?;
606
607 let inner = inner_code.parse_next(input)?;
608
609 let _ = cut_err(
610 preceded(my_space0, alt((Caseless("DEPHASE"), Caseless("REND"))))
611 .context(StrContext::Label("RORG: missing REND"))
612 )
613 .parse_next(input)?;
614
615 let _rorg_stop = input.checkpoint();
616 let token = LocatedTokenInner::Rorg(exp, inner).into_located_token_between(&rorg_start, *input);
617 Ok(token)
618}
619
620pub fn parse_function_listing(
622 input: &mut InnerZ80Span
623) -> ModalResult<LocatedListing, Z80ParserError> {
624 inner_code_with_state(ParsingState::FunctionLimited, false).parse_next(input)
626}
627
628pub fn parse_function(input: &mut InnerZ80Span) -> ModalResult<LocatedToken, Z80ParserError> {
629 let function_start = input.checkpoint();
630 let _ = preceded(my_space0, parse_directive_word(b"FUNCTION")).parse_next(input)?;
631 let name = cut_err(parse_label(false).context(StrContext::Label("FUNCTION: wrong name")))
632 .parse_next(input)?; let cloned = *input;
635 let arguments: Vec<InnerZ80Span> = cut_err(
636 preceded(
637 opt(parse_comma), separated::<_, InnerZ80Span, Vec<InnerZ80Span>, _, _, _, _>(
639 0..,
640 delimited(
642 my_space0,
643 take_till(1.., |c| {
644 c == b'\n' || c == b'\r' || c == b':' || c == b',' || c == b' '
645 })
646 .map(|s: &[u8]| cloned.update_slice(s)),
647 my_space0
648 ),
649 parse_comma
650 )
651 )
652 .context(StrContext::Label("FUNCTION: errors in parameters"))
653 )
654 .parse_next(input)?;
655 let arguments = arguments.into_iter().map(|span| span.into()).collect_vec();
656
657 cut_err(
658 preceded(my_space0, my_line_ending)
659 .context(StrContext::Label("FUNCTION: errors after parameters"))
660 )
661 .parse_next(input)?;
662
663 let listing =
664 cut_err(parse_function_listing.context(StrContext::Label("FUNCTION: invalid content")))
665 .parse_next(input)?;
666
667 repeat::<_, _, (), _, _>(0.., my_line_ending).parse_next(input)?;
668 let _ = alt((
669 parse_directive_word(b"ENDF"),
670 parse_directive_word(b"ENDFUNCTION")
671 ))
672 .parse_next(input)?;
673
674 Ok(LocatedTokenInner::Function(name.into(), arguments, listing)
675 .into_located_token_between(&function_start, *input))
676}
677
678pub fn parse_macro(input: &mut InnerZ80Span) -> ModalResult<LocatedToken, Z80ParserError> {
680 let dir_start = input.checkpoint();
681 let _ = parse_directive_word(b"MACRO").parse_next(input)?;
682
683 let name = cut_err(parse_label(false).context(StrContext::Label("MACRO: wrong name")))
685 .parse_next(input)?; parse_macro_inner(dir_start, name).parse_next(input)
688}
689
690fn parse_macro_inner(
691 dir_start: <InnerZ80Span as Stream>::Checkpoint,
692 name: InnerZ80Span
693) -> impl FnMut(&mut InnerZ80Span) -> ModalResult<LocatedToken, Z80ParserError> {
694 move |input: &mut InnerZ80Span| -> ModalResult<LocatedToken, Z80ParserError> {
695 #[derive(Clone, Copy, Debug)]
696 enum CommaOrParenthesis {
697 Comma,
698 Parenthesis
699 };
700
701 let comma_or_parenthesis = opt(alt((
702 parse_comma.value(CommaOrParenthesis::Comma),
703 '('.value(CommaOrParenthesis::Parenthesis)
704 )))
705 .parse_next(input)?;
706
707 let arguments = separated::<_, _, Vec<&[u8]>, _, _, _, _>(
709 0..,
710 delimited(
712 my_space0,
713 take_till(1.., |c| {
714 c == b'\n' || c == b'\r' || c == b':' || c == b',' || c == b' ' || c == b')'
715 }),
716 my_space0
717 ),
718 parse_comma
719 )
720 .parse_next(input)?;
721
722 if let Some(CommaOrParenthesis::Parenthesis) = comma_or_parenthesis {
723 let _ = cut_err(
724 (my_space0, ')', my_space0)
725 .value(())
726 .context("`)` expected`")
727 )
728 .parse_next(input)?;
729 }
730
731 let arguments = arguments
732 .into_iter()
733 .map(|span| (*input).update_slice(span))
734 .map(|span| span.into())
735 .collect_vec();
736
737 alt((my_space0.value(()), my_line_ending.value(()))).parse_next(input)?;
738
739 let before_content = input.checkpoint();
741 let (_, end) = cut_err(
742 repeat_till::<_, _, (), _, _, _, _>(
743 0..,
744 take(1usize),
745 alt((
746 parse_directive_word(b"ENDM"),
747 parse_directive_word(b"ENDMACRO"),
748 parse_directive_word(b"MEND")
749 ))
750 )
751 .context(StrContext::Label(
752 "MACRO: impossible to collect macro content"
753 ))
754 )
755 .parse_next(input)?;
756
757 let content_length = end.offset_from(&before_content);
758 let mut content = *input;
759 content.reset(&before_content);
760 let content: &BStr = unsafe { std::mem::transmute(&content.as_bstr()[..content_length]) };
761 let content = (*input).update_slice(content); Ok(LocatedTokenInner::Macro {
764 name: name.into(),
765 params: arguments,
766 content: content.into(),
767 flavor: input.state.options().assembler_flavor
768 }
769 .into_located_token_between(&dir_start, *input))
770 }
771}
772
773pub fn parse_while(input: &mut InnerZ80Span) -> ModalResult<LocatedToken, Z80ParserError> {
775 let _ = my_space0(input)?;
776 let while_start = input.checkpoint();
777 let _ = parse_directive_word(b"WHILE").parse_next(input)?;
778
779 let cond = cut_err(located_expr.context(StrContext::Label("WHILE: error in condition")))
780 .parse_next(input)?;
781
782 alt((
784 delimited(my_space0, ':', my_space0).value(()),
785 preceded(my_space0, line_ending).value(())
786 ))
787 .parse_next(input)?;
788
789 let inner = cut_err(inner_code.context(StrContext::Label("WHILE: issue in the content")))
790 .parse_next(input)?;
791 let _ = cut_err(
792 preceded(
793 my_space0,
794 alt((parse_directive_word(b"ENDW"), parse_directive_word(b"WEND")))
795 )
796 .context(StrContext::Label("WHILE: not closed"))
797 )
798 .parse_next(input)?;
799
800 let token =
801 LocatedTokenInner::While(cond, inner).into_located_token_between(&while_start, *input);
802 Ok(token)
803}
804
805pub fn parse_module(input: &mut InnerZ80Span) -> ModalResult<LocatedToken, Z80ParserError> {
806 let module_start = input.checkpoint();
807 let _ = parse_directive_word(b"MODULE").parse_next(input)?;
808
809 let name = cut_err(parse_label(false).context(StrContext::Label("MODULE: error in naming")))
810 .parse_next(input)?;
811
812 let inner = cut_err(inner_code.context(StrContext::Label("MODULE: issue in the content")))
813 .parse_next(input)?;
814 let _ = cut_err(
815 preceded(my_space0, parse_directive_word(b"ENDMODULE"))
816 .context(StrContext::Label("MODULE: not closed"))
817 )
818 .parse_next(input)?;
819
820 let token = LocatedTokenInner::Module(name.into(), inner)
821 .into_located_token_between(&module_start, *input);
822 Ok(token)
823}
824
825pub fn parse_crunched_section(
827 input: &mut InnerZ80Span
828) -> ModalResult<LocatedToken, Z80ParserError> {
829 let crunched_start = input.checkpoint();
830 let kind = preceded(
831 my_space0,
832 alt((
833 #[cfg(not(target_arch = "wasm32"))]
834 parse_directive_word(b"LZEXO").value(CrunchType::LZEXO),
835 #[cfg(not(target_arch = "wasm32"))]
836 parse_directive_word(b"LZ4").value(CrunchType::LZ4),
837 parse_directive_word(b"LZ48").value(CrunchType::LZ48),
838 parse_directive_word(b"LZ49").value(CrunchType::LZ49),
839 #[cfg(not(target_arch = "wasm32"))]
840 parse_directive_word(b"LZSHRINKLER").value(CrunchType::Shrinkler),
841 #[cfg(not(target_arch = "wasm32"))]
842 parse_directive_word(b"LZUPKR").value(CrunchType::Upkr),
843 #[cfg(not(target_arch = "wasm32"))]
844 parse_directive_word(b"LZX7").value(CrunchType::LZX7),
845 #[cfg(not(target_arch = "wasm32"))]
846 parse_directive_word(b"LZX0").value(CrunchType::LZX0),
847 #[cfg(not(target_arch = "wasm32"))]
848 parse_directive_word(b"LZAPU").value(CrunchType::LZAPU),
849 parse_directive_word(b"LZSA1").value(CrunchType::LZSA1),
850 parse_directive_word(b"LZSA2").value(CrunchType::LZSA2)
851 ))
852 )
853 .parse_next(input)?;
854
855 let inner =
856 cut_err(inner_code.context(StrContext::Label("CRUNCHED SECTION: issue in the content")))
857 .parse_next(input)?;
858
859 let _ = cut_err(
860 ((my_space0, parse_directive_word(b"LZCLOSE"), my_space0))
861 .context(StrContext::Label("CRUNCHED SECTION section: not closed"))
862 )
863 .parse_next(input)?;
864
865 let token = LocatedTokenInner::CrunchedSection(kind, inner)
866 .into_located_token_between(&crunched_start, *input);
867 Ok(token)
868}
869
870pub fn parse_switch(input: &mut InnerZ80Span) -> ModalResult<LocatedToken, Z80ParserError> {
872 my_many0_nocollect(alt((my_space1.value(()), my_line_ending.value(())))).parse_next(input)?;
873 let switch_start = *input;
874 let _ = parse_directive_word(b"SWITCH")(input)?;
875
876 let value = cut_err(
877 preceded(my_space0, located_expr).context(StrContext::Label("SWITCH: tested value"))
878 )
879 .parse_next(input)?;
880
881 let mut cases_listing = Vec::new();
882 let mut default_listing = None;
883
884 loop {
885 cut_err(
886 repeat::<_, _, (), _, _>(
887 0..,
888 alt((
889 my_space1.value(()),
890 line_ending.value(()),
891 ':'.value(()),
892 parse_comment.value(())
893 ))
894 )
895 .context(StrContext::Label("SWITCH: whitespace error"))
896 )
897 .parse_next(input)?;
898
899 let endswitch = if default_listing.is_some() {
901 cut_err(
902 preceded(
903 my_space0,
904 alt((
905 parse_directive_word(b"ENDS"),
906 parse_directive_word(b"ENDSWITCH")
907 ))
908 .value(true)
909 )
910 .context(StrContext::Label(
911 "SWITCH: endswitch not present after default listing."
912 ))
913 )
914 .parse_next(input)?
915 }
916 else {
917 preceded(
918 my_space0,
919 opt(alt((
920 parse_directive_word(b"ENDS"),
921 parse_directive_word(b"ENDSWITCH")
922 )))
923 .map(|e| e.is_some())
924 )
925 .parse_next(input)?
926 };
927 if endswitch {
928 let token = LocatedTokenInner::Switch(value, cases_listing, default_listing)
929 .into_located_token_between(&switch_start.checkpoint(), *input);
930 return Ok(token);
931 }
932
933 let value = preceded(my_space0, opt(parse_directive_word(b"CASE"))).parse_next(input)?;
934 if value.is_some() {
935 let value = cut_err(
936 delimited(my_space0, located_expr, opt(':'))
937 .context(StrContext::Label("SWITCH: case value error."))
938 )
939 .parse_next(input)?;
940
941 let inner =
942 cut_err(inner_code.context(StrContext::Label("SWITCH: error in case code")))
943 .parse_next(input)?;
944
945 let do_break =
946 opt(preceded(my_space0, parse_directive_word(b"BREAK"))).parse_next(input)?;
947
948 cases_listing.push((value, inner, do_break.is_some()));
949 }
950 else {
951 let _ = cut_err(
952 delimited(
953 my_space0,
954 parse_directive_word(b"DEFAULT"),
955 opt((my_space0, ':'))
956 )
957 .context(StrContext::Label(
958 "Only CASE, DEFAULT or ENDSWITCH are expected."
959 ))
960 )
961 .parse_next(input)?;
962 let default =
963 cut_err(inner_code.context(StrContext::Label("SWITCH: error in default case")))
964 .parse_next(input)?;
965 default_listing = Some(default);
966 }
967 }
968}
969
970pub fn parse_for(input: &mut InnerZ80Span) -> ModalResult<LocatedToken, Z80ParserError> {
971 let for_start = input.checkpoint();
972 let _ = preceded(my_space0, parse_directive_word(b"FOR")).parse_next(input)?;
973
974 let counter = cut_err(parse_label(false)).parse_next(input)?;
976 let start = cut_err(preceded(parse_comma, located_expr)).parse_next(input)?;
977 let stop = cut_err(preceded(parse_comma, located_expr)).parse_next(input)?;
978 let step = opt(preceded(parse_comma, located_expr)).parse_next(input)?;
979
980 let inner = cut_err(inner_code.context(StrContext::Label("FOR: issue in the content")))
982 .parse_next(input)?;
983
984 let _ = cut_err(
986 preceded(
987 my_space0,
988 alt((
989 parse_directive_word(b"ENDFOR"),
990 parse_directive_word(b"FEND"),
991 parse_directive_word(b"ENDF")
992 ))
993 )
994 .context(StrContext::Label("FOR: not closed"))
995 )
996 .parse_next(input)?;
997
998 let token = LocatedTokenInner::For {
999 label: counter.into(),
1000 start,
1001 stop,
1002 step,
1003 listing: inner
1004 }
1005 .into_located_token_between(&for_start, *input);
1006 Ok(token)
1007}
1008
1009#[cfg_attr(not(target_arch = "wasm32"), inline)]
1010#[cfg_attr(target_arch = "wasm32", inline(never))]
1011pub fn parse_confined(input: &mut InnerZ80Span) -> ModalResult<LocatedToken, Z80ParserError> {
1012 let confined_start = input.checkpoint();
1014
1015 let _ = parse_directive_word(b"CONFINED").parse_next(input)?;
1016
1017 let inner = cut_err(inner_code.context(StrContext::Label("CONFINED: issue in the content")))
1018 .parse_next(input)?;
1019
1020 let _ = cut_err(
1021 preceded(
1022 my_space0,
1023 alt((
1024 parse_directive_word(b"ENDCONFINED"),
1025 parse_directive_word(b"CEND"),
1026 parse_directive_word(b"ENDC")
1027 ))
1028 )
1029 .context(StrContext::Label("CONFINED: not closed"))
1030 )
1031 .parse_next(input)?;
1032
1033 let token =
1034 LocatedTokenInner::Confined(inner).into_located_token_between(&confined_start, *input);
1035 Ok(token)
1036}
1037
1038pub fn parse_repeat(input: &mut InnerZ80Span) -> ModalResult<LocatedToken, Z80ParserError> {
1039 let repeat_start = input.checkpoint();
1040 let _ = preceded(
1041 my_space0,
1042 alt((
1043 parse_directive_word(b"REP"),
1044 parse_directive_word(b"REPT"),
1045 parse_directive_word(b"REPEAT")
1046 ))
1047 )
1048 .parse_next(input)?;
1049
1050 let count = opt(located_expr).parse_next(input)?;
1051 match count {
1052 Some(count) => {
1053 let counter = cut_err(
1054 opt(preceded(parse_comma, parse_label(false)))
1055 .context(StrContext::Label("REPEAT: issue in the counter"))
1056 )
1057 .parse_next(input)?;
1058 let counter_start = opt(preceded(parse_comma, located_expr)).parse_next(input)?;
1059 let counter_step = opt(preceded(parse_comma, located_expr)).parse_next(input)?;
1060
1061 let inner =
1062 cut_err(inner_code.context(StrContext::Label("REPEAT: issue in the content")))
1063 .parse_next(input)?;
1064
1065 let _ = cut_err(
1066 preceded(
1067 my_space0,
1068 alt((
1069 parse_directive_word(b"ENDREPEAT"),
1070 parse_directive_word(b"ENDREPT"),
1071 parse_directive_word(b"ENDREP"),
1072 parse_directive_word(b"ENDR"),
1073 parse_directive_word(b"REND")
1074 ))
1075 )
1076 .context(StrContext::Label("REPEAT: not closed"))
1077 )
1078 .parse_next(input)?;
1079
1080 let token = LocatedTokenInner::Repeat(
1081 count,
1082 inner,
1083 counter.map(|c| c.into()),
1084 counter_start,
1085 counter_step
1086 )
1087 .into_located_token_between(&repeat_start, *input);
1088 Ok(token)
1089 },
1090
1091 None => {
1092 let inner =
1093 cut_err(inner_code.context(StrContext::Label("REPEAT: issue in the content")))
1094 .parse_next(input)?;
1095
1096 let _ = cut_err(
1097 delimited(my_space0, parse_directive_word(b"UNTIL"), my_space0)
1098 .context(StrContext::Label("REPEAT ... UNTIL: not closed"))
1099 )
1100 .parse_next(input)?;
1101 let cond =
1102 cut_err(located_expr.context(StrContext::Label("REPEAT UNTIL: condition error")))
1103 .parse_next(input)?;
1104 let token = LocatedTokenInner::RepeatUntil(cond, inner)
1105 .into_located_token_between(&repeat_start, *input);
1106 Ok(token)
1107 }
1108 }
1109}
1110
1111pub fn parse_iterate(input: &mut InnerZ80Span) -> ModalResult<LocatedToken, Z80ParserError> {
1112 let iterate_start = input.checkpoint();
1113 let _ = preceded(
1114 my_space0,
1115 alt((
1116 parse_directive_word(b"ITERATE"),
1117 parse_directive_word(b"ITER")
1118 ))
1119 )
1120 .parse_next(input)?;
1121
1122 let counter = cut_err(
1123 preceded(my_space0, parse_label(false))
1124 .context(StrContext::Label("ITERATE: issue in the counter"))
1125 )
1126 .parse_next(input)?;
1127
1128 let comma_or_in = cut_err(
1129 preceded(my_space0, alt((parse_word(b"IN"), parse_comma)))
1130 .context(StrContext::Label("ITERATE: expected ',' or 'in'"))
1131 )
1132 .parse_next(input)?;
1133
1134 let values = if comma_or_in.contains(&b',') {
1135 let values = cut_err(expr_list.context(StrContext::Label("ITERATE: values issue")))
1136 .parse_next(input)?;
1137 either::Either::Left(values)
1138 }
1139 else {
1140 let values = cut_err(
1141 alt((
1142 parse_expr_bracketed_list,
1143 parse_unary_function_call,
1144 parse_binary_function_call,
1145 parse_any_function_call,
1146 parse_assemble,
1147 parse_label(false).map(|l| LocatedExpr::Label(l.into()))
1148 ))
1149 .context(StrContext::Label("ITERATE: list issue"))
1150 )
1151 .parse_next(input)?;
1152 either::Either::Right(values)
1153 };
1154
1155 let inner = cut_err(inner_code.context(StrContext::Label("ITERATE: issue in the content")))
1156 .parse_next(input)?;
1157
1158 let _ = cut_err(
1159 ((
1160 my_space0,
1161 alt((
1162 parse_directive_word(b"ENDITERATE"),
1163 parse_directive_word(b"ENDITER"),
1164 parse_directive_word(b"ENDI"),
1165 parse_directive_word(b"IEND")
1166 )),
1167 my_space0
1168 ))
1169 .context(StrContext::Label("ITERATE: not closed"))
1170 )
1171 .parse_next(input)?;
1172
1173 let token = LocatedTokenInner::Iterate(counter.into(), values, inner)
1174 .into_located_token_between(&iterate_start, *input);
1175 Ok(token)
1176}
1177
1178pub fn parse_basic(input: &mut InnerZ80Span) -> ModalResult<LocatedToken, Z80ParserError> {
1180 let basic_start = input.checkpoint();
1181 let _ = ((my_space0, Caseless("LOCOMOTIVE"), my_space0)).parse_next(input)?;
1182
1183 let args: Option<Vec<InnerZ80Span>> = opt(separated(
1185 1..,
1186 preceded(my_space0, parse_label(false)),
1187 parse_comma
1188 ))
1189 .parse_next(input)?;
1190 let args = args.map(|args| args.into_iter().map(Z80Span::from).collect_vec());
1191
1192 (my_space0, opt(line_ending)).parse_next(input)?;
1193
1194 let hidden_lines = opt(terminated(
1195 preceded(my_space0, parse_basic_hide_lines),
1196 my_space0
1197 ))
1198 .parse_next(input)?;
1199
1200 (my_space0, opt(line_ending)).parse_next(input)?;
1201
1202 let before_content = input.checkpoint();
1204 let (_, end) = cut_err(
1205 repeat_till::<_, _, (), _, _, _, _>(
1206 0..,
1207 take(1usize),
1208 parse_directive_word(b"ENDLOCOMOTIVE")
1209 )
1210 .context(StrContext::Label(
1211 "BASIC: impossible to collect BASIC content"
1212 ))
1213 )
1214 .parse_next(input)?;
1215
1216 let content_length = end.offset_from(&before_content);
1217 let mut content = *input;
1218 content.reset(&before_content);
1219 let content: &BStr = unsafe { std::mem::transmute(&content.as_bstr()[..content_length]) };
1220 let basic = (*input).update_slice(content); let _ = my_space0.parse_next(input)?;
1223
1224 let token = LocatedTokenInner::Basic(args, hidden_lines, basic.into())
1225 .into_located_token_between(&basic_start, *input);
1226 Ok(token)
1227}
1228
1229pub fn parse_basic_hide_lines(
1231 input: &mut InnerZ80Span
1232) -> ModalResult<Vec<LocatedExpr>, Z80ParserError> {
1233 let _ = ((Caseless("HIDE_LINES"), my_space1)).parse_next(input)?;
1234 expr_list.parse_next(input)
1235}
1236
1237pub fn parse_flag_value_inner(input: &mut InnerZ80Span) -> ModalResult<FlagValue, Z80ParserError> {
1238 let start = input.checkpoint();
1239 cpclib_sna::parse::parse_flag_value::<InnerZ80Span, Z80ParserError>
1240 .parse_next(input)
1241 .map_err(|e| {
1242 match e {
1243 ErrMode::Incomplete(_) => todo!(),
1244 ErrMode::Backtrack(e) => {
1245 let mut error = Z80ParserError::from_input(input);
1246 for ctx in e.context() {
1247 error = error.add_context(input, &start, ctx.clone());
1248 }
1249
1250 ErrMode::Backtrack(error)
1251 },
1252 ErrMode::Cut(e) => {
1253 let mut error = Z80ParserError::from_input(input);
1254 for ctx in e.context() {
1255 error = error.add_context(input, &start, ctx.clone());
1256 }
1257
1258 ErrMode::Cut(error)
1259 }
1260 }
1261 })
1262}
1263
1264#[cfg_attr(not(target_arch = "wasm32"), inline)]
1265#[cfg_attr(target_arch = "wasm32", inline(never))]
1266pub fn parse_line_component(
1267 input: &mut InnerZ80Span
1268) -> ModalResult<(Option<LocatedToken>, Option<LocatedToken>), Z80ParserError> {
1269 my_space0.parse_next(input)?;
1270
1271 parse_line_component_standard.parse_next(input)
1272}
1273
1274pub fn parse_line_component_standard(
1277 input: &mut InnerZ80Span
1278) -> ModalResult<(Option<LocatedToken>, Option<LocatedToken>), Z80ParserError> {
1279 if input.state.options().is_orgams() {
1280 let repeat = opt(parse_orgams_repeat).parse_next(input)?;
1281 if repeat.is_some() {
1282 return Ok((None, repeat));
1283 }
1284 }
1285
1286 let before_let = input.checkpoint();
1287 let r#let = terminated(opt(parse_directive_word(b"LET")), my_space0).parse_next(input)?;
1288
1289 let before_label = input.checkpoint();
1290
1291 let mut label: Option<InnerZ80Span> = if r#let.is_some() {
1292 cut_err(
1294 parse_label(false)
1295 .context(StrContext::Label("LET: missing label"))
1296 .map(Some)
1297 )
1298 .parse_next(input)?
1299 }
1300 else {
1301 opt(parse_label(false)).parse_next(input)?
1303 };
1304
1305 let build_possible_label = move || {
1307 label.map(|label| LocatedTokenInner::Label(label.into()).into_located_token_direct())
1308 };
1309
1310 let before_double_column = input.checkpoint();
1311 let followed_by_double_column = if label.is_some() {
1312 opt(':').parse_next(input)?
1313 }
1314 else {
1315 None
1316 };
1317
1318 my_space0(input)?;
1319
1320 if r#let.is_none() && input.eof_offset() == 0
1322 || peek(opt(alt((
1323 line_ending.value(()),
1324 ';'.value(()),
1325 "//".value(())
1326 ))))
1327 .parse_next(input)?
1328 .is_some()
1329 {
1330 return Ok((build_possible_label(), None));
1331 }
1332
1333 let before_label_modifier = input.checkpoint();
1335 let label_modifier = if label.is_none() {
1336 None
1337 }
1338 else if r#let.is_some() {
1339 cut_err(b"=".context(StrContext::Label("LET: missing =")))
1341 .map(Some)
1342 .parse_next(input)?;
1343 Some(LabelModifier::Equal(None)) }
1345 else {
1346 opt(alt((
1348 parse_word(b"MACRO").value(LabelModifier::Macro),
1349 parse_word(b"DEFL").value(LabelModifier::Equ),
1350 parse_word(b"EQU").value(LabelModifier::Equ),
1351 parse_word(b"SETN").value(LabelModifier::SetN),
1352 parse_word(b"NEXT").value(LabelModifier::Next),
1353 terminated(parse_word(b"SET"), not((my_space0, expr, parse_comma)))
1354 .map(|_| LabelModifier::Set),
1355 b"=".value(LabelModifier::Equal(None)),
1356 alt((parse_word(b"FIELD").value(()), b"#".value(()))).value(LabelModifier::Field),
1357 alt((
1358 b">>=".value(BinaryOperation::RightShift),
1359 b"<<=".value(BinaryOperation::LeftShift),
1360 b"+=".value(BinaryOperation::Add),
1361 b"-=".value(BinaryOperation::Sub),
1362 b"*=".value(BinaryOperation::Mul),
1363 b"/=".value(BinaryOperation::Div),
1364 b"%=".value(BinaryOperation::Mod),
1365 b"&=".value(BinaryOperation::BinaryAnd),
1366 b"|=".value(BinaryOperation::BinaryOr),
1367 b"^=".value(BinaryOperation::BinaryXor),
1368 b"&&=".value(BinaryOperation::BooleanAnd),
1369 b"||=".value(BinaryOperation::BooleanOr)
1370 ))
1371 .map(|oper| LabelModifier::Equal(Some(oper)))
1372 )))
1373 .parse_next(input)?
1374 };
1375
1376 if let Some(label_modifier) = label_modifier {
1377 if label_modifier == LabelModifier::Macro {
1378 let r#macro = parse_macro_inner(before_label, label.unwrap())
1379 .context(StrContext::Label("MACRO: error on macro definition"))
1380 .parse_next(input)?;
1381 return Ok((None, Some(r#macro)));
1382 }
1383
1384 let expr_arg = match &label_modifier {
1385 LabelModifier::Equ
1386 | LabelModifier::Equal(..)
1387 | LabelModifier::Set
1388 | LabelModifier::Field => {
1389 cut_err(located_expr.map(Some))
1390 .context(StrContext::Label("Value error"))
1391 .parse_next(input)?
1392 },
1393 _ => None
1394 };
1395
1396 let source_label = match &label_modifier {
1397 LabelModifier::Next | LabelModifier::SetN => {
1398 cut_err(
1399 preceded(my_space0, parse_label(false))
1400 .map(Some)
1401 .context(StrContext::Label("Label expected"))
1402 )
1403 .parse_next(input)?
1404 },
1405 _ => None
1406 };
1407
1408 let additional_arg = match &label_modifier {
1410 LabelModifier::Next | LabelModifier::SetN => {
1411 opt(preceded(parse_comma, located_expr)).parse_next(input)?
1412 },
1413 _ => None
1414 };
1415
1416 debug_assert!(label.is_some());
1417 let label = unsafe { label.unwrap_unchecked() };
1418
1419 let token: LocatedToken = match label_modifier {
1421 LabelModifier::Equ => {
1422 LocatedTokenInner::Equ {
1423 label: label.into(),
1424 expr: expr_arg.unwrap()
1425 }
1426 },
1427 LabelModifier::Equal(op) => {
1428 LocatedTokenInner::Assign {
1429 label: label.into(),
1430 expr: expr_arg.unwrap(),
1431 op
1432 }
1433 },
1434 LabelModifier::Set => {
1435 LocatedTokenInner::Assign {
1436 label: label.into(),
1437 expr: expr_arg.unwrap(),
1438 op: None
1439 }
1440 },
1441 LabelModifier::SetN => {
1442 LocatedTokenInner::SetN {
1443 label: label.into(),
1444 source: source_label.unwrap().into(),
1445 expr: additional_arg
1446 }
1447 },
1448 LabelModifier::Next => {
1449 LocatedTokenInner::Next {
1450 label: label.into(),
1451 source: source_label.unwrap().into(),
1452 expr: additional_arg
1453 }
1454 },
1455 LabelModifier::Field => {
1456 LocatedTokenInner::Field {
1457 label: label.into(),
1458 expr: expr_arg.unwrap()
1459 }
1460 },
1461 LabelModifier::Macro => unreachable!("This case must have been handled before")
1462 }
1463 .into_located_token_between(&before_label, *input);
1464
1465 Ok((None, Some(token)))
1466 }
1467 else {
1468 input.reset(&before_label_modifier);
1470
1471 if label.is_some() && followed_by_double_column.is_some() {
1473 input.reset(&before_double_column);
1474 return Ok((build_possible_label(), None));
1475 }
1476
1477 let instruction =
1481 opt(alt((parse_z80_directive_with_block, parse_single_token))).parse_next(input)?;
1482
1483 if label.is_some() && instruction.is_none() {
1484 if let Ok(call) = parse_macro_or_struct_call_inner(false, label.take().unwrap()) .map(Some)
1486 .parse_next(input)
1487 {
1488 let call = call.map(|t| t.into_located_token_between(&before_label, *input));
1490 my_space0.parse_next(input)?;
1491
1492 Ok((None, call))
1493 }
1494 else {
1495 Ok((build_possible_label(), None))
1497 }
1498 }
1499 else {
1500 my_space0.parse_next(input)?;
1502 Ok((build_possible_label(), instruction))
1503 }
1504 }
1505}
1506
1507#[cfg_attr(not(target_arch = "wasm32"), inline)]
1509#[cfg_attr(target_arch = "wasm32", inline(never))]
1510pub fn parse_line_or_with_comment(
1511 input: &mut InnerZ80Span
1512) -> ModalResult<Option<LocatedToken>, Z80ParserError> {
1513 let _before_comment = *input;
1515 let comment = delimited(my_space0, opt(parse_comment), my_space0).parse_next(input)?;
1516 let _ = alt((line_ending, eof)).parse_next(input)?;
1517
1518 Ok(comment)
1526}
1527
1528#[cfg_attr(not(target_arch = "wasm32"), inline)]
1529#[cfg_attr(target_arch = "wasm32", inline(never))]
1530pub fn parse_single_token(input: &mut InnerZ80Span) -> ModalResult<LocatedToken, Z80ParserError> {
1531 alt((parse_token, parse_directive)).parse_next(input)
1533}
1534
1535#[derive(Clone, Copy, Debug, PartialEq)]
1537enum LabelModifier {
1538 Equ,
1539 Set,
1540 Equal(Option<BinaryOperation>),
1541 SetN,
1542 Next,
1543 Field,
1544 Macro
1545}
1546
1547pub fn parse_fname(input: &mut InnerZ80Span) -> ModalResult<LocatedExpr, Z80ParserError> {
1549 alt((
1550 parse_string.map(|s: UnescapedString| LocatedExpr::String(s)),
1551 terminated(parse_label(false), not(alt(("/", "://"))))
1552 .map(|l| LocatedExpr::Label(l.into())),
1553 parse_stringlike_without_quote.map(|s: UnescapedString| LocatedExpr::String(s))
1554 ))
1555 .parse_next(input)
1556}
1557
1558#[cfg_attr(not(target_arch = "wasm32"), inline)]
1559#[cfg_attr(target_arch = "wasm32", inline(never))]
1560pub fn parse_z80_directive_with_block(
1561 input: &mut InnerZ80Span
1562) -> ModalResult<LocatedToken, Z80ParserError> {
1563 let _ = my_space0(input)?;
1564
1565 if input.state.options().is_orgams() {
1566 alt((
1567 parse_macro.context(StrContext::Label("Error in macro")),
1568 parse_repeat.context(StrContext::Label("Error in repetition")),
1569 parse_conditional.context(StrContext::Label("Error in condition")),
1570 parse_orgams_fail ))
1572 .parse_next(input)
1573 }
1574 else {
1575 alt((
1576 parse_basic.context(StrContext::Label("Basic code embedding")),
1577 parse_macro.context(StrContext::Label("Error in macro")),
1578 parse_crunched_section.context(StrContext::Label("Error in crunched section")),
1579 parse_module.context(StrContext::Label("Error in module")),
1580 parse_confined.context(StrContext::Label("Error in confined")),
1581 parse_repeat.context(StrContext::Label("Error in repetition")),
1582 parse_for.context(StrContext::Label("Error in for")),
1583 parse_function.context(StrContext::Label("Error in function definition")),
1584 parse_switch.context(StrContext::Label("Error in switch")),
1585 parse_iterate.context(StrContext::Label("Error in iterate")),
1586 parse_while.context(StrContext::Label("Error in while")),
1587 parse_rorg.context(StrContext::Label("Error in rorg")),
1588 parse_conditional.context(StrContext::Label("Error in condition")),
1589 parse_assembler_control_max_passes_number
1590 .context(StrContext::Label("Error in assembler control"))
1591 ))
1592 .parse_next(input)
1593 }
1594}
1595
1596#[cfg_attr(not(target_arch = "wasm32"), inline)]
1597#[cfg_attr(target_arch = "wasm32", inline(never))]
1598pub fn parse_lines(input: &mut InnerZ80Span) -> ModalResult<Vec<LocatedToken>, Z80ParserError> {
1599 let mut tokens = Vec::with_capacity(100);
1600
1601 loop {
1602 let offset = input.eof_offset();
1603 let res = opt(parse_z80_line_complete(&mut tokens)).parse_next(input)?;
1604 if res.is_none() || offset == input.eof_offset() {
1605 break;
1606 }
1607 }
1608
1609 Ok(tokens)
1610}
1611
1612#[cfg_attr(not(target_arch = "wasm32"), inline)]
1615#[cfg_attr(target_arch = "wasm32", inline(never))]
1616pub fn parse_line(
1617 r#in: &mut Vec<LocatedToken>
1618) -> impl FnMut(&mut InnerZ80Span) -> ModalResult<(), Z80ParserError> + '_ {
1619 move |input: &mut InnerZ80Span| -> ModalResult<(), Z80ParserError> {
1620 my_space0.parse_next(input)?;
1621
1622 let mut components: SmallVec<[_; 1]> = Default::default();
1623 loop {
1624 let local = opt(parse_line_component).parse_next(input)?;
1625 if let Some(local) = local {
1626 components.push(local);
1627 }
1628 else {
1629 break; }
1631
1632 my_space0.value(()).parse_next(input)?;
1633
1634 let delim = opt((':', my_space0.value(())).value(())).parse_next(input)?;
1635 if delim.is_none() {
1636 break;
1637 }
1638 }
1639
1640 let before_end = input.checkpoint();
1642 let stop = opt(parse_end_directive).parse_next(input)?;
1643 let comment = if stop.is_some() {
1644 input.reset(&before_end);
1645 None
1646 }
1647 else {
1648 let comment = opt(parse_comment).parse_next(input)?;
1649
1650 alt((eof::<_, Z80ParserError>, line_ending))
1651 .value(())
1652 .context(StrContext::Label("Line ending expected"))
1653 .parse_next(input)?;
1654
1655 comment
1656 };
1657
1658 for (label, instruction) in components.into_iter() {
1660 if let Some(label) = label {
1661 r#in.push(label);
1662 }
1663 if let Some(instruction) = instruction {
1664 r#in.push(instruction)
1665 }
1666 }
1667
1668 if let Some(comment) = comment {
1670 r#in.push(comment);
1671 }
1672
1673 Ok(())
1674 }
1675}
1676
1677pub fn parse_z80_line_complete(
1678 r#in: &mut Vec<LocatedToken>
1679) -> impl FnMut(&mut InnerZ80Span) -> ModalResult<(), Z80ParserError> + '_ {
1680 parse_line(r#in)
1681}
1682
1683#[cfg_attr(not(target_arch = "wasm32"), inline)]
1684#[cfg_attr(target_arch = "wasm32", inline(never))]
1685pub fn parse_assign_operator(
1686 input: &mut InnerZ80Span
1687) -> ModalResult<Option<BinaryOperation>, Z80ParserError> {
1688 let start = input.checkpoint();
1689 let word = take_while(1..=3, |c| {
1690 c == b'='
1691 || c == b'<'
1692 || c == b'>'
1693 || c == b'+'
1694 || c == b'-'
1695 || c == b'*'
1696 || c == b'/'
1697 || c == b'%'
1698 || c == b'^'
1699 || c == b'|'
1700 || c == b'&'
1701 })
1702 .parse_next(input)?;
1703 let oper = match word {
1704 b"=" => None,
1705
1706 b">>=" => Some(BinaryOperation::RightShift),
1707 b"<<=" => Some(BinaryOperation::LeftShift),
1708
1709 b"+=" => Some(BinaryOperation::Add),
1710 b"-=" => Some(BinaryOperation::Sub),
1711 b"*=" => Some(BinaryOperation::Mul),
1712 b"/=" => Some(BinaryOperation::Div),
1713 b"%=" => Some(BinaryOperation::Mod),
1714
1715 b"&=" => Some(BinaryOperation::BinaryAnd),
1716 b"|=" => Some(BinaryOperation::BinaryOr),
1717 b"^=" => Some(BinaryOperation::BinaryXor),
1718
1719 b"&&=" => Some(BinaryOperation::BooleanAnd),
1720 b"||=" => Some(BinaryOperation::BooleanOr),
1721
1722 _ => {
1723 return Err(ErrMode::Cut(Z80ParserError::from_input(input).add_context(
1724 input,
1725 &start,
1726 "Wrong symbol"
1727 )));
1728 }
1729 };
1730
1731 Ok(oper)
1732}
1733
1734#[cfg_attr(not(target_arch = "wasm32"), inline)]
1736#[cfg_attr(target_arch = "wasm32", inline(never))]
1737pub fn parse_string(input: &mut InnerZ80Span) -> ModalResult<UnescapedString, Z80ParserError> {
1738 let opener = alt(('"', '\'')).parse_next(input)? as char;
1739 let closer = opener;
1740 let (normal, escapable) = match opener {
1741 '\'' => (none_of(('\\', '\'')).take(), one_of(('\\', '\''))),
1742 '"' => (none_of(('\\', '"')).take(), one_of(('\\', '"'))),
1743 _ => unreachable!()
1744 };
1745
1746 let (string, slice) = terminated(
1747 opt(my_escaped(normal, '\\', escapable))
1748 .map(|s| s.unwrap_or_default())
1749 .with_taken(),
1750 closer.context(StrContext::Label("End of string not found"))
1751 )
1752 .parse_next(input)?;
1753
1754 let slice = (*input).update_slice(slice);
1755
1756 Ok(UnescapedString(string, slice.into()))
1757}
1758
1759pub fn parse_stringlike_without_quote(
1760 input: &mut InnerZ80Span
1761) -> ModalResult<UnescapedString, Z80ParserError> {
1762 let (normal, escapable) = (
1763 none_of(('\\', ' ', '\r', '\n', ':', ';')),
1764 one_of(('\\', ' ', ':', ';'))
1765 );
1766 let (string, slice) = opt(my_escaped(normal, '\\', escapable))
1767 .map(|s| s.unwrap_or_default())
1768 .with_taken()
1769 .parse_next(input)?;
1770
1771 let slice = (*input).update_slice(slice);
1772
1773 Ok(UnescapedString(string, slice.into()))
1774}
1775
1776#[cfg_attr(not(target_arch = "wasm32"), inline(always))]
1777#[cfg_attr(target_arch = "wasm32", inline(never))]
1778pub fn my_escaped<'a, I: 'a, Error, F, G, O1, O2>(
1779 mut normal: F,
1780 control_char: char,
1781 mut escapable: G
1782) -> impl Parser<I, String, Error>
1783where
1784 I: crate::parser::parser::winnow::stream::StreamIsPartial,
1785 I: Stream,
1786 <I as Stream>::Token: AsChar + Clone,
1787 <I as Stream>::Slice: AsBytes,
1788 I: cpclib_common::winnow::stream::Compare<char>,
1789 F: Parser<I, O1, Error>,
1790 G: Parser<I, O2, Error>,
1791 Error: ParserError<I> + Debug,
1792 O1: Debug,
1793 O2: Debug + AsChar
1794{
1795 move |input: &mut I| {
1796 let mut res = Vec::new();
1797
1798 let start = input.checkpoint();
1799
1800 while input.eof_offset() > 0 {
1801 let current_len = input.eof_offset();
1802
1803 match opt(normal.by_ref().take()).parse_next(input)? {
1804 Some(c) => {
1805 res.extend(c.as_bytes());
1806 if input.eof_offset() == current_len {
1807 return Ok(String::from_utf8_lossy(&res).into_owned());
1808 }
1809 },
1810 None => {
1811 if opt(control_char).parse_next(input)?.is_some() {
1812 let c = escapable.parse_next(input)?;
1813 let c = c.as_char();
1814 let mut buffer = [0; 4];
1815 let s = c.encode_utf8(&mut buffer);
1816 res.extend(s.bytes());
1817 }
1818 else {
1819 return Ok(String::from_utf8_lossy(&res).into_owned());
1820 }
1821 },
1822 }
1823 }
1824
1825 input.reset(&start);
1826 input.finish();
1827 Ok(String::from_utf8_lossy(&res).into_owned())
1828 }
1829}
1830
1831pub fn parse_charset(input: &mut InnerZ80Span) -> ModalResult<LocatedTokenInner, Z80ParserError> {
1832 let charset =
1833 opt(alt((parse_charset_string, parse_charset_start_stop_end))).parse_next(input)?;
1834
1835 Ok(charset
1836 .map(LocatedTokenInner::Charset)
1837 .unwrap_or_else(|| LocatedTokenInner::Charset(CharsetFormat::Reset)))
1838}
1839
1840pub fn parse_charset_start_stop_end(
1841 input: &mut InnerZ80Span
1842) -> ModalResult<CharsetFormat, Z80ParserError> {
1843 let (start, stop, end) = ((
1844 expr,
1845 preceded(parse_comma, expr),
1846 opt(preceded(parse_comma, expr))
1847 ))
1848 .parse_next(input)?;
1849
1850 let format = if let Some(end) = end {
1851 CharsetFormat::Interval(start, stop, end)
1852 }
1853 else {
1854 CharsetFormat::Char(start, stop)
1855 };
1856 Ok(format)
1857}
1858
1859pub fn parse_charset_string(
1860 input: &mut InnerZ80Span
1861) -> ModalResult<CharsetFormat, Z80ParserError> {
1862 let chars = parse_string
1864 .context(StrContext::Label("Missing string"))
1865 .parse_next(input)?;
1866 let chars = unsafe { std::str::from_utf8_unchecked(chars.as_ref().as_bytes()) };
1867 let start = preceded(parse_comma, expr)
1868 .context(StrContext::Label("Missing start value"))
1869 .parse_next(input)?;
1870 let format = CharsetFormat::CharsList(chars.chars().collect_vec(), start);
1871
1872 Ok(format)
1873}
1874
1875pub fn parse_include(input: &mut InnerZ80Span) -> ModalResult<LocatedTokenInner, Z80ParserError> {
1877 let once_fname = (
1878 opt(delimited(my_space0, parse_word(b"ONCE"), my_space0)),
1879 cut_err(parse_fname.context(StrContext::Label("INCLUDE: error in fname")))
1880 )
1881 .parse_next(input)?;
1882
1883 let (once, fname) = once_fname;
1884
1885 let namespace = opt(preceded(
1886 delimited(
1887 my_space0,
1888 alt((Caseless("namespace"), Caseless("module"), Caseless("as"))),
1889 my_space0
1890 ),
1891 delimited(
1892 '"',
1893 parse_label(false),
1894 '"' )
1896 ))
1897 .parse_next(input)?;
1898
1899 Ok(LocatedTokenInner::Include(
1900 fname,
1901 namespace.map(|n| n.into()),
1902 once.is_some()
1903 ))
1904}
1905
1906#[cfg_attr(not(target_arch = "wasm32"), inline)]
1908#[cfg_attr(target_arch = "wasm32", inline(never))]
1909pub fn parse_incbin(
1910 transformation: BinaryTransformation
1911) -> impl Fn(&mut InnerZ80Span) -> ModalResult<LocatedTokenInner, Z80ParserError> {
1912 move |input: &mut InnerZ80Span| -> ModalResult<LocatedTokenInner, Z80ParserError> {
1913 let fname = preceded(my_space0, parse_fname).parse_next(input)?;
1914
1915 let offset =
1916 opt(preceded((my_space0, (','), my_space0), located_expr)).parse_next(input)?;
1917 let length =
1918 opt(preceded((my_space0, (','), my_space0), located_expr)).parse_next(input)?;
1919 let _extended_offset =
1920 opt(preceded((my_space0, (','), my_space0), expr)).parse_next(input)?;
1921 let off =
1922 opt(preceded((my_space0, (','), my_space0), Caseless("OFF"))).parse_next(input)?;
1923
1924 Ok(LocatedTokenInner::Incbin {
1925 fname,
1926 offset,
1927 length,
1928 extended_offset: None,
1929 off: off.is_some(),
1930 transformation
1931 })
1932 }
1933}
1934
1935pub fn parse_write_direct_memory(
1938 input: &mut InnerZ80Span
1939) -> ModalResult<LocatedTokenInner, Z80ParserError> {
1940 let _ = ((
1942 Caseless("DIRECT"),
1943 my_space1,
1944 Caseless("-1"),
1945 parse_comma,
1946 Caseless("-1"),
1947 parse_comma
1948 ))
1949 .parse_next(input)?;
1950
1951 let bank =
1952 cut_err(located_expr.context(StrContext::Label("WRITE DIRECT -1, -1: BANK expected")))
1953 .parse_next(input)?;
1954
1955 let token = LocatedTokenInner::Bank(Some(bank));
1956
1957 Ok(LocatedTokenInner::WarningWrapper(
1958 Box::new(token),
1959 "Prefer BANK or PAGE directives to write direct -1, -1, XX".to_owned()
1960 ))
1961}
1962
1963#[derive(PartialEq)]
1964pub enum SaveKind {
1965 Save,
1966 WriteDirect
1967}
1968
1969pub fn parse_save(
1971 save_kind: SaveKind
1972) -> impl Fn(&mut InnerZ80Span) -> ModalResult<LocatedTokenInner, Z80ParserError> {
1973 move |input: &mut InnerZ80Span| -> ModalResult<LocatedTokenInner, Z80ParserError> {
1974 if save_kind == SaveKind::WriteDirect {
1975 (parse_word(b"DIRECT"), not((my_space0, "-1"))).parse_next(input)?;
1976 }
1977 else {
1978 not((parse_word(b"DIRECT"), my_space0, "-1")).parse_next(input)?;
1979 }
1980
1981 let filename = located_expr.parse_next(input)?;
1982
1983 let address = opt(preceded(parse_comma, opt(located_expr))).parse_next(input)?;
1984
1985 let size = if address.is_some() {
1986 opt(preceded(parse_comma, opt(located_expr))).parse_next(input)?
1987 }
1988 else {
1989 None
1990 };
1991
1992 let save_type = if size.is_some() && save_kind == SaveKind::Save {
1993 opt(preceded(
1994 parse_comma,
1995 alt((
1996 parse_word(b"AMSDOS").value(SaveType::AmsdosBin),
1997 parse_word(b"BASIC").value(SaveType::AmsdosBas),
1998 parse_word(b"ASCII").value(SaveType::Ascii),
1999 parse_word(b"DSK").value(SaveType::Disc(DiscType::Dsk)),
2000 parse_word(b"HFE").value(SaveType::Disc(DiscType::Hfe)),
2001 parse_word(b"DISC").value(SaveType::Disc(DiscType::Auto)),
2002 parse_word(b"TAPE").value(SaveType::Tape)
2003 ))
2004 ))
2005 .parse_next(input)?
2006 }
2007 else if save_kind == SaveKind::WriteDirect {
2008 Some(SaveType::AmsdosBin)
2009 }
2010 else {
2011 None
2012 };
2013
2014 let dsk_filename = if save_type.is_some() && save_kind == SaveKind::Save {
2015 opt(preceded(parse_comma, parse_fname)).parse_next(input)?
2016 }
2017 else {
2018 None
2019 };
2020
2021 let side = if dsk_filename.is_some() && save_kind == SaveKind::Save {
2022 opt(preceded(parse_comma, located_expr)).parse_next(input)?
2023 }
2024 else {
2025 None
2026 };
2027
2028 Ok(LocatedTokenInner::Save {
2029 filename,
2030 address: address.unwrap_or(None),
2031 size: size.unwrap_or(None),
2032 save_type,
2033 dsk_filename,
2034 side
2035 })
2036 }
2037}
2038
2039pub fn parse_undef(input: &mut InnerZ80Span) -> ModalResult<LocatedTokenInner, Z80ParserError> {
2041 let label = parse_label(false).parse_next(input)?;
2042
2043 Ok(LocatedTokenInner::Undef(label.into()))
2044}
2045
2046pub fn parse_section(input: &mut InnerZ80Span) -> ModalResult<LocatedTokenInner, Z80ParserError> {
2047 let name = preceded(my_space0, parse_label(false)).parse_next(input)?;
2048
2049 Ok(LocatedTokenInner::Section(name.into()))
2050}
2051
2052#[cfg_attr(not(target_arch = "wasm32"), inline)]
2053#[cfg_attr(target_arch = "wasm32", inline(never))]
2054pub fn parse_range(input: &mut InnerZ80Span) -> ModalResult<LocatedTokenInner, Z80ParserError> {
2055 let start = cut_err(
2056 delimited(my_space0, located_expr, my_space0)
2057 .context(StrContext::Label("RANGE: wrong start address"))
2058 )
2059 .parse_next(input)?;
2060 let stop = cut_err(
2061 preceded(parse_comma, delimited(my_space0, located_expr, my_space0))
2062 .context(StrContext::Label("RANGE: wrong end address"))
2063 )
2064 .parse_next(input)?;
2065 let label = cut_err(
2066 preceded(
2067 parse_comma,
2068 delimited(my_space0, parse_label(false), my_space0)
2069 )
2070 .context(StrContext::Label("RANGE: wrong name"))
2071 )
2072 .parse_next(input)?;
2073
2074 Ok(LocatedTokenInner::Range(label.into(), start, stop))
2075}
2076#[cfg_attr(not(target_arch = "wasm32"), inline)]
2087#[cfg_attr(target_arch = "wasm32", inline(never))]
2088pub fn parse_token(input: &mut InnerZ80Span) -> ModalResult<LocatedToken, Z80ParserError> {
2089 let parsing_state = input.state.state;
2090
2091 alt((parse_token1, parse_token2))
2092 .verify(move |t| t.is_accepted(&parsing_state))
2093 .parse_next(input)
2094}
2095
2096#[cfg_attr(not(target_arch = "wasm32"), inline)]
2097#[cfg_attr(target_arch = "wasm32", inline(never))]
2098pub fn parse_token1(input: &mut InnerZ80Span) -> ModalResult<LocatedToken, Z80ParserError> {
2099 parse_opcode_no_arg(input)
2100}
2101
2102#[cfg_attr(not(target_arch = "wasm32"), inline)]
2103#[cfg_attr(target_arch = "wasm32", inline(never))]
2104pub fn parse_token2(input: &mut InnerZ80Span) -> ModalResult<LocatedToken, Z80ParserError> {
2105 let input_start = input.checkpoint();
2106
2107 let word = delimited(my_space0, alpha1, my_space0).parse_next(input)?;
2109
2110 let token: LocatedTokenInner = match word {
2114 choice_nocase!(b"LD") => parse_ld(true).parse_next(input),
2115 choice_nocase!(b"ADC") => parse_add_or_adc(Mnemonic::Adc).parse_next(input),
2116 choice_nocase!(b"ADD") => parse_add_or_adc(Mnemonic::Add).parse_next(input),
2117 choice_nocase!(b"AND") => parse_logical_operator(Mnemonic::And).parse_next(input),
2118
2119 choice_nocase!(b"BIT") => parse_res_set_bit(Mnemonic::Bit).parse_next(input),
2120
2121 choice_nocase!(b"CALL") => parse_call_jp_or_jr(Mnemonic::Call).parse_next(input),
2122 choice_nocase!(b"CP") => parse_cp.parse_next(input),
2123
2124 choice_nocase!(b"DEC") => parse_inc_dec(Mnemonic::Dec).parse_next(input),
2125 choice_nocase!(b"DJNZ") => parse_djnz.parse_next(input),
2126
2127 choice_nocase!(b"EX") => {
2128 alt((parse_ex_af, parse_ex_hl_de, parse_ex_mem_sp)).parse_next(input)
2129 },
2130
2131 choice_nocase!(b"EXA") => Ok(LocatedTokenInner::new_opcode(Mnemonic::ExAf, None, None)),
2132 choice_nocase!(b"EXD") => Ok(LocatedTokenInner::new_opcode(Mnemonic::ExHlDe, None, None)),
2133
2134 choice_nocase!(b"IN") => parse_in.parse_next(input),
2135 choice_nocase!(b"INC") => parse_inc_dec(Mnemonic::Inc).parse_next(input),
2136 choice_nocase!(b"IM") => parse_im.parse_next(input),
2137
2138 choice_nocase!(b"JP") => parse_call_jp_or_jr(Mnemonic::Jp).parse_next(input),
2139 choice_nocase!(b"JR") => parse_call_jp_or_jr(Mnemonic::Jr).parse_next(input),
2140
2141 choice_nocase!(b"OR") => parse_logical_operator(Mnemonic::Or).parse_next(input),
2142 choice_nocase!(b"OUT") => parse_out.parse_next(input),
2143
2144 choice_nocase!(b"POP") => parse_push_n_pop(Mnemonic::Pop).parse_next(input),
2145 choice_nocase!(b"PUSH") => parse_push_n_pop(Mnemonic::Push).parse_next(input),
2146
2147 choice_nocase!(b"RES") => parse_res_set_bit(Mnemonic::Res).parse_next(input),
2148 choice_nocase!(b"RET") => parse_ret.parse_next(input),
2149 choice_nocase!(b"RLC") => alt((
2150 parse_shifts_and_rotations(Mnemonic::Rlc),
2151 parse_shifts_and_rotations_fake(Mnemonic::Rlc)
2152 )).parse_next(input),
2153 choice_nocase!(b"RL") => alt((
2154 parse_shifts_and_rotations(Mnemonic::Rl),
2155 parse_shifts_and_rotations_fake(Mnemonic::Rl)
2156 )).parse_next(input),
2157 choice_nocase!(b"RRC") => alt((
2158 parse_shifts_and_rotations(Mnemonic::Rrc),
2159 parse_shifts_and_rotations_fake(Mnemonic::Rrc)
2160 )).parse_next(input),
2161 choice_nocase!(b"RR") => alt((
2162 parse_shifts_and_rotations(Mnemonic::Rr),
2163 parse_shifts_and_rotations_fake(Mnemonic::Rr),
2164 )).parse_next(input),
2165 choice_nocase!(b"RST") => {
2166 alt((
2167 parse_rst_fake, parse_rst
2168 )).parse_next(input)
2169 },
2170
2171 choice_nocase!(b"SBC") => parse_sbc.parse_next(input),
2172 choice_nocase!(b"SET") => parse_res_set_bit(Mnemonic::Set).parse_next(input),
2173 choice_nocase!(b"SL") => cut_err(preceded(('1', my_space1), parse_shifts_and_rotations(Mnemonic::Sl1))).parse_next(input),
2174 choice_nocase!(b"SLA") => alt((
2175 parse_shifts_and_rotations(Mnemonic::Sla),
2176 parse_shifts_and_rotations_fake(Mnemonic::Sla),
2177 )).parse_next(input),
2178 choice_nocase!(b"SLL") => alt((
2179 parse_shifts_and_rotations(Mnemonic::Sl1),
2180 parse_shifts_and_rotations_fake(Mnemonic::Sl1),
2181 )).parse_next(input),
2182 choice_nocase!(b"SRA") => alt((
2183 parse_shifts_and_rotations(Mnemonic::Sra),
2184 parse_shifts_and_rotations_fake(Mnemonic::Sra)
2185 )).parse_next(input),
2186 choice_nocase!(b"SRL") => alt((
2187 parse_shifts_and_rotations(Mnemonic::Srl),
2188 parse_shifts_and_rotations_fake(Mnemonic::Srl),
2189 )).parse_next(input),
2190 choice_nocase!(b"SUB") => parse_sub.parse_next(input),
2191
2192 choice_nocase!(b"XOR") => parse_logical_operator(Mnemonic::Xor).parse_next(input),
2193
2194 _ => {
2195 Err(ErrMode::Backtrack(Z80ParserError::from_input(
2196 input
2197 )))
2198 },
2199 }?;
2200
2201 let token = token.into_located_token_between(&input_start, *input);
2202 Ok(token)
2203}
2204
2205#[cfg_attr(not(target_arch = "wasm32"), inline)]
2207#[cfg_attr(target_arch = "wasm32", inline(never))]
2208pub fn parse_ex_af(input: &mut InnerZ80Span) -> ModalResult<LocatedTokenInner, Z80ParserError> {
2209 ((
2210 parse_register_af,
2212 parse_comma,
2213 parse_word(b"AF'")
2214 ))
2215 .map(|_| LocatedTokenInner::new_opcode(Mnemonic::ExAf, None, None))
2216 .parse_next(input)
2217}
2218
2219#[cfg_attr(not(target_arch = "wasm32"), inline)]
2221#[cfg_attr(target_arch = "wasm32", inline(never))]
2222pub fn parse_ex_hl_de(input: &mut InnerZ80Span) -> ModalResult<LocatedTokenInner, Z80ParserError> {
2223 alt((
2224 ((
2225 parse_register_hl,
2228 parse_comma,
2229 parse_register_de
2230 ))
2231 .value(()),
2232 ((
2233 parse_register_de,
2236 parse_comma,
2237 parse_register_hl
2238 ))
2239 .value(())
2240 ))
2241 .map(|_| LocatedTokenInner::new_opcode(Mnemonic::ExHlDe, None, None))
2242 .parse_next(input)
2243}
2244
2245#[cfg_attr(not(target_arch = "wasm32"), inline)]
2247#[cfg_attr(target_arch = "wasm32", inline(never))]
2248pub fn parse_ex_mem_sp(input: &mut InnerZ80Span) -> ModalResult<LocatedTokenInner, Z80ParserError> {
2249 let destination = ((
2250 ('('),
2253 my_space0,
2254 parse_register_sp,
2255 my_space0,
2256 (')'),
2257 parse_comma,
2258 alt((parse_register_hl, parse_indexregister16))
2259 ))
2260 .parse_next(input)?;
2261
2262 Ok(LocatedTokenInner::new_opcode(
2263 Mnemonic::ExMemSp,
2264 Some(destination.6),
2265 None
2266 ))
2267}
2268
2269#[cfg_attr(not(target_arch = "wasm32"), inline)]
2270#[cfg_attr(target_arch = "wasm32", inline(never))]
2271pub fn parse_struct_directive(
2272 input: &mut InnerZ80Span
2273) -> ModalResult<LocatedToken, Z80ParserError> {
2274 alt((
2275 parse_struct_directive_inner,
2276 parse_macro_or_struct_call(false, true)
2277 ))
2278 .parse_next(input)
2279}
2280
2281#[cfg_attr(not(target_arch = "wasm32"), inline)]
2282#[cfg_attr(target_arch = "wasm32", inline(never))]
2283fn parse_struct_directive_inner(
2284 input: &mut InnerZ80Span
2285) -> ModalResult<LocatedToken, Z80ParserError> {
2286 let input_start = input.checkpoint();
2290 let parsing_state = ParsingState::StructLimited;
2291 let directive = parse_directive_new(&parsing_state.clone())
2292 .verify(move |d| d.is_accepted(&parsing_state))
2293 .parse_next(input)?;
2294
2295 if (directive.is_db() || directive.is_dw()) && directive.data_exprs().len() > 1 {
2297 return Err(ErrMode::Cut(Z80ParserError::from_input(input).add_context(
2298 input,
2299 &input_start,
2300 "0 or 1 arguments are expected"
2301 )));
2302 }
2303 Ok(directive)
2304}
2305
2306pub fn parse_directive(input: &mut InnerZ80Span) -> ModalResult<LocatedToken, Z80ParserError> {
2308 let parsing_state = input.state.state;
2309 parse_directive_new(&parsing_state.clone())
2310 .verify(move |d| d.is_accepted(&parsing_state))
2311 .parse_next(input)
2312}
2313
2314#[cfg_attr(not(target_arch = "wasm32"), inline)]
2318#[cfg_attr(target_arch = "wasm32", inline(never))]
2319pub fn parse_directive_new(
2320 local_parsing_state: &ParsingState
2321) -> impl Fn(&mut InnerZ80Span) -> ModalResult<LocatedToken, Z80ParserError> + '_ {
2322 #[cfg_attr(not(target_arch = "wasm32"), inline)]
2323 #[cfg_attr(target_arch = "wasm32", inline(never))]
2324 move |input: &mut InnerZ80Span| -> ModalResult<LocatedToken, Z80ParserError> {
2325 let is_orgams = input.state.options().is_orgams();
2326
2327 let input_start = input.checkpoint();
2328
2329 let word = delimited(
2331 my_space0,
2332 terminated(
2333 alphanumeric1,
2334 alt((eof.value(()), not(alt((b'.', b'_'))).value(())))
2335 ),
2336 my_space0
2337 )
2338 .parse_next(input)?;
2339
2340 let within_struct = local_parsing_state == &ParsingState::StructLimited;
2341
2342 let token: LocatedTokenInner = match word.len() {
2345 2 => parse_directive_of_size_2(input, &input_start, is_orgams, within_struct, word),
2346 3 => parse_directive_of_size3(input, &input_start, is_orgams, within_struct, word),
2347 4 => parse_directive_of_size_4(input, &input_start, is_orgams, within_struct, word),
2348 5 => parse_directive_of_size_5(input, &input_start, is_orgams, within_struct, word),
2349 6 => parse_directive_of_size_6(input, &input_start, is_orgams, within_struct, word),
2350 7 => parse_directive_of_size_7(input, &input_start, is_orgams, within_struct, word),
2351 8 => parse_directive_of_size_8(input, &input_start, is_orgams, within_struct, word),
2352 10 => parse_directive_of_size_10(input, &input_start, is_orgams, within_struct, word),
2353 _ => parse_directive_of_size_others(input, &input_start, is_orgams, within_struct, word)
2354 }?;
2355
2356 let token = token.into_located_token_between(&input_start, *input);
2357 Ok(token)
2358 }
2359}
2360
2361fn parse_directive_of_size_others(
2362 input: &mut InnerZ80Span,
2363 input_start: &Checkpoint<
2364 Checkpoint<Checkpoint<&'static BStr, &'static BStr>, LocatingSlice<&'static BStr>>,
2365 Stateful<LocatingSlice<&'static BStr>, &'static context::ParserContext>
2366 >,
2367 is_orgams: bool,
2368 within_struct: bool,
2369 word: &[u8]
2370) -> ModalResult<LocatedTokenInner, Z80ParserError> {
2371 match &word.to_ascii_uppercase()[..] {
2372 #[cfg(not(target_arch = "wasm32"))]
2374 b"INCSHRINKLER" => {
2375 parse_incbin(BinaryTransformation::Crunch(CrunchType::Shrinkler)).parse_next(input)
2376 },
2377
2378 b"STARTINGINDEX" => parse_startingindex.parse_next(input),
2380
2381 _ => {
2382 input.reset(input_start);
2383 Err(ErrMode::Backtrack(Z80ParserError::from_input(input)))
2384 }
2385 }
2386}
2387
2388fn parse_directive_of_size_10(
2389 input: &mut InnerZ80Span,
2390 input_start: &Checkpoint<
2391 Checkpoint<Checkpoint<&'static BStr, &'static BStr>, LocatingSlice<&'static BStr>>,
2392 Stateful<LocatingSlice<&'static BStr>, &'static context::ParserContext>
2393 >,
2394 is_orgams: bool,
2395 within_struct: bool,
2396 word: &[u8]
2397) -> ModalResult<LocatedTokenInner, Z80ParserError> {
2398 match word {
2399 choice_nocase!(b"ASMCONTROL") => parse_assembler_control.parse_next(input),
2400 choice_nocase!(b"BREAKPOINT") => parse_breakpoint.parse_next(input),
2401 choice_nocase!(b"DEFSECTION") => parse_range.parse_next(input),
2402
2403 _ => {
2404 input.reset(input_start);
2405 Err(ErrMode::Backtrack(Z80ParserError::from_input(input)))
2406 }
2407 }
2408}
2409
2410fn parse_directive_of_size_8(
2411 input: &mut InnerZ80Span,
2412 input_start: &Checkpoint<
2413 Checkpoint<Checkpoint<&'static BStr, &'static BStr>, LocatingSlice<&'static BStr>>,
2414 Stateful<LocatingSlice<&'static BStr>, &'static context::ParserContext>
2415 >,
2416 is_orgams: bool,
2417 within_struct: bool,
2418 word: &[u8]
2419) -> ModalResult<LocatedTokenInner, Z80ParserError> {
2420 match word {
2421 choice_nocase!(b"BINCLUDE") => parse_incbin(BinaryTransformation::None).parse_next(input),
2422 choice_nocase!(b"BUILDSNA") => parse_buildsna(true).parse_next(input),
2423 choice_nocase!(b"BUILDCPR") => Ok(LocatedTokenInner::BuildCpr),
2424 choice_nocase!(b"INCLZSA1") => {
2425 parse_incbin(BinaryTransformation::Crunch(CrunchType::LZSA1)).parse_next(input)
2426 },
2427 choice_nocase!(b"INCLZSA2") => {
2428 parse_incbin(BinaryTransformation::Crunch(CrunchType::LZSA1)).parse_next(input)
2429 },
2430 choice_nocase!(b"NOEXPORT") => parse_export(ExportKind::NoExport).parse_next(input),
2431 choice_nocase!(b"WAITNOPS") => parse_waitnops.parse_next(input),
2432 choice_nocase!(b"SNAPINIT") => parse_snainit.parse_next(input),
2433
2434 _ => {
2435 input.reset(input_start);
2436 Err(ErrMode::Backtrack(Z80ParserError::from_input(input)))
2437 }
2438 }
2439}
2440
2441fn parse_directive_of_size_7(
2442 input: &mut InnerZ80Span,
2443 input_start: &Checkpoint<
2444 Checkpoint<Checkpoint<&'static BStr, &'static BStr>, LocatingSlice<&'static BStr>>,
2445 Stateful<LocatingSlice<&'static BStr>, &'static context::ParserContext>
2446 >,
2447 is_orgams: bool,
2448 within_struct: bool,
2449 word: &[u8]
2450) -> ModalResult<LocatedTokenInner, Z80ParserError> {
2451 match word {
2452 choice_nocase!(b"INCLUDE") => parse_include.parse_next(input),
2453 choice_nocase!(b"BANKSET") => parse_bankset.parse_next(input),
2454 choice_nocase!(b"CHARSET") => parse_charset.parse_next(input),
2455 choice_nocase!(b"PROTECT") => parse_protect.parse_next(input),
2456 choice_nocase!(b"SECTION") => parse_section.parse_next(input),
2457 choice_nocase!(b"SNAINIT") => parse_snainit.parse_next(input),
2458 #[cfg(not(target_arch = "wasm32"))]
2459 choice_nocase!(b"INCUPKR") => {
2460 parse_incbin(BinaryTransformation::Crunch(CrunchType::Upkr)).parse_next(input)
2461 },
2462 _ => {
2463 input.reset(input_start);
2464 Err(ErrMode::Backtrack(Z80ParserError::from_input(input)))
2465 }
2466 }
2467}
2468
2469fn parse_directive_of_size_6(
2470 input: &mut InnerZ80Span,
2471 input_start: &Checkpoint<
2472 Checkpoint<Checkpoint<&'static BStr, &'static BStr>, LocatingSlice<&'static BStr>>,
2473 Stateful<LocatingSlice<&'static BStr>, &'static context::ParserContext>
2474 >,
2475 is_orgams: bool,
2476 within_struct: bool,
2477 word: &[u8]
2478) -> ModalResult<LocatedTokenInner, Z80ParserError> {
2479 match word {
2480 choice_nocase!(b"ASSERT") => parse_assert.parse_next(input),
2481
2482 choice_nocase!(b"EXPORT") => parse_export(ExportKind::Export).parse_next(input),
2483 choice_nocase!(b"INCBIN") => parse_incbin(BinaryTransformation::None).parse_next(input),
2484 #[cfg(not(target_arch = "wasm32"))]
2485 choice_nocase!(b"INCEXO") => {
2486 parse_incbin(BinaryTransformation::Crunch(CrunchType::LZEXO)).parse_next(input)
2487 },
2488 #[cfg(not(target_arch = "wasm32"))]
2489 choice_nocase!(b"INCLZ4") => {
2490 parse_incbin(BinaryTransformation::Crunch(CrunchType::LZ4)).parse_next(input)
2491 },
2492
2493 choice_nocase!(b"INCL48") => {
2494 parse_incbin(BinaryTransformation::Crunch(CrunchType::LZ48)).parse_next(input)
2495 },
2496
2497 choice_nocase!(b"INCL49") => {
2498 parse_incbin(BinaryTransformation::Crunch(CrunchType::LZ49)).parse_next(input)
2499 },
2500
2501 #[cfg(not(target_arch = "wasm32"))]
2502 choice_nocase!(b"INCAPU") => {
2503 parse_incbin(BinaryTransformation::Crunch(CrunchType::LZAPU)).parse_next(input)
2504 },
2505
2506 #[cfg(not(target_arch = "wasm32"))]
2507 choice_nocase!(b"INCZX0") => {
2508 parse_incbin(BinaryTransformation::Crunch(CrunchType::LZX0)).parse_next(input)
2509 },
2510
2511 choice_nocase!(b"RETURN") => parse_return.parse_next(input),
2512 choice_nocase!(b"SNASET") => parse_snaset(true).parse_next(input),
2513
2514 choice_nocase!(b"STRUCT") => parse_struct.parse_next(input),
2515 choice_nocase!(b"TICKER") => parse_stable_ticker.parse_next(input),
2516
2517 choice_nocase!(b"NOLIST") => Ok(LocatedTokenInner::NoList),
2518
2519 choice_nocase!(b"IMPORT") if is_orgams => parse_include.parse_next(input), _ => {
2522 input.reset(input_start);
2523 Err(ErrMode::Backtrack(Z80ParserError::from_input(input)))
2524 }
2525 }
2526}
2527
2528fn parse_directive_of_size_5(
2529 input: &mut InnerZ80Span,
2530 input_start: &Checkpoint<
2531 Checkpoint<Checkpoint<&'static BStr, &'static BStr>, LocatingSlice<&'static BStr>>,
2532 Stateful<LocatingSlice<&'static BStr>, &'static context::ParserContext>
2533 >,
2534 is_orgams: bool,
2535 within_struct: bool,
2536 word: &[u8]
2537) -> ModalResult<LocatedTokenInner, Z80ParserError> {
2538 match word {
2539 choice_nocase!(b"ALIGN") => parse_align.parse_next(input),
2540 choice_nocase!(b"ABYTE") => {
2541 parse_db_or_dw_or_str(DbDwStr::Abyte, within_struct).parse_next(input)
2542 },
2543 choice_nocase!(b"LIMIT") => parse_limit.parse_next(input),
2544 choice_nocase!(b"PAUSE") => Ok(LocatedTokenInner::Pause),
2545 choice_nocase!(b"PRINT") => parse_print(true).parse_next(input),
2546 choice_nocase!(b"RANGE") => parse_range.parse_next(input),
2547 choice_nocase!(b"UNDEF") => parse_undef.parse_next(input),
2548
2549 choice_nocase!(b"WRITE") => {
2550 alt((parse_save(SaveKind::WriteDirect), parse_write_direct_memory)).parse_next(input)
2551 },
2552
2553 _ => {
2554 input.reset(input_start);
2555 Err(ErrMode::Backtrack(Z80ParserError::from_input(input)))
2556 }
2557 }
2558}
2559
2560fn parse_directive_of_size_4(
2561 input: &mut InnerZ80Span,
2562 input_start: &Checkpoint<
2563 Checkpoint<Checkpoint<&'static BStr, &'static BStr>, LocatingSlice<&'static BStr>>,
2564 Stateful<LocatingSlice<&'static BStr>, &'static context::ParserContext>
2565 >,
2566 is_orgams: bool,
2567 within_struct: bool,
2568 word: &[u8]
2569) -> ModalResult<LocatedTokenInner, Z80ParserError> {
2570 match word {
2571 choice_nocase!(b"DEFB")
2572 | choice_nocase!(b"DEFM")
2573 | choice_nocase!(b"BYTE")
2574 | choice_nocase!(b"TEXT") => {
2575 parse_db_or_dw_or_str(DbDwStr::Db, within_struct).parse_next(input)
2576 },
2577
2578 choice_nocase!(b"FILL") | choice_nocase!(b"DEFS") | choice_nocase!(b"RMEM") => {
2579 parse_defs.parse_next(input)
2580 },
2581
2582 choice_nocase!(b"BANK") => parse_bank.parse_next(input),
2583 choice_nocase!(b"FAIL") => parse_fail(true).parse_next(input),
2584 choice_nocase!(b"LIST") => Ok(LocatedTokenInner::List),
2585 choice_nocase!(b"READ") => parse_include.parse_next(input),
2586
2587 choice_nocase!(b"SAVE") => parse_save(SaveKind::Save).parse_next(input),
2588
2589 choice_nocase!(b"SKIP") if is_orgams => parse_skip.parse_next(input),
2590
2591 choice_nocase!(b"WORD") | choice_nocase!(b"DEFW") => {
2592 parse_db_or_dw_or_str(DbDwStr::Dw, within_struct).parse_next(input)
2593 },
2594 _ => {
2595 input.reset(input_start);
2596 Err(ErrMode::Backtrack(Z80ParserError::from_input(input)))
2597 }
2598 }
2599}
2600
2601fn parse_directive_of_size3(
2602 input: &mut InnerZ80Span,
2603 input_start: &Checkpoint<
2604 Checkpoint<Checkpoint<&'static BStr, &'static BStr>, LocatingSlice<&'static BStr>>,
2605 Stateful<LocatingSlice<&'static BStr>, &'static context::ParserContext>
2606 >,
2607 is_orgams: bool,
2608 within_struct: bool,
2609 word: &[u8]
2610) -> ModalResult<LocatedTokenInner, Z80ParserError> {
2611 match word {
2612 choice_nocase!(b"BRK") if is_orgams => parse_breakpoint.parse_next(input),
2613
2614 choice_nocase!(b"STR") => {
2615 parse_db_or_dw_or_str(DbDwStr::Str, within_struct).parse_next(input)
2616 },
2617 choice_nocase!(b"END") if !is_orgams => Ok(LocatedTokenInner::End),
2618 choice_nocase!(b"ENT") => parse_run(RunEnt::Ent).parse_next(input),
2619 choice_nocase!(b"MAP") => parse_map.parse_next(input),
2620 choice_nocase!(b"NOP") => parse_nop.parse_next(input),
2621 choice_nocase!(b"ORG") => parse_org.parse_next(input),
2622 choice_nocase!(b"RUN") => parse_run(RunEnt::Run).parse_next(input),
2623 _ => {
2624 input.reset(input_start);
2625 Err(ErrMode::Backtrack(Z80ParserError::from_input(input)))
2626 }
2627 }
2628}
2629
2630fn parse_directive_of_size_2(
2631 input: &mut InnerZ80Span,
2632 input_start: &Checkpoint<
2633 Checkpoint<Checkpoint<&'static BStr, &'static BStr>, LocatingSlice<&'static BStr>>,
2634 Stateful<LocatingSlice<&'static BStr>, &'static context::ParserContext>
2635 >,
2636 is_orgams: bool,
2637 within_struct: bool,
2638 word: &[u8]
2639) -> ModalResult<LocatedTokenInner, Z80ParserError> {
2640 match word {
2641 choice_nocase!(b"BY") if is_orgams => {
2642 parse_db_or_dw_or_str(DbDwStr::Db, within_struct).parse_next(input)
2643 },
2644
2645 choice_nocase!(b"DB") | choice_nocase!(b"DM") => {
2646 parse_db_or_dw_or_str(DbDwStr::Db, within_struct).parse_next(input)
2647 },
2648
2649 choice_nocase!(b"DS") => parse_defs.parse_next(input),
2650
2651 choice_nocase!(b"DW") => {
2652 parse_db_or_dw_or_str(DbDwStr::Dw, within_struct).parse_next(input)
2653 },
2654
2655 _ => {
2656 input.reset(input_start);
2657 Err(ErrMode::Backtrack(Z80ParserError::from_input(input)))
2658 }
2659 }
2660}
2661
2662#[derive(Clone, Copy, Debug)]
2663enum KindOfConditional {
2664 If,
2665 IfNot,
2666 IfDef,
2667 IfNdef,
2668 IfUsed,
2669 IfNused
2670}
2671
2672#[cfg_attr(not(target_arch = "wasm32"), inline)]
2675#[cfg_attr(target_arch = "wasm32", inline(never))]
2676pub fn parse_conditional(input: &mut InnerZ80Span) -> ModalResult<LocatedToken, Z80ParserError> {
2677 let is_orgams = input.state.options().is_orgams();
2678
2679 let if_clone = *input;
2682 let if_start = input.checkpoint();
2683
2684 let mut conditions = Vec::new();
2685 let mut else_clause = None;
2686
2687 loop {
2688 let first_loop = conditions.is_empty();
2689
2690 let if_token_or_error = alt((
2692 parse_directive_word(b"IF").value(KindOfConditional::If),
2693 parse_directive_word(b"IFNOT").value(KindOfConditional::IfNot),
2694 parse_directive_word(b"IFDEF").value(KindOfConditional::IfDef),
2695 parse_directive_word(b"IFNDEF").value(KindOfConditional::IfNdef),
2696 parse_directive_word(b"IFUSED").value(KindOfConditional::IfUsed),
2697 parse_directive_word(b"IFEXIST").value(KindOfConditional::IfUsed),
2698 parse_directive_word(b"IFNUSED").value(KindOfConditional::IfNused)
2699 ))
2700 .parse_next(input);
2701
2702 if first_loop && if_token_or_error.is_err() {
2706 input.reset(&if_start);
2707 return Err(if_token_or_error.err().unwrap());
2708 }
2709
2710 let condition = if let Ok(test_kind) = if_token_or_error {
2712 let cond = cut_err(
2714 delimited(my_space0, parse_conditional_condition(test_kind), my_space0)
2715 .context(StrContext::Label("Condition: error in the condition"))
2716 )
2717 .parse_next(input)?;
2718 Some(cond)
2719 }
2720 else {
2721 None
2722 };
2723
2724 let _ = cut_err(
2728 alt((
2729 delimited(my_space0, parse_comment, line_ending).take(),
2730 line_ending.take(),
2731 ':'.take()
2732 ))
2733 .context(StrContext::Label(
2734 "Condition: condition must end by a new line or ':'"
2735 ))
2736 )
2737 .parse_next(input)
2738 .map_err(|e| e.add_context(input, &if_start, "Error in condition"))?;
2739
2740 let code = cut_err(inner_code.context(StrContext::Label(
2743 "Condition: syntax error in conditionnal code"
2744 )))
2745 .parse_next(input)?;
2746 if let Some(condition) = condition {
2751 conditions.push((condition, code));
2752
2753 let r#else = opt(preceded(
2754 repeat::<_, _, (), _, _>(0.., alt((
2755 my_space1.value(()),
2756 line_ending.value(()),
2757 ':'.value(())
2758 ))),
2759 (winnow::ascii::Caseless(b"ELSE"), my_space0) ))
2761 .parse_next(input)?;
2762 if r#else.is_none() {
2763 break;
2764 }
2765 }
2766 else {
2767 else_clause = Some(code);
2768 break;
2769 }
2770 }
2771
2772 let _ = (
2776 opt(alt((
2777 delimited(my_space0, ':', my_space0).value(()),
2778 delimited(my_space0, parse_comment, line_ending).value(())
2779 ))),
2780 cut_err(preceded(
2781 my_space0,
2782 parse_directive_word(if is_orgams { b"END" } else { b"ENDIF" })
2783 ))
2784 .take()
2785 )
2786 .parse_next(input)
2787 .map_err(|e| e.add_context(&if_clone, &if_start, "End directive not found"))?;
2788
2789 let token = LocatedTokenInner::If(conditions, else_clause)
2792 .into_located_token_between(&if_start, *input);
2793 Ok(token)
2794}
2795
2796#[cfg_attr(not(target_arch = "wasm32"), inline)]
2798#[cfg_attr(target_arch = "wasm32", inline(never))]
2799fn parse_conditional_condition(
2800 code: KindOfConditional
2801) -> impl Fn(&mut InnerZ80Span) -> ModalResult<LocatedTestKind, Z80ParserError> {
2802 move |input: &mut InnerZ80Span| -> ModalResult<LocatedTestKind, Z80ParserError> {
2803 match &code {
2804 KindOfConditional::If => located_expr.map(LocatedTestKind::True).parse_next(input),
2805
2806 KindOfConditional::IfNot => located_expr.map(LocatedTestKind::False).parse_next(input),
2807
2808 KindOfConditional::IfDef => {
2809 preceded(my_space0, parse_label(false))
2810 .map(|l| LocatedTestKind::LabelExists(l.into()))
2811 .parse_next(input)
2812 },
2813
2814 KindOfConditional::IfNdef => {
2815 parse_label(false)
2816 .map(|l| LocatedTestKind::LabelDoesNotExist(l.into()))
2817 .parse_next(input)
2818 },
2819
2820 KindOfConditional::IfUsed => {
2821 parse_label(false)
2822 .map(|l| LocatedTestKind::LabelUsed(l.into()))
2823 .parse_next(input)
2824 },
2825
2826 KindOfConditional::IfNused => {
2827 parse_label(false)
2828 .map(|l| LocatedTestKind::LabelNused(l.into()))
2829 .parse_next(input)
2830 },
2831
2832 _ => unreachable!()
2833 }
2834 }
2835}
2836
2837pub fn parse_breakpoint(
2839 input: &mut InnerZ80Span
2840) -> ModalResult<LocatedTokenInner, Z80ParserError> {
2841 let expr = opt(terminated(located_expr, not('='))
2842 .with_taken()
2843 .verify(|(e, s)| {
2844 !s.eq_ignore_ascii_case(b"READ")
2846 && !s.eq_ignore_ascii_case(b"R")
2847 && !s.eq_ignore_ascii_case(b"WRITE")
2848 && !s.eq_ignore_ascii_case(b"W")
2849 && !s.eq_ignore_ascii_case(b"READWRITE")
2850 && !s.eq_ignore_ascii_case(b"RW")
2851 && !s.eq_ignore_ascii_case(b"MEM")
2852 && !s.eq_ignore_ascii_case(b"MEMORY")
2853 && !s.eq_ignore_ascii_case(b"EXEC")
2854 && !s.eq_ignore_ascii_case(b"EXECUTE")
2855 && !s.eq_ignore_ascii_case(b"STOP")
2856 && !s.eq_ignore_ascii_case(b"STOPPER")
2857 && !s.eq_ignore_ascii_case(b"WATCH")
2858 && !s.eq_ignore_ascii_case(b"WATCHER")
2859 && !s.contains(&b'=')
2860 })
2861 .map(|(e, s)| e))
2862 .parse_next(input)?;
2863
2864 let address = Rc::new(RefCell::new(expr.map(|expr| (None, expr))));
2865 let r#type = Rc::new(RefCell::new(None));
2866 let access = Rc::new(RefCell::new(None));
2867 let run = Rc::new(RefCell::new(None));
2868 let mask = Rc::new(RefCell::new(None));
2869 let size = Rc::new(RefCell::new(None));
2870 let value = Rc::new(RefCell::new(None));
2871 let value_mask = Rc::new(RefCell::new(None));
2872 let condition = Rc::new(RefCell::new(None));
2873 let name = Rc::new(RefCell::new(None));
2874 let step = Rc::new(RefCell::new(None));
2875
2876 let first = std::rc::Rc::new(std::cell::RefCell::new(true));
2877
2878 loop {
2879 cut_err(
2880 opt(parse_breakpoint_argument)
2881 .verify_map(|arg| {
2882 if let Some(arg) = arg {
2884 match arg {
2885 BreakPointArgument::Address { arg, value } => {
2886 let mut address = address.borrow_mut();
2887 let address = address.deref_mut();
2888 if address.is_some() {
2889 None
2890 }
2891 else {
2892 address.replace((Some(arg), value));
2893 Some(())
2894 }
2895 },
2896
2897 BreakPointArgument::Type { arg, value } => {
2898 let mut r#type = r#type.borrow_mut();
2899 let r#type = r#type.deref_mut();
2900 if r#type.is_some() {
2901 None
2902 }
2903 else {
2904 r#type.replace((Some(arg), value));
2905 Some(())
2906 }
2907 },
2908
2909 BreakPointArgument::Access { arg, value } => {
2910 let mut access = access.borrow_mut();
2911 let access = access.deref_mut();
2912 if access.is_some() {
2913 None
2914 }
2915 else {
2916 access.replace((arg, value));
2917 Some(())
2918 }
2919 },
2920
2921 BreakPointArgument::Run { arg, value } => {
2922 let mut run = run.borrow_mut();
2923 let run = run.deref_mut();
2924 if run.is_some() {
2925 None
2926 }
2927 else {
2928 run.replace((arg, value));
2929 Some(())
2930 }
2931 },
2932
2933 BreakPointArgument::Mask { arg, value } => {
2934 let mut item = mask.borrow_mut();
2935 let item = item.deref_mut();
2936 if item.is_some() {
2937 None
2938 }
2939 else {
2940 item.replace((arg, value));
2941 Some(())
2942 }
2943 },
2944
2945 BreakPointArgument::Size { arg, value } => {
2946 let mut item = size.borrow_mut();
2947 let item = item.deref_mut();
2948 if item.is_some() {
2949 None
2950 }
2951 else {
2952 item.replace((arg, value));
2953 Some(())
2954 }
2955 },
2956
2957 BreakPointArgument::Value { arg, value: val } => {
2958 let mut item = value.borrow_mut();
2959 let item = item.deref_mut();
2960 if item.is_some() {
2961 None
2962 }
2963 else {
2964 item.replace((arg, val));
2965 Some(())
2966 }
2967 },
2968
2969 BreakPointArgument::ValueMask { arg, value } => {
2970 let mut item = value_mask.borrow_mut();
2971 let item = item.deref_mut();
2972 if item.is_some() {
2973 None
2974 }
2975 else {
2976 item.replace((arg, value));
2977 Some(())
2978 }
2979 },
2980
2981 BreakPointArgument::Name { arg, value } => {
2982 let mut item = name.borrow_mut();
2983 let item = item.deref_mut();
2984 if item.is_some() {
2985 None
2986 }
2987 else {
2988 item.replace((arg, value));
2989 Some(())
2990 }
2991 },
2992
2993 BreakPointArgument::Condition { arg, value } => {
2994 let mut item = condition.borrow_mut();
2995 let item = item.deref_mut();
2996 if item.is_some() {
2997 None
2998 }
2999 else {
3000 item.replace((arg, value));
3001 Some(())
3002 }
3003 },
3004
3005 BreakPointArgument::Step { arg, value } => {
3006 let mut item = step.borrow_mut();
3007 let item = item.deref_mut();
3008 if item.is_some() {
3009 None
3010 }
3011 else {
3012 item.replace((arg, value));
3013 Some(())
3014 }
3015 },
3016
3017 _ => Some(()) }
3019 }
3020 else if *first.borrow() {
3021 Some(())
3022 }
3023 else {
3024 None
3025 }
3026 })
3027 .context(StrContext::Label("Breapoint parameter error"))
3028 )
3029 .parse_next(input)?;
3030
3031 *first.borrow_mut() = false;
3032
3033 if opt(parse_comma).parse_next(input)?.is_none() {
3034 break;
3035 }
3036 }
3037
3038 let brk = LocatedTokenInner::Breakpoint {
3039 address: Rc::into_inner(address).unwrap().into_inner().map(|a| a.1),
3040 r#type: Rc::into_inner(r#type).unwrap().into_inner().map(|r| r.1),
3041 access: Rc::into_inner(access).unwrap().into_inner().map(|a| a.1),
3042 run: Rc::into_inner(run).unwrap().into_inner().map(|a| a.1),
3043 mask: Rc::into_inner(mask).unwrap().into_inner().map(|a| a.1),
3044 size: Rc::into_inner(size).unwrap().into_inner().map(|a| a.1),
3045 value: Rc::into_inner(value).unwrap().into_inner().map(|a| a.1),
3046 value_mask: Rc::into_inner(value_mask)
3047 .unwrap()
3048 .into_inner()
3049 .map(|a| a.1),
3050 condition: Rc::into_inner(condition).unwrap().into_inner().map(|a| a.1),
3051 name: Rc::into_inner(name).unwrap().into_inner().map(|a| a.1),
3052 step: Rc::into_inner(step).unwrap().into_inner().map(|a| a.1)
3053 };
3054
3055 Ok(brk)
3056}
3057
3058#[derive(Debug)]
3059pub enum BreakPointArgument {
3060 Type {
3061 arg: Option<InnerZ80Span>,
3062 value: RemuBreakPointType
3063 },
3064 Access {
3065 arg: Option<InnerZ80Span>,
3066 value: RemuBreakPointAccessMode
3067 },
3068 Run {
3069 arg: Option<InnerZ80Span>,
3070 value: RemuBreakPointRunMode
3071 },
3072 Address {
3073 arg: InnerZ80Span,
3074 value: LocatedExpr
3075 },
3076 Mask {
3077 arg: InnerZ80Span,
3078 value: LocatedExpr
3079 },
3080 Size {
3081 arg: InnerZ80Span,
3082 value: LocatedExpr
3083 },
3084 Value {
3085 arg: InnerZ80Span,
3086 value: LocatedExpr
3087 },
3088 ValueMask {
3089 arg: InnerZ80Span,
3090 value: LocatedExpr
3091 },
3092 Condition {
3093 arg: InnerZ80Span,
3094 value: LocatedExpr
3095 },
3096 Name {
3097 arg: InnerZ80Span,
3098 value: LocatedExpr
3099 },
3100 Step {
3101 arg: InnerZ80Span,
3102 value: LocatedExpr
3103 }
3104}
3105
3106pub fn parse_breakpoint_argument(
3107 input: &mut InnerZ80Span
3108) -> ModalResult<BreakPointArgument, Z80ParserError> {
3109 alt((
3110 parse_optional_argname_and_value("TYPE", &parse_breakpoint_type_value)
3111 .map(|(k, v)| BreakPointArgument::Type { arg: k, value: v }),
3112 parse_optional_argname_and_value("ACCESS", &parse_breakpoint_access_value)
3113 .map(|(k, v)| BreakPointArgument::Access { arg: k, value: v }),
3114 parse_optional_argname_and_value("RUNMODE", &parse_breakpoint_run_value)
3115 .map(|(k, v)| BreakPointArgument::Run { arg: k, value: v }),
3116 alt((
3117 parse_argname_and_value("ADDRESS", &located_expr),
3118 parse_argname_and_value("ADDR", &located_expr)
3119 ))
3120 .map(|(k, v)| BreakPointArgument::Address { arg: k, value: v }),
3121 parse_argname_and_value("MASK", &located_expr)
3122 .map(|(k, v)| BreakPointArgument::Mask { arg: k, value: v }),
3123 parse_argname_and_value("SIZE", &located_expr)
3124 .map(|(k, v)| BreakPointArgument::Size { arg: k, value: v }),
3125 parse_argname_and_value("VALUE", &located_expr)
3126 .map(|(k, v)| BreakPointArgument::Value { arg: k, value: v }),
3127 parse_argname_and_value("VALMASK", &located_expr)
3128 .map(|(k, v)| BreakPointArgument::ValueMask { arg: k, value: v }),
3129 parse_argname_and_value("STEP", &located_expr)
3130 .map(|(k, v)| BreakPointArgument::Step { arg: k, value: v }),
3131 parse_argname_and_value("CONDITION", &located_expr)
3132 .map(|(k, v)| BreakPointArgument::Condition { arg: k, value: v }),
3133 parse_argname_and_value("NAME", &located_expr)
3134 .map(|(k, v)| BreakPointArgument::Name { arg: k, value: v })
3135 ))
3136 .parse_next(input)
3137}
3138
3139pub fn parse_breakpoint_type_value(
3140 input: &mut InnerZ80Span
3141) -> ModalResult<RemuBreakPointType, Z80ParserError> {
3142 parse_convertible_word(input)
3143}
3144
3145pub fn parse_breakpoint_access_value(
3146 input: &mut InnerZ80Span
3147) -> ModalResult<RemuBreakPointAccessMode, Z80ParserError> {
3148 parse_convertible_word(input)
3149}
3150
3151pub fn parse_breakpoint_run_value(
3152 input: &mut InnerZ80Span
3153) -> ModalResult<RemuBreakPointRunMode, Z80ParserError> {
3154 parse_convertible_word(input)
3155}
3156
3157#[cfg_attr(not(target_arch = "wasm32"), inline(always))]
3158#[cfg_attr(target_arch = "wasm32", inline(never))]
3159pub fn parse_convertible_word<T: FromStr>(
3160 input: &mut InnerZ80Span
3161) -> ModalResult<T, Z80ParserError> {
3162 delimited(my_space0, alpha1, my_space0)
3163 .verify_map(|word| T::from_str(unsafe { std::str::from_utf8_unchecked(word) }).ok())
3164 .parse_next(input)
3165}
3166
3167pub fn parse_argname_to_assign(
3168 argname: &str
3169) -> impl Fn(&mut InnerZ80Span) -> ModalResult<InnerZ80Span, Z80ParserError> + use<'_> {
3170 #[cfg_attr(not(target_arch = "wasm32"), inline)]
3171 #[cfg_attr(target_arch = "wasm32", inline(never))]
3172 move |input: &mut InnerZ80Span| {
3173 let val = Caseless(argname).parse_next(input)?;
3174 let val = (*input).update_slice(val);
3175
3176 (my_space0, '=', my_space0)
3177 .map(|(..)| val)
3178 .parse_next(input)
3179 }
3180}
3181
3182pub fn parse_argname_and_value<'f, 's, O>(
3183 argname: &'s str,
3184 valparser: &'f dyn Fn(&mut InnerZ80Span) -> ModalResult<O, Z80ParserError>
3185) -> impl Fn(&mut InnerZ80Span) -> ModalResult<(InnerZ80Span, O), Z80ParserError> + use<'f, 's, O> {
3186 move |input: &mut InnerZ80Span| (parse_argname_to_assign(argname), valparser).parse_next(input)
3187}
3188
3189pub fn parse_optional_argname_and_value<'f, 's, O>(
3190 argname: &'s str,
3191 valparser: &'f dyn Fn(&mut InnerZ80Span) -> ModalResult<O, Z80ParserError>
3192) -> impl Fn(&mut InnerZ80Span) -> ModalResult<(Option<InnerZ80Span>, O), Z80ParserError> + use<'f, 's, O>
3193{
3194 move |input: &mut InnerZ80Span| {
3195 alt((
3196 (
3197 parse_argname_to_assign(argname),
3198 cut_err(valparser.context(StrContext::Label("Wrong value for argument")))
3199 )
3200 .map(|(a, r)| (Some(a), r)),
3201 (valparser).map(|r| (None, r))
3202 ))
3203 .parse_next(input)
3204 }
3205}
3206
3207pub fn parse_bankset(input: &mut InnerZ80Span) -> ModalResult<LocatedTokenInner, Z80ParserError> {
3208 let count = located_expr.parse_next(input)?;
3209
3210 Ok(LocatedTokenInner::Bankset(count))
3211}
3212
3213pub fn parse_buildsna(
3214 directive_name_parsed: bool
3215) -> impl Fn(&mut InnerZ80Span) -> ModalResult<LocatedTokenInner, Z80ParserError> {
3216 move |input: &mut InnerZ80Span| -> ModalResult<LocatedTokenInner, Z80ParserError> {
3217 if !directive_name_parsed {
3218 parse_word(b"BUILDSNA").parse_next(input)?;
3219 }
3220
3221 terminated(
3222 cut_err(opt(alt((
3223 Caseless("V2").value(SnapshotVersion::V2),
3224 Caseless("V3").value(SnapshotVersion::V3)
3225 ))))
3226 .map(|v: Option<SnapshotVersion>| LocatedTokenInner::BuildSna(v)),
3227 not(alphanumeric1)
3228 )
3229 .parse_next(input)
3230 }
3231}
3232
3233#[derive(PartialEq)]
3234pub enum RunEnt {
3235 Run,
3236 Ent
3237}
3238
3239#[cfg_attr(not(target_arch = "wasm32"), inline)]
3240#[cfg_attr(target_arch = "wasm32", inline(never))]
3241pub fn parse_run(kind: RunEnt) -> impl Parser<InnerZ80Span, LocatedTokenInner, Z80ParserError> {
3242 move |input: &mut InnerZ80Span| -> ModalResult<LocatedTokenInner, Z80ParserError> {
3243 let exp = cut_err(located_expr.context(match &kind {
3244 RunEnt::Run => "RUN expects at least one expression (e.g. RUN $)",
3245 RunEnt::Ent => "ENT expects one expression"
3246 }))
3247 .parse_next(input)?;
3248
3249 let ga = if kind == RunEnt::Run {
3250 opt(preceded((my_space0, (','), my_space0), located_expr)).parse_next(input)?
3251 }
3252 else {
3253 None
3254 };
3255
3256 Ok(LocatedTokenInner::Run(exp, ga))
3257 }
3258}
3259
3260macro_rules! directive_with_expr {
3261 ($name:ident, $enum:tt) => {
3262 #[cfg_attr(not(target_arch = "wasm32"), inline)]
3263 #[cfg_attr(target_arch = "wasm32", inline(never))]
3264 pub fn $name(input: &mut InnerZ80Span) -> ModalResult<LocatedTokenInner, Z80ParserError> {
3265 let exp = located_expr.parse_next(input)?;
3266
3267 Ok((LocatedTokenInner::$enum(exp)))
3268 }
3269 };
3270}
3271
3272directive_with_expr!(parse_map, Map);
3273directive_with_expr!(parse_limit, Limit);
3274directive_with_expr!(parse_waitnops, WaitNops);
3275directive_with_expr!(parse_return, Return);
3276
3277pub fn parse_startingindex(
3278 input: &mut InnerZ80Span
3279) -> ModalResult<LocatedTokenInner, Z80ParserError> {
3280 let start = opt(located_expr).parse_next(input)?;
3281 let step = opt(preceded(parse_comma, located_expr)).parse_next(input)?;
3282
3283 Ok(LocatedTokenInner::StartingIndex { start, step })
3284}
3285
3286pub fn parse_assembler_control(
3287 input: &mut InnerZ80Span
3288) -> ModalResult<LocatedTokenInner, Z80ParserError> {
3289 cut_err(
3290 alt((
3291 parse_assembler_control_print_parse,
3292 parse_assembler_control_print_any_pass
3293 ))
3294 .context(StrContext::Label(
3295 "Wrong argument in ASSEMBLING_CONTROL directive"
3296 ))
3297 )
3298 .parse_next(input)
3299}
3300
3301#[cfg_attr(not(target_arch = "wasm32"), inline)]
3302#[cfg_attr(target_arch = "wasm32", inline(never))]
3303pub fn parse_assembler_control_max_passes_number(
3304 input: &mut InnerZ80Span
3305) -> ModalResult<LocatedToken, Z80ParserError> {
3306 let asmctrl_start = input.checkpoint();
3307
3308 let _ = preceded(
3309 my_space0,
3310 alt((parse_directive_word(b"ASMCONTROLENV"), my_space1))
3311 )
3312 .parse_next(input)?;
3313
3314 let count = cut_err(preceded(
3315 (
3316 parse_word(b"SET_MAX_NB_OF_PASSES")
3317 .context(StrContext::Label("Missing modified option")),
3318 (my_space0, b'=', my_space0).context(StrContext::Label("Missing ="))
3319 ),
3320 located_expr.context(StrContext::Label("Expression expected"))
3321 ))
3322 .parse_next(input)?;
3323
3324 let inner = cut_err(inner_code.context(StrContext::Label(
3325 "ASMCONTROLENV SET_MAX_NB_OF_PASSES: issue in the content"
3326 )))
3327 .parse_next(input)?;
3328
3329 let _ = cut_err(
3330 preceded(
3331 my_space0,
3332 alt((
3333 parse_directive_word(b"ENDASMCONTROLENV"),
3334 parse_directive_word(b"ENDA")
3335 ))
3336 )
3337 .context(StrContext::Label("REPEAT: not closed"))
3338 )
3339 .parse_next(input)?;
3340
3341 Ok(LocatedTokenInner::AssemblerControl(
3342 LocatedAssemblerControlCommand::RestrictedAssemblingEnvironment {
3343 passes: Some(count),
3344 lst: inner
3345 }
3346 )
3347 .into_located_token_between(&asmctrl_start, *input))
3348}
3349
3350#[cfg_attr(not(target_arch = "wasm32"), inline)]
3351#[cfg_attr(target_arch = "wasm32", inline(never))]
3352pub fn parse_assembler_control_print_any_pass(
3353 input: &mut InnerZ80Span
3354) -> ModalResult<LocatedTokenInner, Z80ParserError> {
3355 preceded(
3356 (parse_word(b"PRINT_ANY_PASS"), parse_comma),
3357 parse_print_inner
3358 )
3359 .map(|p| {
3360 LocatedTokenInner::AssemblerControl(LocatedAssemblerControlCommand::PrintAtAssemblingState(
3361 p
3362 ))
3363 })
3364 .parse_next(input)
3365}
3366
3367#[cfg_attr(not(target_arch = "wasm32"), inline)]
3368#[cfg_attr(target_arch = "wasm32", inline(never))]
3369pub fn parse_assembler_control_print_parse(
3370 input: &mut InnerZ80Span
3371) -> ModalResult<LocatedTokenInner, Z80ParserError> {
3372 let input2: InnerZ80Span = *input;
3373
3374 preceded((parse_word(b"PRINT_PARSE"), parse_comma), parse_print_inner)
3375 .map(|p| {
3376 let msg = p.iter().map(|e| e.to_string()).join("");
3377 let ctx = input
3378 .state
3379 .current_filename
3380 .as_ref()
3381 .map(|p| p.to_string())
3382 .unwrap_or_else(|| {
3383 input
3384 .state
3385 .context_name()
3386 .map(|c| c.to_owned())
3387 .unwrap_or_default()
3388 });
3389 let (line, column) = Z80Span::from(input2).relative_line_and_column();
3390 println!("[PARSE] {ctx}:{line}:{column} {msg}");
3391 p
3392 })
3393 .map(|p| {
3394 LocatedTokenInner::AssemblerControl(
3395 LocatedAssemblerControlCommand::PrintAtParsingState(p)
3396 )
3397 })
3398 .parse_next(input)
3399}
3400
3401pub fn parse_stable_ticker(
3403 input: &mut InnerZ80Span
3404) -> ModalResult<LocatedTokenInner, Z80ParserError> {
3405 alt((parse_stable_ticker_start, parse_stable_ticker_stop)).parse_next(input)
3406}
3407
3408#[cfg_attr(not(target_arch = "wasm32"), inline)]
3410#[cfg_attr(target_arch = "wasm32", inline(never))]
3411pub fn parse_stable_ticker_start(
3412 input: &mut InnerZ80Span
3413) -> ModalResult<LocatedTokenInner, Z80ParserError> {
3414 preceded(
3415 (Caseless("start"), alt((my_space1, parse_comma))),
3416 cut_err(parse_label(false).context(StrContext::Label("Missing label")))
3417 )
3418 .map(|name| LocatedTokenInner::StableTicker(StableTickerAction::<Z80Span>::Start(name.into())))
3419 .parse_next(input)
3420}
3421
3422#[cfg_attr(not(target_arch = "wasm32"), inline)]
3424#[cfg_attr(target_arch = "wasm32", inline(never))]
3425pub fn parse_stable_ticker_stop(
3426 input: &mut InnerZ80Span
3427) -> ModalResult<LocatedTokenInner, Z80ParserError> {
3428 Caseless("stop").parse_next(input)?;
3429
3430 let name = opt(preceded(
3431 alt((my_space1, parse_comma)),
3432 parse_label(false).map(Z80Span::from)
3433 ))
3434 .parse_next(input)?;
3435
3436 Ok(LocatedTokenInner::StableTicker(
3437 StableTickerAction::<Z80Span>::Stop(name)
3438 ))
3439}
3440
3441#[cfg_attr(not(target_arch = "wasm32"), inline)]
3442#[cfg_attr(target_arch = "wasm32", inline(never))]
3443pub fn parse_bank(input: &mut InnerZ80Span) -> ModalResult<LocatedTokenInner, Z80ParserError> {
3444 let count = opt(located_expr).parse_next(input)?;
3445
3446 Ok(LocatedTokenInner::Bank(count))
3447}
3448
3449#[cfg_attr(not(target_arch = "wasm32"), inline)]
3450#[cfg_attr(target_arch = "wasm32", inline(never))]
3451pub fn parse_skip(input: &mut InnerZ80Span) -> ModalResult<LocatedTokenInner, Z80ParserError> {
3452 let count = cut_err(located_expr.context(StrContext::Label("SKIP: wrong expression")))
3453 .parse_next(input)?;
3454
3455 Ok(LocatedTokenInner::Skip(count))
3456}
3457
3458#[cfg_attr(not(target_arch = "wasm32"), inline)]
3460#[cfg_attr(target_arch = "wasm32", inline(never))]
3461pub fn parse_ld(
3462 mnemonic_name_parsed: bool
3463) -> impl Fn(&mut InnerZ80Span) -> ModalResult<LocatedTokenInner, Z80ParserError> {
3464 #[cfg_attr(not(target_arch = "wasm32"), inline)]
3465 #[cfg_attr(target_arch = "wasm32", inline(never))]
3466 move |input: &mut InnerZ80Span| -> ModalResult<LocatedTokenInner, Z80ParserError> {
3467 alt((
3468 parse_ld_fake(mnemonic_name_parsed),
3469 parse_ld_normal(mnemonic_name_parsed)
3470 ))
3471 .parse_next(input)
3472 }
3473}
3474#[cfg_attr(not(target_arch = "wasm32"), inline)]
3476#[cfg_attr(target_arch = "wasm32", inline(never))]
3477pub fn parse_ld_fake(
3478 mnemonic_name_parsed: bool
3479) -> impl Fn(&mut InnerZ80Span) -> ModalResult<LocatedTokenInner, Z80ParserError> {
3480 #[cfg_attr(not(target_arch = "wasm32"), inline)]
3481 #[cfg_attr(target_arch = "wasm32", inline(never))]
3482 move |input: &mut InnerZ80Span| -> ModalResult<LocatedTokenInner, Z80ParserError> {
3483 if !mnemonic_name_parsed {
3484 terminated(parse_word(b"LD"), my_space1).parse_next(input)?;
3485 }
3486
3487 let dst = alt((
3488 terminated(
3489 alt((parse_register16, parse_indexregister16)),
3490 not(alt((Caseless(".low"), Caseless(".high"))))
3491 ),
3492 parse_hl_address,
3493 parse_indexregister_with_index
3494 ))
3495 .parse_next(input)?;
3496
3497 let _ = parse_comma(input)?;
3498
3499 let src = if dst.is_register16() {
3501 alt((
3502 terminated(
3503 alt((parse_register16, parse_indexregister16)),
3504 not(alt((Caseless(".low"), Caseless(".high"))))
3505 ),
3506 parse_hl_address,
3507 parse_indexregister_with_index
3508 ))
3509 .parse_next(input)?
3510 }
3511 else
3512 {
3514 terminated(
3515 parse_register16,
3516 not(alt((Caseless(".low"), Caseless(".high"))))
3517 )
3518 .parse_next(input)?
3519 };
3520
3521 let token = LocatedTokenInner::new_opcode(Mnemonic::Ld, Some(dst), Some(src));
3522
3523 let warning = LocatedTokenInner::WarningWrapper(
3524 Box::new(token),
3525 "This is a fake instruction assembled using several opcodes".into()
3526 );
3527
3528 Ok(warning)
3529 }
3530}
3531#[cfg_attr(not(target_arch = "wasm32"), inline)]
3533#[cfg_attr(target_arch = "wasm32", inline(never))]
3534pub fn parse_ld_normal(
3535 mnemonic_name_parsed: bool
3536) -> impl Fn(&mut InnerZ80Span) -> ModalResult<LocatedTokenInner, Z80ParserError> {
3537 move |input: &mut InnerZ80Span| -> ModalResult<LocatedTokenInner, Z80ParserError> {
3538 if !mnemonic_name_parsed {
3539 parse_word(b"LD").parse_next(input)?;
3540 }
3541
3542 let _start = *input;
3543 let dst = cut_err(
3544 alt((
3545 parse_reg_address,
3546 parse_indexregister_address,
3547 parse_indexregister_with_index,
3548 parse_register_sp,
3549 terminated(
3550 parse_register16,
3551 not(alt((Caseless(".low"), Caseless(".high"))))
3552 ),
3553 parse_register8,
3554 parse_indexregister16,
3555 parse_indexregister8,
3556 parse_register_i,
3557 parse_register_r,
3558 parse_hl_address,
3559 parse_address
3560 ))
3561 .context(LD_WRONG_DESTINATION)
3562 )
3563 .parse_next(input)?;
3564
3565 let _ = cut_err(parse_comma.context(StrContext::Label("LD: missing comma")))
3566 .parse_next(input)?;
3567
3568 let src = cut_err(cut_err(parse_ld_normal_src(&dst)))
3570 .context(LD_WRONG_SOURCE)
3571 .parse_next(input)?;
3572
3573 let token = LocatedTokenInner::new_opcode(Mnemonic::Ld, Some(dst), Some(src));
3574
3575 Ok(token)
3576 }
3577}
3578#[cfg_attr(not(target_arch = "wasm32"), inline)]
3580#[cfg_attr(target_arch = "wasm32", inline(never))]
3581fn parse_ld_normal_src(
3582 dst: &LocatedDataAccess
3583) -> impl Fn(&mut InnerZ80Span) -> ModalResult<LocatedDataAccess, Z80ParserError> + '_ {
3584 move |input: &mut InnerZ80Span| {
3585 let input_start = input.checkpoint();
3586 if dst.is_register_sp() {
3587 alt((
3588 parse_register_hl,
3589 parse_indexregister16,
3590 parse_address,
3591 parse_expr
3592 ))
3593 .parse_next(input)
3594 }
3595 else if dst.is_address_in_register16() || dst.is_address_in_indexregister16() {
3596 alt((parse_register8, parse_expr)).parse_next(input)
3598 }
3599 else if dst.is_register16() | dst.is_indexregister16() {
3600 alt((parse_address, parse_expr)).parse_next(input)
3601 }
3602 else if dst.is_register8() {
3603 if dst.is_register_a() {
3605 alt((
3606 parse_indexregister_with_index,
3607 parse_reg_address,
3608 parse_indexregister_address,
3609 parse_address,
3610 parse_register8,
3611 parse_indexregister8,
3612 parse_register_i,
3613 parse_register_r,
3614 parse_expr
3615 ))
3616 .parse_next(input)
3617 }
3618 else {
3619 alt((
3620 parse_indexregister_address,
3621 parse_indexregister_with_index,
3622 parse_hl_address,
3623 parse_address,
3624 parse_register8,
3625 parse_indexregister8.verify(|src| {
3626 !((dst.is_register_h() || dst.is_register_l())
3627 && (src.is_register_ixl()
3628 || src.is_register_ixh()
3629 || src.is_register_ixl()
3630 || src.is_register_ixh()))
3631 }),
3632 parse_expr
3633 ))
3634 .parse_next(input)
3635 }
3636 }
3637 else if dst.is_indexregister8() {
3638 alt((
3639 parse_indexregister_address,
3640 parse_indexregister_with_index,
3641 parse_hl_address,
3642 parse_address,
3643 parse_register8,
3644 (alt((parse_register_ixh, parse_register_ixl))
3645 .verify(|_| dst.is_register_ixl() || dst.is_register_ixh())),
3646 (alt((parse_register_iyh, parse_register_iyl))
3647 .verify(|_| dst.is_register_iyl() || dst.is_register_iyh())),
3648 parse_expr
3649 ))
3650 .parse_next(input)
3651 }
3652 else if dst.is_memory() {
3653 alt((
3654 parse_register16,
3655 parse_register8,
3656 parse_register_sp,
3657 parse_indexregister16
3658 ))
3659 .parse_next(input)
3660 }
3661 else if dst.is_address_in_register16() {
3662 parse_register8(input)
3663 }
3664 else if dst.is_indexregister_with_index() {
3665 alt((parse_register8, parse_expr)).parse_next(input)
3666 }
3667 else if dst.is_register_i() || dst.is_register_r() {
3668 parse_register_a(input)
3669 }
3670 else {
3671 input.reset(&input_start);
3672 Err(ErrMode::Backtrack(Z80ParserError::from_input(input)))
3673 }
3674 }
3675}
3676
3677#[cfg_attr(not(target_arch = "wasm32"), inline)]
3679#[cfg_attr(target_arch = "wasm32", inline(never))]
3680pub fn parse_res_set_bit(
3681 res_or_set: Mnemonic
3682) -> impl Fn(&mut InnerZ80Span) -> ModalResult<LocatedTokenInner, Z80ParserError> {
3683 move |input: &mut InnerZ80Span| -> ModalResult<LocatedTokenInner, Z80ParserError> {
3684 let bit = cut_err(parse_expr.context(StrContext::Label("Wrong bit definition")))
3685 .parse_next(input)?;
3686
3687 let _ = cut_err(parse_comma).parse_next(input)?;
3688
3689 let operand = cut_err(
3690 alt((
3691 parse_register8,
3692 parse_hl_address,
3693 parse_indexregister_with_index
3694 ))
3695 .context(StrContext::Label("Wrong destination"))
3696 )
3697 .parse_next(input)?;
3698
3699 let hidden_arg = if res_or_set == Mnemonic::Bit {
3702 None
3703 }
3704 else {
3705 opt(preceded(parse_comma, parse_register8)).parse_next(input)?
3706 };
3707
3708 Ok(LocatedTokenInner::OpCode(
3709 res_or_set,
3710 Some(bit),
3711 Some(operand),
3712 hidden_arg.map(|d| d.get_register8().unwrap())
3713 ))
3714 }
3715}
3716
3717#[cfg_attr(not(target_arch = "wasm32"), inline)]
3719#[cfg_attr(target_arch = "wasm32", inline(never))]
3720pub fn parse_cp(input: &mut InnerZ80Span) -> ModalResult<LocatedTokenInner, Z80ParserError> {
3721 preceded(
3725 opt((parse_register_a, parse_comma)),
3726 cut_err(
3727 alt((
3728 parse_register8,
3729 parse_indexregister8,
3730 parse_hl_address,
3731 parse_indexregister_with_index,
3732 parse_expr
3733 ))
3734 .context(StrContext::Label("CP: wrong argument"))
3735 )
3736 .map(
3737 |operand| LocatedTokenInner::new_opcode(Mnemonic::Cp, Some(operand), None)
3739 )
3740 )
3741 .parse_next(input)
3742}
3743
3744#[derive(PartialEq)]
3745pub enum ExportKind {
3746 Export,
3747 NoExport
3748}
3749
3750#[cfg_attr(not(target_arch = "wasm32"), inline)]
3751#[cfg_attr(target_arch = "wasm32", inline(never))]
3752pub fn parse_export(
3753 code: ExportKind
3754) -> impl Fn(&mut InnerZ80Span) -> ModalResult<LocatedTokenInner, Z80ParserError> {
3755 #[cfg_attr(not(target_arch = "wasm32"), inline)]
3756 #[cfg_attr(target_arch = "wasm32", inline(never))]
3757 move |input: &mut InnerZ80Span| -> ModalResult<LocatedTokenInner, Z80ParserError> {
3758 let labels: Vec<InnerZ80Span> = cut_err(
3759 separated(0.., parse_label(false), parse_comma)
3760 .context(StrContext::Label("Wrong parameters"))
3761 )
3762 .parse_next(input)?;
3763 let labels = labels.into_iter().map(Z80Span::from).collect_vec();
3764
3765 if code == ExportKind::Export {
3766 Ok(LocatedTokenInner::Export(labels))
3767 }
3768 else {
3769 Ok(LocatedTokenInner::NoExport(labels))
3770 }
3771 }
3772}
3773
3774#[derive(PartialEq)]
3775pub enum DbDwStr {
3776 Abyte,
3777 Db,
3778 Dw,
3779 Str
3780}
3781
3782#[cfg_attr(not(target_arch = "wasm32"), inline)]
3783#[cfg_attr(target_arch = "wasm32", inline(never))]
3784pub fn parse_db_or_dw_or_str(
3786 code: DbDwStr,
3787 empty_list_allowed: bool
3788) -> impl Fn(&mut InnerZ80Span) -> ModalResult<LocatedTokenInner, Z80ParserError> {
3789 move |input: &mut InnerZ80Span| -> ModalResult<LocatedTokenInner, Z80ParserError> {
3790 let abyte_delta = if code == DbDwStr::Abyte {
3791 Some(
3792 cut_err(
3793 terminated(located_expr, parse_comma)
3794 .context(StrContext::Label("ABYTE: delta issue"))
3795 )
3796 .parse_next(input)?
3797 )
3798 }
3799 else {
3800 None
3801 };
3802
3803 let expr = if empty_list_allowed {
3805 expr_list.parse_next(input).unwrap_or(Default::default())
3806 }
3807 else {
3808 expr_list
3809 .context(match code {
3810 DbDwStr::Abyte => "ABYTE: error in arguments",
3811 DbDwStr::Dw => "DEFW: error in arguments",
3812 DbDwStr::Db => "DEFB: error in arguments",
3813 DbDwStr::Str => "STR: error in arguments"
3814 })
3815 .parse_next(input)?
3816 };
3817
3818 Ok(match code {
3819 DbDwStr::Db => LocatedTokenInner::Defb(expr),
3820 DbDwStr::Dw => LocatedTokenInner::Defw(expr),
3821 DbDwStr::Str => LocatedTokenInner::Str(expr),
3822 DbDwStr::Abyte => LocatedTokenInner::Abyte(abyte_delta.unwrap(), expr)
3823 })
3824 }
3825}
3826
3827#[cfg_attr(not(target_arch = "wasm32"), inline)]
3829#[cfg_attr(target_arch = "wasm32", inline(never))]
3830pub fn parse_forbidden_keyword(
3831 input: &mut InnerZ80Span
3832) -> ModalResult<InnerZ80Span, Z80ParserError> {
3833 let start = input.checkpoint();
3834 let _ = my_space0(input)?;
3835 let name = take_while(1.., ('a'..='z', 'A'..='Z', '0'..='9', '_'..='_'))
3836 .context(StrContext::Label("Unable to read directive name"))
3837 .parse_next(input)?;
3838
3839 let mut end_directive_iter = if input.state.options().dotted_directive {
3840 DOTTED_END_DIRECTIVE.iter()
3841 }
3842 else {
3843 END_DIRECTIVE.iter()
3844 };
3845
3846 let name = (*input).update_slice(name);
3847
3848 if !end_directive_iter.any(|&a| a == name.to_ascii_uppercase()) {
3849 input.reset(&start);
3850 return Err(ErrMode::Backtrack(Z80ParserError::from_input(&name)));
3851 }
3852
3853 let _ = my_space0(input)?;
3854
3855 Ok(name)
3856}
3857pub fn parse_macro_arg(input: &mut InnerZ80Span) -> ModalResult<LocatedMacroParam, Z80ParserError> {
3858 let _start_input = input.checkpoint();
3859 let cloned = *input;
3860
3861 let param = alt((
3862 delimited(
3863 (my_space0, ('[')),
3864 separated(0.., parse_macro_arg, ','),
3865 ((']'), my_space0)
3866 )
3867 .map(|l: Vec<LocatedMacroParam>| {
3868 LocatedMacroParam::List(
3869 l.into_iter()
3870 .map(|p| Box::new(p.clone()))
3871 .collect::<Vec<_>>()
3872 )
3873 }),
3874 delimited(
3875 my_space0,
3876 (
3877 opt(Caseless("{eval}").value(())),
3878 alt((
3879 located_expr.take(), parse_string.take(),
3881 repeat::<_, _, (), _, _>(
3882 0..,
3883 none_of((b' ', b',', b'\r', b'\n', b'\t', b']', b'[', b';', b':'))
3884 )
3885 .take()
3886 ))
3887 ), alt((my_space0.value(()), eof.value(())))
3889 )
3890 .map(|(eval, s)| (eval.is_some(), cloned.update_slice(s)))
3891 .map(|(eval, arg)| (eval, Z80Span::from(arg)))
3892 .map(|(eval, arg)| {
3893 if eval {
3894 LocatedMacroParam::EvaluatedArgument(arg)
3895 }
3896 else {
3897 LocatedMacroParam::RawArgument(arg)
3898 }
3899 })
3900 ))
3901 .parse_next(input)?;
3902
3903 Ok(param)
3904}
3905
3906#[cfg_attr(not(target_arch = "wasm32"), inline)]
3908#[cfg_attr(target_arch = "wasm32", inline(never))]
3909pub fn parse_macro_or_struct_call_inner(
3910 for_struct: bool,
3911 name: InnerZ80Span
3912) -> impl Fn(&mut InnerZ80Span) -> ModalResult<LocatedTokenInner, Z80ParserError> {
3913 move |input: &mut InnerZ80Span| {
3914 let input_start = input.checkpoint();
3915
3916 my_space0.parse_next(input)?;
3917 not(':').parse_next(input)?;
3918
3919 if !ignore_ascii_case_allowed_label(
3920 name.as_bstr(),
3921 input.state.options().dotted_directive,
3922 input.state.options().assembler_flavor
3923 ) {
3924 return Err(ErrMode::Backtrack(
3925 Z80ParserError::from_input(input).add_context(
3926 input,
3927 &input_start,
3928 if for_struct {
3929 "STRUCT: forbidden name"
3930 }
3931 else {
3932 "MACRO or STRUCT: forbidden name"
3933 }
3934 )
3935 ));
3936 }
3937
3938 let _ = (my_space0, not(parse_comment)).parse_next(input)?;
3954
3955 let has_parenthesis = opt((
3956 '(',
3957 my_space0,
3958 not(alt((("void", my_space0).value(()), ')'.value(()))))
3959 ))
3960 .parse_next(input)?
3961 .is_some();
3962 let args: Vec<(LocatedMacroParam, &[u8])> = if peek(alt((
3963 eof::<_, Z80ParserError>.value(()),
3964 '\n'.value(()),
3965 ':'.value(())
3966 )))
3967 .parse_next(input)
3968 .is_ok()
3969 {
3970 vec![]
3971 }
3972 else {
3973 cut_err(
3974 alt((
3975 delimited(my_space0, alt(("()", Caseless("(void)"))), my_space0)
3976 .value(Default::default()),
3977 alt((
3978 alt((Caseless("(void)"), "()")).value(Vec::new()),
3979 separated(
3980 1..,
3981 alt((
3982 parse_macro_arg.with_taken(),
3983 my_space1
3984 .map(|space: InnerZ80Span| {
3985 LocatedMacroParam::RawArgument(space.into())
3986 })
3988 .with_taken()
3989 )),
3990 parse_comma
3991 )
3992 ))
3993 ))
3994 .context(if for_struct {
3995 "STRUCT: error in arguments list"
3996 }
3997 else {
3998 "MACRO or STRUCT: forbidden name"
3999 })
4000 )
4001 .parse_next(input)?
4002 };
4003
4004 if has_parenthesis {
4005 (my_space0, ')', my_space0).parse_next(input)?;
4006 }
4007
4008 if args.len() == 1 && args.first().unwrap().0.is_empty() {
4009 panic!();
4010 }
4011
4012 if args.len() == 1 {
4014 let mut arg = (*input).update_slice(args[0].1);
4015 if alt((parse_word(b"NOP").take(), parse_opcode_no_arg.take()))
4016 .parse_next(&mut arg)
4017 .is_ok()
4018 {
4019 return Err(ErrMode::Cut(Z80ParserError::from_input(input).add_context(
4020 input,
4021 &input_start,
4022 if for_struct {
4023 "First argument of STRUCT cannot be an opcode with no argument"
4024 }
4025 else {
4026 "First argument of MACRO or STRUCT cannot be an opcode with no argument"
4027 }
4028 )));
4029 }
4030 }
4031
4032 let args = args.into_iter().map(|(a, _b)| a).collect_vec();
4033 Ok(LocatedTokenInner::MacroCall(name.into(), args))
4034 }
4035}
4036
4037#[cfg_attr(not(target_arch = "wasm32"), inline)]
4038#[cfg_attr(target_arch = "wasm32", inline(never))]
4039pub fn parse_macro_or_struct_call(
4041 allowed_to_return_a_label: bool,
4042 for_struct: bool
4043) -> impl Fn(&mut InnerZ80Span) -> ModalResult<LocatedToken, Z80ParserError> {
4044 move |input: &mut InnerZ80Span| -> ModalResult<LocatedToken, Z80ParserError> {
4045 my_space0(input)?;
4046 let input_start = input.checkpoint();
4047 let name = terminated(
4048 parse_macro_name,
4049 not(alt((
4050 (
4051 my_space0,
4052 alt((':'.value(()), line_ending.value(()), eof.value(())))
4053 )
4054 .take(),
4055 ('.').take()
4056 )))
4057 )
4058 .parse_next(input)?;
4059
4060 if !ignore_ascii_case_allowed_label(
4062 name.as_bstr(),
4063 input.state.options().dotted_directive,
4064 input.state.options().assembler_flavor
4065 ) {
4066 return Err(ErrMode::Backtrack(
4067 Z80ParserError::from_input(input).add_context(
4068 input,
4069 &input_start,
4070 if for_struct {
4071 "STRUCT: forbidden name"
4072 }
4073 else {
4074 "MACRO or STRUCT: forbidden name"
4075 }
4076 )
4077 ));
4078 }
4079
4080 let inner = parse_macro_or_struct_call_inner(for_struct, name).parse_next(input)?;
4081 let inner = inner.into_located_token_between(&input_start, *input);
4082 Ok(inner)
4083 }
4084}
4085
4086#[cfg_attr(not(target_arch = "wasm32"), inline)]
4087#[cfg_attr(target_arch = "wasm32", inline(never))]
4088fn parse_directive_word(
4089 name: &'static [u8]
4090) -> impl Fn(&mut InnerZ80Span) -> ModalResult<InnerZ80Span, Z80ParserError> + 'static {
4091 move |input: &mut InnerZ80Span| {
4092 if input.state.options().dotted_directive {
4093 preceded(b'.', parse_word(name)).parse_next(input)
4094 }
4095 else {
4096 parse_word(name).parse_next(input)
4097 }
4098 }
4099}
4100
4101#[cfg_attr(not(target_arch = "wasm32"), inline)]
4102#[cfg_attr(target_arch = "wasm32", inline(never))]
4103fn parse_word(
4105 name: &'static [u8]
4106) -> impl Fn(&mut InnerZ80Span) -> ModalResult<InnerZ80Span, Z80ParserError> {
4107 #[cfg_attr(not(target_arch = "wasm32"), inline)]
4108 #[cfg_attr(target_arch = "wasm32", inline(never))]
4109 move |input: &mut InnerZ80Span| -> ModalResult<InnerZ80Span, Z80ParserError> {
4110 let word = terminated(
4111 Caseless(name),
4112 alt((
4113 eof.value(()),
4114 (
4115 not(one_of((b'a'..=b'z', b'A'..=b'Z', b'0'..=b'9', b'_'))),
4116 my_space0
4117 )
4118 .value(())
4119 ))
4120 )
4121 .parse_next(input)?;
4122
4123 let word = (*input).update_slice(word);
4124 Ok(word)
4125 }
4126}
4127
4128#[cfg_attr(not(target_arch = "wasm32"), inline)]
4130#[cfg_attr(target_arch = "wasm32", inline(never))]
4131pub fn parse_djnz(input: &mut InnerZ80Span) -> ModalResult<LocatedTokenInner, Z80ParserError> {
4132 preceded(opt(parse_comma), parse_expr)
4133 .map(|expr| LocatedTokenInner::new_opcode(Mnemonic::Djnz, Some(expr), None))
4134 .parse_next(input)
4135}
4136
4137#[cfg_attr(not(target_arch = "wasm32"), inline)]
4139#[cfg_attr(target_arch = "wasm32", inline(never))]
4140pub fn expr_list(input: &mut InnerZ80Span) -> ModalResult<Vec<LocatedExpr>, Z80ParserError> {
4141 let mut exprs = Vec::new();
4142 loop {
4143 let expr = opt(located_expr).parse_next(input)?;
4144 match expr {
4145 Some(expr) => exprs.push(expr),
4146 None => break
4147 }
4148
4149 let comma = opt(parse_comma).parse_next(input)?;
4150 match comma {
4151 Some(_) => {},
4152 None => break
4153 }
4154 }
4155
4156 Ok(exprs)
4157}
4158
4159pub fn parse_assert(input: &mut InnerZ80Span) -> ModalResult<LocatedTokenInner, Z80ParserError> {
4161 let expr = cut_err(located_expr.context(StrContext::Label("ASSERT: expression error")))
4162 .parse_next(input)?;
4163
4164 let exps = cut_err(
4165 opt(preceded(parse_comma, parse_print_inner))
4166 .context(StrContext::Label("ASSERT: comment error"))
4167 )
4168 .parse_next(input)?;
4169
4170 Ok(LocatedTokenInner::Assert(expr, exps))
4171}
4172
4173pub fn parse_align(input: &mut InnerZ80Span) -> ModalResult<LocatedTokenInner, Z80ParserError> {
4175 let boundary = located_expr.parse_next(input)?;
4176 let fill = opt(preceded(parse_comma, located_expr)).parse_next(input)?;
4177
4178 Ok(LocatedTokenInner::Align(boundary, fill))
4179}
4180
4181pub fn parse_print_inner(
4182 input: &mut InnerZ80Span
4183) -> ModalResult<Vec<FormattedExpr>, Z80ParserError> {
4184 separated(
4185 1..,
4186 alt((
4187 formatted_expr,
4188 expr.map(FormattedExpr::from),
4189 parse_string.map({
4190 |s: UnescapedString| {
4191 let s = s.as_ref();
4192 FormattedExpr::from(Expr::String(SmolStr::from_iter(s.chars())))
4193 }
4194 })
4195 )),
4196 parse_comma
4197 )
4198 .parse_next(input)
4199}
4200
4201#[cfg_attr(not(target_arch = "wasm32"), inline)]
4203#[cfg_attr(target_arch = "wasm32", inline(never))]
4204pub fn parse_print(
4205 directive_name_parsed: bool
4206) -> impl Fn(&mut InnerZ80Span) -> ModalResult<LocatedTokenInner, Z80ParserError> {
4207 #[cfg_attr(not(target_arch = "wasm32"), inline)]
4208 #[cfg_attr(target_arch = "wasm32", inline(never))]
4209 move |input: &mut InnerZ80Span| -> ModalResult<LocatedTokenInner, Z80ParserError> {
4210 if !directive_name_parsed {
4211 parse_word(b"PRINT").parse_next(input)?;
4212 }
4213
4214 cut_err(parse_print_inner)
4215 .map(LocatedTokenInner::Print)
4216 .parse_next(input)
4217 }
4218}
4219
4220pub fn parse_fail(
4221 directive_name_parsed: bool
4222) -> impl Fn(&mut InnerZ80Span) -> ModalResult<LocatedTokenInner, Z80ParserError> {
4223 move |input: &mut InnerZ80Span| -> ModalResult<LocatedTokenInner, Z80ParserError> {
4224 if !directive_name_parsed {
4225 parse_word(b"FAIL").parse_next(input)?;
4226 }
4227
4228 opt(parse_print_inner)
4229 .map(LocatedTokenInner::Fail)
4230 .parse_next(input)
4231 }
4232}
4233
4234#[cfg_attr(not(target_arch = "wasm32"), inline)]
4237#[cfg_attr(target_arch = "wasm32", inline(never))]
4238fn formatted_expr(input: &mut InnerZ80Span) -> ModalResult<FormattedExpr, Z80ParserError> {
4239 let _ = ('{').parse_next(input)?;
4240 let format = alt((
4241 Caseless("INT").value(ExprFormat::Int),
4242 Caseless("HEX4").value(ExprFormat::Hex(Some(4))),
4243 Caseless("HEX8").value(ExprFormat::Hex(Some(8))),
4244 Caseless("HEX2").value(ExprFormat::Hex(Some(2))),
4245 Caseless("HEX").value(ExprFormat::Hex(None)),
4246 Caseless("BIN8").value(ExprFormat::Bin(Some(8))),
4247 Caseless("BIN16").value(ExprFormat::Bin(Some(16))),
4248 Caseless("BIN32").value(ExprFormat::Bin(Some(32))),
4249 Caseless("BIN").value(ExprFormat::Bin(None))
4250 ))
4251 .parse_next(input)?;
4252 let _ = ('}').parse_next(input)?;
4253
4254 let _ = my_space0(input)?;
4255
4256 let exp = expr(input)?;
4257
4258 Ok(FormattedExpr::Formatted(format, exp))
4259}
4260
4261#[cfg_attr(not(target_arch = "wasm32"), inline)]
4263#[cfg_attr(target_arch = "wasm32", inline(never))]
4264pub fn my_space0(input: &mut InnerZ80Span) -> ModalResult<InnerZ80Span, Z80ParserError> {
4265 let cloned = *input;
4266 opt(my_space1)
4267 .take()
4268 .map(|s| cloned.update_slice(s))
4269 .parse_next(input)
4270}
4271
4272pub fn my_repeat1<I, O, C, E, F>(mut f: F) -> impl Parser<I, C, E>
4273where
4274 I: Stream,
4275 C: Accumulate<O>,
4276 F: Parser<I, O, E>,
4277 E: ParserError<I>
4278{
4279 move |i: &mut I| my_repeat1_(&mut f, i)
4280}
4281
4282#[cfg_attr(not(target_arch = "wasm32"), inline)]
4283#[cfg_attr(target_arch = "wasm32", inline(never))]
4284fn my_repeat1_<I, O, C, E, F>(f: &mut F, i: &mut I) -> ModalResult<C, E>
4285where
4286 I: Stream,
4287 C: Accumulate<O>,
4288 F: Parser<I, O, E>,
4289 E: ParserError<I>
4290{
4291 let start = i.checkpoint();
4292 match f.parse_next(i) {
4293 #[allow(deprecated)]
4294 Err(e) => Err(e.append(i, &start, ErrorKind::Many)),
4295 Ok(o) => {
4296 let mut acc = C::initial(None);
4297 acc.accumulate(o);
4298
4299 loop {
4300 let start = i.checkpoint();
4301 let len = i.eof_offset();
4302 match f.parse_next(i) {
4303 Err(ErrMode::Backtrack(_)) => {
4304 i.reset(&start);
4305 return Ok(acc);
4306 },
4307 Err(e) => return Err(e),
4308 Ok(o) => {
4309 if i.eof_offset() == len {
4311 return Ok(acc);
4312 }
4313
4314 acc.accumulate(o);
4315 }
4316 }
4317 }
4318 }
4319 }
4320}
4321
4322#[cfg_attr(not(target_arch = "wasm32"), inline)]
4324#[cfg_attr(target_arch = "wasm32", inline(never))]
4325pub fn my_space1(input: &mut InnerZ80Span) -> ModalResult<InnerZ80Span, Z80ParserError> {
4326 let cloned = *input;
4327
4328 let spaces = alt((
4329 eof.value(()).context(StrContext::Label("End of file")), one_of(|c: u8| c.is_space())
4331 .value(())
4332 .context(StrContext::Label("Space")), (
4334 space0,
4336 '\\',
4337 space0,
4338 opt(parse_comment),
4339 line_ending,
4340 space0
4341 )
4342 .value(())
4343 .context(StrContext::Label("continuated line")),
4344 parse_multiline_comment.value(())
4345 ));
4346
4347 my_repeat1::<_, _, (), Z80ParserError, _>(spaces)
4348 .take()
4349 .map(|s| cloned.update_slice(s))
4350 .parse_next(input)
4351}
4352
4353#[cfg_attr(not(target_arch = "wasm32"), inline)]
4354#[cfg_attr(target_arch = "wasm32", inline(never))]
4355fn my_line_ending(input: &mut InnerZ80Span) -> ModalResult<InnerZ80Span, Z80ParserError> {
4356 let cloned = *input;
4357 alt((line_ending.take(), ':'.take()))
4358 .map(|s| cloned.update_slice(s))
4359 .parse_next(input)
4360}
4361
4362#[cfg_attr(not(target_arch = "wasm32"), inline)]
4363#[cfg_attr(target_arch = "wasm32", inline(never))]
4364fn parse_comma(input: &mut InnerZ80Span) -> ModalResult<InnerZ80Span, Z80ParserError> {
4365 let cloned = *input;
4366 delimited(my_space0, ','.take(), my_space0)
4367 .map(|s| cloned.update_slice(s))
4368 .parse_next(input)
4369}
4370
4371pub fn parse_protect(input: &mut InnerZ80Span) -> ModalResult<LocatedTokenInner, Z80ParserError> {
4373 let start = located_expr.parse_next(input)?;
4374
4375 let end = preceded(parse_comma, located_expr).parse_next(input)?;
4376
4377 Ok(LocatedTokenInner::Protect(start, end))
4378}
4379
4380#[cfg_attr(not(target_arch = "wasm32"), inline)]
4381#[cfg_attr(target_arch = "wasm32", inline(never))]
4382pub fn parse_logical_operator(
4384 operator: Mnemonic
4385) -> impl Fn(&mut InnerZ80Span) -> ModalResult<LocatedTokenInner, Z80ParserError> {
4386 move |input: &mut InnerZ80Span| -> ModalResult<LocatedTokenInner, Z80ParserError> {
4387 let operand = alt((
4388 parse_register8,
4389 parse_indexregister8,
4390 parse_hl_address,
4391 parse_indexregister_with_index,
4392 parse_expr
4393 ))
4394 .context(StrContext::Label("Wrong logical operand"))
4395 .parse_next(input)?;
4396
4397 Ok(LocatedTokenInner::new_opcode(operator, Some(operand), None))
4398 }
4399}
4400
4401#[cfg_attr(not(target_arch = "wasm32"), inline)]
4403#[cfg_attr(target_arch = "wasm32", inline(never))]
4404pub fn parse_sub(input: &mut InnerZ80Span) -> ModalResult<LocatedTokenInner, Z80ParserError> {
4405 let _first = opt(terminated(parse_register_a, parse_comma)).parse_next(input)?;
4409
4410 let operand = alt((
4411 parse_register8,
4412 parse_indexregister8,
4413 parse_hl_address,
4414 parse_indexregister_with_index,
4415 parse_expr
4416 ))
4417 .parse_next(input)?;
4418
4419 Ok(LocatedTokenInner::new_opcode(
4420 Mnemonic::Sub,
4421 Some(operand),
4422 None
4423 ))
4424}
4425
4426#[cfg_attr(not(target_arch = "wasm32"), inline)]
4428#[cfg_attr(target_arch = "wasm32", inline(never))]
4429pub fn parse_sbc(input: &mut InnerZ80Span) -> ModalResult<LocatedTokenInner, Z80ParserError> {
4430 let opera = opt(terminated(
4434 alt((parse_register_a, parse_register_hl)),
4435 parse_comma
4436 ))
4437 .parse_next(input)?;
4438
4439 let operb = if opera.as_ref().map(|r| r.is_register_a()).unwrap_or(true) {
4440 alt((
4441 parse_register8,
4442 parse_indexregister8,
4443 parse_hl_address,
4444 parse_indexregister_with_index,
4445 parse_expr
4446 ))
4447 .parse_next(input)
4448 }
4449 else {
4450 alt((parse_register16, parse_register_sp)).parse_next(input)
4451 }?;
4452
4453 Ok(LocatedTokenInner::new_opcode(
4454 Mnemonic::Sbc,
4455 opera,
4456 Some(operb)
4457 ))
4458}
4459
4460#[cfg_attr(not(target_arch = "wasm32"), inline)]
4462#[cfg_attr(target_arch = "wasm32", inline(never))]
4463pub fn parse_add_or_adc(
4464 add_or_adc: Mnemonic
4465) -> impl Fn(&mut InnerZ80Span) -> ModalResult<LocatedTokenInner, Z80ParserError> {
4466 move |input: &mut InnerZ80Span| -> ModalResult<LocatedTokenInner, Z80ParserError> {
4467 let first = opt(terminated(
4468 alt((parse_register_a, parse_register_hl, parse_indexregister16)),
4469 parse_comma
4470 ))
4471 .parse_next(input)?;
4472
4473 let second = if first.as_ref().map(|f| f.is_register8()).unwrap_or(true) {
4474 alt((
4475 parse_register8,
4476 parse_indexregister8,
4477 parse_hl_address,
4478 parse_indexregister_with_index,
4479 parse_expr
4480 ))
4481 .parse_next(input)
4482 }
4483 else if first.as_ref().unwrap().is_register16() {
4484 alt((parse_register16, parse_register_sp)).parse_next(input) }
4486 else if first.as_ref().unwrap().is_indexregister16() {
4487 alt((
4488 parse_register_bc,
4489 parse_register_de,
4490 parse_register_hl,
4491 parse_register_sp,
4492 parse_register_ix.verify(|_| first.as_ref().unwrap().is_register_ix()),
4493 parse_register_iy.verify(|_| first.as_ref().unwrap().is_register_iy())
4494 ))
4495 .parse_next(input)
4496 }
4497 else {
4498 return Err(ErrMode::Cut(Z80ParserError::from_input(input)));
4499 }?;
4500
4501 Ok(LocatedTokenInner::new_opcode(
4502 add_or_adc,
4503 first,
4504 Some(second)
4505 ))
4506 }
4507}
4508
4509#[cfg_attr(not(target_arch = "wasm32"), inline)]
4511#[cfg_attr(target_arch = "wasm32", inline(never))]
4512pub fn parse_push_n_pop(
4513 push_or_pop: Mnemonic
4514) -> impl Fn(&mut InnerZ80Span) -> ModalResult<LocatedTokenInner, Z80ParserError> {
4515 move |input: &mut InnerZ80Span| -> ModalResult<LocatedTokenInner, Z80ParserError> {
4516 let mut registers: Vec<_> = separated(
4517 1..,
4518 alt((parse_register16, parse_indexregister16)),
4519 parse_comma
4520 )
4521 .parse_next(input)?;
4522
4523 if registers.len() > 1 {
4524 match push_or_pop {
4525 Mnemonic::Push => Ok(LocatedTokenInner::MultiPush(registers)),
4526 Mnemonic::Pop => Ok(LocatedTokenInner::MultiPop(registers)),
4527 _ => unreachable!()
4528 }
4529 }
4530 else {
4531 let register = registers.remove(0);
4532 Ok(LocatedTokenInner::new_opcode(
4533 push_or_pop,
4534 Some(register),
4535 None
4536 ))
4537 }
4538 }
4539}
4540
4541#[cfg_attr(not(target_arch = "wasm32"), inline)]
4543#[cfg_attr(target_arch = "wasm32", inline(never))]
4544pub fn parse_ret(input: &mut InnerZ80Span) -> ModalResult<LocatedTokenInner, Z80ParserError> {
4545 let (cond, cond_bytes) = opt(parse_flag_test).with_taken().parse_next(input)?;
4546
4547 let token = LocatedTokenInner::new_opcode(
4548 Mnemonic::Ret,
4549 cond.map(|cond| {
4550 LocatedDataAccess::FlagTest(cond, (*input).update_slice(cond_bytes).into())
4551 }),
4552 None
4553 );
4554
4555 Ok(token)
4556}
4557
4558#[cfg_attr(not(target_arch = "wasm32"), inline)]
4560#[cfg_attr(target_arch = "wasm32", inline(never))]
4561pub fn parse_inc_dec(
4562 inc_or_dec: Mnemonic
4563) -> impl Fn(&mut InnerZ80Span) -> ModalResult<LocatedTokenInner, Z80ParserError> {
4564 move |input: &mut InnerZ80Span| -> ModalResult<LocatedTokenInner, Z80ParserError> {
4565 let register = alt((
4566 parse_register16,
4567 parse_indexregister16,
4568 parse_register8,
4569 parse_indexregister8,
4570 parse_register_sp,
4571 parse_hl_address,
4572 parse_indexregister_with_index
4573 ))
4574 .parse_next(input)?;
4575
4576 Ok(LocatedTokenInner::new_opcode(
4577 inc_or_dec,
4578 Some(register),
4579 None
4580 ))
4581 }
4582}
4583
4584#[cfg_attr(not(target_arch = "wasm32"), inline)]
4586#[cfg_attr(target_arch = "wasm32", inline(never))]
4587pub fn parse_out(input: &mut InnerZ80Span) -> ModalResult<LocatedTokenInner, Z80ParserError> {
4588 let port = alt((parse_portc, parse_portnn)).parse_next(input)?;
4592
4593 let cloned = *input;
4595 let (value, span) = if port.is_port_c() {
4596 opt(preceded(
4598 parse_comma,
4599 alt((
4600 parse_register8,
4601 alt((parse_word(b"f").take(), "0")).map(|w| {
4602 LocatedDataAccess::Expression(LocatedExpr::Value(
4603 0,
4604 cloned.update_slice(w).into()
4605 ))
4606 })
4607 ))
4608 ))
4609 .with_taken()
4610 .parse_next(input)?
4611 }
4612 else {
4613 preceded(parse_comma, parse_register_a)
4614 .map(Some)
4615 .with_taken()
4616 .parse_next(input)?
4617 };
4618
4619 let cloned = *input;
4620 let value = value.unwrap_or(LocatedDataAccess::Expression(LocatedExpr::Value(0, {
4621 cloned.update_slice(span).into()
4622 })));
4623
4624 Ok(LocatedTokenInner::new_opcode(
4625 Mnemonic::Out,
4626 Some(port),
4627 Some(value)
4628 ))
4629}
4630
4631#[cfg_attr(not(target_arch = "wasm32"), inline)]
4633#[cfg_attr(target_arch = "wasm32", inline(never))]
4634pub fn parse_in(input: &mut InnerZ80Span) -> ModalResult<LocatedTokenInner, Z80ParserError> {
4635 let cloned = *input;
4637 let (destination, span) = opt(terminated(
4639 alt((
4640 parse_register8,
4641 alt((Caseless("f").take(), "0")).map(|span| {
4642 LocatedDataAccess::Expression(LocatedExpr::Value(
4643 0,
4644 cloned.update_slice(span).into()
4645 ))
4646 })
4647 )),
4648 parse_comma
4649 ))
4650 .with_taken()
4651 .parse_next(input)?;
4652
4653 let cloned = *input;
4654 let destination = destination.unwrap_or(LocatedDataAccess::Expression(LocatedExpr::Value(
4655 0,
4656 cloned.update_slice(span).into()
4657 )));
4658
4659 let port = cut_err(alt((
4660 parse_portc,
4661 parse_portnn.verify(|_| {
4662 destination
4663 .get_register8()
4664 .map(|r| r.is_a())
4665 .unwrap_or(false)
4666 })
4667 )))
4668 .parse_next(input)?;
4669
4670 Ok(LocatedTokenInner::new_opcode(
4671 Mnemonic::In,
4672 Some(destination),
4673 Some(port)
4674 ))
4675}
4676
4677#[cfg_attr(not(target_arch = "wasm32"), inline)]
4679#[cfg_attr(target_arch = "wasm32", inline(never))]
4680pub fn parse_rst(input: &mut InnerZ80Span) -> ModalResult<LocatedTokenInner, Z80ParserError> {
4681 let val = parse_expr(input)?;
4683
4684 Ok(LocatedTokenInner::new_opcode(
4685 Mnemonic::Rst,
4686 Some(val),
4687 None
4688 ))
4689}
4690
4691#[cfg_attr(not(target_arch = "wasm32"), inline)]
4692#[cfg_attr(target_arch = "wasm32", inline(never))]
4693pub fn parse_rst_fake(input: &mut InnerZ80Span) -> ModalResult<LocatedTokenInner, Z80ParserError> {
4694 let (flag, _, val) = (
4695 parse_flag_test
4696 .verify(|t| {
4697 t == &FlagTest::Z || t == &FlagTest::NZ || t == &FlagTest::C || t == &FlagTest::NC
4698 })
4699 .with_taken(),
4700 parse_comma,
4701 parse_expr
4702 )
4703 .parse_next(input)?;
4704
4705 let flag = {
4706 let span = (*input).update_slice(flag.1);
4707 LocatedDataAccess::FlagTest(flag.0, span.into())
4708 };
4709
4710 let token = LocatedTokenInner::new_opcode(Mnemonic::Rst, Some(flag), Some(val));
4711 let warning = LocatedTokenInner::WarningWrapper(
4712 Box::new(token),
4713 "This is a fake instruction assembled using several opcodes".into()
4714 );
4715
4716 Ok(warning)
4717}
4718
4719#[cfg_attr(not(target_arch = "wasm32"), inline)]
4721#[cfg_attr(target_arch = "wasm32", inline(never))]
4722pub fn parse_im(input: &mut InnerZ80Span) -> ModalResult<LocatedTokenInner, Z80ParserError> {
4723 let val = parse_expr(input)?;
4725
4726 Ok(LocatedTokenInner::new_opcode(Mnemonic::Im, Some(val), None))
4727}
4728
4729#[cfg_attr(not(target_arch = "wasm32"), inline)]
4741#[cfg_attr(target_arch = "wasm32", inline(never))]
4742pub fn parse_shifts_and_rotations(
4743 oper: Mnemonic
4744) -> impl Fn(&mut InnerZ80Span) -> ModalResult<LocatedTokenInner, Z80ParserError> {
4745 #[cfg_attr(not(target_arch = "wasm32"), inline)]
4746 #[cfg_attr(target_arch = "wasm32", inline(never))]
4747 move |input: &mut InnerZ80Span| -> ModalResult<LocatedTokenInner, Z80ParserError> {
4748 let _start = *input;
4749 let arg = alt((
4750 parse_register8,
4751 parse_hl_address,
4752 parse_indexregister_with_index
4753 ))
4754 .parse_next(input)?;
4755
4756 let arg2 = opt(preceded(parse_comma, parse_register8)).parse_next(input)?;
4758
4759 Ok(LocatedTokenInner::new_opcode(oper, Some(arg), arg2))
4760 }
4761}
4762
4763pub fn parse_shifts_and_rotations_fake(
4764 oper: Mnemonic
4765) -> impl Fn(&mut InnerZ80Span) -> ModalResult<LocatedTokenInner, Z80ParserError> {
4766 move |input: &mut InnerZ80Span| -> ModalResult<LocatedTokenInner, Z80ParserError> {
4767 let _start = *input;
4768 let arg = alt((parse_register16,)).parse_next(input)?;
4769
4770 let token = LocatedTokenInner::new_opcode(oper, Some(arg), None);
4771 let warning = LocatedTokenInner::WarningWrapper(
4772 Box::new(token),
4773 "This is a fake instruction assembled using several opcodes".into()
4774 );
4775
4776 Ok(warning)
4777 }
4778}
4779
4780#[cfg_attr(not(target_arch = "wasm32"), inline)]
4782#[cfg_attr(target_arch = "wasm32", inline(never))]
4783pub fn parse_call_jp_or_jr(
4784 call_jp_or_jr: Mnemonic
4785) -> impl Fn(&mut InnerZ80Span) -> ModalResult<LocatedTokenInner, Z80ParserError> {
4786 #[cfg_attr(not(target_arch = "wasm32"), inline)]
4787 #[cfg_attr(target_arch = "wasm32", inline(never))]
4788 move |input: &mut InnerZ80Span| -> ModalResult<LocatedTokenInner, Z80ParserError> {
4789 let _start = *input;
4790
4791 let flag_test =
4792 opt(terminated(parse_flag_test.with_taken(), parse_comma)).parse_next(input)?;
4793
4794 let dst = cut_err(
4795 alt((
4796
4797 alt((
4798 parse_hl_address,
4799 parse_indexregister_address,
4800 parse_register_hl,
4801 parse_indexregister16
4802 ))
4803 .verify(|_| call_jp_or_jr.is_jp() && flag_test.is_none()), parse_expr
4805 ))
4806 .context(match call_jp_or_jr {
4807 Mnemonic::Jp => JP_WRONG_PARAM,
4808 Mnemonic::Jr => JR_WRONG_PARAM,
4809 Mnemonic::Call => CALL_WRONG_PARAM,
4810 _ => unreachable!()
4811 })
4812 )
4813 .parse_next(input)?;
4814
4815 let dst = match dst {
4817 LocatedDataAccess::IndexRegister16(reg, span) => {
4818 LocatedDataAccess::MemoryIndexRegister16(reg, span)
4819 },
4820 LocatedDataAccess::Register16(reg, span) => {
4821 LocatedDataAccess::MemoryRegister16(reg, span)
4822 },
4823 other => other
4824 };
4825
4826 let flag_test = flag_test.map(|(f, s)| {
4827 let span = (*input).update_slice(s);
4828 LocatedDataAccess::FlagTest(f, span.into())
4829 });
4830
4831 Ok(LocatedTokenInner::new_opcode(
4832 call_jp_or_jr,
4833 flag_test,
4834 Some(dst)
4835 ))
4836 }
4837}
4838
4839#[cfg_attr(not(target_arch = "wasm32"), inline)]
4841#[cfg_attr(target_arch = "wasm32", inline(never))]
4842pub fn parse_flag_test(input: &mut InnerZ80Span) -> ModalResult<FlagTest, Z80ParserError> {
4843 alt((
4844 parse_word(b"NZ").value(FlagTest::NZ),
4845 parse_word(b"Z").value(FlagTest::Z),
4846 parse_word(b"NC").value(FlagTest::NC),
4847 parse_word(b"C").value(FlagTest::C),
4848 parse_word(b"PO").value(FlagTest::PO),
4849 parse_word(b"PE").value(FlagTest::PE),
4850 parse_word(b"P").value(FlagTest::P),
4851 parse_word(b"M").value(FlagTest::M)
4852 ))
4853 .parse_next(input)
4854}
4855
4856#[cfg_attr(not(target_arch = "wasm32"), inline)]
4867#[cfg_attr(target_arch = "wasm32", inline(never))]
4868pub fn parse_register16(
4869 input: &mut InnerZ80Span
4870) -> ModalResult<LocatedDataAccess, Z80ParserError> {
4871 let _start = input.checkpoint();
4872 let code = terminated(take(2usize), not(alpha1)).parse_next(input)?;
4873
4874 let reg = match code {
4875 choice_nocase!(b"AF") => Register16::Af,
4876 choice_nocase!(b"BC") => Register16::Bc,
4877 choice_nocase!(b"DE") => Register16::De,
4878 choice_nocase!(b"HL") => Register16::Hl,
4879 _ => return Err(ErrMode::Backtrack(Z80ParserError::from_input(input)))
4880 };
4881
4882 let span = (*input).update_slice(code);
4883 let reg = LocatedDataAccess::Register16(reg, span.into());
4884
4885 Ok(reg)
4886}
4887
4888#[cfg_attr(not(target_arch = "wasm32"), inline)]
4891#[cfg_attr(target_arch = "wasm32", inline(never))]
4892pub fn parse_register8(input: &mut InnerZ80Span) -> ModalResult<LocatedDataAccess, Z80ParserError> {
4893 #[derive(PartialEq)]
4894 enum Reg16Modifier {
4895 Low,
4896 High
4897 };
4898
4899 alt((
4900 ((
4901 parse_register16,
4902 preceded(
4903 b'.',
4904 alt((
4905 Caseless("low").map(|_| Reg16Modifier::Low),
4906 Caseless("high").map(|_| Reg16Modifier::High)
4907 ))
4908 ),
4909 my_space0
4910 ))
4911 .map(|(r16, code, _)| {
4912 match code {
4913 Reg16Modifier::Low => r16.to_data_access_for_low_register().unwrap(),
4914 Reg16Modifier::High => r16.to_data_access_for_high_register().unwrap()
4915 }
4916 }),
4917 parse_register_a,
4918 parse_register_b,
4919 parse_register_c,
4920 parse_register_d,
4921 parse_register_e,
4922 parse_register_h,
4923 parse_register_l
4924 ))
4925 .parse_next(input)
4926}
4927
4928#[cfg_attr(not(target_arch = "wasm32"), inline)]
4930#[cfg_attr(target_arch = "wasm32", inline(never))]
4931pub fn parse_register_i(
4932 input: &mut InnerZ80Span
4933) -> ModalResult<LocatedDataAccess, Z80ParserError> {
4934 let da = ((Caseless("I"), not(alphanumeric1)))
4935 .take()
4936 .parse_next(input)?;
4937 let da = LocatedDataAccess::SpecialRegisterI((*input).update_slice(da).into());
4938 Ok(da)
4939}
4940
4941#[cfg_attr(not(target_arch = "wasm32"), inline)]
4943#[cfg_attr(target_arch = "wasm32", inline(never))]
4944pub fn parse_register_r(
4945 input: &mut InnerZ80Span
4946) -> ModalResult<LocatedDataAccess, Z80ParserError> {
4947 let da = ((Caseless("R"), not(alphanumeric1)))
4948 .take()
4949 .parse_next(input)?;
4950 let da = LocatedDataAccess::SpecialRegisterR((*input).update_slice(da).into());
4951 Ok(da)
4952}
4953
4954macro_rules! parse_any_register8 {
4955 ($name:ident, $char:expr, $reg:expr) => {
4956 #[cfg_attr(not(target_arch = "wasm32"), inline)]
4958 #[cfg_attr(target_arch = "wasm32", inline(never))]
4959 pub fn $name(i: &mut InnerZ80Span) -> ModalResult<LocatedDataAccess, Z80ParserError> {
4960 let span = parse_word($char)(i)?;
4961
4962 Ok((LocatedDataAccess::Register8($reg, span.into())))
4963 }
4964 };
4965}
4966
4967parse_any_register8!(parse_register_a, b"A", Register8::A);
4968parse_any_register8!(parse_register_b, b"B", Register8::B);
4969parse_any_register8!(parse_register_c, b"C", Register8::C);
4970parse_any_register8!(parse_register_d, b"d", Register8::D);
4971parse_any_register8!(parse_register_e, b"e", Register8::E);
4972parse_any_register8!(parse_register_h, b"h", Register8::H);
4973parse_any_register8!(parse_register_l, b"l", Register8::L);
4974
4975#[cfg_attr(not(target_arch = "wasm32"), inline)]
4977#[cfg_attr(target_arch = "wasm32", inline(never))]
4978fn register16_parser(
4979 representation: &'static str,
4980 register: Register16
4981) -> impl for<'src, 'ctx> Fn(&mut InnerZ80Span) -> ModalResult<LocatedDataAccess, Z80ParserError> {
4982 #[cfg_attr(not(target_arch = "wasm32"), inline)]
4983 #[cfg_attr(target_arch = "wasm32", inline(never))]
4984 move |input: &mut InnerZ80Span| {
4985 let span = ((
4986 Caseless(representation),
4987 not(one_of(('a'..='z', 'A'..='Z', '0'..='9', '_')))
4988 ))
4989 .take()
4990 .parse_next(input)?;
4991
4992 let span = (*input).update_slice(span);
4993
4994 Ok(LocatedDataAccess::Register16(register, span.into()))
4995 }
4996}
4997
4998macro_rules! parse_any_register16 {
4999 ($name:ident, $char:expr, $reg:expr) => {
5000 #[cfg_attr(not(target_arch = "wasm32"), inline)]
5002 #[cfg_attr(target_arch = "wasm32", inline(never))]
5003 pub fn $name(input: &mut InnerZ80Span) -> ModalResult<LocatedDataAccess, Z80ParserError> {
5004 register16_parser($char, $reg).parse_next(input)
5005 }
5006 };
5007}
5008
5009parse_any_register16!(parse_register_sp, "SP", Register16::Sp);
5010parse_any_register16!(parse_register_af, "AF", Register16::Af);
5011parse_any_register16!(parse_register_bc, "BC", Register16::Bc);
5012parse_any_register16!(parse_register_de, "DE", Register16::De);
5013parse_any_register16!(parse_register_hl, "HL", Register16::Hl);
5014
5015#[cfg_attr(not(target_arch = "wasm32"), inline)]
5017#[cfg_attr(target_arch = "wasm32", inline(never))]
5018pub fn parse_register_ix(
5019 input: &mut InnerZ80Span
5020) -> ModalResult<LocatedDataAccess, Z80ParserError> {
5021 parse_indexregister16
5022 .verify(|d: &LocatedDataAccess| d.is_register_ix())
5023 .parse_next(input)
5024}
5025
5026#[cfg_attr(not(target_arch = "wasm32"), inline)]
5028#[cfg_attr(target_arch = "wasm32", inline(never))]
5029pub fn parse_register_iy(
5030 input: &mut InnerZ80Span
5031) -> ModalResult<LocatedDataAccess, Z80ParserError> {
5032 parse_indexregister16
5033 .verify(|d: &LocatedDataAccess| d.is_register_iy())
5034 .parse_next(input)
5035}
5036
5037macro_rules! parse_any_indexregister8 {
5039 ($($reg:ident, $alias1:ident, $alias2:ident)*) => {$(
5040 paste::paste! {
5041 #[cfg_attr(not(target_arch = "wasm32"), inline)]
5043#[cfg_attr(target_arch = "wasm32", inline(never))]
5044 pub fn [<parse_register_ $reg:lower>] (input: &mut InnerZ80Span) -> ModalResult<LocatedDataAccess, Z80ParserError> {
5045 let _start = input.clone();
5046 let span = ((
5047 alt((
5048 parse_word( stringify!($reg).as_bytes()),
5049 parse_word( stringify!($alias1).as_bytes()),
5050 parse_word( stringify!($alias2).as_bytes()),
5051 ))
5052 , not(alphanumeric1)))
5053 .take()
5054 .parse_next(input)?;
5055
5056 let span = input.clone().update_slice(span);
5057
5058 Ok((LocatedDataAccess::IndexRegister8(IndexRegister8::$reg, span.into())))
5059
5060
5061 }
5062 }
5063 )*}
5064 }
5065parse_any_indexregister8!(
5066 Ixh,hx,xh
5067 Ixl,lx,xl
5068 Iyh,hy,yh
5069 Iyl,ly,yl
5070);
5071
5072#[cfg_attr(not(target_arch = "wasm32"), inline)]
5074#[cfg_attr(target_arch = "wasm32", inline(never))]
5075pub fn parse_indexregister8(
5076 input: &mut InnerZ80Span
5077) -> ModalResult<LocatedDataAccess, Z80ParserError> {
5078 alt((
5079 parse_register_ixh,
5080 parse_register_iyh,
5081 parse_register_ixl,
5082 parse_register_iyl
5083 ))
5084 .parse_next(input)
5085}
5086
5087#[cfg_attr(not(target_arch = "wasm32"), inline)]
5089#[cfg_attr(target_arch = "wasm32", inline(never))]
5090pub fn parse_indexregister16(
5091 input: &mut InnerZ80Span
5092) -> ModalResult<LocatedDataAccess, Z80ParserError> {
5093 let code = terminated(take(2usize), not(alpha1))
5094 .take()
5095 .parse_next(input)?;
5096
5097 let reg = match code {
5098 choice_nocase!(b"IX") => IndexRegister16::Ix,
5099 choice_nocase!(b"IY") => IndexRegister16::Iy,
5100 _ => return Err(ErrMode::Backtrack(Z80ParserError::from_input(input)))
5101 };
5102
5103 let span = (*input).update_slice(code);
5104 let reg = LocatedDataAccess::IndexRegister16(reg, span.into());
5105
5106 Ok(reg)
5107}
5108
5109#[cfg_attr(not(target_arch = "wasm32"), inline)]
5111#[cfg_attr(target_arch = "wasm32", inline(never))]
5112pub fn parse_indexregister_with_index(
5113 input: &mut InnerZ80Span
5114) -> ModalResult<LocatedDataAccess, Z80ParserError> {
5115 let start_checkpoint = input.checkpoint();
5116 let start_eof_offset = input.eof_offset();
5117 let (open, _, reg) =
5118 ((alt((b'(', b'[')), my_space0, parse_indexregister16)).parse_next(input)?;
5119
5120 let op = opt(preceded(
5121 my_space0,
5122 alt((
5123 b'+'.value(BinaryOperation::Add),
5124 b'-'.value(BinaryOperation::Sub)
5125 ))
5126 ))
5127 .parse_next(input)?;
5128
5129 let close = if open == b'(' { b')' } else { b']' };
5130
5131 let expr = if op.is_some() {
5132 terminated(located_expr, (my_space0, close)).parse_next(input)?
5133 }
5134 else {
5135 (my_space0, close)
5136 .value(LocatedExpr::Value(0, (*input).into()))
5137 .parse_next(input)?
5138 };
5139
5140 let span = build_span(start_eof_offset, &start_checkpoint, *input);
5141 Ok(LocatedDataAccess::IndexRegister16WithIndex(
5142 reg.get_indexregister16().unwrap(),
5143 op.unwrap_or(BinaryOperation::Add),
5144 expr,
5145 span.into()
5146 ))
5147}
5148
5149#[cfg_attr(not(target_arch = "wasm32"), inline)]
5151#[cfg_attr(target_arch = "wasm32", inline(never))]
5152pub fn parse_portc(input: &mut InnerZ80Span) -> ModalResult<LocatedDataAccess, Z80ParserError> {
5153 let span = alt((
5154 ((b'(', my_space0, parse_register_c, my_space0, b')')),
5155 ((b'[', my_space0, parse_register_c, my_space0, b']'))
5156 ))
5157 .take()
5158 .parse_next(input)?;
5159 let span = (*input).update_slice(span);
5160
5161 Ok(LocatedDataAccess::PortC(span.into()))
5162}
5163
5164#[cfg_attr(not(target_arch = "wasm32"), inline)]
5166#[cfg_attr(target_arch = "wasm32", inline(never))]
5167pub fn parse_portnn(input: &mut InnerZ80Span) -> ModalResult<LocatedDataAccess, Z80ParserError> {
5168 let (address, span) = alt((
5169 delimited("(", located_expr, preceded(my_space0, ")")),
5170 delimited("[", located_expr, preceded(my_space0, "]"))
5171 ))
5172 .with_taken()
5173 .parse_next(input)?;
5174 let span = (*input).update_slice(span);
5175
5176 Ok(LocatedDataAccess::PortN(address, span.into()))
5177}
5178
5179#[cfg_attr(not(target_arch = "wasm32"), inline)]
5181#[cfg_attr(target_arch = "wasm32", inline(never))]
5182pub fn parse_address(input: &mut InnerZ80Span) -> ModalResult<LocatedDataAccess, Z80ParserError> {
5183 let first_char = alt((b'(', b'[')).parse_next(input)?;
5196 let address = terminated(
5197 located_expr,
5198 (
5199 my_space0,
5200 if first_char == b'(' { b')' } else { b']' },
5201 peek(
5202 preceded(
5204 my_space0,
5205 alt((
5206 eof.value(()),
5207 my_line_ending.value(()),
5208 ','.value(()),
5209 ':'.value(()),
5210 ';'.value(()),
5211 "//".value(())
5212 ))
5213 )
5214 )
5215 )
5216 )
5217 .parse_next(input)?;
5218
5219 Ok(LocatedDataAccess::Memory(address))
5220}
5221
5222#[cfg_attr(not(target_arch = "wasm32"), inline)]
5224#[cfg_attr(target_arch = "wasm32", inline(never))]
5225pub fn parse_reg_address(
5226 input: &mut InnerZ80Span
5227) -> ModalResult<LocatedDataAccess, Z80ParserError> {
5228 let (reg, span) = alt((
5229 delimited(
5230 terminated("(", my_space0),
5231 parse_register16,
5232 preceded(my_space0, ")")
5233 ),
5234 delimited(
5235 terminated("[", my_space0),
5236 parse_register16,
5237 preceded(my_space0, "]")
5238 )
5239 ))
5240 .with_taken()
5241 .parse_next(input)?;
5242
5243 let da = LocatedDataAccess::MemoryRegister16(
5244 reg.get_register16().unwrap(),
5245 (*input).update_slice(span).into()
5246 );
5247 Ok(da)
5248}
5249
5250#[cfg_attr(not(target_arch = "wasm32"), inline)]
5252#[cfg_attr(target_arch = "wasm32", inline(never))]
5253pub fn parse_hl_address(
5254 input: &mut InnerZ80Span
5255) -> ModalResult<LocatedDataAccess, Z80ParserError> {
5256 let span = alt((
5257 delimited(
5258 terminated("(", my_space0),
5259 parse_register_hl,
5260 preceded(my_space0, ")")
5261 ),
5262 delimited(
5263 terminated("[", my_space0),
5264 parse_register_hl,
5265 preceded(my_space0, "]")
5266 )
5267 ))
5268 .take()
5269 .parse_next(input)?;
5270
5271 Ok(LocatedDataAccess::MemoryRegister16(
5272 Register16::Hl,
5273 (*input).update_slice(span).into()
5274 ))
5275}
5276
5277#[cfg_attr(not(target_arch = "wasm32"), inline)]
5279#[cfg_attr(target_arch = "wasm32", inline(never))]
5280pub fn parse_indexregister_address(
5281 input: &mut InnerZ80Span
5282) -> ModalResult<LocatedDataAccess, Z80ParserError> {
5283 let (reg, res) = delimited(
5284 terminated("(", my_space0),
5285 parse_indexregister16,
5286 preceded(my_space0, ")")
5287 )
5288 .with_taken()
5289 .parse_next(input)?;
5290
5291 let span = (*input).update_slice(res);
5292 Ok(LocatedDataAccess::MemoryIndexRegister16(
5293 reg.get_indexregister16().unwrap(),
5294 span.into()
5295 ))
5296}
5297
5298#[cfg_attr(not(target_arch = "wasm32"), inline)]
5300#[cfg_attr(target_arch = "wasm32", inline(never))]
5301pub fn parse_expr(input: &mut InnerZ80Span) -> ModalResult<LocatedDataAccess, Z80ParserError> {
5302 let expr = located_expr.parse_next(input)?;
5303 Ok(LocatedDataAccess::Expression(expr))
5304}
5305
5306pub fn parse_org(input: &mut InnerZ80Span) -> ModalResult<LocatedTokenInner, Z80ParserError> {
5308 let val1 =
5309 cut_err(located_expr.context(StrContext::Label("Invalid argument"))).parse_next(input)?;
5310 let val2 = opt(preceded(parse_comma, located_expr)).parse_next(input)?;
5311
5312 Ok(LocatedTokenInner::Org { val1, val2 })
5313}
5314
5315pub fn parse_defs(input: &mut InnerZ80Span) -> ModalResult<LocatedTokenInner, Z80ParserError> {
5317 let val = separated(
5318 1..,
5319 cut_err(
5320 ((located_expr, opt(preceded(parse_comma, located_expr))))
5321 .context(StrContext::Label("Wrong argument"))
5322 ),
5323 parse_comma
5324 )
5325 .parse_next(input)?;
5326
5327 Ok(LocatedTokenInner::Defs(val))
5328}
5329
5330pub fn parse_nop(input: &mut InnerZ80Span) -> ModalResult<LocatedTokenInner, Z80ParserError> {
5331 let val = cut_err(
5332 opt(located_expr.map(LocatedDataAccess::from)).context(StrContext::Label(
5333 "Wrong argument. NOP expects an expression"
5334 ))
5335 )
5336 .parse_next(input)?;
5337
5338 Ok(LocatedTokenInner::OpCode(Mnemonic::Nop, val, None, None))
5339}
5340
5341pub fn parse_opcode_no_arg(input: &mut InnerZ80Span) -> ModalResult<LocatedToken, Z80ParserError> {
5343 let cloned = *input;
5344 let input_start = input.checkpoint();
5345
5346 let token: LocatedToken = preceded(
5347 my_space0,
5348 alpha1.verify_map(|word: &[u8]| {
5349 match word {
5350 choice_nocase!(b"CCF") => Some(Mnemonic::Ccf),
5351 choice_nocase!(b"CPD") => Some(Mnemonic::Cpd),
5352 choice_nocase!(b"CPDR") => Some(Mnemonic::Cpdr),
5353 choice_nocase!(b"CPI") => Some(Mnemonic::Cpi),
5354 choice_nocase!(b"CPIR") => Some(Mnemonic::Cpir),
5355 choice_nocase!(b"CPL") => Some(Mnemonic::Cpl),
5356 choice_nocase!(b"DAA") => Some(Mnemonic::Daa),
5357 choice_nocase!(b"DI") => Some(Mnemonic::Di),
5358 choice_nocase!(b"EI") => Some(Mnemonic::Ei),
5359 choice_nocase!(b"EXX") => Some(Mnemonic::Exx),
5360 choice_nocase!(b"HALT") => Some(Mnemonic::Halt),
5361 choice_nocase!(b"IND") => Some(Mnemonic::Ind),
5362 choice_nocase!(b"INDR") => Some(Mnemonic::Indr),
5363 choice_nocase!(b"INI") => Some(Mnemonic::Ini),
5364 choice_nocase!(b"INIR") => Some(Mnemonic::Inir),
5365 choice_nocase!(b"LDD") => Some(Mnemonic::Ldd),
5366 choice_nocase!(b"LDDR") => Some(Mnemonic::Lddr),
5367 choice_nocase!(b"LDI") => Some(Mnemonic::Ldi),
5368 choice_nocase!(b"LDIR") => Some(Mnemonic::Ldir),
5369 choice_nocase!(b"NEG") => Some(Mnemonic::Neg),
5370 choice_nocase!(b"NOPS2") => Some(Mnemonic::Nop2),
5371 choice_nocase!(b"OTDR") => Some(Mnemonic::Otdr),
5372 choice_nocase!(b"OTIR") => Some(Mnemonic::Otir),
5373 choice_nocase!(b"OUTD") => Some(Mnemonic::Outd),
5374 choice_nocase!(b"OUTDR") => Some(Mnemonic::Otdr),
5375 choice_nocase!(b"OUTI") => Some(Mnemonic::Outi),
5376 choice_nocase!(b"OUTIR") => Some(Mnemonic::Otir),
5377 choice_nocase!(b"RETI") => Some(Mnemonic::Reti),
5378 choice_nocase!(b"RETN") => Some(Mnemonic::Retn),
5379 choice_nocase!(b"RLA") => Some(Mnemonic::Rla),
5380 choice_nocase!(b"RLCA") => Some(Mnemonic::Rlca),
5381 choice_nocase!(b"RLD") => Some(Mnemonic::Rld),
5382 choice_nocase!(b"RRA") => Some(Mnemonic::Rra),
5383 choice_nocase!(b"RRCA") => Some(Mnemonic::Rrca),
5384 choice_nocase!(b"RRD") => Some(Mnemonic::Rrd),
5385 choice_nocase!(b"SCF") => Some(Mnemonic::Scf),
5386 _ => None
5387 }
5388 })
5389 )
5390 .with_taken()
5391 .map(|(mne, span)| {
5392 let span = cloned.update_slice(span);
5393 LocatedTokenInner::OpCode(mne, None, None, None).into_located_token_at(span)
5394 })
5395 .parse_next(input)?;
5396
5397 let token: LocatedToken = match &token.inner.as_ref().left().unwrap() {
5400 LocatedTokenInner::OpCode(
5401 Mnemonic::Ldi
5402 | Mnemonic::Ldd
5403 | Mnemonic::Rlca
5404 | Mnemonic::Rrca
5405 | Mnemonic::Ini
5406 | Mnemonic::Ind
5407 | Mnemonic::Outi
5408 | Mnemonic::Outd
5409 | Mnemonic::Halt,
5410 located_data_access,
5411 located_data_access1,
5412 register8
5413 ) => {
5414 debug_assert!(located_data_access.is_none());
5415 debug_assert!(located_data_access1.is_none());
5416 debug_assert!(register8.is_none());
5417
5418 let repeat = opt(preceded(my_space1, located_expr)).parse_next(input)?;
5419 if let Some(repeat) = repeat {
5420 LocatedTokenInner::RepeatToken {
5421 token: Box::new(token),
5422 repeat
5423 }
5424 .into_located_token_between(&input_start, *input)
5425 }
5426 else {
5427 token
5428 }
5429 },
5430
5431 _ => token
5432 };
5433
5434 Ok(token)
5435}
5436
5437fn parse_snainit(input: &mut InnerZ80Span) -> ModalResult<LocatedTokenInner, Z80ParserError> {
5438 let fname = parse_fname(input)?;
5439
5440 Ok(LocatedTokenInner::SnaInit(fname))
5441}
5442
5443fn parse_struct(input: &mut InnerZ80Span) -> ModalResult<LocatedTokenInner, Z80ParserError> {
5444 let name = cut_err(parse_label(false)).parse_next(input)?;
5445
5446 let fields: Vec<(Z80Span, LocatedToken)> = cut_err(
5449 repeat(
5450 1..,
5451 delimited(
5452 repeat::<_, _, (), _, _>(
5453 0..,
5454 alt((
5455 my_space1.value(()),
5456 parse_comment.value(()),
5457 line_ending.value(()),
5458 ':'.value(())
5459 ))
5460 ),
5461 (
5462 terminated(
5463 parse_label(false),
5464 alt((((my_space0, ':', my_space0)).take(), my_space1.take()))
5465 )
5466 .verify(|label: &InnerZ80Span| !label.eq_ignore_ascii_case(b"endstruct"))
5467 .context(StrContext::Label("STRUCT: label error"))
5468 .map(|span: InnerZ80Span| Z80Span::from(span)),
5469 cut_err(
5470 parse_struct_directive
5471 .context(StrContext::Label("STRUCT: Invalid operation"))
5472 )
5473 ),
5474 repeat::<_, _, (), _, _>(
5475 0..,
5476 alt((
5477 my_space1.value(()),
5478 parse_comment.value(()),
5479 line_ending.value(()),
5480 ':'.value(())
5481 ))
5482 )
5483 )
5484 )
5485 .context(StrContext::Label("STRUCT: error in inner content"))
5486 )
5487 .parse_next(input)?;
5488
5489 let _ = cut_err(preceded(
5490 my_space0,
5491 alt((
5492 parse_directive_word(b"ENDSTRUCT"),
5493 parse_directive_word(b"ENDS")
5494 ))
5495 ))
5496 .parse_next(input)?;
5497
5498 Ok(LocatedTokenInner::Struct(name.into(), fields))
5499}
5500
5501#[cfg_attr(not(target_arch = "wasm32"), inline)]
5502#[cfg_attr(target_arch = "wasm32", inline(never))]
5503fn parse_snaset(
5504 directive_name_parsed: bool
5505) -> impl Fn(&mut InnerZ80Span) -> ModalResult<LocatedTokenInner, Z80ParserError> {
5506 move |input: &mut InnerZ80Span| -> ModalResult<LocatedTokenInner, Z80ParserError> {
5507 if !directive_name_parsed {
5508 parse_word(b"SNASET").parse_next(input)?;
5509 }
5510
5511 let input_start = input.checkpoint();
5512 let flagname = cut_err(parse_label(false).context(SNASET_WRONG_LABEL)).parse_next(input)?;
5513 let _ = cut_err(parse_comma.context(SNASET_MISSING_COMMA)).parse_next(input)?;
5514
5515 let values: Vec<_> = cut_err(separated(
5516 1..,
5517 parse_flag_value_inner.context(StrContext::Label("SNASET: wrong flag value")),
5518 delimited(my_space0, parse_comma, my_space0)
5519 ))
5520 .parse_next(input)?;
5521
5522 let flagname = flagname.as_bstr();
5523 let flagname = unsafe { std::str::from_utf8_unchecked(flagname) };
5524 let (flagname, value) = if values.len() == 1 {
5525 (Cow::Borrowed(flagname), values[0].clone())
5526 }
5527 else {
5528 (
5529 Cow::Owned(format!("{}:{}", flagname, values[0].as_u16().unwrap())),
5530 values[1].clone()
5531 )
5532 };
5533
5534 let flag = parse_flag::<_, ()>(&mut flagname.as_bytes()).map_err(|_e| {
5535 input.reset(&input_start);
5536 ErrMode::Backtrack(Z80ParserError::from_input(input).add_context(
5537 input,
5538 &input_start,
5539 "Wrong flag"
5540 ))
5541 })?;
5542 Ok(LocatedTokenInner::SnaSet(flag, value))
5543 }
5544}
5545
5546#[cfg_attr(not(target_arch = "wasm32"), inline)]
5548#[cfg_attr(target_arch = "wasm32", inline(never))]
5549pub fn parse_comment(input: &mut InnerZ80Span) -> ModalResult<LocatedToken, Z80ParserError> {
5550 let cloned = *input;
5551 preceded(alt((b";", b"//")), take_till(0.., |ch| ch == b'\n'))
5552 .take()
5553 .map(|string: &[u8]| {
5554 LocatedTokenInner::Comment(cloned.update_slice(string).into())
5555 .into_located_token_direct()
5556 })
5557 .parse_next(input)
5558}
5559
5560#[cfg_attr(not(target_arch = "wasm32"), inline)]
5561#[cfg_attr(target_arch = "wasm32", inline(never))]
5562pub fn parse_multiline_comment(
5563 input: &mut InnerZ80Span
5564) -> ModalResult<LocatedToken, Z80ParserError> {
5565 let cloned = *input;
5566 delimited(b"/*", take_until(0.., "*/"), b"*/")
5567 .map(|string: &[u8]| {
5568 LocatedTokenInner::Comment(cloned.update_slice(string).into())
5569 .into_located_token_direct()
5570 })
5571 .parse_next(input)
5572}
5573
5574#[cfg_attr(not(target_arch = "wasm32"), inline)]
5576#[cfg_attr(target_arch = "wasm32", inline(never))]
5577pub fn string_expr(input: &mut InnerZ80Span) -> ModalResult<LocatedExpr, Z80ParserError> {
5578 parse_string.map(LocatedExpr::String).parse_next(input)
5579}
5580
5581#[cfg_attr(not(target_arch = "wasm32"), inline)]
5584#[cfg_attr(target_arch = "wasm32", inline(never))]
5585pub fn parse_label(
5586 doubledots: bool
5587) -> impl Fn(&mut InnerZ80Span) -> ModalResult<InnerZ80Span, Z80ParserError> {
5588 #[cfg_attr(not(target_arch = "wasm32"), inline)]
5589 #[cfg_attr(target_arch = "wasm32", inline(never))]
5590 move |input: &mut InnerZ80Span| {
5591 let start = input.checkpoint();
5592
5593 let is_orgams = input.state.options().is_orgams();
5594 let obtained_label = if is_orgams {
5595 ((
5596 opt(alt(("::", "@", "."))).value(()),
5597 alt((
5598 one_of((
5599 b'a'..=b'z',
5600 b'A'..=b'Z',
5601 b'_',
5602 b'#', b'\'' )).value(()),
5604 delimited('{', expr, '}').value(())
5605 )),
5606 repeat::<_, _, (), _, _>(0.., alt((
5607 take_while(1..,
5608 (b'a'..=b'z',
5609 b'A'..=b'Z',
5610 b'0'..=b'9',
5611 b'_',
5612 b'#', b'\'' )
5614 ).value(()),
5615 ".".value(()),
5616 delimited('{', opt(expr), '}').value(())
5617 )))
5618 )).take()
5619 .parse_next(input)?
5620 } else {
5621 ((
5622 opt(alt(("::", "@", "."))).value(()),
5623 alt((
5624 one_of((
5625 b'a'..=b'z',
5626 b'A'..=b'Z',
5627 b'_',
5628 )).value(()),
5629 delimited('{', expr, '}').value(())
5630 )),
5631 repeat::<_, _, (), _, _>(0.., alt((
5632 take_while(1..,
5633 (b'a'..=b'z',
5634 b'A'..=b'Z',
5635 b'0'..=b'9',
5636 b'_',
5637 )
5638 ).value(()),
5639 ".".value(()),
5640 delimited('{', opt(expr), '}').value(())
5641 )))
5642 )).take()
5643 .parse_next(input)?
5644 };
5645
5646
5647
5648let start_with_double_dots = obtained_label.len() > 2 && &obtained_label[..2] == b"::";
5659 let true_label = if start_with_double_dots {
5660 &obtained_label[2..]
5661 }
5662 else {
5663 obtained_label
5664 };
5665
5666 let input = if doubledots {
5668 let _ =opt(Caseless(":")).parse_next(input)?;
5669 input
5670 }
5671 else {
5672 input
5673 };
5674
5675
5676 let label_len = true_label.len();
5678 if label_len >= MIN_MAX_LABEL_SIZE.0 &&
5679 label_len <= DOTTED_MIN_MAX_LABEL_SIZE.1 &&
5680 !ignore_ascii_case_allowed_label( true_label, input.state.options().dotted_directive, input.state.options().assembler_flavor) {
5681 input.reset(&start);
5682 Err(ErrMode::Backtrack(Z80ParserError::from_input(
5683 input
5684 ).add_context(input, &start, "You cannot use a directive or an instruction as a label")
5685 ))
5686 }
5687 else {
5688 Ok((*input).update_slice(obtained_label))
5689 }
5690 }
5691}
5692
5693#[cfg_attr(not(target_arch = "wasm32"), inline)]
5694#[cfg_attr(target_arch = "wasm32", inline(never))]
5695fn impossible_names(dotted_directive: bool, flavor: AssemblerFlavor) -> &'static [&'static [u8]] {
5696 if flavor == AssemblerFlavor::Basm {
5697 if dotted_directive {
5698 &DOTTED_IMPOSSIBLE_NAMES
5699 }
5700 else {
5701 &IMPOSSIBLE_NAMES
5702 }
5703 }
5704 else {
5705 assert_eq!(flavor, AssemblerFlavor::Orgams);
5706 &IMPOSSIBLE_NAMES_ORGAMS
5707 }
5708}
5709
5710#[cfg_attr(not(target_arch = "wasm32"), inline)]
5711#[cfg_attr(target_arch = "wasm32", inline(never))]
5712fn ignore_ascii_case_allowed_label(
5713 name: &[u8],
5714 dotted_directive: bool,
5715 flavor: AssemblerFlavor
5716) -> bool {
5717 #[cfg(all(not(target_arch = "wasm32"), feature = "rayon"))]
5718 let iter = impossible_names(dotted_directive, flavor).par_iter();
5719
5720 #[cfg(any(target_arch = "wasm32", not(feature = "rayon")))]
5721 let mut iter = impossible_names(dotted_directive, flavor).iter();
5722
5723 !iter.any(|&content| content.eq_ignore_ascii_case(name))
5724}
5725
5726#[cfg_attr(not(target_arch = "wasm32"), inline)]
5727#[cfg_attr(target_arch = "wasm32", inline(never))]
5728pub fn parse_end_directive(input: &mut InnerZ80Span) -> ModalResult<InnerZ80Span, Z80ParserError> {
5729 if input.state.options().dotted_directive {
5730 b'.'.parse_next(input)?;
5731 }
5732
5733 if input.state.options().is_orgams() {
5734 let bracket = opt("]").parse_next(input)?;
5735 if let Some(bracket) = bracket {
5736 return Ok((*input).update_slice(bracket));
5737 }
5738 }
5739
5740 let keyword =
5741 take_while(1.., (b'a'..=b'z', b'A'..=b'Z', b'0'..=b'9', b'_')).parse_next(input)?;
5742
5743 if END_DIRECTIVE
5744 .iter()
5745 .any(|&val| val.eq_ignore_ascii_case(keyword))
5746 {
5747 Ok((*input).update_slice(keyword))
5748 }
5749 else {
5750 Err(ErrMode::Backtrack(Z80ParserError::from_input(input)))
5751 }
5752}
5753
5754#[cfg_attr(not(target_arch = "wasm32"), inline)]
5755#[cfg_attr(target_arch = "wasm32", inline(never))]
5756pub fn parse_macro_name(input: &mut InnerZ80Span) -> ModalResult<InnerZ80Span, Z80ParserError> {
5757 let dotted_directive = input.state.options().dotted_directive;
5758 let flavor = input.state.options().assembler_flavor;
5759
5760 let name = (
5761 one_of((b'a'..=b'z', b'A'..=b'Z', b'_')),
5762 take_while(0.., (b'a'..=b'z', b'A'..=b'Z', b'0'..=b'9', b'_')),
5763 not('{')
5764 )
5765 .take()
5766 .verify(move |name: &[u8]| {
5767 !(!ignore_ascii_case_allowed_label(name, dotted_directive, flavor))
5768 })
5769 .parse_next(input)?;
5770
5771 Ok((*input).update_slice(name))
5772}
5773
5774#[cfg_attr(not(target_arch = "wasm32"), inline)]
5775#[cfg_attr(target_arch = "wasm32", inline(never))]
5776pub fn prefixed_label_expr(input: &mut InnerZ80Span) -> ModalResult<LocatedExpr, Z80ParserError> {
5777 let _ = my_space0(input)?;
5778 let input_start = input.checkpoint();
5779 let input_offset = input.eof_offset();
5780
5781 let prefix = alt((
5782 Caseless("{bank}").value(LabelPrefix::Bank),
5783 Caseless("{page}").value(LabelPrefix::Page),
5784 Caseless("{pageset}").value(LabelPrefix::Pageset)
5785 ))
5786 .parse_next(input)?;
5787
5788 let label =
5789 preceded(my_space0, alt((parse_label(false).take(), "$$", "$"))).parse_next(input)?;
5790
5791 let span = build_span(input_offset, &input_start, *input);
5792 Ok(LocatedExpr::PrefixedLabel(
5793 prefix,
5794 (*input).update_slice(label).into(),
5795 span.into()
5796 ))
5797}
5798
5799#[cfg_attr(not(target_arch = "wasm32"), inline)]
5814#[cfg_attr(target_arch = "wasm32", inline(never))]
5815pub fn parse_value(input: &mut InnerZ80Span) -> ModalResult<LocatedExpr, Z80ParserError> {
5816 let (val, span) = cpclib_common::parse_value.with_taken().parse_next(input)?;
5817
5818 let span = (*input).update_slice(span);
5819 Ok(LocatedExpr::Value(val as i32, span.into()))
5820}
5821
5822#[cfg_attr(not(target_arch = "wasm32"), inline)]
5824#[cfg_attr(target_arch = "wasm32", inline(never))]
5825pub fn parse_counter(input: &mut InnerZ80Span) -> ModalResult<LocatedExpr, Z80ParserError> {
5826 let cloned = *input;
5827 delimited(
5828 b'{',
5829 parse_label(false), (b'}', not(alphanumeric1))
5831 )
5832 .take()
5833 .map(|l| LocatedExpr::Label(cloned.update_slice(l).into()))
5834 .parse_next(input)
5835}
5836
5837#[cfg_attr(not(target_arch = "wasm32"), inline)]
5839#[cfg_attr(target_arch = "wasm32", inline(never))]
5840pub fn parens(input: &mut InnerZ80Span) -> ModalResult<LocatedExpr, Z80ParserError> {
5841 let input_start = input.checkpoint();
5842 let input_offset = input.eof_offset();
5843
5844 let (open, close) = if input.state.options().is_orgams() {
5845 ('[', ']')
5846 }
5847 else {
5848 ('(', ')')
5849 };
5850
5851 let exp = delimited(
5852 delimited(my_space0, open, my_space0),
5853 located_expr,
5854 delimited(my_space0, close, my_space0)
5855 )
5856 .parse_next(input)?;
5857
5858 let span = build_span(input_offset, &input_start, *input);
5859 Ok(LocatedExpr::Paren(Box::new(exp), span.into()))
5860}
5861
5862#[cfg_attr(not(target_arch = "wasm32"), inline)]
5863#[cfg_attr(target_arch = "wasm32", inline(never))]
5864pub fn parse_expr_bracketed_list(
5865 input: &mut InnerZ80Span
5866) -> ModalResult<LocatedExpr, Z80ParserError> {
5867 let input_start = input.checkpoint();
5868 let input_offset = input.eof_offset();
5869
5870 let list = delimited(
5871 ("[", my_space0),
5872 separated(0.., located_expr, parse_comma),
5873 (my_space0, "]")
5874 )
5875 .parse_next(input)?;
5876
5877 let span = build_span(input_offset, &input_start, *input);
5878 Ok(LocatedExpr::List(list, span.into()))
5879}
5880
5881#[cfg_attr(not(target_arch = "wasm32"), inline)]
5882#[cfg_attr(target_arch = "wasm32", inline(never))]
5883pub fn parse_bool_expr(input: &mut InnerZ80Span) -> ModalResult<LocatedExpr, Z80ParserError> {
5884 let input_start = input.checkpoint();
5885 let input_offset = input.eof_offset();
5886 let bool = alt((
5887 parse_word(b"true").value(true),
5888 parse_word(b"false").value(false)
5889 ))
5890 .parse_next(input)?;
5891 let span = build_span(input_offset, &input_start, *input);
5892 Ok(LocatedExpr::Bool(bool, span.into()))
5893}
5894
5895#[cfg_attr(not(target_arch = "wasm32"), inline)]
5897#[cfg_attr(target_arch = "wasm32", inline(never))]
5898pub fn parse_factor(input: &mut InnerZ80Span) -> ModalResult<LocatedExpr, Z80ParserError> {
5899 let is_orgams = input.state.options().is_orgams();
5900
5901 let input_start = input.checkpoint();
5902 let input_offset = input.eof_offset();
5903
5904 let not = opt(delimited(
5905 my_space0,
5906 alt(('!'.take(), parse_word(b"NOT").take())),
5907 my_space0
5908 ))
5909 .parse_next(input)?;
5910
5911 let binary_not = opt(delimited(my_space0, '~', my_space0)).parse_next(input)?;
5912 let high_or_low = opt(preceded(my_space0, alt((b'>', b'<')))).parse_next(input)?;
5913
5914 let cloned = *input;
5915 let factor = preceded(
5916 my_space0,
5917 alt((
5918 prefixed_label_expr,
5919 parse_expr_bracketed_list.verify(|_| !is_orgams),
5920 parse_word(b"RND()").map(|w| LocatedExpr::Rnd(w.into())),
5922 parse_unary_function_call,
5923 parse_binary_function_call,
5924 parse_duration,
5925 parse_assemble,
5926 parse_any_function_call,
5927 alt((positive_number, negative_number)),
5929 parse_string.map(|s| {
5930 if s.as_ref().len() == 1 {
5931 LocatedExpr::Char(s.0.chars().next().unwrap(), s.1)
5932 }
5933 else {
5934 LocatedExpr::String(s)
5935 }
5936 }),
5937 parse_counter,
5938 alt(("$$", "$")).map(|l| LocatedExpr::Label(cloned.update_slice(l).into())),
5940 (
5941 "-",
5942 alt(("$$", "$"))
5943 .map(|l| Box::new(LocatedExpr::Label(cloned.update_slice(l).into())))
5944 )
5945 .with_taken()
5946 .map(|((m, dollar), content)| {
5947 LocatedExpr::UnaryOperation(
5948 UnaryOperation::Neg,
5949 dollar,
5950 cloned.update_slice(content).into()
5951 )
5952 }),
5953 parse_bool_expr,
5954 parse_label(false).map(|l| LocatedExpr::Label(l.into())),
5956 parens
5957 )) )
5960 .parse_next(input)?;
5961
5962 let factor = match not {
5965 Some(_) => {
5966 LocatedExpr::UnaryOperation(
5967 UnaryOperation::Not,
5968 Box::new(factor),
5969 build_span(input_offset, &input_start, *input).into()
5970 )
5971 },
5972 None => factor
5973 };
5974
5975 let factor = match binary_not {
5976 Some(_) => {
5977 LocatedExpr::UnaryOperation(
5978 UnaryOperation::BinaryNot,
5979 Box::new(factor),
5980 build_span(input_offset, &input_start, *input).into()
5981 )
5982 },
5983 None => factor
5984 };
5985
5986 let factor = match high_or_low {
5987 Some(k) => {
5988 LocatedExpr::UnaryFunction(
5989 match k {
5990 b'>' => UnaryFunction::High,
5991 b'<' => UnaryFunction::Low,
5992 _ => unreachable!()
5993 },
5994 Box::new(factor),
5995 build_span(input_offset, &input_start, *input).into()
5996 )
5997 },
5998 None => factor
5999 };
6000
6001 Ok(factor)
6002}
6003
6004#[cfg_attr(not(target_arch = "wasm32"), inline)]
6005#[cfg_attr(target_arch = "wasm32", inline(never))]
6006pub fn negative_number(input: &mut InnerZ80Span) -> ModalResult<LocatedExpr, Z80ParserError> {
6007 let input_start = input.checkpoint();
6008 let input_offset = input.eof_offset();
6009
6010 let v = preceded(b'-', number)
6011 .map(|exp| {
6012 match exp {
6013 LocatedExpr::Value(v, _) => -v,
6014 _ => unreachable!()
6015 }
6016 })
6017 .parse_next(input)?;
6018
6019 let span = build_span(input_offset, &input_start, *input);
6020 Ok(LocatedExpr::Value(v, span.into()))
6021}
6022
6023#[cfg_attr(not(target_arch = "wasm32"), inline)]
6024#[cfg_attr(target_arch = "wasm32", inline(never))]
6025pub fn number(input: &mut InnerZ80Span) -> ModalResult<LocatedExpr, Z80ParserError> {
6026 let _input_start = input.checkpoint();
6027 let _input_offset = input.eof_offset();
6028
6029 terminated(
6030 parse_value,
6031 not(one_of((
6032 b'A'..=b'Z',
6033 b'a'..=b'z',
6034 b'0'..=b'9',
6035 b'#',
6036 b'@',
6037 b'_'
6038 )))
6039 )
6040 .parse_next(input)
6041}
6042
6043#[cfg_attr(not(target_arch = "wasm32"), inline)]
6044#[cfg_attr(target_arch = "wasm32", inline(never))]
6045pub fn positive_number(input: &mut InnerZ80Span) -> ModalResult<LocatedExpr, Z80ParserError> {
6046 preceded(opt('+'), number).parse_next(input)
6047}
6048
6049#[cfg_attr(not(target_arch = "wasm32"), inline)]
6050#[cfg_attr(target_arch = "wasm32", inline(never))]
6051pub fn parse_labelprefix(input: &mut InnerZ80Span) -> ModalResult<LabelPrefix, Z80ParserError> {
6052 alt((
6053 Caseless("{pageset}").value(LabelPrefix::Pageset),
6054 Caseless("{bank}").value(LabelPrefix::Bank),
6055 Caseless("{page}").value(LabelPrefix::Page)
6056 ))
6057 .parse_next(input)
6058}
6059
6060#[cfg_attr(not(target_arch = "wasm32"), inline)]
6061#[cfg_attr(target_arch = "wasm32", inline(never))]
6062fn fold_exprs(
6063 initial: LocatedExpr,
6064 remainder: Vec<(BinaryOperation, LocatedExpr)>,
6065 span: InnerZ80Span
6066) -> LocatedExpr {
6067 remainder.into_iter().fold(initial, move |acc, pair| {
6068 let (oper, expr) = pair;
6069 LocatedExpr::BinaryOperation(oper, Box::new(acc), Box::new(expr), span.into())
6070 })
6071}
6072
6073#[cfg_attr(not(target_arch = "wasm32"), inline)]
6075#[cfg_attr(target_arch = "wasm32", inline(never))]
6076pub fn term(input: &mut InnerZ80Span) -> ModalResult<LocatedExpr, Z80ParserError> {
6077 let input_start = input.checkpoint();
6078 let input_offset = input.eof_offset();
6079
6080 let initial = parse_factor(input)?;
6081 let remainder = repeat(
6082 0..,
6083 alt((
6084 parse_oper(parse_factor, "*", BinaryOperation::Mul),
6085 parse_oper(parse_factor, "%", BinaryOperation::Mod),
6086 parse_oper(parse_factor, "MOD", BinaryOperation::Mod),
6087 parse_oper(parse_factor, "/", BinaryOperation::Div)
6088 ))
6089 )
6090 .parse_next(input)?;
6091
6092 let span = build_span(input_offset, &input_start, *input);
6093 Ok(fold_exprs(initial, remainder, span))
6094}
6095
6096#[cfg_attr(not(target_arch = "wasm32"), inline)]
6101#[cfg_attr(target_arch = "wasm32", inline(never))]
6102fn parse_oper<F>(
6103 inner: F,
6104 pattern: &'static str,
6105 symbol: BinaryOperation
6106) -> impl Fn(&mut InnerZ80Span) -> ModalResult<(BinaryOperation, LocatedExpr), Z80ParserError>
6107where
6108 F: Fn(&mut InnerZ80Span) -> ModalResult<LocatedExpr, Z80ParserError>
6109{
6110 #[cfg_attr(not(target_arch = "wasm32"), inline)]
6111 #[cfg_attr(target_arch = "wasm32", inline(never))]
6112 move |input: &mut InnerZ80Span| {
6113 let _ = my_space0(input)?;
6114 let _ = Caseless(pattern).parse_next(input)?;
6115
6116 if input.state.options().is_orgams() && pattern == "*" {
6118 not(pattern).parse_next(input)?;
6119 }
6120
6121 let _ = my_space0(input)?;
6122 let operation = inner(input)?;
6123
6124 Ok((symbol, operation))
6125 }
6126}
6127#[cfg_attr(not(target_arch = "wasm32"), inline)]
6128#[cfg_attr(target_arch = "wasm32", inline(never))]
6129fn parse_bool<F>(
6130 inner: F,
6131 pattern: &'static str,
6132 symbol: BinaryOperation
6133) -> impl Fn(&mut InnerZ80Span) -> ModalResult<(BinaryOperation, LocatedExpr), Z80ParserError>
6134where
6135 F: Fn(&mut InnerZ80Span) -> ModalResult<LocatedExpr, Z80ParserError>
6136{
6137 #[cfg_attr(not(target_arch = "wasm32"), inline)]
6138 #[cfg_attr(target_arch = "wasm32", inline(never))]
6139 move |input: &mut InnerZ80Span| {
6140 let _ = my_space0(input)?;
6141 let _ = Caseless(pattern).parse_next(input)?;
6142 let _ = my_space0(input)?;
6143 let operation = inner(input)?;
6144
6145 Ok((symbol, operation))
6146 }
6147}
6148
6149#[cfg_attr(not(target_arch = "wasm32"), inline)]
6151#[cfg_attr(target_arch = "wasm32", inline(never))]
6152pub fn expr2(input: &mut InnerZ80Span) -> ModalResult<LocatedExpr, Z80ParserError> {
6153 let input_start = input.checkpoint();
6154 let input_offset = input.eof_offset();
6155
6156 let initial = shift(input)?;
6157 let remainder = repeat(
6158 0..,
6159 alt((
6160 parse_oper(shift, "<=", BinaryOperation::LowerOrEqual),
6161 parse_oper(shift, "<", BinaryOperation::StrictlyLower),
6162 parse_oper(shift, ">=", BinaryOperation::GreaterOrEqual),
6163 parse_oper(shift, ">", BinaryOperation::StrictlyGreater),
6164 parse_oper(shift, "==", BinaryOperation::Equal),
6165 parse_oper(shift, "=", BinaryOperation::Equal),
6166 parse_oper(shift, "!=", BinaryOperation::Different)
6167 ))
6168 )
6169 .parse_next(input)?;
6170
6171 let span = build_span(input_offset, &input_start, *input);
6172 Ok(fold_exprs(initial, remainder, span))
6173}
6174
6175fn expr(input: &mut InnerZ80Span) -> ModalResult<Expr, Z80ParserError> {
6176 located_expr
6177 .map(|e| e.to_expr().into_owned())
6178 .parse_next(input)
6179}
6180
6181#[cfg_attr(not(target_arch = "wasm32"), inline)]
6183#[cfg_attr(target_arch = "wasm32", inline(never))]
6184pub fn located_expr(input: &mut InnerZ80Span) -> ModalResult<LocatedExpr, Z80ParserError> {
6185 if input.state.options().is_orgams() {
6186 return parse_orgams_expression.parse_next(input);
6187 }
6188
6189 let input_start = input.checkpoint();
6190 let input_offset = input.eof_offset();
6191
6192 let initial = expr2(input)?;
6193 let remainder = repeat(
6194 0..,
6195 alt((
6196 parse_oper(expr2, "&&", BinaryOperation::BooleanAnd),
6197 parse_oper(expr2, "||", BinaryOperation::BooleanOr)
6198 ))
6199 )
6200 .parse_next(input)?;
6201 let span = build_span(input_offset, &input_start, *input);
6202 Ok(fold_exprs(initial, remainder, span))
6203}
6204
6205#[cfg_attr(not(target_arch = "wasm32"), inline)]
6207#[cfg_attr(target_arch = "wasm32", inline(never))]
6208pub fn parse_unary_function_call(
6209 input: &mut InnerZ80Span
6210) -> ModalResult<LocatedExpr, Z80ParserError> {
6211 let input_start = input.checkpoint();
6212 let input_offset = input.eof_offset();
6213
6214 let (word, exp) = (
6215 delimited(my_space0, alpha1, my_space0),
6216 delimited((my_space0, "(", my_space0), located_expr, (my_space0, ")"))
6217 .context(StrContext::Label("UNARY function: error in parameters"))
6218 )
6219 .parse_next(input)?;
6220
6221 let func = match word {
6222 choice_nocase!(b"HIGH") | choice_nocase!(b"HI") => Some(UnaryFunction::High),
6223 choice_nocase!(b"LOW") | choice_nocase!(b"LO") => Some(UnaryFunction::Low),
6224 choice_nocase!(b"PEEK") | choice_nocase!(b"MEMORY") => Some(UnaryFunction::Memory),
6225 choice_nocase!(b"FLOOR") => Some(UnaryFunction::Floor),
6226 choice_nocase!(b"CEIL") => Some(UnaryFunction::Ceil),
6227 choice_nocase!(b"FRAC") => Some(UnaryFunction::Frac),
6228 choice_nocase!(b"CHAR") => Some(UnaryFunction::Char),
6229 choice_nocase!(b"INT") => Some(UnaryFunction::Int),
6230 choice_nocase!(b"SIN") => Some(UnaryFunction::Sin),
6231 choice_nocase!(b"COS") => Some(UnaryFunction::Cos),
6232 choice_nocase!(b"ASIN") => Some(UnaryFunction::ASin),
6233 choice_nocase!(b"ACOS") => Some(UnaryFunction::ACos),
6234 choice_nocase!(b"LN") => Some(UnaryFunction::Ln),
6235 choice_nocase!(b"LOG10") => Some(UnaryFunction::Log10),
6236 choice_nocase!(b"EXP") => Some(UnaryFunction::Exp),
6237 choice_nocase!(b"SQRT") => Some(UnaryFunction::Sqrt),
6238 choice_nocase!(b"ABS") => Some(UnaryFunction::Abs),
6239 _ => None
6240 };
6241
6242 let span = build_span(input_offset, &input_start, *input);
6243 let word = (*input).update_slice(word);
6244
6245 let token = match func {
6246 Some(func) => LocatedExpr::UnaryFunction(func, Box::new(exp), span.into()),
6247 None => LocatedExpr::AnyFunction(word.into(), vec![exp], span.into())
6248 };
6249
6250 Ok(token)
6251}
6252
6253#[cfg_attr(not(target_arch = "wasm32"), inline)]
6255#[cfg_attr(target_arch = "wasm32", inline(never))]
6256pub fn parse_binary_function_call(
6257 input: &mut InnerZ80Span
6258) -> ModalResult<LocatedExpr, Z80ParserError> {
6259 let input_start = input.checkpoint();
6260 let input_offset = input.eof_offset();
6261
6262 let func = alt((
6263 Caseless("MIN").value(BinaryFunction::Min),
6264 Caseless("MAX").value(BinaryFunction::Max),
6265 Caseless("POW").value(BinaryFunction::Pow)
6266 ))
6267 .parse_next(input)?;
6268
6269 let _ = ((my_space0, "(", my_space0)).parse_next(input)?;
6270
6271 let arg1 = located_expr.parse_next(input)?;
6272 let _ = ((my_space0, ',', my_space0)).parse_next(input)?;
6273 let arg2 = located_expr.parse_next(input)?;
6274
6275 let _ = ((my_space0, ")")).parse_next(input)?;
6276
6277 let span = build_span(input_offset, &input_start, *input);
6278
6279 Ok(LocatedExpr::BinaryFunction(
6280 func,
6281 Box::new(arg1),
6282 Box::new(arg2),
6283 span.into()
6284 ))
6285}
6286
6287#[cfg_attr(not(target_arch = "wasm32"), inline)]
6288#[cfg_attr(target_arch = "wasm32", inline(never))]
6289pub fn parse_any_function_call(
6290 input: &mut InnerZ80Span
6291) -> ModalResult<LocatedExpr, Z80ParserError> {
6292 let input_start = input.checkpoint();
6293 let input_offset = input.eof_offset();
6294
6295 let function_name = parse_label(false).parse_next(input)?;
6296 let arguments = delimited(
6297 ("(", my_space0),
6298 separated(0.., located_expr, parse_comma),
6299 (my_space0, ")")
6300 )
6301 .parse_next(input)?;
6302
6303 let span = build_span(input_offset, &input_start, *input);
6304 Ok(LocatedExpr::AnyFunction(
6305 function_name.into(),
6306 arguments,
6307 span.into()
6308 ))
6309}
6310
6311#[cfg_attr(not(target_arch = "wasm32"), inline)]
6313#[cfg_attr(target_arch = "wasm32", inline(never))]
6314pub fn token_function<'a>(
6315 function_name: &'static str
6316) -> impl Fn(&mut InnerZ80Span) -> ModalResult<LocatedToken, Z80ParserError> {
6317 #[cfg_attr(not(target_arch = "wasm32"), inline)]
6318 #[cfg_attr(target_arch = "wasm32", inline(never))]
6319 move |input: &mut InnerZ80Span| {
6320 let _ = ((Caseless(function_name), my_space0, ('('), my_space0)).parse_next(input)?;
6321
6322 let token = parse_token(input)?;
6323
6324 let _ = ((my_space0, ")")).parse_next(input)?;
6325
6326 Ok(token)
6327 }
6328}
6329
6330pub fn parse_duration(input: &mut InnerZ80Span) -> ModalResult<LocatedExpr, Z80ParserError> {
6332 let (token, span) = token_function("duration").with_taken().parse_next(input)?;
6333
6334 let span = (*input).update_slice(span).into();
6335 Ok(LocatedExpr::UnaryTokenOperation(
6336 UnaryTokenOperation::Duration,
6337 Box::new(token),
6338 span
6339 ))
6340}
6341
6342pub fn parse_assemble(input: &mut InnerZ80Span) -> ModalResult<LocatedExpr, Z80ParserError> {
6344 let (token, span) = token_function("opcode").with_taken().parse_next(input)?;
6345
6346 let span = (*input).update_slice(span).into();
6347 Ok(LocatedExpr::UnaryTokenOperation(
6348 UnaryTokenOperation::Opcode,
6349 Box::new(token),
6350 span
6351 ))
6352}
6353
6354#[cfg_attr(not(target_arch = "wasm32"), inline)]
6355#[cfg_attr(target_arch = "wasm32", inline(never))]
6356pub fn shift(input: &mut InnerZ80Span) -> ModalResult<LocatedExpr, Z80ParserError> {
6357 let start = input.checkpoint();
6358 let start_eof_offset = input.eof_offset();
6359
6360 let initial = comp(input)?;
6361 let remainder = repeat(
6362 0..,
6363 alt((
6364 parse_oper(comp, "<<", BinaryOperation::LeftShift),
6365 parse_oper(comp, ">>", BinaryOperation::RightShift)
6366 ))
6367 )
6368 .parse_next(input)?;
6369
6370 Ok(fold_exprs(
6371 initial,
6372 remainder,
6373 build_span(start_eof_offset, &start, *input)
6374 ))
6375}
6376
6377#[cfg_attr(not(target_arch = "wasm32"), inline)]
6379#[cfg_attr(target_arch = "wasm32", inline(never))]
6380pub fn comp(input: &mut InnerZ80Span) -> ModalResult<LocatedExpr, Z80ParserError> {
6381 let start = input.checkpoint();
6382 let start_eof_offset = input.eof_offset();
6383
6384 let initial = term(input)?;
6385 let remainder = repeat(0.., alt((
6386 parse_oper(term, "+", BinaryOperation::Add),
6387 parse_oper(term, "-", BinaryOperation::Sub),
6388 parse_oper(term, "&", BinaryOperation::BinaryAnd), parse_oper(term, "AND", BinaryOperation::BinaryAnd),
6390 parse_oper(term, "|", BinaryOperation::BinaryOr), parse_oper(term, "OR", BinaryOperation::BinaryOr),
6392 parse_oper(term, "^", BinaryOperation::BinaryXor), parse_oper(term, "XOR", BinaryOperation::BinaryXor)
6394 ))).parse_next(input)?;
6395
6396 Ok(fold_exprs(
6397 initial,
6398 remainder,
6399 build_span(start_eof_offset, &start, *input)
6400 ))
6401}
6402
6403#[cfg(test)]
6405mod test {
6406 use std::ops::Deref;
6407
6408 use cpclib_common::winnow::error::ParseError;
6409
6410 use super::*;
6411
6412 #[derive(Debug)]
6413 struct TestResult<O: std::fmt::Debug> {
6414 ctx: Box<ParserContext>,
6415 span: Z80Span,
6416 res: Result<O, ParseError<InnerZ80Span, Z80ParserError>>
6417 }
6418
6419 impl<O: std::fmt::Debug> Deref for TestResult<O> {
6420 type Target = Result<O, ParseError<InnerZ80Span, Z80ParserError>>;
6421
6422 fn deref(&self) -> &Self::Target {
6423 &self.res
6424 }
6425 }
6426
6427 #[derive(Debug)]
6428 struct TestResultRest<O: std::fmt::Debug> {
6429 ctx: Box<ParserContext>,
6430 span: Z80Span,
6431 res: Result<O, ErrMode<Z80ParserError>>
6432 }
6433
6434 impl<O: std::fmt::Debug> Deref for TestResultRest<O> {
6435 type Target = Result<O, ErrMode<Z80ParserError>>;
6436
6437 fn deref(&self) -> &Self::Target {
6438 &self.res
6439 }
6440 }
6441
6442 fn parse_test<O, P: Parser<InnerZ80Span, O, Z80ParserError>>(
6443 mut parser: P,
6444 code: &'static str
6445 ) -> TestResult<O>
6446 where
6447 O: std::fmt::Debug
6448 {
6449 let (ctx, span) = ctx_and_span(code);
6450 let res = parser.parse(span.0);
6451 if let Err(e) = &res {
6452 let e = e.inner();
6453 let e = AssemblerError::SyntaxError { error: e.clone() };
6454 eprintln!("Parse error: {}", e);
6455 }
6456
6457 TestResult { ctx, span, res }
6458 }
6459
6460 fn parse_test_rest<O, P: Parser<InnerZ80Span, O, Z80ParserError>>(
6461 mut parser: P,
6462 code: &'static str,
6463 next: &str
6464 ) -> TestResultRest<O>
6465 where
6466 O: std::fmt::Debug
6467 {
6468 let (ctx, mut span) = ctx_and_span(code);
6469 let res = parser.parse_next(&mut span.0);
6470 if let Err(ErrMode::Backtrack(e) | ErrMode::Cut(e)) = &res {
6471 let e = AssemblerError::SyntaxError { error: e.clone() };
6472 eprintln!("Parse error: {}", e);
6473 }
6474 else {
6475 assert!(
6476 unsafe { std::str::from_utf8_unchecked(span.0.as_bstr()) }
6477 .trim_start()
6478 .starts_with(next)
6479 );
6480 }
6481
6482 TestResultRest { ctx, span, res }
6483 }
6484
6485 fn ctx_and_span(code: &'static str) -> (Box<ParserContext>, Z80Span) {
6486 let ctx = Box::new(
6487 ParserContextBuilder::default()
6488 .set_context_name("TEST")
6489 .build(code)
6490 );
6491 let span = Z80Span::new_extra(code, ctx.deref());
6492 (ctx, span)
6493 }
6494
6495 #[test]
6496 fn test_parse_end_directive() {
6497 let res = parse_test(parse_end_directive, "endif");
6498 assert!(res.is_ok());
6499
6500 let res = parse_test(parse_end_directive, "ENDIF");
6501 assert!(res.is_ok());
6502 }
6503
6504 #[test]
6505 fn test_parse_directive() {
6506 let res = parse_test(parse_directive, "nop");
6507 assert!(res.is_ok());
6508
6509 let res = parse_test(parse_directive, "ORG 10");
6510 assert!(res.is_ok());
6511
6512 let res = parse_test(
6513 parse_assembler_control_max_passes_number,
6514 "ASMCONTROLENV SET_MAX_NB_OF_PASSES=10: nop : ENDA"
6515 );
6516 assert!(res.is_ok());
6517 }
6518
6519 #[test]
6520 fn parse_test_cond() {
6521 let res = parse_test_rest(
6522 inner_code,
6523 " nop
6524 endif",
6525 "endif"
6526 );
6527 assert!(res.is_ok());
6528 assert_eq!(res.res.unwrap().len(), 1);
6529
6530 let res = parse_test_rest(
6531 inner_code,
6532 " nop
6533 else",
6534 "else"
6535 );
6536 assert!(res.is_ok());
6537 assert_eq!(res.res.unwrap().len(), 1);
6538
6539 let res = parse_test(parse_conditional_condition(KindOfConditional::If), "THING");
6540 assert!(res.is_ok());
6541
6542 let res = parse_test(
6543 (parse_conditional, line_ending, my_space1),
6544 "if THING
6545 nop
6546 endif
6547 "
6548 );
6549 assert!(res.is_ok());
6550
6551 let res = parse_test(
6552 parse_conditional,
6553 "ifnot 5
6554print glop
6555else
6556endif"
6557 );
6558 assert!(res.is_ok());
6559
6560 let res = parse_test(
6561 parse_conditional,
6562 "if THING
6563 nop
6564 endif "
6565 );
6566 assert!(res.is_ok());
6567
6568 let res = parse_test(
6569 parse_conditional,
6570 "if THING
6571 nop
6572 else
6573 nop
6574 endif"
6575 );
6576 assert!(res.is_ok());
6577
6578 let res = parse_test(
6579 parse_conditional,
6580 "ifndef THING
6581 nop
6582 else
6583 nop
6584 endif"
6585 );
6586 assert!(res.is_ok());
6587
6588 let res = parse_test(
6589 parse_conditional,
6590 "if demo_system_music_activated != 0
6591 ; XXX Ensure memory is properly set
6592 ld bc, 0x7fc2 : out (c), c
6593 jp PLY_AKYst_Play
6594 else
6595 WAIT_CYCLES 64*16
6596 ret
6597 endif"
6598 );
6599 assert!(res.is_ok());
6600
6601 let res = parse_test(
6602 parse_conditional,
6603 "ifndef __DEFINED_DEBUG__
6604 __DEFINED_DEBUG__ equ 1
6605 endif"
6606 );
6607 assert!(res.is_ok());
6608
6609 let mut r#in = Default::default();
6610 let res = parse_test(
6611 parse_z80_line_complete(&mut r#in),
6612 " ifndef __DEFINED_DEBUG__
6613 __DEFINED_DEBUG__ equ 1
6614 endif"
6615 );
6616 assert!(res.is_ok(), "{:?}", res);
6617 }
6618
6619 #[test]
6620 fn parse_indexregister8() {
6621 assert_eq!(
6622 parse_test(parse_register_ixl, "ixl")
6623 .res
6624 .unwrap()
6625 .to_data_access(),
6626 DataAccess::IndexRegister8(IndexRegister8::Ixl)
6627 );
6628
6629 assert_eq!(
6630 parse_test(parse_register_ixl, "lx")
6631 .res
6632 .unwrap()
6633 .to_data_access(),
6634 DataAccess::IndexRegister8(IndexRegister8::Ixl)
6635 );
6636
6637 assert!(parse_test(parse_register_iyl, "ixl").is_err());
6638 }
6639
6640 #[test]
6641 fn test_parse_prefix_label() {
6642 let res = parse_test(parse_labelprefix, "{bank}");
6643 let res = res.res.unwrap();
6644 assert_eq!(res, LabelPrefix::Bank);
6645
6646 let res = parse_test(expr, "{bank}label"); let res = res.res.unwrap();
6648 assert_eq!(res, Expr::PrefixedLabel(LabelPrefix::Bank, "label".into()));
6649 }
6650
6651 #[test]
6652 fn test_undocumented_code() {
6653 let listing = parse_z80_str(" RLC (IY+2), B").unwrap();
6654 let token = &listing[0];
6655 let token = token.as_simple_token().into_owned();
6656 assert_eq!(
6657 token,
6658 Token::OpCode(
6659 Mnemonic::Rlc,
6660 Some(DataAccess::IndexRegister16WithIndex(
6661 IndexRegister16::Iy,
6662 BinaryOperation::Add,
6663 2.into()
6664 )),
6665 Some(DataAccess::Register8(Register8::B)),
6666 None
6667 )
6668 );
6669
6670 let listing = parse_z80_str(" RES 5, (IY-2), B").unwrap();
6671 let token = &listing[0];
6672 let token = token.as_simple_token().into_owned();
6673 assert_eq!(
6674 token,
6675 Token::OpCode(
6676 Mnemonic::Res,
6677 Some(DataAccess::Expression(5.into())),
6678 Some(DataAccess::IndexRegister16WithIndex(
6679 IndexRegister16::Iy,
6680 BinaryOperation::Sub,
6681 2.into()
6682 )),
6683 Some(Register8::B)
6684 )
6685 );
6686 }
6687
6688 #[test]
6689 fn test_parse_run() {
6690 let res: TestResult<LocatedTokenInner> = parse_test(parse_run(RunEnt::Run), "0x50, 0xc0");
6691 assert!(res.is_ok(), "{:?}", &res);
6692 }
6693
6694 #[test]
6695 fn test_parse_print() {
6696 let res = parse_test(parse_print(false), "PRINT VAR");
6697 let res = res.res.unwrap();
6698 assert_eq!(
6699 res,
6700 LocatedTokenInner::Print(vec![FormattedExpr::Raw(Expr::Label("VAR".into()))])
6701 );
6702
6703 let res = parse_test(parse_print(false), "PRINT VAR, VAR");
6704 let res = res.res.unwrap();
6705 assert_eq!(
6706 res,
6707 LocatedTokenInner::Print(vec![
6708 FormattedExpr::Raw(Expr::Label("VAR".into())),
6709 FormattedExpr::Raw(Expr::Label("VAR".into()))
6710 ])
6711 );
6712
6713 let res = parse_test(parse_print(false), "PRINT {hex}VAR");
6714 let res = res.res.unwrap();
6715 assert_eq!(
6716 res,
6717 LocatedTokenInner::Print(vec![FormattedExpr::Formatted(
6718 ExprFormat::Hex(None),
6719 Expr::Label("VAR".into())
6720 )])
6721 );
6722
6723 let res = parse_test(parse_print(false), "PRINT \"hello\"");
6724 let res = res.res.unwrap();
6725 assert_eq!(
6726 res,
6727 LocatedTokenInner::Print(vec![FormattedExpr::Raw(Expr::String("hello".into()))])
6728 );
6729 }
6730
6731 #[test]
6732 fn test_parse_advanced_breakpoints() {
6733 assert!(dbg!(parse_test(parse_argname_to_assign("TYPE"), "TYPE=")).is_ok());
6734 assert!(dbg!(parse_test(parse_breakpoint_type_value, "mem")).is_ok());
6735 assert!(
6736 dbg!(parse_test(
6737 parse_argname_and_value("TYPE", &parse_breakpoint_type_value),
6738 "TYPE=mem"
6739 ))
6740 .is_ok()
6741 );
6742
6743 assert!(
6744 dbg!(parse_test(
6745 parse_optional_argname_and_value("TYPE", &parse_breakpoint_type_value),
6746 "TYPE=mem"
6747 ))
6748 .is_ok()
6749 );
6750 assert!(
6751 dbg!(parse_test(
6752 parse_optional_argname_and_value("TYPE", &parse_breakpoint_type_value),
6753 "TYPE = mem"
6754 ))
6755 .is_ok()
6756 );
6757
6758 assert!(dbg!(parse_test(parse_breakpoint_argument, "mem")).is_ok());
6759 assert!(dbg!(parse_test(parse_breakpoint_argument, "read")).is_ok());
6760 assert!(dbg!(parse_test(parse_breakpoint_argument, "TYPE=mem")).is_ok());
6761
6762 assert!(dbg!(parse_test(parse_breakpoint, "")).is_ok());
6764 assert!(dbg!(parse_test(parse_breakpoint, "address")).is_ok());
6765 assert!(dbg!(parse_test(parse_breakpoint, "mem")).is_ok());
6766 assert!(dbg!(parse_test(parse_breakpoint, "TYPE=mem")).is_ok());
6767 assert!(dbg!(parse_test(parse_breakpoint, "ACCESS=READ")).is_ok());
6768 assert!(dbg!(parse_test(parse_breakpoint, "READ")).is_ok());
6769 assert!(dbg!(parse_test(parse_breakpoint, "RUNMODE=STOP")).is_ok());
6770 assert!(dbg!(parse_test(parse_breakpoint, "ADDR=here")).is_ok());
6771 assert!(dbg!(parse_test(parse_breakpoint, "MASK=12")).is_ok());
6772 assert!(dbg!(parse_test(parse_breakpoint, "SIZE=1")).is_ok());
6773 assert!(dbg!(parse_test(parse_breakpoint, "VALUE=1")).is_ok());
6774 assert!(dbg!(parse_test(parse_breakpoint, "VALMASK=1")).is_ok());
6775 assert!(dbg!(parse_test(parse_breakpoint, "condition=\"fdfdfd\"")).is_ok());
6776 assert!(dbg!(parse_test(parse_breakpoint, "name=\"fdfdfd\"")).is_ok());
6777
6778 assert!(dbg!(parse_test(parse_breakpoint, "step=10,name=\"fdfdfd\"")).is_ok());
6779 }
6780
6781 #[test]
6782 fn test_standard_repeat() {
6783 let z80 = std::dbg!(
6784 " repeat 5
6785 nop
6786 endrepeat"
6787 );
6788 let res = parse_test(parse_repeat, z80);
6789 assert!(res.is_ok(), "{:?}", res);
6790 }
6791
6792 #[test]
6793 fn test_parse_address() {
6794 let res = parse_test(parse_address, "(here)");
6795 assert!(res.is_ok(), "{:?}", res);
6796 }
6797
6798 #[test]
6799 fn test_parse_word() {
6800 let res = parse_test(parse_word(b"SNASET"), "SNASET");
6801 assert!(res.is_ok(), "{:?}", res);
6802
6803 let res = parse_test(terminated(parse_word(b"SNASET"), my_space1), "SNASET ");
6804 assert!(res.is_ok(), "{:?}", res);
6805 }
6806
6807 #[test]
6808 fn parser_regression_1() {
6809 let res = parse_test(parse_ld_normal(false), "ld a, chessboard_file");
6810 assert!(res.is_ok(), "{:?}", res);
6811 }
6812 #[test]
6813 fn parser_regression_1a() {
6814 let code = " nop
6815 "
6816 .replace("\u{C2}\u{A0}", " ");
6817 let code: &'static str = unsafe { std::mem::transmute(code.as_str()) };
6818 let mut vec = Vec::new();
6819 let res: TestResult<()> = parse_test(repeat(2, parse_z80_line_complete(&mut vec)), code);
6820 assert!(res.is_ok(), "{:?}", &res);
6821 }
6822 #[test]
6823 fn parser_regression_1c() {
6824 let code = " nop
6825 nop
6826 "
6827 .replace("\u{C2}\u{A0}", " ");
6828 let code: &'static str = unsafe { std::mem::transmute(code.as_str()) };
6829 let res = parse_z80_str(code);
6830 assert!(res.is_ok(), "{:?}", &res);
6831 }
6832 #[test]
6833 fn parser_regression_1d() {
6834 let code = " nop
6835 nop
6836 "
6837 .replace("\u{C2}\u{A0}", " ");
6838 let code: &'static str = unsafe { std::mem::transmute(code.as_str()) };
6839 let res = parse_test(inner_code, code);
6840 assert!(res.is_ok());
6841 }
6842 #[test]
6843 fn parser_regression_1e() {
6844 let res = std::dbg!(parse_z80_str(
6845 "
6846 ld a, chessboard_file
6847 jp .common_part_loading_in_main_memory
6848 "
6849 ));
6850
6851 assert!(res.is_ok(), "{:?}", &res);
6852 }
6854 #[test]
6855 fn parser_regression_1f() {
6856 let res = parse_test(
6857 inner_code,
6858 "
6859.load_chessboard
6860 ld de, .load_chessboard2
6861 ld a, main_memory_chessboard_extra_file
6862 jp .common_part_loading_in_main_memory
6863.load_chessboard2
6864 ld de, .load_chessboard2
6865 ld a, main_memory_chessboard_extra_file
6866 ld a, chessboard_file
6867 jp .common_part_loading_in_main_memory
6868"
6869 );
6870 assert!(res.is_ok(), "{:?}", &res);
6871 }
6872 #[test]
6873 fn parser_regression_1g() {
6874 let res = parse_test(
6875 parse_conditional,
6876 "if 0
6877 .load_chessboard
6878 ld de, .load_chessboard2
6879 ld a, main_memory_chessboard_extra_file
6880 jp .common_part_loading_in_main_memory
6881 .load_chessboard2
6882 ld de, .load_chessboard2
6883 ld a, chessboard_file
6884 jp .common_part_loading_in_main_memory
6885
6886 endif"
6887 );
6888 assert!(res.is_ok(), "{:?}", res);
6889 }
6890
6891 #[test]
6892 fn parser_regression2() {
6893 let res = parse_test(
6894 parse_assert,
6895 "assert (BREAKPOINT_METHOD == BREAKPOINT_WITH_WINAPE_BYTES) || (BREAKPOINT_METHOD == BREAKPOINT_WITH_SNAPSHOT_MODIFICATION)"
6896 );
6897 assert!(res.is_ok(), "{:?}", &res);
6898 }
6899
6900 #[test]
6901 fn parser_sna() {
6902 let res = parse_test(parse_buildsna(false), "BUILDSNA");
6903 assert!(res.is_ok(), "{:?}", &res);
6904
6905 let res = parse_test(parse_buildsna(false), "BUILDSNA V2");
6906 assert!(res.is_ok(), "{:?}", &res);
6907
6908 let res = parse_test(parse_buildsna(false), "BUILDSNA V3");
6909 assert!(res.is_ok(), "{:?}", &res);
6910
6911 let res = parse_test(parse_buildsna(false), "BUILDSNA V4");
6912 assert!(res.is_err(), "{:?}", &res);
6913 }
6914
6915 #[test]
6916 fn test_parse_snaset() {
6917 let res = parse_test(parse_snaset(false), "SNASET Z80_SP, 0x500");
6918 assert!(res.is_ok(), "{:?}", &res);
6919
6920 let res = parse_test(parse_snaset(false), "SNASET GA_PAL, 0, 30");
6921 assert!(res.is_ok(), "{:?}", &res);
6922
6923 let res = parse_test(parse_snaset(false), "SNASET CRTC_REG, 1, 48");
6924 assert!(res.is_ok(), "{:?}", &res);
6925 }
6926
6927 #[test]
6928 fn test_parse_r16_to_r8() {
6929 let mut r#in = Vec::new();
6930 let res = parse_test(parse_z80_line_complete(&mut r#in), " ld a, hl.low");
6931 assert!(res.is_ok(), "{:?}", &res);
6932 res.res.unwrap();
6933
6934 let res = parse_test(parse_ld_normal(false), "ld bc.low, a");
6935 assert!(res.is_ok(), "{:?}", &res);
6936 let res = res.res.unwrap().to_token().into_owned();
6937
6938 assert_eq!(
6939 res,
6940 Token::new_opcode(
6941 Mnemonic::Ld,
6942 Some(Register8::C.into()),
6943 Some(Register8::A.into()),
6944 )
6945 );
6946
6947 r#in.clear();
6948 let res = parse_test(parse_z80_line_complete(&mut r#in), " ld bc.low, a");
6949 assert!(res.is_ok(), "{:?}", &res);
6950
6951 assert_eq!(
6952 r#in.iter().map(|t| t.to_token().into_owned()).collect_vec(),
6953 vec![Token::new_opcode(
6954 Mnemonic::Ld,
6955 Some(Register8::C.into()),
6956 Some(Register8::A.into()),
6957 )]
6958 );
6959
6960 r#in.clear();
6961 let res: TestResult<()> = parse_test(
6962 repeat(2, parse_z80_line_complete(&mut r#in)),
6963 "\t\tld bc.low, a\n\t"
6964 );
6965 assert!(res.is_ok(), "{:?}", &res);
6966 }
6967
6968 #[test]
6969 fn test_line() {
6970 let mut tokens = Vec::new();
6971
6972 let res = parse_test(parse_line(&mut tokens), " hello ");
6973 assert!(res.is_ok(), "{:?}", &res);
6974 tokens.clear();
6975
6976 let res = parse_test(parse_line(&mut tokens), " ");
6977 assert!(res.is_ok(), "{:?}", &res);
6978 tokens.clear();
6979
6980 let res = parse_test(parse_line(&mut tokens), " ; comment");
6981 assert!(res.is_ok(), "{:?}", &res);
6982 tokens.clear();
6983
6984 let res = parse_test(parse_line(&mut tokens), " : ");
6985 assert!(res.is_ok(), "{:?}", &res);
6986 tokens.clear();
6987
6988 let res = parse_test(parse_line(&mut tokens), "hello:world");
6989 assert!(res.is_ok(), "{:?}", &res);
6990 tokens.clear();
6991
6992 let res = parse_test(parse_line(&mut tokens), " hello : world");
6993 assert!(res.is_ok(), "{:?}", &res);
6994 tokens.clear();
6995
6996 let res = dbg!(parse_test(parse_line(&mut tokens), " hello /* : world*/"));
6997 dbg!(&tokens);
6998
6999 assert!(res.is_ok(), "{:?}", &res);
7000 assert!(!tokens[0].is_call_macro_or_build_struct());
7001 tokens.clear();
7002
7003 let res = parse_test(parse_line(&mut tokens), " hello: set world ");
7004 assert!(res.is_ok(), "{:?}", &res);
7005 tokens.clear();
7006
7007 let res = parse_test(parse_line(&mut tokens), "data1 SETN data");
7008 assert!(res.is_ok(), "{:?}", &res);
7009
7010 let res = parse_test(parse_line(&mut tokens), "data1 SETN data ; comment");
7011 assert!(res.is_ok(), "{:?}", &res);
7012 }
7013
7014 #[test]
7015 fn test_parse_multiline_comment() {
7016 let res = parse_test(parse_multiline_comment, "/* fdfsdfgd */");
7017 assert!(res.is_ok(), "{:?}", &res);
7018
7019 let res = parse_test(parse_multiline_comment, "/* fdf\n*\n*\nsdfgd */");
7020 assert!(res.is_ok(), "{:?}", &res);
7021 }
7022
7023 #[test]
7024 fn test_parse_ticker() {
7025 let res = parse_test(parse_stable_ticker_start, "start mc");
7026 assert!(res.is_ok(), "{:?}", &res);
7027
7028 let res = parse_test(parse_stable_ticker_start, "start, mc");
7029 assert!(res.is_ok(), "{:?}", &res);
7030 }
7031 #[test]
7032 fn test_parse_line_component() {
7033 let res = parse_test(parse_line_component, "ticker start, mc");
7034 assert!(res.is_ok(), "{:?}", &res);
7035
7036 let res = parse_test(parse_line_component, "JP HL_div_2");
7037 assert!(res.is_ok(), "{:?}", &res);
7038
7039 let res = parse_test(parse_line_component, "ld a,(2 - $b06e) and $ff");
7040 assert!(res.is_ok(), "{:?}", &res);
7041
7042 let res = parse_test(parse_line_component, " DJNZ CHECK");
7043 assert!(res.is_ok(), "{:?}", &res);
7044
7045 let res = parse_test(parse_line_component, "ld a, d");
7046 assert!(res.is_ok(), "{:?}", &res);
7047
7048 let res = parse_test(parse_line_component, "sbc h");
7049 assert!(res.is_ok(), "{:?}", &res);
7050
7051 let res = parse_test(parse_line_component, "data1 SETN data");
7052 assert!(res.is_ok(), "{:?}", &res);
7053
7054 let res = parse_test(parse_line_component, "data2 next data, 2");
7055 assert!(res.is_ok(), "{:?}", &res);
7056
7057 let res = parse_test(
7058 (parse_line_component, my_space1, parse_comment),
7059 "data1 SETN data ; comment"
7060 );
7061 assert!(res.is_ok(), "{:?}", &res);
7062
7063 let res = parse_test(
7064 (parse_line_component, my_space1, parse_comment),
7065 "data1 setn data ; comment"
7066 );
7067 assert!(res.is_ok(), "{:?}", &res);
7068
7069 let res = parse_test(parse_line_component, " IN a,(c)");
7070 assert!(res.is_ok(), "{:?}", &res);
7071
7072 let res = parse_test(parse_line_component, " IN (c)");
7073 assert!(res.is_ok(), "{:?}", &res);
7074
7075 let res = parse_test(parse_line_component, " IN (c) ");
7076 assert!(res.is_ok(), "{:?}", &res);
7077
7078 let res = parse_test(parse_line_component, " DJNZ label");
7079 assert!(res.is_ok(), "{:?}", &res);
7080
7081 let res = parse_test(parse_line_component, "label DJNZ label");
7082 assert!(res.is_ok(), "{:?}", &res);
7083
7084 let res = parse_test(parse_line_component, " ");
7085 assert!(res.is_ok(), "{:?}", &res);
7086
7087 let res = parse_test((parse_line_component, parse_comment), " ; cxcx");
7088 assert!(res.is_ok(), "{:?}", &res);
7089
7090 let res = parse_test(parse_line_component, " \\\n");
7091 assert!(res.is_ok(), "{:?}", &res);
7092
7093 let res = parse_test((parse_line_component, "\n"), " \n");
7094 assert!(res.is_ok(), "{:?}", &res);
7095
7096 let res = parse_test(parse_line_component, "hello");
7097 assert!(res.is_ok(), "{:?}", &res);
7098
7099 let res = parse_test(parse_line_component, " hello ");
7100 assert!(res.is_ok(), "{:?}", &res);
7101
7102 let res = parse_test(parse_line_component, "defb 5, 20");
7103 assert!(res.is_ok(), "{:?}", &res);
7104
7105 let res = parse_test(parse_line_component, "defb 5, 20 ");
7106 assert!(res.is_ok(), "{:?}", &res);
7107
7108 let res = parse_test(parse_line_component, "xor a");
7109 assert!(res.is_ok(), "{:?}", &res);
7110
7111 let res = parse_test(parse_line_component, "xor a ");
7112 assert!(res.is_ok(), "{:?}", &res);
7113
7114 let res = parse_test(parse_line_component, "hello xor a ");
7115 assert!(res.is_ok(), "{:?}", &res);
7116
7117 let res = parse_test(parse_line_component, "VAR = 20");
7118 assert!(res.is_ok(), "{:?}", &res);
7119
7120 let res = parse_test(parse_line_component, "VAR <<= 20");
7121 assert!(res.is_ok(), "{:?}", &res);
7122
7123 let res = parse_test(parse_line_component, "VAR EQU 20");
7124 assert!(res.is_ok(), "{:?}", &res);
7125
7126 let res = parse_test(parse_line_component, "VAR SET 20");
7127 assert!(res.is_ok(), "{:?}", &res);
7128
7129 let res = parse_test(parse_line_component, "VAR FIELD 20");
7130 assert!(res.is_ok(), "{:?}", &res);
7131
7132 let res = parse_test(parse_line_component, "VAR # 20");
7133 assert!(res.is_ok(), "{:?}", &res);
7134
7135 let res = parse_test(parse_line_component, "VAR NEXT VAR2");
7136 assert!(res.is_ok(), "{:?}", &res);
7137
7138 let res = parse_test(parse_line_component, "VAR SETN VAR2");
7139 assert!(res.is_ok(), "{:?}", &res);
7140
7141 let res = parse_test(parse_line_component, "LET VAR = 5");
7142 assert!(res.is_ok(), "{:?}", &res);
7143
7144 let res = parse_test(parse_line_component, "LET 5");
7145 assert!(res.is_err(), "{:?}", &res);
7146
7147 let res = parse_test(parse_line_component, "LET VAR");
7148 assert!(res.is_err(), "{:?}", &res);
7149
7150 let res = parse_test(
7151 parse_line_component,
7152 "for count, 0, 10, 3
7153 db {count}
7154 endfor"
7155 );
7156 assert!(res.is_ok(), "{:?}", &res);
7157
7158 let res = parse_test(
7159 parse_line_component,
7160 "for count, 0, 10, 3 : db {count} : endfor"
7161 );
7162 assert!(res.is_ok(), "{:?}", &res);
7163
7164 let res = parse_test(parse_line_component, "FAIL");
7165 assert!(res.is_ok(), "{:?}", &res);
7166 }
7167
7168 #[test]
7169 fn test_regression_while_cpt() {
7170 let res = parse_test(parse_line_component, "CPT=CPT+1");
7171 assert!(
7172 res.as_ref().unwrap().1.as_ref().unwrap().is_assign(),
7173 "{:?}",
7174 &res
7175 );
7176 }
7177
7178 #[test]
7179 fn test_parse_marco_arg() {
7180 assert_eq!(
7181 parse_test(parse_macro_arg, "arg")
7182 .as_ref()
7183 .unwrap()
7184 .to_macro_param(),
7185 MacroParam::RawArgument("arg".into())
7186 );
7187 assert_eq!(
7188 parse_test(parse_macro_arg, "{eval}arg")
7189 .as_ref()
7190 .unwrap()
7191 .to_macro_param(),
7192 MacroParam::EvaluatedArgument("arg".into())
7193 );
7194 }
7195
7196 #[test]
7197 fn test_parse_label() {
7198 assert!(dbg!(parse_test(parse_label(false), "HL_div_2")).is_ok());
7199 assert!(dbg!(parse_test(parse_label(false), "CHECK")).is_ok());
7200 assert!(dbg!(parse_test(parse_label(false), "label")).is_ok());
7201 assert!(dbg!(parse_test(parse_label(false), "label.label")).is_ok());
7202 assert!(dbg!(parse_test(parse_label(false), "label{after}")).is_ok());
7203 assert!(dbg!(parse_test(parse_label(false), "{before}label")).is_ok());
7204 assert!(dbg!(parse_test(parse_label(false), "la{inner}bel")).is_ok());
7205 assert!(dbg!(parse_test(parse_label(false), "label{i+5}")).is_ok());
7206
7207 assert!(dbg!(parse_test(parse_label(false), "_JP")).is_ok());
7208 }
7209
7210 #[test]
7211 fn test_parse_macro_call() {
7212 assert!(dbg!(parse_test(parse_line_component, "empty (void)")).is_ok());
7213
7214 let res = dbg!(parse_test(
7215 (parse_line_component, ':', parse_line_component),
7216 "empty (void):ld a,1"
7217 ))
7218 .res
7219 .unwrap();
7220
7221 assert!(res.0.0.is_none());
7222 assert!(res.0.1.is_some());
7223 assert!(res.2.0.is_none());
7224 assert!(res.2.1.is_some());
7225
7226 assert!(
7227 dbg!(parse_test(
7228 parse_line_component,
7229 "notempty \"arg1\", \"arg2\""
7230 ))
7231 .is_ok()
7232 );
7233 }
7234
7235 #[test]
7236 fn test_regression_check() {
7237 let check = "CHECK";
7238
7239 let (ctx, mut span) = ctx_and_span("CHECK");
7240 assert!(dbg!(parse_factor.parse_next(&mut span.0)).is_ok());
7241
7242 assert!(dbg!(parse_test(parse_label(false), check)).is_ok());
7243 assert!(dbg!(parse_test(parse_factor, check)).is_ok());
7244 }
7245
7246 #[test]
7247 fn test_parse_expr() {
7248 for code in &[
7249 "(2 - $b06e) and $ff",
7250 "'o'",
7251 "'o' + 0x80",
7252 "CHECK",
7253 "\"\\\" et voila\"",
7254 "0X1234",
7255 "<0X1234",
7256 ">0X1234",
7257 "TOTO",
7258 "_TOTO"
7259 ] {
7260 assert!(dbg!(parse_test(parse_expr, code)).is_ok());
7261
7262 assert!(dbg!(parse_test(expr_list, code)).is_ok());
7263 }
7264 }
7265
7266 #[test]
7267 fn debug_label_expression() {
7268 for code in &["TOTO", "_TOTO", "_JP"] {
7269 assert!(dbg!(parse_test(parse_label(false), code)).is_ok());
7270 assert!(dbg!(parse_test(parse_factor, code)).is_ok());
7271 assert!(dbg!(parse_test(term, code)).is_ok());
7272 assert!(dbg!(parse_test(comp, code)).is_ok());
7273 assert!(dbg!(parse_test(shift, code)).is_ok());
7274 assert!(dbg!(parse_test(expr2, code)).is_ok());
7275 assert!(dbg!(parse_test(located_expr, code)).is_ok());
7276 assert!(dbg!(parse_test(expr, code)).is_ok());
7277 }
7278 }
7279
7280 #[test]
7281 fn regression_parse_hl() {
7282 for code in &mut [
7283 "ld hl, TOTO",
7284 "ld HL, _TOTO",
7285 "ld hl, _JP",
7286 "ld a, TOTO",
7287 "ld a, _TOTO",
7288 "ld a, _JP",
7289 "ld a,_JP"
7290 ] {
7291 dbg!("Handle", &code);
7292 dbg!("parse_ld");
7293 assert!(dbg!(parse_test(parse_ld(false), code)).is_ok());
7294 dbg!("parse_instruction");
7295 assert!(dbg!(parse_test(parse_token, code)).is_ok());
7296 dbg!("parse_line");
7297 let mut tokens = Vec::new();
7298 assert!(dbg!(parse_test(parse_line(&mut tokens), code)).is_ok());
7299 }
7300 }
7301
7302 #[test]
7304 fn test_parse_string() {
7305 for string in &[
7306 r#""\" et voila""#,
7307 r#""kjkjhkl""#,
7308 r#""kjk'jhkl""#,
7309 r#""kj\"kjhkl""#,
7310 r#"'kjkjhkl'"#,
7311 r#"'kjk\\"jhkl'"#,
7312 r#"'kjkj\'hkl'"#,
7313 r#""""#,
7314 r#"''"#,
7315 r#""fdfd\" et voila""#
7316 ] {
7317 let res = parse_test(parse_string, string);
7318 assert!(dbg!(&res).is_ok());
7319
7320 assert_eq!(
7321 res.res.unwrap().1.as_bstr(),
7322 (&string[1..string.len() - 1]).as_bstr()
7323 );
7324
7325 assert!(dbg!(parse_test(parse_factor, string)).is_ok());
7326
7327 assert!(dbg!(parse_test(parse_expr, string)).is_ok());
7328 }
7329 }
7330
7331 #[test]
7332 fn test_parse_macro() {
7333 let mut tokens = Vec::new();
7334 let r#macro = "macro bankm
7335 call xxx
7336 endm;";
7337 tokens.clear();
7338 assert!(dbg!(parse_test(parse_line(&mut tokens), r#macro)).is_ok());
7339
7340 let r#macro = "bankm macro
7341 call xxx
7342 endm;";
7343 tokens.clear();
7344 assert!(dbg!(parse_test(parse_line(&mut tokens), r#macro)).is_ok());
7345 }
7346
7347 #[test]
7348 fn test_expression_list() {
7349 assert!(dbg!(parse_test(expr_list, "1")).is_ok());
7350 assert!(dbg!(parse_test(expr_list, "1,2")).is_ok());
7351 assert!(dbg!(parse_test(expr_list, "1, 2")).is_ok());
7352 assert!(dbg!(parse_test(expr_list, "1 ,2")).is_ok());
7353 assert!(dbg!(parse_test(expr_list, "1 , 2")).is_ok());
7354 assert!(dbg!(parse_test(expr_list, "1,2,")).is_ok());
7355 }
7356
7357 #[test]
7358 fn test_bitwise_or() {
7359 let res = dbg!(parse_test(expr, "1|2"));
7360 let res = res.as_ref().unwrap();
7361 match res {
7362 Expr::BinaryOperation(BinaryOperation::BinaryOr, ..) => {},
7363 _ => panic!("Wrong operation")
7364 }
7365 }
7366
7367 #[test]
7368 fn test_fname() {
7369 assert!(parse_test(parse_fname, "\"test.asm\"").is_ok());
7370 assert!(parse_test(parse_fname, "test.asm").is_ok());
7371 assert!(dbg!(parse_test(parse_fname, "src/credits_screen.asm")).is_ok());
7372
7373 assert!(parse_test(parse_directive, "include \"test.asm\"").is_ok());
7374 assert!(parse_test(parse_directive, "include test.asm").is_ok());
7375 assert!(parse_test(parse_directive, "include good_db.asm").is_ok());
7376 assert!(parse_test(parse_include, "good_db.asm").is_ok());
7377 assert!(dbg!(parse_test(parse_include, "src/credits_screen.asm")).is_ok());
7378
7379 assert!(dbg!(parse_test((parse_directive, " "), "incbin \"test.asm\" ")).is_ok());
7380 assert!(parse_test((parse_directive, " "), "incbin test.asm ").is_ok());
7381 }
7382}