csx64/asm/
asm_args.rs

1use std::iter::{self, Peekable};
2use std::str::CharIndices;
3use std::cmp::Ordering;
4use rug::{Integer, Float};
5
6use super::*;
7use super::expr::{ExprData, OP, Value, FLOAT_PRECISION};
8use super::caseless::Caseless;
9
10#[cfg(test)]
11use super::expr::ValueCow;
12
13// advances the cursor iterator to the specified character index.
14// end_pos is the exclusive upper bound index of cursor.
15fn advance_cursor(cursor: &mut Peekable<CharIndices>, to: usize, end_pos: usize) {
16    loop {
17        match cursor.peek().copied() {
18            None => return assert_eq!(to, end_pos),
19            Some((p, _)) => {
20                if p < to { cursor.next(); }
21                else if p == to { return }
22                else { panic!() }
23            }
24        }
25    }
26}
27#[test]
28fn test_advance_cursor() {
29    let mut cursor = "hello world".char_indices().peekable();
30    assert_eq!(cursor.peek().unwrap().0, 0);
31    advance_cursor(&mut cursor, 5, 11);
32    assert_eq!(cursor.peek().unwrap().0, 5);
33    advance_cursor(&mut cursor, 5, 11);
34    assert_eq!(cursor.peek().unwrap().0, 5);
35    advance_cursor(&mut cursor, 10, 11);
36    assert_eq!(cursor.peek().unwrap().0, 10);
37    advance_cursor(&mut cursor, 11, 11);
38    assert_eq!(cursor.peek(), None);
39    advance_cursor(&mut cursor, 11, 11);
40    assert_eq!(cursor.peek(), None);
41}
42
43fn next_nonwhite_pos(s: &str, pos: usize) -> Option<usize> {
44    s[pos..].find(|c: char| !c.is_whitespace()).map(|p| p + pos)
45}
46#[test]
47fn test_next_nonwhite_pos() {
48    assert_eq!(next_nonwhite_pos("hello    world", 0), Some(0));
49    assert_eq!(next_nonwhite_pos("hello    world", 2), Some(2));
50    assert_eq!(next_nonwhite_pos("hello    world", 4), Some(4));
51    assert_eq!(next_nonwhite_pos("hello    world", 5), Some(9));
52    assert_eq!(next_nonwhite_pos("hello    world", 8), Some(9));
53    assert_eq!(next_nonwhite_pos("hello    world", 9), Some(9));
54    assert_eq!(next_nonwhite_pos("hello    world", 10), Some(10));
55    assert_eq!(next_nonwhite_pos("hello    world", 13), Some(13));
56    assert_eq!(next_nonwhite_pos("hello    world", 14), None);
57    assert_eq!(next_nonwhite_pos("hello    world       ", 14), None);
58    assert_eq!(next_nonwhite_pos("hello    world       ", 16), None);
59}
60
61fn parse_size_str(val: &str, success: usize, failure: usize) -> (Option<Size>, usize) {
62    match SIZE_KEYWORDS.get(&Caseless(val)) {
63        Some(size) => (Some(*size), success),
64        None => (None, failure),
65    }
66}
67
68#[derive(Clone, Copy, PartialEq, Eq, Debug)]
69pub(super) struct TimesInfo {
70    pub(super) total_count: u64,
71    pub(super) current: u64,
72}
73
74#[derive(Clone, Copy, PartialEq, Eq, Debug)]
75pub(super) enum Locality {
76    Local,
77    Nonlocal,
78}
79
80pub(super) struct AssembleArgs<'a> {
81    pub(super) file_name: &'a str,
82    pub(super) file: ObjectFile,
83    
84    pub(super) current_seg: Option<AsmSegment>,
85    pub(super) done_segs: Vec<AsmSegment>,
86
87    pub(super) line_num: usize,
88    pub(super) line_pos_in_seg: usize,
89
90    pub(super) last_nonlocal_label: Option<String>,
91    pub(super) label_def: Option<(String, Locality)>,
92
93    pub(super) times: Option<TimesInfo>,
94}
95impl AssembleArgs<'_> {
96    /// Updates the segment positioning info.
97    /// This must be called prior to parsing a line (includingthe header), and once before each first-order assembly action (times iter).
98    pub(super) fn update_line_pos_in_seg(&mut self) {
99        match self.current_seg {
100            None => (),
101            Some(AsmSegment::Text) => self.line_pos_in_seg = self.file.text.len(),
102            Some(AsmSegment::Rodata) => self.line_pos_in_seg = self.file.rodata.len(),
103            Some(AsmSegment::Data) => self.line_pos_in_seg = self.file.data.len(),
104            Some(AsmSegment::Bss) => self.line_pos_in_seg = self.file.bss_len,
105        }
106    }
107
108    // attempt to mutate a symbol name from the line, transforming local symbols names to their full name.
109    fn mutate_name(&self, name: &str, err_pos: usize) -> Result<(String, Locality), AsmError> {
110        if name.starts_with('.') {
111            // local can't be empty after dot or be followed by a digit (ambig floating point)
112            if name.len() <= 1 || name.chars().nth(1).unwrap().is_digit(10) { return Err(AsmError { kind: AsmErrorKind::InvalidSymbolName, line_num: self.line_num, pos: Some(err_pos), inner_err: None }); }
113            match &self.last_nonlocal_label {
114                None => return Err(AsmError { kind: AsmErrorKind::LocalSymbolBeforeNonlocal, line_num: self.line_num, pos: Some(err_pos), inner_err: None }),
115                Some(nonlocal) => {
116                    let mutated = format!("{}{}", nonlocal, name);
117                    if !is_valid_symbol_name(&mutated) { return Err(AsmError { kind: AsmErrorKind::InvalidSymbolName, line_num: self.line_num, pos: Some(err_pos), inner_err: None }); }
118                    Ok((mutated, Locality::Local))
119                }
120            }
121        }
122        else {
123            if !is_valid_symbol_name(name) { return Err(AsmError { kind: AsmErrorKind::InvalidSymbolName, line_num: self.line_num, pos: Some(err_pos), inner_err: None }); }
124            Ok((name.into(), Locality::Nonlocal))
125        }
126    }
127
128    // attempts to read a binary op from the string, allowing leading whitespace.
129    // if a binary op is present, returns the op and the character index just after it, otherwise returns None.
130    fn extract_binary_op(&self, raw_line: &str, raw_start: usize, raw_stop: usize) -> Option<(OP, usize)> {
131        let mut pos = raw_line[raw_start..raw_stop].char_indices();
132
133        loop {
134            match pos.next() {
135                None => return None,
136                Some((p, c)) => {
137                    if c.is_whitespace() { continue; }
138
139                    let val = &raw_line[raw_start + p..];
140                    for (repr, op) in BINARY_OP_STR.iter() {
141                        if val.starts_with(repr) {
142                            return Some((*op, raw_start + p + repr.len()));
143                        }
144                    }
145                    return None;
146                }
147            }
148        }
149    }
150
151    // attempts to read a delimited string literal from the string, allowing leading whitespace.
152    // if a string is present, returns Ok with the binary string contents and the character index just after its ending quote, otherwise returns Err.
153    fn extract_string(&self, raw_line: &str, raw_start: usize, raw_stop: usize) -> Result<(Vec<u8>, usize), AsmError> {
154        // find the next starting quote char
155        let mut pos = raw_line[raw_start..raw_stop].char_indices();
156        let (quote_pos, quote_char) = loop {
157            match pos.next() {
158                None => return Err(AsmError { kind: AsmErrorKind::ExpectedString, line_num: self.line_num, pos: Some(raw_stop), inner_err: None }),
159                Some((p, ch)) => {
160                    if ['\'', '"'].contains(&ch) {
161                        break (p, ch);
162                    }
163                    else if !ch.is_whitespace() {
164                        return Err(AsmError { kind: AsmErrorKind::ExpectedString, line_num: self.line_num, pos: Some(raw_start + p), inner_err: None });
165                    }
166                }
167            }
168        };
169
170        let mut res = vec![];
171        let mut buf = [0u8; 4];
172
173        // consume the entire string, applying escape sequences as needed
174        loop {
175            match pos.next() {
176                None => return Err(AsmError { kind: AsmErrorKind::IncompleteString, line_num: self.line_num, pos: Some(raw_start + quote_pos), inner_err: None }),
177                Some((p, ch)) => {
178                    if ch == quote_char {
179                        return Ok((res, raw_start + p + 1));
180                    }
181                    else if ch == '\\' {
182                        match pos.next() {
183                            None => return Err(AsmError { kind: AsmErrorKind::IncompleteEscape, line_num: self.line_num, pos: Some(raw_start + p), inner_err: None }),
184                            Some((_, esc)) => {
185                                let mapped = match esc {
186                                    '\\' => Some('\\'),
187                                    '\'' => Some('\''),
188                                    '"' => Some('"'),
189                                    'n' => Some('\n'),
190                                    't' => Some('\t'),
191                                    'r' => Some('\r'),
192                                    '0' => Some('\0'),
193                                    'x' => {
194                                        let mut vals = [0; 2];
195                                        for val in vals.iter_mut() {
196                                            *val = match pos.next().map(|(_, x)| x.to_digit(16)).flatten() {
197                                                None => return Err(AsmError { kind: AsmErrorKind::IncompleteEscape, line_num: self.line_num, pos: Some(raw_start + p), inner_err: None }),
198                                                Some(v) => v,
199                                            };
200                                        }
201                                        let val = vals[0] * 16 + vals[1];
202                                        res.push(val as u8);
203                                        None
204                                    }
205                                    _ => return Err(AsmError { kind: AsmErrorKind::InvalidEscape, line_num: self.line_num, pos: Some(raw_start + p), inner_err: None }),
206                                };
207                                if let Some(mapped) = mapped {
208                                    res.extend(mapped.encode_utf8(&mut buf).as_bytes());
209                                }
210                            }
211                        }
212                    }
213                    else {
214                        res.extend(ch.encode_utf8(&mut buf).as_bytes());
215                    }
216                }
217            }
218        }
219    }
220
221    // attempts to parse a sequence of 1+ comma-separated expressions.
222    fn parse_comma_sep_exprs(&mut self, raw_line: &str, raw_start: usize, raw_stop: usize) -> Result<Vec<Expr>, AsmError> {
223        let mut args = vec![];
224        
225        let mut pos = raw_start;
226        loop {
227            // extract an expr and add it to args
228            let (expr, aft) = self.extract_expr(raw_line, pos, raw_stop)?;
229            args.push(expr);
230
231            // check if we have another arg after this one (comma)
232            let mut tail = raw_line[aft..raw_stop].char_indices();
233            loop {
234                match tail.next() {
235                    None => return Ok(args), // nothing after expr means we're done
236                    Some((p, c)) => {
237                        if c.is_whitespace() { continue; } // skip whitespace
238                        else if c == ',' { pos = aft + p + 1; break; } // if we have a comma, we expect another arg
239                        else { return Err(AsmError { kind: AsmErrorKind::ExpectedCommaBeforeToken, line_num: self.line_num, pos: Some(aft + p, ), inner_err: None }); }
240                    }
241                }
242            }
243        }
244    }
245
246    // attempts to extract an expression from the string, allowing leading whitespace.
247    // if a well-formed expression is found, returns Ok with it and the character index just after it, otherwise returns Err.
248    fn extract_expr(&mut self, raw_line: &str, raw_start: usize, raw_stop: usize) -> Result<(Expr, usize), AsmError> {
249        let mut parsing_pos = raw_start;
250
251        let mut unary_stack: Vec<OP> = Vec::with_capacity(8);
252        let mut binary_stack: Vec<OP> = Vec::with_capacity(8);
253
254        let mut output_stack: Vec<Expr> = Vec::with_capacity(8);
255
256        loop {
257            let mut chars = raw_line[parsing_pos..raw_stop].char_indices().peekable();
258
259            // consume all unary ops up to a token and push onto unary stack
260            debug_assert!(unary_stack.is_empty());
261            let (term_start, numeric) = loop {
262                match chars.peek().copied() {
263                    None => return Err(AsmError { kind: AsmErrorKind::ExpectedExpr, line_num: self.line_num, pos: Some(raw_stop), inner_err: None }),
264                    Some((_, x)) if x.is_whitespace() || x == '+' => (), // whitespace and unary plus do nothing
265                    Some((_, '-')) => unary_stack.push(OP::Neg),         // push unary ops onto the stack
266                    Some((_, '!')) => unary_stack.push(OP::Not),
267                    Some((p, '~')) => return Err(AsmError { kind: AsmErrorKind::UseOfTildeNot, line_num: self.line_num, pos: Some(parsing_pos + p), inner_err: None }),
268                    Some((p, c)) => break (parsing_pos + p, c.is_digit(10)), // otherwise is a token, which also means end of term
269                }
270                chars.next(); // if we get here, we consumed it
271            };
272
273            // move to next logical separator (white space, open paren, or binary op - but only at depth 0)
274            // bin_op holds op and aft, token_stop holds one past end of token
275            let mut paren_positions = vec![]; // holds actual position
276            let (term_stop, bin_op) = loop {
277                let end_content = chars.peek().cloned();
278                match end_content {
279                    // if there's not a next character we're either done (depth 0), or failed
280                    None => {
281                        if paren_positions.is_empty() { break (raw_stop, None); }
282                        else { return Err(AsmError { kind: AsmErrorKind::UnclosedParen, line_num: self.line_num, pos: Some(paren_positions.last().copied().unwrap()), inner_err: None }); }
283                    }
284                    // otherwise account for important characters
285                    Some((p, ch)) => match ch {
286                        '(' => {
287                            if numeric { // if we find the start of a parenthesized group in numeric mode, that's the end of the number (and there is no op)
288                                break (parsing_pos + p, None)
289                            }
290                            paren_positions.push(parsing_pos + p);
291                        }
292                        ')' => match paren_positions.len() {
293                            0 => break (parsing_pos + p, None), // if we find an unmatched close paren, this is the end of expr (and there is no op)
294                            1 => match self.extract_binary_op(raw_line, parsing_pos + p + 1, raw_stop) { // this would drop down to level 0, so end of term
295                                Some((op, aft)) => break (parsing_pos + p + 1, Some((op, aft))),
296                                None => break (parsing_pos + p + 1, None),
297                            }
298                            _ => { paren_positions.pop(); }
299                        }
300                        '"' | '\'' => {
301                            if numeric { // if we find the start of a string in numeric mode, that's the end of the number (and there is no op)
302                                break (parsing_pos + p, None)
303                            }
304                            let (_, aft) = self.extract_string(raw_line, parsing_pos + p, raw_stop)?;  // if we run into a string, refer to the string extractor to get aft
305                            advance_cursor(&mut chars, aft - 1 - parsing_pos, raw_stop); // jump to just before aft position (the end quote) (account for base index change)
306                            debug_assert_ne!(chars.peek().unwrap().0, p);
307                            debug_assert_eq!(chars.peek().unwrap().1, ch); // sanity check: should not be same position, but should be same char
308                        }
309                        'e' | 'E' if numeric => {
310                            if let Some((_, x)) = chars.clone().nth(1) {  // look at next char
311                                if x == '+' || x == '-' { chars.next(); } // make sure an exponent sign won't be parsed as binary + or - by skipping it
312                            }
313                        }
314                        _ => {
315                            if paren_positions.is_empty() {
316                                if let Some((op, aft)) = self.extract_binary_op(raw_line, parsing_pos + p, raw_stop) {
317                                    break (parsing_pos + p, Some((op, aft))); // if we find a binary op, we're done
318                                }
319                                else if ch.is_whitespace() || ch == ',' || ch == ']' || ch == '}' || ch == COMMENT_CHAR {
320                                    break (parsing_pos + p, None); // otherwise if we're on a term-breaking char we're done (but we have no binary op)
321                                }
322                            }
323                        }
324                    }
325                }
326                chars.next(); // if we get here, we consumed the char
327            };
328            drop(chars); // we're done with this now and it's not guaranteed to be in correct position - drop it so it can't accidentally be used again
329
330            // grab the term we just found
331            let term = &raw_line[term_start..term_stop];
332            debug_assert_eq!(term, term.trim());
333            if term.is_empty() { return Err(AsmError { kind: AsmErrorKind::ExpectedExpr, line_num: self.line_num, pos: Some(term_start), inner_err: None }); }
334
335            let term_expr = match term.chars().next().unwrap() {
336                '(' => { // if it's a sub-expression (paren grouped expr)
337                    debug_assert_eq!(term.chars().rev().next().unwrap(), ')'); // last char of term should be a closing paren
338                    let (expr, aft) = self.extract_expr(raw_line, term_start + 1, term_stop - 1)?; // parse interior as an expr
339                    match raw_line[aft..term_stop-1].trim_start().chars().next() {
340                        None => (),
341                        Some(x) if x == COMMENT_CHAR => return Err(AsmError { kind: AsmErrorKind::UnclosedParen, line_num: self.line_num, pos: Some(term_start), inner_err: None }),
342                        Some(_) => return Err(AsmError { kind: AsmErrorKind::ParenInteriorNotExpr, line_num: self.line_num, pos: Some(term_start), inner_err: None }), // we should be able to consume the whole interior
343                    }
344                    expr
345                }
346                '$' => match term { // if it's a user-level macro
347                    "$" => match self.current_seg { // current line macro
348                        None => return Err(AsmError { kind: AsmErrorKind::AddressOutsideOfSegment, line_num: self.line_num, pos: Some(term_start), inner_err: None }),
349                        Some(seg) => (OP::Add, ExprData::Ident(get_seg_offset_str(seg).into()), self.line_pos_in_seg as i64).into(),
350                    }
351                    "$$" => match self.current_seg { // start of seg macro
352                        None => return Err(AsmError { kind: AsmErrorKind::AddressOutsideOfSegment, line_num: self.line_num, pos: Some(term_start), inner_err: None }),
353                        Some(seg) => ExprData::Ident(get_seg_origin_str(seg).into()).into(),
354                    }
355                    "$file" => Value::Binary(self.file_name.as_bytes().into()).into(),
356                    "$i" => match &self.times { // times iter macro
357                        None => return Err(AsmError { kind: AsmErrorKind::TimesIterOutisideOfTimes, line_num: self.line_num, pos: Some(term_start), inner_err: None }),
358                        Some(info) => (info.current as u64).into(),
359                    }
360                    _ => { // otherwise it is either invalid or function-like - assume it's function-like
361                        let paren_pos = match term.find('(') {
362                            None => return Err(AsmError { kind: AsmErrorKind::UnrecognizedMacroInvocation, line_num: self.line_num, pos: Some(term_start), inner_err: None }),
363                            Some(p) => p,
364                        };
365                        if term.chars().rev().next() != Some(')') {
366                            return Err(AsmError { kind: AsmErrorKind::UnrecognizedMacroInvocation, line_num: self.line_num, pos: Some(term_start), inner_err: None });
367                        }
368                        let func = &term[..paren_pos];
369                        let args = self.parse_comma_sep_exprs(raw_line, term_start + paren_pos + 1, term_stop - 1)?;
370
371                        fn chain_encode(args: Vec<Expr>, encoder: OP, line_num: usize, term_start: usize) -> Result<Expr, AsmError> {
372                            if args.len() == 0 { return Err(AsmError { kind: AsmErrorKind::ArgsExpectedCountAtLeast(1), line_num, pos: Some(term_start), inner_err: None }); }
373                            Ok(Expr::chain_add(args.into_iter().map(|x| Expr::from((encoder, x))).collect()).unwrap())
374                        }
375                        match func {
376                            "$if" => {
377                                if args.len() != 3 { return Err(AsmError { kind: AsmErrorKind::ArgsExpectedCount(&[3]), line_num: self.line_num, pos: Some(term_start), inner_err: None }); }
378                                let mut args = args.into_iter();
379                                let cond = args.next().unwrap();
380                                let left = args.next().unwrap();
381                                let right = args.next().unwrap();
382                                (OP::Condition, cond, Expr::from((OP::Pair, left, right))).into()
383                            }
384
385                            "$eb" => chain_encode(args, OP::EncodeBin8, self.line_num, term_start)?,
386                            "$ew" => chain_encode(args, OP::EncodeBin16, self.line_num, term_start)?,
387                            "$ed" => chain_encode(args, OP::EncodeBin32, self.line_num, term_start)?,
388                            "$eq" => chain_encode(args, OP::EncodeBin64, self.line_num, term_start)?,
389                            "$et" => chain_encode(args, OP::EncodeBin80, self.line_num, term_start)?,
390                            
391                            "$db" => Expr::from((OP::Intern, chain_encode(args, OP::EncodeBin8, self.line_num, term_start)?)),
392                            "$dw" => Expr::from((OP::Intern, chain_encode(args, OP::EncodeBin16, self.line_num, term_start)?)),
393                            "$dd" => Expr::from((OP::Intern, chain_encode(args, OP::EncodeBin32, self.line_num, term_start)?)),
394                            "$dq" => Expr::from((OP::Intern, chain_encode(args, OP::EncodeBin64, self.line_num, term_start)?)),
395                            "$dt" => Expr::from((OP::Intern, chain_encode(args, OP::EncodeBin80, self.line_num, term_start)?)),
396
397                            _ => match UNARY_FUNCTION_OPERATOR_TO_OP.get(func).copied() {
398                                Some(op) => {
399                                    if args.len() != 1 { return Err(AsmError { kind: AsmErrorKind::ArgsExpectedCount(&[1]), line_num: self.line_num, pos: Some(term_start), inner_err: None }); }
400                                    (op, args.into_iter().next().unwrap()).into()
401                                }
402                                None => return Err(AsmError { kind: AsmErrorKind::UnrecognizedMacroInvocation, line_num: self.line_num, pos: Some(term_start), inner_err: None }),
403                            }
404                        }
405                    }
406                }
407                str_char @ '\'' | str_char @ '"' => {
408                    debug_assert_eq!(term.chars().rev().next().unwrap(), term.chars().next().unwrap()); // first and last char should be the same
409                    let (content, _) = self.extract_string(raw_line, term_start, term_stop)?;
410                    match str_char {
411                        '"' => ExprData::Value(Value::Binary(content)).into(),
412                        '\'' => match String::from_utf8(content) {
413                            Err(_) => return Err(AsmError { kind: AsmErrorKind::CharacterLiteralNotUnicode, line_num: self.line_num, pos: Some(term_start), inner_err: None }),
414                            Ok(string) => {
415                                let mut chars = string.chars();
416                                let res = chars.next();
417                                if res.is_none() || chars.next().is_some() {
418                                    return Err(AsmError { kind: AsmErrorKind::CharacterLiteralNotSingleChar, line_num: self.line_num, pos: Some(term_start), inner_err: None });
419                                }
420                                ExprData::Value(Value::Character(res.unwrap())).into()
421                            }
422                        }
423                        _ => unreachable!(),
424                    }
425                    
426                }
427                '0'..='9' => { // if it's a numeric constant
428                    let (term_fix, radix) = match term { // check for radix prefix and remove it if present
429                        x if x.starts_with("0x") => (&x[2..], 16),
430                        x if x.starts_with("0o") => (&x[2..], 8),
431                        x if x.starts_with("0b") => (&x[2..], 2),
432                        x => (x, 10),
433                    };
434                    let term_fix = { // trim off all leading and trailing underscores (might be after a prefix)
435                        let start = term_fix.find(|c: char| c != '_').unwrap_or(term_fix.len());
436                        let term_fix = &term_fix[start..];
437                        let stop = term_fix.rfind(|c: char| c != '_').map(|p| p + 1).unwrap_or(0);
438                        &term_fix[..stop]
439                    };
440                    // terms should not have signs - this should be exclusively handled by unary ops
441                    debug_assert!(!term_fix.starts_with('+') && !term_fix.starts_with('-'));
442                
443                    // first, try to parse the value as an integer
444                    match Integer::from_str_radix(&term_fix, radix) {
445                        Ok(v) => {
446                            if radix == 10 && term_fix.len() > 1 && term_fix.starts_with('0') { // disambig from C-style octal literals, which we do not support
447                                return Err(AsmError { kind: AsmErrorKind::NumericLiteralWithZeroPrefix, line_num: self.line_num, pos: Some(term_start), inner_err: None });
448                            }
449                            v.into()
450                        }
451                        Err(_) => {
452                            // if we had a prefix, it was supposed to be an integer (failure)
453                            if radix != 10 { return Err(AsmError { kind: AsmErrorKind::IllFormedNumericLiteral, line_num: self.line_num, pos: Some(term_start), inner_err: None }); }
454
455                            // otherwise we can attempt to parse as float
456                            match Float::parse(term_fix) { // failed signed (int) could just mean that it's (signed) float
457                                Err(_) => return Err(AsmError { kind: AsmErrorKind::IllFormedNumericLiteral, line_num: self.line_num, pos: Some(term_start), inner_err: None }),
458                                Ok(v) => Float::with_val(FLOAT_PRECISION, v).into(),
459                            }
460                        }
461                    }
462                }
463                _ => { // otherwise it must be an keyword/identifier - keywords are always case insensitive
464                    if Caseless(term) == Caseless("TRUE") { true.into() }
465                    else if Caseless(term) == Caseless("FALSE") { false.into() }
466                    else if Caseless(term) == Caseless("NULL") { Value::Integer(Integer::new()).into() }
467                    else if Caseless(term) == Caseless("EOF") { Value::Integer((-1).into()).into() }
468                    else { // if none of above, must be an identifier
469                        let (mutated, _) = self.mutate_name(term, term_start)?;
470                        ExprData::Ident(mutated).into()
471                    }
472                }
473            };
474
475            // update parsing pos - either after term (no bin op) or after bin op
476            parsing_pos = match bin_op {
477                None => term_stop,
478                Some((_, aft)) => aft,
479            };
480
481            // add the term to the output
482            output_stack.push(term_expr);
483
484            // apply any unary ops to the term before we begin
485            while let Some(op) = unary_stack.pop() {
486                let last = output_stack.pop().unwrap();
487                output_stack.push((op, last).into());
488            }
489
490            // handle the bin op (if present)
491            match bin_op {
492                Some((op, _)) => { // if there's an op, we need to handle precedence logic (shunting-yard algorithm)
493                    // handle any required ops that are still on the stack
494                    let op_prec = *PRECEDENCE.get(&op).unwrap();
495                    loop {
496                        let top = match binary_stack.last() {
497                            None => break,
498                            Some(op) => *op,
499                        };
500                        let top_prec = *PRECEDENCE.get(&top).unwrap();
501                        if top_prec.0 >= op_prec.0 && (top_prec.0 != op_prec.0 || op_prec.1 != Associativity::Left) {
502                            break;
503                        }
504
505                        // pop off op stack and put on output stack (but resolve the tree structure immediately)
506                        binary_stack.pop();
507                        let right = output_stack.pop().unwrap();
508                        let left = output_stack.pop().unwrap();
509                        output_stack.push((top, left, right).into()); // plop it back onto the output queue
510                    }
511
512                    // push this op onto the stack
513                    binary_stack.push(op);
514                }
515                None => {
516                    break; // if there wasn't a bin op, we're done parsing
517                }
518            }
519        }
520
521        // pop any remaining binary ops off the stack
522        while let Some(op) = binary_stack.pop() {
523            let right = output_stack.pop().unwrap();
524            let left = output_stack.pop().unwrap();
525            output_stack.push((op, left, right).into()); // plop it back onto the output queue
526        }
527
528        // there should now be only one thing in output, which is the result
529        debug_assert_eq!(output_stack.len(), 1);
530        let res = self.apply_ptrdiff(output_stack.into_iter().next().unwrap());
531
532        Ok((res, parsing_pos))
533    }
534
535    fn get_ptr_offset<'a>(&'a self, expr: &'a Expr, base: &str) -> Option<Expr> {
536        let target = match &*expr.data.borrow() {
537            ExprData::Value(_) => return None,
538            ExprData::Ident(ident) => {
539                if ident == base { return Some(0.into()); } // if this is the base itself, offset is zero (signed because we want the offset value)
540                match self.file.symbols.get(ident) {
541                    None => return None,
542                    Some((symbol, _)) => symbol,
543                }
544            }
545            ExprData::Uneval { .. } => expr,
546        };
547        match &*target.data.borrow() {
548            ExprData::Uneval { op: OP::Add, left, right } => match &*left.as_ref().unwrap().data.borrow() { // unwraps are ok cause we know we generated the value just before
549                ExprData::Ident(ident) if ident == base => match &*right.as_ref().unwrap().data.borrow() {
550                    ExprData::Value(Value::Integer(v)) => Some(v.clone().into()), // rhs of address should always be an integer constant (never needs to be evaluated)
551                    _ => panic!("address improperly constructed"),
552                }
553                _ => None,
554            }
555            _ => None,
556        }
557    }
558    // applies ptrdiff logic (e.g. $-$ == 0) to an expr and returns the resulting expression.
559    // if no ptrdiff logic can be performed, returns the original expression,
560    // otherwise returns a modified expression which is guaranteed to yield the same value.
561    fn apply_ptrdiff(&self, expr: Expr) -> Expr {
562        let (mut add, mut sub) = expr.break_add_sub();
563        for base in PTRDIFF_IDS { // look for add/sub pairs that have a common base
564            let a = add.iter_mut().filter_map(|x| self.get_ptr_offset(x, base).map(|r| (x, r)));
565            let b = sub.iter_mut().filter_map(|x| self.get_ptr_offset(x, base).map(|r| (x, r)));
566            for (a, b) in a.zip(b) {
567                *a.0 = a.1; // every time we get a pair, replace them with their offset values
568                *b.0 = b.1;
569            }
570        }
571
572        // recurse to non-leaf children
573        let recurse = |x: Expr| match x.data.into_inner() {
574            x @ ExprData::Value(_) => Expr::from(x),
575            x @ ExprData::Ident(_) => Expr::from(x),
576            ExprData::Uneval { op, left, right } => {
577                let left = left.map(|x| Box::new(self.apply_ptrdiff(*x)));
578                let right = right.map(|x| Box::new(self.apply_ptrdiff(*x)));
579                ExprData::Uneval { op, left, right }.into()
580            }
581        };
582        let add = add.into_iter().map(recurse).collect();
583        let sub = sub.into_iter().map(recurse).collect();
584
585        Expr::chain_add_sub(add, sub).unwrap() // assemble the result
586    }
587
588    fn extract_imm(&mut self, raw_line: &str, raw_start: usize, raw_stop: usize) -> Result<(Imm, usize), AsmError> {
589        // check if we had an explicit size and get the expr start position (after size if present)
590        let (token, token_stop) = grab_alnum_token(raw_line, raw_start, raw_stop);
591        let (size, expr_start) = parse_size_str(token, token_stop, raw_start);
592
593        // and finally, read the expr
594        let (expr, aft) = self.extract_expr(raw_line, expr_start, raw_stop)?;
595        Ok((Imm { expr, size }, aft))
596    }
597    fn extract_address(&mut self, raw_line: &str, raw_start: usize, raw_stop: usize) -> Result<(Address, usize), AsmError> {
598        // check if we had an explicit size and get the expr start position (after size if present)
599        let (token, token_stop) = grab_whitespace_sep_token(raw_line, raw_start, raw_stop);
600        let addr_start = token_stop - token.len();
601        if token.is_empty() {
602            return Err(AsmError { kind: AsmErrorKind::ExpectedAddress, line_num: self.line_num, pos: Some(raw_stop), inner_err: None });
603        }
604        if let Some(p) = token.find('[') {
605            let token_fix = &token[..p];
606            if Caseless(token_fix) == Caseless("PTR") {
607                return Err(AsmError { kind: BadAddress::PtrSpecWithoutSize.into(), line_num: self.line_num, pos: Some(addr_start), inner_err: None });
608            }
609            if p != 0 && token_fix.chars().all(|c| c.is_ascii_alphanumeric()) { // if token has an open bracket that's not the first char then we have something bad
610                if parse_size_str(token_fix, 0, 0).0.is_some() { // if we can parse it as a size, then we're missing ptr spec
611                    return Err(AsmError { kind: BadAddress::SizeMissingPtr.into(), line_num: self.line_num, pos: Some(addr_start + p), inner_err: None });
612                }
613                else { // otherwise we have an unknown size
614                    return Err(AsmError { kind: BadAddress::SizeNotRecognized.into(), line_num: self.line_num, pos: Some(addr_start), inner_err: None });
615                }
616            }
617        }
618        if Caseless(token) == Caseless("PTR") {
619            return Err(AsmError { kind: BadAddress::PtrSpecWithoutSize.into(), line_num: self.line_num, pos: Some(addr_start), inner_err: None });
620        }
621        let (pointed_size, mut next_start) = parse_size_str(token, token_stop, raw_start);
622        match pointed_size {
623            Some(_) => { // explicit size requires the ptr specifier
624                let (mut token, mut token_stop) = grab_whitespace_sep_token(raw_line, next_start, raw_stop);
625                let bracket_pos = token.find('[');
626                if let Some(p) = bracket_pos {
627                    token_stop -= token.len() - p; // chop off at an open bracket if we have one
628                    token = &token[..p];
629                }
630
631                if Caseless(token) == Caseless("PTR") { next_start = token_stop; }
632                else {
633                    // look ahead to see if this is illegal
634                    let should_be_addr = match bracket_pos {
635                        Some(_) => token.chars().all(|c| c.is_ascii_alphanumeric()),
636                        None => {
637                            let (next_tok, _) = grab_whitespace_sep_token(raw_line, token_stop, raw_stop);
638                            next_tok.starts_with('[')
639                        }
640                    };
641                    if should_be_addr {
642                        return Err(AsmError { kind: BadAddress::SizeMissingPtr.into(), line_num: self.line_num, pos: Some(token_stop - token.len()), inner_err: None }); 
643                    } else {
644                        return Err(AsmError { kind: AsmErrorKind::ExpectedAddress, line_num: self.line_num, pos: Some(token_stop - token.len()), inner_err: None }); 
645                    }
646                }
647            }
648            None => { // if we failed to parse the size, we must start with an open bracket (just the address component)
649                if token.chars().next() != Some('[') {
650                    let should_be_addr = { // look ahead to see if this is illegal
651                        let (next_tok, _) = grab_whitespace_sep_token(raw_line, token_stop, raw_stop);
652                        match next_tok.find('[') {
653                            Some(p) => p == 0 || Caseless(&next_tok[..p]) == Caseless("PTR"),
654                            None => Caseless(next_tok) == Caseless("PTR")
655                        }
656                    };
657                    if should_be_addr {
658                        return Err(AsmError { kind: BadAddress::SizeNotRecognized.into(), line_num: self.line_num, pos: Some(addr_start), inner_err: None });
659                    } else {
660                        return Err(AsmError { kind: AsmErrorKind::ExpectedAddress, line_num: self.line_num, pos: Some(addr_start), inner_err: None });
661                    }
662                }
663            }
664        }
665
666        // after the size part, we need to find the start of the core address component [expr]
667        let address_start = match raw_line[next_start..raw_stop].find(|c: char| !c.is_whitespace()) {
668            None => return Err(AsmError { kind: AsmErrorKind::ExpectedAddress, line_num: self.line_num, pos: Some(raw_stop), inner_err: None }),
669            Some(p) => next_start + p,
670        };
671        if raw_line[address_start..].chars().next().unwrap() != '[' {
672            return Err(AsmError { kind: AsmErrorKind::ExpectedAddress, line_num: self.line_num, pos: Some(address_start), inner_err: None });
673        }
674        let (mut imm, imm_aft) = match self.extract_imm(raw_line, address_start + 1, raw_stop) {
675            Err(e) => return Err(AsmError { kind: BadAddress::BadBase.into(), line_num: self.line_num, pos: Some(address_start), inner_err: Some(Box::new(e)) }),
676            Ok(x) => x,
677        };
678        let (tail, tail_start) = trim_start_with_pos(raw_line, imm_aft, raw_stop);
679        match tail.chars().next() {
680            Some(']') => (),
681            None => return Err(AsmError { kind: BadAddress::Unterminated.into(), line_num: self.line_num, pos: Some(tail_start), inner_err: None }),
682            Some(_) => return Err(AsmError { kind: BadAddress::InteriorNotSingleExpr.into(), line_num: self.line_num, pos: Some(tail_start), inner_err: None }),
683        }
684        if let Some(size) = imm.size {
685            match size {
686                Size::Word | Size::Dword | Size::Qword => (),
687                _ => return Err(AsmError { kind: BadAddress::SizeUnsupported.into(), line_num: self.line_num, pos: Some(address_start), inner_err: None }),
688            }
689        }
690
691        // now we need to handle all the register arithmetic stuff
692        let mut r1: Option<(u8, u8)> = None; // reg id and multiplier
693        let mut r2: Option<u8> = None; // reg id
694        for reg in CPU_REGISTER_INFO.iter() {
695            if let Some(mult) = self.get_reg_mult(*reg.0, &imm.expr, raw_line, address_start)? { // see if this register is present in the expression
696                match imm.size {
697                    None => {
698                        match reg.1.size {
699                            Size::Word | Size::Dword | Size::Qword => (),
700                            _ => return Err(AsmError { kind: BadAddress::SizeUnsupported.into(), line_num: self.line_num, pos: Some(address_start), inner_err: None }),
701                        }
702                        imm.size = Some(reg.1.size); // if we don't already have a required size, set it to this register size
703                    }
704                    Some(size) => if size != reg.1.size { // otherwise enforce pre-existing value
705                        return Err(AsmError { kind: BadAddress::ConflictingSizes.into(), line_num: self.line_num, pos: Some(address_start), inner_err: None });
706                    }
707                }
708
709                let mut mult = match mult.eval(&self.file.symbols) { // if it is then it must be a critical expression
710                    Err(e) => return Err(AsmError { kind: BadAddress::RegMultNotCriticalExpr(e).into(), line_num: self.line_num, pos: Some(address_start), inner_err: None }),
711                    Ok(val) => match &*val {
712                        Value::Integer(v) => match v.to_u64() {
713                            None => return Err(AsmError { kind: BadAddress::InvalidRegMults.into(), line_num: self.line_num, pos: Some(address_start), inner_err: None }),
714                            Some(v) => v,
715                        }
716                        _ => unreachable!(), // get_reg_mult should ensure that this is impossible
717                    },
718                };
719                if mult == 0 { continue; } // if it's zero then it canceled out and we don't need it
720
721                // if the multiplier is trivial or has a trivial component (of 1)
722                if mult & 1 != 0 {
723                    mult &= !1; // remove the trivial component
724                    if r2.is_none() { r2 = Some(reg.1.id); } // prioritize putting it in r2 since r2 can't have a multiplier (other than 1)
725                    else if r1.is_none() { r1 = Some((reg.1.id, 0)); } // otherwise we have to put it in r1 and give it a multiplier of 1 (mult code 0)
726                    else { return Err(AsmError { kind: BadAddress::InvalidRegMults.into(), line_num: self.line_num, pos: Some(address_start), inner_err: None }); } // if we don't have anywhere to put it, failure
727                }
728                // now, if a (non-trivial) component is present
729                if mult != 0 {
730                    let multcode = match mult { // decode the multiplier into its sizecode equivalent
731                        1 => 0,
732                        2 => 1,
733                        4 => 2,
734                        8 => 3,
735                        _ => return Err(AsmError { kind: BadAddress::InvalidRegMults.into(), line_num: self.line_num, pos: Some(address_start), inner_err: None }),
736                    };
737
738                    if r1.is_none() { r1 = Some((reg.1.id, multcode)); }
739                    else { return Err(AsmError { kind: BadAddress::InvalidRegMults.into(), line_num: self.line_num, pos: Some(address_start), inner_err: None }); }
740                }
741            }
742        }
743
744        let address_size = imm.size.unwrap_or(Size::Qword); // if we still don't have a size, default to 64-bit addressing
745        let base = {
746            let present = match imm.expr.eval(&self.file.symbols) {
747                Err(e) => match e { 
748                    EvalError::Illegal(reason) => return Err(AsmError { kind: BadAddress::IllegalExpr(reason).into(), line_num: self.line_num, pos: Some(address_start), inner_err: None }),
749                    EvalError::UndefinedSymbol(_) => true, // a (recoverable) failure to evaluate means we can't statically elide it, so we assume it's present
750                }
751                Ok(v) => match &*v {
752                    Value::Integer(v) => *v != 0, // if it's nonzero we have to keep it
753                    t => return Err(AsmError { kind: BadAddress::TypeUnsupported(t.get_type()).into(), line_num: self.line_num, pos: Some(address_start), inner_err: None }), // if it's some other type it's invalid
754                }
755            };
756            if present { Some(imm.expr) } else { None }
757        };
758
759        Ok((Address { address_size, r1, r2, base, pointed_size }, tail_start + 1))
760    }
761    fn get_reg_mult(&self, reg: Caseless, expr: &Expr, raw_line: &str, err_pos: usize) -> Result<Option<Expr>, AsmError> {
762        let handle = &mut *expr.data.borrow_mut();
763        match handle {
764            ExprData::Value(_) => Ok(None),
765            ExprData::Ident(ident) => {
766                if Caseless(ident) == reg {
767                    *handle = 0.into(); // if we got a register, replace it with zero
768                    Ok(Some(1.into())) // report a multiplier of 1
769                }
770                else {
771                    Ok(None)
772                }
773            }
774            ExprData::Uneval { op, left, right } => {
775                let a = self.get_reg_mult(reg, left.as_ref().unwrap(), raw_line, err_pos)?;
776                match op {
777                    OP::Neg => {
778                        if let Some(_) = right.as_ref() { panic!(); }
779                        Ok(a.map(|t| (OP::Neg, t).into())) // just return the negative if we had something
780                    }
781                    OP::Add | OP::Sub => {
782                        let b = self.get_reg_mult(reg, right.as_ref().unwrap(), raw_line, err_pos)?;
783                        
784                        // if neither existed, return None, otherwise combine them with defaults of 0 if either is not present
785                        if a.is_none() && b.is_none() { Ok(None) }
786                        else { Ok(Some((*op, a.unwrap_or(0.into()), b.unwrap_or(0.into())).into())) }
787                    }
788                    OP::Mul => match a { // reg must not be present in both branches - this is done by allowing either as wildcard, which will always fail to evaluate
789                        Some(a) => Ok(Some((OP::Mul, a, (**right.as_ref().unwrap()).clone()).into())), // return what we got times the other side (currently unmodified due to not recursing to it)
790                        None => match self.get_reg_mult(reg, right.as_ref().unwrap(), raw_line, err_pos)? {
791                            Some(b) => Ok(Some((OP::Mul, (**left.as_ref().unwrap()).clone(), b).into())), // return what we got times the other side (currently unmodified do to left returning None)
792                            None => Ok(None), // if we got nothing for both, report nothing
793                        }
794                    }
795                    _ => { // for any other (unsuported) operation, just ensure that the register was not present
796                        if let Some(_) = a {
797                            return Err(AsmError { kind: BadAddress::RegIllegalOp.into(), line_num: self.line_num, pos: Some(err_pos), inner_err: None });
798                        }
799                        if let Some(v) = right.as_ref() {
800                            if let Some(_) = self.get_reg_mult(reg, v, raw_line, err_pos)? {
801                                return Err(AsmError { kind: BadAddress::RegIllegalOp.into(), line_num: self.line_num, pos: Some(err_pos), inner_err: None });
802                            }
803                        }
804                        Ok(None)
805                    }
806                }
807            }
808        }
809    }
810
811    fn extract_vpu_bracket_arg(&mut self, raw_line: &str, raw_start: usize, raw_stop: usize) -> Result<(Option<Argument>, usize), AsmError> {
812        match raw_line[raw_start..raw_stop].find(|c: char| !c.is_whitespace()).map(|v| v + raw_start) {
813            Some(open_pos) if raw_line[open_pos..].chars().next().unwrap() == '{' => {
814                let (arg, aft) = match self.extract_arg(raw_line, open_pos + 1, raw_stop) {
815                    Ok(v) => v,
816                    Err(e) => return Err(AsmError { kind: AsmErrorKind::VPUFailedToParseMaskArg, line_num: self.line_num, pos: Some(open_pos), inner_err: Some(Box::new(e)) }),
817                };
818                match raw_line[aft..raw_stop].find(|c: char| !c.is_whitespace()).map(|v| v + aft) {
819                    Some(close_pos) if raw_line[close_pos..].chars().next().unwrap() == '}' => Ok((Some(arg), close_pos + 1)),
820                    _ => Err(AsmError { kind: AsmErrorKind::VPUMaskUnclosedBracket, line_num: self.line_num, pos: Some(open_pos), inner_err: None }),
821                }
822            }
823            _ => Ok((None, raw_start)),
824        }
825    }
826    fn extract_vpu_mask(&mut self, soft_err_pos: usize, raw_line: &str, raw_start: usize, raw_stop: usize) -> Result<(Option<VPUMaskType>, usize), AsmError> {
827        match self.extract_vpu_bracket_arg(raw_line, raw_start, raw_stop)? {
828            (Some(mask), mask_aft) => {
829                match mask {
830                    Argument::Imm(imm) => { // this is just to give better error messages
831                        if imm.size.is_some() { return Err(AsmError { kind: AsmErrorKind::SizeSpecNotAllowed { index: None }, line_num: self.line_num, pos: Some(raw_start), inner_err: None }); }
832                        match imm.expr.to_ident() {
833                            Some(ident) if ident == "z" || ident == "Z" => return Err(AsmError { kind: AsmErrorKind::VPUZeroingWithoutOpmask, line_num: self.line_num, pos: Some(soft_err_pos), inner_err: None }),
834                            _ => return Err(AsmError { kind: AsmErrorKind::ArgumentInvalidType { index: None, got: ArgumentType::Imm, expected: &[ArgumentType::VPUMaskRegister] }, line_num: self.line_num, pos: Some(raw_start), inner_err: None }),
835                        }
836                    }
837                    Argument::VPUMaskRegister(reg) => {
838                        // id zero is a special encoding for no mask, so not allowed as an opmask
839                        if reg.id == 0 { return Err(AsmError { kind: AsmErrorKind::VPUOpmaskWasK0, line_num: self.line_num, pos: Some(soft_err_pos), inner_err: None }); }
840
841                        match self.extract_vpu_bracket_arg(raw_line, mask_aft, raw_stop)? {
842                            (Some(extra), extra_aft) => match extra {
843                                Argument::Imm(imm) => {
844                                    if imm.size.is_some() { return Err(AsmError { kind: AsmErrorKind::SizeSpecNotAllowed { index: None }, line_num: self.line_num, pos: Some(raw_start), inner_err: None }); }
845                                    match imm.expr.into_ident() {
846                                        Some(ident) if ident == "z" || ident == "Z" => Ok((Some(VPUMaskType::Zero(reg)), extra_aft)),
847                                        _ => return Err(AsmError { kind: AsmErrorKind::VPUMaskUnrecognizedMode, line_num: self.line_num, pos: Some(mask_aft), inner_err: None }),
848                                    }
849                                }
850                                x => return Err(AsmError { kind: AsmErrorKind::ArgumentInvalidType { index: None, got: x.get_type(), expected: &[ArgumentType::Ident] }, line_num: self.line_num, pos: Some(mask_aft), inner_err: None }),
851                            }
852                            (None, extra_aft) => Ok((Some(VPUMaskType::Blend(reg)), extra_aft)),
853                        }
854                    }
855                    x => return Err(AsmError { kind: AsmErrorKind::ArgumentInvalidType { index: None, got: x.get_type(), expected: &[ArgumentType::VPUMaskRegister] }, line_num: self.line_num, pos: Some(raw_start), inner_err: None }),
856                }
857            }
858            (None, _) => Ok((None, raw_start))
859        }
860    }
861
862    /// Attempts to extract an argument from the string, be it a register, address, or imm.
863    /// On success, returns the extracted argument and the index just after it.
864    /// On failure, the returned error is from imm extraction due to being the most general.
865    fn extract_arg(&mut self, raw_line: &str, raw_start: usize, raw_stop: usize) -> Result<(Argument, usize), AsmError> {
866        // first try named items since we can do this without copying anything
867        let (token, token_aft) = grab_alnum_token(raw_line, raw_start, raw_stop);
868        if let Some(reg) = CPU_REGISTER_INFO.get(&Caseless(token)) { return Ok((Argument::CPURegister(*reg), token_aft)); }
869        if let Some(reg) = FPU_REGISTER_INFO.get(&Caseless(token)) { return Ok((Argument::FPURegister(*reg), token_aft)); }
870        if let Some(reg) = VPU_MASK_REGISTER_INFO.get(&Caseless(token)) { return Ok((Argument::VPUMaskRegister(*reg), token_aft)); }
871        if let Some(seg) = SEGMENTS.get(&Caseless(token)) { return Ok((Argument::Segment(*seg), token_aft)); }
872        
873        // also try matching vpu register by name, but it has some extra parsing logic
874        if let Some(reg) = VPU_REGISTER_INFO.get(&Caseless(token)) {
875            let (mask, mask_aft) = self.extract_vpu_mask(token_aft - token.len(), raw_line, token_aft, raw_stop)?;
876            return Ok((Argument::VPURegister { reg: *reg, mask }, mask_aft)); 
877        }
878
879        // next, try address, since it could parse as an expr if given explicit size
880        match self.extract_address(raw_line, raw_start, raw_stop) {
881            Ok((addr, aft)) => {
882                let err_pos = next_nonwhite_pos(raw_line, raw_start).unwrap();
883                let (mask, mask_aft) = self.extract_vpu_mask(err_pos, raw_line, aft, raw_stop)?;
884                return Ok((Argument::Address { addr, mask }, mask_aft))
885            }
886            Err(e) => if let AsmErrorKind::BadAddress(_) = e.kind { return Err(e); } // if we know it was an address, fail here
887        }
888
889        // otherwise parse as imm
890        let (imm, aft) = self.extract_imm(raw_line, raw_start, raw_stop)?;
891        Ok((Argument::Imm(imm), aft))
892    }
893    
894    /// Attempts to extract the header of the given line.
895    /// This includes label_def, times, and instruction.
896    /// On success, returns the parsed instruction (if present) and one past the index of the last character extracted.
897    pub(super) fn extract_header(&mut self, raw_line: &str) -> Result<(Option<(Option<(Prefix, usize)>, (Instruction, usize))>, usize), AsmError> {
898        self.label_def = None;
899        self.times = None;
900
901        // grab a token - if it's empty or starts a comment, we're done
902        let mut token = grab_whitespace_sep_token(raw_line, 0, raw_line.len());
903        if token.0.is_empty() { return Ok((None, 0)); }
904        if token.0.ends_with(LABEL_DEF_CHAR) { // if we got a label, set it and grab another token
905            let mutated = self.mutate_name(&token.0[..token.0.len()-1], token.1 - token.0.len())?;
906            if is_reserved_symbol_name(&mutated.0) { return Err(AsmError { kind: AsmErrorKind::ReservedSymbolName, line_num: self.line_num, pos: Some(token.1 - token.0.len()), inner_err: None }); }
907            self.label_def = Some(mutated);
908
909            let new_token = grab_whitespace_sep_token(raw_line, token.1, raw_line.len());
910            if new_token.0.is_empty() { return Ok((None, token.1)); }
911            token = new_token;
912        }
913        if Caseless(token.0) == Caseless("TIMES") { // if we got a TIMES prefix, extract its part and grab another token
914            let err_pos = token.1 - token.0.len();
915            let (arg, aft) = match self.extract_arg(raw_line, token.1, raw_line.len()) {
916                Err(e) => return Err(AsmError { kind: AsmErrorKind::TimesMissingCount, line_num: self.line_num, pos: e.pos, inner_err: None }),
917                Ok(x) => x,
918            };
919            let count = match arg {
920                Argument::Imm(imm) => {
921                    if imm.size.is_some() { return Err(AsmError { kind: AsmErrorKind::SizeSpecNotAllowed { index: None }, line_num: self.line_num, pos: Some(err_pos), inner_err: None }); }
922                    match imm.expr.eval(&self.file.symbols) {
923                        Err(e) => return Err(AsmError { kind: AsmErrorKind::FailedCriticalExpression(e), line_num: self.line_num, pos: Some(err_pos), inner_err: None }),
924                        Ok(val) => match &*val {
925                            Value::Integer(v) => {
926                                if v.cmp0() == Ordering::Less { return Err(AsmError { kind: AsmErrorKind::TimesCountWasNegative, line_num: self.line_num, pos: Some(err_pos), inner_err: None }); }
927                                match v.to_u64() {
928                                    None => return Err(AsmError { kind: AsmErrorKind::TimesCountTooLarge, line_num: self.line_num, pos: Some(err_pos), inner_err: None }),
929                                    Some(v) => v,
930                                }
931                            }
932                            x => return Err(AsmError { kind: AsmErrorKind::ValueInvalidType { index: None, got: x.get_type(), expected: &[ValueType::Integer] }, line_num: self.line_num, pos: Some(err_pos), inner_err: None }),
933                        }
934                    }
935                }
936                x => return Err(AsmError { kind: AsmErrorKind::ArgumentInvalidType { index: None, got: x.get_type(), expected: &[ArgumentType::Imm] }, line_num: self.line_num, pos: Some(err_pos), inner_err: None }),
937            };
938            self.times = Some(TimesInfo { total_count: count, current: 0 });
939
940            token = grab_whitespace_sep_token(raw_line, aft, raw_line.len());
941            if token.0.is_empty() { return Err(AsmError { kind: AsmErrorKind::TimesUsedOnEmptyLine, line_num: self.line_num, pos: Some(err_pos), inner_err: None }); }
942        }
943        else if Caseless(token.0) == Caseless("IF") { // if we got an IF prefix, extract its part and grab another token
944            let err_pos = token.1 - token.0.len();
945            let (arg, aft) = match self.extract_arg(raw_line, token.1, raw_line.len()) {
946                Err(e) => return Err(AsmError { kind: AsmErrorKind::IfMissingExpr, line_num: self.line_num, pos: e.pos, inner_err: None }),
947                Ok(x) => x,
948            };
949            let cond = match arg {
950                Argument::Imm(imm) => {
951                    if imm.size.is_some() { return Err(AsmError { kind: AsmErrorKind::SizeSpecNotAllowed { index: None }, line_num: self.line_num, pos: Some(err_pos), inner_err: None }); }
952                    match imm.expr.eval(&self.file.symbols) {
953                        Err(e) => return Err(AsmError { kind: AsmErrorKind::FailedCriticalExpression(e), line_num: self.line_num, pos: Some(err_pos), inner_err: None }),
954                        Ok(val) => match &*val {
955                            Value::Logical(v) => *v,
956                            x => return Err(AsmError { kind: AsmErrorKind::ValueInvalidType { index: None, got: x.get_type(), expected: &[ValueType::Logical] }, line_num: self.line_num, pos: Some(err_pos), inner_err: None }),
957                        }
958                    }
959                }
960                x => return Err(AsmError { kind: AsmErrorKind::ArgumentInvalidType { index: None, got: x.get_type(), expected: &[ArgumentType::Imm] }, line_num: self.line_num, pos: Some(err_pos), inner_err: None }),
961            };
962            self.times = Some(TimesInfo { total_count: if cond { 1 } else { 0 }, current: 0 });
963
964            token = grab_whitespace_sep_token(raw_line, aft, raw_line.len());
965            if token.0.is_empty() { return Err(AsmError { kind: AsmErrorKind::IfUsedOnEmptyLine, line_num: self.line_num, pos: Some(err_pos), inner_err: None }); }
966        }
967
968        // check if we got a prefix
969        let prefix = match PREFIXES.get(&Caseless(token.0)) {
970            None => None,
971            Some(prefix) => {
972                let err_pos = token.1 - token.0.len();
973                token = grab_whitespace_sep_token(raw_line, token.1, raw_line.len());
974                if token.0.is_empty() { return Err(AsmError { kind: AsmErrorKind::PrefixWithoutInstruction, line_num: self.line_num, pos: Some(err_pos), inner_err: None }); }
975                Some((*prefix, err_pos))
976            }
977        };
978
979        // the token we have at this point should be the instruction - parse it
980        let ins_pos = token.1 - token.0.len();
981        match INSTRUCTIONS.get(&Caseless(token.0)) {
982            None => return Err(AsmError { kind: AsmErrorKind::UnrecognizedInstruction, line_num: self.line_num, pos: Some(ins_pos), inner_err: None }),
983            Some(ins) => Ok((Some((prefix, (*ins, ins_pos))), token.1)),
984        }
985    }
986    pub(super) fn extract_arguments(&mut self, raw_line: &str, raw_start: usize) -> Result<Vec<Argument>, AsmError> {
987        let mut args = vec![];
988
989        // parse the rest of the line as comma-separated arguments
990        let (tail, mut pos) = trim_start_with_pos(raw_line, raw_start, raw_line.len());
991        if !tail.is_empty() { // check if we're done with line or entering a comment section (no args)
992            loop { // parse one or more comma-separated arguments
993                let (arg, aft) = self.extract_arg(raw_line, pos, raw_line.len())?;
994                args.push(arg);
995
996                let (tail, tail_pos) = trim_start_with_pos(raw_line, aft, raw_line.len());
997                if tail.chars().next() != Some(',') { // if we're not followed by a comma we're done
998                    pos = aft;
999                    break;
1000                } 
1001                pos = tail_pos + 1;
1002            }
1003
1004            // make sure we consumed the entire line
1005            let (tail, tail_pos) = trim_start_with_pos(raw_line, pos, raw_line.len());
1006            if !tail.is_empty() { return Err(AsmError { kind: AsmErrorKind::ExtraContentAfterArgs, line_num: self.line_num, pos: Some(tail_pos), inner_err: None }); }
1007        }
1008
1009        Ok(args)
1010    }
1011
1012    /// Gets the current segment for writing. Returns the segment, the symbol table, and the set of holes.
1013    /// Fails if not currently in a segment or if in a non-writable segment (like bss).
1014    pub(super) fn get_current_segment_for_writing(&mut self) -> Result<(&mut Vec<u8>, &dyn SymbolTableCore, &mut Vec<Hole>), AsmError> {
1015        Ok(match self.current_seg {
1016            None => return Err(AsmError { kind: AsmErrorKind::WriteOutsideOfSegment, line_num: self.line_num, pos: None, inner_err: None }),
1017            Some(seg) => match seg {
1018                AsmSegment::Text => (&mut self.file.text, &self.file.symbols, &mut self.file.text_holes),
1019                AsmSegment::Rodata => (&mut self.file.rodata, &self.file.symbols, &mut self.file.rodata_holes),
1020                AsmSegment::Data => (&mut self.file.data, &self.file.symbols, &mut self.file.data_holes),
1021                AsmSegment::Bss => return Err(AsmError { kind: AsmErrorKind::WriteInBssSegment, line_num: self.line_num, pos: None, inner_err: None }),
1022            }
1023        })
1024    }
1025    /// Appends a byte to the current segment, if valid.
1026    pub(super) fn append_byte(&mut self, val: u8) -> Result<(), AsmError> {
1027        let (seg, _, _) = self.get_current_segment_for_writing()?;
1028        seg.push(val);
1029        Ok(())
1030    }
1031    /// Appends a value to the current segment, if valid.
1032    /// If it is immediately evaluatable, appends the value, otherwise writes a placeholder and generates a hole entry to be patched later.
1033    pub(super) fn append_val(&mut self, size: Size, expr: Expr, allowed_type: HoleType) -> Result<(), AsmError> {
1034        let line_num = self.line_num;
1035        let (seg, symbols, holes) = self.get_current_segment_for_writing()?;
1036        let hole = Hole { // generate the hole info
1037            address: seg.len(),
1038            size, expr, allowed_type, line_num,
1039        };
1040        seg.extend(iter::once(0xffu8).cycle().take(size.size())); // make room for the value (all 1's is arbitrary)
1041        match patch_hole(seg, &hole, symbols) {
1042            Ok(_) => (), // on success we're golden - hole was patched immediately
1043            Err(e) => match e.kind {
1044                PatchErrorKind::Illegal(r) => return Err(AsmError { kind: AsmErrorKind::IllegalPatch(r), line_num: e.line_num, pos: None, inner_err: None }), // anything illegal is a hard pass
1045                PatchErrorKind::NotPatched(_) => holes.push(hole), // an eval error just means we need to add it to the list of holes for this segment
1046            }
1047        }
1048        Ok(())
1049    }
1050    /// Appends an address to the current segment, if valid.
1051    pub(super) fn append_address(&mut self, addr: Address) -> Result<(), AsmError> {
1052        let a = (if addr.base.is_some() { 0x80 } else { 0 }) | (addr.r1.unwrap_or((0, 0)).1 << 4) | (addr.address_size.basic_sizecode().unwrap() << 2) | (if addr.r1.is_some() { 2 } else { 0 }) | (if addr.r2.is_some() { 1 } else { 0 });
1053        let b = (addr.r1.unwrap_or((0, 0)).0 << 4) | addr.r2.unwrap_or(0);
1054
1055        self.append_byte(a)?;
1056        if a & 3 != 0 { self.append_byte(b)?; }
1057        if a & 0x80 != 0 { self.append_val(addr.address_size, addr.base.unwrap(), HoleType::Integer)? }
1058        Ok(())
1059    }
1060
1061    /// Handles instructions which take no arguments.
1062    /// Writes `op`, followed by `ext_op` (if valid).
1063    pub(super) fn process_no_arg_op(&mut self, args: Vec<Argument>, op: Option<u8>, ext_op: Option<u8>) -> Result<(), AsmError> {
1064        if self.current_seg != Some(AsmSegment::Text) { return Err(AsmError { kind: AsmErrorKind::InstructionOutsideOfTextSegment, line_num: self.line_num, pos: None, inner_err: None }); }
1065        if args.len() != 0 { return Err(AsmError { kind: AsmErrorKind::ArgsExpectedCount(&[0]), line_num: self.line_num, pos: None, inner_err: None }); }
1066        if let Some(ext) = op { self.append_byte(ext)? }
1067        if let Some(ext) = ext_op { self.append_byte(ext)? }
1068        Ok(())
1069    }
1070    pub(super) fn process_ternary_op(&mut self, args: Vec<Argument>, op: u8, ext_op: Option<u8>, allowed_type: HoleType, allowed_sizes: &'static [Size], force_b_rm_size: Option<Size>, force_b_imm_size: Option<Size>) -> Result<(), AsmError> {
1071        if self.current_seg != Some(AsmSegment::Text) { return Err(AsmError { kind: AsmErrorKind::InstructionOutsideOfTextSegment, line_num: self.line_num, pos: None, inner_err: None }); }
1072        if args.len() != 3 { return Err(AsmError { kind: AsmErrorKind::ArgsExpectedCount(&[3]), line_num: self.line_num, pos: None, inner_err: None }); }
1073        let mut args = args.into_iter();
1074        let arg1 = args.next().unwrap();
1075        let mut arg2 = args.next().unwrap();
1076        let arg3 = args.next().unwrap();
1077
1078        self.append_byte(op)?;
1079        if let Some(ext) = ext_op { self.append_byte(ext)?; }
1080
1081        let supported_types = slice_slice!(ArgumentType:
1082            [CPURegister, CPURegister, CPURegister],
1083            [CPURegister, CPURegister, Imm],
1084            [CPURegister, CPURegister, Address],
1085            [CPURegister, Address, CPURegister],
1086            [CPURegister, Address, Imm],
1087        );
1088
1089        let r1 = match arg1 {
1090            Argument::CPURegister(r) => r,
1091            _ => return Err(AsmError { kind: AsmErrorKind::ArgumentsInvalidTypes { got: vec![arg1.get_type(), arg2.get_type(), arg3.get_type()], expected: supported_types }, line_num: self.line_num, pos: None, inner_err: None }),
1092        };
1093        let pseudo_op = (r1.id << 4) | (if r1.high { 1 } else { 0 });
1094
1095        let mismatched_sizes = match (&mut arg2, &arg3) {
1096            (Argument::CPURegister(reg), Argument::CPURegister(_)) | (Argument::CPURegister(reg), Argument::Imm(_)) | (Argument::CPURegister(reg), Argument::Address { .. }) => {
1097                if reg.size != r1.size { Some((reg.size, r1.size)) } else { None }
1098            }
1099            (Argument::Address { addr, .. }, Argument::CPURegister(_)) | (Argument::Address { addr, .. }, Argument::Imm(_)) => match addr.pointed_size {
1100                Some(size) => if size != r1.size { Some((size, r1.size)) } else { None },
1101                None => { addr.pointed_size = Some(r1.size); None }, // if no size present, set it to r1 size to propagate to binary handler
1102            }
1103            _ => return Err(AsmError { kind: AsmErrorKind::ArgumentsInvalidTypes { got: vec![arg1.get_type(), arg2.get_type(), arg3.get_type()], expected: supported_types }, line_num: self.line_num, pos: None, inner_err: None }),
1104        };
1105        if let Some((s1, s2)) = mismatched_sizes { return Err(AsmError { kind: AsmErrorKind::OperandsHadDifferentSizes(s1, s2), line_num: self.line_num, pos: None, inner_err: None }); }
1106
1107        // we validated everything we need, so hand over to the binary formatter
1108        self.process_binary_op(vec![arg2, arg3], pseudo_op, None, allowed_type, allowed_sizes, force_b_rm_size, force_b_imm_size)
1109    }
1110    /// Attempts to assemble an operation which uses the binary op format.
1111    /// `force_b_size` can be set to artificially force the size of the second argument (e.g. shifts uses an 8-bit second argument regardless of the first argument).
1112    pub(super) fn process_binary_op(&mut self, args: Vec<Argument>, op: u8, ext_op: Option<u8>, allowed_type: HoleType, allowed_sizes: &'static [Size], force_b_rm_size: Option<Size>, force_b_imm_size: Option<Size>) -> Result<(), AsmError> {
1113        if self.current_seg != Some(AsmSegment::Text) { return Err(AsmError { kind: AsmErrorKind::InstructionOutsideOfTextSegment, line_num: self.line_num, pos: None, inner_err: None }); }
1114        if args.len() != 2 { return Err(AsmError { kind: AsmErrorKind::ArgsExpectedCount(&[2]), line_num: self.line_num, pos: None, inner_err: None }); }
1115        let mut args = args.into_iter();
1116        let arg1 = args.next().unwrap();
1117        let arg2 = args.next().unwrap();
1118
1119        self.append_byte(op)?;
1120        if let Some(ext) = ext_op { self.append_byte(ext)?; }
1121
1122        let supported_types = slice_slice!(ArgumentType:
1123            [CPURegister, CPURegister],
1124            [CPURegister, Address],
1125            [CPURegister, Imm],
1126            [Address, CPURegister],
1127            [Address, Imm],
1128        );
1129
1130        match (arg1, arg2) {
1131            (Argument::CPURegister(dest), Argument::CPURegister(src)) => {
1132                match force_b_rm_size {
1133                    Some(b_size) => if src.size != b_size { return Err(AsmError { kind: AsmErrorKind::ArgumentInvalidSize { index: Some(1), got: src.size, expected: vec![b_size] }, line_num: self.line_num, pos: None, inner_err: None }); }
1134                    None => if src.size != dest.size { return Err(AsmError { kind: AsmErrorKind::OperandsHadDifferentSizes(src.size, dest.size), line_num: self.line_num, pos: None, inner_err: None }); }
1135                }
1136                if !allowed_sizes.contains(&dest.size) { return Err(AsmError { kind: AsmErrorKind::UnsupportedOperandSize { got: dest.size, expected: allowed_sizes }, line_num: self.line_num, pos: None, inner_err: None }); }
1137                self.append_byte((dest.id << 4) | (dest.size.basic_sizecode().unwrap() << 2) | (if dest.high { 2 } else { 0 }) | (if src.high { 1 } else { 0 }))?;
1138                self.append_byte(src.id)?;
1139            }
1140            (Argument::CPURegister(dest), Argument::Address { addr: src, mask }) => {
1141                if mask.is_some() { return Err(AsmError { kind: AsmErrorKind::VPUMaskOnNonVecInstruction, line_num: self.line_num, pos: None, inner_err: None }); }
1142                if let Some(src_size) = src.pointed_size {
1143                    match force_b_rm_size {
1144                        Some(b_size) => if src_size != b_size { return Err(AsmError { kind: AsmErrorKind::ArgumentInvalidSize { index: Some(1), got: src_size, expected: vec![b_size] }, line_num: self.line_num, pos: None, inner_err: None }); }
1145                        None => if src_size != dest.size { return Err(AsmError { kind: AsmErrorKind::OperandsHadDifferentSizes(src_size, dest.size), line_num: self.line_num, pos: None, inner_err: None }); }
1146                    }
1147                }
1148                if !allowed_sizes.contains(&dest.size) { return Err(AsmError { kind: AsmErrorKind::UnsupportedOperandSize { got: dest.size, expected: allowed_sizes }, line_num: self.line_num, pos: None, inner_err: None }); }
1149                self.append_byte((dest.id << 4) | (dest.size.basic_sizecode().unwrap() << 2) | (if dest.high { 2 } else { 0 }))?;
1150                self.append_byte(2 << 4)?;
1151                self.append_address(src)?;
1152            }
1153            (Argument::CPURegister(dest), Argument::Imm(mut src)) => {
1154                match (force_b_imm_size, src.size) {
1155                    (Some(b_size), Some(src_size)) => if src_size != b_size { return Err(AsmError { kind: AsmErrorKind::ArgumentInvalidSize { index: Some(1), got: src_size, expected: vec![b_size] }, line_num: self.line_num, pos: None, inner_err: None }); }
1156                    (Some(b_size), None) => src.size = Some(b_size),
1157                    (None, Some(src_size)) => if src_size != dest.size { return Err(AsmError { kind: AsmErrorKind::OperandsHadDifferentSizes(src_size, dest.size), line_num: self.line_num, pos: None, inner_err: None }); },
1158                    (None, None) => src.size = Some(dest.size),
1159                }
1160                if !allowed_sizes.contains(&dest.size) { return Err(AsmError { kind: AsmErrorKind::UnsupportedOperandSize { got: dest.size, expected: allowed_sizes }, line_num: self.line_num, pos: None, inner_err: None }); }
1161                self.append_byte((dest.id << 4) | (dest.size.basic_sizecode().unwrap() << 2) | (if dest.high { 2 } else { 0 }))?;
1162                self.append_byte(1 << 4)?;
1163                self.append_val(src.size.unwrap(), src.expr, allowed_type)?;
1164            }
1165            (Argument::Address { addr: dest, mask }, Argument::CPURegister(src)) => {
1166                if mask.is_some() { return Err(AsmError { kind: AsmErrorKind::VPUMaskOnNonVecInstruction, line_num: self.line_num, pos: None, inner_err: None }); }
1167                let a_size = match force_b_rm_size {
1168                    Some(b_size) => {
1169                        if src.size != b_size { return Err(AsmError { kind: AsmErrorKind::ArgumentInvalidSize { index: Some(1), got: src.size, expected: vec![b_size] }, line_num: self.line_num, pos: None, inner_err: None }); }
1170                        match dest.pointed_size {
1171                            None => { return Err(AsmError { kind: AsmErrorKind::CouldNotDeduceOperandSize, line_num: self.line_num, pos: None, inner_err: None }); }
1172                            Some(a_size) => a_size,
1173                        }
1174                    }
1175                    None => {
1176                        if let Some(a_size) = dest.pointed_size {
1177                            if a_size != src.size { return Err(AsmError { kind: AsmErrorKind::OperandsHadDifferentSizes(a_size, src.size), line_num: self.line_num, pos: None, inner_err: None }); }
1178                        }
1179                        src.size
1180                    }
1181                };
1182                if !allowed_sizes.contains(&a_size) { return Err(AsmError { kind: AsmErrorKind::UnsupportedOperandSize { got: a_size, expected: allowed_sizes }, line_num: self.line_num, pos: None, inner_err: None }); }
1183                self.append_byte((a_size.basic_sizecode().unwrap() << 2) | (if src.high { 1 } else { 0 }))?;
1184                self.append_byte((3 << 4) | src.id)?;
1185                self.append_address(dest)?;
1186            }
1187            (Argument::Address { addr: dest, mask }, Argument::Imm(mut src)) => {
1188                if mask.is_some() { return Err(AsmError { kind: AsmErrorKind::VPUMaskOnNonVecInstruction, line_num: self.line_num, pos: None, inner_err: None }); }
1189                let a_size = match force_b_imm_size {
1190                    Some(b_size) => {
1191                        match src.size {
1192                            Some(size) => if size != b_size { return Err(AsmError { kind: AsmErrorKind::ArgumentInvalidSize { index: Some(1), got: size, expected: vec![b_size] }, line_num: self.line_num, pos: None, inner_err: None }); }
1193                            None => src.size = Some(b_size),
1194                        }
1195                        match dest.pointed_size {
1196                            None => return Err(AsmError { kind: AsmErrorKind::CouldNotDeduceOperandSize, line_num: self.line_num, pos: None, inner_err: None }),
1197                            Some(size) => size,
1198                        }
1199                    }
1200                    None => match (dest.pointed_size, src.size) {
1201                        (Some(a), Some(b)) => {
1202                            if a != b { return Err(AsmError { kind: AsmErrorKind::OperandsHadDifferentSizes(a, b), line_num: self.line_num, pos: None, inner_err: None }); }
1203                            a
1204                        }
1205                        (None, Some(b)) => b,
1206                        (Some(a), None) => { src.size = Some(a); a },
1207                        (None, None) => return Err(AsmError { kind: AsmErrorKind::CouldNotDeduceOperandSize, line_num: self.line_num, pos: None, inner_err: None }),
1208                    }
1209                };
1210                if !allowed_sizes.contains(&a_size) { return Err(AsmError { kind: AsmErrorKind::UnsupportedOperandSize { got: a_size, expected: allowed_sizes }, line_num: self.line_num, pos: None, inner_err: None }); }
1211                self.append_byte(a_size.basic_sizecode().unwrap() << 2)?;
1212                self.append_byte(4 << 4)?;
1213                self.append_address(dest)?;
1214                self.append_val(src.size.unwrap(), src.expr, allowed_type)?;
1215            }
1216            (a, b) => return Err(AsmError { kind: AsmErrorKind::ArgumentsInvalidTypes { got: vec![a.get_type(), b.get_type()], expected: supported_types }, line_num: self.line_num, pos: None, inner_err: None }),
1217        }
1218
1219        Ok(())
1220    }
1221    /*
1222    [1: signed][1: sh][1: mem][2:][3: mode]    [4: dest][4: src]
1223    mode = 0: ext =  8 -> 16
1224    mode = 1: ext =  8 -> 32
1225    mode = 2: ext =  8 -> 64
1226    mode = 3: ext = 16 -> 32
1227    mode = 4: ext = 16 -> 64
1228    mode = 5: ext = 32 -> 64
1229    mem = 0:              dest <- ext(src)
1230    mem = 1: [address]    dest <- ext(M[address])
1231    */
1232    pub (super) fn process_mov_ext_op(&mut self, args: Vec<Argument>, op: u8, ext_op: Option<u8>, signed: bool) -> Result<(), AsmError> {
1233        if self.current_seg != Some(AsmSegment::Text) { return Err(AsmError { kind: AsmErrorKind::InstructionOutsideOfTextSegment, line_num: self.line_num, pos: None, inner_err: None }); }
1234        if args.len() != 2 { return Err(AsmError { kind: AsmErrorKind::ArgsExpectedCount(&[2]), line_num: self.line_num, pos: None, inner_err: None }); }
1235        let mut args = args.into_iter();
1236        let arg1 = args.next().unwrap();
1237        let arg2 = args.next().unwrap();
1238
1239        self.append_byte(op)?;
1240        if let Some(ext) = ext_op { self.append_byte(ext)?; }
1241
1242        let supported_types = slice_slice!(ArgumentType:
1243            [CPURegister, CPURegister],
1244            [CPURegister, Address],
1245        );
1246        let supported_sizes = slice_slice!(Size:
1247            [Word,  Byte],
1248            [Dword, Byte],
1249            [Qword, Byte],
1250            [Dword, Word],
1251            [Qword, Word],
1252            [Qword, Dword],
1253        );
1254        let get_mode = |dest_size: Size, src_size: Size, args: &AssembleArgs| -> Result<u8, AsmError> {
1255            Ok(match (dest_size, src_size) {
1256                (Size::Word,  Size::Byte) => 0,
1257                (Size::Dword, Size::Byte) => 1,
1258                (Size::Qword, Size::Byte) => 2,
1259                (Size::Dword, Size::Word) => 3,
1260                (Size::Qword, Size::Word) => 4,
1261                (Size::Qword, Size::Dword) => 5,
1262                (a, b) => return Err(AsmError { kind: AsmErrorKind::ArgumentsInvalidSizes { got: vec![a, b], expected: supported_sizes }, line_num: args.line_num, pos: None, inner_err: None }),
1263            })
1264        };
1265
1266        match (arg1, arg2) {
1267            (Argument::CPURegister(dest), Argument::CPURegister(src)) => {
1268                let mode = get_mode(dest.size, src.size, self)?;
1269
1270                self.append_byte((if signed { 0x80 } else { 0 }) | (if src.high { 0x40 } else { 0 }) | mode)?;
1271                self.append_byte((dest.id << 4) | src.id)?;
1272            }
1273            (Argument::CPURegister(dest), Argument::Address { addr: src, mask }) => {
1274                if mask.is_some() { return Err(AsmError { kind: AsmErrorKind::VPUMaskOnNonVecInstruction, line_num: self.line_num, pos: None, inner_err: None }); }
1275                let mode = match src.pointed_size {
1276                    None => return Err(AsmError { kind: AsmErrorKind::CouldNotDeduceOperandSize, line_num: self.line_num, pos: None, inner_err: None }),
1277                    Some(src_size) => get_mode(dest.size, src_size, self)?,
1278                };
1279
1280                self.append_byte((if signed { 0x80 } else { 0 }) | 0x20 | mode)?;
1281                self.append_byte(dest.id << 4)?;
1282                self.append_address(src)?;
1283            }
1284            (a, b) => return Err(AsmError { kind: AsmErrorKind::ArgumentsInvalidTypes { got: vec![a.get_type(), b.get_type()], expected: supported_types }, line_num: self.line_num, pos: None, inner_err: None }),
1285        }
1286
1287        Ok(())
1288    }
1289    pub(super) fn process_binary_lvalue_op(&mut self, args: Vec<Argument>, op: u8, ext_op: Option<u8>, allowed_sizes: &'static [Size]) -> Result<(), AsmError> {
1290        if self.current_seg != Some(AsmSegment::Text) { return Err(AsmError { kind: AsmErrorKind::InstructionOutsideOfTextSegment, line_num: self.line_num, pos: None, inner_err: None }); }
1291        if args.len() != 2 { return Err(AsmError { kind: AsmErrorKind::ArgsExpectedCount(&[2]), line_num: self.line_num, pos: None, inner_err: None }); }
1292        let mut args = args.into_iter();
1293        let arg1 = args.next().unwrap();
1294        let arg2 = args.next().unwrap();
1295
1296        self.append_byte(op)?;
1297        if let Some(ext) = ext_op { self.append_byte(ext)?; }
1298
1299        let supported_types = slice_slice!(ArgumentType:
1300            [CPURegister, CPURegister],
1301            [CPURegister, Address],
1302        );
1303
1304        match (arg1, arg2) {
1305            (Argument::CPURegister(dest), Argument::CPURegister(src)) => {
1306                if dest.size != src.size { return Err(AsmError { kind: AsmErrorKind::OperandsHadDifferentSizes(dest.size, src.size), line_num: self.line_num, pos: None, inner_err: None }); }
1307                if !allowed_sizes.contains(&dest.size) { return Err(AsmError { kind: AsmErrorKind::UnsupportedOperandSize { got: dest.size, expected: allowed_sizes }, line_num: self.line_num, pos: None, inner_err: None }); }
1308                self.append_byte((dest.id << 4) | (dest.size.basic_sizecode().unwrap() << 2) | (if dest.high { 2 } else { 0 }) | 0)?;
1309                self.append_byte((if src.high { 0x80 } else { 0 }) | src.id)?;
1310            }
1311            (Argument::CPURegister(dest), Argument::Address { addr: src, mask }) => {
1312                if mask.is_some() { return Err(AsmError { kind: AsmErrorKind::VPUMaskOnNonVecInstruction, line_num: self.line_num, pos: None, inner_err: None }); }
1313                if let Some(size) = src.pointed_size {
1314                    if dest.size != size { return Err(AsmError { kind: AsmErrorKind::OperandsHadDifferentSizes(dest.size, size), line_num: self.line_num, pos: None, inner_err: None }); }
1315                }
1316                if !allowed_sizes.contains(&dest.size) { return Err(AsmError { kind: AsmErrorKind::UnsupportedOperandSize { got: dest.size, expected: allowed_sizes }, line_num: self.line_num, pos: None, inner_err: None }); }
1317                self.append_byte((dest.id << 4) | (dest.size.basic_sizecode().unwrap() << 2) | (if dest.high { 2 } else { 0 }) | 1)?;
1318                self.append_address(src)?;
1319            }
1320            (a, b) => return Err(AsmError { kind: AsmErrorKind::ArgumentsInvalidTypes { got: vec![a.get_type(), b.get_type()], expected: supported_types }, line_num: self.line_num, pos: None, inner_err: None }),
1321        }
1322
1323        Ok(())
1324    }
1325    pub(super) fn process_binary_lvalue_unordered_op(&mut self, mut args: Vec<Argument>, op: u8, ext_op: Option<u8>, allowed_sizes: &'static [Size]) -> Result<(), AsmError> {
1326        if self.current_seg != Some(AsmSegment::Text) { return Err(AsmError { kind: AsmErrorKind::InstructionOutsideOfTextSegment, line_num: self.line_num, pos: None, inner_err: None }); }
1327        if args.len() != 2 { return Err(AsmError { kind: AsmErrorKind::ArgsExpectedCount(&[2]), line_num: self.line_num, pos: None, inner_err: None }); }
1328        
1329        let supported_types = slice_slice!(ArgumentType:
1330            [CPURegister, CPURegister],
1331            [CPURegister, Address],
1332            [Address, CPURegister],
1333        );
1334        
1335        match (&args[0], &args[1]) {
1336            (Argument::CPURegister(_), Argument::CPURegister(_)) => (),
1337            (Argument::CPURegister(_), Argument::Address { .. }) => (),
1338            (Argument::Address { .. }, Argument::CPURegister(_)) => args.swap(0, 1),
1339            _ => return Err(AsmError { kind: AsmErrorKind::ArgumentsInvalidTypes { got: vec![args[0].get_type(), args[1].get_type()], expected: supported_types }, line_num: self.line_num, pos: None, inner_err: None }),
1340        }
1341        self.process_binary_lvalue_op(args, op, ext_op, allowed_sizes)
1342    }
1343    pub(super) fn process_unary_op(&mut self, args: Vec<Argument>, op: u8, ext_op: Option<u8>, allowed_sizes: &'static [Size]) -> Result<(), AsmError> {
1344        if self.current_seg != Some(AsmSegment::Text) { return Err(AsmError { kind: AsmErrorKind::InstructionOutsideOfTextSegment, line_num: self.line_num, pos: None, inner_err: None }); }
1345        if args.len() != 1 { return Err(AsmError { kind: AsmErrorKind::ArgsExpectedCount(&[1]), line_num: self.line_num, pos: None, inner_err: None }); }
1346
1347        self.append_byte(op)?;
1348        if let Some(ext) = ext_op { self.append_byte(ext)?; }
1349
1350        match args.into_iter().next().unwrap() {
1351            Argument::CPURegister(reg) => {
1352                if !allowed_sizes.contains(&reg.size) { return Err(AsmError { kind: AsmErrorKind::UnsupportedOperandSize { got: reg.size, expected: allowed_sizes }, line_num: self.line_num, pos: None, inner_err: None }); }
1353                self.append_byte((reg.id << 4) | (reg.size.basic_sizecode().unwrap() << 2) | (if reg.high { 2 } else { 0 }))?;
1354            }
1355            Argument::Address { addr, mask } => {
1356                if mask.is_some() { return Err(AsmError { kind: AsmErrorKind::VPUMaskOnNonVecInstruction, line_num: self.line_num, pos: None, inner_err: None }); }
1357                let size = match addr.pointed_size {
1358                    None => return Err(AsmError { kind: AsmErrorKind::CouldNotDeduceOperandSize, line_num: self.line_num, pos: None, inner_err: None }),
1359                    Some(s) => s,
1360                };
1361                if !allowed_sizes.contains(&size) { return Err(AsmError { kind: AsmErrorKind::UnsupportedOperandSize { got: size, expected: allowed_sizes }, line_num: self.line_num, pos: None, inner_err: None }); }
1362                self.append_byte((size.basic_sizecode().unwrap() << 2) | 1)?;
1363                self.append_address(addr)?;
1364            }
1365            x => return Err(AsmError { kind: AsmErrorKind::ArgumentInvalidType { index: None, got: x.get_type(), expected: &[ArgumentType::CPURegister, ArgumentType::Address] }, line_num: self.line_num, pos: None, inner_err: None }),
1366        }
1367
1368        Ok(())
1369    }
1370    pub(super) fn process_value_op(&mut self, args: Vec<Argument>, op: u8, ext_op: Option<u8>, allowed_type: HoleType, allowed_sizes: &'static [Size], default_size: Option<Size>) -> Result<(), AsmError> {
1371        if self.current_seg != Some(AsmSegment::Text) { return Err(AsmError { kind: AsmErrorKind::InstructionOutsideOfTextSegment, line_num: self.line_num, pos: None, inner_err: None }); }
1372        if args.len() != 1 { return Err(AsmError { kind: AsmErrorKind::ArgsExpectedCount(&[1]), line_num: self.line_num, pos: None, inner_err: None }); }
1373
1374        self.append_byte(op)?;
1375        if let Some(ext) = ext_op { self.append_byte(ext)?; }
1376
1377        match args.into_iter().next().unwrap() {
1378            Argument::CPURegister(reg) => {
1379                if !allowed_sizes.contains(&reg.size) { return Err(AsmError { kind: AsmErrorKind::UnsupportedOperandSize { got: reg.size, expected: allowed_sizes }, line_num: self.line_num, pos: None, inner_err: None }); }
1380                self.append_byte((reg.id << 4) | (reg.size.basic_sizecode().unwrap() << 2) | (if reg.high { 1 } else { 0 }))?;
1381            }
1382            Argument::Imm(imm) => {
1383                let size = match imm.size.or(default_size) {
1384                    None => return Err(AsmError { kind: AsmErrorKind::CouldNotDeduceOperandSize, line_num: self.line_num, pos: None, inner_err: None }),
1385                    Some(s) => s,
1386                };
1387                if !allowed_sizes.contains(&size) { return Err(AsmError { kind: AsmErrorKind::UnsupportedOperandSize { got: size, expected: allowed_sizes }, line_num: self.line_num, pos: None, inner_err: None }); }
1388                self.append_byte((size.basic_sizecode().unwrap() << 2) | 2)?;
1389                self.append_val(size, imm.expr, allowed_type)?;
1390            }
1391            Argument::Address { addr, mask } => {
1392                if mask.is_some() { return Err(AsmError { kind: AsmErrorKind::VPUMaskOnNonVecInstruction, line_num: self.line_num, pos: None, inner_err: None }); }
1393                let size = match addr.pointed_size.or(default_size) {
1394                    None => return Err(AsmError { kind: AsmErrorKind::CouldNotDeduceOperandSize, line_num: self.line_num, pos: None, inner_err: None }),
1395                    Some(s) => s,
1396                };
1397                if !allowed_sizes.contains(&size) { return Err(AsmError { kind: AsmErrorKind::UnsupportedOperandSize { got: size, expected: allowed_sizes }, line_num: self.line_num, pos: None, inner_err: None }); }
1398                self.append_byte((size.basic_sizecode().unwrap() << 2) | 3)?;
1399                self.append_address(addr)?;
1400            }
1401            x => return Err(AsmError { kind: AsmErrorKind::ArgumentInvalidType { index: None, got: x.get_type(), expected: &[ArgumentType::CPURegister, ArgumentType::Imm, ArgumentType::Address] }, line_num: self.line_num, pos: None, inner_err: None }),
1402        }
1403
1404        Ok(())
1405    }
1406
1407    pub(super) fn process_fpu_value_op(&mut self, args: Vec<Argument>, op: u8, ext_op: Option<u8>, integral: bool) -> Result<(), AsmError> {
1408        if self.current_seg != Some(AsmSegment::Text) { return Err(AsmError { kind: AsmErrorKind::InstructionOutsideOfTextSegment, line_num: self.line_num, pos: None, inner_err: None }); }
1409        if args.len() != 1 { return Err(AsmError { kind: AsmErrorKind::ArgsExpectedCount(&[1]), line_num: self.line_num, pos: None, inner_err: None }); }
1410
1411        self.append_byte(op)?;
1412        if let Some(ext) = ext_op { self.append_byte(ext)?; }
1413
1414        match args.into_iter().next().unwrap() {
1415            Argument::FPURegister(src) => self.append_byte((src.id << 4) | 0)?,
1416            Argument::Address { addr, mask } => {
1417                if mask.is_some() { return Err(AsmError { kind: AsmErrorKind::VPUMaskOnNonVecInstruction, line_num: self.line_num, pos: None, inner_err: None }); }
1418                let size = match addr.pointed_size {
1419                    None => return Err(AsmError { kind: AsmErrorKind::CouldNotDeduceOperandSize, line_num: self.line_num, pos: None, inner_err: None }),
1420                    Some(s) => s,
1421                };
1422                let mode = match (integral, size) {
1423                    (false, Size::Dword) => 1,
1424                    (false, Size::Qword) => 2,
1425                    (false, Size::Tword) => 3,
1426                    (false, _) => return Err(AsmError { kind: AsmErrorKind::UnsupportedOperandSize { got: size, expected: &[Size::Dword, Size::Qword, Size::Tword] }, line_num: self.line_num, pos: None, inner_err: None }),
1427                    (true, Size::Word) => 4,
1428                    (true, Size::Dword) => 5,
1429                    (true, Size::Qword) => 6,
1430                    (true, _) => return Err(AsmError { kind: AsmErrorKind::UnsupportedOperandSize { got: size, expected: &[Size::Word, Size::Dword, Size::Qword] }, line_num: self.line_num, pos: None, inner_err: None }),
1431                };
1432                self.append_byte(mode)?;
1433                self.append_address(addr)?;
1434            }
1435            x => return Err(AsmError { kind: AsmErrorKind::ArgumentInvalidType { index: None, got: x.get_type(), expected: &[ArgumentType::FPURegister, ArgumentType::Address] }, line_num: self.line_num, pos: None, inner_err: None }),
1436        }
1437
1438        Ok(())
1439    }
1440    pub(super) fn process_fpu_binary_op(&mut self, args: Vec<Argument>, op: u8, ext_op: Option<u8>, integral: bool, pop: bool) -> Result<(), AsmError> {
1441        if self.current_seg != Some(AsmSegment::Text) { return Err(AsmError { kind: AsmErrorKind::InstructionOutsideOfTextSegment, line_num: self.line_num, pos: None, inner_err: None }); }
1442
1443        self.append_byte(op)?;
1444        if let Some(ext) = ext_op { self.append_byte(ext)?; }
1445
1446        if integral && pop { panic!() }
1447        else if integral {
1448            if args.len() != 1 { return Err(AsmError { kind: AsmErrorKind::ArgsExpectedCount(&[1]), line_num: self.line_num, pos: None, inner_err: None }); }
1449            match args.into_iter().next().unwrap() {
1450                Argument::Address { addr, mask } => {
1451                    if mask.is_some() { return Err(AsmError { kind: AsmErrorKind::VPUMaskOnNonVecInstruction, line_num: self.line_num, pos: None, inner_err: None }); }
1452                    let mode = match addr.pointed_size {
1453                        None => return Err(AsmError { kind: AsmErrorKind::CouldNotDeduceOperandSize, line_num: self.line_num, pos: None, inner_err: None }),
1454                        Some(size) => match size {
1455                            Size::Word => 6,
1456                            Size::Dword => 7,
1457                            Size::Qword => 8,
1458                            _ => return Err(AsmError { kind: AsmErrorKind::UnsupportedOperandSize { got: size, expected: &[Size::Word, Size::Dword, Size::Qword] }, line_num: self.line_num, pos: None, inner_err: None }),
1459                        }
1460                    };
1461                    self.append_byte(mode)?;
1462                    self.append_address(addr)?;
1463                }
1464                x => return Err(AsmError { kind: AsmErrorKind::ArgumentInvalidType { index: None, got: x.get_type(), expected: &[ArgumentType::Address] }, line_num: self.line_num, pos: None, inner_err: None }),
1465            }
1466        }
1467        else if pop {
1468            match args.len() {
1469                0 => self.append_byte((1 << 4) | 2)?,
1470                2 => {
1471                    let mut args = args.into_iter();
1472                    let a = args.next().unwrap();
1473                    let b = args.next().unwrap();
1474
1475                    let supported_types = slice_slice!(ArgumentType:
1476                        [FPURegister, FPURegister],
1477                    );
1478
1479                    match (a, b) {
1480                        (Argument::FPURegister(a), Argument::FPURegister(b)) => {
1481                            if b.id != 0 { return Err(AsmError { kind: AsmErrorKind::FPUBinaryOpPop2SrcNotST0, line_num: self.line_num, pos: None, inner_err: None }); }
1482                            self.append_byte((a.id << 4) | 2)?;
1483                        }
1484                        (a, b) => return Err(AsmError { kind: AsmErrorKind::ArgumentsInvalidTypes { got: vec![a.get_type(), b.get_type()], expected: supported_types }, line_num: self.line_num, pos: None, inner_err: None }),
1485                    }
1486                }
1487                _ => return Err(AsmError { kind: AsmErrorKind::ArgsExpectedCount(&[0, 2]), line_num: self.line_num, pos: None, inner_err: None }),
1488            }
1489        }
1490        else {
1491            match args.len() {
1492                1 => match args.into_iter().next().unwrap() {
1493                    Argument::Address { addr, mask } => {
1494                        if mask.is_some() { return Err(AsmError { kind: AsmErrorKind::VPUMaskOnNonVecInstruction, line_num: self.line_num, pos: None, inner_err: None }); }
1495                        let mode = match addr.pointed_size {
1496                            None => return Err(AsmError { kind: AsmErrorKind::CouldNotDeduceOperandSize, line_num: self.line_num, pos: None, inner_err: None }),
1497                            Some(size) => match size {
1498                                Size::Dword => 3,
1499                                Size::Qword => 4,
1500                                Size::Tword => 5,
1501                                _ => return Err(AsmError { kind: AsmErrorKind::UnsupportedOperandSize { got: size, expected: &[Size::Dword, Size::Qword, Size::Tword] }, line_num: self.line_num, pos: None, inner_err: None }),
1502                            }
1503                        };
1504                        self.append_byte(mode)?;
1505                        self.append_address(addr)?;
1506                    }
1507                    x => return Err(AsmError { kind: AsmErrorKind::ArgumentInvalidType { index: None, got: x.get_type(), expected: &[ArgumentType::Address] }, line_num: self.line_num, pos: None, inner_err: None }),
1508                },
1509                2 => {
1510                    let mut args = args.into_iter();
1511                    let a = args.next().unwrap();
1512                    let b = args.next().unwrap();
1513
1514                    let supported_types = slice_slice!(ArgumentType:
1515                        [FPURegister, FPURegister]
1516                    );
1517
1518                    match (a, b) {
1519                        (Argument::FPURegister(a), Argument::FPURegister(b)) => {
1520                            if a.id == 0 { self.append_byte((b.id << 4) | 0)?; }
1521                            else if b.id == 0 { self.append_byte((a.id << 4) | 1)?; }
1522                            else { return Err(AsmError { kind: AsmErrorKind::FPUBinaryOpNeitherST0, line_num: self.line_num, pos: None, inner_err: None }); }
1523                        }
1524                        (a, b) => return Err(AsmError { kind: AsmErrorKind::ArgumentsInvalidTypes { got: vec![a.get_type(), b.get_type()], expected: supported_types }, line_num: self.line_num, pos: None, inner_err: None }),
1525                    }
1526                }
1527                _ => return Err(AsmError { kind: AsmErrorKind::ArgsExpectedCount(&[1, 2]), line_num: self.line_num, pos: None, inner_err: None }),
1528            }
1529        }
1530
1531        Ok(())
1532    }
1533
1534    // breaks down an opmask into its kreg id and zeroing flag - k0 is forbidden
1535    fn decompose_opmask(&self, opmask: Option<VPUMaskType>) -> Result<(u8, bool), AsmError> {
1536        Ok(match opmask {
1537            Some(VPUMaskType::Blend(r)) if r.id != 0 => (r.id, false),
1538            Some(VPUMaskType::Zero(r)) if r.id != 0 => (r.id, true),
1539            Some(_) => return Err(AsmError { kind: AsmErrorKind::VPUOpmaskWasK0, line_num: self.line_num, pos: None, inner_err: None }),
1540            None => (0, false),
1541        })
1542    }
1543
1544    pub(crate) fn process_vpu_move(&mut self, args: Vec<Argument>, elem_size: Option<Size>, packed: bool, aligned: bool, special_transfer: bool) -> Result<(), AsmError> {
1545        debug_assert!(packed || elem_size.is_some()); // scalar => elem_size
1546        debug_assert!(packed || !aligned);            // scalar => !aligned
1547        debug_assert!(!special_transfer || !packed);  // special_transfer => scalar
1548        let ext_op_base = (if !packed { 44 } else if aligned { 20 } else { 32 }) + elem_size.unwrap_or(Size::Qword).basic_sizecode().unwrap();
1549
1550        if args.len() != 2 { return Err(AsmError { kind: AsmErrorKind::ArgsExpectedCount(&[2]), line_num: self.line_num, pos: None, inner_err: None }); }
1551        let mut args = args.into_iter();
1552        let arg1 = args.next().unwrap();
1553        let arg2 = args.next().unwrap();
1554
1555        self.append_byte(OPCode::TRANS as u8)?;
1556
1557        if !special_transfer {
1558            let supported_types = slice_slice!(ArgumentType:
1559                [VPURegister, Address],
1560                [Address, VPURegister],
1561                [VPURegister, VPURegister],
1562            );
1563
1564            match (arg1, arg2) {
1565                (Argument::VPURegister { reg, mask }, Argument::Address { addr, mask: src_mask }) => {
1566                    if src_mask.is_some() { return Err(AsmError { kind: AsmErrorKind::VPUMaskOnNondestArg, line_num: self.line_num, pos: None, inner_err: None }); }
1567                    if let Some(size) = addr.pointed_size {
1568                        if packed && size != reg.size { return Err(AsmError { kind: AsmErrorKind::OperandsHadDifferentSizes(reg.size, size), line_num: self.line_num, pos: None, inner_err: None }); }
1569                        if !packed && size != elem_size.unwrap() { return Err(AsmError { kind: AsmErrorKind::ArgumentInvalidSize { index: Some(1), got: size, expected: vec![elem_size.unwrap()] }, line_num: self.line_num, pos: None, inner_err: None }); }
1570                    }
1571                    if elem_size.is_none() && mask.is_some() { return Err(AsmError { kind: AsmErrorKind::VPUMaskOnInstructionWithoutElemSize, line_num: self.line_num, pos: None, inner_err: None }); }
1572                    let (mask_reg, zeroing) = self.decompose_opmask(mask)?;
1573
1574                    self.append_byte(ext_op_base + 0)?;
1575                    self.append_byte((mask_reg << 5) | reg.id)?;
1576                    self.append_byte((if zeroing { 0x80 } else { 0 }) | (reg.size.vector_sizecode().unwrap() << 5))?;
1577                    self.append_address(addr)?;
1578                }
1579                (Argument::Address { addr, mask }, Argument::VPURegister { reg, mask: src_mask }) => {
1580                    if src_mask.is_some() { return Err(AsmError { kind: AsmErrorKind::VPUMaskOnNondestArg, line_num: self.line_num, pos: None, inner_err: None }); }
1581                    if let Some(size) = addr.pointed_size {
1582                        if packed && size != reg.size { return Err(AsmError { kind: AsmErrorKind::OperandsHadDifferentSizes(size, reg.size), line_num: self.line_num, pos: None, inner_err: None }); }
1583                        if !packed && size != elem_size.unwrap() { return Err(AsmError { kind: AsmErrorKind::ArgumentInvalidSize { index: Some(1), got: size, expected: vec![elem_size.unwrap()] }, line_num: self.line_num, pos: None, inner_err: None }); }
1584                    }
1585                    if elem_size.is_none() && mask.is_some() { return Err(AsmError { kind: AsmErrorKind::VPUMaskOnInstructionWithoutElemSize, line_num: self.line_num, pos: None, inner_err: None }); }
1586                    let (mask_reg, zeroing) = self.decompose_opmask(mask)?;
1587
1588                    self.append_byte(ext_op_base + 4)?;
1589                    self.append_byte(mask_reg << 5)?;
1590                    self.append_byte((if zeroing { 0x80 } else { 0 }) | (reg.size.vector_sizecode().unwrap() << 5) | reg.id)?;
1591                    self.append_address(addr)?;
1592                }
1593                (Argument::VPURegister { reg: dest_reg, mask }, Argument::VPURegister { reg: src_reg, mask: src_mask }) => {
1594                    if src_mask.is_some() { return Err(AsmError { kind: AsmErrorKind::VPUMaskOnNondestArg, line_num: self.line_num, pos: None, inner_err: None }); }
1595                    if dest_reg.size != src_reg.size { return Err(AsmError { kind: AsmErrorKind::OperandsHadDifferentSizes(dest_reg.size, src_reg.size), line_num: self.line_num, pos: None, inner_err: None }); }
1596                    if elem_size.is_none() && mask.is_some() { return Err(AsmError { kind: AsmErrorKind::VPUMaskOnInstructionWithoutElemSize, line_num: self.line_num, pos: None, inner_err: None }); }
1597                    let (mask_reg, zeroing) = self.decompose_opmask(mask)?;
1598
1599                    self.append_byte(ext_op_base + 8)?;
1600                    self.append_byte((mask_reg << 5) | dest_reg.id)?;
1601                    self.append_byte((if zeroing { 0x80 } else { 0 }) | (dest_reg.size.vector_sizecode().unwrap() << 5) | src_reg.id)?;
1602                }
1603                (a, b) => return Err(AsmError { kind: AsmErrorKind::ArgumentsInvalidTypes { got: vec![a.get_type(), b.get_type()], expected: supported_types }, line_num: self.line_num, pos: None, inner_err: None }),
1604            }
1605        } else { // else special transfer
1606            let supported_types = slice_slice!(ArgumentType:
1607                [CPURegister, VPURegister],
1608                [VPURegister, CPURegister],
1609                [VPURegister, Address],
1610                [Address, VPURegister],
1611            );
1612
1613            let cpu_trans = |args: &mut AssembleArgs, op: u8, vreg_idx: usize, reg_idx: usize, vreg: VPURegisterInfo, reg: CPURegisterInfo, mask: Option<VPUMaskType>| -> Result<(), AsmError> {
1614                if mask.is_some() { return Err(AsmError { kind: AsmErrorKind::VPUMaskOnNonVecInstruction, line_num: args.line_num, pos: None, inner_err: None }) }
1615                if reg.size != elem_size.unwrap() { return Err(AsmError { kind: AsmErrorKind::ArgumentInvalidSize { index: Some(reg_idx), got: reg.size, expected: vec![elem_size.unwrap()] }, line_num: args.line_num, pos: None, inner_err: None }) }
1616                if vreg.size != Size::Xword { return Err(AsmError { kind: AsmErrorKind::ArgumentInvalidSize { index: Some(vreg_idx), got: vreg.size, expected: vec![Size::Xword] }, line_num: args.line_num, pos: None, inner_err: None }) }
1617    
1618                args.append_byte(op)?;
1619                args.append_byte((reg.size.basic_sizecode().unwrap() << 6) | vreg.id)?;
1620                args.append_byte((if reg.high { 0x80 } else { 0 }) | reg.id)
1621            };
1622            let mem_trans = |args: &mut AssembleArgs, op: u8, vreg_idx: usize, addr_idx: usize, vreg: VPURegisterInfo, addr: Address, masks: &[Option<VPUMaskType>]| -> Result<(), AsmError> {
1623                if masks.iter().any(|s| s.is_some()) { return Err(AsmError { kind: AsmErrorKind::VPUMaskOnNonVecInstruction, line_num: args.line_num, pos: None, inner_err: None }) }
1624                if let Some(size) = addr.pointed_size {
1625                    if size != elem_size.unwrap() { return Err(AsmError { kind: AsmErrorKind::ArgumentInvalidSize { index: Some(addr_idx), got: size, expected: vec![elem_size.unwrap()] }, line_num: args.line_num, pos: None, inner_err: None }) }
1626                }
1627                if vreg.size != Size::Xword { return Err(AsmError { kind: AsmErrorKind::ArgumentInvalidSize { index: Some(vreg_idx), got: vreg.size, expected: vec![Size::Xword] }, line_num: args.line_num, pos: None, inner_err: None }) }
1628    
1629                args.append_byte(op)?;
1630                args.append_byte((elem_size.unwrap().basic_sizecode().unwrap() << 6) | vreg.id)?;
1631                args.append_address(addr)
1632            };
1633
1634            match (arg1, arg2) {
1635                (Argument::VPURegister { reg: vreg, mask }, Argument::CPURegister(reg)) => cpu_trans(self, 56, 0, 1, vreg, reg, mask)?,
1636                (Argument::CPURegister(reg), Argument::VPURegister { reg: vreg, mask }) => cpu_trans(self, 57, 1, 0, vreg, reg, mask)?,
1637                (Argument::VPURegister { reg: vreg, mask: mask1 }, Argument::Address { addr, mask: mask2 }) => mem_trans(self, 58, 0, 1, vreg, addr, &[mask1, mask2])?,
1638                (Argument::Address { addr, mask: mask2 }, Argument::VPURegister { reg: vreg, mask: mask1 }) => mem_trans(self, 59, 1, 0, vreg, addr, &[mask1, mask2])?,
1639                (a, b) => return Err(AsmError { kind: AsmErrorKind::ArgumentsInvalidTypes { got: vec![a.get_type(), b.get_type()], expected: supported_types }, line_num: self.line_num, pos: None, inner_err: None }),
1640            }
1641        }
1642
1643        Ok(())
1644    }
1645
1646    pub(crate) fn process_vpu_binary_op(&mut self, args: Vec<Argument>, op: u8, ext_op: Option<u8>, elem_size: Size, packed: bool) -> Result<(), AsmError> {
1647        if args.len() != 2 && args.len() != 3 { return Err(AsmError { kind: AsmErrorKind::ArgsExpectedCount(&[2, 3]), line_num: self.line_num, pos: None, inner_err: None }); }
1648        
1649        let shorthand = args.len() == 2;
1650        let mut args = args.into_iter();
1651        let (dest, mask_reg, zeroing) = match args.next().unwrap() {
1652            Argument::VPURegister { reg, mask } => self.decompose_opmask(mask).map(|r| (reg, r.0, r.1))?,
1653            x => return Err(AsmError { kind: AsmErrorKind::ArgumentInvalidType { index: Some(0), got: x.get_type(), expected: &[ArgumentType::VPURegister] }, line_num: self.line_num, pos: None, inner_err: None }),
1654        };
1655        let src1 = if shorthand { dest } else {
1656            match args.next().unwrap() {
1657                Argument::VPURegister { reg, mask } => {
1658                    if reg.size != dest.size { return Err(AsmError { kind: AsmErrorKind::OperandsHadDifferentSizes(dest.size, reg.size), line_num: self.line_num, pos: None, inner_err: None }); }
1659                    if mask.is_some() { return Err(AsmError { kind: AsmErrorKind::VPUMaskOnNondestArg, line_num: self.line_num, pos: None, inner_err: None }); }
1660                    reg
1661                }
1662                x => return Err(AsmError { kind: AsmErrorKind::ArgumentInvalidType { index: Some(1), got: x.get_type(), expected: &[ArgumentType::VPURegister] }, line_num: self.line_num, pos: None, inner_err: None }),
1663            }
1664        };
1665        //[8: op]    [3: mask][5: dest]    [1: zeroing][2: vreg size][5: src1]    [1: mem][2:][5: src2]    ([address src2])
1666        self.append_byte(op)?;
1667        if let Some(ext) = ext_op { self.append_byte(ext)? }
1668        
1669        self.append_byte((mask_reg << 5) | dest.id)?;
1670        self.append_byte((if zeroing { 0x80 } else { 0 }) | (if packed { dest.size.vector_sizecode().unwrap() } else { 3 } << 5) | src1.id)?;
1671
1672        let last_index = Some(if shorthand { 1 } else { 2 });
1673        match args.next().unwrap() {
1674            Argument::VPURegister { reg, mask } => {
1675                if reg.size != dest.size { return Err(AsmError { kind: AsmErrorKind::OperandsHadDifferentSizes(dest.size, reg.size), line_num: self.line_num, pos: None, inner_err: None }); }
1676                if mask.is_some() { return Err(AsmError { kind: AsmErrorKind::VPUMaskOnNondestArg, line_num: self.line_num, pos: None, inner_err: None }); }
1677                
1678                self.append_byte(reg.id)?;
1679            }
1680            Argument::Address { addr, mask } => {
1681                if mask.is_some() { return Err(AsmError { kind: AsmErrorKind::VPUMaskOnNondestArg, line_num: self.line_num, pos: None, inner_err: None }); }
1682                if let Some(size) = addr.pointed_size {
1683                    if packed && size != dest.size { return Err(AsmError { kind: AsmErrorKind::OperandsHadDifferentSizes(dest.size, size), line_num: self.line_num, pos: None, inner_err: None }); }
1684                    if !packed && size != elem_size { return Err(AsmError { kind: AsmErrorKind::ArgumentInvalidSize { index: Some(1), got: size, expected: vec![elem_size] }, line_num: self.line_num, pos: None, inner_err: None }); }
1685                }
1686
1687                self.append_byte(0x80)?;
1688                self.append_address(addr)?;
1689            }
1690            x => return Err(AsmError { kind: AsmErrorKind::ArgumentInvalidType { index: last_index, got: x.get_type(), expected: &[ArgumentType::VPURegister, ArgumentType::Address] }, line_num: self.line_num, pos: None, inner_err: None }),
1691        };
1692
1693        Ok(())
1694    }
1695
1696    pub(crate) fn process_kmov(&mut self, args: Vec<Argument>, size: Size) -> Result<(), AsmError> {
1697        let reg_size = match size {
1698            Size::Byte | Size::Word | Size::Dword => Size::Dword,
1699            Size::Qword => Size::Qword,
1700            _ => panic!(),
1701        };
1702
1703        if args.len() != 2 { return Err(AsmError { kind: AsmErrorKind::ArgsExpectedCount(&[2]), line_num: self.line_num, pos: None, inner_err: None }); }
1704        let mut args = args.into_iter();
1705        let arg1 = args.next().unwrap();
1706        let arg2 = args.next().unwrap();
1707
1708        let supported_types = slice_slice!(ArgumentType:
1709            [VPUMaskRegister, CPURegister],
1710            [CPURegister, VPUMaskRegister],
1711            [VPUMaskRegister, VPUMaskRegister],
1712            [VPUMaskRegister, Address],
1713            [Address, VPUMaskRegister],
1714        );
1715
1716        self.append_byte(OPCode::TRANS as u8)?;
1717
1718        match (arg1, arg2) {
1719            (Argument::VPUMaskRegister(dest), Argument::CPURegister(src)) => {
1720                if src.size != reg_size { return Err(AsmError { kind: AsmErrorKind::ArgumentInvalidSize { index: Some(1), got: src.size, expected: vec![reg_size] }, line_num: self.line_num, pos: None, inner_err: None }); }
1721
1722                debug_assert_ne!(src.size, Size::Byte); // we don't support high registers
1723                self.append_byte(0 + size.basic_sizecode().unwrap())?;
1724                self.append_byte((dest.id << 5) | src.id)?;
1725            }
1726            (Argument::CPURegister(dest), Argument::VPUMaskRegister(src)) => {
1727                if dest.size != reg_size { return Err(AsmError { kind: AsmErrorKind::ArgumentInvalidSize { index: Some(0), got: dest.size, expected: vec![reg_size] }, line_num: self.line_num, pos: None, inner_err: None }); }
1728
1729                debug_assert_ne!(dest.size, Size::Byte); // we don't support high registers
1730                self.append_byte(4 + size.basic_sizecode().unwrap())?;
1731                self.append_byte((dest.id << 4) | src.id)?;
1732            }
1733            (Argument::VPUMaskRegister(dest), Argument::VPUMaskRegister(src)) => {
1734                self.append_byte(8 + size.basic_sizecode().unwrap())?;
1735                self.append_byte((dest.id << 5) | src.id)?;
1736            }
1737            (Argument::VPUMaskRegister(dest), Argument::Address { addr, mask }) => {
1738                if mask.is_some() { return Err(AsmError { kind: AsmErrorKind::VPUMaskOnNonVecInstruction, line_num: self.line_num, pos: None, inner_err: None }); }
1739                if let Some(s) = addr.pointed_size {
1740                    if s != size { return Err(AsmError { kind: AsmErrorKind::ArgumentInvalidSize { index: Some(1), got: s, expected: vec![size] }, line_num: self.line_num, pos: None, inner_err: None }); }
1741                }
1742                
1743                self.append_byte(12 + size.basic_sizecode().unwrap())?;
1744                self.append_byte(dest.id << 5)?;
1745                self.append_address(addr)?;
1746            }
1747            (Argument::Address { addr, mask }, Argument::VPUMaskRegister(src)) => {
1748                if mask.is_some() { return Err(AsmError { kind: AsmErrorKind::VPUMaskOnNonVecInstruction, line_num: self.line_num, pos: None, inner_err: None }); }
1749                if let Some(s) = addr.pointed_size {
1750                    if s != size { return Err(AsmError { kind: AsmErrorKind::ArgumentInvalidSize { index: Some(0), got: s, expected: vec![size] }, line_num: self.line_num, pos: None, inner_err: None }); }
1751                }
1752                
1753                self.append_byte(16 + size.basic_sizecode().unwrap())?;
1754                self.append_byte(src.id)?;
1755                self.append_address(addr)?;
1756            }
1757            (a, b) => return Err(AsmError { kind: AsmErrorKind::ArgumentsInvalidTypes { got: vec![a.get_type(), b.get_type()], expected: supported_types }, line_num: self.line_num, pos: None, inner_err: None }),
1758        }
1759
1760        Ok(())
1761    }
1762
1763    fn verify_legal_expr(&self, expr: &Expr, line_num: usize) -> Result<(), AsmError> {
1764        match &*expr.data.borrow() {
1765            ExprData::Value(_) => (),
1766            ExprData::Ident(ident) => if !self.file.symbols.is_defined(ident) && !self.file.extern_symbols.contains_key(ident) && !ident.starts_with('#') {
1767                return Err(AsmError { kind: AsmErrorKind::UnknownSymbol(ident.clone()), line_num, pos: None, inner_err: None });
1768            }
1769            ExprData::Uneval { op: _, left, right } => {
1770                self.verify_legal_expr(left.as_ref().unwrap(), line_num)?;
1771                if let Some(right) = right { self.verify_legal_expr(right, line_num)?; }
1772            }
1773        }
1774        Ok(())
1775    }
1776    pub(super) fn finalize(self) -> Result<ObjectFile, AsmError> {
1777        // make sure all globals are defined
1778        for (global, &line_num) in self.file.global_symbols.iter() {
1779            if !self.file.symbols.is_defined(global) {
1780                return Err(AsmError { kind: AsmErrorKind::GlobalSymbolWasNotDefined(global.clone()), line_num, pos: None, inner_err: None });
1781            }
1782        }
1783
1784        for (_, (expr, line_num)) in self.file.symbols.iter() { self.verify_legal_expr(expr, *line_num)?; }
1785
1786        for hole in self.file.text_holes.iter() { self.verify_legal_expr(&hole.expr, hole.line_num)?; }
1787        for hole in self.file.rodata_holes.iter() { self.verify_legal_expr(&hole.expr, hole.line_num)?; }
1788        for hole in self.file.data_holes.iter() { self.verify_legal_expr(&hole.expr, hole.line_num)?; }
1789
1790        Ok(self.file) // if we're ok, return the final result
1791    }
1792}
1793
1794#[cfg(test)]
1795fn create_context() -> AssembleArgs<'static> {
1796    AssembleArgs {
1797        file_name: "test.asm",
1798        file: ObjectFile {
1799            global_symbols: Default::default(),
1800            extern_symbols: Default::default(),
1801
1802            symbols: Default::default(),
1803
1804            text_align: Default::default(),
1805            rodata_align: Default::default(),
1806            data_align: Default::default(),
1807            bss_align: Default::default(),
1808
1809            text_holes: Default::default(),
1810            rodata_holes: Default::default(),
1811            data_holes: Default::default(),
1812
1813            text: Default::default(),
1814            rodata: Default::default(),
1815            data: Default::default(),
1816            bss_len: Default::default(),
1817        },
1818    
1819        current_seg: Some(AsmSegment::Text),
1820        done_segs: Default::default(),
1821
1822        line_num: Default::default(),
1823        line_pos_in_seg: Default::default(),
1824
1825        last_nonlocal_label: Default::default(),
1826        label_def: Default::default(),
1827
1828        times: Default::default(),
1829    }
1830}
1831
1832#[test]
1833fn test_extr_bin_op() {
1834    let c = create_context();
1835
1836    match c.extract_binary_op("+", 0, 1) {
1837        Some((op ,aft)) => {
1838            assert_eq!(op, OP::Add);
1839            assert_eq!(aft, 1);
1840        }
1841        None => panic!(),
1842    }
1843    match c.extract_binary_op("    +  ", 2, 7) {
1844        Some((op, aft)) => {
1845            assert_eq!(op, OP::Add);
1846            assert_eq!(aft, 5);
1847        }
1848        None => panic!(),
1849    }
1850    match c.extract_binary_op("    a  ", 2, 7) {
1851        Some(_) => panic!(),
1852        None => (),
1853    }
1854}
1855
1856#[test]
1857fn test_extr_string() {
1858    let c = create_context();
1859
1860    match c.extract_string("'hello world'", 0, 13) {
1861        Ok((res, aft)) => {
1862            assert_eq!(res, "hello world".as_bytes());
1863            assert_eq!(aft, 13);
1864        }
1865        Err(_) => panic!(),
1866    }
1867    match c.extract_string("hello      ' wo rld'  ", 5, 22) {
1868        Ok((res, aft)) => {
1869            assert_eq!(res, " wo rld".as_bytes());
1870            assert_eq!(aft, 20);
1871        }
1872        Err(_) => panic!(),
1873    }
1874    match c.extract_string("hello      ' wo rld' b ", 12, 22) {
1875        Ok(_) => panic!(),
1876        Err(e) => {
1877            assert!(matches!(e.kind, AsmErrorKind::ExpectedString));
1878            assert_eq!(e.pos, Some(13));
1879        }
1880    }
1881    match c.extract_string("hello      ' wo rld'y ", 5, 19) {
1882        Ok(_) => panic!(),
1883        Err(e) => {
1884            assert!(matches!(e.kind, AsmErrorKind::IncompleteString));
1885            assert_eq!(e.pos, Some(11));
1886        }
1887    }
1888    match c.extract_string("hello      ' wo rld'  ", 5, 11) {
1889        Ok(_) => panic!(),
1890        Err(e) => {
1891            assert!(matches!(e.kind, AsmErrorKind::ExpectedString));
1892            assert_eq!(e.pos, Some(11));
1893        }
1894    }
1895    match c.extract_string("hello      ' wo rld'  ", 5, 12) {
1896        Ok(_) => panic!(),
1897        Err(e) => {
1898            assert!(matches!(e.kind, AsmErrorKind::IncompleteString));
1899            assert_eq!(e.pos, Some(11));
1900        }
1901    }
1902    match c.extract_string("\"\\\\\\'\\\"'\\n\\t\\r\\0\\x12\\xfe\"\t ", 0, 25) {
1903        Ok((res, aft)) => {
1904            assert_eq!(res, &[92, 39, 34, 39, 10, 9, 13, 0, 0x12, 0xfe]);
1905            assert_eq!(aft, 25);
1906        }
1907        Err(_) => panic!(),
1908    }
1909    match c.extract_string("'\\\\\\'\\\"\"\\n\\t\\r\\0\\x12\\xfe'\t ", 0, 25) {
1910        Ok((res, aft)) => {
1911            assert_eq!(res, &[92, 39, 34, 34, 10, 9, 13, 0, 0x12, 0xfe]);
1912            assert_eq!(aft, 25);
1913        }
1914        Err(_) => panic!(),
1915    }
1916    match c.extract_string("  'hello;world' 'another string' ", 1, 33) {
1917        Ok((res, aft)) => {
1918            assert_eq!(res, "hello;world".as_bytes());
1919            assert_eq!(aft, 15);
1920        }
1921        Err(_) => panic!(),
1922    }
1923    match c.extract_string("  'hello world' 'another string' ", 14, 33) {
1924        Ok((res, aft)) => {
1925            assert_eq!(res, " ".as_bytes());
1926            assert_eq!(aft, 17);
1927        }
1928        Err(_) => panic!(),
1929    }
1930    match c.extract_string("  '\\y'y", 2, 7) {
1931        Ok(_) => panic!(),
1932        Err(e) => {
1933            assert!(matches!(e.kind, AsmErrorKind::InvalidEscape));
1934            assert_eq!(e.pos, Some(3));
1935        }
1936    }
1937    match c.extract_string("  '\\x'", 1, 6) {
1938        Ok(_) => panic!(),
1939        Err(e) => {
1940            assert!(matches!(e.kind, AsmErrorKind::IncompleteEscape));
1941            assert_eq!(e.pos, Some(3));
1942        }
1943    }
1944    match c.extract_string("  '\\x5'", 1, 7) {
1945        Ok(_) => panic!(),
1946        Err(e) => {
1947            assert!(matches!(e.kind, AsmErrorKind::IncompleteEscape));
1948            assert_eq!(e.pos, Some(3));
1949        }
1950    }
1951    match c.extract_string("  '\\x5g'", 2, 8) {
1952        Ok(_) => panic!(),
1953        Err(e) => {
1954            assert!(matches!(e.kind, AsmErrorKind::IncompleteEscape));
1955            assert_eq!(e.pos, Some(3));
1956        }
1957    }
1958    match c.extract_string("  '\\x", 2, 5) {
1959        Ok(_) => panic!(),
1960        Err(e) => {
1961            assert!(matches!(e.kind, AsmErrorKind::IncompleteEscape));
1962            assert_eq!(e.pos, Some(3));
1963        }
1964    }
1965    match c.extract_string("  '\\x4", 1, 6) {
1966        Ok(_) => panic!(),
1967        Err(e) => {
1968            assert!(matches!(e.kind, AsmErrorKind::IncompleteEscape));
1969            assert_eq!(e.pos, Some(3));
1970        }
1971    }
1972    match c.extract_string("  '\\x4b", 2, 7) {
1973        Ok(_) => panic!(),
1974        Err(e) => {
1975            assert!(matches!(e.kind, AsmErrorKind::IncompleteString));
1976            assert_eq!(e.pos, Some(2));
1977        }
1978    }
1979    match c.extract_string("  '\\", 1, 4) {
1980        Ok(_) => panic!(),
1981        Err(e) => {
1982            assert!(matches!(e.kind, AsmErrorKind::IncompleteEscape));
1983            assert_eq!(e.pos, Some(3));
1984        }
1985    }
1986}
1987
1988#[test]
1989fn test_extr_expr() {
1990    let mut c = create_context();
1991    match c.extract_expr("trUe;", 0, 5) {
1992        Ok((expr, aft)) => {
1993            assert_eq!(aft, 4);
1994            match expr.into_eval(&c.file.symbols).unwrap() {
1995                ValueCow::Owned(Value::Logical(val)) => assert_eq!(val, true),
1996                _ => panic!(),
1997            }
1998        }
1999        Err(e) => panic!("{:?}", e),
2000    }
2001    match c.extract_expr("faLse", 0, 5) {
2002        Ok((expr, aft)) => {
2003            assert_eq!(aft, 5);
2004            match expr.into_eval(&c.file.symbols).unwrap() {
2005                ValueCow::Owned(Value::Logical(val)) => assert_eq!(val, false),
2006                _ => panic!(),
2007            }
2008        }
2009        Err(e) => panic!("{:?}", e),
2010    }
2011    match c.extract_expr("nuLl", 0, 4) {
2012        Ok((expr, aft)) => {
2013            assert_eq!(aft, 4);
2014            match expr.into_eval(&c.file.symbols).unwrap() {
2015                ValueCow::Owned(Value::Integer(val)) => assert_eq!(val, 0),
2016                _ => panic!(),
2017            }
2018        }
2019        Err(e) => panic!("{:?}", e),
2020    }
2021    match c.extract_expr("5", 0, 1) {
2022        Ok((expr, aft)) => {
2023            assert_eq!(aft, 1);
2024            match expr.into_eval(&c.file.symbols).unwrap() {
2025                ValueCow::Owned(Value::Integer(val)) => assert_eq!(val, 5),
2026                _ => panic!(),
2027            }
2028        }
2029        Err(e) => panic!("{:?}", e),
2030    }
2031    match c.extract_expr("  010  ", 1, 7) {
2032        Ok(r) => panic!("{:?}", r),
2033        Err(e) => {
2034            assert_eq!(e.pos, Some(2));
2035            assert!(matches!(e.kind, AsmErrorKind::NumericLiteralWithZeroPrefix));
2036        }
2037    }
2038    match c.extract_expr("  00  ", 1, 6) {
2039        Ok(r) => panic!("{:?}", r),
2040        Err(e) => {
2041            assert_eq!(e.pos, Some(2));
2042            assert!(matches!(e.kind, AsmErrorKind::NumericLiteralWithZeroPrefix));
2043        }
2044    }
2045    match c.extract_expr("  00_  ", 1, 7) {
2046        Ok(r) => panic!("{:?}", r),
2047        Err(e) => {
2048            assert_eq!(e.pos, Some(2));
2049            assert!(matches!(e.kind, AsmErrorKind::NumericLiteralWithZeroPrefix));
2050        }
2051    }
2052    match c.extract_expr("  0_0_  ", 1, 8) {
2053        Ok(r) => panic!("{:?}", r),
2054        Err(e) => {
2055            assert_eq!(e.pos, Some(2));
2056            assert!(matches!(e.kind, AsmErrorKind::NumericLiteralWithZeroPrefix));
2057        }
2058    }
2059    match c.extract_expr("0", 0, 1) {
2060        Ok((expr, aft)) => {
2061            assert_eq!(aft, 1);
2062            match expr.into_eval(&c.file.symbols).unwrap() {
2063                ValueCow::Owned(Value::Integer(val)) => assert_eq!(val, 0),
2064                _ => panic!(),
2065            }
2066        }
2067        Err(e) => panic!("{:?}", e),
2068    }
2069    match c.extract_expr("3.14", 0, 4) {
2070        Ok((expr, aft)) => {
2071            assert_eq!(aft, 4);
2072            match expr.into_eval(&c.file.symbols).unwrap() {
2073                ValueCow::Owned(Value::Float(val)) => assert!(Float::from(val - 3.14).abs() < 0.00000001),
2074                _ => panic!(),
2075            }
2076        }
2077        Err(e) => panic!("{:?}", e),
2078    }
2079    match c.extract_expr("  .14  ", 1, 7) {
2080        Ok(v) => panic!("{:?}", v),
2081        Err(e) => {
2082            assert_eq!(e.pos, Some(2));
2083            assert!(matches!(e.kind, AsmErrorKind::InvalidSymbolName));
2084        }
2085    }
2086    match c.extract_expr("5+8", 0, 3) {
2087        Ok((expr, aft)) => {
2088            assert_eq!(aft, 3);
2089            match expr.into_eval(&c.file.symbols).unwrap() {
2090                ValueCow::Owned(Value::Integer(val)) => assert_eq!(val, 13),
2091                _ => panic!(),
2092            }
2093        }
2094        Err(e) => panic!("{:?}", e),
2095    }
2096    match c.extract_expr("5+8*2-1", 0, 7) {
2097        Ok((expr, aft)) => {
2098            assert_eq!(aft, 7);
2099            match expr.into_eval(&c.file.symbols).unwrap() {
2100                ValueCow::Owned(Value::Integer(val)) => assert_eq!(val, 20),
2101                _ => panic!(),
2102            }
2103        }
2104        Err(e) => panic!("{:?}", e),
2105    }
2106    match c.extract_expr("(5+1)*(5-1) g", 0, 13) {
2107        Ok((expr, aft)) => {
2108            assert_eq!(aft, 11);
2109            match expr.into_eval(&c.file.symbols).unwrap() {
2110                ValueCow::Owned(Value::Integer(val)) => assert_eq!(val, 24),
2111                _ => panic!(),
2112            }
2113        }
2114        Err(e) => panic!("{:?}", e),
2115    }
2116    match c.extract_expr("(5+1)*;(5-1) g", 0, 13) {
2117        Ok(r) => panic!("{:?}", r),
2118        Err(e) => {
2119            assert_eq!(e.pos, Some(6));
2120            assert!(matches!(e.kind, AsmErrorKind::ExpectedExpr));
2121        }
2122    }
2123    match c.extract_expr("(5+1);*(5-1) g", 0, 13) {
2124        Ok((expr, aft)) => {
2125            assert_eq!(aft, 5);
2126            match expr.into_eval(&c.file.symbols).unwrap() {
2127                ValueCow::Owned(Value::Integer(val)) => assert_eq!(val, 6),
2128                _ => panic!(),
2129            }
2130        }
2131        Err(e) => panic!("{:?}", e),
2132    }
2133    match c.extract_expr("(5;+1)", 0, 6) {
2134        Ok(r) => panic!("{:?}", r),
2135        Err(e) => {
2136            assert_eq!(e.pos, Some(0));
2137            assert!(matches!(e.kind, AsmErrorKind::UnclosedParen));
2138        }
2139    }
2140    match c.extract_expr(";(5;+1)", 0, 7) {
2141        Ok(r) => panic!("{:?}", r),
2142        Err(e) => {
2143            assert_eq!(e.pos, Some(0));
2144            assert!(matches!(e.kind, AsmErrorKind::ExpectedExpr));
2145        }
2146    }
2147    match c.extract_expr("  (  5-3 )     *--+ -(5 -1)f  ", 1, 30) {
2148        Ok((expr, aft)) => {
2149            assert_eq!(aft, 27);
2150            match expr.into_eval(&c.file.symbols).unwrap() {
2151                ValueCow::Owned(Value::Integer(val)) => assert_eq!(val, -8),
2152                _ => panic!(),
2153            }
2154        }
2155        Err(e) => panic!("{:?}", e),
2156    }
2157    match c.extract_expr("  \"hello world\"  ", 1, 17) {
2158        Ok((expr, aft)) => {
2159            assert_eq!(aft, 15);
2160            match expr.into_eval(&c.file.symbols).unwrap() {
2161                ValueCow::Owned(Value::Binary(val)) => assert_eq!(val, "hello world".as_bytes()),
2162                _ => panic!(),
2163            }
2164        }
2165        Err(e) => panic!("{:?}", e),
2166    }
2167    match c.extract_expr("  \"hello;world\"  ", 1, 17) {
2168        Ok((expr, aft)) => {
2169            assert_eq!(aft, 15);
2170            match expr.into_eval(&c.file.symbols).unwrap() {
2171                ValueCow::Owned(Value::Binary(val)) => assert_eq!(val, "hello;world".as_bytes()),
2172                _ => panic!(),
2173            }
2174        }
2175        Err(e) => panic!("{:?}", e),
2176    }
2177    match c.extract_expr("  'hello;world\"  ", 1, 17) {
2178        Ok(r) => panic!("{:?}", r),
2179        Err(e) => {
2180            assert_eq!(e.pos, Some(2));
2181            assert!(matches!(e.kind, AsmErrorKind::IncompleteString));
2182        }
2183    }
2184    match c.extract_expr("$if(TrUe,6 / -2,3 << 2)", 0, 23) {
2185        Ok((expr, aft)) => {
2186            assert_eq!(aft, 23);
2187            match expr.into_eval(&c.file.symbols).unwrap() {
2188                ValueCow::Owned(Value::Integer(val)) => assert_eq!(val, -3),
2189                _ => panic!(),
2190            }
2191        }
2192        Err(e) => panic!("{:?}", e),
2193    }
2194    match c.extract_expr("   $if(  False == true  ,    6 / -  2 ,  3 << 2   )  ", 1, 53) {
2195        Ok((expr, aft)) => {
2196            assert_eq!(aft, 51);
2197            match expr.into_eval(&c.file.symbols).unwrap() {
2198                ValueCow::Owned(Value::Integer(val)) => assert_eq!(val, 12),
2199                _ => panic!(),
2200            }
2201        }
2202        Err(e) => panic!("{:?}", e),
2203    }
2204}
2205#[test]
2206fn test_float_lexer() {
2207    let mut c = create_context();
2208    match c.extract_expr("     1.234 - 1.0    ", 1, 20) {
2209        Err(e) => panic!("{:?}", e),
2210        Ok((expr, aft)) => {
2211            assert_eq!(aft, 16);
2212            match expr.into_eval(&c.file.symbols).unwrap() {
2213                ValueCow::Owned(v) => match v {
2214                    Value::Float(v) => assert!(v - 0.234 < 0.00000000001),
2215                    x => panic!("{:?}", x),
2216                }
2217                _ => panic!(),
2218            }
2219        }
2220    }
2221    match c.extract_expr("     1.234 -1.0    ", 1, 19) {
2222        Err(e) => panic!("{:?}", e),
2223        Ok((expr, aft)) => {
2224            assert_eq!(aft, 15);
2225            match expr.into_eval(&c.file.symbols).unwrap() {
2226                ValueCow::Owned(v) => match v {
2227                    Value::Float(v) => assert!(v - 0.234 < 0.00000000001),
2228                    x => panic!("{:?}", x),
2229                }
2230                _ => panic!(),
2231            }
2232        }
2233    }
2234    match c.extract_expr("     1.234- 1.0    ", 1, 19) {
2235        Err(e) => panic!("{:?}", e),
2236        Ok((expr, aft)) => {
2237            assert_eq!(aft, 15);
2238            match expr.into_eval(&c.file.symbols).unwrap() {
2239                ValueCow::Owned(v) => match v {
2240                    Value::Float(v) => assert!(v - 0.234 < 0.00000000001),
2241                    x => panic!("{:?}", x),
2242                }
2243                _ => panic!(),
2244            }
2245        }
2246    }
2247    match c.extract_expr("     1.234-1.0    ", 1, 18) {
2248        Err(e) => panic!("{:?}", e),
2249        Ok((expr, aft)) => {
2250            assert_eq!(aft, 14);
2251            match expr.into_eval(&c.file.symbols).unwrap() {
2252                ValueCow::Owned(v) => match v {
2253                    Value::Float(v) => assert!(v - 0.234 < 0.00000000001),
2254                    x => panic!("{:?}", x),
2255                }
2256                _ => panic!(),
2257            }
2258        }
2259    }
2260    match c.extract_expr("     574.35849590905674857649 - 8576485769847659845769845769845646534457546756867867823344356    ", 1, 97) {
2261        Err(e) => panic!("{:?}", e),
2262        Ok((expr, aft)) => {
2263            assert_eq!(aft, 93);
2264            match expr.into_eval(&c.file.symbols) {
2265                Ok(v) => panic!("{:?}", v),
2266                Err(e) => match e {
2267                    EvalError::Illegal(e) => match e {
2268                        IllegalReason::IncompatibleTypes(OP::Sub, ValueType::Float, ValueType::Integer) => (),
2269                        r => panic!("{:?}", r),
2270                    }
2271                    e => panic!("{:?}", e),
2272                }
2273            }
2274        }
2275    }
2276    match c.extract_expr("     574.35849590905674857649-8576485769847659845769845769845646534457546756867867823344356    ", 1, 95) {
2277        Err(e) => panic!("{:?}", e),
2278        Ok((expr, aft)) => {
2279            assert_eq!(aft, 91);
2280            match expr.into_eval(&c.file.symbols) {
2281                Ok(v) => panic!("{:?}", v),
2282                Err(e) => match e {
2283                    EvalError::Illegal(e) => match e {
2284                        IllegalReason::IncompatibleTypes(OP::Sub, ValueType::Float, ValueType::Integer) => (),
2285                        r => panic!("{:?}", r),
2286                    }
2287                    e => panic!("{:?}", e),
2288                }
2289            }
2290        }
2291    }
2292}
2293#[test]
2294fn test_ptriff() {
2295    let mut c = create_context();
2296    c.file.symbols.define("foo".into(), (OP::Add, Expr::from(ExprData::Ident("#t".into())), 10i64).into(), 0).unwrap();
2297    c.file.symbols.define("bar".into(), (OP::Add, Expr::from(ExprData::Ident("#t".into())), 14i64).into(), 0).unwrap();
2298    match c.extract_expr("$-$", 0, 3) {
2299        Ok((expr, aft)) => {
2300            assert_eq!(aft, 3);
2301            match expr.into_eval(&c.file.symbols).unwrap() {
2302                ValueCow::Owned(Value::Integer(val)) => assert_eq!(val, 0),
2303                _ => panic!(),
2304            }
2305        }
2306        Err(e) => panic!("{:?}", e),
2307    }
2308    match c.extract_expr("    $ + 3  + 3 - 1 - 2  - -- $ - 2 + 1  ", 2, 40) {
2309        Ok((expr, aft)) => {
2310            assert_eq!(aft, 38);
2311            match expr.into_eval(&c.file.symbols).unwrap() {
2312                ValueCow::Owned(Value::Integer(val)) => assert_eq!(val, 2),
2313                _ => panic!(),
2314            }
2315        }
2316        Err(e) => panic!("{:?}", e),
2317    }
2318    match c.extract_expr("5*(($ + 8)-($ - 3))", 0, 19) {
2319        Ok((expr, aft)) => {
2320            assert_eq!(aft, 19);
2321            match expr.into_eval(&c.file.symbols).unwrap() {
2322                ValueCow::Owned(Value::Integer(val)) => assert_eq!(val, 55),
2323                _ => panic!(),
2324            }
2325        }
2326        Err(e) => panic!("{:?}", e),
2327    }
2328    match c.extract_expr("foo-foo", 0, 7) {
2329        Ok((expr, aft)) => {
2330            assert_eq!(aft, 7);
2331            match expr.into_eval(&c.file.symbols).unwrap() {
2332                ValueCow::Owned(Value::Integer(val)) => assert_eq!(val, 0),
2333                _ => panic!(),
2334            }
2335        }
2336        Err(e) => panic!("{:?}", e),
2337    }
2338    match c.extract_expr("foo-$;comment", 0, 5) {
2339        Ok((expr, aft)) => {
2340            assert_eq!(aft, 5);
2341            match expr.into_eval(&c.file.symbols).unwrap() {
2342                ValueCow::Owned(Value::Integer(val)) => assert_eq!(val, 10),
2343                _ => panic!(),
2344            }
2345        }
2346        Err(e) => panic!("{:?}", e),
2347    }
2348    match c.extract_expr("$-foo", 0, 5) {
2349        Ok((expr, aft)) => {
2350            assert_eq!(aft, 5);
2351            match expr.into_eval(&c.file.symbols).unwrap() {
2352                ValueCow::Owned(Value::Integer(val)) => assert_eq!(val, -10),
2353                _ => panic!(),
2354            }
2355        }
2356        Err(e) => panic!("{:?}", e),
2357    }
2358    match c.extract_expr("bar-foo", 0, 7) {
2359        Ok((expr, aft)) => {
2360            assert_eq!(aft, 7);
2361            match expr.into_eval(&c.file.symbols).unwrap() {
2362                ValueCow::Owned(Value::Integer(val)) => assert_eq!(val, 4),
2363                _ => panic!(),
2364            }
2365        }
2366        Err(e) => panic!("{:?}", e),
2367    }
2368}
2369#[test]
2370fn test_numeric_formats() {
2371    let mut c = create_context();
2372    match c.extract_expr("0x2Ff4;comment", 0, 6) {
2373        Ok((expr, aft)) => {
2374            assert_eq!(aft, 6);
2375            match expr.eval(&c.file.symbols) {
2376                Ok(v) => match &*v {
2377                    Value::Integer(v) => assert_eq!(*v, 0x2Ff4),
2378                    x => panic!("unexpected type: {:?}", x),
2379                }
2380                Err(e) => panic!("{:?}", e),
2381            }
2382        }
2383        Err(e) => panic!("{:?}", e),
2384    }
2385    match c.extract_expr("0x02Ff4", 0, 7) {
2386        Ok((expr, aft)) => {
2387            assert_eq!(aft, 7);
2388            match expr.eval(&c.file.symbols) {
2389                Ok(v) => match &*v {
2390                    Value::Integer(v) => assert_eq!(*v, 0x2Ff4),
2391                    x => panic!("unexpected type: {:?}", x),
2392                }
2393                Err(e) => panic!("{:?}", e),
2394            }
2395        }
2396        Err(e) => panic!("{:?}", e),
2397    }
2398    match c.extract_expr("0o23_34", 0, 7) {
2399        Ok((expr, aft)) => {
2400            assert_eq!(aft, 7);
2401            match expr.eval(&c.file.symbols) {
2402                Ok(v) => match &*v {
2403                    Value::Integer(v) => assert_eq!(*v, 0o23_34),
2404                    x => panic!("unexpected type: {:?}", x),
2405                }
2406                Err(e) => panic!("{:?}", e),
2407            }
2408        }
2409        Err(e) => panic!("{:?}", e),
2410    }
2411    match c.extract_expr("0o23_34_", 0, 8) {
2412        Ok((expr, aft)) => {
2413            assert_eq!(aft, 8);
2414            match expr.eval(&c.file.symbols) {
2415                Ok(v) => match &*v {
2416                    Value::Integer(v) => assert_eq!(*v, 0o23_34_),
2417                    x => panic!("unexpected type: {:?}", x),
2418                }
2419                Err(e) => panic!("{:?}", e),
2420            }
2421        }
2422        Err(e) => panic!("{:?}", e),
2423    }
2424    match c.extract_expr("0b1011_0010", 0, 11) {
2425        Ok((expr, aft)) => {
2426            assert_eq!(aft, 11);
2427            match expr.eval(&c.file.symbols) {
2428                Ok(v) => match &*v {
2429                    Value::Integer(v) => assert_eq!(*v, 0b1011_0010),
2430                    x => panic!("unexpected type: {:?}", x),
2431                }
2432                Err(e) => panic!("{:?}", e),
2433            }
2434        }
2435        Err(e) => panic!("{:?}", e),
2436    }
2437    match c.extract_expr("0b1011_0010", 0, 11) {
2438        Ok((expr, aft)) => {
2439            assert_eq!(aft, 11);
2440            match expr.eval(&c.file.symbols) {
2441                Ok(v) => match &*v {
2442                    Value::Integer(v) => assert_eq!(*v, 0b1011_0010),
2443                    x => panic!("unexpected type: {:?}", x),
2444                }
2445                Err(e) => panic!("{:?}", e),
2446            }
2447        }
2448        Err(e) => panic!("{:?}", e),
2449    }
2450}
2451#[test]
2452fn test_imm_parser() {
2453    let mut c = create_context();
2454    match c.extract_imm("   dword]  ", 1, 11) {
2455        Ok(x) => panic!("{:?}", x),
2456        Err(e) => {
2457            match e.kind {
2458                AsmErrorKind::ExpectedExpr => (),
2459                x => panic!("{:?}", x),
2460            }
2461            assert_eq!(e.pos, Some(8));
2462            assert!(e.inner_err.is_none());
2463        }
2464    }
2465}
2466#[test]
2467fn test_address_parser() {
2468    let mut c = create_context();
2469    match c.extract_address("   dword     ptr  [0x2334]  ", 2, 28) {
2470        Ok((addr, aft)) => {
2471            assert_eq!(aft, 26);
2472            assert_eq!(addr.address_size, Size::Qword);
2473            assert_eq!(addr.r1, None);
2474            assert_eq!(addr.r2, None);
2475            assert_eq!(addr.pointed_size, Some(Size::Dword));
2476            match addr.base.as_ref().unwrap().eval(&c.file.symbols) {
2477                Ok(v) => match &*v {
2478                    Value::Integer(v) => assert_eq!(*v, 0x2334),
2479                    x => panic!("unexpected type {:?}", x),
2480                }
2481                Err(e) => panic!("{:?}", e),
2482            }
2483        }
2484        Err(e) => panic!("{:?}", e),
2485    }
2486    match c.extract_address("   dword     ptr  [   0x2334   ]  ", 2, 34) {
2487        Ok((addr, aft)) => {
2488            assert_eq!(aft, 32);
2489            assert_eq!(addr.address_size, Size::Qword);
2490            assert_eq!(addr.r1, None);
2491            assert_eq!(addr.r2, None);
2492            assert_eq!(addr.pointed_size, Some(Size::Dword));
2493            match addr.base.as_ref().unwrap().eval(&c.file.symbols) {
2494                Ok(v) => match &*v {
2495                    Value::Integer(v) => assert_eq!(*v, 0x2334),
2496                    x => panic!("unexpected type {:?}", x),
2497                }
2498                Err(e) => panic!("{:?}", e),
2499            }
2500        }
2501        Err(e) => panic!("{:?}", e),
2502    }
2503    match c.extract_address(".  tword     ptr  [4*eax] _", 2, 27) {
2504        Ok((addr, aft)) => {
2505            assert_eq!(aft, 25);
2506            assert_eq!(addr.address_size, Size::Dword);
2507            assert_eq!(addr.r1, Some((0, 2)));
2508            assert_eq!(addr.r2, None);
2509            assert_eq!(addr.pointed_size, Some(Size::Tword));
2510            assert!(addr.base.is_none());
2511        }
2512        Err(e) => panic!("{:?}", e),
2513    }
2514    match c.extract_address(".  tword     ptr  [3*eax + EaX] _", 2, 33) {
2515        Ok((addr, aft)) => {
2516            assert_eq!(aft, 31);
2517            assert_eq!(addr.address_size, Size::Dword);
2518            assert_eq!(addr.r1, Some((0, 2)));
2519            assert_eq!(addr.r2, None);
2520            assert_eq!(addr.pointed_size, Some(Size::Tword));
2521            assert!(addr.base.is_none());
2522        }
2523        Err(e) => panic!("{:?}", e),
2524    }
2525    match c.extract_address("   tword     ptr  [ax+bx]  ", 2, 27) {
2526        Ok((addr, aft)) => {
2527            assert_eq!(aft, 25);
2528            assert_eq!(addr.address_size, Size::Word);
2529            assert_eq!(addr.r1.unwrap().1, 0);
2530            assert_eq!(addr.r1.unwrap().0 + addr.r2.unwrap(), 1);
2531            assert_eq!(addr.pointed_size, Some(Size::Tword));
2532            assert!(addr.base.is_none());
2533        }
2534        Err(e) => panic!("{:?}", e),
2535    }
2536    match c.extract_address("   zWord     ptr  [4*(r8d + 7)]   ", 2, 34) {
2537        Ok((addr, aft)) => {
2538            assert_eq!(aft, 31);
2539            assert_eq!(addr.address_size, Size::Dword);
2540            assert_eq!(addr.r1, Some((8, 2)));
2541            assert_eq!(addr.r2, None);
2542            assert_eq!(addr.pointed_size, Some(Size::Zword));
2543            match addr.base.as_ref().unwrap().eval(&c.file.symbols) {
2544                Ok(v) => match &*v {
2545                    Value::Integer(v) => assert_eq!(*v, 28),
2546                    x => panic!("unexpected type {:?}", x),
2547                }
2548                Err(e) => panic!("{:?}", e),
2549            }
2550        }
2551        Err(e) => panic!("{:?}", e),
2552    }
2553    match c.extract_address("  [4*(4 * (2 + r8) + (7 + -r8 * (1 + 1 * 1))) + R8] g ", 2, 54) {
2554        Ok((addr, aft)) => {
2555            assert_eq!(aft, 51);
2556            assert_eq!(addr.address_size, Size::Qword);
2557            assert_eq!(addr.r1, Some((8, 3)));
2558            assert_eq!(addr.r2, Some(8));
2559            assert_eq!(addr.pointed_size, None);
2560            match addr.base.as_ref().unwrap().eval(&c.file.symbols) {
2561                Ok(v) => match &*v {
2562                    Value::Integer(v) => assert_eq!(*v, 60),
2563                    x => panic!("unexpected type {:?}", x),
2564                }
2565                Err(e) => panic!("{:?}", e),
2566            }
2567        }
2568        Err(e) => panic!("{:?}", e),
2569    }
2570    match c.extract_address("[5*ax] extra stuff after", 0, 24) {
2571        Ok((addr, aft)) => {
2572            assert_eq!(aft, 6);
2573            assert_eq!(addr.address_size, Size::Word);
2574            assert_eq!(addr.r1, Some((0, 2)));
2575            assert_eq!(addr.r2, Some(0));
2576            assert_eq!(addr.pointed_size, None);
2577            assert!(addr.base.is_none());
2578        }
2579        Err(e) => panic!("{:?}", e),
2580    }
2581    match c.extract_address("   5 * ax]  ", 2, 12) {
2582        Ok(_) => panic!(),
2583        Err(e) => {
2584            assert_eq!(e.pos, Some(3));
2585            assert!(matches!(e.kind, AsmErrorKind::ExpectedAddress));
2586        }
2587    }
2588    match c.extract_address("   [5 * ax  ", 2, 12) {
2589        Ok(_) => panic!(),
2590        Err(e) => {
2591            assert_eq!(e.pos, Some(12));
2592            assert!(matches!(e.kind, AsmErrorKind::BadAddress(BadAddress::Unterminated)));
2593        }
2594    }
2595    match c.extract_address("   word [5 * ax  ", 2, 17) {
2596        Ok(_) => panic!(),
2597        Err(e) => {
2598            assert_eq!(e.pos, Some(8));
2599            assert!(matches!(e.kind, AsmErrorKind::BadAddress(BadAddress::SizeMissingPtr)));
2600        }
2601    }
2602    match c.extract_address("   wOrd[5 * ax  ", 2, 16) {
2603        Ok(_) => panic!(),
2604        Err(e) => {
2605            assert_eq!(e.pos, Some(7));
2606            assert!(matches!(e.kind, AsmErrorKind::BadAddress(BadAddress::SizeMissingPtr)));
2607        }
2608    }
2609    match c.extract_address("   WORD ptr[5 * ax  ", 2, 20) {
2610        Ok(_) => panic!(),
2611        Err(e) => {
2612            assert_eq!(e.pos, Some(20));
2613            assert!(matches!(e.kind, AsmErrorKind::BadAddress(BadAddress::Unterminated)));
2614        }
2615    }
2616    match c.extract_address("   word ptr[9 * ax]  ", 2, 21) {
2617        Ok((addr, aft)) => {
2618            assert_eq!(aft, 19);
2619            assert_eq!(addr.address_size, Size::Word);
2620            assert_eq!(addr.r1, Some((0, 3)));
2621            assert_eq!(addr.r2, Some(0));
2622            assert_eq!(addr.pointed_size, Some(Size::Word));
2623            assert!(addr.base.is_none());
2624        }
2625        Err(e) => panic!("{:?}", e),
2626    }
2627    match c.extract_address("  word pTr[   ]  ", 1, 17) {
2628        Ok(_) => panic!(),
2629        Err(e) => {
2630            match e.kind {
2631                AsmErrorKind::BadAddress(BadAddress::BadBase) => (),
2632                _ => panic!("{:?}", e.kind),
2633            }
2634            assert_eq!(e.pos, Some(10));
2635            let inner = *e.inner_err.unwrap();
2636            match inner.kind {
2637                AsmErrorKind::ExpectedExpr => (),
2638                _ => panic!("{:?}", inner.kind),
2639            }
2640            assert_eq!(inner.pos, Some(14));
2641            assert!(inner.inner_err.is_none());
2642        }
2643    }
2644    match c.extract_address("  word ptr[]  ", 1, 14) {
2645        Ok(_) => panic!(),
2646        Err(e) => {
2647            match e.kind {
2648                AsmErrorKind::BadAddress(BadAddress::BadBase) => (),
2649                _ => panic!("{:?}", e.kind),
2650            }
2651            assert_eq!(e.pos, Some(10));
2652            let inner = *e.inner_err.unwrap();
2653            match inner.kind {
2654                AsmErrorKind::ExpectedExpr => (),
2655                _ => panic!("{:?}", inner.kind),
2656            }
2657            assert_eq!(inner.pos, Some(11));
2658            assert!(inner.inner_err.is_none());
2659        }
2660    }
2661    match c.extract_address("  word ptr[a b]  ", 1, 17) {
2662        Ok(_) => panic!(),
2663        Err(e) => {
2664            assert_eq!(e.pos, Some(13));
2665            assert!(matches!(e.kind, AsmErrorKind::BadAddress(BadAddress::InteriorNotSingleExpr)));
2666        }
2667    }
2668    match c.extract_address("  ptr[45]  ", 1, 11) {
2669        Ok(_) => panic!(),
2670        Err(e) => {
2671            assert_eq!(e.pos, Some(2));
2672            assert!(matches!(e.kind, AsmErrorKind::BadAddress(BadAddress::PtrSpecWithoutSize)));
2673        }
2674    }
2675    match c.extract_address("  ptr [45]  ", 1, 12) {
2676        Ok(_) => panic!(),
2677        Err(e) => {
2678            assert_eq!(e.pos, Some(2));
2679            assert!(matches!(e.kind, AsmErrorKind::BadAddress(BadAddress::PtrSpecWithoutSize)));
2680        }
2681    }
2682    match c.extract_address("  sefsfsd[45]  ", 1, 15) {
2683        Ok(_) => panic!(),
2684        Err(e) => {
2685            assert_eq!(e.pos, Some(2));
2686            assert!(matches!(e.kind, AsmErrorKind::BadAddress(BadAddress::SizeNotRecognized)));
2687        }
2688    }
2689    match c.extract_address("  sefsfsd [45]  ", 1, 16) {
2690        Ok(_) => panic!(),
2691        Err(e) => {
2692            assert_eq!(e.pos, Some(2));
2693            match e.kind {
2694                AsmErrorKind::BadAddress(BadAddress::SizeNotRecognized) => (),
2695                k => panic!("{:?}", k),
2696            }
2697        }
2698    }
2699    match c.extract_address("  sefsfsd ptr [45]  ", 1, 20) {
2700        Ok(_) => panic!(),
2701        Err(e) => {
2702            assert_eq!(e.pos, Some(2));
2703            match e.kind {
2704                AsmErrorKind::BadAddress(BadAddress::SizeNotRecognized) => (),
2705                k => panic!("{:?}", k),
2706            }
2707        }
2708    }
2709    match c.extract_address("  sefsfsd ptr[45]  ", 1, 19) {
2710        Ok(_) => panic!(),
2711        Err(e) => {
2712            assert_eq!(e.pos, Some(2));
2713            match e.kind {
2714                AsmErrorKind::BadAddress(BadAddress::SizeNotRecognized) => (),
2715                k => panic!("{:?}", k),
2716            }
2717        }
2718    }
2719    match c.extract_address("          ", 1, 10) {
2720        Ok(_) => panic!(),
2721        Err(e) => {
2722            assert_eq!(e.pos, Some(10));
2723            assert!(matches!(e.kind, AsmErrorKind::ExpectedAddress));
2724        }
2725    }
2726    match c.extract_address("  [  byte 45]   ", 1, 16) {
2727        Ok(_) => panic!(),
2728        Err(e) => {
2729            assert_eq!(e.pos, Some(2));
2730            assert!(matches!(e.kind, AsmErrorKind::BadAddress(BadAddress::SizeUnsupported)));
2731        }
2732    }
2733    match c.extract_address("  [tword 45]   ", 1, 15) {
2734        Ok(_) => panic!(),
2735        Err(e) => {
2736            assert_eq!(e.pos, Some(2));
2737            assert!(matches!(e.kind, AsmErrorKind::BadAddress(BadAddress::SizeUnsupported)));
2738        }
2739    }
2740    match c.extract_address("  [ xwoRd 45]   ", 1, 16) {
2741        Ok(_) => panic!(),
2742        Err(e) => {
2743            assert_eq!(e.pos, Some(2));
2744            assert!(matches!(e.kind, AsmErrorKind::BadAddress(BadAddress::SizeUnsupported)));
2745        }
2746    }
2747    match c.extract_address("  [ yword 45]   ", 1, 16) {
2748        Ok(_) => panic!(),
2749        Err(e) => {
2750            assert_eq!(e.pos, Some(2));
2751            assert!(matches!(e.kind, AsmErrorKind::BadAddress(BadAddress::SizeUnsupported)));
2752        }
2753    }
2754    match c.extract_address("  [ ZWORD 45]   ", 1, 16) {
2755        Ok(_) => panic!(),
2756        Err(e) => {
2757            assert_eq!(e.pos, Some(2));
2758            assert!(matches!(e.kind, AsmErrorKind::BadAddress(BadAddress::SizeUnsupported)));
2759        }
2760    }
2761    match c.extract_address("  word ptr [al]   ", 1, 18) {
2762        Ok(_) => panic!(),
2763        Err(e) => {
2764            assert_eq!(e.pos, Some(11));
2765            assert!(matches!(e.kind, AsmErrorKind::BadAddress(BadAddress::SizeUnsupported)));
2766        }
2767    }
2768    match c.extract_address("  [ah]   ", 1, 9) {
2769        Ok(_) => panic!(),
2770        Err(e) => {
2771            assert_eq!(e.pos, Some(2));
2772            assert!(matches!(e.kind, AsmErrorKind::BadAddress(BadAddress::SizeUnsupported)));
2773        }
2774    }
2775    match c.extract_address("  [ax*bx]   ", 1, 12) {
2776        Ok(_) => panic!(),
2777        Err(e) => {
2778            assert_eq!(e.pos, Some(2));
2779            assert!(matches!(e.kind, AsmErrorKind::BadAddress(BadAddress::RegMultNotCriticalExpr(_))));
2780        }
2781    }
2782    match c.extract_address("  [ax*ax]   ", 1, 12) {
2783        Ok(_) => panic!(),
2784        Err(e) => {
2785            assert_eq!(e.pos, Some(2));
2786            assert!(matches!(e.kind, AsmErrorKind::BadAddress(BadAddress::RegMultNotCriticalExpr(_))));
2787        }
2788    }
2789    match c.extract_address("  [ax;*ax]   ", 1, 12) {
2790        Ok(_) => panic!(),
2791        Err(e) => {
2792            assert!(matches!(e.kind, AsmErrorKind::BadAddress(BadAddress::Unterminated)));
2793            assert_eq!(e.pos, Some(5));
2794        }
2795    }
2796}
2797#[test]
2798fn test_parse_arg() {
2799    let mut c = create_context();
2800    match c.extract_arg("g RaX g", 1, 7).unwrap() {
2801        (Argument::CPURegister(CPURegisterInfo { id: 0, size: Size::Qword, high: false }), 5) => (),
2802        e => panic!("wrong value: {:?}", e),
2803    }
2804    match c.extract_arg("g sT0 g", 1, 7).unwrap() {
2805        (Argument::FPURegister(FPURegisterInfo { id: 0 }), 5) => (),
2806        e => panic!("wrong value: {:?}", e),
2807    }
2808    match c.extract_arg("g XmM0 g", 1, 8).unwrap() {
2809        (Argument::VPURegister { reg: VPURegisterInfo { id: 0, size: Size::Xword }, mask: None }, 6) => (),
2810        e => panic!("wrong value: {:?}", e),
2811    }
2812    match c.extract_arg("g XmM0{k1} g", 1, 12).unwrap() {
2813        (Argument::VPURegister { reg: VPURegisterInfo { id: 0, size: Size::Xword }, mask: Some(VPUMaskType::Blend(VPUMaskRegisterInfo { id: 1 })) }, 10) => (),
2814        e => panic!("wrong value: {:?}", e),
2815    }
2816    match c.extract_arg("g XmM0{k3}{z} g", 1, 15).unwrap() {
2817        (Argument::VPURegister { reg: VPURegisterInfo { id: 0, size: Size::Xword }, mask: Some(VPUMaskType::Zero(VPUMaskRegisterInfo { id: 3 })) }, 13) => (),
2818        e => panic!("wrong value: {:?}", e),
2819    }
2820    match c.extract_arg("g  XmM0  {  k3  }  {  z  }  g", 1, 29).unwrap() {
2821        (Argument::VPURegister { reg: VPURegisterInfo { id: 0, size: Size::Xword }, mask: Some(VPUMaskType::Zero(VPUMaskRegisterInfo { id: 3 })) }, 26) => (),
2822        e => panic!("wrong value: {:?}", e),
2823    }
2824    match c.extract_arg("g [0]{k1} g", 1, 11).unwrap() {
2825        (Argument::Address { addr: Address { address_size: Size::Qword, r1: None, r2: None, pointed_size: None, base: None }, mask: Some(VPUMaskType::Blend(VPUMaskRegisterInfo { id: 1 })) }, 9) => (),
2826        e => panic!("wrong value: {:?}", e),
2827    }
2828    match c.extract_arg("g byte ptr [eax + edi]{k3}{z} g", 1, 31).unwrap() {
2829        (Argument::Address { addr: Address { address_size: Size::Dword, r1: Some(_), r2: Some(_), pointed_size: Some(Size::Byte), base: None }, mask: Some(VPUMaskType::Zero(VPUMaskRegisterInfo { id: 3 })) }, 29) => (),
2830        e => panic!("wrong value: {:?}", e),
2831    }
2832    match c.extract_arg("g  dword    ptr  [qword rdi + 0x6]  {  k3  }  {  z  }  g", 1, 56).unwrap() {
2833        (Argument::Address { addr: Address { address_size: Size::Qword, r1: _, r2: _, pointed_size: Some(Size::Dword), base: Some(_) }, mask: Some(VPUMaskType::Zero(VPUMaskRegisterInfo { id: 3 })) }, 53) => (),
2834        e => panic!("wrong value: {:?}", e),
2835    }
2836    match c.extract_arg("g  XmM0{z}  g", 1, 13) {
2837        Ok(v) => panic!("{:?}", v),
2838        Err(e) => {
2839            match e.kind {
2840                AsmErrorKind::VPUZeroingWithoutOpmask => (),
2841                k => panic!("{:?}", k),
2842            }
2843            assert_eq!(e.pos, Some(3));
2844        }
2845    }
2846    match c.extract_arg("g  XmM0{k0}  g", 1, 14) {
2847        Ok(v) => panic!("{:?}", v),
2848        Err(e) => {
2849            match e.kind {
2850                AsmErrorKind::VPUOpmaskWasK0 => (),
2851                k => panic!("{:?}", k),
2852            }
2853            assert_eq!(e.pos, Some(3));
2854        }
2855    }
2856    match c.extract_arg("g  XmM0{eax}  g", 1, 15) {
2857        Ok(v) => panic!("{:?}", v),
2858        Err(e) => {
2859            match e.kind {
2860                AsmErrorKind::ArgumentInvalidType { index: None, got: ArgumentType::CPURegister, expected: &[ArgumentType::VPUMaskRegister] } => (),
2861                k => panic!("{:?}", k),
2862            }
2863            assert_eq!(e.pos, Some(7));
2864        }
2865    }
2866    match c.extract_arg("g  XmM0  {  eax  g", 1, 18) {
2867        Ok(v) => panic!("{:?}", v),
2868        Err(e) => {
2869            match e.kind {
2870                AsmErrorKind::VPUMaskUnclosedBracket => (),
2871                k => panic!("{:?}", k),
2872            }
2873            assert_eq!(e.pos, Some(9));
2874        }
2875    }
2876    match c.extract_arg("g bYte PtR [20+rax] g", 1, 21).unwrap() {
2877        (Argument::Address { .. }, 19) => (),
2878        e => panic!("wrong value: {:?}", e),
2879    }
2880    match c.extract_arg("g foo + bar g", 1, 13).unwrap() {
2881        (Argument::Imm(Imm { .. }), 11) => (),
2882        e => panic!("wrong value: {:?}", e),
2883    }
2884    match c.extract_arg("       ", 1, 7) {
2885        Ok(x) => panic!("{:?}", x),
2886        Err(e) => {
2887            assert_eq!(e.pos, Some(7));
2888            assert!(matches!(e.kind, AsmErrorKind::ExpectedExpr));
2889        }
2890    }
2891    match c.extract_arg("  \"[]\"  ", 1, 8) {
2892        Ok((x, aft)) => {
2893            assert_eq!(aft, 6);
2894            match x {
2895                Argument::Imm(imm) => {
2896                    assert_eq!(imm.size, None);
2897                    match &*imm.expr.eval(&c.file.symbols).unwrap() {
2898                        Value::Binary(bin) => assert_eq!(bin, "[]".as_bytes()),
2899                        x => panic!("{:?}", x),
2900                    }
2901                }
2902                _ => panic!("{:?}", x),
2903            }
2904        }
2905        Err(e) => panic!("{:?}", e),
2906    }
2907    match c.extract_arg("  dword \"[]\"  ", 1, 14) {
2908        Ok((x, aft)) => {
2909            assert_eq!(aft, 12);
2910            match x {
2911                Argument::Imm(imm) => {
2912                    assert_eq!(imm.size, Some(Size::Dword));
2913                    match &*imm.expr.eval(&c.file.symbols).unwrap() {
2914                        Value::Binary(bin) => assert_eq!(bin, "[]".as_bytes()),
2915                        x => panic!("{:?}", x),
2916                    }
2917                }
2918                _ => panic!("{:?}", x),
2919            }
2920        }
2921        Err(e) => panic!("{:?}", e),
2922    }
2923}
2924#[test]
2925fn test_tilde_parse() {
2926    let mut c = create_context();
2927    match c.extract_arg("  !32  ", 1, 7) {
2928        Ok((x, aft)) => {
2929            assert_eq!(aft, 5);
2930            match x {
2931                Argument::Imm(imm) => {
2932                    assert_eq!(imm.size, None);
2933                    match &*imm.expr.eval(&c.file.symbols).unwrap() {
2934                        Value::Integer(v) => assert_eq!(*v, !32),
2935                        x => panic!("{:?}", x),
2936                    }
2937                }
2938                _ => panic!("{:?}", x),
2939            }
2940        }
2941        Err(e) => panic!("{:?}", e),
2942    }
2943    match c.extract_arg("  ~32  ", 1, 7) {
2944        Ok(_) => panic!(),
2945        Err(e) => {
2946            assert_eq!(e.pos, Some(2));
2947            assert!(e.inner_err.is_none());
2948            match e.kind {
2949                AsmErrorKind::UseOfTildeNot => (),
2950                x => panic!("{:?}", x),
2951            }
2952        }
2953    }
2954    match c.extract_arg("      +  --~-32  ", 3, 17) {
2955        Ok(_) => panic!(),
2956        Err(e) => {
2957            assert_eq!(e.pos, Some(11));
2958            assert!(e.inner_err.is_none());
2959            match e.kind {
2960                AsmErrorKind::UseOfTildeNot => (),
2961                x => panic!("{:?}", x),
2962            }
2963        }
2964    }
2965}
2966#[test]
2967fn test_extract_header() {
2968    let mut c = create_context();
2969    
2970    assert_eq!(c.extract_header("").unwrap(), (None, 0));
2971    assert_eq!(c.label_def, None);
2972    assert_eq!(c.times, None);
2973
2974    assert_eq!(c.extract_header(" \t   ").unwrap(), (None, 0));
2975    assert_eq!(c.label_def, None);
2976    assert_eq!(c.times, None);
2977
2978    assert_eq!(c.extract_header(" \t  ; this is a comment ").unwrap(), (None, 0));
2979    assert_eq!(c.label_def, None);
2980    assert_eq!(c.times, None);
2981
2982    assert_eq!(c.extract_header("  label:    ; this is a comment ").unwrap(), (None, 8));
2983    assert_eq!(c.label_def.as_ref().unwrap().0, "label");
2984    assert_eq!(c.label_def.as_ref().unwrap().1, Locality::Nonlocal);
2985    assert_eq!(c.times, None);
2986
2987    match c.extract_header("  label%^:    ; this is a comment ") {
2988        Ok(_) => panic!(),
2989        Err(e) => {
2990            assert_eq!(e.pos, Some(2));
2991            assert!(matches!(e.kind, AsmErrorKind::InvalidSymbolName));
2992        }
2993    }
2994
2995    match c.extract_header("  .top:    ; this is a comment ") {
2996        Ok(_) => panic!(),
2997        Err(e) => {
2998            assert_eq!(e.pos, Some(2));
2999            assert!(matches!(e.kind, AsmErrorKind::LocalSymbolBeforeNonlocal));
3000        }
3001    }
3002    c.last_nonlocal_label = Some("thingy".into());
3003    assert_eq!(c.extract_header("  .top:    ; this is a comment ").unwrap(), (None, 7));
3004    assert_eq!(c.label_def.as_ref().unwrap().0, "thingy.top");
3005    assert_eq!(c.label_def.as_ref().unwrap().1, Locality::Local);
3006    assert_eq!(c.times, None);
3007
3008    assert_eq!(c.extract_header("  times 45 nop  ; this is a comment ").unwrap(), (Some((None, (Instruction::NoArg { op: Some(OPCode::NOP as u8), ext_op: None }, 11))), 14));
3009    assert_eq!(c.label_def, None);
3010    assert_eq!(c.times, Some(TimesInfo { total_count: 45, current: 0 }));
3011
3012    match c.extract_header("  times 45     ; this is a comment ") {
3013        Ok(_) => panic!(),
3014        Err(e) => {
3015            assert_eq!(e.pos, Some(2));
3016            assert!(matches!(e.kind, AsmErrorKind::TimesUsedOnEmptyLine));
3017        }
3018    }
3019    match c.extract_header("  times abc     ; this is a comment ") {
3020        Ok(_) => panic!(),
3021        Err(e) => {
3022            assert_eq!(e.pos, Some(2));
3023            assert!(matches!(e.kind, AsmErrorKind::FailedCriticalExpression(_)));
3024        }
3025    }
3026    match c.extract_header("  times -45     ; this is a comment ") {
3027        Ok(_) => panic!(),
3028        Err(e) => {
3029            assert_eq!(e.pos, Some(2));
3030            assert!(matches!(e.kind, AsmErrorKind::TimesCountWasNegative));
3031        }
3032    }
3033
3034    match c.extract_header("  times 5 UNKNOWN_COMMAND    ; this is a comment ") {
3035        Ok(_) => panic!(),
3036        Err(e) => {
3037            assert_eq!(e.pos, Some(10));
3038            assert!(matches!(e.kind, AsmErrorKind::UnrecognizedInstruction));
3039        }
3040    }
3041    match c.extract_header("   UNKNOWN_COMMAND    ; this is a comment ") {
3042        Ok(_) => panic!(),
3043        Err(e) => {
3044            assert_eq!(e.pos, Some(3));
3045            assert!(matches!(e.kind, AsmErrorKind::UnrecognizedInstruction));
3046        }
3047    }
3048
3049    assert_eq!(c.extract_header("  label: tiMEs 5 NoP  ; this is a comment ").unwrap(), (Some((None, (Instruction::NoArg { op: Some(OPCode::NOP as u8), ext_op: None }, 17))), 20));
3050    assert_eq!(c.label_def.as_ref().unwrap().0, "label");
3051    assert_eq!(c.label_def.as_ref().unwrap().1, Locality::Nonlocal);
3052    assert_eq!(c.times, Some(TimesInfo { total_count: 5, current: 0 }));
3053
3054    assert_eq!(c.extract_header("  label: tiMEs 0 NoP  ; this is a comment ").unwrap(), (Some((None, (Instruction::NoArg { op: Some(OPCode::NOP as u8), ext_op: None }, 17))), 20));
3055    assert_eq!(c.label_def.as_ref().unwrap().0, "label");
3056    assert_eq!(c.label_def.as_ref().unwrap().1, Locality::Nonlocal);
3057    assert_eq!(c.times, Some(TimesInfo { total_count: 0, current: 0 }));
3058
3059    assert_eq!(c.extract_header("  merp: NoP  ; this is a comment ").unwrap(), (Some((None, (Instruction::NoArg { op: Some(OPCode::NOP as u8), ext_op: None }, 8))), 11));
3060    assert_eq!(c.label_def.as_ref().unwrap().0, "merp");
3061    assert_eq!(c.label_def.as_ref().unwrap().1, Locality::Nonlocal);
3062    assert_eq!(c.times, None);
3063
3064    assert_eq!(c.extract_header("  NoP  ; this is a comment ").unwrap(), (Some((None, (Instruction::NoArg { op: Some(OPCode::NOP as u8), ext_op: None }, 2))), 5));
3065    assert_eq!(c.label_def, None);
3066    assert_eq!(c.times, None);
3067
3068    assert_eq!(c.extract_header("  NoP; this is a comment ").unwrap(), (Some((None, (Instruction::NoArg { op: Some(OPCode::NOP as u8), ext_op: None }, 2))), 5));
3069    assert_eq!(c.label_def, None);
3070    assert_eq!(c.times, None);
3071
3072    assert_eq!(c.extract_header(" lab: if true nop ' ; this is a comment ").unwrap(), (Some((None, (Instruction::NoArg { op: Some(OPCode::NOP as u8), ext_op: None }, 14))), 17));
3073    assert_eq!(c.label_def.as_ref().unwrap().0, "lab");
3074    assert_eq!(c.label_def.as_ref().unwrap().1, Locality::Nonlocal);
3075    assert_eq!(c.times, Some(TimesInfo { total_count: 1, current: 0 }));
3076
3077    assert_eq!(c.extract_header(" .lab2: if false nop ' ; this is a comment ").unwrap(), (Some((None, (Instruction::NoArg { op: Some(OPCode::NOP as u8), ext_op: None }, 17))), 20));
3078    assert_eq!(c.label_def.as_ref().unwrap().0, "thingy.lab2");
3079    assert_eq!(c.label_def.as_ref().unwrap().1, Locality::Local);
3080    assert_eq!(c.times, Some(TimesInfo { total_count: 0, current: 0 }));
3081
3082    match c.extract_header("   rax:    ; this is a comment ") {
3083        Ok(_) => panic!(),
3084        Err(e) => {
3085            assert_eq!(e.pos, Some(3));
3086            assert!(matches!(e.kind, AsmErrorKind::ReservedSymbolName));
3087        }
3088    }
3089
3090    assert_eq!(c.extract_header("  reP NoP  ").unwrap(), (Some((Some((Prefix::REP, 2)), (Instruction::NoArg { op: Some(OPCode::NOP as u8), ext_op: None }, 6))), 9));
3091    assert_eq!(c.label_def, None);
3092    assert_eq!(c.times, None);
3093
3094    match c.extract_header("  reP  ") {
3095        Ok(_) => panic!(),
3096        Err(e) => {
3097            assert_eq!(e.pos, Some(2));
3098            assert!(matches!(e.kind, AsmErrorKind::PrefixWithoutInstruction));
3099        }
3100    }
3101}
3102#[test]
3103fn test_unexpected_string() {
3104    let mut c = create_context();
3105    match c.extract_arg("  37'hello'  ", 1, 13) {
3106        Ok((x, aft)) => {
3107            assert_eq!(aft, 4);
3108            match x {
3109                Argument::Imm(imm) => {
3110                    assert_eq!(imm.size, None);
3111                    match &*imm.expr.eval(&c.file.symbols).unwrap() {
3112                        Value::Integer(v) => assert_eq!(*v, 37),
3113                        x => panic!("{:?}", x),
3114                    }
3115                }
3116                _ => panic!("{:?}", x),
3117            }
3118        }
3119        Err(e) => panic!("{:?}", e),
3120    }
3121    match c.extract_arg("  (37'hello')  ", 1, 15) {
3122        Ok((x, _)) => panic!("{:?}", x),
3123        Err(e) => {
3124            match e.kind {
3125                AsmErrorKind::ParenInteriorNotExpr => (),
3126                k => panic!("{:?}", k),
3127            }
3128            assert_eq!(e.pos, Some(2));
3129            assert!(e.inner_err.is_none());
3130        }
3131    }
3132    match c.extract_arg("  (37'hello'  ", 1, 14) {
3133        Ok((x, _)) => panic!("{:?}", x),
3134        Err(e) => {
3135            match e.kind {
3136                AsmErrorKind::UnclosedParen => (),
3137                k => panic!("{:?}", k),
3138            }
3139            assert_eq!(e.pos, Some(2));
3140            assert!(e.inner_err.is_none());
3141        }
3142    }
3143    match c.extract_arg("  (37'hello)  ", 1, 14) {
3144        Ok((x, _)) => panic!("{:?}", x),
3145        Err(e) => {
3146            match e.kind {
3147                AsmErrorKind::IncompleteString => (),
3148                k => panic!("{:?}", k),
3149            }
3150            assert_eq!(e.pos, Some(5));
3151            assert!(e.inner_err.is_none());
3152        }
3153    }
3154    match c.extract_arg("  (37'hello  ", 1, 13) {
3155        Ok((x, _)) => panic!("{:?}", x),
3156        Err(e) => {
3157            match e.kind {
3158                AsmErrorKind::IncompleteString => (),
3159                k => panic!("{:?}", k),
3160            }
3161            assert_eq!(e.pos, Some(5));
3162            assert!(e.inner_err.is_none());
3163        }
3164    }
3165}
3166#[test]
3167fn test_unexpected_open_paren() {
3168    let mut c = create_context();
3169    match c.extract_arg("  37(hello'  ", 1, 13) {
3170        Ok((x, aft)) => {
3171            assert_eq!(aft, 4);
3172            match x {
3173                Argument::Imm(imm) => {
3174                    assert_eq!(imm.size, None);
3175                    match &*imm.expr.eval(&c.file.symbols).unwrap() {
3176                        Value::Integer(v) => assert_eq!(*v, 37),
3177                        x => panic!("{:?}", x),
3178                    }
3179                }
3180                _ => panic!("{:?}", x),
3181            }
3182        }
3183        Err(e) => panic!("{:?}", e),
3184    }
3185    match c.extract_arg("  (37(hello))  ", 1, 15) {
3186        Ok((x, _)) => panic!("{:?}", x),
3187        Err(e) => {
3188            match e.kind {
3189                AsmErrorKind::ParenInteriorNotExpr => (),
3190                k => panic!("{:?}", k),
3191            }
3192            assert_eq!(e.pos, Some(2));
3193            assert!(e.inner_err.is_none());
3194        }
3195    }
3196    match c.extract_arg("  (37(hello)  ", 1, 14) {
3197        Ok((x, _)) => panic!("{:?}", x),
3198        Err(e) => {
3199            match e.kind {
3200                AsmErrorKind::UnclosedParen => (),
3201                k => panic!("{:?}", k),
3202            }
3203            assert_eq!(e.pos, Some(2));
3204            assert!(e.inner_err.is_none());
3205        }
3206    }
3207}