Поэтому я пытаюсь использовать yecc.
Terminals string string_delimeter.
Nonterminals value string_content.
Rootsymbol value.
value -> string : extract_value('$1').
value -> string_delimeter string_content string_delimeter : '$2'.
string_content -> value string_content : ['$1' | '$2'].
string_content -> value : '$1'.
Erlang code.
extract_value({_, Value}) -> Value.
Для ввода:
[string_delimeter: '\'', string: 'test', string_delimeter: '\'']
я получил
{:error, {:undefined, :standard_syntax_parser, ['syntax error before: ', []]}}
Если я удалю любой (левый или правый) string_delimeter, окружающий string_content:
value -> string_delimeter string_content string_delimeter : '$2'.
value -> string_delimeter string_content : '$2'.
для
[string_delimeter: '\'', string: 'test']
он возвращается
{:ok, 'test'}
Я не очень понимаю это поведение, в чем проблема?





For the input:
[string_delimeter: '\'', string: 'test', string_delimeter: '\'']
Я думаю, что вы запутались (или, может быть, это я!). yecc ожидает список токенов, где токен представляет собой кортеж из 2 или 3 элементов. Из документы yecc:
The user should implement a scanner that segments the input text, and turns it into one or more lists of tokens. Each token should be a tuple containing information about syntactic category, position in the text (e.g. line number), and the actual terminal symbol found in the text:
{Category, LineNumber, Symbol}.If a terminal symbol is the only member of a category, and the symbol name is identical to the category name, the token format may be
{Symbol, LineNumber}...
Вот пример того, что ожидает yecc:
[
{'[',1},
{atom,1,foo},
{',',1},
{'[',1},
{int,1,1},
{']',1},
{',',1},
{'[',1},
{atom,1,bar},
{',',1},
{'[',1},
{int,1,2},
{',',1},
{int,1,3},
{']',1},
{']',1},
{']',1}
]
Вы можете реализовать такой сканер с помощью leex, а затем передать результат вашему синтаксическому анализатору yecc.
Небольшой совет: вы никогда не должны публиковать вопрос, описывающий, как вы запускали свой код — это просто большая трата времени. Вместо этого скопируйте и вставьте точные команды, которые вы выполнили, а также результат, полученный этими командами. Все, что вам нужно сказать, это:
- This is what I tried:
[Your code here]
- Here is the output:
[all commands you ran and the output here]
- This is the output I expect/want, or Wtf??!! is going on.
[expected/desired output here]
Ах хорошо. Вы используете elixir, хотя теги в вашем вопросе говорят, что вы используете erlang. Я могу заставить работать более простую версию вашего парсера:
string_parser.yrl:
Nonterminals the_string content.
Terminals '\'' string.
Rootsymbol the_string.
the_string -> '\'' content '\'' : '$2'.
%I guess the atom :string has to be the first element of the tuple
%returned by '$2' in previous line:
content -> string : extract_value('$1')
Erlang code.
extract_value({_, _, Value}) -> Value.
В иксе:
iex(1)> :yecc.file('string_parser.yrl')
{:ok, 'string_parser.erl'}
iex(2)> c("string_parser.erl")
[:string_parser]
iex(3)> :string_parser.parse([{:"'", 1}, {:string, 1, "hello"}, {:"'", 1}])
{:ok, "hello"}
Но я не могу заставить рекурсивное определение работать.
Хорошо, я приближаюсь:
string_parser.yrl:
Nonterminals string interior_strings interior_string.
Terminals left_delim right_delim result.
Rootsymbol string.
string -> left_delim interior_strings right_delim : '$2'.
string -> left_delim right_delim : "".
interior_strings -> interior_string : ['$1'].
interior_strings -> interior_string interior_strings : ['$1' | '$2'].
interior_string -> result : extract_value('$1').
interior_string -> string : '$1'.
Erlang code.
extract_value({_, _, Value}) -> Value.
В иксе:
iex(49)> :yecc.file('string_parser.yrl')
{:ok, 'string_parser.erl'}
iex(50)> c("string_parser.erl")
[:string_parser]
iex(51)> :string_parser.parse([{:left_delim, 1}, {:result, 1, "hello"}, {:left_delim, 1}, {:result, 1, "goodbye"}, {:right_delim, 1}, {:right_delim, 1}])
{:ok, ["hello", ["goodbye"]]}
iex(53)>
На данный момент я не знаю, почему взорванные скобки вокруг "goodbye".
Успех!
string_parser.yrl:
Nonterminals string interior_strings interior_string.
Terminals left_delim right_delim result.
Rootsymbol string.
string -> left_delim interior_strings right_delim : '$2'.
string -> left_delim right_delim : "".
interior_strings -> left_delim interior_string right_delim: ['$2'].
interior_strings -> interior_string interior_strings : ['$1' | '$2'].
interior_string -> result : extract_value('$1').
interior_string -> string : '$1'.
Erlang code.
extract_value({_, _, Value}) -> Value.
В иксе:
iex(53)> :yecc.file('string_parser.yrl')
{:ok, 'string_parser.erl'}
iex(54)> c("string_parser.erl")
[:string_parser]
iex(55)> :string_parser.parse([{:left_delim, 1}, {:result, 1, "hello"}, {:left_delim, 1}, {:result, 1, "goodbye"}, {:right_delim, 1}, {:right_delim, 1}])
{:ok, ["hello", "goodbye"]}
Единственное, что я до сих пор не могу сделать, это если я явно укажу '\'' для разделителя:
Nonterminals string interior_strings interior_string.
Terminals '\'' result.
Rootsymbol string.
Endsymbol '$end'.
string -> '\'' interior_strings '\'' : '$2'.
string -> '\'' '\'' : "".
interior_strings -> '\'' interior_string '\'': ['$2'].
interior_strings -> interior_string interior_strings : ['$1' | '$2'].
interior_string -> result : extract_value('$1').
interior_string -> string : '$1'.
Erlang code.
extract_value({_, _, Value}) -> Value.
В иксе:
iex(3)> :string_parser.parse([{:"'", 1}, {:result, 1, "hello"},
{:"'", 1}, {:result, 1, "goodbye"}, {:"'", 1}, {:"'", 1}, {:"$end", 1}])
{:error, {1, :string_parser, ['syntax error before: ', []]}}
Слишком неприятно работать с такими ужасными сообщениями об ошибках. Синтаксическая ошибка перед пустым списком []??! Где пустой список в списке токенов?
Я думал, что добавление последнего кортежа: {:"$end", 1} может сработать, но не повезло. Та же ошибка.
Я могу заставить string_parser работать, если использую буквальные скобки в качестве разделителей:
string_parser.yrl:
Nonterminals string interior_strings interior_string.
Terminals '[' ']' content.
Rootsymbol string.
string -> '[' interior_strings ']' : '$2'.
string -> '[' ']' : "".
interior_strings -> '[' interior_string ']' : ['$2'].
interior_strings -> interior_string interior_strings : ['$1' | '$2'].
interior_string -> content : extract_content('$1').
interior_string -> string : '$1'.
Erlang code.
extract_content({_, _, Content}) -> Content.
В иксе:
iex(11)> :yecc.file('string_parser.yrl')
{:ok, 'string_parser.erl'}
iex(12)> c("string_parser.erl")
[:string_parser]
iex(13)> :string_parser.parse([{:"[", 1}, {:content, 1, "hello"}, {:"[", 1}, {:content, 1, "goodbye"}, {:"]", 1}, {:"]", 1}])
{:ok, ["hello", "goodbye"]}
iex(14)>
Я попытался написать string_parser в erlang и получил ту же ошибку. Файлы .yrl не имеют синтаксиса erlang, поэтому все, что анализирует файлы .yrl, похоже, не может разобрать атом с одной кавычкой: '\''.
Я не понял, почему это проблема в yecc: токен_1 токен_2 токен_1 Я действительно хотел бы знать, но у меня есть способ обойти это:
Terminals string string_delimeter whitespace.
Nonterminals value string_content.
Rootsymbol value.
value -> string : extract_value('$1').
value -> string_delimeter string_content : '$2'.
string_content -> string_content string_delimeter : '$1'.
string_content -> value whitespace string_content : ['$1' | '$3'].
string_content -> value : ['$1'].
Erlang code.
extract_value({_, Value}) -> Value.
[token1, token2, token1] -- Эй, ты прав! Я думал, что проблема была только в одинарных кавычках. Я не могу разобрать следующее: :string_parser.parse([{:a, 1}, {:content, 1, "hello"}, {:a, 1}]) => {:error, {1, :string_parser, ['syntax error before: ', []]}}. Та же ошибка, что и с одинарной кавычкой.
Но... я могу разобрать [token1, token2, token1] -- даже с одинарными кавычками для token1 -- если я напишу нерекурсивный парсер: :string_parser.parse([{:"'", 1}, {:content, 1, "hello"}, {:"'", 1}]) => {:ok, "hello"}
Я использую leex и добавил TokenLine, без разницы. Из того, что я вижу, я не могу сделать: token_1 token_2 token_1 -> и т. д. Облом, как я мог справиться с foobarfoo, когда я хочу foo.