Определение сигнатур функций в грамматике простого языка

В настоящее время я учусь создавать простой язык выражений с использованием иронии. У меня возникли небольшие проблемы с поиском наилучшего способа определения сигнатур функций и определением ответственности за проверку входных данных для этих функций.

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

<expression> ::= <number> | <parenexp> | <binexp> | <fncall> | <identifier>
<parenexp>   ::= ( <expression> )
<fncall>     ::= <identifier> ( <argumentlist> )
<binexp>     ::= <expression> <binop> <expression>
<binop>      ::= + - * / %
... the rest of the grammar definition

Используя парсер Irony, я могу проверить синтаксис различных входных строк, чтобы убедиться, что они соответствуют этой грамматике:

x + y / z * AVG(a + b, p)   -> Valid Syntax
x +/ AVG(x                  -> Invalid Syntax

Все это хорошо, но теперь я хочу пойти дальше и определить доступные функции, а также количество параметров, необходимых для каждой функции. Так, например, я хочу иметь функцию FOO который принимает один параметр и BAR который принимает два параметра:

FOO(a + b) * BAR(x + y, p + q)    -> Valid
FOO(a + b, 13)                    ->  Invalid

Когда второй оператор проанализирован, я хотел бы иметь возможность вывести сообщение об ошибке, которое знает об ожидаемом вводе для этой функции:

Too many arguments specified for function 'FOO'

На самом деле мне не нужно оценивать какие-либо из этих утверждений, я только проверяю синтаксис операторов и определяю, являются ли они действительными выражениями или нет.

Как именно я должен это делать? Я знаю, что технически я мог бы просто добавить функции к грамматике следующим образом:

<foofncall> ::= FOO( <expression> )
<barfncall> ::= BAR( <expression>, <expression> )

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

  • Как это обычно делается на других языках?
  • Как называются компоненты, которые должны выполнять обязанности по анализу основного синтаксиса грамматики языка по сравнению с более конкретными элементами, такими как определения функций? Должны ли обе обязанности обрабатываться одним и тем же компонентом?

1 ответ

Решение

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

В обычном случае компилятора синтаксический анализатор просто создает абстрактное синтаксическое дерево или некоторое эквивалентное представление программы. Затем проход проверки типов выполняется по AST, что обеспечивает надлежащее совпадение всех типов - гарантирует, что функции имеют правильное количество аргументов, и эти аргументы имеют правильный тип, а также гарантирует, что переменные имеют правильный тип для того, что назначено их и как они используются.

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

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