首页 > 其他分享 >6.1 LALRPOP's lexer generator LALRPOP 的词法分析器生成器

6.1 LALRPOP's lexer generator LALRPOP 的词法分析器生成器

时间:2025-01-05 14:55:38浏览次数:8  
标签:匹配 22 generator 正则表达式 生成器 LALRPOP GPT match

This example dives a bit deeper into how LALRPOP works. In particular, it dives into the meaning of those strings and regular expression that we used in the previous tutorial, and how they are used to process the input string (a process which you can control). This first step of breaking up the input using regular expressions is often called lexing or tokenizing.

MST - 此示例更深入地探讨了 LALRPOP 的工作原理。特别是,它深入探讨了我们在上一个教程中使用的字符串和正则表达式的含义,以及如何使用它们来处理输入字符串(您可以控制的过程)。使用正则表达式分解输入的第一步通常称为词法或分词。

GPT - 这个示例深入探讨了 LALRPOP 的工作原理。特别是,它深入解释了我们在之前的教程中使用的那些字符串和正则表达式的含义,以及它们是如何用来处理输入字符串的(这个过程你可以控制)。这个将输入拆分成词法单元的第一步通常被称为词法分析或标记化。

If you're comfortable with the idea of a lexer or tokenizer, you may wish to skip ahead to the calculator3 example, which covers parsing bigger expressions, and come back here only when you find you want more control. You may also be interested in the tutorial on writing a custom lexer.

MST - 如果你对词法分析器或分词器的想法感到满意,你可能希望跳到 calculator3 示例,该示例涵盖了解析更大的表达式,并且仅在你发现需要更多控制时才返回这里。你可能还对编写自定义词法分析器的教程感兴趣。

GPT - 如果你对词法分析器或标记化器的概念已经比较熟悉,你可以跳过到 calculator3 示例,这里讲解了如何解析更复杂的表达式,只有当你需要更多控制时再回到这一部分。你也可能会对写自定义词法分析器的教程感兴趣。

Terminals vs nonterminals

You may have noticed that our grammar included two distinct kinds of symbols. There were the nonterminals, Term and Num, which we defined by specifying a series of symbols that they must match, along with some action code that should execute once they have matched:

MST - 你可能已经注意到,我们的语法包括两种不同的符号。有非终端 Term 和 Num,我们通过指定它们必须匹配的一系列符号来定义它们,以及一些一旦它们匹配就应该执行的操作代码:

GPT - 你可能已经注意到,我们的语法中包含了两种不同类型的符号。它们是非终结符 TermNum,我们通过指定一系列它们必须匹配的符号,并且附上匹配后应执行的一些操作代码来定义它们:

   Num: i32 = r"[0-9]+" => i32::from_str(<>).unwrap();
// ~~~  ~~~   ~~~~~~~~~    ~~~~~~~~~~~~~~~~~~~~~~~~~~
// |    |     |                Action code
// |    |     Symbol(s) that should match
// |    Return type
// Name of nonterminal

But there are also terminals, which consist of the string literals and regular expressions sprinkled throughout the grammar. (Terminals are also often called tokens, and I will use the terms interchangeably.)

MST - 但也有终端,它由散布在整个语法中的字符串 Literals 和正则表达式组成。(终端通常也称为令牌,我将互换使用这些术语。

GPT - 但也有终结符,它们由语法中散布的字符串字面量和正则表达式组成。(终结符也常被称为词法单元(tokens),我会交替使用这两个术语。)

This distinction between terminals and nonterminals is very important to how LALRPOP works. In fact, when LALRPOP generates a parser, it always works in a two-phase process. The first phase is called the lexer or tokenizer. It has the job of figuring out the sequence of terminals: so basically it analyzes the raw characters of your text and breaks them into a series of terminals. It does this without having any idea about your grammar or where you are in your grammar. Next, the parser proper is a bit of code that looks at this stream of tokens and figures out which nonterminals apply:

MST - 终端和非终端之间的这种区别对于 LALRPOP 的工作原理非常重要。事实上,当 LALRPOP 生成解析器时,它总是在一个两阶段的过程中工作。第一阶段称为词法分析器或分词器。它的工作是弄清楚终端的顺序:所以基本上它分析文本的原始字符并将它们分解成一系列终端。它这样做时不需要任何关于你的语法或你在语法中的位置。接下来,解析器本身是一段代码,它查看此令牌流并找出哪些非终端适用:

GPT - 终结符和非终结符之间的区分对 LALRPOP 的工作方式非常重要。事实上,当 LALRPOP 生成一个解析器时,它总是分为两个阶段。第一个阶段被称为词法分析器(lexer)或分词器(tokenizer)。它的任务是确定终结符的序列:基本上,它分析输入文本的原始字符,并将其分解为一系列终结符。它在这个过程中不需要知道你的语法或你在语法中的位置。接下来,真正的解析器是一些代码,它会查看这个终结符流,并弄清楚哪些非终结符适用:

        +-------------------+    +---------------------+
Text -> | Lexer             | -> | Parser              |
        |                   |    |                     |
        | Applies regex to  |    | Consumes terminals, |
        | produce terminals |    | executes your code  |
        +-------------------+    | as it recognizes    |
                                 | nonterminals        |
                                 +---------------------+

LALRPOP's default lexer is based on regular expressions. By default, it works by extracting all the terminals (e.g., "(" or r"\d+") from your grammar and compiling them into one big list. At runtime, it will walk over the string and, at each point, find the longest match from the literals and regular expressions in your grammar and produces one of those. As an example, let's look again at our example grammar:

MST - LALRPOP 的默认词法分析器基于正则表达式。默认情况下,它的工作原理是从语法中提取所有终端(例如,“(” 或 r“d+”)并将它们编译成一个大列表。在运行时,它将遍历字符串,并在每个点从语法中的 Literals 和 Regular Expressions 中找到最长的匹配项,并生成其中一个匹配项。例如,让我们再次看一下我们的示例语法:

GPT - LALRPOP 的默认词法分析器是基于正则表达式的。默认情况下,它通过从你的语法中提取所有的终结符(例如,"(" 或 r"\d+")并将它们编译成一个大的列表来工作。在运行时,它会遍历字符串,并在每个位置从语法中的字面量和正则表达式中找到最长的匹配项,并生成其中一个终结符。举个例子,我们再来看一下我们的示例语法:

pub Term: i32 = {
    <n:Num> => n,
    "(" <t:Term> ")" => t,
};

Num: i32 = <s:r"[0-9]+"> => i32::from_str(s).unwrap();

This grammar in fact contains three terminals:

MST - 此语法实际上包含三个终端:

GPT - 这个语法实际上包含了三个终结符:

  • "(" -- a string literal, which must match exactly // 字符串文本,必须完全匹配
  • ")" -- a string literal, which must match exactly // 字符串文本,必须完全匹配
  • r"[0-9]+" -- a regular expression

When we generate a lexer, it is effectively going to be checking for each of these three terminals in a loop, sort of like this pseudocode:

MST - 当我们生成一个词法分析器时,它实际上是在一个循环中检查这三个终端中的每一个,有点像这个伪代码:

GPT - 当我们生成一个词法分析器时,它实际上会在一个循环中检查这三个终结符,类似于下面的伪代码:

let mut i = 0; // index into string
loop {
    skip whitespace; // we do this implicitly, at least by default
    if (data at index i is "(") { produce "("; }
    else if (data at index i is ")") { produce ")"; }
    else if (data at index i matches regex "[0-9]+") { produce r"[0-9]+"; }
}

Note that this has nothing to do with your grammar. For example, the tokenizer would happily tokenize a string like this one, which doesn't fit our grammar:

MST - 请注意,这与你的语法无关。例如,分词器会很乐意对像这样的字符串进行分词,这不符合我们的语法:

GPT - 请注意,这与您的语法无关。例如,词法分析器会愉快地将一个像这样的字符串进行词法分析,即使它不符合我们的语法:

  (  22   44  )     )
  ^  ^^   ^^  ^     ^
  |  |    |   |     ")" terminal
  |  |    |   |
  |  |    |   ")" terminal
  |  +----+
  |  |
  |  2 r"[0-9]+" terminals
  |
  "(" terminal

When these tokens are fed into the parser, it would notice that we have one left paren but then two numbers (r"[0-9]+" terminals), and hence report an error.

MST - 当这些标记被输入到解析器中时,它会注意到我们有一个左边的括号,但后面有两个数字(r“[0-9]+” 终端),因此报告错误。

GPT - 当这些令牌被送入解析器时,它会注意到我们有一个左括号,但接下来是两个数字(r"[0-9]+" 终结符),因此会报告一个错误。

Precedence of fixed strings 固定字符串的优先级

Terminals in LALRPOP can be specified (by default) in two ways. As a fixed string (like "(") or a regular expression (like r[0-9]+). There is actually an important difference: if, at some point in the input, both a fixed string and a regular expression could match, LALRPOP gives the fixed string precedence. To demonstrate this, let's modify our parser. If you recall, the current parser parses parenthesized numbers, producing a i32. We're going to modify if to produce a string, and we'll add an "easter egg" so that 22 (or (22), ((22)), etc) produces the string "Twenty-two":

MST - LALRPOP 中的终端可以通过两种方式(默认情况下)指定。作为固定字符串(如 “(”)或正则表达式(如 r[0-9]+)。实际上有一个重要的区别:如果在输入中的某个点,固定字符串和正则表达式都可以匹配,则 LALRPOP 会给出固定字符串的优先级。为了演示这一点,让我们修改我们的解析器。如果您还记得,当前解析器解析带括号的数字,生成 i32。我们将修改 if 以生成一个字符串,并且我们将添加一个 “复活节彩蛋”,以便 22 (或 (22), ((22)) 等) 生成字符串 “Twenty-two”:

GPT - LALRPOP 中的终结符可以通过两种方式来指定(默认情况下):一种是固定字符串(如 "("),另一种是正则表达式(如 r"[0-9]+")。实际上,它们之间有一个重要的区别:如果在输入的某个位置,固定字符串和正则表达式都可以匹配,LALRPOP 会优先选择固定字符串。为了演示这一点,我们可以修改当前的解析器。如果你记得,当前的解析器解析的是带括号的数字,最终生成一个 i32。我们将修改它,让它生成一个字符串,并添加一个“彩蛋”,使得 22(或者 (22)((22)) 等)会生成字符串 "Twenty-two"

pub Term = {
    Num,
    "(" <Term> ")",
    "22" => "Twenty-two!".to_string(),
};

Num: String = r"[0-9]+" => <>.to_string();

If we write some simple unit tests, we can see that in fact an input of 22 has matched the string literal. Interestingly, the input 222 matches the regular expression instead; this is because LALRPOP prefers to find the longest match first. After that, if there are two matches of equal length, it prefers the fixed string:

MST - 如果我们编写一些简单的单元测试,我们可以看到实际上输入 22 与字符串 Literals 匹配。有趣的是,输入 222 与正则表达式匹配;这是因为 LALRPOP 更喜欢先找到最长的匹配项。之后,如果有两个长度相等的匹配项,则首选固定字符串:

GPT - 如果我们写一些简单的单元测试,我们可以看到,实际上输入 22 匹配了字符串字面量。有趣的是,输入 222 匹配了正则表达式;这是因为 LALRPOP 优先找到最长匹配项。之后,如果有两个匹配项长度相同,它会优先选择固定字符串。

#[test]
fn calculator2b() {
    let result = calculator2b::TermParser::new().parse("33").unwrap();
    assert_eq!(result, "33");

    let result = calculator2b::TermParser::new().parse("(22)").unwrap();
    assert_eq!(result, "Twenty-two!");

    let result = calculator2b::TermParser::new().parse("(222)").unwrap();
    assert_eq!(result, "222");
}

Ambiguities between regular expressions 正则表达式之间的歧义

In the previous section, we saw that fixed strings have precedence over regular expressions. But what if we have two regular expressions that can match the same input? Which one wins? For example, consider this variation of the grammar above, where we also try to support parenthesized identifiers like ((foo22)):

MST - 在上一节中,我们看到固定字符串优先于正则表达式。但是,如果我们有两个可以匹配相同输入的正则表达式呢?哪一个赢了?例如,考虑上面语法的这种变体,我们还尝试支持带括号的标识符,如 ((foo22)):

GPT - 在上一节中,我们看到固定字符串优先于正则表达式。但是,如果我们有两个正则表达式能够匹配相同的输入,那么哪个会优先呢?例如,考虑这个语法的变体,其中我们还尝试支持类似 ((foo22)) 这样的带括号标识符:

pub Term = {
    Num,
    "(" <Term> ")",
    "22" => format!("Twenty-two!"),
    r"\w+" => format!("Id({})", <>), // <-- we added this
};

Num: String = r"[0-9]+" => <>.to_string();

Here I've written the regular expression r\w+. However, if you check out the docs for regex, you'll see that \w is defined to match alphabetic characters but also digits. So there is actually an ambiguity here: if we have something like 123, it could be considered to match either r"[0-9]+" or r"\w+". If you try this grammar, you'll find that LALRPOP helpfully reports an error:

MST - 在这里,我编写了正则表达式 rw+。但是,如果你查看正则表达式的文档,你会发现 w 被定义为匹配字母字符和数字。所以这里实际上存在歧义:如果我们有类似 123 的东西,它可以被认为是匹配 r“[0-9]+” 或 r“w+”。如果你尝试这种语法,你会发现 LALRPOP 会帮助报告一个错误:

GPT - 在这里,我编写了正则表达式 r\w+。然而,如果你查看正则表达式的文档,你会看到 \w 被定义为匹配字母字符,同时也匹配数字。所以在这里实际上存在歧义:如果我们有类似 123 的输入,它可能会被视为匹配 r"[0-9]+" 或者 r"\w+"。如果你尝试这个语法,你会发现 LALRPOP 会友好地报告一个错误:

error: ambiguity detected between the terminal `r#"\w+"#` and the terminal `r#"[0-9]+"#`

      r"\w+" => <>.to_string(),
      ~~~~~~

There are various ways to fix this. We might try adjusting our regular expression so that the first character cannot be a number, so perhaps something like r"[[:alpha:]]\w*". This will work, but it actually matches something different than what we had before (e.g., 123foo will not be considered to match, for better or worse). And anyway it's not always convenient to make your regular expressions completely disjoint like that. Another option is to use a match declaration, which lets you control the precedence between regular expressions.

MST - 有多种方法可以解决这个问题。我们可能会尝试调整我们的正则表达式,使第一个字符不能是数字,所以可能像 r“[[:alpha:]]w*” 这样。这将起作用,但它实际上匹配的内容与我们之前匹配的内容不同(例如,无论好坏,123foo 都不会被视为匹配)。无论如何,像这样让你的正则表达式完全不相交并不总是很方便的。另一个选项是使用 match 声明,它允许您控制正则表达式之间的优先级。

GPT - 有几种方法可以解决这个问题。我们可以尝试调整我们的正则表达式,使得第一个字符不能是数字,例如 r"[[:alpha:]]\w*". 这种方式可以解决问题,但它实际上匹配的内容与之前不同(例如,123foo 将不再被视为匹配,可能是好事也可能是坏事)。而且,通常不太方便让正则表达式完全互斥。另一种选择是使用 match 声明,这可以让你控制正则表达式之间的优先级。

Simple match declarations 简单 match 声明

A match declaration lets you explicitly give the precedence between terminals. In its simplest form, it consists of just ordering regular expressions and string literals into groups, with the higher precedence items coming first. So, for example, we could resolve our conflict above by giving r"[0-9]+" precedence over r"\w+", thus saying that if something can be lexed as a number, we'll do that, and otherwise consider it to be an identifier.

MST - match 声明允许您显式指定终端之间的优先级。在最简单的形式中,它只包括将正则表达式和字符串文本排序到组中,优先级较高的项排在最前面。因此,例如,我们可以通过赋予 r“[0-9]+” 优先于 r“w+” 来解决上面的冲突,从而表示如果某个东西可以被解释为一个数字,我们就会这样做,否则就将其视为标识符。

GPT - match 声明允许你显式地指定终结符之间的优先级。它的最简单形式是将正则表达式和字符串字面量按顺序分组,高优先级的项排在前面。例如,我们可以通过将 r"[0-9]+" 放在 r"\w+" 之前来解决上面的冲突,这样就表示如果某个内容可以被解析为数字,就优先作为数字处理,否则就当作标识符处理。

match {
    r"[0-9]+"
} else {
    r"\w+",
    _
}

Here the match contains two levels; each level can have more than one item in it. The top-level contains only r"[0-9]+", which means that this regular expression is given highest priority. The next level contains r\w+, so that will match afterwards.

MST - 此处的匹配项包含两个级别;每个级别可以包含多个项。顶层仅包含 r“[0-9]+”,这意味着此正则表达式被赋予最高优先级。下一个级别包含 rw+,因此稍后将匹配。

GPT - 这里的 match 声明包含了两个层级;每个层级可以有多个项。顶层只包含 r"[0-9]+",这意味着这个正则表达式被赋予了最高优先级。下一个层级包含了 r"\w+",因此它会在第一个层级匹配失败后进行匹配。

The final _ indicates that other string literals and regular expressions that appear elsewhere in the grammar (e.g., "(" or "22") should be added into that final level of precedence (without an _, it is illegal to use a terminal that does not appear in the match declaration).

MST - 最后的 _ 表示出现在语法中其他位置的字符串文字和正则表达式(例如,“(” 或 “22”)应添加到该最终优先级中(如果没有 _,使用未出现在 match 声明中的终端是非法的)。

GPT - 最终的 _ 表示其他出现在语法其他部分的字符串文字和正则表达式(例如 (22)应该被加入到最终的优先级层级中(如果没有 _,那么在 match 声明中没有出现的终结符是非法的)。

If we add this match section into our example, we'll find that it compiles, but it doesn't work exactly like we wanted. Let's update our unit test a bit to include some identifier examples::

MST - 如果我们将此 match 部分添加到我们的示例中,我们会发现它可以编译,但它并没有完全按照我们想要的方式工作。让我们稍微更新一下我们的单元测试以包含一些标识符示例:

GPT - 如果我们将这个 match 部分添加到我们的示例中,我们会发现它编译通过,但它的行为并不是我们想要的。让我们更新一下单元测试,加入一些标识符示例:

#[test]
fn calculator2b() {
    // These will all work:

    let result = calculator2b::TermParser::new().parse("33").unwrap();
    assert_eq!(result, "33");

    let result = calculator2b::TermParser::new().parse("foo33").unwrap();
    assert_eq!(result, "Id(foo33)");

    let result = calculator2b::TermParser::new().parse("(foo33)").unwrap();
    assert_eq!(result, "Id(foo33)");

    // This one will fail:

    let result = calculator2b::TermParser::new().parse("(22)").unwrap();
    assert_eq!(result, "Twenty-two!");
}

The problem comes about when we parse 22. Before, the fixed string 22 got precedence, but with the new match declaration, we've explicitly stated that the regular expression r"[0-9]+" has full precedence. Since the 22 is not listed explicitly, it gets added at the last level, where the _ appears. We can fix this by adjusting our match to mention 22 explicitly:

MST - 当我们解析 22 时,问题就出现了。以前,固定字符串 22 具有优先权,但在新的 match 声明中,我们已经明确声明正则表达式 r“[0-9]+” 具有完全优先权。由于 22 未明确列出,因此它会添加到最后一级,其中 _ 出现。我们可以通过调整匹配以显式提及 22 来解决此问题:

GPT - 问题出在我们解析 22 时。之前,固定字符串 22 的优先级较高,但在新的 match 声明中,我们明确声明了正则表达式 r"[0-9]+" 拥有完全的优先权。由于 22 没有明确列出,它被添加到了最后一个优先级层级(即 _ 所在的层级)。我们可以通过调整 match 来显式提到 22,从而解决这个问题:

match {
    r"[0-9]+",
    "22"
} else {
    r"\w+",
    _
}

This raises the interesting question of what the precedence is within a match rung -- after all, both the regex and "22" can match the same string. The answer is that within a match rung, fixed literals get precedence over regular expressions, just as before, and all regular expressions must not overlap.

MST - 这就提出了一个有趣的问题,即匹配梯级中的优先级是什么 -- 毕竟,正则表达式和 “22” 都可以匹配同一个字符串。答案是,在 match rung 中,固定文本优先于正则表达式,就像以前一样,并且所有正则表达式都不能重叠。

GPT - 这引出了一个有趣的问题,即在一个 match 层级内,优先级如何决定——毕竟,正则表达式和 "22" 都可以匹配相同的字符串。答案是,在一个 match 层级内,固定字符串的优先级高于正则表达式,正如之前所说的那样,并且所有的正则表达式不得重叠。

With this new match declaration, we will find that our tests all pass.

MST - 使用这个新的 match 声明,我们将发现我们的测试都通过了。

GPT - 通过这个新的 match 声明,我们会发现所有的测试都通过了。

Renaming match declarations 重命名匹配声明

There is one final twist before we reach the final version of our example that you will find in the repository. We can also use match declarations to give names to regular expressions, so that we don't have to type them directly in our grammar. For example, maybe instead of writing r"\w+", we would prefer to write ID. We could do that by modifying the match declaration like so:

MST - 在我们到达示例的最终版本之前,还有最后一个转折,您将在存储库中找到该版本。我们还可以使用 match 声明为正则表达式命名,这样我们就不必直接在语法中输入它们。例如,也许我们更愿意写 ID,而不是写 r“w+”。我们可以通过修改 match 声明来实现,如下所示:

GPT - 在我们最终版本的示例之前,还有一个小细节。我们还可以使用 match 声明为正则表达式命名,这样就不需要在语法中直接编写它们。例如,也许我们不想每次都写 r"\w+",而是更希望写 ID。我们可以通过如下修改 match 声明来实现这一点:

match {
    r"[0-9]+",
    "22"
} else {
    r"\w+" => ID, // <-- give a name here
    _
}

And then adjusting the definition of Term to reference ID instead:

MST - 然后将 Term 的定义调整为引用 ID:

GPT - 然后,我们调整 Term 的定义,以引用 ID,而不是直接写正则表达式 r"\w+"

pub Term = {
    Num,
    "(" <Term> ")",
    "22" => "Twenty-two!".to_string(),
    ID => format!("Id({})", <>), // <-- changed this
};

In fact, the match declaration can map a regular expression to any kind of symbol you want (i.e., you can also map to a string literal or even a regular expression). Whatever symbol appears after the => is what you should use in your grammar. As an example, some languages have case-insensitive keywords; if you wanted to write "BEGIN" in the grammar itself, but have that map to a regular expression in the lexer, you might write:

MST - 事实上, match 声明可以将正则表达式映射到您想要的任何类型的符号(即,您还可以映射到字符串文字甚至正则表达式)。无论 => 后面出现什么符号,你都应该在语法中使用这个符号。例如,某些语言具有不区分大小写的关键字;如果你想在语法本身中写 “BEGIN”,但要把这个 map 写成词法分析器中的正则表达式,你可以这样写:

GPT - 实际上,match 声明可以将正则表达式映射到任何你想要的符号(即,你也可以映射到字符串文字或甚至其他正则表达式)。无论 => 后面是什么符号,你在语法中使用的就是这个符号。举个例子,一些语言有大小写不敏感的关键字;如果你想在语法中写 "BEGIN",但希望它在词法分析器中映射到一个正则表达式,你可以写:

match {
    r"(?i)begin" => "BEGIN",
    ...
}

And now any reference in your grammar to "BEGIN" will actually match any capitalization.

MST - 现在,语法中对 “BEGIN” 的任何引用实际上都将匹配任何大写。

GPT - 现在,语法中对 "BEGIN" 的任何引用实际上都会匹配任何大小写形式的 "BEGIN"。

Customizing skipping between tokens 自定义在标记之间跳过

If we want to support comments we will need to skip more than just whitespace in our lexer. To this end ignore patterns can be specified.

MST - 如果我们想支持注释,我们需要跳过词法分析器中的空格以外的更多内容。为此,可以指定 ignore 模式。

GPT - 如果我们想要支持注释,我们将需要在词法分析器中跳过的不仅仅是空白字符。为此,可以指定忽略模式。

match {
    r"\s*" => { }, // The default whitespace skipping is disabled if an `ignore pattern` is specified
    r"//[^\n\r]*[\n\r]*" => { }, // Skip `// comments`
    r"/\*[^*]*\*+(?:[^/*][^*]*\*+)*/" => { },  // Skip `/* comments */`
}

Unicode compatibility Unicode 兼容性

LALRPOP is capable of lexing tokens that match the full unicode character set, or those that just match ASCII. If you need unicode matching, you should enable features = [ "unicode" ] in your Cargo.toml. Because lexing unicode requires loading the full unicode character set, enabling this feature will increase binary size, so you may wish to avoid it if you do not need unicode support.

MST - LALRPOP 能够对与完整 unicode 字符集匹配的词元或仅与 ASCII 匹配的词元进行词法分析。如果你需要 unicode 匹配,你应该在 Cargo.toml 中启用 features = [ “unicode” ]。因为对 Unicode 进行词法编译需要加载完整的 unicode 字符集,所以启用此功能会增加二进制大小,因此如果您不需要 unicode 支持,您可能希望避免这样做。

GPT - LALRPOP 可以对匹配整个 Unicode 字符集的令牌进行词法分析,也可以仅匹配 ASCII 字符。如果需要 Unicode 匹配,应该在 Cargo.toml 中启用 features = [ "unicode" ]。由于 Unicode 词法分析需要加载整个 Unicode 字符集,启用此功能会增加二进制文件的大小,因此如果不需要 Unicode 支持,您可能希望避免启用此功能。

It's important to note that certain character classes from perl regex extensions are "unicode friendly", and require unicode support. For example, "\s" matches unicode whitespace characters, not just ASCII ones, and likewise "\d" matches unicode digits (such as numerals in non-latin character sets). If you use those patterns in your lexer, you will require unicode.

MST - 重要的是要注意 perl 正则表达式扩展中的某些字符类是 “unicode 友好”的,并且需要 unicode 支持。例如,“s” 匹配 Unicode 空白字符,而不仅仅是 ASCII 字符,同样,“d” 匹配 Unicode 数字(例如非拉丁字符集中的数字)。如果你在 lexer 中使用这些模式,你将需要 unicode。

GPT - 需要注意的是,某些来自 Perl 正则表达式扩展的字符类是“Unicode 友好的”,并且需要启用 Unicode 支持。例如,\s 匹配的是 Unicode 空白字符,而不仅仅是 ASCII 空白字符;同样,\d 匹配的是 Unicode 数字(例如,非拉丁字符集中的数字)。如果在您的词法分析器中使用了这些模式,您将需要启用 Unicode 支持。

You may wish to match only the ASCII subset of these characters, in which case, you can use the ASCII only character classes described here as substitutes and avoid adding unicode dependencies.

MST - 您可能希望仅匹配这些字符的 ASCII 子集,在这种情况下,您可以使用此处描述的仅 ASCII 字符类作为替代,并避免添加 unicode 依赖项。

GPT - 如果您只希望匹配这些字符的 ASCII 子集,那么可以使用这里描述的仅限 ASCII 的字符类作为替代,从而避免添加 Unicode 依赖。

标签:匹配,22,generator,正则表达式,生成器,LALRPOP,GPT,match
From: https://www.cnblogs.com/Tifahfyf/p/18653358

相关文章

  • MybatisX-Generator不生成domain文件夹解决方案
    问题描述使用MybatisX-Generator生成数据库表实体以后,发现没有生成domain文件夹以及User.java文件问题原因以及解决方案因为MybatisX版本更新,最新版需要在options里额外勾选model才能生成domain勾选model,点击finish,成功生成domain文件夹,以及User.java文件......
  • 如何在 Python 中使用 generators 和 yield
     是否曾经需要处理一个大到足以耗尽机器内存的数据集?或者有一个复杂的函数,每次调用时都需要维护内部状态,但这个函数太小,不适合创建自己的类。在这些情况以及更多情况下,Generators和yield语句都能帮上忙。 使用generatorgenerator函数是一种返回懒惰迭代器的特殊函数。g......
  • 使用 Python 的 yield 创建生成器函数
     Python中的yield关键字将常规函数转换为生成器,它可以按需生成一系列值,而不是一次性计算所有值。Python函数并不总是有返回语句。生成器函数是用yield关键字代替return的函数。这些函数产生生成器迭代器,它是表示数据流的对象。迭代器所代表的元素只有在需要时才会被创......
  • 【语法】生成器
    python中的推导式、生成器_python生成器推导式-CSDN博客“”“生成器应用的场景是在大数据的范围中使用,切记不可直接用for遍历所有,可能无法短时间内获取所有数据”“”使用yield来实现生成器,并使用next进行激活1,__next__()&next()的区别__next__()是生成器对象......
  • C#实现LALR(1)解析器的生成器
    YetAnotherCompiler参考lex和yacc的输入格式,参考虎书《现代编译原理-C语言描述》的算法,大力整合优化,实现了LALR(1)的C#生成器(暂命名为bitParser)。词法分析器根据DFA和最小化DFA分别生成词法分析器代码(状态转换表、保留字、Token类型等)支持全Unicode字符。支持int.MaxVal......
  • 在js中箭头函数可以当做Generator函数吗?
    在JavaScript中,箭头函数(ArrowFunctions)不能用作Generator函数。Generator函数是一种特殊类型的函数,允许函数在执行过程中被暂停和恢复,而不是只能从头到尾连续运行。它们通过使用function*语法进行定义,并且可以在函数体内部使用yield关键字来暂停函数的执行并返回一个值。箭头函......
  • Marigold:Repurposing Diffusion-Based Image Generators for Monocular Depth Estimat
    目录一、概述二、相关工作1、单目深度估计2、扩散模型3、单目深度估计的扩散模型4、基础模型三、Method四、实验一、概述    Marigold是一个扩散模型和通过微调手段的单目深度估计方法,可以利用预先训练好的StableDiffusion中的视觉知识,来实现更好更通用......
  • 5. LALRPOP Tutorial LALRPOP 教程
    https://lalrpop.github.io/lalrpop/tutorial/index.htmlThisisatutorialforhowtowriteacompleteparserforasimplecalculatorusingLALRPOP.Ifyouareunfamiliarwithwhataparsergeneratoris,youshouldreadCrashcourseonparsersfirst.gpt-......
  • 1. LALRPOP
    LALRPOPisaparsergenerator,similarinprincipletoYACC,ANTLR,Menhir,andothersuchprograms.Ingeneral,ithasthegrandambitionofbeingthemostusableparsergeneratorever.Thisambitionismostcertainlynotfullyrealized:rightnow,it�......
  • 你根据这个写一个完整的能运行的结合logos和lalrpop的Rust示例程序,并且要求有AST部分
    gpt好的,下面是一个结合logos和lalrpop的完整Rust示例,展示了如何使用logos编写词法分析器(lexer),然后用lalrpop来解析语法,并生成AST(抽象语法树)。这个示例将包括以下几个部分:logos词法分析器:用来从输入文本中生成tokens。lalrpop语法分析器:用来解析这些tokens,生成......