2017-01-08 13 views
1

Я бы написал простой лексер, который распознает слова без цифр и числа, игнорирующие пробелы.Почему этот простой jparsec lexer не удается?

Я написал следующий код, используя jparsec v3.0:

final Parser<String> words = Patterns.isChar(CharPredicates.IS_ALPHA).many1().toScanner("word").source(); 
final Parser<String> nums = Patterns.isChar(CharPredicates.IS_DIGIT).many1().toScanner("num").source(); 
final Parser<Tokens.Fragment> tokenizer = Parsers.or(
     words.map(it -> Tokens.fragment(it, "WORD")), 
     nums.map(it -> Tokens.fragment(it, "NUM"))); 
final Parser<List<Token>> lexer = tokenizer.lexer(Scanners.WHITESPACES); 

Но следующий тест не пройден за исключением org.jparsec.error.ParserException: line 1, column 7: EOF expected, 1 encountered. Вместо этого, используя строку «abc cd 123», синтаксический анализ выполняется успешно.

final List<Token> got = lexer.parse("abc cd123"); 
final List<Token> expected = Arrays.asList(
     new Token(0, 3, Tokens.fragment("abc", "WORD")), 
     new Token(4, 2, Tokens.fragment("cd", "WORD")), 
     new Token(6, 3, Tokens.fragment("123", "NUM"))); 
assertEquals(expected, got); 

На ваш взгляд, что это не так?

+0

не соответствует вашим лексемам списка будет ли '«а CD 123»', так как вы лексический на основе пробельного? – Aurora0001

+0

Согласно документации, 'Parser.lexer (...)' должен запускать синтаксический анализатор ('токенизатор'), повторно игнорируя шаблон, распознаваемый делителем (' WHITESPACES') до и после каждого события. Неясно, является ли разделитель необязательным, поэтому я ожидаю, что полученный лексер будет соответствовать «abc», затем проигнорирует пробел, затем сопоставляет «cd» и, наконец, соответствует «123». – maurocchi

ответ

0

Проблема была решена просто путем разделителя дополнительно:

tokenizer.lexer(Scanners.WHITESPACES.optional(null)) 
1

Следующий тест проход:

public class SOTest { 
    final Parser<String> words = Patterns.isChar(CharPredicates.IS_ALPHA).many1().toScanner("word").source(); 
    final Parser<String> nums = Patterns.isChar(CharPredicates.IS_DIGIT).many1().toScanner("num").source(); 
    final Parser<Tokens.Fragment> tokenizer = Parsers.or(
    words.map(it -> Tokens.fragment(it, "WORD")), 
    nums.map(it -> Tokens.fragment(it, "NUM"))); 
    final Parser<List<Token>> lexer = tokenizer.lexer(Scanners.WHITESPACES); 


    @Test public void test(){ 
    final List<Token> got = lexer.parse("abc cd 123"); 
    Asserts.assertArrayEquals(got.toArray(new Token[0]), 
     new Token(0, 3, Tokens.fragment("abc", "WORD")), 
     new Token(4, 2, Tokens.fragment("cd", "WORD")), 
     new Token(7, 3, Tokens.fragment("123", "NUM"))); 
    }  
} 

Ваши жетоны либо только ALPHA символы или только DIGITS, следовательно, это нормально, вы не можете разобрать abc cd123.

Тот факт, что документация говорит «разделители игнорируются до или после каждого вхождения» следует толковать в том смысле, что разделители появляются до или после списка Token сек разобранных игнорируются. Но разделители не игнорируются, чтобы разделить токены, за исключением случаев операторов (см. Класс Terminals) для получения дополнительной информации).

+0

Спасибо за разъяснение. Поэтому я неправильно понял метод «Parser.lexer», и он не должен использоваться таким образом. Но, наконец, как реализовать лексер, который принимает «abc cd123» и «abc cd 123», создавая ту же последовательность токенов (за исключением смещений, очевидно)? – maurocchi