Должен ли диалект PARSE использоваться для задач, которые в основном касаются изменения ввода?
В честь того, что Rebol 3 станет открытым исходным кодом в любую минуту (?), Я вернулся к этому. В качестве упражнения я пытаюсь написать свой собственный анализатор JSON на диалекте PARSE.
Поскольку Дуглас Крокфорд считает, что Рибол повлиял на его открытие JSON, я подумал, что это будет легко. Помимо замены скобок скобками и избавления от всех этих запятых, один из барьеров для простого использования LOAD
на строке является тот факт, что, когда они хотят сделать эквивалент SET-WORD!
они используют что-то похожее на строку для токенизатора Ребола, с недопустимым заблудшим двоеточием:
{
"key one": {
"summary": "This is the string content for key one's summary",
"value": 7
},
"key two": {
"summary": "Another actually string, not supposed to be a 'symbol'",
"value": 100
}
}
В основном я хотел найти все случаи, которые были похожи "foo bar":
и превратить их в foo-bar:
оставляя совпадающие пары кавычек, за которыми не следовали только двоеточия.
Когда я занялся этим в PARSE (который я довольно хорошо понимаю в принципе, но до сих пор мало использовал), возникла пара вопросов. Но главным образом, каковы обещанные условия, при которых вы можете выйти в код и изменить ряд из-под парсера... в частности, в Rebol 3? В более общем смысле, это "правильный инструмент для работы"?
Вот правило, которое я пробовал, которое, кажется, работает для этой части задачи:
any [
; require a matched pair of quotes & capture series positions before
; and after the first quote, and before the last quote
to {"} beforePos: skip startPos: to {"} endPos: skip
; optional colon next (if not there the rest of the next rule is skipped)
opt [
{:}
; if we got to this part of the optional match rule, there was a colon.
; we escape to code changing spaces to dashes in the range we captured
(
setWordString: copy/part startPos endPos
replace/all setWordString space "-"
change startPos setWordString
)
; break back out into the parse dialect, and instead of changing the
; series length out from under the parser we jump it back to the position
; before that first quote that we saw
:beforePos
; Now do the removals through a match rule. We know they are there and
; this will not cause this "colon-case" match rule to fail...because we
; saw those two quotes on the first time through!
remove [{"}] to {"} remove [{"}]
]
]
Все хорошо? Есть ли шанс change startPos setWordString
в открытом коде испортить внешний синтаксический анализ... если не в этом случае, то в чем-то неуловимо другом?
Как всегда, любой дидактический совет "чище / короче / лучше другим способом" ценится.
PS почему нет replace/all/part
?
3 ответа
Новые ключевые слова, такие как change
, insert
а также remove
должно облегчить этот тип вещей. Я полагаю, что основным недостатком этого подхода являются проблемы с задержкой при развертывании серий (я уже упоминал, что быстрее создавать новые строки, чем манипулировать ими).
token: [
and [{"} thru {"} any " " ":"]
remove {"} copy key to {"} remove {"} remove any " "
(key: replace/all key " " "-")
]
parse/all json [
any [
to {"} [
and change token key
; next rule here, example:
copy new-key thru ":" (probe new-key)
| skip
]
]
]
Это немного запутанно, так как я не могу заставить меня работать так, как я ожидал (ведет себя как change
не change/part
), но в теории вы должны быть в состоянии сделать его короче в этом направлении и иметь довольно четкое правило. Идеально может быть:
token: [
{"} copy key to {"} skip any " " and ":"
(key: replace/all key " " "-")
]
parse/all json [
any [
to {"} change token key
| thru {"}
]
]
Изменить: еще одна выдумка change
-
token: [
and [{"} key: to {"} key.: skip any " " ":"]
(key: replace/all copy/part key key. " " "-")
remove to ":" insert key
]
parse/all json [
any [to {"} [token | skip]]
]
Другой способ - думать о разборе как о компиляторе-компиляторе с EBNF. Если я правильно помню синтаксис R2:
copy token [rule] (append output token)
Предполагая правильный синтаксис, и нет {"}
в строках:
thru {"} skip copy key to {"} skip
; we know ":" must be there, no check
thru {"} copy content to {"} skip
(append output rejoin[ {"} your-magic-with key {":"} content {"} ])
Точнее, вместо to
, char by char:
any space {"} copy key some [ string-char | "\" skip ] {"}
any space ":" any space {"} copy content any [ string-char | "\" skip ] {"}
(append output rejoin[ {"} your-magic-with key {":"} content {"} ])
; content can be empty -> any, key not -> some
string-char
будет кодировка с чем угодно, кроме {\}
а также {"}
Синтаксис?
Не знаю, работает ли R3 еще так...:-/
Так как другие ответили на parse
вопрос, я отвечу на PS:
Есть несколько предложенных вариантов, которые никогда не были добавлены в replace
, и основная причина в том, что параметры обработки имеют накладные расходы, и эта функция уже нуждается в некоторых интересных оптимизациях, чтобы даже обрабатывать параметры, которые у нее уже есть. Мы собирались попытаться заменить функцию на нативную, как только мы немного улучшили ее API. Это в основном ситуация, аналогичная reword
функция, где мы не определились с окончательным API до недавнего времени. За replace
мы еще даже не обсуждали это.
В случае с /part
вариант, он просто не был предложен кем-то ранее, и может быть немного концептуально неудобным для объединения с существующими вычислениями внутренней длины. Было бы возможно иметь ограниченный /part
опция, просто целое число вместо смещения ссылки. Вероятно, было бы лучше, если бы /part
длина имеет приоритет над внутренне рассчитанной длиной. Тем не менее, если мы в конечном итоге перейдем на скорректированный API, может не потребоваться /part
вариант.