Krolyxon
Commit 86cbab6

Branches

Tags

Refs →

bench51cratesbench51-coresrcasmexpr.rs

251 lines · 7.5 KB · Rust

1use super::lexer::{Tok, Token};
2
3#[derive(Debug, Clone)]
4pub enum Expr {
5 Num(i64),
6 Sym(String, usize),
7 /// `$`, the current location counter.
8 Here,
9 Neg(Box<Expr>),
10 Not(Box<Expr>),
11 High(Box<Expr>),
12 Low(Box<Expr>),
13 Bin(BinOp, Box<Expr>, Box<Expr>),
14}
15
16#[derive(Debug, Clone, Copy)]
17pub enum BinOp {
18 Add,
19 Sub,
20 Mul,
21 Div,
22 Mod,
23 And,
24 Or,
25 Xor,
26 Shl,
27 Shr,
28 /// `byte.bit` bit addressing, e.g. `P1.3` or `20h.0`.
29 Dot,
30}
31
32pub enum EvalError {
33 Undefined(String, usize),
34 Other(String),
35}
36
37pub trait Env {
38 fn lookup(&self, name: &str) -> Option<i64>;
39 fn here(&self) -> i64;
40}
41
42impl Expr {
43 pub fn eval(&self, env: &dyn Env) -> Result<i64, EvalError> {
44 Ok(match self {
45 Expr::Num(n) => *n,
46 Expr::Here => env.here(),
47 Expr::Sym(s, col) => env
48 .lookup(s)
49 .ok_or_else(|| EvalError::Undefined(s.clone(), *col))?,
50 Expr::Neg(e) => e.eval(env)?.wrapping_neg(),
51 Expr::Not(e) => !e.eval(env)? & 0xFFFF,
52 Expr::High(e) => (e.eval(env)? >> 8) & 0xFF,
53 Expr::Low(e) => e.eval(env)? & 0xFF,
54 Expr::Bin(op, l, r) => {
55 let (l, r) = (l.eval(env)?, r.eval(env)?);
56 match op {
57 BinOp::Add => l.wrapping_add(r),
58 BinOp::Sub => l.wrapping_sub(r),
59 BinOp::Mul => l.wrapping_mul(r),
60 BinOp::Div | BinOp::Mod if r == 0 => {
61 return Err(EvalError::Other("division by zero".into()));
62 }
63 BinOp::Div => l / r,
64 BinOp::Mod => l % r,
65 BinOp::And => l & r,
66 BinOp::Or => l | r,
67 BinOp::Xor => l ^ r,
68 BinOp::Shl => l.wrapping_shl(r as u32),
69 BinOp::Shr => l.wrapping_shr(r as u32),
70 BinOp::Dot => bit_address(l, r)?,
71 }
72 }
73 })
74 }
75}
76
77fn bit_address(byte: i64, bit: i64) -> Result<i64, EvalError> {
78 if !(0..8).contains(&bit) {
79 return Err(EvalError::Other(format!(
80 "bit index {bit} out of range 0..7"
81 )));
82 }
83 match byte {
84 0x20..=0x2F => Ok((byte - 0x20) * 8 + bit),
85 0x80..=0xFF if byte % 8 == 0 => Ok(byte + bit),
86 _ => Err(EvalError::Other(format!(
87 "address {byte:02X}h is not bit-addressable"
88 ))),
89 }
90}
91
92/// Recursive-descent parser over a token slice.
93pub struct Parser<'a> {
94 toks: &'a [Token],
95 pos: usize,
96}
97
98type PResult<T> = Result<T, (usize, String)>;
99
100impl<'a> Parser<'a> {
101 pub fn new(toks: &'a [Token]) -> Self {
102 Self { toks, pos: 0 }
103 }
104
105 /// Parses the whole slice as one expression.
106 pub fn parse_all(toks: &'a [Token]) -> PResult<Expr> {
107 let mut p = Self::new(toks);
108 let e = p.expr()?;
109 if let Some(t) = p.toks.get(p.pos) {
110 return Err((t.col, "unexpected token in expression".into()));
111 }
112 Ok(e)
113 }
114
115 fn peek(&self) -> Option<&Tok> {
116 self.toks.get(self.pos).map(|t| &t.tok)
117 }
118
119 fn peek_word(&self, w: &str) -> bool {
120 matches!(self.peek(), Some(Tok::Ident(s)) if s == w)
121 }
122
123 fn col(&self) -> usize {
124 self.toks
125 .get(self.pos)
126 .or(self.toks.last())
127 .map_or(0, |t| t.col)
128 }
129
130 fn expr(&mut self) -> PResult<Expr> {
131 let mut l = self.xor()?;
132 while self.peek_word("OR") || self.peek() == Some(&Tok::Punct('|')) {
133 self.pos += 1;
134 l = Expr::Bin(BinOp::Or, Box::new(l), Box::new(self.xor()?));
135 }
136 Ok(l)
137 }
138
139 fn xor(&mut self) -> PResult<Expr> {
140 let mut l = self.and()?;
141 while self.peek_word("XOR") || self.peek() == Some(&Tok::Punct('^')) {
142 self.pos += 1;
143 l = Expr::Bin(BinOp::Xor, Box::new(l), Box::new(self.and()?));
144 }
145 Ok(l)
146 }
147
148 fn and(&mut self) -> PResult<Expr> {
149 let mut l = self.shift()?;
150 while self.peek_word("AND") || self.peek() == Some(&Tok::Punct('&')) {
151 self.pos += 1;
152 l = Expr::Bin(BinOp::And, Box::new(l), Box::new(self.shift()?));
153 }
154 Ok(l)
155 }
156
157 fn shift(&mut self) -> PResult<Expr> {
158 let mut l = self.add()?;
159 loop {
160 let op = match self.peek() {
161 Some(Tok::Shl) => BinOp::Shl,
162 Some(Tok::Shr) => BinOp::Shr,
163 Some(Tok::Ident(s)) if s == "SHL" => BinOp::Shl,
164 Some(Tok::Ident(s)) if s == "SHR" => BinOp::Shr,
165 _ => return Ok(l),
166 };
167 self.pos += 1;
168 l = Expr::Bin(op, Box::new(l), Box::new(self.add()?));
169 }
170 }
171
172 fn add(&mut self) -> PResult<Expr> {
173 let mut l = self.mul()?;
174 loop {
175 let op = match self.peek() {
176 Some(Tok::Punct('+')) => BinOp::Add,
177 Some(Tok::Punct('-')) => BinOp::Sub,
178 _ => return Ok(l),
179 };
180 self.pos += 1;
181 l = Expr::Bin(op, Box::new(l), Box::new(self.mul()?));
182 }
183 }
184
185 fn mul(&mut self) -> PResult<Expr> {
186 let mut l = self.unary()?;
187 loop {
188 let op = match self.peek() {
189 Some(Tok::Punct('*')) => BinOp::Mul,
190 Some(Tok::Punct('/')) => BinOp::Div,
191 Some(Tok::Punct('%')) => BinOp::Mod,
192 Some(Tok::Ident(s)) if s == "MOD" => BinOp::Mod,
193 _ => return Ok(l),
194 };
195 self.pos += 1;
196 l = Expr::Bin(op, Box::new(l), Box::new(self.unary()?));
197 }
198 }
199
200 fn unary(&mut self) -> PResult<Expr> {
201 let wrap: fn(Box<Expr>) -> Expr = match self.peek() {
202 Some(Tok::Punct('-')) => Expr::Neg,
203 Some(Tok::Punct('+')) => {
204 self.pos += 1;
205 return self.unary();
206 }
207 Some(Tok::Punct('~')) => Expr::Not,
208 Some(Tok::Ident(s)) if s == "NOT" => Expr::Not,
209 Some(Tok::Ident(s)) if s == "HIGH" => Expr::High,
210 Some(Tok::Ident(s)) if s == "LOW" => Expr::Low,
211 _ => return self.dot(),
212 };
213 self.pos += 1;
214 Ok(wrap(Box::new(self.unary()?)))
215 }
216
217 fn dot(&mut self) -> PResult<Expr> {
218 let l = self.primary()?;
219 if self.peek() == Some(&Tok::Punct('.')) {
220 self.pos += 1;
221 let r = self.primary()?;
222 return Ok(Expr::Bin(BinOp::Dot, Box::new(l), Box::new(r)));
223 }
224 Ok(l)
225 }
226
227 fn primary(&mut self) -> PResult<Expr> {
228 let col = self.col();
229 let Some(tok) = self.peek().cloned() else {
230 return Err((col, "expected expression".into()));
231 };
232 self.pos += 1;
233 match tok {
234 Tok::Num(n) => Ok(Expr::Num(n)),
235 Tok::Punct('$') => Ok(Expr::Here),
236 Tok::Ident(s) => Ok(Expr::Sym(s, col)),
237 Tok::Str(b) if b.len() == 1 => Ok(Expr::Num(b[0] as i64)),
238 Tok::Str(b) if b.len() == 2 => Ok(Expr::Num(((b[0] as i64) << 8) | b[1] as i64)),
239 Tok::Str(_) => Err((col, "string too long for an expression".into())),
240 Tok::Punct('(') => {
241 let e = self.expr()?;
242 if self.peek() != Some(&Tok::Punct(')')) {
243 return Err((self.col(), "expected ')'".into()));
244 }
245 self.pos += 1;
246 Ok(e)
247 }
248 _ => Err((col, "expected expression".into())),
249 }
250 }
251}