cpclib_asm/parser/
parser.rs

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;
35// use crc::*;
36use 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// const CRC: Crc<u32> = Crc::<u32>::new(&CRC_32_ISCSI);
46
47#[derive(Clone, Debug, PartialEq, Eq)]
48pub enum Z80ParserErrorKind {
49    /// Static string added by the `context` function
50    Context(StrContext),
51    /// Indicates which character was expected by the `char` function
52    Char(char),
53    /// Error kind given by various nom parsers
54    Winnow,
55    /// Chain of errors provided by an inner listing
56    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
152/// ...
153pub mod error_code {
154    /// ...
155    pub const ASSERT_MUST_BE_FOLLOWED_BY_AN_EXPRESSION: u32 = 128;
156    /// ...
157    pub const INVALID_ARGUMENT: u32 = 129;
158    /// ...
159    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
172// TODO search why they are listed to forbid label naming. Delete it if unneeded
173const 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"END",
220    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
313// This table is supposed to contain the keywords that finish a section
314const END_DIRECTIVE: &[&[u8]] = &[
315    b"END", // for orgams
316    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", // if directive
337    b"ENDITER",
338    b"ENDITERATE",
339    b"ENDM",
340    b"ENDMACRO",
341    b"ENDMODULE",
342    b"ENDR",
343    b"ENDREP", // repeat directive
344    b"ENDREPEAT",
345    b"ENDS",
346    b"ENDSWITCH",
347    b"ENDW",
348    b"FEND",
349    b"IEND",
350    b"LZCLOSE",
351    b"REND", // rorg directive
352    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
363// tODO use hash-based structures
364static _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
450/// Produce the stream of tokens. In case of error, return an explanatory string.
451/// In case of success loop over all the tokens in order to expand those that read files
452pub 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]) }; // The bytes live longer than input
470    input.update_slice(bytes)
471}
472
473/// TODO better to build parse_z80_with_options from parse_z80_span than the opposite
474// pub fn parse_z80_span(span: InnerZ80Span) -> Result<LocatedListing, AssemblerError> {
475//    let ctx = span.extra.clone();
476//    parse_z80_with_options(span.as_str(), ctx)
477//}
478
479#[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/// Parse a string and return the corresponding listing
486#[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(()); // diff is here
515                    }
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                            // infinite loop check: the parser must always consume
551                            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/// Workaround because many0 is not used in the main root function
577/// TODO add an argument to handle context change
578#[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        // dbg!("Requested state", &new_state);
588        LocatedListing::parse_inner(input, new_state, only_one_instruction)
589            .map(|l| (Arc::<LocatedListing>::try_unwrap(l).unwrap()))
590    }
591}
592
593/// TODO
594pub 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
620/// TODO - limit the listing possibilities
621pub fn parse_function_listing(
622    input: &mut InnerZ80Span
623) -> ModalResult<LocatedListing, Z80ParserError> {
624    // dbg!("parse_function_listing requests FunctionLimited state");
625    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)?; // TODO use a specific function for that
633
634    let cloned = *input;
635    let arguments: Vec<InnerZ80Span> = cut_err(
636        preceded(
637            opt(parse_comma), // comma after macro name is not mandatory
638            separated::<_, InnerZ80Span, Vec<InnerZ80Span>, _, _, _, _>(
639                0..,
640                // parse_label(false)
641                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
678/// TODO
679pub 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    // macro name
684    let name = cut_err(parse_label(false).context(StrContext::Label("MACRO: wrong name")))
685        .parse_next(input)?; // TODO use a specific function for that
686
687    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        // macro arguments
708        let arguments = separated::<_, _, Vec<&[u8]>, _, _, _, _>(
709            0..,
710            // parse_label(false)
711            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        // TODO factorize with the code of parse_basic
740        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); // TODO find a way to improve that part. I'd like to not make the conversion
762
763        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
773/// TODO
774pub 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    // we must have either a new line or :
783    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
825/// Parse a sub-listing part that aims at being crunched after being assembled at first pass
826pub 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
870/// Parse the switch directive
871pub 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        // after default it is mandatory to end the block
900        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    // Get parameters
975    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    // Get loop content
981    let inner = cut_err(inner_code.context(StrContext::Label("FOR: issue in the content")))
982        .parse_next(input)?;
983
984    // Collect end of loop
985    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 _ = my_space0(input)?;
1013    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
1178/// TODO
1179pub 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    // collect the labels that are spread to the basic environnement
1184    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    // TODO factorize with the the code of parse_macro
1203    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); // TODO find a way to improve that part. I'd like to not make the conversion
1221
1222    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
1229/// Parse the instruction to hide basic lines
1230pub 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
1274/// Optionally return a label and a command
1275/// next  token is a separator :, \n, eof
1276pub 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        // label is mandatory when there is let
1293        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        // let was absent
1302        opt(parse_label(false)).parse_next(input)?
1303    };
1304
1305    // build the label token later when needed
1306    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    // early exit if at the end of the line or if there is a comment
1321    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    // check if we have a label modifier if and only if we provide a label
1334    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        // LET needs =
1340        cut_err(b"=".context(StrContext::Label("LET: missing =")))
1341            .map(Some)
1342            .parse_next(input)?;
1343        Some(LabelModifier::Equal(None)) // TODO check it is ok
1344    }
1345    else {
1346        // label can have a modifier
1347        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        // optional expression to control the displacement
1409        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        // Build the needed token for the label of interest
1420        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        // ensure we have not eaten some label modifier bytes in case of error
1469        input.reset(&before_label_modifier);
1470
1471        // if a label was present as well as :, we prefer to stop here
1472        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        // otherwise this is a normal stuff
1478
1479        // we must have an instruction if label is missing; otherwise it is optional
1480        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()) // label is eaten
1485                .map(Some)
1486                .parse_next(input)
1487            {
1488                // this is a macro call
1489                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                // this is a label
1496                Ok((build_possible_label(), None))
1497            }
1498        }
1499        else {
1500            // this cannot be a macro as there is an instruction
1501            my_space0.parse_next(input)?;
1502            Ok((build_possible_label(), instruction))
1503        }
1504    }
1505}
1506
1507/// TODO - currently consume several lines. Should do it only one time
1508#[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 _ =opt(line_ending).parse_next(input)?;
1514    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    // let res = if comment.is_some() {
1519    // let size = before_comment.input_len() - input.input_len();
1520    // Some(comment.unwrap().locate(before_comment, size))
1521    // }
1522    // else {
1523    // None
1524    // };
1525    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    // Get the token
1532    alt((parse_token, parse_directive)).parse_next(input)
1533}
1534
1535// TODO add struct and Macro
1536#[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
1547/// Accept "fname" as in most assemblers and fname as in vasm
1548pub 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 // TODO call it elsewhere
1571        ))
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/// Parse a line (ie a set of components separated by :) until the end of the line or a stop directive
1613/// XXX: In opposite to the other functions, the result is stored in the parameter (to avoid unnecessary memory allocations and copies)
1614#[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; //  macro content ?
1630            }
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        // early stop parsing in case of stop directive
1641        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        // Inject the list of instructions
1659        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        // Inject the comment
1669        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/// Parser for file names in appropriate directives
1735#[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    // manage the string format - TODO manage the others too
1863    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
1875/// Parser for the include directive
1876pub 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            '"' // TODO modify to accept only labels without dot
1895        )
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/// Parse for the various binary include directives
1907#[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
1935/// parse write direct in memory / converted to a bank directive
1936/// we do not care of the parameters for roms as we are not working in an emulator
1937pub fn parse_write_direct_memory(
1938    input: &mut InnerZ80Span
1939) -> ModalResult<LocatedTokenInner, Z80ParserError> {
1940    // filter all the stuff before
1941    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
1969/// Parse both save directive and write direct in a file
1970pub 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
2039/// Parse  UNDEF directive.
2040pub 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// pub fn parse_assign(input: &mut InnerZ80Span) -> ModalResult<TokenInner, Z80ParserError> {
2077// let ((label, op, value)) = ((
2078// parse_label(false),
2079// delimited(space0, parse_assign_operator, space0),
2080// expr
2081// )).parse_next(input)?;
2082//
2083// Ok((TokenInner::Assign{label, value, op}))
2084// }
2085
2086#[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    // Get the first word that will drive the rest of parsing
2108    let word = delimited(my_space0, alpha1, my_space0).parse_next(input)?;
2109
2110    // Apply the right parsing
2111    // We use this way of doing to reduce function calls and error. Let's hope it will speed everything
2112    // choice_no_case is used to avoid memory allocation of uppercased mnemonic
2113    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") /*1*/  => 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/// Parse ex af, af' instruction
2206#[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_word(b"EX"),
2211        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/// Parse ex hl, de instruction
2220#[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            //          Caseless("EX"),
2226            //          space1,
2227            parse_register_hl,
2228            parse_comma,
2229            parse_register_de
2230        ))
2231            .value(()),
2232        ((
2233            //            Caseless("EX"),
2234            //        space1,
2235            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/// Parse ex (sp), hl
2246#[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        //     Caseless("EX"),
2251        //      space1,
2252        ('('),
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    // XXX Sadly the state is stored within the context that cannot
2287    //     by changed. So we can cannot really use parsing state sutf
2288
2289    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    // Only one argument is allowed
2296    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
2306/// Parse any directive
2307pub 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/// Here local_parsing_state only serves to adapt DB/DW/STR behavior in struct.
2315/// Maybe it should be used to control the directives of interest BEFORE there parsing instead of after.
2316/// No filtering is done
2317#[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        // Get the first word that will drive the rest of parsing
2330        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        //   dbg!("Directive:", unsafe{std::str::from_utf8_unchecked(word)});
2343
2344        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        // 12
2373        #[cfg(not(target_arch = "wasm32"))]
2374        b"INCSHRINKLER" => {
2375            parse_incbin(BinaryTransformation::Crunch(CrunchType::Shrinkler)).parse_next(input)
2376        },
2377
2378        // 13
2379        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), /* TODO filter to remove the orgams specificies */
2520
2521        _ => {
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/// Parse if expression.TODO finish the implementation in order to have ELSEIF and ELSE branches"
2673/// TODO shorten the string code source
2674#[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    //  dbg!(&input);
2680
2681    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        // Gest the kind of test to do - it can fail after an else
2691        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        //  dbg!(&if_token_or_error);
2703
2704        // leave if the first loop does not have a test
2705        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        // Get the current condition or nothing for the very last branch
2711        let condition = if let Ok(test_kind) = if_token_or_error {
2712            // Get the corresponding test
2713            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        //   dbg!(&condition);
2725
2726        // Remove empty stuff
2727        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        // get the conditionnal code
2741        // dbg!("Listing to extract code", &input);
2742        let code = cut_err(inner_code.context(StrContext::Label(
2743            "Condition: syntax error in conditionnal code"
2744        )))
2745        .parse_next(input)?;
2746        //  dbg!(unsafe{std::str::from_utf8_unchecked(input.as_bytes())});
2747
2748        //  dbg!(&code);
2749
2750        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) // no word to allow ELSEIF in addition to ELSE IF
2760            ))
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    // Here we have read the latest block
2773    //   dbg!("Everythng  has been read", &input);
2774
2775    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    // dbg!(unsafe{std::str::from_utf8_unchecked(input.as_bytes())}); // endif must have been eaten
2790
2791    let token = LocatedTokenInner::If(conditions, else_clause)
2792        .into_located_token_between(&if_start, *input);
2793    Ok(token)
2794}
2795
2796/// Read the condition part in the parse_conditional macro
2797#[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
2837/// Parse a breakpoint instruction
2838pub 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            // disallow labels that are similar to some keywords
2845            !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                    // at the same time verify if it is ok and update
2883                    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(()) // TODO implement the tests
3018                        }
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
3401/// Parse tickin directives
3402pub 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/// Parse begining of ticker
3409#[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/// Parse end of ticker
3423#[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/// Parse fake and real LD instructions
3459#[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/// Parse artifical LD instruction (would be replaced by several real instructions)
3475#[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        // TODO - add https://z00m128.github.io/sjasmplus/documentation.html#s_fake_instructions
3500        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        // mem-like
3513        {
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/// Parse the valids LD versions
3532#[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        // src possibilities depend on dst
3569        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/// Parse the source of LD depending on its destination
3579#[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            // by construction is t is HL/IX/IY
3597            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            // todo find a way to merge them together
3604            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/// Parse RES, SET and BIT instructions
3678#[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        // Res can copy the result in a reg
3700        // not bit http://www.z80.info/z80undoc.htm
3701        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/// Parse CP tokens
3718#[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(
3722    //    parse_word(b"CP"),
3723
3724    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            //   )
3738            |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))]
3784/// Parse DB DW directives
3785pub 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        // STRUCT directive allows to have no arguments
3804        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// Fail if we do not read a forbidden keyword
3828#[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(), // TODO handle evaluation or transposition
3880                    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            ), // TODO find a way to give arguments with space
3888            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/// Manage the call of a macro.
3907#[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        // if uncommented we do not detect (void) !
3939        // let nothing_after = peek((
3940        // space0,
3941        // alt((parse_comment.take(), ':', '\n'))
3942        // ))
3943        // .parse_next(input)
3944        // .is_ok();
3945
3946        // if allowed_to_return_a_label && nothing_after {
3947        // let token = LocatedTokenInner::Label(name.into());
3948        // let msg = format!("Ambiguous code. Use (void) for macro with no args, (default) for struct with default parameters; avoid labels that do not start at beginning of a line. {} is considered to be a label, not a macro.", String::from_utf8_lossy(name.as_bstr()));
3949        // let warning = LocatedTokenInner::WarningWrapper(Box::new(token), msg);
3950        // return Ok(warning.into_located_token_at(name.clone()));
3951        // }
3952
3953        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                                        // string of size 0;
3987                                    })
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        // avoid ambiguate code such as label nop
4013        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))]
4039/// TODO remove by restore the way to parse the macro name
4040pub 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        // Check if the macro name is allowed
4061        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))]
4103/// Consume the word and the empty space after
4104fn 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/// ...
4129#[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/// ...
4138#[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
4159/// ...
4160pub 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
4173/// ...
4174pub 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/// ...
4202#[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/// Parse formatted expression for print like directives
4235/// WARNING: only formated case is taken into account
4236#[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/// Handle \ in end of line
4262#[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                        // infinite loopmeans eof has been hit
4310                        if i.eof_offset() == len {
4311                            return Ok(acc);
4312                        }
4313
4314                        acc.accumulate(o);
4315                    }
4316                }
4317            }
4318        }
4319    }
4320}
4321
4322/// Handle \ in end of line
4323#[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")), // end of file
4330        one_of(|c: u8| c.is_space())
4331            .value(())
4332            .context(StrContext::Label("Space")), // space char
4333        (
4334            // continuated line
4335            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
4371/// ...
4372pub 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))]
4382/// ...
4383pub 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/// Substraction with A register
4402#[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 _ =Caseless("SUB").parse_next(input)?;
4406    //  let _ =space1(input)?;
4407
4408    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/// Par se the SBC instruction
4427#[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 _ =Caseless("SBC").parse_next(input)?;
4431    //   let _ =space1(input)?;
4432
4433    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/// Parse ADC and ADD instructions
4461#[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) // Case for HL XXX AF is accepted whereas it is not the case in real life
4485        }
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/// ...
4510#[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/// ...
4542#[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/// ...
4559#[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/// TODO manage other out formats
4585#[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 _ =parse_word(b"OUT").parse_next(input)?;
4589
4590    // get the port proposal
4591    let port = alt((parse_portc, parse_portnn)).parse_next(input)?;
4592
4593    // the vlaue depends on the port
4594    let cloned = *input;
4595    let (value, span) = if port.is_port_c() {
4596        // reg c
4597        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/// Parse all the in flavors
4632#[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 _ =parse_word(b"IN").parse_next(input)?;
4636    let cloned = *input;
4637    // get the port proposal
4638    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/// Parse the rst instruction
4678#[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 _ =parse_word(b"RST").parse_next(input)?;
4682    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/// Parse the IM instruction
4720#[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 _ =parse_word(b"IM").parse_next(input)?;
4724    let val = parse_expr(input)?;
4725
4726    Ok(LocatedTokenInner::new_opcode(Mnemonic::Im, Some(val), None))
4727}
4728
4729/// Parse all RLC, RL, RR, SLA, SRA flavors
4730/// RLC A
4731/// RLC B
4732/// RLC C
4733/// RLC D
4734/// RLC E
4735/// RLC H
4736/// RLC L
4737/// RLC (HL)
4738/// RLC (IX+n)
4739/// RLC (IY+n)
4740#[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        // hidden opcodes
4757        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/// TODO reduce the flag space for jr"],
4781#[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()), // not possible for call and for jp/jr when there is flag
4804                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        // Allow to parse JP HL as to be JP (HL) original notation is misleading
4816        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/// ...
4840#[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// XXX to remove as soon as possible
4857// named_attr!(#[doc="TODO"],
4858// parse_dollar <&str, Expr>, do_parse!(
4859// tag!("$") >>
4860// (Expr::Label(String::from("$")))
4861// )
4862// );
4863
4864/// Parse any standard 16bits register
4865/// TODO rename to emphasize it is standard reigsters
4866#[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/// Parse any standard 16bits register
4889/// TODO rename to emphasize it is standard reigsters
4890#[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/// Parse register i
4929#[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/// Parse register r
4942#[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        /// Parse register $char
4957        #[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/// Produce the function that parse a given register
4976#[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        /// Parse the $char register and return it as a DataAccess
5001        #[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/// Parse the IX register
5016#[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/// Parse the IY register
5027#[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
5037// TODO find a way to not use that
5038macro_rules! parse_any_indexregister8 {
5039    ($($reg:ident, $alias1:ident, $alias2:ident)*) => {$(
5040        paste::paste! {
5041            /// Parse register $reg
5042            #[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/// Parse and indexed register in 8bits
5073#[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/// Parse a 16 bits indexed register
5088#[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/// Parse the use of an indexed register as (IX + 5)"
5110#[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/// Parse (C) used in in/out
5150#[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/// Parse (nn) used in in/out
5165#[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/// Parse an address access `(expression)`
5180#[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 filter = |c: u8| {
5184    // c == b'/'
5185    // || c == b'+'
5186    // || c == b'='
5187    // || c == b'-'
5188    // || c == b'*'
5189    // || c == b'<'
5190    // || c == b'>'
5191    // || c == b'%'
5192    // || c == b'&'
5193    // || c == b'|'
5194    // };
5195    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                // filter expressions ; they are followed by some operators
5203                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/// Parse (R16)
5223#[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/// Parse (HL)
5251#[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/// Parse (ix) and (iy)
5278#[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/// Parse an expression and returns it inside a DataAccession::Expression
5299#[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
5306/// Parse standard org directive
5307pub 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
5315/// Parse defs instruction. TODO add optional parameters
5316pub 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
5341/// Parse any opcode having no argument
5342pub 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    // http://rasm.wikidot.com/directives:repete
5398    // Some instructions may have repeated counts, so we modify them
5399    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    // TODO parse inner with filtering on the allowed operations
5447    // would be easier to write and would allow conditional operations
5448    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/// Parse a comment that start by `;` and ends at the end of the line.
5547#[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/// TODO
5575#[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/// Parse a label(label: S)
5582/// TODO reimplement to never build a string
5583#[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'\'' // orgams additions
5603                    )).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'\'' // orgams additions
5613                    )
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
5648/*
5649        // fail to parse a label when it is 100% sure it corresponds to  a macro call
5650        let (macro_arg) = opt(preceded(space1, Caseless("(void)".into()))).parse_next(input)?;
5651        if macro_arg.is_some() {
5652            return Err(cpclib_common::nom::ErrMode::Backtrack(error_position!(
5653                input,
5654                ErrorKind::OneOf
5655            )));
5656        }
5657*/
5658        let 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        //needed because of AT2
5667        let input = if doubledots {
5668            let _ =opt(Caseless(":")).parse_next(input)?;
5669            input
5670        }
5671        else {
5672            input
5673        };
5674
5675
5676        // Be sure that ::ld is not considered to be a label
5677        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// Parse an ASM file an returns the stream of tokens.
5800// pub fn parse_file(fname: String) -> Vec<Token> {
5801// let mut f = File::open(fnmae).expect(format!("{} not found", fname));
5802// let mut contents = String::new();
5803// f.read_to_string(&mut contents)
5804// .expect(format!("Something went wrong reading {}", fname));
5805//
5806//
5807// parse_binary_stream(fname.to_bytes())
5808// }
5809
5810// XXX Code greatly inspired from https://github.com/Geal/nom/blob/master/tests/arithmetic_ast.rs
5811
5812/// Read a value
5813#[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/// Parse a repetition counter
5823#[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), // BUG will accept too many cases
5830        (b'}', not(alphanumeric1))
5831    )
5832    .take()
5833    .map(|l| LocatedExpr::Label(cloned.update_slice(l).into()))
5834    .parse_next(input)
5835}
5836
5837/// Read a parenthesed expression
5838#[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/// Get a factor
5896#[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            // Manage functions
5921            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            // manage values
5928            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            // manage $ and $$
5939            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            // manage labels
5955            parse_label(false).map(|l| LocatedExpr::Label(l.into())),
5956            parens
5957        )) /* ,
5958            * my_space0 */
5959    )
5960    .parse_next(input)?;
5961
5962    // XXX I have replaced Neg by Not, this seems the most coherent stuff
5963    // XXX Need to check later
5964    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/// Compute operations related to * % /
6074#[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/// Generate a parser of comparison symbol
6097/// inner: the function the parse the right operand of the symbol
6098/// pattern: the pattern to match in the source code
6099/// symbol: the symbol corresponding to the operation
6100#[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        // for orgams we cannot accept * as being a mulitplication if it is followed by another * as it repreents a repetition
6117        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/// Parse an expression
6150#[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/// TODO replace ALL expr parse by a located version
6182#[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/// parse functions with one argument
6206#[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/// parse functions with two arguments
6254#[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        (/* space0, */ "(", 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/// Parser for functions taking into argument a token
6312#[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
6330/// Parse the duration function
6331pub 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
6342/// Parse the single opcode assembling function
6343pub 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/// Parse operation related to + - & |
6378#[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), /* TODO check if it works and not compete with && */
6389        parse_oper(term, "AND", BinaryOperation::BinaryAnd),
6390        parse_oper(term, "|", BinaryOperation::BinaryOr), /* TODO check if it works and not compete with || */
6391        parse_oper(term, "OR", BinaryOperation::BinaryOr),
6392        parse_oper(term, "^", BinaryOperation::BinaryXor), /* TODO check if it works and not compete with ^^ */
6393        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// Test are deactivated, API is not enough stabilized and tests are broken
6404#[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"); // TODO code that
6647        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        // breakpoint keyword has alrady been consumed
6763        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        //   assert_eq!(res.clone().unwrap().0.trim().len(), 0, "{:?}", res);
6853    }
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    // TODO find why this test fails wheras cpclib_common::tests::parse_string succeed. I do not get the differences
7303    #[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}