Грамматика ANTLR C не распознает точечную нотацию

Мы используем ANTLR для синтаксического анализа C, и большая часть нашего кода имеет точечную нотацию для структур. Прошло некоторое время с тех пор, как я написал C, но насколько я помню, эти два утверждения синонимичны:

void hello() {
    this->hello = "hello";
    this.hello = "hello";
}

ANTLR может анализировать greeting->hello без каких-либо проблем, однако точечная нотация вызывает следующую ошибку:

line 3:4 mismatched input 'this.hello' expecting '}'

Если мы поменяем операторы следующим образом:

void hello() {
    this.hello = "hello";
    this->hello = "hello";
}

Ошибки:

line 2:4 mismatched input 'this.hello' expecting {'__extension__', '__builtin_va_arg', '__builtin_offsetof', '__m128', '__m128d', '__m128i', '__typeof__', '__inline__', '__stdcall', '__declspec', '__asm', '__attribute__', '__asm__', 'auto', 'break', 'case', 'char', 'const', 'continue', 'default', 'do', 'double', 'enum', 'extern', 'float', 'for', 'goto', 'if', 'inline', 'int', 'long', 'register', 'restrict', 'return', 'short', 'signed', 'sizeof', 'static', 'struct', 'switch', 'typedef', 'union', 'unsigned', 'void', 'volatile', 'while', '_Alignas', '_Alignof', '_Atomic', '_Bool', '_Complex', '_Generic', '_Noreturn', '_Static_assert', '_Thread_local', '(', '{', '}', '+', '++', '-', '--', '*', '&', '&&', '!', '~', ';', Identifier, Constant, DigitSequence, StringLiteral}
line 3:8 no viable alternative at input 'this->'
line 4:0 extraneous input '}' expecting <EOF>

Мы используем Грамматика C из Репозиторий ANTLR Grammars. При этом мы настроили его для обработки операторов #include, и его можно увидеть здесь. Мы добавили эти два парсера и эти два лексера:

includeExpression
    : IncludeDirective includedLibExpression '"'
    | IncludeDirective includedLibExpression '>'
    ;

includedLibExpression
    : IncludedHeaderDirective
    ;

IncludeDirective
    : '#' Whitespace? 'include' Whitespace '"'
    | '#' Whitespace? 'include' Whitespace '<'
    ;

IncludedHeaderDirective
    : ('a'..'z' | 'A'..'Z' | '.' | '_' | '/')+
    ;

Затем, чтобы использовать новые парсеры, мы добавили в translationUnit следующее. Чтобы еще больше запутать ситуацию, если строка с includeExpression в translationUnit закомментирована, мы все равно получим ошибки.

translationUnit
    :   externalDeclaration
    |   translationUnit externalDeclaration
    |   includeExpression+?
    ;

Конкретный парсер, который должен это уловить, таков:

postfixExpression
    :   primaryExpression
    |   postfixExpression '[' expression ']'
    |   postfixExpression '(' argumentExpressionList? ')'
    |   postfixExpression '.' Identifier
    |   postfixExpression '->' Identifier
    |   postfixExpression '++'
    |   postfixExpression '--'
    |   '(' typeName ')' '{' initializerList '}'
    |   '(' typeName ')' '{' initializerList ',' '}'
    |   '__extension__' '(' typeName ')' '{' initializerList '}'
    |   '__extension__' '(' typeName ')' '{' initializerList ',' '}'
;

Что меня действительно озадачивает, так это то, что точечное обозначение и обозначение стрелок идут друг за другом, но распознаются только обозначения стрелок.

«эти два утверждения синонимичны» - это не так, но это зависит от типа greeting, поэтому не должно иметь значения для грамматики. Вы используете точку, когда приветствие является структурой (или в C++ ссылкой на структуру), а стрелку, когда приветствие является указателем на структуру.

Rup 17.09.2018 15:21

Rup прав (я собирался сказать то же самое), но это не должно вызывать ошибку разбирать. Замечу, что ваше сообщение об ошибке не соответствует вашему коду: this.hello вместо greeting.hello. Вы уверены, что получили тот результат, который вы указали?

zwol 17.09.2018 15:23

Ааа сегодня узнал. Это очень похожий код, который я просто написал в файле на своей машине, а здесь написал немного по-другому. Я обновлю ОП.

skylerl 17.09.2018 15:23

Вы изменили что-нибудь в грамматике или используете связанную грамматику как есть? Каким типом токена распознается this.hello?

sepp2k 17.09.2018 15:33

Где в грамматике обрабатывается точка с запятой? Я не вижу правила для этого

Ctx 17.09.2018 15:34

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

Antti Haapala 17.09.2018 15:39

Вы, ребята, определенно что-то поняли ... Я забыл, что мы адаптировали грамматику для обработки операторов include для нашего варианта использования. Я добавил ту грамматику, которую мы изменили, до сути. Что касается отмены заявлений, я обновлю OP.

skylerl 17.09.2018 15:45
Стоит ли изучать PHP в 2023-2024 годах?
Стоит ли изучать PHP в 2023-2024 годах?
Привет всем, сегодня я хочу высказать свои соображения по поводу вопроса, который я уже много раз получал в своем сообществе: "Стоит ли изучать PHP в...
Поведение ключевого слова "this" в стрелочной функции в сравнении с нормальной функцией
Поведение ключевого слова "this" в стрелочной функции в сравнении с нормальной функцией
В JavaScript одним из самых запутанных понятий является поведение ключевого слова "this" в стрелочной и обычной функциях.
Приемы CSS-макетирования - floats и Flexbox
Приемы CSS-макетирования - floats и Flexbox
Здравствуйте, друзья-студенты! Готовы совершенствовать свои навыки веб-дизайна? Сегодня в нашем путешествии мы рассмотрим приемы CSS-верстки - в...
Тестирование функциональных ngrx-эффектов в Angular 16 с помощью Jest
В системе управления состояниями ngrx, совместимой с Angular 16, появились функциональные эффекты. Это здорово и делает код определенно легче для...
Концепция локализации и ее применение в приложениях React ⚡️
Концепция локализации и ее применение в приложениях React ⚡️
Локализация - это процесс адаптации приложения к различным языкам и культурным требованиям. Это позволяет пользователям получить опыт, соответствующий...
Пользовательский скаляр GraphQL
Пользовательский скаляр GraphQL
Листовые узлы системы типов GraphQL называются скалярами. Достигнув скалярного типа, невозможно спуститься дальше по иерархии типов. Скалярный тип...
0
7
577
1
Перейти к ответу Данный вопрос помечен как решенный

Ответы 1

Ответ принят как подходящий

Вы добавили в грамматику следующее правило лексера:

IncludedHeaderDirective
    : ('a'..'z' | 'A'..'Z' | '.' | '_' | '/')+
    ;

Этот шаблон соответствует строке this.hello. Поэтому, когда лексер достигает строки 2 вашего ввода, он может либо применить правило Identifier для сопоставления с this, либо правило IncludeHeaderDirective для сопоставления с this.hello. Поскольку последний вариант является более длинным, он выбирается в соответствии с правилом максимального пережевывания.

Поскольку IncludedHeaderDirective не является допустимым выражением, вы получите ошибку, которую делаете. Чтобы соответствовать правилу postfixExpression '.' Identifier, this.hello должен был быть токенизирован как Identifier, '.', Identifier, но существование правила IncludedHeaderDirective препятствует этому.

Я понимаю, в этом есть смысл. Как лучше всего настроить этот лексер, чтобы он по-прежнему находил . в операторе #include, но не затрагивал точечную нотацию?

skylerl 17.09.2018 16:13

@skylerl Препроцессор действительно должен быть отдельной фазой. Вы можете решить вашу текущую проблему, используя лексические режимы, но это все равно не приведет вас к стандартной реализации препроцессора. Обратите внимание, например, что что-то вроде int x = \n#include "file-that-only-contains-the-number-42"\n; (где \n должны представлять фактические символы новой строки) является допустимым C. Другими словами: файлы C не должны быть синтаксически действительными до окончания предварительной обработки, и вы не можете реализовать это в одной грамматике. как это.

sepp2k 17.09.2018 16:23

Если я правильно помню и, как я уже сказал, мой C в настоящее время очень туманный, но препроцессор эффективно комбинирует связанный файл заголовка и файл C, который вы пишете, верно? Наш вариант использования - просто найти ссылки между FileA.c и FileB.h, если FileA.c имеет #include "FileB.h". Итак, вы говорите, что у нас должна быть грамматика для препроцессора и грамматика для правильного языка C?

skylerl 17.09.2018 16:32

@skylerl: Это частично зависит от сложности (зверства) кода, который вы анализируете. Полноценный компилятор C должен будет распознать все злоупотребления, которые может нанести ему непослушный программист. Ваш код может быть в порядке, будучи более слабым. (Сегодня утром я работал над самодельным синтаксическим анализатором C - кто-то в моем проекте создал его много лет назад для C90; его нужно немного обновить для C99, C11, C18! Это больно!) Синтаксис необработанного C не обязательно имеет большое отношение к предварительно обработанным C. #define IF if ( - #define THEN ) { - #define ELSE } else { - #define ENDIF }.

Jonathan Leffler 17.09.2018 16:58

@skylerl Препроцессор эффективно скопировал и вставил содержимое включенного файла в позицию директивы #include, да. Настоящий синтаксический анализ затем выполняется после этого копирования и вставки (и других заданий препроцессора, таких как раскрытие макросов). Итак, да, для правильной реализации этого процесса потребуется одна грамматика для препроцессора и другая для предварительно обработанного кода. Затем препроцессор мог бы создать поток токенов, который мог бы обработать настоящий синтаксический анализатор C (или он мог бы просто записать предварительно обработанный C в файл, который затем анализируется реальным ...

sepp2k 17.09.2018 17:37

... синтаксический анализатор C). Действительно ли вам все это нужно, зависит от того, что именно вы делаете, и, как уже указывал Джонатан Леффер, от того, нужно ли вашему инструменту обрабатывать все возможные файлы C. Если все, что вам нужно сделать, это выяснить, какой файл включает какие другие файлы, вам даже не нужно вообще разделять C-часть - только директиву препроцессора. Итак, все, что вам нужно, это грамматика для препроцессора.

sepp2k 17.09.2018 17:37

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