#计算器 #解释器 #数学表达式 #解析器

mate-rs

一个简单轻量级的算术表达式解释器

5 个版本

0.1.4 2022年11月2日
0.1.3 2022年9月28日
0.1.2 2022年9月25日
0.1.1 2022年9月23日
0.1.0 2022年9月19日

#311数学 类别中

自定义许可

110KB
2K SLoC

Package Logo
License: MIT

Mate 是一个用于解析和计算以字符串形式输入的算术表达式的库。使用类似于解释型编程语言的词法分析器结构来解析字符串输入到令牌列表,并使用计算器结构从令牌列表计算最终结果。还实现了一个通用的包装结构,在其中实现了词法分析和计算器。这使得直接计算算术表达式的结果变得简单,无需手动处理解析和计算。

用法

该制品在 crates.io 上,可以通过将 mate-rs 添加到您项目中的 Cargo.toml 依赖项中来使用。

[dependencies]
mate-rs = "0.1.4"

示例:使用 Mate

MateLexerCalculator 的一般包装结构。它只有一个方法,用于通过字符串输入(&str)来计算结果。

use mate_rs::mate::Mate;

let result = Mate::calculate("6 * 7");
match result {
    Ok(v) => assert_eq!(v, 42.0),
    Err(_) => {
        // Do something ...
    }
};

示例:使用 LexerCalculator

Lexer 是主要结构,用于将字符串输入解析为令牌列表。Calculator 是用于通过 Lexer 的结果计算最终结果的结构。

use mate_rs::{calculator::Calculator, lexer::Lexer};

// Generated tokens gonna be something like:
//  |
//  | Token(
//  |   type: SUBEXP,
//  |   tokens: [
//  |        Token(
//  |          type: SUBEXP,
//  |          tokens: [
//  |               Token(type: NUMBER,  literal: "2")
//  |               Token(type: PLUS,    literal: "+")
//  |               Token(type: NUMBER,  literal: "5")
//  |          ],
//  |        ),
//  |        Token(type: PRODUCT, literal: "*"),
//  |        Token(
//  |          type: SUBEXP,
//  |          tokens: [
//  |               Token(type: NUMBER,  literal: "5")
//  |               Token(type: MINUS,   literal: "-")
//  |               Token(type: NUMBER,  literal: "9")
//  |               Token(type: PLUS,    literal: "+")
//  |               Token(
//  |                 type: SUBEXP,
//  |                 tokens: [
//  |                      Token(type: NUMBER,  literal: "8")
//  |                      Token(type: PLUS,    literal: "-")
//  |                      Token(type: NUMBER,  literal: "5")
//  |                 ],
//  |               ),
//  |          ],
//  |        ),
//  |   ],
//  | ),
//  | Token(type: PLUS,    literal: "+")
//  | Token(type: NUMBER,  literal: "35")
//  |
let input = "[ (2 + 5) * (5 - 9 + (8 - 5)) ] + 35";
let tokens = Lexer::lex(input.clone()).unwrap(); // should handle error case also

// Result will be calculated from tokens, by X/O/Y algorithm.
//
//  ╭────────╮ ╭───────────╮ ╭────────╮
//  │ NUMBER │ │ OPERATION │ │ NUMBER │
//  ╰────────╯ ╰───────────╯ ╰────────╯
//       ╰───╮       │        ╭───╯
//           ▼       ▼        ▼
//           X  [+, -, *, /]  Y
//
let result = Calculator::calculate(tokens, input.clone());

工作原理

Mate 主要关注两个主要结构:[Lexer] 和 [Calculator]。[Lexer] 是负责解析给定字符串表达式的结构,[Calculator] 是负责通过解析后的令牌计算最终结果的结构。

词法分析器

遍历给定的输入字符串,读取并将每个字符转换为 [Token] 结构。我们有许多主要令牌类型,如下:

  • ILLEGAL - 非法字符。
  • NUMBER - 数字类型。
  • MINUSPLUSPRODUCTDIVIDEPERCENTAGEPOWER - 操作。
  • LPARENRPAREN - 括号。
  • LABSLPAREN - 绝对值。
  • SUBEXP - 子表达式,括号内、绝对值内或除法和乘法组合的表达式。

词法分析器中的 lex 功能将每个字符转换为这些标记之一。它将乘法或除法操作相关标记合并为一个子表达式,以保持操作优先级正确。并且使用自定义的级别到表达式的算法嵌套括号、绝对值和幂。

级别到表达式算法是一种映射算法,它将具体表达式映射到其嵌套级别。

例如,如果给定的标记列表是 -> (2 + 5) : (5 - 9 / (8 - 5))生成的结果将是:

mate

通过这样做,我们可以轻松地保持操作优先级的安全性。

计算器

计算器接收解析后的标记列表,并计算其最终结果。使用自定义的 X/O/Y 算法,也称为 X/OPERATION/Y,其中 XY 是数字,而 O 是操作。如果无法获取 XY,则将它们视为零。

╭────────╮ ╭───────────╮ ╭────────╮
│ NUMBER │ │ OPERATION │ │ NUMBER │
╰────────╯ ╰───────────╯ ╰────────╯
     ╰───╮       │        ╭───╯
         ▼       ▼        ▼
         X  [+, -, *, /]  Y

语法

Mate 的语法尽可能地简单和基本。它只是带有少量自定义的纯文本数学语法。让我们看看一些例子

加法和减法

2 + 52 - 5 是有效的语法。(在 x <operation> y 中,x 和 y 可以是 NUMBERSUBEXP)。

乘法、除法和百分比

4 * 24 / 24 % 2 是有效的语法。(在 x <operation> y 中,x 和 y 可以是 NUMBERSUBEXP)。此外,在 乘法 的情况下,4(10 / 2)4(2) 是有效的语法。

4 ^ 2 是有效的语法。(在 x <operation> y 中,x 和 y 可以是 NUMBERSUBEXP)。此外,连续的幂也是可接受的:4 ^ 2 ^ 3 将自动转换为 4 ^ (2 ^ 3)

括号

(2 * 5) 是有效的语法。 (在 x <operation> y 中,x 和 y 可以是 NUMBERSUBEXP)。另外,嵌套括号表达式也是可接受的:(2 * ((5 * 2) / (9 - 5)))

绝对值

[2 - 12] 是有效的语法。 (在 x <operation> y 中,x 和 y 可以是 NUMBERSUBEXP)。另外,嵌套绝对值表达式也是可接受的:[7 - 14] * [5 - 9 / [5 - 3]]


错误

这里不需要说明错误,以下是一些示例

unknown-char-error expected-an-token-error

依赖

~2.1–3MB
~53K SLoC