cpclib_basic/
tokens.rs

1use std::fmt;
2
3impl TryFrom<u8> for BasicTokenNoPrefix {
4    type Error = String;
5
6    fn try_from(value: u8) -> Result<Self, String> {
7        match value {
8            5 => Err(format!("{value} is invalid")),
9            _ => Ok(unsafe { std::mem::transmute(value) })
10        }
11    }
12}
13
14impl From<BasicTokenNoPrefix> for u8 {
15    fn from(val: BasicTokenNoPrefix) -> Self {
16        val as u8
17    }
18}
19
20#[derive(Copy, Clone, PartialEq, Debug)]
21#[repr(u8)]
22#[allow(missing_docs)]
23pub enum BasicTokenNoPrefix {
24    EndOfTokenisedLine = 0,
25
26    StatementSeparator = 1,
27
28    IntegerVariableDefinition = 2,
29    StringVariableDefinition = 3,
30    FloatingPointVariableDefinition = 4,
31
32    VarUnknown1 = 6,
33    VarUnknown2 = 7,
34    VarUnknown3 = 8,
35    CharTab = 9, // XXX Not sure of that
36    VarUnknown5 = 0xA,
37
38    VariableDefinition1 = 0xB,
39    VariableDefinition2 = 0xC,
40    VariableDefinition3 = 0xD,
41
42    ConstantNumber0 = 0x0E,
43    ConstantNumber1 = 0x0F,
44    ConstantNumber2 = 0x10,
45    ConstantNumber3 = 0x11,
46    ConstantNumber4 = 0x12,
47    ConstantNumber5 = 0x13,
48    ConstantNumber6 = 0x14,
49    ConstantNumber7 = 0x15,
50    ConstantNumber8 = 0x16,
51    ConstantNumber9 = 0x17,
52    ConstantNumber10 = 0x18,
53
54    ValueIntegerDecimal8bits = 0x19,
55
56    ValueIntegerDecimal16bits = 0x1A,
57    ValueIntegerBinary16bits = 0x1B,
58    ValueIntegerHexadecimal16bits = 0x1C,
59
60    LineMemoryAddressPointer = 0x1D,
61    LineNumber = 0x1E,
62
63    ValueFloatingPoint = 0x1F,
64
65    CharSpace = 0x20,
66    CharExclamation = 0x21,
67
68    ValueQuotedString = 0x22,
69
70    CharNumber,
71    CharDollar,
72    CharPerCent,
73    CharAmpersand,
74    CharSingleQuote,
75    CharOpenParenthesis,
76    CharCloseParenthesis,
77    CharAsterix,
78    CharPlus,
79    CharComma,
80    CharHyphen,
81    CharDot,
82    CharSlash,
83    Char0,
84    Char1,
85    Char2,
86    Char3,
87    Char4,
88    Char5,
89    Char6,
90    Char7,
91    Char8,
92    Char9,
93    CharColon,
94    CharSemiColon,
95    CharLess,
96    CharEquals,
97    CharGreater,
98    CharQuestionMark,
99    CharAt,
100
101    // TODO add all ascii symbols from 23 to 7b
102    CharUpperA = 65,
103    CharUpperB,
104    CharUpperC,
105    CharUpperD,
106    CharUpperE,
107    CharUpperF,
108    CharUpperG,
109    CharUpperH,
110    CharUpperI,
111    CharUpperJ,
112    CharUpperK,
113    CharUpperL,
114    CharUpperM,
115    CharUpperN,
116    CharUpperO,
117    CharUpperP,
118    CharUpperQ,
119    CharUpperR,
120    CharUpperS,
121    CharUpperT,
122    CharUpperU,
123    CharUpperV,
124    CharUpperW,
125    CharUpperX,
126    CharUpperY,
127    CharUpperZ,
128
129    CharLowerA = 97,
130    CharLowerB,
131    CharLowerC,
132    CharLowerD,
133    CharLowerE,
134    CharLowerF,
135    CharLowerG,
136    CharLowerH,
137    CharLowerI,
138    CharLowerJ,
139    CharLowerK,
140    CharLowerL,
141    CharLowerM,
142    CharLowerN,
143    CharLowerO,
144    CharLowerP,
145    CharLowerQ,
146    CharLowerR,
147    CharLowerS,
148    CharLowerT,
149    CharLowerU,
150    CharLowerV,
151    CharLowerW,
152    CharLowerX,
153    CharLowerY,
154    CharLowerZ,
155
156    Pipe = 0x7C,
157
158    Unused7d = 0x7D,
159    Unused7e = 0x7E,
160    Unused7f = 0x7F,
161
162    After = 0x80,
163    Auto,
164    Border,
165    Call,
166    Cat,
167    Chain,
168    Clear,
169    Clg,
170    Closein,
171    Closeout,
172    Cls,
173    Cont,
174    Data,
175    Def,
176    Defint,
177    Defreal,
178    Defstr,
179    Deg,
180    Delete,
181    Dim,
182    Draw,
183    Drawr,
184    Edit,
185    Else,
186    End,
187    Ent,
188    Env,
189    Erase,
190    Error,
191    Every,
192    For,
193    Gosub,
194    Goto,
195    If,
196    Ink,
197    Input,
198    Key,
199    Let,
200    Line,
201    List,
202    Load,
203    Locate,
204    Memory,
205    Merge,
206    MidDollar,
207    Mode,
208    Move,
209    Mover,
210    Next,
211    New,
212    On,
213    OnBreak,
214    OnErrorGoto,
215    Sq,
216    Openin,
217    Openout,
218    Origin,
219    Out,
220    Paper,
221    Pen,
222    Plot,
223    Plotr,
224    Poke,
225    Print,
226    SymbolQuote,
227    Rad,
228    Randomize,
229    Read,
230    Release,
231    Rem,
232    Renum,
233    Restore,
234    Resume,
235    Return,
236    Run,
237    Save,
238    Sound,
239    Speed,
240    Stop,
241    Symbol,
242    Tag,
243    Tagoff,
244    Troff,
245    Tron,
246    Wait,
247    Wend,
248    While,
249    Width,
250    Window,
251    Write,
252    Zone,
253    Di,
254    Ei,
255    Fill,
256    Graphics,
257    Mask,
258    Frame,
259    Cursor,
260    UnusedE2,
261    Erl,
262    Fn,
263    Spc,
264    Step,
265    Swap,
266    UnusedE8,
267    UnusedE9,
268    Tab,
269    Then,
270    To,
271    Using,
272    GreaterThan,
273    Equal,
274    GreaterOrEqual,
275    LessThan,
276    NotEqual,
277    LessThanOrEqual,
278    Addition,
279    SubstractionOrUnaryMinus,
280    Multiplication,
281    Division,
282    Power,
283    IntegerDivision,
284    And,
285    Mod,
286    Or,
287    Xor,
288    AdditionalTokenMarker
289}
290
291impl From<char> for BasicTokenNoPrefix {
292    fn from(c: char) -> Self {
293        match c {
294            // ':' => (BasicTokenNoPrefix::StatementSeparator),
295            ' ' => BasicTokenNoPrefix::CharSpace,
296            'A' => BasicTokenNoPrefix::CharUpperA,
297            'B' => BasicTokenNoPrefix::CharUpperB,
298            'C' => BasicTokenNoPrefix::CharUpperC,
299            'D' => BasicTokenNoPrefix::CharUpperD,
300            'E' => BasicTokenNoPrefix::CharUpperE,
301            'F' => BasicTokenNoPrefix::CharUpperF,
302            'G' => BasicTokenNoPrefix::CharUpperG,
303            'H' => BasicTokenNoPrefix::CharUpperH,
304            'I' => BasicTokenNoPrefix::CharUpperI,
305            'J' => BasicTokenNoPrefix::CharUpperJ,
306            'K' => BasicTokenNoPrefix::CharUpperK,
307            'L' => BasicTokenNoPrefix::CharUpperL,
308            'M' => BasicTokenNoPrefix::CharUpperM,
309            'N' => BasicTokenNoPrefix::CharUpperN,
310            'O' => BasicTokenNoPrefix::CharUpperO,
311            'P' => BasicTokenNoPrefix::CharUpperP,
312            'Q' => BasicTokenNoPrefix::CharUpperQ,
313            'R' => BasicTokenNoPrefix::CharUpperR,
314            'S' => BasicTokenNoPrefix::CharUpperS,
315            'T' => BasicTokenNoPrefix::CharUpperT,
316            'U' => BasicTokenNoPrefix::CharUpperU,
317            'V' => BasicTokenNoPrefix::CharUpperV,
318            'W' => BasicTokenNoPrefix::CharUpperW,
319            'X' => BasicTokenNoPrefix::CharUpperX,
320            'Y' => BasicTokenNoPrefix::CharUpperY,
321            'Z' => BasicTokenNoPrefix::CharUpperZ,
322            'a' => BasicTokenNoPrefix::CharLowerA,
323            'b' => BasicTokenNoPrefix::CharLowerB,
324            'c' => BasicTokenNoPrefix::CharLowerC,
325            'd' => BasicTokenNoPrefix::CharLowerD,
326            'e' => BasicTokenNoPrefix::CharLowerE,
327            'f' => BasicTokenNoPrefix::CharLowerF,
328            'g' => BasicTokenNoPrefix::CharLowerG,
329            'h' => BasicTokenNoPrefix::CharLowerH,
330            'i' => BasicTokenNoPrefix::CharLowerI,
331            'j' => BasicTokenNoPrefix::CharLowerJ,
332            'k' => BasicTokenNoPrefix::CharLowerK,
333            'l' => BasicTokenNoPrefix::CharLowerL,
334            'm' => BasicTokenNoPrefix::CharLowerM,
335            'n' => BasicTokenNoPrefix::CharLowerN,
336            'o' => BasicTokenNoPrefix::CharLowerO,
337            'p' => BasicTokenNoPrefix::CharLowerP,
338            'q' => BasicTokenNoPrefix::CharLowerQ,
339            'r' => BasicTokenNoPrefix::CharLowerR,
340            's' => BasicTokenNoPrefix::CharLowerS,
341            't' => BasicTokenNoPrefix::CharLowerT,
342            'u' => BasicTokenNoPrefix::CharLowerU,
343            'v' => BasicTokenNoPrefix::CharLowerV,
344            'w' => BasicTokenNoPrefix::CharLowerW,
345            'x' => BasicTokenNoPrefix::CharLowerX,
346            'y' => BasicTokenNoPrefix::CharLowerY,
347            'z' => BasicTokenNoPrefix::CharLowerZ,
348
349            '#' => BasicTokenNoPrefix::CharNumber,
350            '$' => BasicTokenNoPrefix::CharDollar,
351            '%' => BasicTokenNoPrefix::CharPerCent,
352            '&' => BasicTokenNoPrefix::CharAmpersand,
353            '\'' => BasicTokenNoPrefix::CharSingleQuote,
354            '(' => BasicTokenNoPrefix::CharOpenParenthesis,
355            ')' => BasicTokenNoPrefix::CharCloseParenthesis,
356            '*' => BasicTokenNoPrefix::CharAsterix,
357            '+' => BasicTokenNoPrefix::CharPlus,
358            ',' => BasicTokenNoPrefix::CharComma,
359            '_' => BasicTokenNoPrefix::CharHyphen,
360            '.' => BasicTokenNoPrefix::CharDot,
361            '/' => BasicTokenNoPrefix::CharSlash,
362            '0' => BasicTokenNoPrefix::Char0,
363            '1' => BasicTokenNoPrefix::Char1,
364            '2' => BasicTokenNoPrefix::Char2,
365            '3' => BasicTokenNoPrefix::Char3,
366            '4' => BasicTokenNoPrefix::Char4,
367            '5' => BasicTokenNoPrefix::Char5,
368            '6' => BasicTokenNoPrefix::Char6,
369            '7' => BasicTokenNoPrefix::Char7,
370            '8' => BasicTokenNoPrefix::Char8,
371            '9' => BasicTokenNoPrefix::Char9,
372            ':' => BasicTokenNoPrefix::CharColon,
373            ';' => BasicTokenNoPrefix::CharSemiColon,
374            '<' => BasicTokenNoPrefix::CharLess,
375            '=' => BasicTokenNoPrefix::CharEquals,
376            '>' => BasicTokenNoPrefix::CharGreater,
377            '?' => BasicTokenNoPrefix::CharQuestionMark,
378            '@' => BasicTokenNoPrefix::CharAt,
379
380            '\t' => BasicTokenNoPrefix::CharTab,
381
382            _ => unimplemented!("'{}'", c)
383        }
384    }
385}
386
387impl fmt::Display for BasicTokenNoPrefix {
388    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
389        match self {
390            Self::Call => write!(f, "CALL"),
391            Self::For => write!(f, "FOR"),
392            Self::Load => write!(f, "LOAD"),
393            Self::Memory => write!(f, "MEMORY"),
394            Self::Print => write!(f, "PRINT"),
395            Self::Rem => write!(f, "REM"),
396
397            Self::SymbolQuote => write!(f, "'"),
398            Self::StatementSeparator => write!(f, ":"),
399
400            Self::EndOfTokenisedLine => Ok(()),
401
402            _ => {
403                let c = (*self as u8) as char;
404                match c {
405                    ' '..='z' => write!(f, "{c}"),
406
407                    _ => unimplemented!("{:?}", self)
408                }
409            }
410        }
411    }
412}
413
414impl BasicTokenNoPrefix {
415    /// Returns the 8bit code that represents the token
416    pub fn value(self) -> u8 {
417        self.into()
418    }
419}
420
421impl TryFrom<u8> for BasicTokenPrefixed {
422    type Error = String;
423
424    fn try_from(value: u8) -> Result<Self, String> {
425        match value {
426            0x1E..0x40 | 0x50..0x71 | 0x80.. => Err(format!("{value} is invalid")),
427            _ => Ok(unsafe { std::mem::transmute(value) })
428        }
429    }
430}
431
432impl From<BasicTokenPrefixed> for u8 {
433    fn from(val: BasicTokenPrefixed) -> Self {
434        val as u8
435    }
436}
437
438#[derive(Copy, Clone, PartialEq, Debug)]
439#[repr(u8)]
440#[allow(missing_docs)]
441pub enum BasicTokenPrefixed {
442    Abs = 0,
443    Asc,
444    Atn,
445    ChrDollar,
446    Cint,
447    Cos,
448    Creal,
449    Exp,
450    Fix,
451    Fre,
452    Inkey,
453    Inp,
454    Int,
455    Joy,
456    Len,
457    Log,
458    Log10,
459    LowerDollar,
460    Peek,
461    Remain,
462    Sign,
463    Sin,
464    SpaceDollar,
465    Sq,
466    Sqr,
467    StrDollar,
468    Tan,
469    Unt,
470    UpperDollar,
471    Val = 0x1D,
472
473    Eof = 0x40,
474    Err,
475    Himem,
476    InkeyDollar,
477    Pi,
478    Rnd,
479    Time,
480    Xpos,
481    Ypos,
482    Derr = 0x49,
483
484    BinDollar = 0x71,
485    DecDollar,
486    HexDollar,
487    Instr,
488    LeftDollar,
489    Max,
490    Min,
491    Pos,
492    RightDollar,
493    Round,
494    StringDollar,
495    Test,
496    Teststr,
497    CopycharDollar,
498    Vpos = 0x7F
499}
500
501impl fmt::Display for BasicTokenPrefixed {
502    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
503        let tag = match self {
504            Self::Abs => "ABS",
505            _ => unimplemented!("{}", self)
506        };
507        write!(f, "{tag}")
508    }
509}
510
511// impl From<u8> for BasicTokenPrefixed {
512// fn from(val: u8) -> BasicTokenPrefixed {
513// val.try_into().unwrap()
514// }
515// }
516
517impl BasicTokenPrefixed {
518    /// Returns the 8bits code that represents the prefixed token
519    pub fn value(self) -> u8 {
520        self.into()
521    }
522}
523
524#[derive(Debug, Clone, PartialEq)]
525/// Encode a Basic value
526pub enum BasicValue {
527    /// 16bits integer value in saved order
528    Integer(u8, u8),
529    /// 5bytes float value in saved order
530    Float(u8, u8, u8, u8, u8),
531    /// String
532    String(String)
533}
534
535#[allow(missing_docs)]
536impl BasicValue {
537    pub fn new_integer(word: i16) -> Self {
538        let word: u16 = unsafe { i16::cast_unsigned(word) };
539        BasicValue::Integer((word % 256) as u8, (word / 256) as u8)
540    }
541
542    pub fn new_integer_by_bytes(low: u8, high: u8) -> Self {
543        BasicValue::Integer(low, high)
544    }
545
546    pub fn new_string(_value: &str) -> Self {
547        unimplemented!()
548    }
549
550    pub fn new_float(_value: i32) -> Self {
551        unimplemented!()
552    }
553
554    pub fn as_bytes(&self) -> Vec<u8> {
555        match self {
556            Self::Integer(low, high) => vec![*low, *high],
557            _ => unimplemented!()
558        }
559    }
560
561    /// Return the integer value when it is an integer
562    pub fn as_integer(&self) -> Option<u16> {
563        match self {
564            Self::Integer(low, high) => Some(u16::from(*low) + 256 * u16::from(*high)),
565            _ => None
566        }
567    }
568
569    pub fn int_hexdecimal_representation(&self) -> Option<String> {
570        self.as_integer().map(|i| format!("&{i:X}"))
571    }
572
573    pub fn int_decimal_representation(&self) -> Option<String> {
574        self.as_integer().map(|i| format!("{i}"))
575    }
576}
577
578/// Represents any kind of token
579#[derive(Debug, Clone, PartialEq)]
580pub enum BasicToken {
581    /// Simple tokens.
582    SimpleToken(BasicTokenNoPrefix),
583    /// Tokens prefixed by 0xff
584    PrefixedToken(BasicTokenPrefixed),
585    /// Encode a RSX call
586    Rsx(String),
587    /// Encode a variable set
588    Variable(String, BasicValue),
589    /// Encode a constant. The first field can only take ValueIntegerDecimal8bits, ValueIntegerDecimal16bits, ValueIntegerBinary16bits, ValueIntegerHexadecimal16bits
590    Constant(BasicTokenNoPrefix, BasicValue),
591    /// Encode a comment
592    Comment(BasicTokenNoPrefix, Vec<u8>)
593}
594
595impl fmt::Display for BasicToken {
596    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
597        match self {
598            BasicToken::SimpleToken(tok) => {
599                write!(f, "{tok}")?;
600            },
601            BasicToken::PrefixedToken(tok) => {
602                write!(f, "{tok}")?;
603            },
604            BasicToken::Comment(tok, comment) => {
605                write!(f, "{tok}")?;
606                write!(f, "{},", String::from_utf8(comment.to_vec()).unwrap())?;
607            },
608            BasicToken::Constant(kind, constant) => {
609                let repr = match kind {
610                    BasicTokenNoPrefix::ValueIntegerHexadecimal16bits => {
611                        constant.int_hexdecimal_representation().unwrap()
612                    },
613                    BasicTokenNoPrefix::ValueIntegerDecimal16bits => {
614                        constant.int_decimal_representation().unwrap()
615                    },
616                    _ => unimplemented!("{:?}", kind)
617                };
618                write!(f, "{repr}")?;
619            },
620            _ => unimplemented!("{:?}", self)
621        }
622
623        Ok(())
624    }
625}
626
627#[allow(missing_docs)]
628impl BasicToken {
629    pub fn as_bytes(&self) -> Vec<u8> {
630        match self {
631            BasicToken::SimpleToken(tok) => vec![tok.value()],
632
633            BasicToken::PrefixedToken(tok) => {
634                vec![
635                    BasicTokenNoPrefix::AdditionalTokenMarker.value(),
636                    tok.value(),
637                ]
638            },
639
640            BasicToken::Rsx(_name) => {
641                let encoded_name = self.rsx_encoded_name().unwrap();
642                let mut data = vec![BasicTokenNoPrefix::Pipe.value(), encoded_name.len() as u8];
643                data.extend_from_slice(&encoded_name);
644                data
645            },
646
647            BasicToken::Constant(kind, constant) => {
648                let mut data = vec![kind.value()];
649                data.extend_from_slice(&constant.as_bytes());
650                data
651            },
652
653            BasicToken::Comment(comment_type, comment) => {
654                let mut data = vec![comment_type.value()];
655                data.extend_from_slice(comment);
656                data
657            },
658
659            _ => unimplemented!()
660        }
661    }
662
663    /// Returns the encoded version of the rsx name (bit 7 to 1 of last char)
664    pub fn rsx_encoded_name(&self) -> Option<Vec<u8>> {
665        match self {
666            BasicToken::Rsx(name) => Some(Self::encode_string(name)),
667            _ => None
668        }
669    }
670
671    pub fn variable_encoded_name(&self) -> Option<Vec<u8>> {
672        match self {
673            BasicToken::Variable(name, _) => Some(Self::encode_string(name)),
674            _ => None
675        }
676    }
677
678    /// Encode a string by setting the bit 7 of last char. Returns a vector of bytes.
679    fn encode_string(name: &str) -> Vec<u8> {
680        let mut copy = name.as_bytes().to_vec();
681        copy.pop(); // Remove \0
682        if let Some(c) = copy.last_mut() {
683            *c += 0b1000_0000; // Set bit 7 to last char
684        }
685        copy
686    }
687}
688
689#[cfg(test)]
690mod test {
691    use std::convert::TryInto;
692
693    use crate::tokens::*;
694
695    #[test]
696    fn test_conversion() {
697        assert_eq!(BasicTokenNoPrefix::Pipe.value(), 0x7C);
698        assert_eq!(BasicTokenNoPrefix::After.value(), 0x80);
699
700        assert_eq!(BasicTokenNoPrefix::Goto.value(), 0xA0);
701
702        assert_eq!(BasicTokenNoPrefix::SymbolQuote.value(), 0xC0);
703
704        assert_eq!(BasicTokenNoPrefix::Frame.value(), 0xE0);
705
706        assert_eq!(BasicTokenNoPrefix::GreaterOrEqual.value(), 0xF0);
707
708        assert_eq!(BasicTokenNoPrefix::Division.value(), 0xF7);
709
710        let token: BasicTokenNoPrefix = 0xF7.try_into().unwrap();
711        assert_eq!(token, BasicTokenNoPrefix::Division);
712    }
713}