2012-01-31 2 views
3

Я пытался сделать парсер (очень) простой язык, который выглядит следующим образом:Scala комбинаторы синтаксического анализа для (почти) тривиальна грамматика

block{you are a cow too blkA{ but maybe not} and so is he} hear me moo blockZ{moooooo} 

Я могу разорвать его на части с помощью регулярных выражений:

.*?[^ ]*?\\{ 
.*?\\} 

, который по существу продолжал бы есть символов, пока не найдет что-то, что соответствует [^ ]*?\\{ или \\}: начало или конец блока. Мой вопрос: если я хочу это сделать, используя Scars's Parser Combinators, как мне это сделать? Я в настоящее время:

def expr: Parser[Any] = (block | text)+ 
    def text = ".+?".r 
    def block = "[^ ]*?\\{".r ~ expr ~ "}" 

, но это не работает:

parsed: List(b, l, o, c, k, {, y, o, u, a, r, e, a, c, o, w, t, o, o, b, l, k, A, {, b, u, t, m, a, y, b, e, n, o, t, }, a, n, d, s, o, i, s, h, e, }, h, e, a, r, m, e, m, o, o) 

кажется, что block анализатор не стреляя, и поэтому text парсер увольняют неоднократно. но когда я удалить text анализатор:

def expr: Parser[Any] = (block)+ 

я получаю:

failure: string matching regex `[^ ]*?\{' expected but `y' found 

block{you are a cow too blkA{ but maybe not} and so is he} hear me moo 
    ^

Так, очевидно, blockанализатор делает работу, за исключением того, не тогда, когда text анализатор присутствует. Что происходит? и есть ли «правильный» способ сделать это, потому что для такой базовой грамматики?

EDIT: Изменены название, так как это не так много о нежелании больше, как только решая проблему

EDIT: У меня теперь есть это:

def expr: Parser[Any] = (block | text)+ 

def text = "[^\\}]".r 

def block = "[^ ]*?\\{".r ~ expr ~ "}" 

Логика в том, что для каждого character, он проверяет, является ли это началом блока. Если это не так, он переходит к следующему символу. Это дает мне:

parsed: List(((block{~List(y, o, u, a, r, e, a, c, o, w, t, o, o, ((blkA{~List(b, u, t, m, a, y, b, e, n, o, t))~}), a, n, d, s, o, i, s, h, e))~}), h, e, a, r, m, e, m, o, o) 

который является правильным. Он анализирует неблокированные символы один за другим, хотя это, вероятно, проблема с производительностью (я думаю?). Есть ли способ разобрать все эти неблокированные символы сразу и оставить их в одной большой строке?

+0

Пожалуйста, не добавляйте новые вопросы к существующему. Усильте, конечно, но задавайте новые вопросы по новым вопросам. Он разобирается один за другим, потому что вы использовали неживую звезду. Просто прекратите не жадность. –

ответ

2

Проблема в том, что text потребляет все закрывающиеся фигурные скобки (}). Это выглядит следующим образом:

expr -> block -> expr -> text.+ (until all input is consumed) 

На данный момент, он выходит из expr и пытается разобрать }, который не существует, терпит неудачу, и падает обратно text на первом expr.

Вы можете использовать log, чтобы узнать, что происходит при анализе.

+0

Который задает вопрос: как я могу прекратить «текст» от употребления моего закрывающего '}'? Как и в regex-land, я мог бы совместить '. +? [^] *? \\ {' и '. +? \\}', чтобы захватить весь текст до следующего символа открытого или закрытого блока, но нет '. +?'в библиотеке ПК (насколько я могу судить). Есть ли другой способ добиться такого же эффекта? –

+0

@LiHaoyi Ну, '.. *?' Эквивалентно '. +?', Но почему бы не просто '[^ {}] +' вместо этого? Если у вас есть вложенные фигурные скобки, вам все равно придется превращать 'text' в рекурсивный синтаксический анализатор, а не в регулярное выражение - регулярное выражение не обрабатывает рекурсию. –

Смежные вопросы