Skip to main content

mech_syntax/
mika.rs

1#[macro_use]
2use crate::*;
3
4#[cfg(not(feature = "no-std"))] use core::fmt;
5#[cfg(feature = "no-std")] use alloc::fmt;
6#[cfg(feature = "no-std")] use alloc::string::String;
7#[cfg(feature = "no-std")] use alloc::vec::Vec;
8use nom::{
9  IResult,
10  branch::alt,
11  sequence::{tuple as nom_tuple, pair},
12  combinator::{opt, eof, peek},
13  multi::{many1, many_till, many0, separated_list1,separated_list0},
14  bytes::complete::{take_until, take_while},
15  Err,
16  Err::Failure
17};
18
19use std::collections::HashMap;
20use colored::*;
21
22use crate::*;
23
24// Mika
25// ============================================================================
26
27pub static MICROMIKA_WAVE: &[&str] = &[
28  "╭◉╮", "╭◉─", "╭◉╯", "╭◉─", "╭◉╯", "╭◉─", "╭◉╮", " "
29];
30
31pub static MICROMIKA_SLEEP: &[&str] = &[
32  "╭◉╮", "╭⦾╮", "╭⊚╮", "╭⊙╮", "╭◯╮"
33];
34
35pub static MICROMIKA_WAKE: &[&str] = &[
36  "╭◯╮", "╭⊙╮", "╭⊚╮", "╭⦾╮", "╭◉╮"
37];
38
39pub static MICROMIKA_BLINK: &[&str] = &[
40  "╭◉╮", "╭⊖╮", "╭◉╮", "╭⊖╮", "╭◉╮", "╭◉╮", "╭◉╮", "╭◉╮"
41];
42
43pub static MICROMIKA_PULSE: &[&str] = &[
44  "╭◉╮", "╭⦾╮", "╭⊚╮", "╭⊙╮", "╭⊚╮", "╭⦾╮", "╭◉╮"
45];
46
47pub static MICROMIKA_RAISE: &[&str] = &[
48  "╭◉╮", "─◉─", "╰◉╯"
49];
50
51pub static MICROMIKA_FLAP: &[&str] = &[
52  "╭◉╮", "─◉─", "╰◉╯", "─◉─", "╭◉╮"
53];
54
55pub static MICROMIKA_ATTENTION: &[&str] = &[
56  "╭◉╯", "╭◉╯", "╭◉╯","╭◉╯","╭◉╯", "╭◉─", "╭◉╯", "╭◉─",
57];
58
59// Mika Section
60// ---------------------------------------------------------------------------
61
62// mika-speech-bubble := "⸢", +section-element, "⸥" ;
63pub fn mika_section(input: ParseString) -> ParseResult<MikaSection> {
64  let msg = "Expects ⸥ to close speech bubble";
65  let (input, (_, r)) = range(mika_section_open)(input)?;
66  let (input, elements) = section(input)?;
67  let (input, _) = label!(mika_section_close, msg, r)(input)?;
68  Ok((input, MikaSection { elements }))
69}
70
71// Face / Arm Primitives
72// ---------------------------------------------------------------------------
73
74// Longer multi-char tokens matched before their single-char prefixes.
75pub fn mika_arm_left(input: ParseString) -> ParseResult<MikaArm> {
76  let (input, tok) = alt((
77    tag("Ɔ∞"),  // BigGripperLeft
78    tag("›─"),  // GripperLeft
79    tag("›⌣"),  // GestureLeft (decorated)
80    tag("·¬"),  // ShootLeft
81    tag("-◡"),  // ShrugLeft
82    tag("ᗑ"),  // BatWing
83    tag("ᕦ"),  // CurlLeft
84    tag("~"),  // Dance
85    tag("⌣"),  // GestureLeft (bare)
86    tag("╭"),  // Left
87    tag("⸌"),  // RaisedLeft
88    tag("⸸"),  // Sword
89    tag("─"),  // Point
90    tag("ᓂ"),  // PunchLeft
91    tag("ᓇ"),  // PunchLowLeft
92    tag("╰"),  // UpLeft
93  ))(input)?;
94  let arm = match tok.chars().collect::<String>().as_str() {
95    "ᗑ"        => MikaArm::BatWing,
96    "Ɔ∞"       => MikaArm::BigGripperLeft,
97    "ᕦ"        => MikaArm::CurlLeft,
98    "~"         => MikaArm::Dance,
99    "›⌣" | "⌣" => MikaArm::GestureLeft,
100    "›─"        => MikaArm::GripperLeft,
101    "╭"         => MikaArm::Left,
102    "⸌"         => MikaArm::RaisedLeft,
103    "·¬"        => MikaArm::ShootLeft,
104    "-◡"        => MikaArm::ShrugLeft,
105    "⸸"         => MikaArm::Sword,
106    "─"         => MikaArm::Point,
107    "ᓂ"         => MikaArm::PunchLeft,
108    "ᓇ"         => MikaArm::PunchLowLeft,
109    "╰"         => MikaArm::UpLeft,
110    _ => unreachable!(),
111  };
112  Ok((input, arm))
113}
114
115pub fn mika_arm_right(input: ParseString) -> ParseResult<MikaArm> {
116  let (input, tok) = alt((
117    tag("∞C"),  // BigGripperRight
118    tag("─‹"),  // GripperRight
119    tag("⌣‹"),  // GestureRight (decorated)
120    tag("⌐·"),  // ShootRight
121    tag("◡-"),  // ShrugRight
122    tag("ᗑ"),  // BatWing
123    tag("ᕤ"),  // CurlRight
124    tag("~"),   // Dance
125    tag("⌣"),  // GestureRight (bare)
126    tag("╮"),  // Right
127    tag("⸍"),  // RaisedRight
128    tag("ᗢ"),  // Shield
129    tag("─"),  // Point  (NB: also used as left Point; position in the grammar disambiguates)
130    tag("ᓀ"),  // PunchRight
131    tag("ᓄ"),  // PunchLowRight
132    tag("╯"),  // UpRight
133  ))(input)?;
134  let arm = match tok.chars().collect::<String>().as_str() {
135    "ᗑ"         => MikaArm::BatWing,
136    "∞C"        => MikaArm::BigGripperRight,
137    "ᕤ"         => MikaArm::CurlRight,
138    "~"          => MikaArm::Dance,
139    "⌣‹" | "⌣"  => MikaArm::GestureRight,
140    "─‹"         => MikaArm::GripperRight,
141    "╮"          => MikaArm::Right,
142    "⸍"          => MikaArm::RaisedRight,
143    "⌐·"         => MikaArm::ShootRight,
144    "◡-"         => MikaArm::ShrugRight,
145    "ᗢ"          => MikaArm::Shield,
146    "─"          => MikaArm::Point,
147    "ᓀ"          => MikaArm::PunchRight,
148    "ᓄ"          => MikaArm::PunchLowRight,
149    "╯"          => MikaArm::UpRight,
150    _ => unreachable!(),
151  };
152  Ok((input, arm))
153}
154
155// Expression Primitives
156// ---------------------------------------------------------------------------
157
158// The order matters — longer tags must be tried before any of their prefixes.
159// MikaEyeLeft::Shades is "⌐▰" (two chars) and must precede all single-char tags.
160const LEFT_EYE_ORDER: &[MikaEyeLeft] = &[
161  MikaEyeLeft::Shades,        // "⌐▰"  — multi-char, must be first
162  MikaEyeLeft::Content,       // "ˆ"
163  MikaEyeLeft::Confused,      // "ಠ"
164  MikaEyeLeft::Crying,        // "╥"
165  MikaEyeLeft::Dazed,         // "⋇"
166  MikaEyeLeft::Dead,          // "✖"
167  MikaEyeLeft::EyesSqueezed,  // "≻"
168  MikaEyeLeft::SuperSqueezed, // "ᗒ"
169  MikaEyeLeft::Glaring,       // "ㆆ"
170  MikaEyeLeft::Happy,         // "◜"
171  MikaEyeLeft::Normal,        // "˙"
172  MikaEyeLeft::PeerRight,     // "⚆"
173  MikaEyeLeft::PeerStraight,  // "☉"
174  MikaEyeLeft::Pleased,       // "◠"
175  MikaEyeLeft::Resolved,      // "◡̀"
176  MikaEyeLeft::RollingEyes,   // "◕"
177  MikaEyeLeft::Sad,            // "◞"
178  MikaEyeLeft::Scared,        // "Ͼ"
179  MikaEyeLeft::Sleeping,      // "⹇"
180  MikaEyeLeft::Smiling,       // "ᗣ"
181  MikaEyeLeft::Squinting,     // "≖"
182  MikaEyeLeft::Surprised,     // "°"
183  MikaEyeLeft::TearingUp,     // "ᗩ"
184  MikaEyeLeft::Unimpressed,   // "¬"
185  MikaEyeLeft::Wired,         // "◉"
186];
187
188const RIGHT_EYE_ORDER: &[MikaEyeRight] = &[
189  MikaEyeRight::Content,
190  MikaEyeRight::Confused,
191  MikaEyeRight::Crying,
192  MikaEyeRight::Dazed,
193  MikaEyeRight::Dead,
194  MikaEyeRight::EyesSqueezed,
195  MikaEyeRight::SuperSqueezed,
196  MikaEyeRight::Glaring,
197  MikaEyeRight::Happy,
198  MikaEyeRight::Normal,
199  MikaEyeRight::PeerRight,
200  MikaEyeRight::PeerStraight,
201  MikaEyeRight::Pleased,
202  MikaEyeRight::Resolved,
203  MikaEyeRight::RollingEyes,
204  MikaEyeRight::Sad,
205  MikaEyeRight::Scared,
206  MikaEyeRight::Shades,       // "▰"
207  MikaEyeRight::Sleeping,
208  MikaEyeRight::Smiling,
209  MikaEyeRight::Squinting,
210  MikaEyeRight::Surprised,
211  MikaEyeRight::TearingUp,
212  MikaEyeRight::Unimpressed,
213  MikaEyeRight::Wired,
214];
215
216const NOSE_ORDER: &[MikaNose] = &[
217  MikaNose::Normal,
218  MikaNose::Open,
219  MikaNose::Back,
220  MikaNose::Stage1,
221  MikaNose::Stage2,
222  MikaNose::Stage3,
223  MikaNose::Blink,
224  MikaNose::Wide,
225  MikaNose::Error,
226  MikaNose::Filled,
227  MikaNose::FlatMouth,
228  MikaNose::Hexagon,
229  MikaNose::Pentagon,
230  MikaNose::Hexagon2,
231  MikaNose::HexagonOpen,
232];
233
234// All known Mika expressions — used by mika_expression_inner to resolve
235// (left_eye, nose) -> MikaExpression and then consume the expected right eye.
236const EXPRESSIONS: &[MikaExpression] = &[
237  MikaExpression::Content,
238  MikaExpression::Confused,
239  MikaExpression::Crying,
240  MikaExpression::Dazed,
241  MikaExpression::Dead,
242  MikaExpression::EyesSqueezed,
243  MikaExpression::SuperSqueezed,
244  MikaExpression::Glaring,
245  MikaExpression::Happy,
246  MikaExpression::Normal,
247  MikaExpression::PeerRight,
248  MikaExpression::PeerStraight,
249  MikaExpression::Pleased,
250  MikaExpression::Resolved,
251  MikaExpression::RollingEyes,
252  MikaExpression::Sad,
253  MikaExpression::Scared,
254  MikaExpression::Shades,
255  MikaExpression::Sleeping,
256  MikaExpression::Smiling,
257  MikaExpression::Squinting,
258  MikaExpression::Surprised,
259  MikaExpression::TearingUp,
260  MikaExpression::Unimpressed,
261  MikaExpression::Wired,
262];
263
264pub fn mika_eye_left(input: ParseString) -> ParseResult<MikaEyeLeft> {
265  for &variant in LEFT_EYE_ORDER {
266    if let Ok((rest, _)) = tag(variant.symbol())(input.clone()) {
267      return Ok((rest, variant));
268    }
269  }
270  Err(nom::Err::Error(ParseError {
271    cause_range: SourceRange::default(),
272    remaining_input: input,
273    error_detail: ParseErrorDetail {
274      message: "Expected Mika left eye",
275      annotation_rngs: Vec::new(),
276    },
277  }))
278}
279
280pub fn mika_eye_right(input: ParseString) -> ParseResult<MikaEyeRight> {
281  for &variant in RIGHT_EYE_ORDER {
282    if let Ok((rest, _)) = tag(variant.symbol())(input.clone()) {
283      return Ok((rest, variant));
284    }
285  }
286  Err(nom::Err::Error(ParseError {
287    cause_range: SourceRange::default(),
288    remaining_input: input,
289    error_detail: ParseErrorDetail {
290      message: "Expected Mika right eye",
291      annotation_rngs: Vec::new(),
292    },
293  }))
294}
295
296// mika-nose := "⦿" | "◯" | "⊕" | "∘" | "⦾" | "⊖" | "⦵" | "⊗" | "⏺" | "⍜" ;
297pub fn mika_nose(input: ParseString) -> ParseResult<MikaNose> {
298  for &variant in NOSE_ORDER {
299    if let Ok((rest, _)) = tag(variant.symbol())(input.clone()) {
300      return Ok((rest, variant));
301    }
302  }
303  Err(nom::Err::Error(ParseError {
304    cause_range: SourceRange::default(),
305    remaining_input: input,
306    error_detail: ParseErrorDetail {
307      message: "Expected Mika nose",
308      annotation_rngs: Vec::new(),
309    },
310  }))
311}
312
313// mika-expression-inner := eye-left, nose, eye-right;
314pub fn mika_expression_inner(input: ParseString) -> ParseResult<MikaExpression> {
315  let (input, left) = mika_eye_left(input)?;
316  let (input, nose) = mika_nose(input)?;
317
318  let expr = EXPRESSIONS.iter().find(|&&e| {
319    let (l, n, _) = e.symbols();
320    l == left && n == nose
321  });
322
323  match expr {
324    Some(&expr) => {
325      let (_, _, right) = expr.symbols();
326      let (input, _) = tag(right.symbol())(input)?;
327      Ok((input, expr))
328    }
329    None => Err(nom::Err::Error(ParseError {
330      cause_range: SourceRange::default(),
331      remaining_input: input,
332      error_detail: ParseErrorDetail {
333        message: "Unrecognized Mika expression",
334        annotation_rngs: Vec::new(),
335      },
336    })),
337  }
338}
339
340// Mika Character
341// ---------------------------------------------------------------------------
342
343// micro-mika := arm-left, face, arm-right ;   e.g. ╭⦿╮  ╰⦿╯  ─⦿╮  ╭⦿⌣
344pub fn micro_mika(input: ParseString) -> ParseResult<Mika> {
345  let (input, left_arm)  = mika_arm_left(input)?;
346  let (input, nose)      = mika_nose(input)?;
347  let (input, right_arm) = mika_arm_right(input)?;
348  Ok((input, Mika::Micro(MicroMika{ left_arm, nose, right_arm})))
349}
350
351// mini-mika := arm-left, expression-inner, arm-right ;   e.g. ╭(˙◯˙)╮
352pub fn mini_mika(input: ParseString) -> ParseResult<Mika> {
353  let (input, left_arm)        = opt(mika_arm_left)(input)?;
354  let (input, _)               = left_parenthesis(input)?;
355  let (input, right_arm_left)  = opt(mika_arm_right)(input)?;
356  let (input, expression)      = mika_expression_inner(input)?;
357  let (input, left_arm_right)  = opt(mika_arm_left)(input)?;
358  let (input, _)               = right_parenthesis(input)?;
359  let (input, right_arm)       = opt(mika_arm_right)(input)?;
360  Ok((input, Mika::Mini(MiniMika { expression, left_arm: left_arm.or(right_arm_left), right_arm: right_arm.or(left_arm_right) })))
361}
362
363// mika := mini-mika | micro-mika ;
364pub fn mika(input: ParseString) -> ParseResult<(Mika,Option<MikaSection>)> {
365  let (input, mika) = alt((mini_mika, micro_mika))(input)?;
366  let (input, _) = whitespace0(input)?;
367  let (input, mika_section) = opt(mika_section)(input)?;
368  let (input, _) = whitespace0(input)?;
369  Ok((input, (mika, mika_section)))
370}