cpclib_basic/
binary_parser.rs

1use cpclib_common::itertools::Itertools;
2use cpclib_common::winnow;
3use cpclib_common::winnow::combinator::{eof, repeat, terminated};
4use cpclib_common::winnow::{ModalResult, Parser};
5use winnow::binary::{le_u16, u8};
6use winnow::combinator::cut_err;
7
8use crate::binary_parser::winnow::error::ContextError;
9use crate::binary_parser::winnow::token::take;
10use crate::tokens::{BasicTokenPrefixed, *};
11use crate::{BasicLine, BasicProgram};
12
13pub fn program(bytes: &mut &[u8]) -> ModalResult<BasicProgram, ContextError<&'static str>> {
14    let lines: Vec<BasicLine> = repeat(0.., line_or_end.context("Error when parsing a basic line"))
15        .verify(|lines: &Vec<Option<BasicLine>>| lines.last().map(|l| l.is_none()).unwrap_or(true))
16        .map(|mut lines: Vec<Option<BasicLine>>| {
17            lines.pop();
18            lines.into_iter().map(|l| l.unwrap()).collect_vec()
19        })
20        .parse_next(bytes)?;
21
22    Ok(BasicProgram { lines })
23}
24
25// https://www.cpcwiki.eu/index.php?title=Technical_information_about_Locomotive_BASIC&mobileaction=toggle_view_desktop#Structure_of_a_BASIC_program
26// Some(BasicLine) for a Line
27// None for End
28pub fn line_or_end(
29    bytes: &mut &[u8]
30) -> ModalResult<Option<BasicLine>, ContextError<&'static str>> {
31    let length = cut_err(le_u16.context("Expecting a line length")).parse_next(bytes)?;
32
33    // leave if it is the end of the program
34    if length == 0 {
35        return Ok(None);
36    }
37
38    let line_number = cut_err(le_u16.context("Expecting a line number")).parse_next(bytes)?;
39
40    dbg!("Tentative for line", line_number);
41
42    let mut buffer = cut_err(take(length - 4).context("Wrong number of bytes"))
43        .verify(|buffer: &[u8]| buffer[buffer.len() - 1] == 0)
44        .context("Last byte should be 0")
45        .parse_next(bytes)?;
46
47    let tokens = terminated(parse_tokens, eof).parse_next(&mut buffer)?;
48
49    let line = BasicLine {
50        line_number,
51        forced_length: None,
52        tokens
53    };
54
55    dbg!(&line);
56    dbg!(line.to_string());
57
58    Ok(Some(line))
59}
60
61pub fn parse_tokens(bytes: &mut &[u8]) -> ModalResult<Vec<BasicToken>, ContextError<&'static str>> {
62    let mut tokens = Vec::with_capacity(bytes.len());
63    while !bytes.is_empty() {
64        let code = BasicTokenNoPrefix::try_from(u8.parse_next(bytes)?).unwrap();
65
66        match code {
67            BasicTokenNoPrefix::ValueIntegerDecimal8bits => {
68                todo!()
69            },
70
71            BasicTokenNoPrefix::ValueIntegerDecimal16bits => {
72                todo!()
73            },
74
75            BasicTokenNoPrefix::ValueIntegerBinary16bits => {
76                todo!()
77            },
78
79            BasicTokenNoPrefix::ValueIntegerHexadecimal16bits => {
80                let low = u8.parse_next(bytes)?;
81                let high = u8.parse_next(bytes)?;
82                let value = BasicValue::new_integer_by_bytes(low, high);
83                let token = BasicToken::Constant(code, value);
84                tokens.push(token);
85            },
86
87            BasicTokenNoPrefix::AdditionalTokenMarker => {
88                let code2 = BasicTokenPrefixed::try_from(u8.parse_next(bytes)?).unwrap();
89                let token = BasicToken::PrefixedToken(code2);
90                tokens.push(token);
91            },
92
93            _ => {
94                tokens.push(BasicToken::SimpleToken(code));
95            }
96        }
97    }
98
99    Ok(tokens)
100}