Skip to content

Instantly share code, notes, and snippets.

@tcortega
Created December 1, 2023 23:17
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save tcortega/7f5ff7c49aa8fa718ec8064545c85fd3 to your computer and use it in GitHub Desktop.
Save tcortega/7f5ff7c49aa8fa718ec8064545c85fd3 to your computer and use it in GitHub Desktop.
%{
const symbols = {};
let currentScope = 0;
let nextScope = 1;
let lastType = null;
function enterScope() {
currentScope = nextScope;
nextScope++;
}
function exitScope() {
for (const key in symbols) {
if (symbols[key].scope === currentScope) {
delete symbols[key];
}
}
currentScope--;
}
function addSymbol(id, type) {
const scopedId = id + "@" + currentScope;
if (symbols[scopedId]) {
throw new Error(`Simbolo "${id}" já declarado neste escopo`);
}
symbols[scopedId] = { type, scope: currentScope };
}
function checkSymbol(id) {
let scope = currentScope;
while (scope >= 0) {
const scopedId = id + "@" + scope;
if (symbols[scopedId]) {
return symbols[scopedId];
}
scope--;
}
throw new Error(`Simbolo "${id}" ainda não foi declarado`);
}
function checkTypeCompatibility(type1, type2) {
if (type1 === type2) {
return;
}
if ((type1 === 'double' && type2 === 'float') || (type1 === 'float' && type2 === 'double')) {
return;
}
throw new Error(`Erro de tipagem: ${type1} vs ${type2}`);
}
function printSymbolTable() {
console.log('Tabela de Simbolos:');
for (const [key, value] of Object.entries(symbols)) {
console.log(` ${key}: ${JSON.stringify(value)}`);
}
}
%}
%lex
%%
\s+ /* ignore */
\"([^"\\]|\\.)*\" return 'STRING_LIT'
"int" return 'INT'
"void" return 'VOID'
"#include" return 'INCLUDE'
"#define" return 'DEFINE'
"<".*">" return 'PREPROCESSOR'
"double" return 'DOUBLE'
"float" return 'FLOAT'
"'"[^']"'" return 'CHAR_LIT'
"char" return 'CHAR'
"if" return 'IF'
"switch" return 'SWITCH'
"case" return 'CASE'
"break" return 'BREAK'
"default" return 'DEFAULT'
"else" return 'ELSE'
"while" return 'WHILE'
"for" return 'FOR'
"Variável" return 'VAR'
"return" return 'RETURN'
"do while" return 'DO_WHILE'
"do" return 'DO'
"#" return '#'
"define" return 'DEFINE'
"//"[^\n]* /* Ignore single-line comments */
[a-zA-Z][a-zA-Z0-9_]* return 'IDF'
[0-9]*\.[0-9]+([eE][+-][0-9]+)? return 'F_LIT'
[0-9]+ return 'INT_LIT'
"++" return 'INCREMENT'
"--" return 'DECREMENT'
"+=" return 'PLUS_ASSIGN'
"-=" return 'MINUS_ASSIGN'
"*" return '*'
"+" return '+'
"-" return '-'
"/" return '/'
"%" return 'MOD'
"," return ','
";" return ';'
":" return ':'
"." return '.'
"'" return 'QUOTE'
'"' return 'DQUOTE'
"(" return '('
")" return ')'
"[" return '['
"]" return ']'
"{" return '{'
"}" return '}'
"<=" return 'LE'
">=" return 'GE'
"==" return 'EQ'
"!=" return 'NE'
"<" return '<'
">" return '>'
"=" return '='
"&&" return 'AND'
"||" return 'OR'
"!" return 'NOT'
<<EOF>> return 'EOF'
/lex
%left 'AND' 'OR'
%left '<' '>' 'EQ' 'NE' 'LE' 'GE'
%left '+' '-'
%left '*' '/'
%nonassoc 'UMINUS' // To handle unary minus
%start program
%%
program
: statement_list EOF { enterScope(); printSymbolTable(); exitScope(); }
;
statement_list
: statement
| statement_list statement
;
statement
: var_declaration
| function_declaration
| assignment ';'
| function_call_statement
| if_statement
| switch_statement
| loop
| preprocessing_statement
| block
| return_statement
;
preprocessing_statement
: INCLUDE '<' IDF '.' IDF '>' ';'
| DEFINE IDF INT_LIT
| PREPROCESSOR
| INCLUDE PREPROCESSOR
;
block
: '{' '}' // Allow empty block
| '{' statement_list '}'
;
var_declaration
: type var_list ';'
;
var_list
: var_init
| var_list ',' var_init
;
var_init
: IDF { addSymbol($1, lastType); $$ = { id: $1, type: lastType }; }
| IDF '=' expression { addSymbol($1, lastType); checkSymbol($1); checkTypeCompatibility(symbols[$1 + "@" + currentScope].type, $3.type); $$ = { id: $1, type: symbols[$1 + "@" + currentScope].type }; }
| IDF '[' INT_LIT ']' { addSymbol($1, lastType + "[]"); $$ = { id: $1, type: lastType + "[]" }; }
| IDF '[' ']' '=' '{' array_init_list '}' { addSymbol($1, lastType + "[]"); $$ = { id: $1, type: lastType + "[]" }; } // Handle array initialization
| IDF '[' IDF ']' { checkSymbol($3); checkTypeCompatibility(symbols[$3 + "@" + currentScope].type, 'int'); addSymbol($1, lastType + "[]"); $$ = { id: $1, type: lastType + "[]" }; }
;
id_list
: IDF
| id_list ',' IDF
;
array_init_list
: expression
| array_init_list ',' expression
;
type
: INT { lastType = 'int'; }
| DOUBLE { lastType = 'double'; }
| FLOAT { lastType = 'float'; }
| CHAR { lastType = 'char'; }
| VOID { lastType = 'void'; }
;
assignment
: IDF '=' expression { checkSymbol($1); checkTypeCompatibility(symbols[$1 + "@" + currentScope].type, $3.type); }
| IDF 'INCREMENT' { checkSymbol($1); }
| IDF 'DECREMENT' { checkSymbol($1); }
| IDF 'PLUS_ASSIGN' expression { checkSymbol($1); }
| IDF 'MINUS_ASSIGN' expression { checkSymbol($1); }
;
expression
: IDF { const symbolIDF = checkSymbol($1); $$ = { type: symbolIDF.type }; }
| INT_LIT { $$ = { type: 'int' }; }
| F_LIT { $$ = { type: 'float' }; }
| CHAR_LIT { $$ = { type: 'char' }; }
| STRING_LIT { $$ = { type: 'string' }; }
| IDF '[' expression ']' { const symbolExpr = checkSymbol($1); if (!symbolExpr.type.endsWith('[]')) throw new Error(`Type of ${$1} is not an array`); $$ = { type: symbolExpr.type.slice(0, -2) }; }
| expression 'MOD' expression { checkTypeCompatibility($1.type, $3.type); $$ = { type: $1.type }; }
| expression '+' expression { checkTypeCompatibility($1.type, $3.type); $$ = { type: $1.type }; }
| expression '-' expression { checkTypeCompatibility($1.type, $3.type); $$ = { type: $1.type }; }
| expression '*' expression { checkTypeCompatibility($1.type, $3.type); $$ = { type: $1.type }; }
| expression '/' expression { checkTypeCompatibility($1.type, $3.type); $$ = { type: $1.type }; }
| expression 'EQ' expression { checkTypeCompatibility($1.type, $3.type); $$ = { type: 'int' }; }
| expression 'NE' expression { checkTypeCompatibility($1.type, $3.type); $$ = { type: 'int' }; }
| expression '<' expression { checkTypeCompatibility($1.type, $3.type); $$ = { type: 'int' }; }
| expression '>' expression { checkTypeCompatibility($1.type, $3.type); $$ = { type: 'int' }; }
| expression 'LE' expression { checkTypeCompatibility($1.type, $3.type); $$ = { type: 'int' }; }
| expression 'GE' expression { checkTypeCompatibility($1.type, $3.type); $$ = { type: 'int' }; }
| expression 'AND' expression { checkTypeCompatibility($1.type, $3.type); $$ = { type: 'int' }; }
| expression 'OR' expression { checkTypeCompatibility($1.type, $3.type); $$ = { type: 'int' }; }
| '(' expression ')' { $$ = $2; }
| '-' expression %prec UMINUS { checkTypeCompatibility($2.type, 'int'); $$ = { type: 'int' }; }
| '(' type ')' expression { checkTypeCompatibility($2, $4.type); $$ = { type: $2 }; }
| IDF 'INCREMENT' { checkSymbol($1); $$ = { type: 'int' }; }
| IDF 'DECREMENT' { checkSymbol($1); $$ = { type: 'int' }; }
| 'NOT' expression { checkTypeCompatibility($2.type, 'int'); $$ = { type: 'int' }; }
| function_call { $$ = $1; }
;
if_statement
: IF '(' expression ')' '{' statement_list '}' { checkTypeCompatibility($3.type, 'int'); }
| IF '(' expression ')' statement { checkTypeCompatibility($3.type, 'int'); }
| IF '(' expression ')' '{' statement_list '}' ELSE '{' statement_list '}' { checkTypeCompatibility($3.type, 'int'); }
| IF '(' expression ')' statement ELSE statement { checkTypeCompatibility($3.type, 'int'); }
| IF '(' expression ')' '{' statement_list '}' ELSE if_statement { checkTypeCompatibility($3.type, 'int'); }
;
switch_statement
: SWITCH '(' expression ')' '{' case_statements '}' { checkTypeCompatibility($3.type, 'int'); }
| SWITCH '(' expression ')' statement { checkTypeCompatibility($3.type, 'int'); }
;
case_statements
: CASE expression ':' { checkTypeCompatibility($2.type, 'int'); }
| CASE expression ':' statement_list { checkTypeCompatibility($2.type, 'int'); }
| CASE expression ':' BREAK ';' { checkTypeCompatibility($2.type, 'int'); }
| DEFAULT ':' { }
| DEFAULT ':' statement_list
| DEFAULT ':' BREAK ';' // Allow 'default: break;' without additional code
| case_statements CASE expression ':' { checkTypeCompatibility($3.type, 'int'); }
| case_statements CASE expression ':' statement_list { checkTypeCompatibility($3.type, 'int'); }
| case_statements CASE expression ':' BREAK ';' { checkTypeCompatibility($3.type, 'int'); }
| case_statements DEFAULT ':' { }
| case_statements DEFAULT ':' statement_list
| case_statements DEFAULT ':' BREAK ';' // Allow subsequent 'default: break;' without additional code
;
loop
: WHILE '(' expression ')' statement { checkTypeCompatibility($3.type, 'int'); }
| FOR '(' for_init ';' expression ';' assignment ')' statement { checkTypeCompatibility($5.type, 'int'); }
| DO '{' statement_list '}' WHILE '(' expression ')' ';' { checkTypeCompatibility($7.type, 'int'); }
;
for_init
: type IDF '=' expression { addSymbol($2, $1); $$ = { id: $2, type: $1 }; } // Handle variable declaration and initialization
| assignment // Handle assignment (for existing variables)
;
function_call_statement
: function_call ';'
;
function_call
: IDF '(' ')' { const symbolFuncCall = checkSymbol($1); if (!symbolFuncCall.type.startsWith('function')) throw new Error(`Function ${$1} not defined`); $$ = { type: symbolFuncCall.type.split(':')[1] }; }
| IDF '(' argument_list ')' { const symbolFuncCall2 = checkSymbol($1); if (!symbolFuncCall2.type.startsWith('function')) throw new Error(`Function ${$1} not defined`); $$ = { type: symbolFuncCall2.type.split(':')[1] }; }
;
argument_list
: expression { $$ = [$1]; }
| argument_list ',' expression { $1.push($3); $$ = $1; }
;
function_declaration
: function_definition
| function_prototype
;
function_definition
: type IDF '(' parameters ')' '{' statement_list '}' {
addSymbol($2, `function:${$1}`);
enterScope();
$4.forEach(param => addSymbol(param.id, param.type)); // $4 is the parameters, which is now always an array
$$ = { id: $2, type: `function:${$1}`, parameters: $4 };
exitScope();
}
;
function_prototype
: type IDF '(' parameters ')' ';' {
addSymbol($2, `function:${$1}`);
$$ = { id: $2, type: `function:${$1}` };
}
;
parameter_declaration
: type IDF { addSymbol($2, $1); $$ = { id: $2, type: $1 }; }
| type IDF '[' ']' { addSymbol($2, `${$1}[]`); $$ = { id: $2, type: `${$1}[]` }; }
;
parameters
: /* empty */ { $$ = []; }
| parameter_declaration { $$ = [$1]; }
| parameters ',' parameter_declaration { $1.push($3); $$ = $1; }
;
parameter_list
: parameter { $$ = [$1]; }
| parameter_list ',' parameter { $1.push($3); $$ = $1; }
;
parameter
: type IDF { $$ = { id: $2, type: $1 }; }
| type IDF '[' ']' { $$ = { id: $2, type: `${$1}[]` }; }
;
return_statement
: RETURN ';' { $$ = 'return'; }
| RETURN expression ';' { $$ = `return ${$2}`; }
;
%%
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment