riscv_instructions/
ast.rs

1// SPDX-License-Identifier: LGPL-2.1-or-later
2// See Notices.txt for copyright information
3
4use once_cell::unsync::OnceCell;
5use peg::{str::LineCol, Parse};
6use std::{
7    borrow::Cow,
8    error::Error,
9    fmt, mem,
10    num::NonZeroUsize,
11    ops::{Range, RangeInclusive},
12    str::FromStr,
13};
14use tex_parser::ast::{self, input_context::InputContext, GetPos};
15
16#[derive(Debug)]
17pub struct ParseError {
18    pub file_name: String,
19    pub line: usize,
20    pub column: usize,
21    pub byte_index: usize,
22    pub message: String,
23}
24
25impl fmt::Display for ParseError {
26    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
27        write!(
28            f,
29            "{}:{}:{}: error: {}",
30            self.file_name, self.line, self.column, self.message
31        )
32    }
33}
34
35impl Error for ParseError {}
36
37pub type Result<T, E = ParseError> = std::result::Result<T, E>;
38
39struct Parser<'a> {
40    file_name: &'a str,
41    input: &'a str,
42}
43
44macro_rules! err {
45    ($this:expr, $pos:expr, $($fmt_args:tt)+) => {
46        return Err($this.err_pos($pos, format!($($fmt_args)+)))
47    };
48}
49
50macro_rules! err_if {
51    ($cond:expr, $this:expr, $pos:expr, $($fmt_args:tt)+) => {
52        if $cond {
53            err!($this, $pos, $($fmt_args)+);
54        }
55    };
56}
57
58macro_rules! unwrap_or {
59    ($opt:expr, $($none:tt)+) => {
60        match $opt {
61            Some(v) => v,
62            None => { $($none)+ }
63        }
64    };
65}
66
67struct TableRowIterator<'input, 'rest> {
68    parser: &'rest Parser<'input>,
69    tabular_body: std::slice::Iter<'rest, ast::Token>,
70    pos_after_body: ast::Pos,
71    column_definitions: &'rest [ColumnDefinition],
72    column_ranges: Vec<ColumnRange>,
73    column_range: Option<ColumnRange>,
74    finished: bool,
75}
76
77impl GetPos for TableRowIterator<'_, '_> {
78    fn pos(&self) -> ast::Pos {
79        self.tabular_body
80            .clone()
81            .next()
82            .map_or(self.pos_after_body, GetPos::pos)
83    }
84}
85
86impl<'input, 'rest> TableRowIterator<'input, 'rest> {
87    fn new(
88        parser: &'rest Parser<'input>,
89        tabular_body: std::slice::Iter<'rest, ast::Token>,
90        pos_after_body: impl GetPos,
91        column_definitions: &'rest [ColumnDefinition],
92    ) -> Self {
93        Self {
94            parser,
95            tabular_body,
96            pos_after_body: pos_after_body.pos(),
97            column_definitions,
98            column_ranges: Vec::new(),
99            column_range: None,
100            finished: false,
101        }
102    }
103    fn column_end_index(&self) -> usize {
104        self.column_range
105            .as_ref()
106            .or(self.column_ranges.last())
107            .map_or(0, |c| c.indexes.end)
108    }
109    fn columns_left(&self) -> usize {
110        self.column_definitions.len() - self.column_end_index()
111    }
112    fn is_field_column(&self) -> bool {
113        self.columns_left() > 1
114    }
115    fn is_opcode_column(&self) -> bool {
116        self.columns_left() == 1
117    }
118    fn get_new_column_indexes(
119        &mut self,
120        new_column_width: usize,
121        pos: impl GetPos,
122        reason: impl FnOnce() -> String,
123    ) -> Result<Range<usize>> {
124        let start_index = self.column_end_index();
125        let cols_left = self.columns_left();
126        err_if!(
127            new_column_width > cols_left,
128            self.parser,
129            pos,
130            "{reason}: {cols_left} column(s) available out of {total} total",
131            reason = reason(),
132            cols_left = cols_left,
133            total = self.column_definitions.len()
134        );
135        let end_index = start_index + new_column_width;
136        Ok(start_index..end_index)
137    }
138    fn next_helper(&mut self) -> Result<Option<Vec<ColumnRange>>> {
139        if self.finished {
140            return Ok(None);
141        }
142        let mut columns = loop {
143            let mut tabular_body_temp = self.tabular_body.clone();
144            let token = unwrap_or!(tabular_body_temp.next(), {
145                self.column_ranges.extend(self.column_range.take());
146                self.finished = true;
147                if self.column_ranges.is_empty() {
148                    return Ok(None);
149                }
150                break mem::replace(&mut self.column_ranges, Vec::new());
151            });
152            match token {
153                ast::Token::AlignmentTab(alignment_tab) => {
154                    self.tabular_body = tabular_body_temp;
155                    if let Some(column_range) = self.column_range.take() {
156                        self.column_ranges.push(column_range);
157                    } else {
158                        let indexes = self.get_new_column_indexes(1, alignment_tab, || {
159                            "this column would go past the table column count".into()
160                        })?;
161                        self.column_ranges.push(ColumnRange {
162                            pos: alignment_tab.pos(),
163                            indexes,
164                            multi_column: None,
165                            body: None,
166                        })
167                    }
168                }
169                ast::Token::Macro(macro_) if macro_.name.content == "\\" => {
170                    self.tabular_body = tabular_body_temp;
171                    self.column_ranges.extend(self.column_range.take());
172                    break mem::replace(&mut self.column_ranges, Vec::new());
173                }
174                ast::Token::Macro(m) if m.name.content == "multicolumn" => {
175                    self.tabular_body = tabular_body_temp;
176                    err_if!(
177                        self.column_range.is_some(),
178                        self.parser,
179                        m,
180                        "`\\multicolumn` must be first thing in its column"
181                    );
182                    let (num_cols, num_cols_pos): (NonZeroUsize, _) =
183                        self.parser.parse_number_in_group(
184                            self.pos_after_body,
185                            self.tabular_body.next().map(Cow::Borrowed),
186                            || "expected `{{<number>}}` (like `{{12}}`)".into(),
187                        )?;
188                    let indexes =
189                        self.get_new_column_indexes(num_cols.get(), num_cols_pos, || {
190                            format!(
191                                "`\\multicolumn{{{}}}` would go past the table column count",
192                                num_cols
193                            )
194                        })?;
195                    let cols_group = self.parser.unwrap_group_or_error(
196                        self.pos_after_body,
197                        self.tabular_body.next().map(Cow::Borrowed),
198                    )?;
199                    let mut multi_column = MultiColumn {
200                        pos: cols_group.tokens_pos(),
201                        kind: MultiColumnKind::Center,
202                        vertical_bar_on_left: false,
203                        vertical_bar_on_right: false,
204                    };
205                    let mut cols_group_str = match &*cols_group.tokens {
206                        [ast::Token::CharTokens(char_tokens)] => &*char_tokens.content,
207                        _ => err!(
208                            self.parser,
209                            cols_group.tokens_pos(),
210                            "invalid `\\multicolumn` cols group"
211                        ),
212                    };
213                    if let Some(rest) = cols_group_str.strip_prefix("|") {
214                        cols_group_str = rest;
215                        multi_column.vertical_bar_on_left = true;
216                    }
217                    if let Some(rest) = cols_group_str.strip_suffix("|") {
218                        cols_group_str = rest;
219                        multi_column.vertical_bar_on_right = true;
220                    }
221                    multi_column.kind = unwrap_or!(
222                        MultiColumnKind::from_str(cols_group_str),
223                        err!(
224                            self.parser,
225                            cols_group.tokens_pos(),
226                            "invalid `\\multicolumn` cols group"
227                        )
228                    );
229                    let body = self.parser.unwrap_group_or_error(
230                        self.pos_after_body,
231                        self.tabular_body.next().map(Cow::Borrowed),
232                    )?;
233                    self.parser
234                        .skip_rest_of_column_body_else_error(&mut self.tabular_body, true)?;
235                    let mut body_iter = body.tokens.iter();
236                    let body = self.parser.parse_column_body(
237                        &mut body_iter,
238                        &body.end,
239                        self.column_definitions,
240                        false,
241                        self.is_field_column(),
242                        self.is_opcode_column(),
243                    )?;
244                    self.parser
245                        .skip_rest_of_column_body_else_error(&mut body_iter, false)?;
246                    self.column_range = Some(ColumnRange {
247                        pos: m.pos(),
248                        indexes,
249                        multi_column: Some(multi_column),
250                        body,
251                    });
252                }
253                token if Parser::is_column_body_ignored(token) => {
254                    self.tabular_body = tabular_body_temp;
255                }
256                token => {
257                    assert!(self.column_range.is_none(), "token={:?}", token);
258                    let is_field_column = self.is_field_column();
259                    let is_opcode_column = self.is_opcode_column();
260                    let body = self.parser.parse_column_body(
261                        &mut self.tabular_body,
262                        self.pos_after_body,
263                        self.column_definitions,
264                        true,
265                        is_field_column,
266                        is_opcode_column,
267                    )?;
268                    if body.is_none() {
269                        continue;
270                    }
271                    let indexes = self.get_new_column_indexes(1, token, || {
272                        "this column would go past the table column count".into()
273                    })?;
274                    self.parser
275                        .skip_rest_of_column_body_else_error(&mut self.tabular_body, true)?;
276                    self.column_range = Some(ColumnRange {
277                        pos: token.pos(),
278                        indexes,
279                        multi_column: None,
280                        body: body,
281                    });
282                }
283            }
284        };
285        columns.reverse();
286        for column in &mut columns {
287            let rev_start = self.column_definitions.len() - column.indexes.end;
288            let rev_end = self.column_definitions.len() - column.indexes.start;
289            column.indexes = rev_start..rev_end;
290        }
291        Ok(Some(columns))
292    }
293    fn peekable(self) -> PeekableTableRowIterator<'input, 'rest> {
294        PeekableTableRowIterator {
295            iter: self,
296            peek_value: None,
297        }
298    }
299}
300
301impl<'input, 'rest> Iterator for TableRowIterator<'input, 'rest> {
302    type Item = Result<Vec<ColumnRange>>;
303
304    fn next(&mut self) -> Option<Self::Item> {
305        let retval = self.next_helper();
306        if retval.is_err() {
307            self.finished = true;
308        }
309        retval.transpose()
310    }
311}
312
313struct PeekableTableRowIterator<'input, 'rest> {
314    iter: TableRowIterator<'input, 'rest>,
315    peek_value: Option<Vec<ColumnRange>>,
316}
317
318impl GetPos for PeekableTableRowIterator<'_, '_> {
319    fn pos(&self) -> ast::Pos {
320        self.peek_value
321            .as_ref()
322            .and_then(|v| v.first())
323            .map_or_else(|| self.iter.pos(), |v| v.pos)
324    }
325}
326
327impl<'input, 'rest> PeekableTableRowIterator<'input, 'rest> {
328    fn peek(&mut self) -> Option<Result<&[ColumnRange]>> {
329        if self.peek_value.is_none() {
330            match self.iter.next()? {
331                Ok(value) => {
332                    self.peek_value = Some(value);
333                }
334                Err(e) => return Some(Err(e)),
335            }
336        }
337        Some(Ok(self.peek_value.as_ref().unwrap()))
338    }
339}
340
341impl<'input, 'rest> Iterator for PeekableTableRowIterator<'input, 'rest> {
342    type Item = Result<Vec<ColumnRange>>;
343
344    fn next(&mut self) -> Option<Self::Item> {
345        if let Some(peek_value) = self.peek_value.take() {
346            return Some(Ok(peek_value));
347        }
348        self.iter.next()
349    }
350}
351
352impl<'input> Parser<'input> {
353    const COLUMN_START_PADDING: usize = 1;
354    const COLUMN_END_PADDING: usize = 1;
355    fn new(file_name: &'input str, input: &'input str) -> Self {
356        Self { file_name, input }
357    }
358    fn err_line_col(&self, line_col: LineCol, message: String) -> ParseError {
359        ParseError {
360            file_name: self.file_name.into(),
361            line: line_col.line,
362            column: line_col.column,
363            byte_index: line_col.offset,
364            message,
365        }
366    }
367    fn err_pos(&self, pos: impl GetPos, message: String) -> ParseError {
368        self.err_line_col(self.input.position_repr(pos.pos().byte_index), message)
369    }
370    fn expect_environment<'env>(
371        &self,
372        environment: &'env ast::Environment,
373        name: &str,
374    ) -> Result<&'env ast::Environment> {
375        if environment.name.content == name {
376            Ok(environment)
377        } else {
378            err!(self, environment, "expected `\\begin{{{}}}`", name);
379        }
380    }
381    fn unwrap_group_or_error<'t>(
382        &self,
383        pos_if_token_is_none: impl GetPos,
384        token: Option<Cow<'t, ast::Token>>,
385    ) -> Result<Cow<'t, ast::Group>> {
386        let token_pos = token.as_ref().map(|t| t.pos());
387        let group = token.and_then(|t| match t {
388            Cow::Borrowed(t) => t.group().map(Cow::Borrowed),
389            Cow::Owned(t) => t.into_group().map(Cow::Owned),
390        });
391        Ok(unwrap_or!(
392            group,
393            err!(
394                self,
395                token_pos.unwrap_or(pos_if_token_is_none.pos()),
396                "expected `{{`"
397            )
398        ))
399    }
400    fn parse_number_in_group<T: FromStr>(
401        &self,
402        pos_if_token_is_none: impl GetPos,
403        token: Option<Cow<'_, ast::Token>>,
404        error_message: impl FnOnce() -> String,
405    ) -> Result<(T, ast::Pos)>
406    where
407        T::Err: fmt::Display,
408    {
409        let num_cols_group = self.unwrap_group_or_error(pos_if_token_is_none, token)?;
410        match &*num_cols_group.tokens {
411            [ast::Token::Number(num_cols)] => Ok((
412                num_cols.parse_with_err_arg(self, Parser::err_pos)?,
413                num_cols.pos,
414            )),
415            _ => Err(self.err_pos(&*num_cols_group, error_message())),
416        }
417    }
418    fn parse_tabular_column_definitions(
419        &self,
420        columns_group: &ast::Group,
421    ) -> Result<Vec<ColumnDefinition>> {
422        let mut tokens = ast::SplitCharTokensIter::new(&columns_group.tokens).peekable();
423        let mut retval = Vec::new();
424        loop {
425            let kind = match unwrap_or!(tokens.next().as_deref(), break) {
426                ast::Token::CharTokens(c) if c.content == "l" => {
427                    ColumnKind::LeftJustified { keyword_pos: c.pos }
428                }
429                ast::Token::CharTokens(c) if c.content == "p" => {
430                    let width = self
431                        .unwrap_group_or_error(&columns_group.end, tokens.next())?
432                        .into_owned();
433                    ColumnKind::ParagraphTop {
434                        keyword_pos: c.pos,
435                        width,
436                    }
437                }
438                token => err!(self, token, "expected `tabular` column definition"),
439            };
440            retval.push(ColumnDefinition {
441                kind,
442                merged_def: OnceCell::new(),
443            });
444        }
445        err_if!(
446            retval.len() < 2,
447            self,
448            columns_group,
449            "there must be at least 2 columns"
450        );
451        retval.reverse();
452        Ok(retval)
453    }
454    fn skip_rest_if_matches_else_error<'a, I: Iterator<Item = &'a ast::Token> + Clone>(
455        &self,
456        tokens: &mut I,
457        mut matches: impl FnMut(&'a ast::Token) -> bool,
458        mut is_terminator: impl FnMut(&'a ast::Token) -> bool,
459        error_message: impl FnOnce(&'a ast::Token) -> String,
460    ) -> Result<()> {
461        loop {
462            let mut temp_tokens = tokens.clone();
463            let token = unwrap_or!(temp_tokens.next(), break Ok(()));
464            if is_terminator(token) {
465                break Ok(());
466            } else if !matches(token) {
467                return Err(self.err_pos(token, error_message(token)));
468            }
469            *tokens = temp_tokens;
470        }
471    }
472    fn is_column_body_ignored(token: &ast::Token) -> bool {
473        match token {
474            ast::Token::Whitespace(_) | ast::Token::ParBreak(_) => true,
475            _ => false,
476        }
477    }
478    fn is_column_body_terminator(token: &ast::Token, is_top_level: bool) -> bool {
479        match token {
480            ast::Token::AlignmentTab(_) => is_top_level,
481            ast::Token::Macro(m) if m.name.content == "\\" => is_top_level,
482            _ => false,
483        }
484    }
485    fn is_column_body_text(token: &ast::Token) -> bool {
486        match token {
487            ast::Token::CharTokens(_)
488            | ast::Token::Group(_)
489            | ast::Token::Whitespace(_)
490            | ast::Token::Number(_) => true,
491            ast::Token::Punctuation(ast::Punctuation { pos: _, ch }) if matches!(ch, '-' | '.') => {
492                true
493            }
494            _ => false,
495        }
496    }
497    fn is_column_body_bold_text(token: &ast::Token) -> bool {
498        match token {
499            ast::Token::CharTokens(_) => true,
500            ast::Token::Punctuation(_) => true,
501            ast::Token::Whitespace(_) => true,
502            ast::Token::Macro(_) => true,
503            ast::Token::Group(_) => true,
504            _ => false,
505        }
506    }
507    fn skip_rest_of_column_body_else_error<'a>(
508        &self,
509        tokens: &mut std::slice::Iter<ast::Token>,
510        is_top_level: bool,
511    ) -> Result<()> {
512        self.skip_rest_if_matches_else_error(
513            tokens,
514            Self::is_column_body_ignored,
515            |token| Self::is_column_body_terminator(token, is_top_level),
516            |token| format!("unexpected token: {:?}", token),
517        )
518    }
519    fn is_register_macro(macro_: &ast::Macro) -> bool {
520        matches!(
521            &*macro_.name.content,
522            "rdprime" | "rsoneprime" | "rstwoprime"
523        )
524    }
525    fn parse_field_def_name(
526        &self,
527        first_token: Option<&ast::Token>,
528        tabular_body: &mut std::slice::Iter<ast::Token>,
529        pos_after_body: impl GetPos,
530    ) -> Result<FieldDefName> {
531        let token = unwrap_or!(
532            first_token.or_else(|| tabular_body.next()),
533            err!(self, pos_after_body, "expected field name")
534        );
535        match token {
536            ast::Token::Macro(macro_) if Self::is_register_macro(macro_) => {
537                Ok(FieldDefName::Macro(macro_.name.clone()))
538            }
539            ast::Token::CharTokens(char_tokens) => {
540                Ok(FieldDefName::CharTokens(char_tokens.clone()))
541            }
542            _ => err!(self, token, "expected field name"),
543        }
544    }
545    fn match_token_or_error<'a, T: GetPos>(
546        &self,
547        matches: impl FnOnce(&T) -> bool,
548        tabular_body: &mut std::slice::Iter<'a, T>,
549        pos_after_body: impl GetPos,
550        error_message: impl FnOnce() -> String,
551    ) -> Result<&'a T> {
552        let pos = match tabular_body.next() {
553            Some(token) if matches(token) => return Ok(token),
554            Some(token) => token.pos(),
555            None => pos_after_body.pos(),
556        };
557        Err(self.err_pos(pos, error_message()))
558    }
559    fn parse_field_def_slice(
560        &self,
561        name: FieldDefName,
562        tabular_body: &mut std::slice::Iter<ast::Token>,
563        pos_after_body: impl GetPos,
564    ) -> Result<FieldDefSlice> {
565        self.match_token_or_error(
566            |t| {
567                matches!(
568                    t,
569                    ast::Token::Punctuation(ast::Punctuation { pos: _, ch: '[' })
570                )
571            },
572            tabular_body,
573            &pos_after_body,
574            || "expected `[`".into(),
575        )?;
576        let mut bit_ranges = Vec::new();
577        loop {
578            let (end, end_bit_pos): (u32, _) = match tabular_body.next() {
579                Some(ast::Token::Number(num)) => {
580                    (num.parse_with_err_arg(self, Self::err_pos)?, num.pos())
581                }
582                token => err!(
583                    self,
584                    token.map_or(pos_after_body.pos(), GetPos::pos),
585                    "expected number"
586                ),
587            };
588            if let Some(ast::Token::Punctuation(ast::Punctuation { pos: _, ch: ':' })) =
589                tabular_body.clone().next()
590            {
591                tabular_body.next();
592                let (start, start_bit_pos): (u32, _) = match tabular_body.next() {
593                    Some(ast::Token::Number(num)) => {
594                        (num.parse_with_err_arg(self, Self::err_pos)?, num.pos())
595                    }
596                    token => err!(
597                        self,
598                        token.map_or(pos_after_body.pos(), GetPos::pos),
599                        "expected number"
600                    ),
601                };
602                err_if!(
603                    end < start,
604                    self,
605                    end_bit_pos,
606                    "range end ({}) must not be less than range start ({})",
607                    end,
608                    start
609                );
610                bit_ranges.push(FieldBitRange {
611                    bits: start..=end,
612                    end_bit_pos,
613                    start_bit_pos: Some(start_bit_pos),
614                });
615            } else {
616                bit_ranges.push(FieldBitRange {
617                    bits: end..=end,
618                    end_bit_pos,
619                    start_bit_pos: None,
620                });
621            }
622            match unwrap_or!(
623                tabular_body.next(),
624                err!(self, pos_after_body, "missing `]`")
625            ) {
626                ast::Token::Punctuation(ast::Punctuation { pos: _, ch: ']' }) => {
627                    break;
628                }
629                ast::Token::DollarInlineMath(ast::DollarInlineMath {
630                    begin,
631                    content,
632                    end: _,
633                }) => match &**content {
634                    [ast::MathToken::Macro(ast::Macro { escape: _, name })]
635                        if name.content == "vert" =>
636                    {
637                        continue;
638                    }
639                    _ => err!(self, begin, "expected `$\\vert$`"),
640                },
641                token => err!(self, token, "expected `$\\vert$` or `]`"),
642            }
643        }
644        Ok(FieldDefSlice { name, bit_ranges })
645    }
646    fn parse_field_def_alternate(
647        &self,
648        field_def_name: FieldDefName,
649        tabular_body: &mut std::slice::Iter<ast::Token>,
650        pos_after_body: impl GetPos,
651    ) -> Result<FieldDefAlternate> {
652        let mut names = vec![field_def_name];
653        while let Some(ast::Token::Punctuation(ast::Punctuation { pos: _, ch: '/' })) =
654            tabular_body.clone().next()
655        {
656            tabular_body.next();
657            names.push(self.parse_field_def_name(None, tabular_body, &pos_after_body)?);
658        }
659        Ok(FieldDefAlternate { names })
660    }
661    fn parse_field_def(
662        &self,
663        first_token: &ast::Token,
664        tabular_body: &mut std::slice::Iter<ast::Token>,
665        pos_after_body: impl GetPos,
666    ) -> Result<FieldDef> {
667        match first_token {
668            ast::Token::Number(num) => {
669                return Ok(FieldDef {
670                    body: FieldDefBody::LiteralNumber(num.clone()),
671                    excluded_values: Vec::new(),
672                });
673            }
674            ast::Token::Punctuation(p) if p.ch == '-' => {
675                let first_token = first_token.punctuation().unwrap();
676                let mut content = String::from(first_token.ch);
677                for _ in 1..3 {
678                    let token = self
679                        .match_token_or_error(
680                            |t| matches!(t, ast::Token::Punctuation(p) if p.ch == '-'),
681                            tabular_body,
682                            &pos_after_body,
683                            || "expected: `-`".into(),
684                        )?
685                        .punctuation()
686                        .unwrap();
687                    content.push(token.ch);
688                }
689                return Ok(FieldDef {
690                    body: FieldDefBody::Wildcard(Wildcard {
691                        pos: first_token.pos(),
692                        content,
693                    }),
694                    excluded_values: Vec::new(),
695                });
696            }
697            _ => {}
698        }
699        let field_def_name =
700            self.parse_field_def_name(Some(first_token), tabular_body, &pos_after_body)?;
701        let body = match tabular_body.clone().next() {
702            Some(ast::Token::Punctuation(ast::Punctuation { pos: _, ch: '[' })) => {
703                FieldDefBody::Slice(self.parse_field_def_slice(
704                    field_def_name,
705                    tabular_body,
706                    &pos_after_body,
707                )?)
708            }
709            _ => FieldDefBody::Alternate(self.parse_field_def_alternate(
710                field_def_name,
711                tabular_body,
712                &pos_after_body,
713            )?),
714        };
715        let excluded_values = match tabular_body
716            .clone()
717            .next()
718            .and_then(ast::Token::dollar_inline_math)
719            .map(|v| &*v.content)
720        {
721            Some([ast::MathToken::Macro(macro_)]) if macro_.name.content == "neq" => {
722                tabular_body.next();
723                match tabular_body.next() {
724                    Some(ast::Token::Number(num)) => vec![num.clone()],
725                    Some(ast::Token::DollarInlineMath(dollar_inline_math)) => {
726                        let mut dollar_inline_math_body = dollar_inline_math.content.iter();
727                        self.match_token_or_error(
728                            |t| matches!(t, ast::MathToken::Macro(m) if m.name.content == "{"),
729                            &mut dollar_inline_math_body,
730                            &dollar_inline_math.end,
731                            || "expected `\\{{`".into(),
732                        )?;
733                        let mut excluded_values = match dollar_inline_math_body.next() {
734                            Some(ast::MathToken::Number(num)) => vec![num.clone()],
735                            _ => err!(self, dollar_inline_math.content_pos(), "expected number"),
736                        };
737                        while let Some(token) = dollar_inline_math_body.next() {
738                            if matches!(token, ast::MathToken::Macro(m) if m.name.content == "}") {
739                                if let Some(token) = dollar_inline_math_body.next() {
740                                    err!(self, token, "unexpected token");
741                                }
742                                break;
743                            }
744                            err_if!(
745                                !matches!(
746                                    token,
747                                    ast::MathToken::AnyChar(ast::AnyChar { pos: _, ch: ',' })
748                                ),
749                                self,
750                                token,
751                                "expected `,` or `\\}}`"
752                            );
753                            match dollar_inline_math_body.next() {
754                                Some(ast::MathToken::Number(num)) => {
755                                    excluded_values.push(num.clone())
756                                }
757                                Some(token) => {
758                                    err!(self, token, "expected number")
759                                }
760                                None => {
761                                    err!(self, dollar_inline_math.end.pos(), "expected number")
762                                }
763                            }
764                        }
765                        excluded_values
766                    }
767                    token => err!(self,
768                        token.map_or_else(||pos_after_body.pos(), GetPos::pos),
769                        "expected number or list of numbers (like so: `$\\{{<num>[,<num>[,<num>[...]]]\\}}$`)"
770                    ),
771                }
772            }
773            _ => Vec::new(),
774        };
775        Ok(FieldDef {
776            body,
777            excluded_values,
778        })
779    }
780    fn parse_opcode_name(
781        &self,
782        first_token: &ast::Token,
783        tabular_body: &mut std::slice::Iter<ast::Token>,
784        is_top_level: bool,
785    ) -> Result<OpcodeName> {
786        let pos = first_token.pos();
787        let mut first_token = Some(first_token);
788        let mut name = None;
789        let mut group = None;
790        while let Some(token) = first_token.or_else(|| tabular_body.clone().next()) {
791            if Self::is_column_body_terminator(token, is_top_level) {
792                assert!(first_token.is_none());
793                break;
794            }
795            if first_token.take().is_none() {
796                tabular_body.next();
797            }
798            match token {
799                ast::Token::CharTokens(char_tokens) => name
800                    .get_or_insert(String::new())
801                    .push_str(&char_tokens.content),
802                ast::Token::Punctuation(p) => {
803                    name.get_or_insert(String::new()).push(p.ch);
804                }
805                ast::Token::Group(_) | ast::Token::Whitespace(_) => break,
806                _ => err!(self, token, "expected `{{`"),
807            }
808        }
809        while let Some(token) = tabular_body.clone().next() {
810            if Self::is_column_body_terminator(token, is_top_level) {
811                break;
812            }
813            tabular_body.next();
814            match token {
815                ast::Token::Whitespace(_) => {}
816                ast::Token::Group(g) => {
817                    group = Some(g.clone());
818                    break;
819                }
820                _ => err!(self, token, "expected: `{{`"),
821            }
822        }
823        Ok(OpcodeName {
824            pos,
825            name: unwrap_or!(name, err!(self, pos, "expected: opcode name")),
826            group,
827        })
828    }
829    fn parse_column_body(
830        &self,
831        tabular_body: &mut std::slice::Iter<ast::Token>,
832        pos_after_body: impl GetPos,
833        column_definitions: &[ColumnDefinition],
834        is_top_level: bool,
835        is_field_column: bool,
836        is_opcode_column: bool,
837    ) -> Result<Option<ColumnBody>> {
838        loop {
839            let token = unwrap_or!(tabular_body.clone().next(), return Ok(None));
840            if Self::is_column_body_terminator(token, is_top_level) {
841                return Ok(None);
842            }
843            tabular_body.next();
844            if Self::is_column_body_ignored(token) {
845                continue;
846            }
847            return Ok(Some(match token {
848                ast::Token::Macro(macro_)
849                    if matches!(&*macro_.name.content, "cline" | "whline") && is_top_level =>
850                {
851                    self.unwrap_group_or_error(
852                        &pos_after_body,
853                        tabular_body.next().map(Cow::Borrowed),
854                    )?;
855                    continue;
856                }
857                ast::Token::Macro(macro_) if Self::is_register_macro(macro_) => {
858                    let field_def = self.parse_field_def(token, tabular_body, pos_after_body)?;
859                    self.skip_rest_of_column_body_else_error(tabular_body, is_top_level)?;
860                    ColumnBody::FieldDef(field_def)
861                }
862                ast::Token::Macro(macro_) if macro_.name.content == "instbit" => {
863                    let (bit, pos) = self.parse_number_in_group(
864                        pos_after_body,
865                        tabular_body.next().map(Cow::Borrowed),
866                        || "expected `{{<bit-number>}}` (like `{{12}}`)".into(),
867                    )?;
868                    self.skip_rest_of_column_body_else_error(tabular_body, is_top_level)?;
869                    ColumnBody::InstBit(InstBit { pos, bit })
870                }
871                ast::Token::Macro(macro_) if macro_.name.content == "instbitrange" => {
872                    let (end_bit, end_bit_pos) = self.parse_number_in_group(
873                        &pos_after_body,
874                        tabular_body.next().map(Cow::Borrowed),
875                        || "expected `{{<start-bit-number>}}` (like `{{12}}`)".into(),
876                    )?;
877                    let (start_bit, start_bit_pos) = self.parse_number_in_group(
878                        pos_after_body,
879                        tabular_body.next().map(Cow::Borrowed),
880                        || "expected `{{<end-bit-number>}}` (like `{{12}}`)".into(),
881                    )?;
882                    err_if!(
883                        start_bit > end_bit,
884                        self,
885                        start_bit_pos,
886                        "start bit must not be bigger than end bit"
887                    );
888                    self.skip_rest_of_column_body_else_error(tabular_body, is_top_level)?;
889                    ColumnBody::InstBitRange(InstBitRange {
890                        bits: start_bit..=end_bit,
891                        end_bit_pos,
892                        start_bit_pos,
893                    })
894                }
895                ast::Token::Macro(macro_) if macro_.name.content == "bf" => {
896                    let mut tokens: Vec<ast::Token> = vec![token.clone()];
897                    while let Some(token) = tabular_body
898                        .clone()
899                        .next()
900                        .filter(|token| Self::is_column_body_bold_text(token))
901                    {
902                        tabular_body.next();
903                        tokens.push(token.clone());
904                    }
905                    self.skip_rest_of_column_body_else_error(tabular_body, is_top_level)?;
906                    ColumnBody::ColumnBodyBoldText(ColumnBodyBoldText {
907                        pos: tokens[0].pos(),
908                        tokens,
909                    })
910                }
911                ast::Token::Group(g) => {
912                    self.skip_rest_of_column_body_else_error(tabular_body, is_top_level)?;
913                    ColumnBody::Group(g.clone())
914                }
915                _ if Self::is_column_body_text(token) => {
916                    if is_field_column {
917                        let field_def =
918                            self.parse_field_def(token, tabular_body, pos_after_body)?;
919                        self.skip_rest_of_column_body_else_error(tabular_body, is_top_level)?;
920                        return Ok(Some(ColumnBody::FieldDef(field_def)));
921                    } else if is_opcode_column {
922                        let opcode_name =
923                            self.parse_opcode_name(token, tabular_body, is_top_level)?;
924                        self.skip_rest_of_column_body_else_error(tabular_body, is_top_level)?;
925                        return Ok(Some(ColumnBody::OpcodeName(opcode_name)));
926                    }
927                    let mut tokens: Vec<ast::Token> = vec![token.clone()];
928                    while let Some(token) = tabular_body
929                        .clone()
930                        .next()
931                        .filter(|token| Self::is_column_body_text(token))
932                    {
933                        tabular_body.next();
934                        tokens.push(token.clone());
935                    }
936                    self.skip_rest_of_column_body_else_error(tabular_body, is_top_level)?;
937                    ColumnBody::ColumnBodyText(ColumnBodyText {
938                        pos: tokens[0].pos(),
939                        tokens,
940                    })
941                }
942                _ => err!(self, token, "unexpected token"),
943            }));
944        }
945    }
946    fn assert_column_is_empty(&self, column: &ColumnRange) -> Result<()> {
947        err_if!(
948            column.multi_column.is_some(),
949            self,
950            column.pos,
951            "`\\multicolumn` is not allowed here"
952        );
953        err_if!(
954            column.body.is_some(),
955            self,
956            column.pos,
957            "column must be empty here"
958        );
959        Ok(())
960    }
961    fn parse_blank_row(&self, row_iterator: &mut PeekableTableRowIterator) -> Result<()> {
962        let pos = row_iterator.pos();
963        let columns = unwrap_or!(row_iterator.next(), err!(self, pos, "expected: blank row"))?;
964        for column in columns.iter().rev() {
965            self.assert_column_is_empty(column)?;
966        }
967        Ok(())
968    }
969    fn parse_instruction_bit_ranges_row(
970        &self,
971        row_iterator: &mut PeekableTableRowIterator,
972        column_definitions: &[ColumnDefinition],
973    ) -> Result<()> {
974        let pos = row_iterator.pos();
975        let columns = unwrap_or!(
976            row_iterator.next(),
977            err!(self, pos, "expected: instruction bit ranges row")
978        )?;
979        err_if!(
980            columns.is_empty(),
981            self,
982            columns.last().map_or(pos, |v| v.pos),
983            "not enough columns in instruction bit ranges row"
984        );
985        let (last_column, columns) = columns.split_last().unwrap();
986        self.assert_column_is_empty(last_column)?;
987        for column_range in columns {
988            err_if!(
989                column_range.indexes.len() != 1,
990                self,
991                column_range.pos,
992                "spanning multiple columns using `\\multicolumn{{{}}}` is not allowed in instruction bit ranges row",
993                column_range.indexes.len()
994            );
995            err_if!(
996                column_range.body.is_none(),
997                self,
998                column_range.pos,
999                "empty columns are not allowed in instruction bit ranges row"
1000            );
1001        }
1002        err_if!(
1003            columns.len()
1004                != column_definitions.len() - Self::COLUMN_START_PADDING - Self::COLUMN_END_PADDING,
1005            self,
1006            columns.last().map_or(pos, |v| v.pos),
1007            "not enough columns in instruction bit ranges row"
1008        );
1009        let mut last_range_end = None;
1010        let mut index = 0;
1011        while index < columns.len() {
1012            let mut column_range = index..index + 1;
1013            let column = &columns[index];
1014            let bit_range = match (
1015                column.body.as_ref().unwrap(),
1016                column.multi_column.as_ref().map(|v| v.kind),
1017            ) {
1018                (ColumnBody::InstBit(v), None)
1019                | (ColumnBody::InstBit(v), Some(MultiColumnKind::Center)) => v.clone().into(),
1020                (ColumnBody::InstBitRange(v), None) => v.clone(),
1021                (ColumnBody::InstBit(right), Some(MultiColumnKind::Right)) => {
1022                    err_if!(
1023                        index + 1 >= columns.len(),
1024                        self,
1025                        column.pos,
1026                        // first because we reverse the columns
1027                        "`\\multicolumn{{1}}{{r}}` is not valid here: this is the first column",
1028                    );
1029                    column_range = index..index + 2;
1030                    let left_column = &columns[index + 1];
1031                    err_if!(
1032                        Some(MultiColumnKind::Left)
1033                            != left_column.multi_column.as_ref().map(|v| v.kind),
1034                        self,
1035                        left_column.pos,
1036                        "expected: `\\multicolumn{{1}}{{l}}`",
1037                    );
1038                    if let ColumnBody::InstBit(left) = left_column.body.as_ref().unwrap() {
1039                        InstBitRange {
1040                            bits: right.bit..=left.bit,
1041                            end_bit_pos: left.pos,
1042                            start_bit_pos: right.pos,
1043                        }
1044                    } else {
1045                        err!(
1046                            self,
1047                            left_column.body.as_ref().unwrap().pos(),
1048                            "expected: `\\instbit`"
1049                        )
1050                    }
1051                }
1052                _ => err!(
1053                    self,
1054                    column.body.as_ref().unwrap().pos(),
1055                    "expected `\\instbit` or `\\instbitrange`"
1056                ),
1057            };
1058            if let Some((last_range_end, last_range_end_pos)) = mem::replace(
1059                &mut last_range_end,
1060                Some((*bit_range.bits.end(), bit_range.end_bit_pos)),
1061            ) {
1062                let next_bit_index = unwrap_or!(
1063                    last_range_end.checked_add(1),
1064                    err!(self, last_range_end_pos, "number too big")
1065                );
1066                err_if!(
1067                    *bit_range.bits.start() != next_bit_index,
1068                    self,
1069                    bit_range.start_bit_pos,
1070                    "expected {}",
1071                    next_bit_index
1072                );
1073            } else {
1074                err_if!(
1075                    *bit_range.bits.start() != 0,
1076                    self,
1077                    bit_range.start_bit_pos,
1078                    "expected 0"
1079                );
1080            }
1081            let mut padded_column_range = column_range.clone();
1082            padded_column_range.start += Self::COLUMN_START_PADDING;
1083            padded_column_range.end += Self::COLUMN_START_PADDING;
1084            let merged_def = MergedColumnDefinition {
1085                column_range: padded_column_range.clone(),
1086                bit_range,
1087            };
1088            for c in &column_definitions[padded_column_range.clone()] {
1089                c.merged_def.set(merged_def.clone()).ok().unwrap();
1090            }
1091            index = column_range.end;
1092        }
1093        Ok(())
1094    }
1095    fn parse_instruction_field(
1096        &self,
1097        column: ColumnRange,
1098        column_definitions: &[ColumnDefinition],
1099        is_fence_instruction_form: bool,
1100    ) -> Result<InstructionField> {
1101        let field_def = match column.body {
1102            Some(ColumnBody::FieldDef(field_def)) => field_def,
1103            _ => err!(self, column.pos, "expected: instruction field"),
1104        };
1105        let field_matches_name_or_num = |name: &str, num_len: usize| -> bool {
1106            field_def.only_char_tokens().map(|v| &*v.content) == Some(name)
1107                || matches!(&field_def.body, FieldDefBody::LiteralNumber(num) if num.content.len() == num_len)
1108        };
1109        if is_fence_instruction_form {
1110            let bits = match column.indexes {
1111                Range { start: 5, end: 6 } => {
1112                    err_if!(
1113                        !field_matches_name_or_num("succ", 4),
1114                        self,
1115                        column.pos,
1116                        "expected fence instruction's `succ` field"
1117                    );
1118                    Some(20..=23)
1119                }
1120                Range { start: 6, end: 9 } => {
1121                    err_if!(
1122                        !field_matches_name_or_num("pred", 4),
1123                        self,
1124                        column.pos,
1125                        "expected fence instruction's `pred` field"
1126                    );
1127                    Some(24..=27)
1128                }
1129                _ => None,
1130            };
1131            if let Some(bits) = bits {
1132                return Ok(InstructionField {
1133                    field_def,
1134                    instruction_bit_range: InstBitRange {
1135                        bits,
1136                        end_bit_pos: column.pos,
1137                        start_bit_pos: column.pos,
1138                    },
1139                });
1140            }
1141        }
1142        let column_pos = column.pos;
1143        let mismatch_err = || {
1144            self.err_pos(
1145                column_pos,
1146                "instruction field doesn't match up with instruction-bit-numbers row".into(),
1147            )
1148        };
1149        let start_column_definition = unwrap_or!(
1150            column_definitions[column.indexes.start].merged_def.get(),
1151            return Err(mismatch_err());
1152        );
1153        if start_column_definition.column_range.start != column.indexes.start {
1154            return Err(mismatch_err());
1155        }
1156        let last_column_definition = unwrap_or!(
1157            column_definitions[column.indexes.clone().last().unwrap()].merged_def.get(),
1158            return Err(mismatch_err());
1159        );
1160        if last_column_definition.column_range.end != column.indexes.end {
1161            return Err(mismatch_err());
1162        }
1163        let instruction_bit_range = InstBitRange {
1164            bits: *start_column_definition.bit_range.bits.start()
1165                ..=*last_column_definition.bit_range.bits.end(),
1166            end_bit_pos: last_column_definition.bit_range.end_bit_pos,
1167            start_bit_pos: start_column_definition.bit_range.start_bit_pos,
1168        };
1169        Ok(InstructionField {
1170            instruction_bit_range,
1171            field_def,
1172        })
1173    }
1174    fn parse_instruction_fields(
1175        &self,
1176        row: Vec<ColumnRange>,
1177        column_definitions: &[ColumnDefinition],
1178        is_fence_instruction_form: bool,
1179    ) -> Result<Vec<InstructionField>> {
1180        let first = row.first().expect("row already checked to be non-empty");
1181        err_if!(
1182            first.indexes.len() != Self::COLUMN_START_PADDING,
1183            self,
1184            first.pos,
1185            "spanning multiple columns using `\\multicolumn{{{}}}` is not allowed here",
1186            first.indexes.len()
1187        );
1188        let last = row.last().expect("row already checked to be non-empty");
1189        err_if!(
1190            last.indexes.len() != Self::COLUMN_END_PADDING,
1191            self,
1192            last.pos,
1193            "spanning multiple columns using `\\multicolumn{{{}}}` is not allowed here",
1194            last.indexes.len()
1195        );
1196        err_if!(
1197            row.len() < Self::COLUMN_START_PADDING + Self::COLUMN_END_PADDING,
1198            self,
1199            last.pos,
1200            "row doesn't have enough columns"
1201        );
1202        let row_len = row.len();
1203        let fields = row
1204            .into_iter()
1205            .take(row_len - Self::COLUMN_END_PADDING)
1206            .skip(Self::COLUMN_START_PADDING)
1207            .map(|column| {
1208                self.parse_instruction_field(column, column_definitions, is_fence_instruction_form)
1209            })
1210            .collect::<Result<_>>()?;
1211        Ok(fields)
1212    }
1213    fn parse_instruction_form_rows(
1214        &self,
1215        row_iterator: &mut PeekableTableRowIterator,
1216        column_definitions: &[ColumnDefinition],
1217    ) -> Result<Vec<InstructionForm>> {
1218        let mut instruction_forms = Vec::new();
1219        while let Some(row) = row_iterator.peek().transpose()? {
1220            let name = match row.first() {
1221                Some(ColumnRange {
1222                    body: Some(ColumnBody::OpcodeName(name)),
1223                    ..
1224                }) if name.group.is_none() && name.name.ends_with("-type") => InstructionFormName {
1225                    pos: name.pos,
1226                    name: name.name.clone(),
1227                },
1228                _ => break,
1229            };
1230            let fields = self.parse_instruction_fields(
1231                row_iterator.next().unwrap().unwrap(),
1232                column_definitions,
1233                false,
1234            )?;
1235            instruction_forms.push(InstructionForm { name, fields });
1236        }
1237        Ok(instruction_forms)
1238    }
1239    fn parse_instruction(
1240        &self,
1241        row: Vec<ColumnRange>,
1242        column_definitions: &[ColumnDefinition],
1243    ) -> Result<Instruction> {
1244        let opcode = match row.first().expect("row already checked to be non-empty") {
1245            ColumnRange {
1246                body: Some(ColumnBody::OpcodeName(opcode)),
1247                ..
1248            } => opcode.clone(),
1249            column => err!(self, column.pos, "expected: opcode"),
1250        };
1251        let is_fence_instruction_form = matches!(&*opcode.name, "FENCE" | "FENCE.TSO" | "PAUSE");
1252        Ok(Instruction {
1253            opcode,
1254            fields: self.parse_instruction_fields(
1255                row,
1256                column_definitions,
1257                is_fence_instruction_form,
1258            )?,
1259        })
1260    }
1261    fn parse_instruction_set_section(
1262        &self,
1263        tabular_env: &ast::Environment,
1264    ) -> Result<InstructionSetSection> {
1265        let mut tabular_body = tabular_env.body.iter();
1266        let mut first_token = tabular_body.next();
1267        match first_token.and_then(|t| Some(&*t.char_tokens()?.content)) {
1268            Some(pos) if matches!(pos, "t" | "b" | "c") => {
1269                first_token = None;
1270            }
1271            _ => {}
1272        }
1273        let column_definitions = self.parse_tabular_column_definitions(unwrap_or!(
1274            first_token
1275                .or_else(|| tabular_body.next())
1276                .and_then(ast::Token::group),
1277            err!(
1278                self,
1279                tabular_env.body_pos(),
1280                "expected `tabular` column definitions"
1281            )
1282        ))?;
1283        let mut row_iterator =
1284            TableRowIterator::new(self, tabular_body, &tabular_env.end, &column_definitions)
1285                .peekable();
1286        self.parse_blank_row(&mut row_iterator)?;
1287        self.parse_instruction_bit_ranges_row(&mut row_iterator, &column_definitions)?;
1288        let forms = self.parse_instruction_form_rows(&mut row_iterator, &column_definitions)?;
1289        let mut instructions = Vec::new();
1290        for row in row_iterator {
1291            let row = row?;
1292            match &*row {
1293                [ColumnRange {
1294                    body: Some(ColumnBody::Group(_)),
1295                    ..
1296                }, ..]
1297                | [ColumnRange { body: None, .. }, ColumnRange { body: None, .. }]
1298                | [ColumnRange {
1299                    body: Some(ColumnBody::ColumnBodyBoldText(_)),
1300                    ..
1301                }, ..] => continue,
1302                _ => instructions.push(self.parse_instruction(row, &column_definitions)?),
1303            }
1304        }
1305        Ok(InstructionSetSection {
1306            forms,
1307            instructions,
1308        })
1309    }
1310    fn parse_instruction_set(&self, document: &ast::Document) -> Result<InstructionSet> {
1311        let mut sections = Vec::new();
1312        for table_env in document.content.iter().filter_map(ast::Token::environment) {
1313            let table_env = self.expect_environment(table_env, "table")?;
1314            let mut section = None;
1315            for small_env in table_env.body.iter().filter_map(ast::Token::environment) {
1316                let small_env = self.expect_environment(small_env, "small")?;
1317                for center_env in small_env.body.iter().filter_map(ast::Token::environment) {
1318                    let center_env = self.expect_environment(center_env, "center")?;
1319                    for tabular_env in center_env.body.iter().filter_map(ast::Token::environment) {
1320                        let tabular_env = self.expect_environment(tabular_env, "tabular")?;
1321                        err_if!(
1322                            section.is_some(),
1323                            self,
1324                            tabular_env,
1325                            "multiple `\\begin{{tabular}}` in single `\\begin{{table}}`"
1326                        );
1327                        section = Some(self.parse_instruction_set_section(tabular_env)?);
1328                    }
1329                }
1330            }
1331            sections.push(unwrap_or!(
1332                section,
1333                err!(
1334                    self,
1335                    table_env,
1336                    "missing `\\begin{{tabular}}` in this `\\begin{{table}}`"
1337                )
1338            ));
1339        }
1340        err_if!(sections.is_empty(), self, document, "no environment found");
1341        Ok(InstructionSet { sections })
1342    }
1343}
1344
1345#[derive(Debug, Clone)]
1346pub struct InstructionSetSection {
1347    pub forms: Vec<InstructionForm>,
1348    pub instructions: Vec<Instruction>,
1349}
1350
1351#[derive(Debug, Clone)]
1352pub struct InstructionSet {
1353    pub sections: Vec<InstructionSetSection>,
1354}
1355
1356#[derive(Debug, Clone)]
1357pub enum ColumnKind {
1358    ParagraphTop {
1359        keyword_pos: ast::Pos,
1360        width: ast::Group,
1361    },
1362    LeftJustified {
1363        keyword_pos: ast::Pos,
1364    },
1365    // TODO: add rest
1366}
1367
1368#[derive(Debug, Clone)]
1369pub struct MergedColumnDefinition {
1370    pub column_range: Range<usize>,
1371    pub bit_range: InstBitRange,
1372}
1373
1374#[derive(Debug, Clone)]
1375pub struct ColumnDefinition {
1376    pub kind: ColumnKind,
1377    pub merged_def: OnceCell<MergedColumnDefinition>,
1378}
1379
1380#[derive(Debug, Clone)]
1381pub struct InstBit {
1382    pub pos: ast::Pos,
1383    pub bit: u32,
1384}
1385
1386#[derive(Debug, Clone)]
1387pub struct InstBitRange {
1388    pub bits: RangeInclusive<u32>,
1389    pub end_bit_pos: ast::Pos,
1390    pub start_bit_pos: ast::Pos,
1391}
1392
1393impl From<InstBit> for InstBitRange {
1394    fn from(v: InstBit) -> Self {
1395        Self {
1396            bits: v.bit..=v.bit,
1397            end_bit_pos: v.pos,
1398            start_bit_pos: v.pos,
1399        }
1400    }
1401}
1402
1403#[derive(Debug, Clone)]
1404pub struct FieldBitRange {
1405    pub bits: RangeInclusive<u32>,
1406    pub end_bit_pos: ast::Pos,
1407    pub start_bit_pos: Option<ast::Pos>,
1408}
1409
1410#[derive(Debug, Clone)]
1411pub enum FieldDefName {
1412    Macro(ast::MacroName),
1413    CharTokens(ast::CharTokens),
1414}
1415
1416impl GetPos for FieldDefName {
1417    fn pos(&self) -> ast::Pos {
1418        match self {
1419            FieldDefName::Macro(v) => v.pos(),
1420            FieldDefName::CharTokens(v) => v.pos(),
1421        }
1422    }
1423}
1424
1425#[derive(Debug, Clone)]
1426pub struct FieldDefSlice {
1427    pub name: FieldDefName,
1428    pub bit_ranges: Vec<FieldBitRange>,
1429}
1430
1431impl GetPos for FieldDefSlice {
1432    fn pos(&self) -> ast::Pos {
1433        self.name.pos()
1434    }
1435}
1436
1437#[derive(Debug, Clone)]
1438pub struct FieldDefAlternate {
1439    /// must be non-empty
1440    pub names: Vec<FieldDefName>,
1441}
1442
1443impl GetPos for FieldDefAlternate {
1444    fn pos(&self) -> ast::Pos {
1445        self.names[0].pos()
1446    }
1447}
1448
1449#[derive(Debug, Clone)]
1450pub struct Wildcard {
1451    /// must be non-empty
1452    pub pos: ast::Pos,
1453    pub content: String,
1454}
1455
1456impl GetPos for Wildcard {
1457    fn pos(&self) -> ast::Pos {
1458        self.pos
1459    }
1460}
1461
1462#[derive(Debug, Clone)]
1463pub enum FieldDefBody {
1464    Alternate(FieldDefAlternate),
1465    Slice(FieldDefSlice),
1466    Wildcard(Wildcard),
1467    LiteralNumber(ast::Number),
1468}
1469
1470impl GetPos for FieldDefBody {
1471    fn pos(&self) -> ast::Pos {
1472        match self {
1473            FieldDefBody::Alternate(v) => v.pos(),
1474            FieldDefBody::Slice(v) => v.pos(),
1475            FieldDefBody::Wildcard(v) => v.pos(),
1476            FieldDefBody::LiteralNumber(v) => v.pos(),
1477        }
1478    }
1479}
1480
1481#[derive(Debug, Clone)]
1482pub struct FieldDef {
1483    pub body: FieldDefBody,
1484    pub excluded_values: Vec<ast::Number>,
1485}
1486
1487impl FieldDef {
1488    pub fn only_char_tokens(&self) -> Option<&ast::CharTokens> {
1489        if !self.excluded_values.is_empty() {
1490            return None;
1491        }
1492        match &self.body {
1493            FieldDefBody::Alternate(FieldDefAlternate { names }) => match &**names {
1494                [FieldDefName::CharTokens(char_tokens)] => Some(char_tokens),
1495                _ => None,
1496            },
1497            _ => None,
1498        }
1499    }
1500}
1501
1502impl GetPos for FieldDef {
1503    fn pos(&self) -> ast::Pos {
1504        self.body.pos()
1505    }
1506}
1507
1508#[derive(Debug, Clone)]
1509pub struct ColumnBodyText {
1510    pub pos: ast::Pos,
1511    pub tokens: Vec<ast::Token>,
1512}
1513
1514#[derive(Debug, Clone)]
1515pub struct OpcodeName {
1516    pub pos: ast::Pos,
1517    pub name: String,
1518    pub group: Option<ast::Group>,
1519}
1520
1521#[derive(Debug, Clone)]
1522pub struct ColumnBodyBoldText {
1523    pub pos: ast::Pos,
1524    pub tokens: Vec<ast::Token>,
1525}
1526
1527#[derive(Debug, Clone)]
1528pub enum ColumnBody {
1529    InstBit(InstBit),
1530    InstBitRange(InstBitRange),
1531    FieldDef(FieldDef),
1532    ColumnBodyText(ColumnBodyText),
1533    OpcodeName(OpcodeName),
1534    ColumnBodyBoldText(ColumnBodyBoldText),
1535    Group(ast::Group),
1536}
1537
1538impl GetPos for ColumnBody {
1539    fn pos(&self) -> ast::Pos {
1540        match self {
1541            ColumnBody::InstBit(v) => v.pos,
1542            ColumnBody::InstBitRange(v) => v.end_bit_pos, // end is first in text
1543            ColumnBody::FieldDef(v) => v.pos(),
1544            ColumnBody::ColumnBodyText(v) => v.pos,
1545            ColumnBody::OpcodeName(v) => v.pos,
1546            ColumnBody::ColumnBodyBoldText(v) => v.pos,
1547            ColumnBody::Group(v) => v.pos(),
1548        }
1549    }
1550}
1551
1552#[derive(Debug, Copy, Clone, PartialEq, Eq, Hash)]
1553pub enum MultiColumnKind {
1554    Left,
1555    Right,
1556    Center,
1557}
1558
1559impl MultiColumnKind {
1560    pub fn from_str(s: &str) -> Option<Self> {
1561        match s {
1562            "l" => Some(Self::Left),
1563            "c" => Some(Self::Center),
1564            "r" => Some(Self::Right),
1565            _ => None,
1566        }
1567    }
1568}
1569
1570#[derive(Debug, Clone)]
1571pub struct MultiColumn {
1572    pub pos: ast::Pos,
1573    pub kind: MultiColumnKind,
1574    pub vertical_bar_on_left: bool,
1575    pub vertical_bar_on_right: bool,
1576}
1577
1578#[derive(Debug, Clone)]
1579pub struct ColumnRange {
1580    pub pos: ast::Pos,
1581    pub indexes: Range<usize>,
1582    pub multi_column: Option<MultiColumn>,
1583    pub body: Option<ColumnBody>,
1584}
1585
1586#[derive(Debug, Clone)]
1587pub struct InstructionField {
1588    pub instruction_bit_range: InstBitRange,
1589    pub field_def: FieldDef,
1590}
1591
1592#[derive(Debug, Clone)]
1593pub struct InstructionFormName {
1594    pub pos: ast::Pos,
1595    pub name: String,
1596}
1597
1598#[derive(Debug, Clone)]
1599pub struct InstructionForm {
1600    pub name: InstructionFormName,
1601    pub fields: Vec<InstructionField>,
1602}
1603
1604#[derive(Debug, Clone)]
1605pub struct Instruction {
1606    pub opcode: OpcodeName,
1607    pub fields: Vec<InstructionField>,
1608}
1609
1610pub fn parse(file_name: &str, input: &str) -> Result<InstructionSet> {
1611    ast::Pos::call_with_input_context(InputContext { file_name, input }, || {
1612        let parser = Parser::new(file_name, input);
1613        let document = tex_parser::parse(input)
1614            .map_err(|e| parser.err_line_col(e.location, format!("expected: {}", e.expected)))?;
1615        parser.parse_instruction_set(&document)
1616    })
1617}