2016-04-19 2 views
0

Я пытаюсь реализовать простой токенизатор. НапримерПростой токенизатор с использованием SWI Prolog

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

Я предполагаю, что на 'ЛЕКС (Tail)' 'вы имеете в виду лексического анализатора (Tail)'. И 'lexem -> ?????' должен быть 'lexem (Token) -> ?????'. '?????' описывает, как выглядит токен. Входной поток в ваш DCG будет вашей строкой, по одному символу за раз. Итак, начните оттуда, зная, как вы видели целый токен, характер по характеру. – lurker

+0

Да, это была моя ошибка. К сожалению, я не могу заставить его работать. Не могли бы вы дать мне короткий пример, например, как lexem (Token) -> ????? будет генерировать [sep (;)]? – Dago

+1

см. [Это] (http://stackoverflow.com/a/29048653/874024) ответ – CapelliC

ответ

2

Вы можете добавить 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. 
+0

Я пробовал объяснение ur, но для данного примера я получаю только false;/ – Dago

+0

ok теперь он работает, но у меня есть sep ((;)) вместо sep (;). Почему это? – Dago

+0

@ Dago: Возможно, вы случайно добавили некоторые скобки. Проверьте главу правил. BTW, я обновил свой ответ на номера совпадений и идентификаторы в приведенном выше примере. – tas