240 lines
7.9 KiB
Plaintext
240 lines
7.9 KiB
Plaintext
%{
|
|
#include "vslc.h"
|
|
|
|
// State variables from the flex generated scanner
|
|
extern int yylineno; // The line currently being read
|
|
extern char yytext[]; // The text of the last consumed lexeme
|
|
|
|
// The main flex driver function used by the parser
|
|
int yylex(void);
|
|
|
|
// The function called by the parser when errors occur
|
|
int yyerror(const char *error)
|
|
{
|
|
fprintf(stderr, "%s on line %d\n", error, yylineno);
|
|
exit(EXIT_FAILURE);
|
|
}
|
|
|
|
// Feel free to define #define macros if you want to
|
|
%}
|
|
|
|
%token FUNC VAR RETURN PRINT PRINTLN IF ELSE WHILE BREAK AND OR
|
|
%token NUMBER_TOKEN IDENTIFIER_TOKEN STRING_TOKEN
|
|
|
|
// Use operator precedence to ensure order of operations is correct
|
|
%left '?' ':' // The ternary ? : operator has the lowest precedence of them all
|
|
%left OR // Or has lower precedence than and, just like in C
|
|
%left AND
|
|
%left '=' '!' // == and !=
|
|
%left '<' '>' // < <= > and >=
|
|
%left '+' '-'
|
|
%left '*' '/'
|
|
%right UNARY_OPERATORS
|
|
|
|
// Resolve the nested if-if-else ambiguity with precedence
|
|
%nonassoc ')'
|
|
%nonassoc ELSE
|
|
|
|
%%
|
|
program :
|
|
global_list { root = $1; }
|
|
;
|
|
global_list :
|
|
global { $$ = node_create(LIST, 1, $1); }
|
|
| global_list global { $$ = append_to_list_node($1, $2); }
|
|
;
|
|
global :
|
|
function { $$ = $1; }
|
|
| global_variable_declaration { $$ = $1; }
|
|
;
|
|
global_variable_declaration :
|
|
VAR global_variable_list { $$ = node_create(GLOBAL_VARIABLE_DECLARATION, 1, $2); }
|
|
;
|
|
global_variable_list :
|
|
global_variable { $$ = node_create(LIST, 1, $1); }
|
|
| global_variable_list ',' global_variable { $$ = append_to_list_node($1, $3); }
|
|
;
|
|
global_variable :
|
|
identifier { $$ = $1; }
|
|
| array_indexing { $$ = $1; }
|
|
;
|
|
array_indexing :
|
|
identifier '[' expression ']' { $$ = node_create(ARRAY_INDEXING, 2, $1, $3); }
|
|
;
|
|
parameter_list :
|
|
identifier { $$ = node_create(LIST, 1, $1); }
|
|
| parameter_list ',' identifier { $$ = append_to_list_node($1, $3); }
|
|
| { $$ = node_create(LIST, 0); }
|
|
;
|
|
function :
|
|
FUNC identifier '(' parameter_list ')' statement { $$ = node_create(FUNCTION, 3, $2, $4, $6); }
|
|
;
|
|
statement :
|
|
block { $$ = $1; }
|
|
| assignment_statement { $$ = $1; }
|
|
| return_statement { $$ = $1; }
|
|
| print_statement { $$ = $1; }
|
|
| println_statement { $$ = $1; }
|
|
| if_statement { $$ = $1; }
|
|
| while_statement { $$ = $1; }
|
|
| break_statement { $$ = $1; }
|
|
| function_call { $$ = $1; }
|
|
;
|
|
block :
|
|
'{' statement_or_declaration_list '}' { $$ = node_create(BLOCK, 1, $2); }
|
|
;
|
|
statement_or_declaration_list :
|
|
statement_or_declaration_list statement_or_declaration { $$ = append_to_list_node($1, $2); }
|
|
| { $$ = node_create(LIST, 0); }
|
|
;
|
|
statement_or_declaration :
|
|
statement { $$ = $1; }
|
|
| local_variable_declaration { $$ = $1; }
|
|
;
|
|
local_variable_declaration :
|
|
VAR local_variable_list { $$ = node_create(LOCAL_VARIABLE_DECLARATION, 1, $2); }
|
|
;
|
|
local_variable_list :
|
|
local_variable { $$ = node_create(LIST, 1, $1); }
|
|
| local_variable_list ',' local_variable { $$ = append_to_list_node($1, $3); }
|
|
;
|
|
local_variable :
|
|
identifier { $$ = node_create(LOCAL_VARIABLE, 1, $1); }
|
|
| identifier '=' expression { $$ = node_create(LOCAL_VARIABLE, 2, $1, $3); }
|
|
;
|
|
assignment_statement :
|
|
identifier '=' expression { $$ = node_create(ASSIGNMENT_STATEMENT, 2, $1, $3); }
|
|
| array_indexing '=' expression { $$ = node_create(ASSIGNMENT_STATEMENT, 2, $1, $3); }
|
|
;
|
|
return_statement :
|
|
RETURN expression { $$ = node_create(RETURN_STATEMENT, 1, $2); }
|
|
;
|
|
print_statement :
|
|
PRINT '(' print_list ')' { $$ = node_create(PRINT_STATEMENT, 1, $3); }
|
|
;
|
|
println_statement :
|
|
PRINTLN '(' print_list ')' { $$ = node_create(PRINTLN_STATEMENT, 1, $3); }
|
|
;
|
|
print_list :
|
|
print_item { $$ = node_create(LIST, 1, $1); }
|
|
| print_list ',' print_item { $$ = append_to_list_node($1, $3); }
|
|
;
|
|
print_item :
|
|
expression { $$ = $1; }
|
|
| string { $$ = $1; }
|
|
;
|
|
break_statement :
|
|
BREAK { $$ = node_create(BREAK_STATEMENT, 0); }
|
|
;
|
|
if_statement :
|
|
IF '(' expression ')' statement { $$ = node_create(IF_STATEMENT, 2, $3, $5); }
|
|
| IF '(' expression ')' statement ELSE statement { $$ = node_create(IF_STATEMENT, 3, $3, $5, $7); }
|
|
;
|
|
while_statement :
|
|
WHILE '(' expression ')' statement { $$ = node_create(WHILE_STATEMENT, 2, $3, $5); }
|
|
;
|
|
expression :
|
|
expression '?' expression ':' expression {
|
|
$$ = node_create(OPERATOR, 3, $1, $3, $5);
|
|
$$->data.operator = "?:";
|
|
}
|
|
| expression OR expression {
|
|
$$ = node_create(OPERATOR, 2, $1, $3);
|
|
$$->data.operator = "or";
|
|
}
|
|
| expression AND expression {
|
|
$$ = node_create(OPERATOR, 2, $1, $3);
|
|
$$->data.operator = "and";
|
|
}
|
|
| expression '=' '=' expression {
|
|
$$ = node_create(OPERATOR, 2, $1, $4);
|
|
$$->data.operator = "==";
|
|
}
|
|
| expression '!' '=' expression {
|
|
$$ = node_create(OPERATOR, 2, $1, $4);
|
|
$$->data.operator = "!=";
|
|
}
|
|
| expression '<' expression {
|
|
$$ = node_create(OPERATOR, 2, $1, $3);
|
|
$$->data.operator = "<";
|
|
}
|
|
| expression '<' '=' expression {
|
|
$$ = node_create(OPERATOR, 2, $1, $4);
|
|
$$->data.operator = "<=";
|
|
}
|
|
| expression '>' expression {
|
|
$$ = node_create(OPERATOR, 2, $1, $3);
|
|
$$->data.operator = ">";
|
|
}
|
|
| expression '>' '=' expression {
|
|
$$ = node_create(OPERATOR, 2, $1, $4);
|
|
$$->data.operator = ">=";
|
|
}
|
|
| expression '+' expression {
|
|
$$ = node_create(OPERATOR, 2, $1, $3);
|
|
$$->data.operator = "+";
|
|
}
|
|
| expression '-' expression {
|
|
$$ = node_create(OPERATOR, 2, $1, $3);
|
|
$$->data.operator = "-";
|
|
}
|
|
| expression '*' expression {
|
|
$$ = node_create(OPERATOR, 2, $1, $3);
|
|
$$->data.operator = "*";
|
|
}
|
|
| expression '/' expression {
|
|
$$ = node_create(OPERATOR, 2, $1, $3);
|
|
$$->data.operator = "/";
|
|
}
|
|
| '-' expression {
|
|
$$ = node_create(OPERATOR, 1, $2);
|
|
$$->data.operator = "-";
|
|
} %prec UNARY_OPERATORS
|
|
| '!' expression {
|
|
$$ = node_create(OPERATOR, 1, $2);
|
|
$$->data.operator = "!";
|
|
} %prec UNARY_OPERATORS
|
|
| '(' expression ')' { $$ = $2; }
|
|
| number { $$ = $1; }
|
|
| identifier { $$ = $1; }
|
|
| array_indexing { $$ = $1; }
|
|
| function_call { $$ = $1; }
|
|
;
|
|
function_call :
|
|
identifier '(' argument_list ')' { $$ = node_create(FUNCTION_CALL, 2, $1, $3); }
|
|
;
|
|
argument_list :
|
|
expression_list { $$ = $1; }
|
|
| { $$ = node_create(LIST, 0); }
|
|
;
|
|
expression_list :
|
|
expression { $$ = node_create(LIST, 1, $1); }
|
|
| expression_list ',' expression { $$ = append_to_list_node($1, $3); }
|
|
;
|
|
identifier :
|
|
IDENTIFIER_TOKEN
|
|
{
|
|
// Create a node with 0 children to represent the identifier
|
|
$$ = node_create(IDENTIFIER, 0);
|
|
// Allocate a copy of yytext to keep in the syntax tree as data
|
|
$$->data.identifier = strdup(yytext);
|
|
}
|
|
;
|
|
number :
|
|
NUMBER_TOKEN {
|
|
$$ = node_create(NUMBER_LITERAL, 0);
|
|
char *t;
|
|
$$->data.number_literal = strtol(yytext, &t, 10);
|
|
if (yytext == t) {
|
|
fprintf(stderr, "failed to parse number literal: %s", yytext);
|
|
}
|
|
}
|
|
;
|
|
string :
|
|
STRING_TOKEN {
|
|
$$ = node_create(STRING_LITERAL, 0);
|
|
$$->data.string_literal = strdup(yytext);
|
|
}
|
|
;
|
|
%%
|