Pull to refresh

Comments 6

Каким образом у вас реализовано восстановление после обнаружения ошибки? Например что будет если пользователь передаст в ваш парсер вот такой JSON: { "array": [ 1, 2 3, 4], "object": { "a": true "b": null } }. Здесь есть две ошибки с пропущенными запятыми, одна в массиве, одна в объекте. С точки зрения пользователя ожидается что парсер вернет обе ошибки, а так же какие точены парсер ожидал получить в этих местах.

Я не совсем понимаю как работают правила t_true, t_null, t_string и т.д. Какой объект я получаю, когда пишу подобное правило, как мне получить информацию о том, в каком месте исходного текста находится указанный токен. С точки зрения разработчика DSL мне нужно иметь возможность ссылаться на любой токен исходного текста для подсветки ошибок и перехода между определениями. В идеале у меня должен быть доступ не только с пробельным символам, но и к следующему/предыдущему токенам.

Не нашел ничего про инкрементальный парсинг. Это важный момент для работы с большими файлами. Пользователь может с радостью открыть какой-нибудь XML на 200Мб и начать его править.

Очень необычный синтаксис. Не хватает простых примеров, на которых можно будет по шагам разобраться какие конструкции чему соответствуют. Например, начать с простого парсера который парсит только true и false, затем добавить возможность парсить еще и числа, затем массив разделенный запятыми, потом показать как работать с кардинальностью (? / + / *) и альтернативами. Дальше уже разные типы токенов, комментарии и другие возможности парсера.

PS: На C++ не программирую, возможно чего-то не понял.

Каким образом у вас реализовано восстановление после обнаружения ошибки?

Пока парсер просто при ошибке завершает свою работу и выдаёт на выходе сообщение о том где парсинг остановился.

Например что будет если пользователь передаст в ваш парсер вот такой JSON: { "array": [ 1, 2 3, 4], "object": { "a": true "b": null } }.

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

{"ok":false,"date":"2025.07.09 17:34:37"}@@@{"offset":15}
{"line":0,"pos":15}
"{ \"array\": [ 1, 2 3, 4], \"object\": { \"a\": true \"b\": null } }" EOD
------------------^

С точки зрения пользователя ожидается что парсер вернет обе ошибки, а так же какие точены парсер ожидал получить в этих местах.

Не, пока я ещё до такой круто-ты не дошёл, но планирую так сделать. В этом вроде нет ничего сложного, работы на несколько часов.

Я не совсем понимаю как работают правила t_true, t_null, t_string и т.д. Какой объект я получаю, когда пишу подобное правило

Это наследник полиморфного лексера, их вызывают когда какой-то лексер завернул поддерживаемый ими интерфейс в умный указатель, например вот так:TAutoPtr<i_value>

как мне получить информацию о том, в каком месте исходного текста находится указанный токен.

Пока такое не реализовано, но в этом тоже не особо много трудностей/работы. Тоже планирую такое сделать.

В идеале у меня должен быть доступ не только с пробельным символам, но и к следующему/предыдущему токенам.

При хорошей архитектуре во время обхода дерева несложно реализовать такую логику по запоминанию и предыдущего/следующего токена/лексера/лексемы. Делать как вы просите - это плохой дизайн с моей точки зрения, на данный момент, может когда-то я передумаю.

Не нашел ничего про инкрементальный парсинг. Это важный момент для работы с большими файлами.

Большие файлы, это не то для чего предназначен мой генератор парсеров. Поэтому такой фишке пока нет и не особо-то планируется, но если вам надо то я могу такое сделать.

Очень необычный синтаксис.

Это почти обычный С++ код. Из отличий только то что можно вставлять символы которые надо поглотить и то что писать struct не обязательное требование.

Не хватает простых примеров, на которых можно будет по шагам разобраться какие конструкции чему соответствуют. Например, начать с простого парсера который парсит только true и false, затем добавить возможность парсить еще и числа, затем массив разделенный запятыми, потом показать как работать с кардинальностью (? / + / *) и альтернативами. Дальше уже разные типы токенов, комментарии и другие возможности парсера.

Классная идея, спасибо за неё, обязательно такое сделаю.

Очень необычный синтаксис. Не хватает простых примеров, на которых можно будет по шагам разобраться какие конструкции чему соответствуют. Например, начать с простого парсера который парсит только true и false, затем добавить возможность парсить еще и числа, затем массив разделенный запятыми, потом показать как работать с кардинальностью (? / + / *) и альтернативами. Дальше уже разные типы токенов, комментарии и другие возможности парсера.

Вот сделал:

//парсим только true и false
t_bool{string s=any_str_from_vec(split("true,false",",");}
t_root{t_bool b;}

// с возможностью парсить еще и числа
t_bool:i_value{string s=any_str_from_vec(split("true,false",",");}
t_num:i_value{string s=any(gen_dips("09"));}
t_root{TAutoPtr<i_value> value;}

// про массив разделенный запятыми
t_bool:i_value{string s=any_str_from_vec(split("true,false",",");}
t_num:i_value{string s=any(gen_dips("09"));}
t_arr:i_value{vector<TAutoPtr<i_value>> arr=vec(",");}
t_root{TAutoPtr<i_value> value;}

// опциональные префиксы
t_optional_test{t_opt{"optional_prefix "} t_opt prefix?; string num=any(gen_dips("09"));}
t_root{t_optional_test value;}

// парсер комментариев
t_your_comment{
  "/*"
  string body=end("*/");
}

я тут баги нашёл и опечатки вот исправленный вариант:

t_bool{string s=any_str_from_vec(split("true,false",","));}
t_root{t_bool b;}

// с возможностью парсить еще и числа
t_bool:i_value{string s=any_str_from_vec(split("true,false",","));}
t_num:i_value{string s=any(gen_dips("09"));}
t_root{TAutoPtr<i_value> value;}

// про массив разделенный запятыми
t_bool:i_value{string s=any_str_from_vec(split("true,false",","));}
t_num:i_value{string s=any(gen_dips("09"));}
t_arr:i_value{
  t_num_arr_val:i_arr_val{t_num n;}
  t_bool_arr_val:i_arr_val{t_bool b;}
  vector<TAutoPtr<i_arr_val>> arr=vec(",");// используем i_arr_val вместо i_value чтобы избежать рекурсии.
}
t_root{TAutoPtr<i_value> value;}

// опциональные префиксы
t_optional_test{t_opt{"optional_prefix "} TAutoPtr<t_opt> prefix?; t_num num;}
t_root{t_optional_test value;}

// парсер комментариев
t_your_comment{
  "/*"
  string body=end("*/");
}

Приветствую! Очень интересно! А что-то вроде Паскаля / Бейсика можно сделать на нем?

Звезду в гитхабе поставил )

А что-то вроде Паскаля / Бейсика можно сделать на нем?

Конечно можно. Это же не какие-то питоны со своими пробелами.
Вот пример с чего можно начать(это простой парсер Delphi7 модулей, парсит все объявления, а всю имплментацию пока просто загоняет в строку):

t_sep{
  t_comment:i_sep{"{" string s=end("}");}
  t_c_comment:i_sep{"//" string body=any(dip_inv("\n\r"));}
  t_raw_sep:i_sep{string s=any(" \r\n\t");}
  string value=str<vector<TAutoPtr<i_sep>>>();
}
using " " as t_sep;
t_name{string s=any(gen_dips("azAZ09"));}
t_name_with_sep{" "? t_name n; " "?}
t_class_decl:i_type_decl{
  t_of:i_class_impl{"of" " "? t_name n; " "? ";"}
  t_semicolon:i_class_impl{";"}
  t_class_body:i_class_impl{
    "("
    " "?
    vector<t_name_with_sep> interfaces=vec(",");
    " "?
    ")"
    string body=end("end;"); // заглушка, просто читаем всё до конца пока.
  }
  t_name name;
  " "?
  "="
  " "?
  "class"
  " "?
  TAutoPtr<i_class_impl> impl?;
}
t_field{" "? t_name name; " "? ":" " "? t_name type; " "? ";" " "?}
t_record_decl:i_type_decl{
  t_name name;
  " "?
  "="
  " "?
  "record"
  " "?
  vector<t_field> fields?;
  "end"
  " "?
  ";"
}
t_proc_decl:i_type_decl{
  t_of{" "? "of" " "? t_name n; " "?}
  t_name proc_name;
  " "?
  "="
  " "?
  "procedure"
  " "?
  "("
  t_name param_name;
  " "?
  ":"
  " "?
  t_name param_type;
  ")"
  TAutoPtr<t_of> of?;
  ";"
}
t_type_ptr_decl:i_type_decl{
  t_name name;
  " "?
  "="
  " "?
  "^"
  t_name type;
  " "?
  ";"
}
t_array_decl:i_type_decl{
  t_name name;
  " "?
  "="
  " "?
  "array"
  " "?
  "["
  " "?
  t_name from;
  " "?
  ".."
  " "?
  t_name to;
  " "?
  "]"
  " "?
  "of"?
  " "?
  t_name type;
  " "?
  ";"
}
t_sep_type_decl:i_type_decl{" "}
t_types:i_decl{
  "type"
  " "?
  vector<i_type_decl> arr;
}
t_var:i_decl{
  "var"
  vector<t_field> fields;
}
t_func:i_decl{
  t_ret_type{":" t_name t;}
  string kb=any_str_from_vec(split("function,procedure",","));
  " "?
  t_name name;
  " "?
  "("
  string params=end(")"); // заглушка
  " "?
  TAutoPtr<t_ret_type> type?;
  " "?
  ";"
}
t_sep_decl:i_decl{" "}
t_unit{
  "unit"
  " "?
  t_name name;
  ";"
  " "?
  "interface"
  " "?
  "uses"
  vector<t_name_with_sep> modules_decl=vec(",");
  ";"
  " "?
  vector<i_decl> decls_decl?;
  "implementation"
  " "?
  "uses"
  vector<t_name_with_sep> modules_impl=vec(",");
  ";"
  " "?
  vector<i_decl> decls_impl?;
  string body=end("end."); // заглушка
}

писал быстро(2.5 часа) из которых 40 минут грамматика, а всё остальное отладка грамматики.

тестировал вот на этом файле: Basa.pas

Sign up to leave a comment.

Articles