使用 SWI Prolog 的简单分词器
Simple Tokenizer using SWI Prolog
我正在尝试实现简单的分词器。例如
phrase(lexer(L), "read N; SUM := 0; "), write(L).
会 return:
[key(read),id(N),sep(;),id(SUM),sep(:=), int(0)]
这就是我的。
lexer([Token | Tail]) -->
lexem(Token), //is this way to get tokens?
lexer(Tail).
lexer([]) -->
[].
lexem --> ?????
如有任何关于如何开发它以制作有效分词器的建议,我将不胜感激。
您可以添加描述词素是什么的 DCG 规则。例如:
lexem(key(K)) --> % key(K) is a lexem
key(K). % if K is a key
lexem(sep(S)) --> % sep(S) is a lexem
sep(S). % if S is a separator
% rules for your keywords here
key(read) -->
"read".
key(write) -->
"write".
% rules for your seperators
sep(;) -->
";".
sep(:=) -->
":=".
您可能还想为您的词法分析器添加空格规则,例如:
lexer(Ts) -->
whitespace, % whitespace is ignored
lexer(Ts).
whitespace -->
[W],
{char_type(W,space)}. % space is whitespace
通过这个最小的示例,您已经可以查询一点:
?- phrase(lexer(L), "read ; write").
L = [key(read),sep(;),key(write)] ? ;
no
标识符和数字有点棘手,因为您可能需要最长的输入匹配,例如"SUM"
匹配为 id('SUM')
而不是 id('S'), id('U'), id('M')
。因此,编写 identifier//1 使其产生最长匹配作为第一个解决方案并使用 cut 不搜索进一步的解决方案是合适的。您可以使用内置谓词 atom_chars/2 和 number_chars/2 在 atoms/strings 和 numbers/strings 之间进行转换。其余部分不言自明:
lexem(id(IA)) -->
identifier(I),
!, % longest input match
{atom_chars(IA,I)}.
lexem(int(NA)) -->
number(A),
!, % longest input match
{number_chars(NA,A)}.
identifier([C|Cs]) --> % identifiers are
capital(C), % capital letters
ident(Cs). % followed by other cl's
ident([C|Cs]) -->
capital(C),
ident(Cs).
ident([]) -->
[].
capital(C) -->
[C], % capitals are
{char_type(C,upper)}. % uppercase letters
number([D|Ds]) --> % numbers are
digit(D), % a digit followed
digits(Ds). % by other digits
digits([D|Ds]) -->
digit(D),
digits(Ds).
digits([]) -->
[].
digit(D) --> % a single digit
[D],
{char_type(D,digit)}.
现在你可以查询上面的例子了:
?- phrase(lexer(L), "read N; SUM := 0; ").
L = [key(read), id('N'), sep(;), id('SUM'), sep(:=), int('0'), sep(;)] ;
false.
我正在尝试实现简单的分词器。例如
phrase(lexer(L), "read N; SUM := 0; "), write(L).
会 return:
[key(read),id(N),sep(;),id(SUM),sep(:=), int(0)]
这就是我的。
lexer([Token | Tail]) -->
lexem(Token), //is this way to get tokens?
lexer(Tail).
lexer([]) -->
[].
lexem --> ?????
如有任何关于如何开发它以制作有效分词器的建议,我将不胜感激。
您可以添加描述词素是什么的 DCG 规则。例如:
lexem(key(K)) --> % key(K) is a lexem
key(K). % if K is a key
lexem(sep(S)) --> % sep(S) is a lexem
sep(S). % if S is a separator
% rules for your keywords here
key(read) -->
"read".
key(write) -->
"write".
% rules for your seperators
sep(;) -->
";".
sep(:=) -->
":=".
您可能还想为您的词法分析器添加空格规则,例如:
lexer(Ts) -->
whitespace, % whitespace is ignored
lexer(Ts).
whitespace -->
[W],
{char_type(W,space)}. % space is whitespace
通过这个最小的示例,您已经可以查询一点:
?- phrase(lexer(L), "read ; write").
L = [key(read),sep(;),key(write)] ? ;
no
标识符和数字有点棘手,因为您可能需要最长的输入匹配,例如"SUM"
匹配为 id('SUM')
而不是 id('S'), id('U'), id('M')
。因此,编写 identifier//1 使其产生最长匹配作为第一个解决方案并使用 cut 不搜索进一步的解决方案是合适的。您可以使用内置谓词 atom_chars/2 和 number_chars/2 在 atoms/strings 和 numbers/strings 之间进行转换。其余部分不言自明:
lexem(id(IA)) -->
identifier(I),
!, % longest input match
{atom_chars(IA,I)}.
lexem(int(NA)) -->
number(A),
!, % longest input match
{number_chars(NA,A)}.
identifier([C|Cs]) --> % identifiers are
capital(C), % capital letters
ident(Cs). % followed by other cl's
ident([C|Cs]) -->
capital(C),
ident(Cs).
ident([]) -->
[].
capital(C) -->
[C], % capitals are
{char_type(C,upper)}. % uppercase letters
number([D|Ds]) --> % numbers are
digit(D), % a digit followed
digits(Ds). % by other digits
digits([D|Ds]) -->
digit(D),
digits(Ds).
digits([]) -->
[].
digit(D) --> % a single digit
[D],
{char_type(D,digit)}.
现在你可以查询上面的例子了:
?- phrase(lexer(L), "read N; SUM := 0; ").
L = [key(read), id('N'), sep(;), id('SUM'), sep(:=), int('0'), sep(;)] ;
false.