ilex 0.6.0

quick and easy lexers for C-like languages
Documentation
use ilex::fp::Fp64;
use ilex::rule::*;
use ilex::token;
use ilex::Lexeme;

#[ilex::spec]
struct Numbers {
  #[rule(",")]
  comma: Lexeme<Keyword>,

  #[named("binary number")]
  #[rule(Digital::new(2)
    .separator('_')
    .plus().minus()
    .point_limit(0..2)
    .exponent("2", Digits::new(2).plus().minus())
    .prefixes(["0b", "0B", "%"]))]
  bin: Lexeme<Digital>,

  #[named = "hexadecimal number"]
  #[rule(Digital::new(16)
    .separator('_')
    .plus().minus()
    .point_limit(0..2)
    .exponents(["p", "P"], Digits::new(10).plus().minus())
    .prefixes(["0x", "0X", "$"]))]
  hex: Lexeme<Digital>,

  #[named = "quaternary number"]
  #[rule(Digital::new(4)
    .separator('_')
    .plus().minus()
    .point_limit(0..2)
    .exponents(["p", "P"], Digits::new(10).plus().minus())
    .prefixes(["0q", "0Q"]))]
  qua: Lexeme<Digital>,

  #[named = "octal number"]
  #[rule(Digital::new(8)
    .separator('_')
    .plus().minus()
    .point_limit(0..2)
    .exponents(["p", "P"], Digits::new(10).plus().minus())
    .prefixes(["0o", "0O", "0"]))]
  oct: Lexeme<Digital>,

  #[named = "decimal number"]
  #[rule(Digital::new(10)
    .separator('_')
    .plus().minus()
    .point_limit(0..2)
    .exponents(["e", "E"], Digits::new(10).plus().minus())
    .exponent("^", Digits::new(16).plus().minus()))]
  dec: Lexeme<Digital>,
}

#[test]
fn numbers() {
  let lex = Numbers::get();
  let text = r#"
    0,
    -00,
    -0.0,
    123.456e78,
    9e9,
    -9e9,
    +9e+9,
    9e-9,
    -0777,
    0o777,
    %1210,
    0b0.0000000101,
    0o0.0024,
    0O1.01p01,
    0xfff.eep+10,
    $DEADBEEF,
    -0q0123.0123,
    3^a,
  "#;

  let ctx = ilex::Context::new();
  let _u = ctx.use_for_debugging_spans();
  let report = ctx.new_report();
  let tokens = ctx
    .new_file("test.file", text)
    .lex(lex.spec(), &report)
    .unwrap();
  eprintln!("stream: {tokens:#?}");

  let mut cursor = tokens.cursor();
  let numbers = cursor
    .delimited(lex.comma, |cursor| loop {
      let value = token::switch()
        .case(Lexeme::eof(), |_, _| Err(false))
        .cases([lex.dec, lex.bin, lex.oct, lex.hex, lex.qua], |num, _| {
          Ok(num.to_float::<Fp64>(.., &report).unwrap())
        })
        .take(cursor, &report);
      match value {
        None => {
          cursor.back_up(1);
          return Some(Fp64::nan());
        }
        Some(Err(false)) => return None,
        Some(Err(true)) => continue,
        Some(Ok(v)) => return Some(v),
      }
    })
    .map(|(v, _)| v)
    .collect::<Vec<_>>();
  cursor.expect_finished(&report);
  report.fatal_or(()).unwrap();

  assert_eq!(
    numbers,
    [
      "0",
      "-0",
      "-0",
      "123.456e78",
      "9e9",
      "-9e9",
      "9e9",
      "9e-9",
      "-511",
      "511",
      "4",
      "0.0048828125",
      "0.0048828125",
      "2.03125",
      "4194232",
      "3735928559",
      "-27.10546875",
      "3e10",
    ]
    .into_iter()
    .map(Fp64::new)
    .collect::<Vec<_>>()
  );
}