raku 语法的标记不会命中文档的第一次出现,但会命中类似的后续出现

2024-01-20

我想处理希伯来语的整个 Tanach 文件。为此,我选择了 Raku 语言,因为它的一些功能(语法和 unicode 支持)。

因此,我定义了一些标记来选择相关数据。

grammar HEB {
        token TOP {'<hebrewname>'<t_word>'</hebrewname>'}
        token t_word {<graph>+}
};

grammar CHA {
        token TOP {'<c n="'<t_number>'">'}
        token t_number {\d+}
};

grammar VER {
        token TOP {'<v n="'<t_number>'">'}
        token t_number {\d+}
};

grammar WOR {
        token TOP {'<w>'<t_word>'</w>'}
        token t_word {<graph>+}
};

这里,文档的一小部分(XML 格式的 Tanach)足以说明问题:

<names> <name>Genesis</name> <abbrev>Gen</abbrev> <number>1</number> <filename>Genesis</filename> <hebrewname>בראשית</hebrewname> </names> <c n="1"> <v n="1"> <w>בְּ/רֵאשִׁ֖ית</w> <w>בָּרָ֣א</w> <w>אֱלֹהִ֑ים</w> <w>אֵ֥ת</w> <w>הַ/שָּׁמַ֖יִם</w> <w>וְ/אֵ֥ת</w> <w>הָ/אָֽרֶץ׃</w> </v> <v n="2"> <w>וְ/הָ/אָ֗רֶץ</w> <w>הָיְתָ֥ה</w> <w>תֹ֙הוּ֙</w> <w>וָ/בֹ֔הוּ</w> <w>וְ/חֹ֖שֶׁךְ</w> <w>עַל־</w> <w>פְּנֵ֣י</w> <w>תְה֑וֹם</w> <w>וְ/ר֣וּחַ</w> <w>אֱלֹהִ֔ים</w> <w>מְרַחֶ֖פֶת</w> <w>עַל־</w> <w>פְּנֵ֥י</w> <w>הַ/מָּֽיִם׃</w> </v>

问题是代码无法识别两个第一 words (<w>בְּ/רֵאשִׁ֖ית</w> <w>בָּרָ֣א</w>)但似乎与以下单词配合得很好...... 有人可以向我解释一下出了什么问题吗?

主循环是:

for $file_in.lines -> $line {
    $memline = $line.trim;

    if HEB.parse($memline) {
          say "hebrew name of book is "~ $/<t_word>;
          next;
    }
    if CHA.parse($memline) {
        say "chapitre number is "~ $/<t_number>;
        next;
    }
    if VER.parse($memline) {
        say "verse number is "~ $/<t_number>;
        next;
    }
    if WOR.parse($memline) {
        $computed_word_value = 0;
        say "word is "~ $/<t_word>;
        $file_out.print("$/<t_word>");
        say "numbers of graphemes of word is "~ $/<t_word>.chars;
        @exploded_word = $/<t_word>.comb;
        for @exploded_word {
                say $_.uniname;
        };
        next;
    }
    say "not processed";
}

输出文件 :

请注意,之后诗节编号为1,前 2 个单词不被处理。不要关注扭曲的希伯来语(Windows 控制台)!

not processed
not processed
not processed
not processed
not processed
hebrew name of book is ׳‘׳¨׳׳©׳™׳×
not processed
chapitre number is 1
verse number is 1
not processed
not processed
word is ׳ײ±׳œײ¹׳”ײ´ײ‘׳™׳
numbers of graphemes of word is 5
HEBREW LETTER ALEF
HEBREW LETTER LAMED
HEBREW LETTER HE
HEBREW LETTER YOD
HEBREW LETTER FINAL MEM
word is ׳ײµײ¥׳×
numbers of graphemes of word is 2
HEBREW LETTER ALEF
HEBREW LETTER TAV
not processed
word is ׳•ײ°/׳ײµײ¥׳×
numbers of graphemes of word is 4
HEBREW LETTER VAV
SOLIDUS

我希望我的问题能够得到明确的解答。


我无法重现你的问题。
我唯一能猜测的是您没有使用正确的编码打开文件。

或者更糟糕的是,您从 STDIN 获取文件,但没有选择正确的代码页。 (这是有道理的,因为你的输出也是 mojibake。)
Rakudo 并不真正执行代码页,因此如果您未将环境设置为 utf8,则必须更改$*STDIN (and $*STDOUT) 来匹配任何内容。


我现在将假装您已将帖子发布到 CodeReview.StackExchange.com。

首先,我不知道为什么你要为如此小的东西创建一个完整的语法,而这可以通过简单的正则表达式轻松完成。

my token HEB {
  '<hebrewname>'
  $<t_word> = [<.graph>+]
  '</hebrewname>'
}
my token CHA {
 '<c n="' $<t_number> = [\d+] '">'
}
my token VER {
  '<v n="' $<t_number> = [\d+] '">'
}
my token WOR {
  '<w>' $<t_word> = [<.graph>+] '</w>'
}

老实说,这仍然比您似乎需要的要多,因为每个正则表达式只处理一个元素。

这也忽略了我真的不喜欢你给元素命名,比如t_word and t_number。这是毫无意义的,因为它们在里面$/,并且 Grammar 也没有任何此类类似命名的方法,因此它们不会干扰任何其他名称空间。如果必须给它们起名字,请给它们起描述性的名字。

你可以只限制$/仅对您关心的部分进行字符串化<(…)>。 (它在这里起作用是因为你只捕获一件事。)

<(意味着忽略之前的一切,并且)>意味着忽略之后的一切。

my token HEB {
  '<hebrewname>'
  <( <.graph>+ )> # $/ will contain only what <.graph>+ matches
  '</hebrewname>'
}
my token CHA {
 '<c n="' <( \d+ )> '">'
}
my token VER {
  '<v n="' <( \d+ )> '">'
}
my token WOR {
  '<w>' <( <.graph>+ )> '</w>'
}

您正在解析它,就好像它只是一个面向行的文件一样。
这确实具有一定的意义,因为它被格式化为一个,并且这会导致更少的内存使用。

为此使用命名正则表达式,更不用说整个语法了,有点大材小用了。当对于这种简单的匹配来说并不真正需要逻辑时,它还会分离逻辑。

以下是我如何以面向行的方式解析该文件:

my $in-names = False;
my %names;
my @chapters;
my @verses;
my @current-verse;

for $file_in.lines {
  when /'<names>' / { $in-names = True  }
  when /'</names>'/ { $in-names = False }

  # chapter
  when /'<c n="' <( \d+ )> '">'/ {
    @verses := @chapters[ +$/ - 1 ] //= [];
  }
  when /'</c>'/ {
    # finalize this chapter
    # for example print out statistics
    # (only needed if you don't want `default` to catch it)
  }

  # verse
  when /'<v n="' <( \d+ )> '">'/ {
    @current-verse := @verses[ +$/ - 1 ] //= [];
  }
  when /'</v>'/ {
    # finalize this verse
  }

  # word
  when /'<w>' <( <.graph>+ )> '</w>'/ {
    push @current-verse, ~$/;
  }

  # name tags
  # must be after more specific regexes
  when /'<' <tag=.ident> '>' $<value> = [<.ident>|\d+] {} "</$<tag>>"/ {
    if $in-names {
      %names{~$<tag>} = ~$<value>
    } else {
      note "not handling $<tag> => $<value> outside of <names>"
    }
  }

  default { note "unexpected text '$_'" }
}

注意when让你不必做next.
因为我们只是使用$_代替$line,这样我们就可以直接使用正则表达式作为这些条件的条件when声明。

我懒得用^ or $所以也没有必要trim or use ^\s* and \s*$.
它确实使它变得更加脆弱,所以如果它成为问题,您可能需要更改它。

如果您真的只想像现在一样进行简单的线路处理,我相信您可以更改上述内容以满足您的需求。

我想让这对将来遇到这个问题的人更有用。因此,我从文件创建了一个数据结构,而不是遵循您正在做的事情。


真的,如果我要的话,我可能只会接触语法.parse()一次性完成整个文件。

这就是这样的语法。

grammar Book {
  rule TOP {
    <names>
    <chapter> +
    # note that there needs to be a space between <chapter> and +
    # so that whitespace can be between <c…>…</c> elements
  }

  rule names {
    '<names>'  ~  '</names>'
    <name> +
  }

  token name {
    '<' <tag=.ident> '>'
    $<name> = [<.ident>|\d+]
    {}
    "</$<tag>>"
  }

  rule chapter {
    # note space before ]
    ['<c n="' <number> '">' ]  ~  '</c>'
    <verse> +
  }
  rule verse {
    ['<v n="' <number> '">' ]  ~  '</v>'
    <word> +
  }

  token number { \d+ }
  token word { '<w>' <( <.graph>+ )> '</w>' }
}

进行与您一直在做的类似的处理

class Line-Actions {
  has IO::Handle:D $.file-out is required;
  has $!number-type is default<chapter>;

  method name ($/) {
    if $<tag> eq 'hebrewname' {
      say "hebrew name of book is $<name>";
    }
  }

  # note that .chapter and .verse will run at the end
  # of parsing them, which is too late for when .word is processed
  # so we do it in .number instead
  method number ($/) {
    say "$!number-type number is $/";
    $!number-type = 'verse';
  }
  method chapter ($/) {
    # reset to default of "chapter"
    # as the next .number will be for the next chapter
    $!number-type = Nil;
  }

  method word ($/) {
    say "word is $/";
    $!file-out.print(~$/);
    say "number of graphemes in word is $/.chars()";
    .say for "$/".comb.map: *.uninames.join(', ');
  }
}


Book.parsefile(
  $filename,
  actions => Line-Actions.new( 'outfile.txt'.IO.open(:w) )
);
本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)

raku 语法的标记不会命中文档的第一次出现,但会命中类似的后续出现 的相关文章

随机推荐