我正在尝试实现简单的tokenizer。例如
phrase(lexer(L), "read N; SUM := 0; "), write(L).
会回来:
[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 --> ?????
如果有任何建议如何开发它来制作一个有效的标记器,我将不胜感激。
答案 0 :(得分:2)
你可以添加描述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')
。因此,编写标识符// 1使得它产生最长匹配作为第一解决方案并使用剪切不搜索进一步的解决方案是合适的。您可以使用内置谓词atom_chars / 2和number_chars / 2在原子/字符串和数字/字符串之间进行转换。其余的都是不言自明的:
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.