Как правильно обрабатывать ошибки в C, как языковой компилятор с antlr4? - PullRequest
0 голосов
/ 17 ноября 2018

Я работаю над компилятором языка программирования Dedalus (это язык сценариев серии Gothic), который у deadalus lang фактически аналогичен грамматике языка Си. Мой компилятор использует antlr4 для создания дерева AST. Здесь вы можете взглянуть на грамматику, которую я использую:

grammar Daedalus;

// lexer
Const : 'const' | 'CONST';
Var: 'var' | 'VAR';
If : 'if' | 'IF';
Int: 'int' | 'INT';
Else: 'else' | 'ELSE';
Func: 'func' | 'FUNC';
String: 'string' | 'STRING';
Class: 'class' | 'CLASS';
Void: 'void' | 'VOID';
Return: 'return' | 'RETURN';
Float: 'float' | 'FLOAT';
Prototype: 'prototype' | 'PROTOTYPE';
Instance: 'instance' | 'INSTANCE';
Null: 'null' | 'Null';

Identifier : IdStart IdContinue*;
IntegerLiteral : Digit+;
FloatLiteral : PointFloat | ExponentFloat;
StringLiteral : '"' (~["\\\r\n] | '\\' (. | EOF))* '"';

  Whitespace : [ \t]+ -> skip;
Newline : ('\r''\n'?| '\n') -> skip;
BlockComment :   '/*' .*? '*/' -> skip;
LineComment :   '//' ~[\r\n]* -> skip ;

// fragments
fragment IdStart : GermanCharacter | [a-zA-Z_];
fragment IdContinue : IdStart | Digit;
fragment GermanCharacter : [\u00DF\u00E4\u00F6\u00FC]; //����
fragment Digit : [0-9];
fragment PointFloat : Digit* '.' Digit+ | Digit+ '.';
fragment ExponentFloat : (Digit+ | PointFloat) Exponent;
fragment Exponent : [eE] [+-]? Digit+;


//parser
daedalusFile: (( functionDef | constDef | varDecl | classDef | prototypeDef | instanceDef | instanceDecl )';')*?;

functionDef: Func typeReference nameNode parameterList statementBlock;
constDef: Const typeReference (constValueDef | constArrayDef) (',' (constValueDef | constArrayDef) )*;
classDef: Class nameNode '{' ( varDecl ';' )*? '}';
prototypeDef: Prototype nameNode '(' parentReference ')' statementBlock;
instanceDef: Instance nameNode '(' parentReference ')' statementBlock;
instanceDecl: Instance nameNode ( ',' nameNode )*? '(' parentReference ')';
varDecl: Var typeReference (varValueDecl | varArrayDecl) (',' (varValueDecl | varArrayDecl) )* ;

constArrayDef: nameNode '[' arraySize ']' constArrayAssignment;
constArrayAssignment: '=' '{' ( expressionBlock (',' expressionBlock)*? ) '}';

constValueDef: nameNode constValueAssignment;
constValueAssignment: '=' expressionBlock;

varArrayDecl: nameNode '[' arraySize ']';
varValueDecl: nameNode;

parameterList: '(' (parameterDecl (',' parameterDecl)*? )? ')';
parameterDecl: Var typeReference nameNode ('[' arraySize ']')?;
statementBlock: '{' ( ( (statement ';')  | ( ifBlockStatement ( ';' )? ) ) )*? '}';
statement: assignment | returnStatement | constDef | varDecl | expression;
funcCall: nameNode '(' ( funcArgExpression ( ',' funcArgExpression )*? )? ')';
assignment: referenceLeftSide assignmentOperator expressionBlock;
ifCondition: expressionBlock;
elseBlock: Else statementBlock;
elseIfBlock: Else If ifCondition statementBlock;
ifBlock: If ifCondition statementBlock;
ifBlockStatement: ifBlock ( elseIfBlock )*? ( elseBlock )?;
returnStatement: Return ( expressionBlock )?;

funcArgExpression: expressionBlock; // we use that to detect func call args
expressionBlock: expression; // we use that expression to force parser threat expression as a block

expression
  : '(' expression ')' #bracketExpression
| oneArgOperator expression #oneArgExpression
| expression multOperator expression #multExpression
| expression addOperator expression #addExpression
| expression bitMoveOperator expression #bitMoveExpression
| expression compOperator expression #compExpression
| expression eqOperator expression #eqExpression
| expression binAndOperator expression #binAndExpression
| expression binOrOperator expression #binOrExpression
| expression logAndOperator expression #logAndExpression
| expression logOrOperator expression #logOrExpression
| value #valExpression
;

arrayIndex : IntegerLiteral | referenceAtom;
arraySize : IntegerLiteral | referenceAtom;

value
  : IntegerLiteral #integerLiteralValue
| FloatLiteral #floatLiteralValue
| StringLiteral #stringLiteralValue
| Null #nullLiteralValue
| funcCall #funcCallValue
| reference #referenceValue
;

referenceAtom: Identifier ( '[' arrayIndex ']')?;
reference: referenceAtom ( '.' referenceAtom )?;
referenceLeftSide: referenceAtom ( '.' referenceAtom )?;

typeReference:  ( Identifier | Void | Int | Float | String | Func | Instance);

nameNode: Identifier;

parentReference: Identifier;

assignmentOperator:  '=' | '+=' | '-=' | '*=' | '/=';
addOperator: '+' | '-';
bitMoveOperator: '<<' | '>>';
compOperator: '<' | '>' | '<=' | '>=';
eqOperator: '==' | '!=';
oneArgOperator: '-' | '!' | '~' | '+';
multOperator: '*' | '/' | '%';
binAndOperator: '&';
binOrOperator: '|';
logAndOperator: '&&';
logOrOperator: '||';

(ссылка на проект: https://github.com/dzieje-khorinis/DaedalusCompiler)

Давайте предположим, что я хочу скомпилировать такой неправильный код (назначение b не имеет точки с запятой):

func void test() {
    b = 7
};

Antlr выдает мне следующие ошибки:

line 3:0 extraneous input '}' expecting ';' line 3:2 mismatched input '<EOF>' expecting {'}', '(', '+', '-', '!', '~', Const, Var, If, Return, Null, Identifier, IntegerLiteral, FloatLiteral, StringLiteral}

Дерево: enter image description here

Первая ошибка понятна для меня, после 7 она должна быть ;, также из-за того, что у нас нет точки с запятой, дерево AST нарушено, а следующая } рассматривается как часть блока операторов. Но я ожидаю трактовать это }; как закрывающий элемент функции, поэтому ошибка должна указывать только на проблему с точкой с запятой для выражения x=5.

Другая проблема возникает, когда у меня следующий код:

func void test() {

Antlr бросков:

line 1:18 mismatched input '<EOF>' expecting {'}', '(', '+', '-', '!', '~', Const, Var, If, Return, Null, Identifier, IntegerLiteral, FloatLiteral, StringLiteral}

Эта ошибка мне кажется очевидной, потому что в теле функции мы можем определить выражения, но это не обязательно, antlr должен сказать, что мы должны поставить }; в конце, чтобы закрыть функцию и сделать грамматику правильной.

Как сделать умнее распознавание ошибок в antlr?

...