1use crate::unit::{self, Unit};
8use thiserror::Error;
9
10#[derive(Debug, Error, PartialEq, Eq)]
11pub enum ExprError {
12 #[error("예기치 못한 문자 {0:?} (위치 {1})")]
13 UnexpectedChar(char, usize),
14 #[error("예기치 못한 토큰: {0}")]
15 UnexpectedToken(String),
16 #[error("식이 끝나지 않음")]
17 UnexpectedEnd,
18 #[error("알 수 없는 변수 {0:?}")]
19 UnknownVar(String),
20 #[error("해석할 수 없는 낱자 operand {0:?}")]
21 BadUnit(String),
22 #[error("C0| operand 는 숫자여야 함: {0:?}")]
23 BadCommand(String),
24 #[error("정수가 아닌 값에 산술/비교 연산을 적용")]
25 NotInt,
26}
27
28#[derive(Clone, Copy, Debug, Default)]
32pub struct Ctx {
33 pub t: i64,
35 pub p: i64,
37 pub a: i64,
39 pub b: i64,
41 pub c: i64,
43 pub d: i64,
45 pub e: i64,
47 pub f: i64,
49 pub o: i64,
51}
52
53impl Ctx {
54 fn var(&self, name: &str) -> Option<i64> {
55 Some(match name {
56 "T" => self.t,
57 "P" => self.p,
58 "A" => self.a,
59 "B" => self.b,
60 "C" => self.c,
61 "D" => self.d,
62 "E" => self.e,
63 "F" => self.f,
64 "O" => self.o,
65 _ => return None,
66 })
67 }
68}
69
70#[derive(Clone, Copy, Debug, PartialEq, Eq)]
72pub enum Value {
73 Int(i64),
74 Unit(Unit),
75 Command(u32),
76}
77
78impl Value {
79 fn truthy(self) -> bool {
80 match self {
81 Value::Int(n) => n != 0,
82 _ => true,
84 }
85 }
86 fn as_int(self) -> Result<i64, ExprError> {
87 match self {
88 Value::Int(n) => Ok(n),
89 _ => Err(ExprError::NotInt),
90 }
91 }
92}
93
94#[derive(Clone, Debug, PartialEq, Eq)]
97pub enum Expr {
98 Int(i64),
99 Var(String),
100 Unit(Unit),
101 Command(u32),
102 Unary(UnOp, Box<Expr>),
103 Binary(BinOp, Box<Expr>, Box<Expr>),
104 Ternary(Box<Expr>, Box<Expr>, Box<Expr>),
105}
106
107#[derive(Clone, Copy, Debug, PartialEq, Eq)]
108pub enum UnOp {
109 Not,
110 Neg,
111 BitNot,
112}
113
114#[derive(Clone, Copy, Debug, PartialEq, Eq)]
115pub enum BinOp {
116 Or,
117 And,
118 BitOr,
119 BitXor,
120 BitAnd,
121 Eq,
122 Ne,
123 Lt,
124 Gt,
125 Le,
126 Ge,
127 Shl,
128 Shr,
129 Add,
130 Sub,
131 Mul,
132 Div,
133 Rem,
134}
135
136impl Expr {
137 pub fn parse(src: &str) -> Result<Expr, ExprError> {
139 let tokens = lex(src)?;
140 let mut p = Parser { tokens, pos: 0 };
141 let e = p.parse_ternary()?;
142 if p.pos != p.tokens.len() {
143 return Err(ExprError::UnexpectedToken(format!("{:?}", p.tokens[p.pos])));
144 }
145 Ok(e)
146 }
147
148 pub fn contains_unit(&self) -> bool {
151 match self {
152 Expr::Unit(_) => true,
153 Expr::Int(_) | Expr::Var(_) | Expr::Command(_) => false,
154 Expr::Unary(_, x) => x.contains_unit(),
155 Expr::Binary(_, a, b) => a.contains_unit() || b.contains_unit(),
156 Expr::Ternary(c, t, f) => c.contains_unit() || t.contains_unit() || f.contains_unit(),
157 }
158 }
159
160 pub fn eval(&self, ctx: &Ctx) -> Result<Value, ExprError> {
162 Ok(match self {
163 Expr::Int(n) => Value::Int(*n),
164 Expr::Unit(u) => Value::Unit(*u),
165 Expr::Command(c) => Value::Command(*c),
166 Expr::Var(name) => Value::Int(
167 ctx.var(name)
168 .ok_or_else(|| ExprError::UnknownVar(name.clone()))?,
169 ),
170 Expr::Unary(op, x) => {
171 let v = x.eval(ctx)?.as_int()?;
172 Value::Int(match op {
173 UnOp::Not => (v == 0) as i64,
174 UnOp::Neg => -v,
175 UnOp::BitNot => !v,
176 })
177 }
178 Expr::Ternary(cond, t, f) => {
179 if cond.eval(ctx)?.truthy() {
180 t.eval(ctx)?
181 } else {
182 f.eval(ctx)?
183 }
184 }
185 Expr::Binary(op, l, r) => {
186 match op {
188 BinOp::And => {
189 return Ok(Value::Int(
190 (l.eval(ctx)?.truthy() && r.eval(ctx)?.truthy()) as i64,
191 ))
192 }
193 BinOp::Or => {
194 return Ok(Value::Int(
195 (l.eval(ctx)?.truthy() || r.eval(ctx)?.truthy()) as i64,
196 ))
197 }
198 _ => {}
199 }
200 let a = l.eval(ctx)?.as_int()?;
201 let b = r.eval(ctx)?.as_int()?;
202 Value::Int(match op {
203 BinOp::BitOr => a | b,
204 BinOp::BitXor => a ^ b,
205 BinOp::BitAnd => a & b,
206 BinOp::Eq => (a == b) as i64,
207 BinOp::Ne => (a != b) as i64,
208 BinOp::Lt => (a < b) as i64,
209 BinOp::Gt => (a > b) as i64,
210 BinOp::Le => (a <= b) as i64,
211 BinOp::Ge => (a >= b) as i64,
212 BinOp::Shl => a << b,
213 BinOp::Shr => a >> b,
214 BinOp::Add => a + b,
215 BinOp::Sub => a - b,
216 BinOp::Mul => a * b,
217 BinOp::Div => a / b,
218 BinOp::Rem => a % b,
219 BinOp::And | BinOp::Or => unreachable!(),
220 })
221 }
222 })
223 }
224}
225
226pub fn eval_str(src: &str, ctx: &Ctx) -> Result<Value, ExprError> {
228 Expr::parse(src)?.eval(ctx)
229}
230
231#[derive(Clone, Debug, PartialEq, Eq)]
234enum Tok {
235 Num(i64),
236 Ident(String),
237 Pipe,
238 Question,
239 Colon,
240 OrOr,
241 AndAnd,
242 Caret,
243 Amp,
244 EqEq,
245 Ne,
246 Le,
247 Ge,
248 Shl,
249 Shr,
250 Lt,
251 Gt,
252 Plus,
253 Minus,
254 Star,
255 Slash,
256 Percent,
257 Bang,
258 Tilde,
259 LParen,
260 RParen,
261}
262
263fn lex(src: &str) -> Result<Vec<Tok>, ExprError> {
264 let chars: Vec<char> = src.chars().collect();
265 let mut i = 0;
266 let mut out = Vec::new();
267 while i < chars.len() {
268 let c = chars[i];
269 if c.is_whitespace() {
270 i += 1;
271 continue;
272 }
273 if c.is_ascii_digit() {
275 let start = i;
276 if c == '0' && i + 1 < chars.len() && (chars[i + 1] == 'x' || chars[i + 1] == 'X') {
277 i += 2;
278 while i < chars.len() && chars[i].is_ascii_hexdigit() {
279 i += 1;
280 }
281 let s: String = chars[start + 2..i].iter().collect();
282 let n = i64::from_str_radix(&s, 16)
283 .map_err(|_| ExprError::UnexpectedToken(s.clone()))?;
284 out.push(Tok::Num(n));
285 } else {
286 i += 1;
287 while i < chars.len() && chars[i].is_ascii_digit() {
288 i += 1;
289 }
290 let s: String = chars[start..i].iter().collect();
291 let n: i64 = s
292 .parse()
293 .map_err(|_| ExprError::UnexpectedToken(s.clone()))?;
294 out.push(Tok::Num(n));
295 }
296 continue;
297 }
298 if c.is_ascii_alphabetic() || c == '_' {
300 let start = i;
301 i += 1;
302 while i < chars.len() && (chars[i].is_ascii_alphanumeric() || chars[i] == '_') {
303 i += 1;
304 }
305 out.push(Tok::Ident(chars[start..i].iter().collect()));
306 continue;
307 }
308 let two = |a: char, b: char| i + 1 < chars.len() && chars[i] == a && chars[i + 1] == b;
310 let tok = if two('|', '|') {
311 i += 2;
312 Tok::OrOr
313 } else if two('&', '&') {
314 i += 2;
315 Tok::AndAnd
316 } else if two('=', '=') {
317 i += 2;
318 Tok::EqEq
319 } else if two('!', '=') {
320 i += 2;
321 Tok::Ne
322 } else if two('<', '=') {
323 i += 2;
324 Tok::Le
325 } else if two('>', '=') {
326 i += 2;
327 Tok::Ge
328 } else if two('<', '<') {
329 i += 2;
330 Tok::Shl
331 } else if two('>', '>') {
332 i += 2;
333 Tok::Shr
334 } else {
335 let t = match c {
336 '|' => Tok::Pipe,
337 '?' => Tok::Question,
338 ':' => Tok::Colon,
339 '^' => Tok::Caret,
340 '&' => Tok::Amp,
341 '<' => Tok::Lt,
342 '>' => Tok::Gt,
343 '+' => Tok::Plus,
344 '-' => Tok::Minus,
345 '*' => Tok::Star,
346 '/' => Tok::Slash,
347 '%' => Tok::Percent,
348 '!' => Tok::Bang,
349 '~' => Tok::Tilde,
350 '(' => Tok::LParen,
351 ')' => Tok::RParen,
352 other => return Err(ExprError::UnexpectedChar(other, i)),
353 };
354 i += 1;
355 t
356 };
357 out.push(tok);
358 }
359 Ok(out)
360}
361
362struct Parser {
365 tokens: Vec<Tok>,
366 pos: usize,
367}
368
369impl Parser {
370 fn peek(&self) -> Option<&Tok> {
371 self.tokens.get(self.pos)
372 }
373 fn bump(&mut self) -> Option<Tok> {
374 let t = self.tokens.get(self.pos).cloned();
375 if t.is_some() {
376 self.pos += 1;
377 }
378 t
379 }
380 fn expect(&mut self, t: &Tok) -> Result<(), ExprError> {
381 match self.bump() {
382 Some(ref got) if got == t => Ok(()),
383 Some(got) => Err(ExprError::UnexpectedToken(format!("{got:?}"))),
384 None => Err(ExprError::UnexpectedEnd),
385 }
386 }
387
388 fn parse_ternary(&mut self) -> Result<Expr, ExprError> {
390 let cond = self.parse_binary(0)?;
391 if let Some(Tok::Question) = self.peek() {
392 self.bump();
393 let then = self.parse_ternary()?;
394 self.expect(&Tok::Colon)?;
395 let els = self.parse_ternary()?;
396 Ok(Expr::Ternary(Box::new(cond), Box::new(then), Box::new(els)))
397 } else {
398 Ok(cond)
399 }
400 }
401
402 fn parse_binary(&mut self, min_prec: u8) -> Result<Expr, ExprError> {
404 let mut left = self.parse_unary()?;
405 while let Some(tok) = self.peek() {
406 let (op, prec) = match binop_of(tok) {
407 Some(v) => v,
408 None => break,
409 };
410 if prec < min_prec {
411 break;
412 }
413 self.bump();
414 let right = self.parse_binary(prec + 1)?;
416 left = Expr::Binary(op, Box::new(left), Box::new(right));
417 }
418 Ok(left)
419 }
420
421 fn parse_unary(&mut self) -> Result<Expr, ExprError> {
423 match self.peek() {
424 Some(Tok::Bang) => {
425 self.bump();
426 Ok(Expr::Unary(UnOp::Not, Box::new(self.parse_unary()?)))
427 }
428 Some(Tok::Minus) => {
429 self.bump();
430 Ok(Expr::Unary(UnOp::Neg, Box::new(self.parse_unary()?)))
431 }
432 Some(Tok::Tilde) => {
433 self.bump();
434 Ok(Expr::Unary(UnOp::BitNot, Box::new(self.parse_unary()?)))
435 }
436 _ => self.parse_primary(),
437 }
438 }
439
440 fn parse_primary(&mut self) -> Result<Expr, ExprError> {
441 match self.bump() {
442 Some(Tok::Num(n)) => Ok(Expr::Int(n)),
443 Some(Tok::LParen) => {
444 let e = self.parse_ternary()?;
445 self.expect(&Tok::RParen)?;
446 Ok(e)
447 }
448 Some(Tok::Ident(name)) => {
449 if (name == "H3" || name == "C0") && matches!(self.peek(), Some(Tok::Pipe)) {
451 self.bump(); self.parse_tagged(&name)
453 } else {
454 Ok(Expr::Var(name))
455 }
456 }
457 Some(other) => Err(ExprError::UnexpectedToken(format!("{other:?}"))),
458 None => Err(ExprError::UnexpectedEnd),
459 }
460 }
461
462 fn parse_tagged(&mut self, tag: &str) -> Result<Expr, ExprError> {
464 match self.bump() {
465 Some(Tok::Num(n)) => {
466 let n = n as u32;
467 if tag == "C0" {
468 Ok(Expr::Command(n))
469 } else {
470 let u = unit::resolve_numeric(n)
471 .ok_or_else(|| ExprError::BadUnit(format!("0x{n:X}")))?;
472 Ok(Expr::Unit(u))
473 }
474 }
475 Some(Tok::Ident(s)) => {
476 if tag == "C0" {
477 Err(ExprError::BadCommand(s))
478 } else {
479 let u = unit::resolve_mnemonic(&s, None).ok_or(ExprError::BadUnit(s))?;
480 Ok(Expr::Unit(u))
481 }
482 }
483 Some(other) => Err(ExprError::UnexpectedToken(format!("{other:?}"))),
484 None => Err(ExprError::UnexpectedEnd),
485 }
486 }
487}
488
489fn binop_of(t: &Tok) -> Option<(BinOp, u8)> {
491 Some(match t {
492 Tok::OrOr => (BinOp::Or, 1),
493 Tok::AndAnd => (BinOp::And, 2),
494 Tok::Pipe => (BinOp::BitOr, 3),
495 Tok::Caret => (BinOp::BitXor, 4),
496 Tok::Amp => (BinOp::BitAnd, 5),
497 Tok::EqEq => (BinOp::Eq, 6),
498 Tok::Ne => (BinOp::Ne, 6),
499 Tok::Lt => (BinOp::Lt, 7),
500 Tok::Gt => (BinOp::Gt, 7),
501 Tok::Le => (BinOp::Le, 7),
502 Tok::Ge => (BinOp::Ge, 7),
503 Tok::Shl => (BinOp::Shl, 8),
504 Tok::Shr => (BinOp::Shr, 8),
505 Tok::Plus => (BinOp::Add, 9),
506 Tok::Minus => (BinOp::Sub, 9),
507 Tok::Star => (BinOp::Mul, 10),
508 Tok::Slash => (BinOp::Div, 10),
509 Tok::Percent => (BinOp::Rem, 10),
510 _ => return None,
511 })
512}
513
514#[cfg(test)]
515mod tests {
516 use super::*;
517 use crate::unit::{Category, Jamo};
518
519 fn ev(src: &str, ctx: &Ctx) -> Value {
520 eval_str(src, ctx).unwrap_or_else(|e| panic!("eval {src:?}: {e}"))
521 }
522
523 #[test]
524 fn literals_and_arith() {
525 let ctx = Ctx::default();
526 assert_eq!(ev("0xB7", &ctx), Value::Int(0xB7));
527 assert_eq!(ev("500", &ctx), Value::Int(500));
528 assert_eq!(ev("-2", &ctx), Value::Int(-2));
529 let shifted = Ctx {
531 p: 1,
532 ..Default::default()
533 };
534 assert_eq!(ev("119^(P&1)<<5", &shifted), Value::Int('W' as i64));
535 let unshifted = Ctx {
536 p: 0,
537 ..Default::default()
538 };
539 assert_eq!(ev("119^(P&1)<<5", &unshifted), Value::Int('w' as i64));
540 }
541
542 #[test]
543 fn tagged_units() {
544 let ctx = Ctx::default();
545 assert_eq!(
547 ev("H3|_GG", &ctx),
548 Value::Unit(Unit::Jamo(Jamo::new(Category::Jong, 0x11A9)))
549 );
550 assert_eq!(
552 ev("H3|O_", &ctx),
553 Value::Unit(Unit::Jamo(Jamo::new(Category::Jung, 0x1169)))
554 );
555 assert_eq!(ev("H3|0x820000", &ctx), Value::Unit(Unit::Virtual(130)));
557 assert_eq!(ev("C0|0x82", &ctx), Value::Command(0x82));
559 }
560
561 #[test]
562 fn ternary_with_t() {
563 let composing = Ctx {
565 t: 1,
566 ..Default::default()
567 };
568 assert_eq!(
569 ev("T ? H3|_J : 0x23", &composing),
570 Value::Unit(Unit::Jamo(Jamo::new(Category::Jong, 0x11BD)))
571 );
572 let idle = Ctx {
573 t: 0,
574 ..Default::default()
575 };
576 assert_eq!(ev("T ? H3|_J : 0x23", &idle), Value::Int(0x23));
577 assert_eq!(
579 ev("T ? H3|0x1F4 : 0x24", &composing),
580 Value::Unit(Unit::Toggle)
581 );
582 assert_eq!(ev("T ? H3|0x1F4 : 0x24", &idle), Value::Int(0x24));
583 }
584
585 #[test]
586 fn automata_expr_parses_and_evaluates() {
587 let src = "A&&A!=500 ? 0 : B||C||A==500 ? 2 : -2";
589 assert_eq!(
591 ev(
592 src,
593 &Ctx {
594 a: 0x1100,
595 ..Default::default()
596 }
597 ),
598 Value::Int(0)
599 );
600 assert_eq!(
602 ev(
603 src,
604 &Ctx {
605 c: 0x11A8,
606 ..Default::default()
607 }
608 ),
609 Value::Int(2)
610 );
611 assert_eq!(
613 ev(
614 src,
615 &Ctx {
616 a: 500,
617 ..Default::default()
618 }
619 ),
620 Value::Int(2)
621 );
622 assert_eq!(ev(src, &Ctx::default()), Value::Int(-2));
624 }
625
626 #[test]
627 fn contains_unit_detects_hangul_keys() {
628 assert!(Expr::parse("H3|_GG").unwrap().contains_unit());
630 assert!(Expr::parse("T ? H3|_J : 0x23").unwrap().contains_unit());
631 assert!(!Expr::parse("119^(P&1)<<5").unwrap().contains_unit());
633 assert!(!Expr::parse("0x5B").unwrap().contains_unit());
634 assert!(!Expr::parse("C0|0x82").unwrap().contains_unit());
635 }
636
637 #[test]
638 fn precedence_sanity() {
639 let ctx = Ctx::default();
640 assert_eq!(ev("1+2*3", &ctx), Value::Int(7));
641 assert_eq!(ev("(1+2)*3", &ctx), Value::Int(9));
642 assert_eq!(ev("1<2 && 3>2", &ctx), Value::Int(1));
643 assert_eq!(ev("0 || 0 || 5", &ctx), Value::Int(1)); assert_eq!(ev("7 & 3", &ctx), Value::Int(3));
645 assert_eq!(ev("1 << 4", &ctx), Value::Int(16));
646 }
647}