使用 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.