Merge GCC frontend into the branch
This commit is contained in:
1178
frontend/ast.cc
Normal file
1178
frontend/ast.cc
Normal file
File diff suppressed because it is too large
Load Diff
102
frontend/dependency.cc
Normal file
102
frontend/dependency.cc
Normal file
@@ -0,0 +1,102 @@
|
||||
/* Dependency graph analysis.
|
||||
Copyright (C) 2025 Free Software Foundation, Inc.
|
||||
|
||||
GCC is free software; you can redistribute it and/or modify
|
||||
it under the terms of the GNU General Public License as published by
|
||||
the Free Software Foundation; either version 3, or (at your option)
|
||||
any later version.
|
||||
|
||||
GCC is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU General Public License
|
||||
along with GCC; see the file COPYING3. If not see
|
||||
<http://www.gnu.org/licenses/>. */
|
||||
|
||||
#include "elna/frontend/dependency.h"
|
||||
|
||||
#include <fstream>
|
||||
#include <sstream>
|
||||
#include <string.h>
|
||||
|
||||
#include "elna/frontend/driver.h"
|
||||
#include "elna/frontend/semantic.h"
|
||||
#include "parser.hh"
|
||||
|
||||
namespace elna::frontend
|
||||
{
|
||||
dependency::dependency(const char *path)
|
||||
: error_container(path)
|
||||
{
|
||||
}
|
||||
|
||||
dependency read_source(std::istream& entry_point, const char *entry_path)
|
||||
{
|
||||
driver parse_driver{ entry_path };
|
||||
lexer tokenizer(entry_point);
|
||||
yy::parser parser(tokenizer, parse_driver);
|
||||
|
||||
dependency outcome{ entry_path };
|
||||
if (parser())
|
||||
{
|
||||
std::swap(outcome.errors(), parse_driver.errors());
|
||||
return outcome;
|
||||
}
|
||||
else
|
||||
{
|
||||
std::swap(outcome.tree, parse_driver.tree);
|
||||
}
|
||||
declaration_visitor declaration_visitor(entry_path);
|
||||
outcome.tree->accept(&declaration_visitor);
|
||||
|
||||
if (!declaration_visitor.errors().empty())
|
||||
{
|
||||
std::swap(outcome.errors(), declaration_visitor.errors());
|
||||
}
|
||||
outcome.unresolved = declaration_visitor.unresolved;
|
||||
|
||||
return outcome;
|
||||
}
|
||||
|
||||
error_list analyze_semantics(const char *path, std::unique_ptr<unit>& tree, symbol_bag bag)
|
||||
{
|
||||
name_analysis_visitor name_analyser(path, bag);
|
||||
tree->accept(&name_analyser);
|
||||
|
||||
if (name_analyser.has_errors())
|
||||
{
|
||||
return std::move(name_analyser.errors());
|
||||
}
|
||||
type_analysis_visitor type_analyzer(path, bag);
|
||||
tree->accept(&type_analyzer);
|
||||
|
||||
if (type_analyzer.has_errors())
|
||||
{
|
||||
return std::move(type_analyzer.errors());
|
||||
}
|
||||
return error_list{};
|
||||
}
|
||||
|
||||
std::filesystem::path build_path(const std::vector<std::string>& segments)
|
||||
{
|
||||
std::filesystem::path result;
|
||||
std::vector<std::string>::const_iterator segment_iterator = std::cbegin(segments);
|
||||
|
||||
if (segment_iterator == std::cend(segments))
|
||||
{
|
||||
return result;
|
||||
}
|
||||
result = *segment_iterator;
|
||||
|
||||
++segment_iterator;
|
||||
for (; segment_iterator != std::cend(segments); ++segment_iterator)
|
||||
{
|
||||
result /= *segment_iterator;
|
||||
}
|
||||
result.replace_extension(".elna");
|
||||
|
||||
return result;
|
||||
}
|
||||
}
|
||||
124
frontend/driver.cc
Normal file
124
frontend/driver.cc
Normal file
@@ -0,0 +1,124 @@
|
||||
/* Parsing driver.
|
||||
Copyright (C) 2025 Free Software Foundation, Inc.
|
||||
|
||||
GCC is free software; you can redistribute it and/or modify
|
||||
it under the terms of the GNU General Public License as published by
|
||||
the Free Software Foundation; either version 3, or (at your option)
|
||||
any later version.
|
||||
|
||||
GCC is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU General Public License
|
||||
along with GCC; see the file COPYING3. If not see
|
||||
<http://www.gnu.org/licenses/>. */
|
||||
|
||||
#include "elna/frontend/driver.h"
|
||||
|
||||
namespace elna::frontend
|
||||
{
|
||||
position make_position(const yy::location& location)
|
||||
{
|
||||
position result;
|
||||
result.line = static_cast<std::size_t>(location.begin.line);
|
||||
result.column = static_cast<std::size_t>(location.begin.column);
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
syntax_error::syntax_error(const std::string& message,
|
||||
const char *input_file, const yy::location& location)
|
||||
: error(input_file, make_position(location)), message(message)
|
||||
{
|
||||
}
|
||||
|
||||
std::string syntax_error::what() const
|
||||
{
|
||||
return message;
|
||||
}
|
||||
|
||||
driver::driver(const char *input_file)
|
||||
: error_container(input_file)
|
||||
{
|
||||
}
|
||||
|
||||
char escape_char(char escape)
|
||||
{
|
||||
switch (escape)
|
||||
{
|
||||
case 'n':
|
||||
return '\n';
|
||||
case 'a':
|
||||
return '\a';
|
||||
case 'b':
|
||||
return '\b';
|
||||
case 't':
|
||||
return '\t';
|
||||
case 'f':
|
||||
return '\f';
|
||||
case 'r':
|
||||
return '\r';
|
||||
case 'v':
|
||||
return '\v';
|
||||
case '\\':
|
||||
return '\\';
|
||||
case '\'':
|
||||
return '\'';
|
||||
case '"':
|
||||
return '"';
|
||||
case '?':
|
||||
return '\?';
|
||||
case '0':
|
||||
return '\0';
|
||||
default:
|
||||
return escape_invalid_char;
|
||||
}
|
||||
}
|
||||
|
||||
std::optional<std::string> escape_string(const char *escape)
|
||||
{
|
||||
std::string result;
|
||||
const char *current_position = escape + 1;
|
||||
|
||||
while (*current_position != '\0')
|
||||
{
|
||||
if (*current_position == '\\' && *(current_position + 1) == 'x')
|
||||
{
|
||||
current_position += 2;
|
||||
|
||||
std::size_t processed;
|
||||
char character = static_cast<char>(std::stoi(current_position, &processed, 16));
|
||||
if (processed == 0)
|
||||
{
|
||||
return std::nullopt;
|
||||
}
|
||||
else
|
||||
{
|
||||
current_position += processed - 1;
|
||||
result.push_back(character);
|
||||
}
|
||||
}
|
||||
else if (*current_position == '\\')
|
||||
{
|
||||
++current_position;
|
||||
|
||||
char escape = escape_char(*current_position);
|
||||
if (escape == escape_invalid_char)
|
||||
{
|
||||
return std::nullopt;
|
||||
}
|
||||
result.push_back(escape);
|
||||
}
|
||||
else
|
||||
{
|
||||
result.push_back(*current_position);
|
||||
}
|
||||
++current_position;
|
||||
}
|
||||
result.pop_back(); // Remove the terminating quote character.
|
||||
|
||||
return result;
|
||||
}
|
||||
}
|
||||
320
frontend/lexer.ll
Normal file
320
frontend/lexer.ll
Normal file
@@ -0,0 +1,320 @@
|
||||
/* Lexical analyzer.
|
||||
Copyright (C) 2025 Free Software Foundation, Inc.
|
||||
|
||||
GCC is free software; you can redistribute it and/or modify
|
||||
it under the terms of the GNU General Public License as published by
|
||||
the Free Software Foundation; either version 3, or (at your option)
|
||||
any later version.
|
||||
|
||||
GCC is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU General Public License
|
||||
along with GCC; see the file COPYING3. If not see
|
||||
<http://www.gnu.org/licenses/>. */
|
||||
|
||||
%{
|
||||
#define YY_NO_UNISTD_H
|
||||
#define YY_USER_ACTION this->location.columns(yyleng);
|
||||
|
||||
#include <sstream>
|
||||
#include "parser.hh"
|
||||
|
||||
#undef YY_DECL
|
||||
#define YY_DECL yy::parser::symbol_type elna::frontend::lexer::lex(driver& driver)
|
||||
#define yyterminate() return yy::parser::make_YYEOF(this->location)
|
||||
%}
|
||||
|
||||
%option c++ noyywrap never-interactive
|
||||
%option yyclass="lexer"
|
||||
|
||||
%x IN_COMMENT
|
||||
|
||||
ID1 [A-Za-z_]
|
||||
ID2 [A-Za-z0-9_]
|
||||
HIGIT [0-9a-fA-F]
|
||||
BIGIT [01]
|
||||
|
||||
%%
|
||||
%{
|
||||
this->location.step();
|
||||
%}
|
||||
|
||||
<IN_COMMENT>{
|
||||
\*\) BEGIN(INITIAL);
|
||||
[^*\n]+ ; /* Eat comment in chunks. */
|
||||
\* ; /* Eat the lone star. */
|
||||
\n+ {
|
||||
this->location.lines(yyleng);
|
||||
this->location.step();
|
||||
}
|
||||
}
|
||||
\(\* BEGIN(IN_COMMENT);
|
||||
[ \t\r] {
|
||||
this->location.step();
|
||||
}
|
||||
\n+ {
|
||||
this->location.lines(yyleng);
|
||||
}
|
||||
if {
|
||||
return yy::parser::make_IF(this->location);
|
||||
}
|
||||
then {
|
||||
return yy::parser::make_THEN(this->location);
|
||||
}
|
||||
else {
|
||||
return yy::parser::make_ELSE(this->location);
|
||||
}
|
||||
elsif {
|
||||
return yy::parser::make_ELSIF(this->location);
|
||||
}
|
||||
while {
|
||||
return yy::parser::make_WHILE(this->location);
|
||||
}
|
||||
do {
|
||||
return yy::parser::make_DO(this->location);
|
||||
}
|
||||
proc {
|
||||
return yy::parser::make_PROCEDURE(this->location);
|
||||
}
|
||||
begin {
|
||||
return yy::parser::make_BEGIN_BLOCK(this->location);
|
||||
}
|
||||
end {
|
||||
return yy::parser::make_END_BLOCK(this->location);
|
||||
}
|
||||
extern {
|
||||
return yy::parser::make_EXTERN(this->location);
|
||||
}
|
||||
const {
|
||||
return yy::parser::make_CONST(this->location);
|
||||
}
|
||||
var {
|
||||
return yy::parser::make_VAR(this->location);
|
||||
}
|
||||
type {
|
||||
return yy::parser::make_TYPE(this->location);
|
||||
}
|
||||
record {
|
||||
return yy::parser::make_RECORD(this->location);
|
||||
}
|
||||
union {
|
||||
return yy::parser::make_UNION(this->location);
|
||||
}
|
||||
true {
|
||||
return yy::parser::make_BOOLEAN(true, this->location);
|
||||
}
|
||||
false {
|
||||
return yy::parser::make_BOOLEAN(false, this->location);
|
||||
}
|
||||
nil {
|
||||
return yy::parser::make_NIL(this->location);
|
||||
}
|
||||
\& {
|
||||
return yy::parser::make_AND(this->location);
|
||||
}
|
||||
xor {
|
||||
return yy::parser::make_XOR(this->location);
|
||||
}
|
||||
or {
|
||||
return yy::parser::make_OR(this->location);
|
||||
}
|
||||
\| {
|
||||
return yy::parser::make_PIPE(this->location);
|
||||
}
|
||||
\~ {
|
||||
return yy::parser::make_NOT(this->location);
|
||||
}
|
||||
return {
|
||||
return yy::parser::make_RETURN(this->location);
|
||||
}
|
||||
module {
|
||||
return yy::parser::make_MODULE(this->location);
|
||||
}
|
||||
program {
|
||||
return yy::parser::make_PROGRAM(this->location);
|
||||
}
|
||||
import {
|
||||
return yy::parser::make_IMPORT(this->location);
|
||||
}
|
||||
cast {
|
||||
return yy::parser::make_CAST(this->location);
|
||||
}
|
||||
defer {
|
||||
return yy::parser::make_DEFER(this->location);
|
||||
}
|
||||
case {
|
||||
return yy::parser::make_CASE(this->location);
|
||||
}
|
||||
of {
|
||||
return yy::parser::make_OF(this->location);
|
||||
}
|
||||
{ID1}{ID2}* {
|
||||
return yy::parser::make_IDENTIFIER(yytext, this->location);
|
||||
}
|
||||
#{ID1}{ID2}* {
|
||||
return yy::parser::make_TRAIT(yytext + 1, this->location);
|
||||
}
|
||||
[[:digit:]]+u {
|
||||
unsigned long result = strtoul(yytext, NULL, 10);
|
||||
|
||||
if (errno == ERANGE)
|
||||
{
|
||||
REJECT;
|
||||
}
|
||||
else
|
||||
{
|
||||
return yy::parser::make_WORD(result, this->location);
|
||||
}
|
||||
}
|
||||
[[:digit:]]+ {
|
||||
long result = strtol(yytext, NULL, 10);
|
||||
|
||||
if (errno == ERANGE)
|
||||
{
|
||||
REJECT;
|
||||
}
|
||||
else
|
||||
{
|
||||
return yy::parser::make_INTEGER(result, this->location);
|
||||
}
|
||||
}
|
||||
0x{HIGIT}+ {
|
||||
unsigned long result = strtoul(yytext, NULL, 16);
|
||||
|
||||
if (errno == ERANGE)
|
||||
{
|
||||
REJECT;
|
||||
}
|
||||
else
|
||||
{
|
||||
return yy::parser::make_WORD(result, this->location);
|
||||
}
|
||||
}
|
||||
0b{BIGIT}+ {
|
||||
unsigned long result = strtoul(yytext, NULL, 2);
|
||||
|
||||
if (errno == ERANGE)
|
||||
{
|
||||
REJECT;
|
||||
}
|
||||
else
|
||||
{
|
||||
return yy::parser::make_WORD(result, this->location);
|
||||
}
|
||||
}
|
||||
[[:digit:]]+\.[[:digit:]]+ {
|
||||
float result = strtof(yytext, NULL);
|
||||
|
||||
if (errno == ERANGE)
|
||||
{
|
||||
REJECT;
|
||||
}
|
||||
else
|
||||
{
|
||||
return yy::parser::make_FLOAT(result, this->location);
|
||||
}
|
||||
}
|
||||
'[[:print:]]+' {
|
||||
std::optional<std::string> result = escape_string(yytext);
|
||||
if (!result.has_value() || result.value().size() != 1)
|
||||
{
|
||||
REJECT;
|
||||
}
|
||||
return yy::parser::make_CHARACTER(result.value(), this->location);
|
||||
}
|
||||
\"[[:print:]]*\" {
|
||||
std::optional<std::string> result = escape_string(yytext);
|
||||
if (!result.has_value())
|
||||
{
|
||||
REJECT;
|
||||
}
|
||||
return yy::parser::make_STRING(result.value(), this->location);
|
||||
}
|
||||
\( {
|
||||
return yy::parser::make_LEFT_PAREN(this->location);
|
||||
}
|
||||
\) {
|
||||
return yy::parser::make_RIGHT_PAREN(this->location);
|
||||
}
|
||||
\[ {
|
||||
return yy::parser::make_LEFT_SQUARE(this->location);
|
||||
}
|
||||
\] {
|
||||
return yy::parser::make_RIGHT_SQUARE(this->location);
|
||||
}
|
||||
\<\< {
|
||||
return yy::parser::make_SHIFT_LEFT(this->location);
|
||||
}
|
||||
\>\> {
|
||||
return yy::parser::make_SHIFT_RIGHT(this->location);
|
||||
}
|
||||
\>= {
|
||||
return yy::parser::make_GREATER_EQUAL(this->location);
|
||||
}
|
||||
\<= {
|
||||
return yy::parser::make_LESS_EQUAL(this->location);
|
||||
}
|
||||
\> {
|
||||
return yy::parser::make_GREATER_THAN(this->location);
|
||||
}
|
||||
\< {
|
||||
return yy::parser::make_LESS_THAN(this->location);
|
||||
}
|
||||
\<\> {
|
||||
return yy::parser::make_NOT_EQUAL(this->location);
|
||||
}
|
||||
= {
|
||||
return yy::parser::make_EQUALS(this->location);
|
||||
}
|
||||
; {
|
||||
return yy::parser::make_SEMICOLON(this->location);
|
||||
}
|
||||
\. {
|
||||
return yy::parser::make_DOT(this->location);
|
||||
}
|
||||
, {
|
||||
return yy::parser::make_COMMA(this->location);
|
||||
}
|
||||
\+ {
|
||||
return yy::parser::make_PLUS(this->location);
|
||||
}
|
||||
\-> {
|
||||
return yy::parser::make_ARROW(this->location);
|
||||
}
|
||||
\- {
|
||||
return yy::parser::make_MINUS(this->location);
|
||||
}
|
||||
\* {
|
||||
return yy::parser::make_MULTIPLICATION(this->location);
|
||||
}
|
||||
\/ {
|
||||
return yy::parser::make_DIVISION(this->location);
|
||||
}
|
||||
% {
|
||||
return yy::parser::make_REMAINDER(this->location);
|
||||
}
|
||||
:= {
|
||||
return yy::parser::make_ASSIGNMENT(this->location);
|
||||
}
|
||||
: {
|
||||
return yy::parser::make_COLON(this->location);
|
||||
}
|
||||
\^ {
|
||||
return yy::parser::make_HAT(this->location);
|
||||
}
|
||||
@ {
|
||||
return yy::parser::make_AT(this->location);
|
||||
}
|
||||
! {
|
||||
return yy::parser::make_EXCLAMATION(this->location);
|
||||
}
|
||||
. {
|
||||
std::stringstream ss;
|
||||
|
||||
ss << "Illegal character 0x" << std::hex << static_cast<unsigned int>(yytext[0]);
|
||||
driver.add_error<syntax_error>(ss.str(), driver.input_file, this->location);
|
||||
}
|
||||
%%
|
||||
594
frontend/parser.yy
Normal file
594
frontend/parser.yy
Normal file
@@ -0,0 +1,594 @@
|
||||
/* Syntax analyzer.
|
||||
Copyright (C) 2025 Free Software Foundation, Inc.
|
||||
|
||||
GCC is free software; you can redistribute it and/or modify
|
||||
it under the terms of the GNU General Public License as published by
|
||||
the Free Software Foundation; either version 3, or (at your option)
|
||||
any later version.
|
||||
|
||||
GCC is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU General Public License
|
||||
along with GCC; see the file COPYING3. If not see
|
||||
<http://www.gnu.org/licenses/>. */
|
||||
|
||||
%require "3.4"
|
||||
%language "c++"
|
||||
|
||||
%code {
|
||||
using namespace elna;
|
||||
}
|
||||
|
||||
%code requires {
|
||||
#include <cstdint>
|
||||
#include <iostream>
|
||||
#include "elna/frontend/driver.h"
|
||||
|
||||
#if !defined(yyFlexLexerOnce)
|
||||
#include <FlexLexer.h>
|
||||
#endif
|
||||
|
||||
namespace elna::frontend
|
||||
{
|
||||
class lexer;
|
||||
}
|
||||
}
|
||||
|
||||
%code provides {
|
||||
namespace elna::frontend
|
||||
{
|
||||
|
||||
class lexer: public yyFlexLexer
|
||||
{
|
||||
public:
|
||||
yy::location location;
|
||||
|
||||
lexer(std::istream& arg_yyin)
|
||||
: yyFlexLexer(&arg_yyin)
|
||||
{
|
||||
}
|
||||
|
||||
yy::parser::symbol_type lex(driver& driver);
|
||||
};
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
%define api.token.raw
|
||||
%define api.token.constructor
|
||||
%define api.value.type variant
|
||||
|
||||
%parse-param {elna::frontend::lexer& lexer}
|
||||
%param {elna::frontend::driver& driver}
|
||||
%locations
|
||||
|
||||
%header
|
||||
|
||||
%code {
|
||||
#define yylex lexer.lex
|
||||
}
|
||||
%start program;
|
||||
|
||||
%token <std::string> IDENTIFIER
|
||||
%token <std::string> TRAIT
|
||||
%token <std::int32_t> INTEGER
|
||||
%token <std::uint32_t> WORD
|
||||
%token <float> FLOAT
|
||||
%token <std::string> CHARACTER
|
||||
%token <std::string> STRING
|
||||
%token <bool> BOOLEAN
|
||||
%token LEFT_PAREN "(" RIGHT_PAREN ")" LEFT_SQUARE "[" RIGHT_SQUARE "]"
|
||||
%token ASSIGNMENT ":="
|
||||
ARROW "->" EXCLAMATION "!"
|
||||
AT "@" HAT "^"
|
||||
COLON ":" SEMICOLON ";" DOT "." COMMA ","
|
||||
%token NOT "~"
|
||||
CAST "cast"
|
||||
NIL "nil"
|
||||
CONST "const"
|
||||
VAR "var"
|
||||
PROCEDURE "proc"
|
||||
TYPE "type"
|
||||
RECORD "record"
|
||||
UNION "union"
|
||||
EXTERN "extern"
|
||||
IF "if"
|
||||
WHILE "while"
|
||||
DO "do"
|
||||
THEN "then"
|
||||
ELSE "else"
|
||||
ELSIF "elsif"
|
||||
RETURN "return"
|
||||
PROGRAM "program"
|
||||
MODULE "module"
|
||||
IMPORT "import"
|
||||
BEGIN_BLOCK "begin"
|
||||
END_BLOCK "end"
|
||||
DEFER "defer"
|
||||
CASE "case"
|
||||
OF "of"
|
||||
PIPE "|"
|
||||
%token OR "or" AND "&" XOR "xor"
|
||||
EQUALS "=" NOT_EQUAL "<>" LESS_THAN "<" GREATER_THAN ">" LESS_EQUAL "<=" GREATER_EQUAL ">="
|
||||
SHIFT_LEFT "<<" SHIFT_RIGHT ">>"
|
||||
PLUS "+" MINUS "-"
|
||||
MULTIPLICATION "*" DIVISION "/" REMAINDER "%"
|
||||
|
||||
%left "or" "&" "xor"
|
||||
%left "=" "<>" "<" ">" "<=" ">="
|
||||
%left "<<" ">>"
|
||||
%left "+" "-"
|
||||
%left "*" "/" "%"
|
||||
|
||||
%type <elna::frontend::literal_expression *> literal;
|
||||
%type <std::vector<elna::frontend::expression *>> case_labels;
|
||||
%type <elna::frontend::switch_case> switch_case;
|
||||
%type <std::vector<elna::frontend::switch_case>> switch_cases;
|
||||
%type <elna::frontend::constant_declaration *> constant_declaration;
|
||||
%type <std::vector<elna::frontend::constant_declaration *>> constant_part constant_declarations;
|
||||
%type <elna::frontend::variable_declaration *> variable_declaration;
|
||||
%type <std::vector<elna::frontend::variable_declaration *>> variable_declarations variable_part;
|
||||
%type <elna::frontend::type_expression *> type_expression;
|
||||
%type <std::vector<elna::frontend::type_expression *>> type_expressions;
|
||||
%type <elna::frontend::traits_expression *> traits_expression;
|
||||
%type <elna::frontend::expression *> expression operand simple_expression;
|
||||
%type <elna::frontend::unary_expression *> unary_expression;
|
||||
%type <elna::frontend::binary_expression *> binary_expression;
|
||||
%type <std::vector<elna::frontend::expression *>> expressions actual_parameter_list;
|
||||
%type <elna::frontend::designator_expression *> designator_expression;
|
||||
%type <elna::frontend::procedure_call*> call_expression;
|
||||
%type <elna::frontend::return_statement *> return_statement;
|
||||
%type <elna::frontend::statement *> statement;
|
||||
%type <std::vector<elna::frontend::statement *>> required_statements optional_statements statement_part;
|
||||
%type <elna::frontend::procedure_declaration *> procedure_declaration;
|
||||
%type <std::pair<std::vector<std::string>, elna::frontend::procedure_type_expression *>> procedure_heading;
|
||||
%type <elna::frontend::procedure_type_expression::return_t> return_declaration;
|
||||
%type <std::vector<elna::frontend::procedure_declaration *>> procedure_declarations procedure_part;
|
||||
%type <elna::frontend::type_declaration *> type_declaration;
|
||||
%type <std::vector<elna::frontend::type_declaration *>> type_declarations type_part;
|
||||
%type <std::unique_ptr<elna::frontend::block>> block;
|
||||
%type <elna::frontend::field_declaration> field_declaration formal_parameter;
|
||||
%type <std::vector<std::pair<std::string, elna::frontend::type_expression *>>>
|
||||
optional_fields required_fields formal_parameters formal_parameter_list;
|
||||
%type <std::vector<elna::frontend::conditional_statements *>> elsif_then_statements elsif_do_statements;
|
||||
%type <std::vector<elna::frontend::statement *> *> else_statements;
|
||||
%type <elna::frontend::cast_expression *> cast_expression;
|
||||
%type <elna::frontend::identifier_definition> identifier_definition;
|
||||
%type <std::vector<elna::frontend::identifier_definition>> identifier_definitions;
|
||||
%type <std::vector<std::string>> identifiers import_declaration;
|
||||
%type <std::vector<elna::frontend::import_declaration *>> import_declarations import_part;
|
||||
%%
|
||||
program:
|
||||
"program" ";" import_part constant_part type_part variable_part procedure_part statement_part "end" "."
|
||||
{
|
||||
auto tree = new frontend::program(frontend::make_position(@1));
|
||||
|
||||
std::swap(tree->imports, $3);
|
||||
std::swap(tree->constants, $4);
|
||||
std::swap(tree->types , $5);
|
||||
std::swap(tree->variables, $6);
|
||||
std::swap(tree->procedures, $7);
|
||||
std::swap(tree->body, $8);
|
||||
|
||||
driver.tree.reset(tree);
|
||||
}
|
||||
| "module" ";" import_part constant_part type_part variable_part procedure_part "end" "."
|
||||
{
|
||||
auto tree = new frontend::unit(frontend::make_position(@1));
|
||||
|
||||
std::swap(tree->imports, $3);
|
||||
std::swap(tree->constants, $4);
|
||||
std::swap(tree->types , $5);
|
||||
std::swap(tree->variables, $6);
|
||||
std::swap(tree->procedures, $7);
|
||||
|
||||
driver.tree.reset(tree);
|
||||
}
|
||||
block: constant_part variable_part statement_part "end"
|
||||
{
|
||||
$$ = std::make_unique<frontend::block>(std::move($1), std::move($2), std::move($3));
|
||||
}
|
||||
statement_part:
|
||||
/* no statements */ {}
|
||||
| "begin" required_statements { std::swap($$, $2); }
|
||||
| return_statement { $$.push_back($1); }
|
||||
| "begin" required_statements ";" return_statement
|
||||
{
|
||||
std::swap($$, $2);
|
||||
$$.push_back($4);
|
||||
}
|
||||
identifier_definition:
|
||||
IDENTIFIER "*" { $$ = frontend::identifier_definition{ $1, true }; }
|
||||
| IDENTIFIER { $$ = frontend::identifier_definition{ $1, false }; }
|
||||
identifier_definitions:
|
||||
identifier_definition "," identifier_definitions
|
||||
{
|
||||
std::swap($$, $3);
|
||||
$$.emplace($$.cbegin(), $1);
|
||||
}
|
||||
| identifier_definition { $$.emplace_back(std::move($1)); }
|
||||
return_declaration:
|
||||
/* proper procedure */ {}
|
||||
| "->" "!" { $$ = frontend::procedure_type_expression::return_t(std::monostate{}); }
|
||||
| "->" type_expression { $$ = frontend::procedure_type_expression::return_t($2); }
|
||||
procedure_heading: formal_parameter_list return_declaration
|
||||
{
|
||||
$$.second = new frontend::procedure_type_expression(frontend::make_position(@1), std::move($2));
|
||||
for (auto& [name, type] : $1)
|
||||
{
|
||||
$$.first.emplace_back(std::move(name));
|
||||
$$.second->parameters.push_back(type);
|
||||
}
|
||||
}
|
||||
procedure_declaration:
|
||||
"proc" identifier_definition procedure_heading ";" block ";"
|
||||
{
|
||||
$$ = new frontend::procedure_declaration(frontend::make_position(@1), std::move($2), $3.second, std::move(*$5));
|
||||
std::swap($3.first, $$->parameter_names);
|
||||
}
|
||||
| "proc" identifier_definition procedure_heading ";" "extern" ";"
|
||||
{
|
||||
$$ = new frontend::procedure_declaration(frontend::make_position(@1), std::move($2), $3.second);
|
||||
std::swap($3.first, $$->parameter_names);
|
||||
}
|
||||
procedure_declarations:
|
||||
procedure_declaration procedure_declarations
|
||||
{
|
||||
std::swap($$, $2);
|
||||
$$.emplace($$.cbegin(), std::move($1));
|
||||
}
|
||||
| procedure_declaration { $$.emplace_back(std::move($1)); }
|
||||
procedure_part:
|
||||
/* no procedure definitions */ {}
|
||||
| procedure_declarations { std::swap($$, $1); }
|
||||
call_expression: designator_expression actual_parameter_list
|
||||
{
|
||||
$$ = new frontend::procedure_call(frontend::make_position(@1), $1);
|
||||
std::swap($$->arguments, $2);
|
||||
}
|
||||
cast_expression: "cast" "(" expression ":" type_expression ")"
|
||||
{ $$ = new frontend::cast_expression(frontend::make_position(@1), $5, $3); }
|
||||
elsif_do_statements:
|
||||
"elsif" expression "do" optional_statements elsif_do_statements
|
||||
{
|
||||
frontend::conditional_statements *branch = new frontend::conditional_statements($2, std::move($4));
|
||||
std::swap($5, $$);
|
||||
$$.emplace($$.begin(), branch);
|
||||
}
|
||||
| {}
|
||||
else_statements:
|
||||
"else" optional_statements { $$ = new std::vector<frontend::statement *>(std::move($2)); }
|
||||
| { $$ = nullptr; }
|
||||
elsif_then_statements:
|
||||
"elsif" expression "then" optional_statements elsif_then_statements
|
||||
{
|
||||
frontend::conditional_statements *branch = new frontend::conditional_statements($2, std::move($4));
|
||||
std::swap($5, $$);
|
||||
$$.emplace($$.begin(), branch);
|
||||
}
|
||||
| {}
|
||||
return_statement: "return" expression
|
||||
{ $$ = new frontend::return_statement(frontend::make_position(@1), $2); }
|
||||
literal:
|
||||
INTEGER { $$ = new frontend::literal<std::int32_t>(frontend::make_position(@1), $1); }
|
||||
| WORD { $$ = new frontend::literal<std::uint32_t>(frontend::make_position(@1), $1); }
|
||||
| FLOAT { $$ = new frontend::literal<double>(frontend::make_position(@1), $1); }
|
||||
| BOOLEAN { $$ = new frontend::literal<bool>(frontend::make_position(@1), $1); }
|
||||
| CHARACTER { $$ = new frontend::literal<unsigned char>(frontend::make_position(@1), $1.at(0)); }
|
||||
| "nil" { $$ = new frontend::literal<std::nullptr_t>(frontend::make_position(@1), nullptr); }
|
||||
| STRING { $$ = new frontend::literal<std::string>(frontend::make_position(@1), $1); }
|
||||
traits_expression:
|
||||
TRAIT "(" type_expressions ")"
|
||||
{
|
||||
$$ = new frontend::traits_expression(frontend::make_position(@1), $1);
|
||||
std::swap($3, $$->parameters);
|
||||
}
|
||||
simple_expression:
|
||||
literal { $$ = $1; }
|
||||
| designator_expression { $$ = $1; }
|
||||
| traits_expression { $$ = $1; }
|
||||
| cast_expression { $$ = $1; }
|
||||
| call_expression { $$ = $1; }
|
||||
| "(" expression ")" { $$ = $2; }
|
||||
operand:
|
||||
unary_expression { $$ = $1; }
|
||||
| simple_expression { $$ = $1; }
|
||||
expression:
|
||||
binary_expression { $$ = $1; }
|
||||
| operand { $$ = $1; }
|
||||
binary_expression:
|
||||
expression "*" expression
|
||||
{
|
||||
$$ = new frontend::binary_expression(frontend::make_position(@2), $1, $3, frontend::binary_operator::multiplication);
|
||||
}
|
||||
| expression "/" expression
|
||||
{
|
||||
$$ = new frontend::binary_expression(frontend::make_position(@2), $1, $3, frontend::binary_operator::division);
|
||||
}
|
||||
| expression "%" expression
|
||||
{
|
||||
$$ = new frontend::binary_expression(frontend::make_position(@2), $1, $3, frontend::binary_operator::remainder);
|
||||
}
|
||||
| expression "+" expression
|
||||
{
|
||||
$$ = new frontend::binary_expression(frontend::make_position(@2), $1, $3, frontend::binary_operator::sum);
|
||||
}
|
||||
| expression "-" expression
|
||||
{
|
||||
$$ = new frontend::binary_expression(frontend::make_position(@2), $1, $3, frontend::binary_operator::subtraction);
|
||||
}
|
||||
| expression "=" expression
|
||||
{
|
||||
$$ = new frontend::binary_expression(frontend::make_position(@2), $1, $3, frontend::binary_operator::equals);
|
||||
}
|
||||
| expression "<>" expression
|
||||
{
|
||||
$$ = new frontend::binary_expression(frontend::make_position(@2), $1, $3, frontend::binary_operator::not_equals);
|
||||
}
|
||||
| expression "<" expression
|
||||
{
|
||||
$$ = new frontend::binary_expression(frontend::make_position(@2), $1, $3, frontend::binary_operator::less);
|
||||
}
|
||||
| expression ">" expression
|
||||
{
|
||||
$$ = new frontend::binary_expression(frontend::make_position(@2), $1, $3, frontend::binary_operator::greater);
|
||||
}
|
||||
| expression "<=" expression
|
||||
{
|
||||
$$ = new frontend::binary_expression(frontend::make_position(@2), $1, $3,
|
||||
frontend::binary_operator::less_equal);
|
||||
}
|
||||
| expression ">=" expression
|
||||
{
|
||||
$$ = new frontend::binary_expression(frontend::make_position(@2), $1, $3, frontend::binary_operator::greater_equal);
|
||||
}
|
||||
| expression "&" expression
|
||||
{
|
||||
$$ = new frontend::binary_expression(frontend::make_position(@2), $1, $3, frontend::binary_operator::conjunction);
|
||||
}
|
||||
| expression "or" expression
|
||||
{
|
||||
$$ = new frontend::binary_expression(frontend::make_position(@2), $1, $3, frontend::binary_operator::disjunction);
|
||||
}
|
||||
| expression "xor" expression
|
||||
{
|
||||
$$ = new frontend::binary_expression(frontend::make_position(@2), $1, $3,
|
||||
frontend::binary_operator::exclusive_disjunction);
|
||||
}
|
||||
| expression "<<" expression
|
||||
{
|
||||
$$ = new frontend::binary_expression(frontend::make_position(@2), $1, $3, frontend::binary_operator::shift_left);
|
||||
}
|
||||
| expression ">>" expression
|
||||
{
|
||||
$$ = new frontend::binary_expression(frontend::make_position(@2), $1, $3, frontend::binary_operator::shift_right);
|
||||
}
|
||||
unary_expression:
|
||||
"@" operand
|
||||
{
|
||||
$$ = new frontend::unary_expression(frontend::make_position(@1), $2, frontend::unary_operator::reference);
|
||||
}
|
||||
| "~" operand
|
||||
{
|
||||
$$ = new frontend::unary_expression(frontend::make_position(@1), $2, frontend::unary_operator::negation);
|
||||
}
|
||||
| "-" operand
|
||||
{
|
||||
$$ = new frontend::unary_expression(frontend::make_position(@1), $2, frontend::unary_operator::minus);
|
||||
}
|
||||
expressions:
|
||||
expression "," expressions
|
||||
{
|
||||
std::swap($$, $3);
|
||||
$$.emplace($$.cbegin(), $1);
|
||||
}
|
||||
| expression { $$.push_back($1); }
|
||||
type_expressions:
|
||||
type_expression "," type_expressions
|
||||
{
|
||||
std::swap($$, $3);
|
||||
$$.emplace($$.cbegin(), $1);
|
||||
}
|
||||
| type_expression { $$.push_back($1); }
|
||||
designator_expression:
|
||||
simple_expression "[" expression "]"
|
||||
{ $$ = new frontend::array_access_expression(frontend::make_position(@2), $1, $3); }
|
||||
| simple_expression "." IDENTIFIER
|
||||
{ $$ = new frontend::field_access_expression(frontend::make_position(@2), $1, $3); }
|
||||
| simple_expression "^"
|
||||
{ $$ = new frontend::dereference_expression(frontend::make_position(@1), $1); }
|
||||
| IDENTIFIER
|
||||
{ $$ = new frontend::variable_expression(frontend::make_position(@1), $1); }
|
||||
statement:
|
||||
designator_expression ":=" expression
|
||||
{ $$ = new frontend::assign_statement(frontend::make_position(@1), $1, $3); }
|
||||
| "while" expression "do" optional_statements elsif_do_statements "end"
|
||||
{
|
||||
frontend::conditional_statements *body = new frontend::conditional_statements($2, std::move($4));
|
||||
$$ = new frontend::while_statement(frontend::make_position(@1), body, std::move($5));
|
||||
}
|
||||
| "if" expression "then" optional_statements elsif_then_statements else_statements "end"
|
||||
{
|
||||
frontend::conditional_statements *then = new frontend::conditional_statements($2, std::move($4));
|
||||
$$ = new frontend::if_statement(frontend::make_position(@1), then, std::move($5), $6);
|
||||
}
|
||||
| call_expression { $$ = $1; }
|
||||
| "defer" optional_statements "end"
|
||||
{ $$ = new frontend::defer_statement(frontend::make_position(@1), std::move($2)); }
|
||||
| "case" expression "of" switch_cases else_statements "end"
|
||||
{ $$ = new frontend::case_statement(frontend::make_position(@1), $2, std::move($4), $5); }
|
||||
switch_case: case_labels ":" optional_statements
|
||||
{ $$ = { .labels = std::move($1), .statements = std::move($3) }; }
|
||||
switch_cases:
|
||||
switch_case "|" switch_cases
|
||||
{
|
||||
std::swap($$, $3);
|
||||
$$.emplace($$.cbegin(), $1);
|
||||
}
|
||||
| switch_case { $$.push_back($1); }
|
||||
case_labels:
|
||||
expression "," case_labels
|
||||
{
|
||||
std::swap($$, $3);
|
||||
$$.emplace($$.cbegin(), $1);
|
||||
}
|
||||
| expression { $$.push_back($1); }
|
||||
required_statements:
|
||||
required_statements ";" statement
|
||||
{
|
||||
std::swap($$, $1);
|
||||
$$.insert($$.cend(), $3);
|
||||
}
|
||||
| statement { $$.push_back($1); }
|
||||
optional_statements:
|
||||
required_statements { std::swap($$, $1); }
|
||||
| /* no statements */ {}
|
||||
field_declaration:
|
||||
IDENTIFIER ":" type_expression { $$ = std::make_pair($1, $3); }
|
||||
required_fields:
|
||||
field_declaration ";" required_fields
|
||||
{
|
||||
std::swap($$, $3);
|
||||
$$.emplace($$.cbegin(), $1);
|
||||
}
|
||||
| field_declaration { $$.emplace_back($1); }
|
||||
optional_fields:
|
||||
required_fields { std::swap($$, $1); }
|
||||
| /* no fields */ {}
|
||||
type_expression:
|
||||
"[" INTEGER "]" type_expression
|
||||
{
|
||||
$$ = new frontend::array_type_expression(frontend::make_position(@1), $4, $2);
|
||||
}
|
||||
| "^" type_expression
|
||||
{
|
||||
$$ = new frontend::pointer_type_expression(frontend::make_position(@1), $2);
|
||||
}
|
||||
| "record" optional_fields "end"
|
||||
{
|
||||
$$ = new frontend::record_type_expression(frontend::make_position(@1), std::move($2));
|
||||
}
|
||||
| "union" required_fields "end"
|
||||
{
|
||||
$$ = new frontend::union_type_expression(frontend::make_position(@1), std::move($2));
|
||||
}
|
||||
| "proc" "(" type_expressions ")" return_declaration
|
||||
{
|
||||
auto result = new frontend::procedure_type_expression(frontend::make_position(@1), std::move($5));
|
||||
std::swap(result->parameters, $3);
|
||||
$$ = result;
|
||||
}
|
||||
| "(" identifiers ")"
|
||||
{
|
||||
$$ = new frontend::enumeration_type_expression(frontend::make_position(@1), std::move($2));
|
||||
}
|
||||
| IDENTIFIER
|
||||
{
|
||||
$$ = new frontend::named_type_expression(frontend::make_position(@1), $1);
|
||||
}
|
||||
identifiers:
|
||||
IDENTIFIER "," identifiers
|
||||
{
|
||||
std::swap($$, $3);
|
||||
$$.emplace($$.cbegin(), std::move($1));
|
||||
}
|
||||
| IDENTIFIER { $$.emplace_back(std::move($1)); }
|
||||
variable_declaration:
|
||||
identifier_definitions ":" type_expression ";"
|
||||
{
|
||||
std::shared_ptr<frontend::type_expression> shared_type{ $3 };
|
||||
$$ = new frontend::variable_declaration( frontend::make_position(@2), std::move($1), shared_type);
|
||||
}
|
||||
| identifier_definitions ":" type_expression ":=" "extern" ";"
|
||||
{
|
||||
std::shared_ptr<frontend::type_expression> shared_type{ $3 };
|
||||
$$ = new frontend::variable_declaration( frontend::make_position(@2), std::move($1), shared_type,
|
||||
std::monostate{});
|
||||
}
|
||||
| identifier_definitions ":" type_expression ":=" expression ";"
|
||||
{
|
||||
std::shared_ptr<frontend::type_expression> shared_type{ $3 };
|
||||
$$ = new frontend::variable_declaration( frontend::make_position(@2), std::move($1), shared_type, $5);
|
||||
}
|
||||
variable_declarations:
|
||||
/* no variable declarations */ {}
|
||||
| variable_declaration variable_declarations
|
||||
{
|
||||
std::swap($$, $2);
|
||||
$$.insert(std::cbegin($$), $1);
|
||||
}
|
||||
variable_part:
|
||||
/* no variable declarations */ {}
|
||||
| "var" variable_declarations { std::swap($$, $2); }
|
||||
constant_declaration: identifier_definition ":=" expression ";"
|
||||
{
|
||||
$$ = new frontend::constant_declaration(frontend::make_position(@1), std::move($1), $3);
|
||||
}
|
||||
constant_declarations:
|
||||
constant_declaration constant_declarations
|
||||
{
|
||||
std::swap($$, $2);
|
||||
$$.insert(std::cbegin($$), $1);
|
||||
}
|
||||
| /* no constant definitions */ {}
|
||||
constant_part:
|
||||
/* no constant definitions */ {}
|
||||
| "const" constant_declarations { std::swap($$, $2); }
|
||||
import_declaration:
|
||||
IDENTIFIER "." import_declaration
|
||||
{
|
||||
std::swap($$, $3);
|
||||
$$.emplace($$.cbegin(), std::move($1));
|
||||
}
|
||||
| IDENTIFIER { $$.emplace_back(std::move($1)); }
|
||||
import_declarations:
|
||||
import_declaration "," import_declarations
|
||||
{
|
||||
std::swap($$, $3);
|
||||
$$.emplace($$.cbegin(), new frontend::import_declaration(frontend::make_position(@1), std::move($1)));
|
||||
}
|
||||
| import_declaration
|
||||
{
|
||||
$$.emplace_back(new frontend::import_declaration(frontend::make_position(@1), std::move($1)));
|
||||
}
|
||||
import_part:
|
||||
/* no import declarations */ {}
|
||||
| "import" import_declarations ";" { std::swap($$, $2); }
|
||||
type_declaration: identifier_definition "=" type_expression ";"
|
||||
{
|
||||
$$ = new frontend::type_declaration(frontend::make_position(@1), std::move($1), $3);
|
||||
}
|
||||
type_declarations:
|
||||
type_declaration type_declarations
|
||||
{
|
||||
std::swap($$, $2);
|
||||
$$.insert($$.cbegin(), $1);
|
||||
}
|
||||
| /* no type definitions */ {}
|
||||
type_part:
|
||||
/* no type definitions */ {}
|
||||
| "type" type_declarations { std::swap($$, $2); }
|
||||
formal_parameter:
|
||||
IDENTIFIER ":" type_expression { $$ = std::make_pair($1, $3); }
|
||||
formal_parameter_list:
|
||||
"(" ")" {}
|
||||
| "(" formal_parameters ")" { std::swap($$, $2); }
|
||||
formal_parameters:
|
||||
formal_parameter "," formal_parameters
|
||||
{
|
||||
std::swap($$, $3);
|
||||
$$.emplace($$.cbegin(), std::move($1));
|
||||
}
|
||||
| formal_parameter { $$.emplace_back(std::move($1)); }
|
||||
actual_parameter_list:
|
||||
"(" ")" {}
|
||||
| "(" expressions ")" { std::swap($$, $2); }
|
||||
%%
|
||||
|
||||
void yy::parser::error(const location_type& loc, const std::string& message)
|
||||
{
|
||||
driver.add_error<frontend::syntax_error>(message, driver.input_file, loc);
|
||||
}
|
||||
67
frontend/result.cc
Normal file
67
frontend/result.cc
Normal file
@@ -0,0 +1,67 @@
|
||||
/* Miscellaneous types used across stage boundaries.
|
||||
Copyright (C) 2025 Free Software Foundation, Inc.
|
||||
|
||||
GCC is free software; you can redistribute it and/or modify
|
||||
it under the terms of the GNU General Public License as published by
|
||||
the Free Software Foundation; either version 3, or (at your option)
|
||||
any later version.
|
||||
|
||||
GCC is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU General Public License
|
||||
along with GCC; see the file COPYING3. If not see
|
||||
<http://www.gnu.org/licenses/>. */
|
||||
|
||||
#include "elna/frontend/result.h"
|
||||
|
||||
namespace elna::frontend
|
||||
{
|
||||
error::error(const char *path, const struct position position)
|
||||
: position(position), path(path)
|
||||
{
|
||||
}
|
||||
|
||||
std::size_t error::line() const noexcept
|
||||
{
|
||||
return this->position.line;
|
||||
}
|
||||
|
||||
std::size_t error::column() const noexcept
|
||||
{
|
||||
return this->position.column;
|
||||
}
|
||||
|
||||
error_container::error_container(const char *input_file)
|
||||
: input_file(input_file)
|
||||
{
|
||||
}
|
||||
|
||||
std::deque<std::unique_ptr<error>>& error_container::errors()
|
||||
{
|
||||
return m_errors;
|
||||
}
|
||||
|
||||
bool error_container::has_errors() const
|
||||
{
|
||||
return !m_errors.empty();
|
||||
}
|
||||
|
||||
bool identifier_definition::operator==(const identifier_definition& that) const
|
||||
{
|
||||
return *this == that.name;
|
||||
}
|
||||
|
||||
bool identifier_definition::operator==(const std::string& that) const
|
||||
{
|
||||
return this->name == that;
|
||||
}
|
||||
}
|
||||
|
||||
std::size_t std::hash<elna::frontend::identifier_definition>::operator()(
|
||||
const elna::frontend::identifier_definition& key) const
|
||||
{
|
||||
return std::hash<std::string>{}(key.name);
|
||||
}
|
||||
644
frontend/semantic.cc
Normal file
644
frontend/semantic.cc
Normal file
@@ -0,0 +1,644 @@
|
||||
/* Name analysis.
|
||||
Copyright (C) 2025 Free Software Foundation, Inc.
|
||||
|
||||
GCC is free software; you can redistribute it and/or modify
|
||||
it under the terms of the GNU General Public License as published by
|
||||
the Free Software Foundation; either version 3, or (at your option)
|
||||
any later version.
|
||||
|
||||
GCC is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU General Public License
|
||||
along with GCC; see the file COPYING3. If not see
|
||||
<http://www.gnu.org/licenses/>. */
|
||||
|
||||
#include "elna/frontend/semantic.h"
|
||||
|
||||
#include <algorithm>
|
||||
#include <set>
|
||||
|
||||
namespace elna::frontend
|
||||
{
|
||||
undeclared_error::undeclared_error(const std::string& identifier, const char *path, const struct position position)
|
||||
: error(path, position), identifier(identifier)
|
||||
{
|
||||
}
|
||||
|
||||
std::string undeclared_error::what() const
|
||||
{
|
||||
return "Type '" + identifier + "' not declared";
|
||||
}
|
||||
|
||||
already_declared_error::already_declared_error(const std::string& identifier,
|
||||
const char *path, const struct position position)
|
||||
: error(path, position), identifier(identifier)
|
||||
{
|
||||
}
|
||||
|
||||
std::string already_declared_error::what() const
|
||||
{
|
||||
return "Symbol '" + identifier + "' has been already declared";
|
||||
}
|
||||
|
||||
field_duplication_error::field_duplication_error(const std::string& field_name,
|
||||
const char *path, const struct position position)
|
||||
: error(path, position), field_name(field_name)
|
||||
{
|
||||
}
|
||||
|
||||
std::string field_duplication_error::what() const
|
||||
{
|
||||
return "Repeated field name '" + field_name + "'";
|
||||
}
|
||||
|
||||
cyclic_declaration_error::cyclic_declaration_error(const std::vector<std::string>& cycle,
|
||||
const char *path, const struct position position)
|
||||
: error(path, position), cycle(cycle)
|
||||
{
|
||||
}
|
||||
|
||||
std::string cyclic_declaration_error::what() const
|
||||
{
|
||||
auto segment = std::cbegin(this->cycle);
|
||||
std::string message = "Type declaration forms a cycle: " + *segment;
|
||||
|
||||
++segment;
|
||||
for (; segment != std::cend(this->cycle); ++segment)
|
||||
{
|
||||
message += " -> " + *segment;
|
||||
}
|
||||
return message;
|
||||
}
|
||||
|
||||
return_error::return_error(const std::string& identifier, const char *path, const struct position position)
|
||||
: error(path, position), identifier(identifier)
|
||||
{
|
||||
}
|
||||
|
||||
std::string return_error::what() const
|
||||
{
|
||||
return "Procedure '" + identifier + "' is expected to return, but does not have a return statement";
|
||||
}
|
||||
|
||||
variable_initializer_error::variable_initializer_error(const char *path, const struct position position)
|
||||
: error(path, position)
|
||||
{
|
||||
}
|
||||
|
||||
std::string variable_initializer_error::what() const
|
||||
{
|
||||
return "Only one variable can be initialized";
|
||||
}
|
||||
|
||||
type_analysis_visitor::type_analysis_visitor(const char *path, symbol_bag bag)
|
||||
: error_container(path), bag(bag)
|
||||
{
|
||||
}
|
||||
|
||||
void type_analysis_visitor::visit(program *program)
|
||||
{
|
||||
visit(static_cast<unit *>(program));
|
||||
}
|
||||
|
||||
void type_analysis_visitor::visit(procedure_declaration *definition)
|
||||
{
|
||||
if (definition->body.has_value() && definition->heading().return_type.proper_type != nullptr)
|
||||
{
|
||||
for (statement *const statement : definition->body.value().body())
|
||||
{
|
||||
statement->accept(this);
|
||||
}
|
||||
if (!this->returns)
|
||||
{
|
||||
add_error<return_error>(definition->identifier.name, this->input_file, definition->position());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void type_analysis_visitor::visit(assign_statement *)
|
||||
{
|
||||
}
|
||||
|
||||
void type_analysis_visitor::visit(if_statement *)
|
||||
{
|
||||
}
|
||||
|
||||
void type_analysis_visitor::visit(while_statement *)
|
||||
{
|
||||
}
|
||||
|
||||
void type_analysis_visitor::visit(return_statement *)
|
||||
{
|
||||
this->returns = true;
|
||||
}
|
||||
|
||||
void type_analysis_visitor::visit(defer_statement *)
|
||||
{
|
||||
}
|
||||
|
||||
void type_analysis_visitor::visit(case_statement *)
|
||||
{
|
||||
}
|
||||
|
||||
void type_analysis_visitor::visit(procedure_call *)
|
||||
{
|
||||
}
|
||||
|
||||
bool type_analysis_visitor::check_unresolved_symbol(std::shared_ptr<alias_type> alias,
|
||||
std::vector<std::string>& alias_path)
|
||||
{
|
||||
if (std::find(std::cbegin(alias_path), std::cend(alias_path), alias->name) != std::cend(alias_path))
|
||||
{
|
||||
return false;
|
||||
}
|
||||
alias_path.push_back(alias->name);
|
||||
|
||||
if (auto another_alias = alias->reference.get<alias_type>())
|
||||
{
|
||||
return check_unresolved_symbol(another_alias, alias_path);
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
void type_analysis_visitor::visit(unit *unit)
|
||||
{
|
||||
for (type_declaration *const type : unit->types)
|
||||
{
|
||||
type->accept(this);
|
||||
}
|
||||
for (procedure_declaration *const procedure : unit->procedures)
|
||||
{
|
||||
this->returns = false;
|
||||
procedure->accept(this);
|
||||
}
|
||||
}
|
||||
|
||||
void type_analysis_visitor::visit(type_declaration *definition)
|
||||
{
|
||||
std::vector<std::string> alias_path;
|
||||
auto unresolved_type = this->bag.lookup(definition->identifier.name)->is_type()->symbol.get<alias_type>();
|
||||
|
||||
if (!check_unresolved_symbol(unresolved_type, alias_path))
|
||||
{
|
||||
add_error<cyclic_declaration_error>(alias_path, this->input_file, definition->position());
|
||||
}
|
||||
}
|
||||
|
||||
name_analysis_visitor::name_analysis_visitor(const char *path, symbol_bag bag)
|
||||
: error_container(path), bag(bag)
|
||||
{
|
||||
}
|
||||
|
||||
procedure_type name_analysis_visitor::build_procedure(procedure_type_expression& type_expression)
|
||||
{
|
||||
procedure_type::return_t result_return;
|
||||
|
||||
if (type_expression.return_type.no_return)
|
||||
{
|
||||
result_return = procedure_type::return_t(std::monostate{});
|
||||
}
|
||||
else if (type_expression.return_type.proper_type != nullptr)
|
||||
{
|
||||
type_expression.return_type.proper_type->accept(this);
|
||||
result_return = procedure_type::return_t(this->current_type);
|
||||
}
|
||||
else
|
||||
{
|
||||
result_return = procedure_type::return_t();
|
||||
}
|
||||
procedure_type result_type = procedure_type(result_return);
|
||||
|
||||
for (struct type_expression *parameter : type_expression.parameters)
|
||||
{
|
||||
parameter->accept(this);
|
||||
result_type.parameters.push_back(this->current_type);
|
||||
}
|
||||
return result_type;
|
||||
}
|
||||
|
||||
void name_analysis_visitor::visit(program *program)
|
||||
{
|
||||
visit(static_cast<unit *>(program));
|
||||
|
||||
for (statement *const statement : program->body)
|
||||
{
|
||||
statement->accept(this);
|
||||
}
|
||||
}
|
||||
|
||||
void name_analysis_visitor::visit(type_declaration *definition)
|
||||
{
|
||||
definition->body().accept(this);
|
||||
auto resolved = this->bag.resolve(definition->identifier.name, this->current_type);
|
||||
auto info = std::make_shared<type_info>(type(resolved));
|
||||
|
||||
info->exported = definition->identifier.exported;
|
||||
this->bag.enter(definition->identifier.name, info);
|
||||
}
|
||||
|
||||
void name_analysis_visitor::visit(named_type_expression *type_expression)
|
||||
{
|
||||
auto unresolved_alias = this->bag.declared(type_expression->name);
|
||||
|
||||
if (unresolved_alias != nullptr)
|
||||
{
|
||||
this->current_type = type(unresolved_alias);
|
||||
}
|
||||
else if (auto from_symbol_table = this->bag.lookup(type_expression->name))
|
||||
{
|
||||
this->current_type = from_symbol_table->is_type()->symbol;
|
||||
}
|
||||
else
|
||||
{
|
||||
add_error<undeclared_error>(type_expression->name, this->input_file, type_expression->position());
|
||||
this->current_type = type();
|
||||
}
|
||||
}
|
||||
|
||||
void name_analysis_visitor::visit(pointer_type_expression *type_expression)
|
||||
{
|
||||
type_expression->base().accept(this);
|
||||
this->current_type = type(std::make_shared<pointer_type>(this->current_type));
|
||||
}
|
||||
|
||||
void name_analysis_visitor::visit(array_type_expression *type_expression)
|
||||
{
|
||||
type_expression->base().accept(this);
|
||||
this->current_type = type(std::make_shared<array_type>(this->current_type, type_expression->size));
|
||||
}
|
||||
|
||||
std::vector<type_field> name_analysis_visitor::build_composite_type(const std::vector<field_declaration>& fields)
|
||||
{
|
||||
std::vector<type_field> result;
|
||||
std::set<std::string> field_names;
|
||||
|
||||
for (auto& field : fields)
|
||||
{
|
||||
if (field_names.find(field.first) != field_names.cend())
|
||||
{
|
||||
add_error<field_duplication_error>(field.first, this->input_file, field.second->position());
|
||||
}
|
||||
else
|
||||
{
|
||||
field_names.insert(field.first);
|
||||
field.second->accept(this);
|
||||
result.push_back(std::make_pair(field.first, this->current_type));
|
||||
}
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
void name_analysis_visitor::visit(record_type_expression *type_expression)
|
||||
{
|
||||
auto result_type = std::make_shared<record_type>();
|
||||
|
||||
result_type->fields = build_composite_type(type_expression->fields);
|
||||
|
||||
this->current_type = type(result_type);
|
||||
}
|
||||
|
||||
void name_analysis_visitor::visit(union_type_expression *type_expression)
|
||||
{
|
||||
auto result_type = std::make_shared<union_type>();
|
||||
|
||||
result_type->fields = build_composite_type(type_expression->fields);
|
||||
|
||||
this->current_type = type(result_type);
|
||||
}
|
||||
|
||||
void name_analysis_visitor::visit(procedure_type_expression *type_expression)
|
||||
{
|
||||
std::shared_ptr<procedure_type> result_type =
|
||||
std::make_shared<procedure_type>(std::move(build_procedure(*type_expression)));
|
||||
|
||||
this->current_type = type(result_type);
|
||||
}
|
||||
|
||||
void name_analysis_visitor::visit(enumeration_type_expression *type_expression)
|
||||
{
|
||||
std::shared_ptr<enumeration_type> result_type = std::make_shared<enumeration_type>(type_expression->members);
|
||||
|
||||
this->current_type = type(result_type);
|
||||
}
|
||||
|
||||
void name_analysis_visitor::visit(variable_declaration *declaration)
|
||||
{
|
||||
declaration->variable_type().accept(this);
|
||||
|
||||
for (const auto& variable_identifier : declaration->identifiers)
|
||||
{
|
||||
auto variable_symbol = std::make_shared<variable_info>(this->current_type, declaration->is_extern);
|
||||
|
||||
variable_symbol->exported = variable_identifier.exported;
|
||||
if (!this->bag.enter(variable_identifier.name, variable_symbol))
|
||||
{
|
||||
add_error<already_declared_error>(variable_identifier.name, this->input_file,
|
||||
declaration->position());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void name_analysis_visitor::visit(constant_declaration *definition)
|
||||
{
|
||||
definition->body().accept(this);
|
||||
auto constant_symbol = std::make_shared<constant_info>(this->current_literal);
|
||||
|
||||
constant_symbol->exported = definition->identifier.exported;
|
||||
this->bag.enter(definition->identifier.name, constant_symbol);
|
||||
}
|
||||
|
||||
void name_analysis_visitor::visit(procedure_declaration *definition)
|
||||
{
|
||||
std::shared_ptr<procedure_info> info;
|
||||
auto heading = build_procedure(definition->heading());
|
||||
|
||||
if (definition->body.has_value())
|
||||
{
|
||||
info = std::make_shared<procedure_info>(heading, definition->parameter_names, this->bag.enter());
|
||||
|
||||
for (constant_declaration *const constant : definition->body.value().constants())
|
||||
{
|
||||
constant->accept(this);
|
||||
}
|
||||
for (variable_declaration *const variable : definition->body.value().variables())
|
||||
{
|
||||
variable->accept(this);
|
||||
}
|
||||
for (statement *const statement : definition->body.value().body())
|
||||
{
|
||||
statement->accept(this);
|
||||
}
|
||||
this->bag.leave();
|
||||
}
|
||||
else
|
||||
{
|
||||
info = std::make_shared<procedure_info>(heading, definition->parameter_names);
|
||||
}
|
||||
info->exported = definition->identifier.exported;
|
||||
this->bag.enter(definition->identifier.name, info);
|
||||
}
|
||||
|
||||
void name_analysis_visitor::visit(assign_statement *statement)
|
||||
{
|
||||
statement->lvalue().accept(this);
|
||||
statement->rvalue().accept(this);
|
||||
}
|
||||
|
||||
void name_analysis_visitor::visit(if_statement *statement)
|
||||
{
|
||||
statement->body().prerequisite().accept(this);
|
||||
for (struct statement *const statement : statement->body().statements)
|
||||
{
|
||||
statement->accept(this);
|
||||
}
|
||||
for (const auto branch : statement->branches)
|
||||
{
|
||||
branch->prerequisite().accept(this);
|
||||
|
||||
for (struct statement *const statement : branch->statements)
|
||||
{
|
||||
statement->accept(this);
|
||||
}
|
||||
}
|
||||
if (statement->alternative != nullptr)
|
||||
{
|
||||
for (struct statement *const statement : *statement->alternative)
|
||||
{
|
||||
statement->accept(this);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void name_analysis_visitor::visit(import_declaration *)
|
||||
{
|
||||
}
|
||||
|
||||
void name_analysis_visitor::visit(while_statement *statement)
|
||||
{
|
||||
statement->body().prerequisite().accept(this);
|
||||
for (struct statement *const statement : statement->body().statements)
|
||||
{
|
||||
statement->accept(this);
|
||||
}
|
||||
for (const auto branch : statement->branches)
|
||||
{
|
||||
branch->prerequisite().accept(this);
|
||||
|
||||
for (struct statement *const statement : branch->statements)
|
||||
{
|
||||
statement->accept(this);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void name_analysis_visitor::visit(return_statement *statement)
|
||||
{
|
||||
statement->return_expression().accept(this);
|
||||
}
|
||||
|
||||
void name_analysis_visitor::visit(defer_statement *statement)
|
||||
{
|
||||
for (struct statement *const statement : statement->statements)
|
||||
{
|
||||
statement->accept(this);
|
||||
}
|
||||
}
|
||||
|
||||
void name_analysis_visitor::visit(case_statement *statement)
|
||||
{
|
||||
statement->condition().accept(this);
|
||||
for (const switch_case& case_block : statement->cases)
|
||||
{
|
||||
for (expression *const case_label : case_block.labels)
|
||||
{
|
||||
case_label->accept(this);
|
||||
}
|
||||
for (struct statement *const statement : case_block.statements)
|
||||
{
|
||||
statement->accept(this);
|
||||
}
|
||||
}
|
||||
if (statement->alternative != nullptr)
|
||||
{
|
||||
for (struct statement *const statement : *statement->alternative)
|
||||
{
|
||||
statement->accept(this);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void name_analysis_visitor::visit(procedure_call *call)
|
||||
{
|
||||
call->callable().accept(this);
|
||||
for (expression *const argument: call->arguments)
|
||||
{
|
||||
argument->accept(this);
|
||||
}
|
||||
}
|
||||
|
||||
void name_analysis_visitor::visit(unit *unit)
|
||||
{
|
||||
for (type_declaration *const type : unit->types)
|
||||
{
|
||||
type->accept(this);
|
||||
}
|
||||
for (variable_declaration *const variable : unit->variables)
|
||||
{
|
||||
variable->accept(this);
|
||||
}
|
||||
for (procedure_declaration *const procedure : unit->procedures)
|
||||
{
|
||||
procedure->accept(this);
|
||||
}
|
||||
}
|
||||
|
||||
void name_analysis_visitor::visit(traits_expression *trait)
|
||||
{
|
||||
if (!trait->parameters.empty())
|
||||
{
|
||||
trait->parameters.front()->accept(this);
|
||||
trait->types.push_back(this->current_type);
|
||||
}
|
||||
}
|
||||
|
||||
void name_analysis_visitor::visit(cast_expression *expression)
|
||||
{
|
||||
expression->value().accept(this);
|
||||
expression->target().accept(this);
|
||||
expression->expression_type = this->current_type;
|
||||
}
|
||||
|
||||
void name_analysis_visitor::visit(binary_expression *expression)
|
||||
{
|
||||
expression->lhs().accept(this);
|
||||
expression->rhs().accept(this);
|
||||
}
|
||||
|
||||
void name_analysis_visitor::visit(unary_expression *expression)
|
||||
{
|
||||
expression->operand().accept(this);
|
||||
}
|
||||
|
||||
void name_analysis_visitor::visit(variable_expression *)
|
||||
{
|
||||
}
|
||||
|
||||
void name_analysis_visitor::visit(array_access_expression *expression)
|
||||
{
|
||||
expression->base().accept(this);
|
||||
expression->index().accept(this);
|
||||
}
|
||||
|
||||
void name_analysis_visitor::visit(field_access_expression *expression)
|
||||
{
|
||||
expression->base().accept(this);
|
||||
}
|
||||
|
||||
void name_analysis_visitor::visit(dereference_expression *expression)
|
||||
{
|
||||
expression->base().accept(this);
|
||||
}
|
||||
|
||||
void name_analysis_visitor::visit(literal<std::int32_t> *literal)
|
||||
{
|
||||
this->current_literal = literal->value;
|
||||
}
|
||||
|
||||
void name_analysis_visitor::visit(literal<std::uint32_t> *literal)
|
||||
{
|
||||
this->current_literal = literal->value;
|
||||
}
|
||||
|
||||
void name_analysis_visitor::visit(literal<double> *literal)
|
||||
{
|
||||
this->current_literal = literal->value;
|
||||
}
|
||||
|
||||
void name_analysis_visitor::visit(literal<bool> *literal)
|
||||
{
|
||||
this->current_literal = literal->value;
|
||||
}
|
||||
|
||||
void name_analysis_visitor::visit(literal<unsigned char> *literal)
|
||||
{
|
||||
this->current_literal = literal->value;
|
||||
}
|
||||
|
||||
void name_analysis_visitor::visit(literal<std::nullptr_t> *literal)
|
||||
{
|
||||
this->current_literal = literal->value;
|
||||
}
|
||||
|
||||
void name_analysis_visitor::visit(literal<std::string> *literal)
|
||||
{
|
||||
this->current_literal = literal->value;
|
||||
}
|
||||
|
||||
declaration_visitor::declaration_visitor(const char *path)
|
||||
: error_container(path)
|
||||
{
|
||||
}
|
||||
|
||||
void declaration_visitor::visit(program *program)
|
||||
{
|
||||
visit(static_cast<unit *>(program));
|
||||
}
|
||||
|
||||
void declaration_visitor::visit(import_declaration *)
|
||||
{
|
||||
}
|
||||
|
||||
void declaration_visitor::visit(unit *unit)
|
||||
{
|
||||
for (import_declaration *const _import : unit->imports)
|
||||
{
|
||||
_import->accept(this);
|
||||
}
|
||||
for (type_declaration *const type : unit->types)
|
||||
{
|
||||
type->accept(this);
|
||||
}
|
||||
for (variable_declaration *const variable : unit->variables)
|
||||
{
|
||||
variable->accept(this);
|
||||
}
|
||||
for (procedure_declaration *const procedure : unit->procedures)
|
||||
{
|
||||
procedure->accept(this);
|
||||
}
|
||||
}
|
||||
|
||||
void declaration_visitor::visit(type_declaration *definition)
|
||||
{
|
||||
const std::string& type_identifier = definition->identifier.name;
|
||||
|
||||
if (!this->unresolved.insert({ type_identifier, std::make_shared<alias_type>(type_identifier) }).second)
|
||||
{
|
||||
add_error<already_declared_error>(definition->identifier.name, this->input_file,
|
||||
definition->position());
|
||||
}
|
||||
}
|
||||
|
||||
void declaration_visitor::visit(variable_declaration *declaration)
|
||||
{
|
||||
if (declaration->has_initializer() && declaration->identifiers.size() > 1)
|
||||
{
|
||||
add_error<variable_initializer_error>(this->input_file, declaration->position());
|
||||
}
|
||||
}
|
||||
|
||||
void declaration_visitor::visit(procedure_declaration *definition)
|
||||
{
|
||||
if (!definition->body.has_value())
|
||||
{
|
||||
return;
|
||||
}
|
||||
for (variable_declaration *const variable : definition->body.value().variables())
|
||||
{
|
||||
variable->accept(this);
|
||||
}
|
||||
}
|
||||
}
|
||||
427
frontend/symbol.cc
Normal file
427
frontend/symbol.cc
Normal file
@@ -0,0 +1,427 @@
|
||||
/* Symbol definitions.
|
||||
Copyright (C) 2025 Free Software Foundation, Inc.
|
||||
|
||||
GCC is free software; you can redistribute it and/or modify
|
||||
it under the terms of the GNU General Public License as published by
|
||||
the Free Software Foundation; either version 3, or (at your option)
|
||||
any later version.
|
||||
|
||||
GCC is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU General Public License
|
||||
along with GCC; see the file COPYING3. If not see
|
||||
<http://www.gnu.org/licenses/>. */
|
||||
|
||||
#include "elna/frontend/symbol.h"
|
||||
|
||||
namespace elna::frontend
|
||||
{
|
||||
type::type()
|
||||
{
|
||||
}
|
||||
|
||||
type::type(std::shared_ptr<alias_type> alias)
|
||||
: tag(type_tag::alias), alias(alias)
|
||||
{
|
||||
}
|
||||
|
||||
type::type(std::shared_ptr<primitive_type> primitive)
|
||||
: tag(type_tag::primitive), primitive(primitive)
|
||||
{
|
||||
}
|
||||
|
||||
type::type(std::shared_ptr<record_type> record)
|
||||
: tag(type_tag::record), record(record)
|
||||
{
|
||||
}
|
||||
|
||||
type::type(std::shared_ptr<union_type> _union)
|
||||
: tag(type_tag::_union), _union(_union)
|
||||
{
|
||||
}
|
||||
|
||||
type::type(std::shared_ptr<pointer_type> pointer)
|
||||
: tag(type_tag::pointer), pointer(pointer)
|
||||
{
|
||||
}
|
||||
|
||||
type::type(std::shared_ptr<array_type> array)
|
||||
: tag(type_tag::array), array(array)
|
||||
{
|
||||
}
|
||||
|
||||
type::type(std::shared_ptr<procedure_type> procedure)
|
||||
: tag(type_tag::procedure), procedure(procedure)
|
||||
{
|
||||
}
|
||||
|
||||
type::type(std::shared_ptr<enumeration_type> enumeration)
|
||||
: tag(type_tag::enumeration), enumeration(enumeration)
|
||||
{
|
||||
}
|
||||
|
||||
void type::copy(const type& other)
|
||||
{
|
||||
switch (other.tag)
|
||||
{
|
||||
case type_tag::empty:
|
||||
break;
|
||||
case type_tag::alias:
|
||||
new (&alias) std::weak_ptr<alias_type>(other.alias);
|
||||
break;
|
||||
case type_tag::primitive:
|
||||
new (&primitive) std::shared_ptr<primitive_type>(other.primitive);
|
||||
break;
|
||||
case type_tag::record:
|
||||
new (&record) std::shared_ptr<record_type>(other.record);
|
||||
break;
|
||||
case type_tag::_union:
|
||||
new (&_union) std::shared_ptr<union_type>(other._union);
|
||||
break;
|
||||
case type_tag::pointer:
|
||||
new (&pointer) std::shared_ptr<pointer_type>(other.pointer);
|
||||
break;
|
||||
case type_tag::array:
|
||||
new (&array) std::shared_ptr<array_type>(other.array);
|
||||
break;
|
||||
case type_tag::procedure:
|
||||
new (&procedure) std::shared_ptr<procedure_type>(other.procedure);
|
||||
break;
|
||||
case type_tag::enumeration:
|
||||
new (&enumeration) std::shared_ptr<enumeration_type>(other.enumeration);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
type::type(const type& other)
|
||||
: tag(other.tag)
|
||||
{
|
||||
copy(other);
|
||||
}
|
||||
|
||||
void type::move(type&& other)
|
||||
{
|
||||
switch (other.tag)
|
||||
{
|
||||
case type_tag::empty:
|
||||
break;
|
||||
case type_tag::alias:
|
||||
new (&alias) std::weak_ptr<alias_type>(std::move(other.alias));
|
||||
break;
|
||||
case type_tag::primitive:
|
||||
new (&primitive) std::shared_ptr<primitive_type>(std::move(other.primitive));
|
||||
break;
|
||||
case type_tag::record:
|
||||
new (&record) std::shared_ptr<record_type>(std::move(other.record));
|
||||
break;
|
||||
case type_tag::_union:
|
||||
new (&_union) std::shared_ptr<union_type>(std::move(other._union));
|
||||
break;
|
||||
case type_tag::pointer:
|
||||
new (&pointer) std::shared_ptr<pointer_type>(std::move(other.pointer));
|
||||
break;
|
||||
case type_tag::array:
|
||||
new (&array) std::shared_ptr<array_type>(std::move(other.array));
|
||||
break;
|
||||
case type_tag::procedure:
|
||||
new (&procedure) std::shared_ptr<procedure_type>(std::move(other.procedure));
|
||||
break;
|
||||
case type_tag::enumeration:
|
||||
new (&enumeration) std::shared_ptr<enumeration_type>(std::move(other.enumeration));
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
type& type::operator=(const type& other)
|
||||
{
|
||||
this->~type();
|
||||
this->tag = other.tag;
|
||||
copy(other);
|
||||
return *this;
|
||||
}
|
||||
|
||||
type::type(type&& other)
|
||||
: tag(other.tag)
|
||||
{
|
||||
move(std::move(other));
|
||||
}
|
||||
|
||||
type& type::operator=(type&& other)
|
||||
{
|
||||
this->~type();
|
||||
this->tag = other.tag;
|
||||
move(std::move(other));
|
||||
return *this;
|
||||
}
|
||||
|
||||
bool type::operator==(const std::nullptr_t&)
|
||||
{
|
||||
return empty();
|
||||
}
|
||||
|
||||
type::~type()
|
||||
{
|
||||
switch (tag)
|
||||
{
|
||||
case type_tag::empty:
|
||||
break;
|
||||
case type_tag::alias:
|
||||
this->alias.~weak_ptr<alias_type>();
|
||||
break;
|
||||
case type_tag::primitive:
|
||||
this->primitive.~shared_ptr<primitive_type>();
|
||||
break;
|
||||
case type_tag::record:
|
||||
this->record.~shared_ptr<record_type>();
|
||||
break;
|
||||
case type_tag::_union:
|
||||
this->_union.~shared_ptr<union_type>();
|
||||
break;
|
||||
case type_tag::pointer:
|
||||
this->pointer.~shared_ptr<pointer_type>();
|
||||
break;
|
||||
case type_tag::array:
|
||||
this->array.~shared_ptr<array_type>();
|
||||
break;
|
||||
case type_tag::procedure:
|
||||
this->procedure.~shared_ptr<procedure_type>();
|
||||
break;
|
||||
case type_tag::enumeration:
|
||||
this->enumeration.~shared_ptr<enumeration_type>();
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
template<>
|
||||
std::shared_ptr<alias_type> type::get<alias_type>() const
|
||||
{
|
||||
return tag == type_tag::alias ? this->alias.lock() : nullptr;
|
||||
}
|
||||
|
||||
template<>
|
||||
std::shared_ptr<primitive_type> type::get<primitive_type>() const
|
||||
{
|
||||
return tag == type_tag::primitive ? this->primitive : nullptr;
|
||||
}
|
||||
|
||||
template<>
|
||||
std::shared_ptr<record_type> type::get<record_type>() const
|
||||
{
|
||||
return tag == type_tag::record ? this->record : nullptr;
|
||||
}
|
||||
|
||||
template<>
|
||||
std::shared_ptr<union_type> type::get<union_type>() const
|
||||
{
|
||||
return tag == type_tag::_union ? this->_union : nullptr;
|
||||
}
|
||||
|
||||
template<>
|
||||
std::shared_ptr<pointer_type> type::get<pointer_type>() const
|
||||
{
|
||||
return tag == type_tag::pointer ? this->pointer : nullptr;
|
||||
}
|
||||
|
||||
template<>
|
||||
std::shared_ptr<array_type> type::get<array_type>() const
|
||||
{
|
||||
return tag == type_tag::array ? this->array : nullptr;
|
||||
}
|
||||
|
||||
template<>
|
||||
std::shared_ptr<procedure_type> type::get<procedure_type>() const
|
||||
{
|
||||
return tag == type_tag::procedure ? this->procedure : nullptr;
|
||||
}
|
||||
|
||||
template<>
|
||||
std::shared_ptr<enumeration_type> type::get<enumeration_type>() const
|
||||
{
|
||||
return tag == type_tag::enumeration ? this->enumeration : nullptr;
|
||||
}
|
||||
|
||||
bool type::empty() const
|
||||
{
|
||||
return tag == type_tag::empty;
|
||||
}
|
||||
|
||||
alias_type::alias_type(const std::string& name)
|
||||
: name(name), reference()
|
||||
{
|
||||
}
|
||||
|
||||
pointer_type::pointer_type(type base)
|
||||
: base(base)
|
||||
{
|
||||
}
|
||||
|
||||
array_type::array_type(type base, std::uint64_t size)
|
||||
: base(base), size(size)
|
||||
{
|
||||
}
|
||||
|
||||
primitive_type::primitive_type(const std::string& identifier)
|
||||
: identifier(identifier)
|
||||
{
|
||||
}
|
||||
|
||||
procedure_type::procedure_type(return_t return_type)
|
||||
: return_type(return_type)
|
||||
{
|
||||
}
|
||||
|
||||
enumeration_type::enumeration_type(const std::vector<std::string>& members)
|
||||
: members(members)
|
||||
{
|
||||
}
|
||||
|
||||
info::~info()
|
||||
{
|
||||
}
|
||||
|
||||
std::shared_ptr<type_info> info::is_type()
|
||||
{
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
std::shared_ptr<procedure_info> info::is_procedure()
|
||||
{
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
std::shared_ptr<constant_info> info::is_constant()
|
||||
{
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
std::shared_ptr<variable_info> info::is_variable()
|
||||
{
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
type_info::type_info(const type symbol)
|
||||
: symbol(symbol)
|
||||
{
|
||||
}
|
||||
|
||||
std::shared_ptr<type_info> type_info::is_type()
|
||||
{
|
||||
return std::static_pointer_cast<type_info>(shared_from_this());
|
||||
}
|
||||
|
||||
procedure_info::procedure_info(const procedure_type symbol, const std::vector<std::string> names,
|
||||
std::shared_ptr<symbol_table> scope)
|
||||
: symbol(symbol), names(names), scope(scope)
|
||||
{
|
||||
}
|
||||
|
||||
std::shared_ptr<procedure_info> procedure_info::is_procedure()
|
||||
{
|
||||
return std::static_pointer_cast<procedure_info>(shared_from_this());
|
||||
}
|
||||
|
||||
bool procedure_info::is_extern() const
|
||||
{
|
||||
return this->scope == nullptr;
|
||||
}
|
||||
|
||||
constant_info::constant_info(const variant& symbol)
|
||||
: symbol(symbol)
|
||||
{
|
||||
}
|
||||
|
||||
std::shared_ptr<constant_info> constant_info::is_constant()
|
||||
{
|
||||
return std::static_pointer_cast<constant_info>(shared_from_this());
|
||||
}
|
||||
|
||||
variable_info::variable_info(const type symbol, bool is_extern)
|
||||
: symbol(symbol), is_extern(is_extern)
|
||||
{
|
||||
}
|
||||
|
||||
std::shared_ptr<variable_info> variable_info::is_variable()
|
||||
{
|
||||
return std::static_pointer_cast<variable_info>(shared_from_this());
|
||||
}
|
||||
|
||||
std::shared_ptr<symbol_table> builtin_symbol_table()
|
||||
{
|
||||
auto result = std::make_shared<symbol_table>();
|
||||
|
||||
result->enter("Int", std::make_shared<type_info>(type(std::make_shared<primitive_type>("Int"))));
|
||||
result->enter("Word", std::make_shared<type_info>(type(std::make_shared<primitive_type>("Word"))));
|
||||
result->enter("Char", std::make_shared<type_info>(type(std::make_shared<primitive_type>("Char"))));
|
||||
result->enter("Bool", std::make_shared<type_info>(type(std::make_shared<primitive_type>("Bool"))));
|
||||
result->enter("Pointer", std::make_shared<type_info>(type(std::make_shared<primitive_type>("Pointer"))));
|
||||
result->enter("Float", std::make_shared<type_info>(type(std::make_shared<primitive_type>("Float"))));
|
||||
result->enter("String", std::make_shared<type_info>(type(std::make_shared<primitive_type>("String"))));
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
symbol_bag::symbol_bag(forward_table&& unresolved, std::shared_ptr<symbol_table> global_table)
|
||||
: unresolved(unresolved)
|
||||
{
|
||||
this->symbols = std::make_shared<symbol_table>(global_table);
|
||||
}
|
||||
|
||||
std::shared_ptr<info> symbol_bag::lookup(const std::string& name)
|
||||
{
|
||||
for (auto import_bag : this->imports)
|
||||
{
|
||||
if (auto result = import_bag->lookup(name))
|
||||
{
|
||||
return result;
|
||||
}
|
||||
}
|
||||
return this->symbols->lookup(name);
|
||||
}
|
||||
|
||||
bool symbol_bag::enter(const std::string& name, std::shared_ptr<info> entry)
|
||||
{
|
||||
return this->symbols->enter(name, entry);
|
||||
}
|
||||
|
||||
std::shared_ptr<symbol_table> symbol_bag::enter()
|
||||
{
|
||||
this->symbols = std::make_shared<symbol_table>(this->symbols);
|
||||
return this->symbols;
|
||||
}
|
||||
|
||||
void symbol_bag::enter(std::shared_ptr<symbol_table> child)
|
||||
{
|
||||
this->symbols = child;
|
||||
}
|
||||
|
||||
std::shared_ptr<symbol_table> symbol_bag::leave()
|
||||
{
|
||||
std::shared_ptr<symbol_table> result = this->symbols;
|
||||
|
||||
this->symbols = result->scope();
|
||||
return result;
|
||||
}
|
||||
|
||||
std::shared_ptr<alias_type> symbol_bag::declared(const std::string& symbol_name)
|
||||
{
|
||||
auto unresolved_alias = this->unresolved.find(symbol_name);
|
||||
|
||||
return unresolved_alias == this->unresolved.end() ? std::shared_ptr<alias_type>() : unresolved_alias->second;
|
||||
}
|
||||
|
||||
std::shared_ptr<alias_type> symbol_bag::resolve(const std::string& symbol_name, type& resolution)
|
||||
{
|
||||
auto unresolved_declaration = this->unresolved.at(symbol_name);
|
||||
|
||||
unresolved_declaration->reference = resolution;
|
||||
return unresolved_declaration;
|
||||
}
|
||||
|
||||
void symbol_bag::add_import(const symbol_bag& bag)
|
||||
{
|
||||
this->imports.push_front(bag.symbols);
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user