Должен ли диалект 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 вариант.

Другие вопросы по тегам