1 Commits

Author SHA1 Message Date
16266b63ce Rename AST types to type expressions 2025-03-02 23:35:07 +01:00
42 changed files with 2988 additions and 8203 deletions

1
.ruby-version Normal file
View File

@@ -0,0 +1 @@
3.3.6

View File

@@ -24,7 +24,7 @@ in the `boot/` directory.
## Build
The frontend requires GCC 15.1.0 (not tested with other versions).
The frontend requires GCC 14.2.0 (not tested with other versions).
Download the GCC source. Copy the contents of this repository into `gcc/elna`
inside GCC. Finally build GCC enabling the frontend with
@@ -41,12 +41,5 @@ and Mac OS. In the latter case GCC is patched with the patches used by Homebrew
rake boot
```
`gcc` binary is used by default, but a different gcc version can be specified
by passing `CC` and `CXX` environment variables to rake, e.g.:
```sh
rake CC=gcc-15 CXX=g++-15 boot
```
See `rake -T` for more tasks. The GCC source is under `build/tools`. The
installation path is `build/host/install`.

185
Rakefile
View File

@@ -1,43 +1,182 @@
# This Source Code Form is subject to the terms of the Mozilla Public License,
# v. 2.0. If a copy of the MPL was not distributed with this file, You can
# obtain one at https://mozilla.org/MPL/2.0/.
# obtain one at https://mozilla.org/MPL/2.0/. -}
require 'pathname'
require 'open3'
require 'rake/clean'
require_relative 'tools/support'
TMP = Pathname.new('./build')
# Dependencies.
GCC_VERSION = "14.2.0"
GCC_PATCH = 'https://raw.githubusercontent.com/Homebrew/formula-patches/f30c309442a60cfb926e780eae5d70571f8ab2cb/gcc/gcc-14.2.0-r2.diff'
# Paths.
HOST_GCC = TMP + 'host/gcc'
HOST_INSTALL = TMP + 'host/install'
CLOBBER.include TMP
CLEAN.include(TMP + 'boot')
directory(TMP + 'tools')
directory HOST_GCC
directory HOST_INSTALL
task default: ['source/main.elna', TMP + 'boot/elna'] do |t|
sources, compiler = t.prerequisites.partition { |f| f.end_with? '.elna' }
sh *compiler, '--parse', *sources
task default: [TMP + 'elna'] do
sh (TMP + 'elna').to_path, '--tokenize', 'source.elna'
end
rule(/boot\/.+\.o$/ => ->(file) {
source = Pathname.new('source') +
Pathname.new(file).relative_path_from(TMP + 'boot').sub_ext('.elna')
namespace :boot do
desc 'Download and configure the bootstrap compiler'
task configure: [TMP + 'tools', HOST_GCC, HOST_INSTALL] do
url = URI.parse "https://gcc.gnu.org/pub/gcc/releases/gcc-#{GCC_VERSION}/gcc-#{GCC_VERSION}.tar.xz"
options = find_build_target GCC_VERSION
source_directory = TMP + "tools/gcc-#{GCC_VERSION}"
frontend_link = source_directory + 'gcc'
[HOST_INSTALL + 'bin/gelna', source]
}) do |t|
Pathname.new(t.name).dirname.mkpath
sources, compiler = t.prerequisites.partition { |source| source.end_with? '.elna' }
download_and_pipe url, source_directory.dirname, ['tar', '-Jxv']
download_and_pipe URI.parse(GCC_PATCH), source_directory, ['patch', '-p1']
sh *compiler, '-c', '-O0', '-g', '-o', t.name, *sources
sh 'contrib/download_prerequisites', chdir: source_directory.to_path
File.symlink Pathname.new('.').relative_path_from(frontend_link), (frontend_link + 'elna')
configure_options = [
"--prefix=#{HOST_INSTALL.realpath}",
"--with-sysroot=#{options.sysroot.realpath}",
'--enable-languages=c,c++,elna',
'--disable-bootstrap',
'--disable-multilib',
"--target=#{options.build}",
"--build=#{options.build}",
"--host=#{options.build}"
]
flags = '-O0 -g -fPIC -I/opt/homebrew/Cellar/flex/2.6.4_2/include'
env = {
'CC' => options.gcc,
'CXX' => options.gxx,
'CFLAGS' => flags,
'CXXFLAGS' => flags,
}
configure = source_directory.relative_path_from(HOST_GCC) + 'configure'
sh env, configure.to_path, *configure_options, chdir: HOST_GCC.to_path
end
desc 'Make and install the bootstrap compiler'
task :make do
cwd = HOST_GCC.to_path
sh 'make', '-j', Etc.nprocessors.to_s, chdir: cwd
sh 'make', 'install', chdir: cwd
end
end
file TMP + 'boot/elna' => FileList['source/**/*.elna'].reject { |file|
file != file.downcase
}.map { |file|
TMP + 'boot' +
Pathname.new(file).relative_path_from('source').sub_ext('.o')
} do |t|
compiler = HOST_INSTALL + 'bin/gcc'
desc 'Build the bootstrap compiler'
task boot: %w[boot:configure boot:make]
sh compiler.to_path, '-o', t.name, *t.prerequisites
file (TMP + 'elna').to_path => ['source.elna']
file (TMP + 'elna').to_path => [(HOST_INSTALL + 'bin/gelna').to_path] do |task|
sh (HOST_INSTALL + 'bin/gelna').to_path, '-o', task.name, task.prerequisites.first
end
namespace :cross do
desc 'Build cross toolchain'
task :init, [:target] do |_, args|
args.with_defaults target: 'riscv32-unknown-linux-gnu'
options = find_build_target GCC_VERSION, args[:target]
env = {
'PATH' => "#{options.rootfs.realpath + 'bin'}:#{ENV['PATH']}"
}
sh env, 'riscv32-unknown-linux-gnu-gcc',
'-ffreestanding', '-static',
'-o', (options.tools + 'init').to_path,
'tools/init.c'
end
end
namespace :test do
test_sources = FileList['tests/vm/*.elna', 'tests/vm/*.s']
compiler = TMP + 'bin/elna'
object_directory = TMP + 'riscv/tests'
root_directory = TMP + 'riscv/root'
executable_directory = root_directory + 'tests'
expectation_directory = root_directory + 'expectations'
init = TMP + 'riscv/root/init'
builtin = TMP + 'riscv/builtin.o'
directory root_directory
directory object_directory
directory executable_directory
directory expectation_directory
file builtin => ['tools/builtin.s', object_directory] do |task|
sh AS, '-o', task.name, task.prerequisites.first
end
test_files = test_sources.flat_map do |test_source|
test_basename = File.basename(test_source, '.*')
test_object = object_directory + test_basename.ext('.o')
file test_object => [test_source, object_directory] do |task|
case File.extname(task.prerequisites.first)
when '.s'
sh AS, '-mno-relax', '-o', task.name, task.prerequisites.first
when '.elna'
sh compiler, '--output', task.name, task.prerequisites.first
else
raise "Unknown source file extension #{task.prerequisites.first}"
end
end
test_executable = executable_directory + test_basename
file test_executable => [test_object, executable_directory, builtin] do |task|
objects = task.prerequisites.filter { |prerequisite| File.file? prerequisite }
sh LINKER, '-o', test_executable.to_path, *objects
end
expectation_name = test_basename.ext '.txt'
source_expectation = "tests/expectations/#{expectation_name}"
target_expectation = expectation_directory + expectation_name
file target_expectation => [source_expectation, expectation_directory] do
cp source_expectation, target_expectation
end
[test_executable, target_expectation]
end
file init => [root_directory] do |task|
cp (TMP + 'tools/init'), task.name
end
# Directories should come first.
test_files.unshift executable_directory, expectation_directory, init
file (TMP + 'riscv/root.cpio') => test_files do |task|
root_files = task.prerequisites
.map { |prerequisite| Pathname.new(prerequisite).relative_path_from(root_directory).to_path }
File.open task.name, 'wb' do |cpio_file|
cpio_options = {
chdir: root_directory.to_path
}
cpio_stream = Open3.popen2 'cpio', '-o', '--format=newc', cpio_options do |stdin, stdout, wait_thread|
stdin.write root_files.join("\n")
stdin.close
stdout.each { |chunk| cpio_file.write chunk }
wait_thread.value
end
end
end
task :vm => (TMP + 'riscv/root.cpio') do |task|
kernels = FileList.glob(TMP + 'tools/linux-*/arch/riscv/boot/Image')
sh 'qemu-system-riscv32',
'-nographic',
'-M', 'virt',
'-bios', 'default',
'-kernel', kernels.first,
'-append', 'quiet panic=1',
'-initrd', task.prerequisites.first,
'-no-reboot'
end
end

File diff suppressed because it is too large Load Diff

View File

@@ -1,102 +0,0 @@
/* 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/boot/dependency.h"
#include <fstream>
#include <sstream>
#include <string.h>
#include "elna/boot/driver.h"
#include "elna/boot/semantic.h"
#include "parser.hh"
namespace elna::boot
{
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;
}
}

View File

@@ -17,7 +17,9 @@ along with GCC; see the file COPYING3. If not see
#include "elna/boot/driver.h"
namespace elna::boot
namespace elna
{
namespace boot
{
position make_position(const yy::location& location)
{
@@ -40,10 +42,20 @@ namespace elna::boot
}
driver::driver(const char *input_file)
: error_container(input_file)
: input_file(input_file)
{
}
void driver::error(const yy::location& loc, const std::string& message)
{
m_errors.emplace_back(new boot::syntax_error(message, input_file, loc));
}
const std::list<std::unique_ptr<struct error>>& driver::errors() const noexcept
{
return m_errors;
}
char escape_char(char escape)
{
switch (escape)
@@ -76,49 +88,5 @@ namespace elna::boot
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;
}
}
}

View File

@@ -23,298 +23,273 @@ along with GCC; see the file COPYING3. If not see
#include "parser.hh"
#undef YY_DECL
#define YY_DECL yy::parser::symbol_type elna::boot::lexer::lex(driver& driver)
#define YY_DECL yy::parser::symbol_type elna::boot::lexer::lex(elna::boot::driver& driver)
#define yyterminate() return yy::parser::make_YYEOF(this->location)
%}
%option c++ noyywrap never-interactive
%option yyclass="lexer"
%option yyclass="elna::boot::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);
\*\) BEGIN(INITIAL);
[^*\n]+ ; /* Eat comment in chunks. */
\* ; /* Eat the lone star. */
\n+ {
this->location.lines(yyleng);
this->location.step();
}
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);
\(\* 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);
}
and {
return yy::parser::make_AND(this->location);
}
xor {
return yy::parser::make_XOR(this->location);
}
or {
return yy::parser::make_OR(this->location);
}
not {
return yy::parser::make_NOT(this->location);
}
return {
return yy::parser::make_RETURN(this->location);
}
cast {
return yy::parser::make_CAST(this->location);
}
defer {
return yy::parser::make_DEFER(this->location);
}
[A-Za-z_][A-Za-z0-9_]* {
return yy::parser::make_IDENTIFIER(yytext, this->location);
}
#[A-Za-z_][A-Za-z0-9_]* {
return yy::parser::make_TRAIT(yytext + 1, this->location);
}
[0-9]+u {
return yy::parser::make_WORD(strtoul(yytext, NULL, 10), this->location);
}
[0-9]+ {
return yy::parser::make_INTEGER(strtol(yytext, NULL, 10), this->location);
}
[0-9]+\.[0-9] {
return yy::parser::make_FLOAT(strtof(yytext, NULL), this->location);
}
'[[:print:]]' {
if (yytext[1] == '\\' || yytext[1] == '\'')
{
REJECT;
}
else
{
return yy::parser::make_CHARACTER(std::string(yytext, 1, 1), this->location);
}
}
'\\x[0-9a-fA-F]{1,2}' {
char character = static_cast<char>(std::stoi(yytext + 3, nullptr, 16));
if (errno == ERANGE)
{
REJECT;
}
else
{
return yy::parser::make_WORD(result, this->location);
}
}
[[:digit:]]+ {
long result = strtol(yytext, NULL, 10);
return yy::parser::make_CHARACTER(std::string(&character, 1), this->location);
}
'\\[0nabtfrv\\'"?]' {
char escape = elna::boot::escape_char(yytext[2]);
if (escape == escape_invalid_char)
{
REJECT;
}
return yy::parser::make_CHARACTER(std::string(&escape, 1), this->location);
}
\"[[:print:]]*\" {
std::string result;
const char *current_position = yytext + 1;
if (errno == ERANGE)
{
REJECT;
}
else
{
return yy::parser::make_INTEGER(result, this->location);
}
}
0x{HIGIT}+ {
unsigned long result = strtoul(yytext, NULL, 16);
while (*current_position != '\0')
{
if (*current_position == '\\' && *(current_position + 1) == 'x')
{
current_position += 2;
if (errno == ERANGE)
{
REJECT;
}
else
{
return yy::parser::make_WORD(result, this->location);
}
}
0b{BIGIT}+ {
unsigned long result = strtoul(yytext, NULL, 2);
std::size_t processed;
char character = static_cast<char>(std::stoi(current_position, &processed, 16));
if (processed == 0)
{
REJECT;
}
else
{
current_position += processed - 1;
result.push_back(character);
}
}
else if (*current_position == '\\')
{
++current_position;
if (errno == ERANGE)
{
REJECT;
}
else
{
return yy::parser::make_WORD(result, this->location);
}
}
[[:digit:]]+\.[[:digit:]]+ {
float result = strtof(yytext, NULL);
char escape = elna::boot::escape_char(*current_position);
if (escape == elna::boot::escape_invalid_char)
{
REJECT;
}
result.push_back(escape);
}
else
{
result.push_back(*current_position);
}
++current_position;
}
result.pop_back();
return yy::parser::make_STRING(result, 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;
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);
}
ss << "Illegal character 0x" << std::hex << static_cast<unsigned int>(yytext[0]);
driver.error(this->location, ss.str());
}
%%

View File

@@ -18,10 +18,6 @@ along with GCC; see the file COPYING3. If not see
%require "3.4"
%language "c++"
%code {
using namespace elna;
}
%code requires {
#include <cstdint>
#include <iostream>
@@ -56,7 +52,7 @@ along with GCC; see the file COPYING3. If not see
{
}
yy::parser::symbol_type lex(driver& driver);
yy::parser::symbol_type lex(elna::boot::driver& driver);
};
}
@@ -91,7 +87,7 @@ along with GCC; see the file COPYING3. If not see
ARROW "->" EXCLAMATION "!"
AT "@" HAT "^"
COLON ":" SEMICOLON ";" DOT "." COMMA ","
%token NOT "~"
%token NOT "not"
CAST "cast"
NIL "nil"
CONST "const"
@@ -108,107 +104,86 @@ along with GCC; see the file COPYING3. If not see
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"
%token OR "or" AND "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 "or" "and" "xor"
%left "=" "<>" "<" ">" "<=" ">="
%left "<<" ">>"
%left "+" "-"
%left "*" "/" "%"
%type <elna::boot::literal_expression *> literal;
%type <std::vector<elna::boot::expression *>> case_labels;
%type <elna::boot::switch_case> switch_case;
%type <std::vector<elna::boot::switch_case>> switch_cases;
%type <elna::boot::constant_declaration *> constant_declaration;
%type <std::vector<elna::boot::constant_declaration *>> constant_part constant_declarations;
%type <elna::boot::variable_declaration *> variable_declaration;
%type <std::vector<elna::boot::variable_declaration *>> variable_declarations variable_part;
%type <elna::boot::type_expression *> type_expression;
%type <std::vector<elna::boot::type_expression *>> type_expressions;
%type <elna::boot::literal *> literal;
%type <elna::boot::constant_definition *> constant_definition;
%type <std::vector<elna::boot::constant_definition *>> constant_part constant_definitions;
%type <std::vector<elna::boot::variable_declaration *>> variable_declarations variable_part variable_declaration
formal_parameters formal_parameter_list;
%type <elna::boot::variable_declaration *> formal_parameter
%type <std::shared_ptr<elna::boot::type_expression>> type_expression;
%type <elna::boot::traits_expression *> traits_expression;
%type <elna::boot::expression *> expression operand simple_expression;
%type <elna::boot::expression *> expression operand;
%type <elna::boot::unary_expression *> unary_expression;
%type <elna::boot::binary_expression *> binary_expression;
%type <std::vector<elna::boot::expression *>> expressions actual_parameter_list;
%type <elna::boot::designator_expression *> designator_expression;
%type <elna::boot::assign_statement *> assign_statement;
%type <elna::boot::procedure_call*> call_expression;
%type <elna::boot::while_statement *> while_statement;
%type <elna::boot::if_statement *> if_statement;
%type <elna::boot::return_statement *> return_statement;
%type <elna::boot::statement *> statement;
%type <std::vector<elna::boot::statement *>> required_statements optional_statements statement_part;
%type <elna::boot::procedure_declaration *> procedure_declaration;
%type <std::pair<std::vector<std::string>, elna::boot::procedure_type_expression *>> procedure_heading;
%type <elna::boot::procedure_type_expression::return_t> return_declaration;
%type <std::vector<elna::boot::procedure_declaration *>> procedure_declarations procedure_part;
%type <elna::boot::type_declaration *> type_declaration;
%type <std::vector<elna::boot::type_declaration *>> type_declarations type_part;
%type <std::unique_ptr<elna::boot::block>> block;
%type <elna::boot::field_declaration> field_declaration formal_parameter;
%type <std::vector<std::pair<std::string, elna::boot::type_expression *>>>
optional_fields required_fields formal_parameters formal_parameter_list;
%type <std::vector<elna::boot::statement *>> statements;
%type <elna::boot::procedure_definition *> procedure_definition;
%type <std::shared_ptr<elna::boot::procedure_type_expression>> procedure_heading;
%type <std::vector<elna::boot::procedure_definition *>> procedure_definitions procedure_part;
%type <elna::boot::type_definition *> type_definition;
%type <std::vector<elna::boot::type_definition *>> type_definitions type_part;
%type <elna::boot::block *> block;
%type <elna::boot::field_t> field_declaration;
%type <std::vector<std::pair<std::string, std::shared_ptr<elna::boot::type_expression>>>>
optional_fields required_fields;
%type <std::vector<elna::boot::conditional_statements *>> elsif_then_statements elsif_do_statements;
%type <std::vector<elna::boot::statement *> *> else_statements;
%type <elna::boot::cast_expression *> cast_expression;
%type <elna::boot::identifier_definition> identifier_definition;
%type <std::vector<elna::boot::identifier_definition>> identifier_definitions;
%type <std::vector<std::string>> identifiers import_declaration;
%type <std::vector<elna::boot::import_declaration *>> import_declarations import_part;
%type <elna::boot::defer_statement *> defer_statement;
%type <std::pair<std::string, bool>> identifier_definition;
%type <std::vector<std::pair<std::string, bool>>> identifier_definitions;
%%
program:
"program" ";" import_part constant_part type_part variable_part procedure_part statement_part "end" "."
constant_part type_part variable_part procedure_part "begin" statements "end" "."
{
auto tree = new boot::program(boot::make_position(@1));
auto tree = new elna::boot::program(elna::boot::make_position(@5));
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);
std::swap(tree->constants, $1);
std::swap(tree->types , $2);
std::swap(tree->variables, $3);
std::swap(tree->procedures, $4);
std::swap(tree->body, $6);
driver.tree.reset(tree);
}
| "module" ";" import_part constant_part type_part variable_part procedure_part "end" "."
block: constant_part variable_part "begin" statements "end"
{
auto tree = new boot::unit(boot::make_position(@1));
$$ = new elna::boot::block(elna::boot::make_position(@3));
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<boot::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);
std::swap($$->constants, $1);
std::swap($$->variables, $2);
std::swap($$->body, $4);
}
identifier_definition:
IDENTIFIER "*" { $$ = boot::identifier_definition{ $1, true }; }
| IDENTIFIER { $$ = boot::identifier_definition{ $1, false }; }
IDENTIFIER "*"
{
$$ = std::make_pair($1, true);
}
| IDENTIFIER
{
$$ = std::make_pair($1, false);
}
identifier_definitions:
identifier_definition "," identifier_definitions
{
@@ -216,174 +191,246 @@ identifier_definitions:
$$.emplace($$.cbegin(), $1);
}
| identifier_definition { $$.emplace_back(std::move($1)); }
return_declaration:
/* proper procedure */ {}
| "->" "!" { $$ = boot::procedure_type_expression::return_t(std::monostate{}); }
| "->" type_expression { $$ = boot::procedure_type_expression::return_t($2); }
procedure_heading: formal_parameter_list return_declaration
procedure_heading:
formal_parameter_list
{
$$.second = new boot::procedure_type_expression(boot::make_position(@1), std::move($2));
for (auto& [name, type] : $1)
{
$$.first.emplace_back(std::move(name));
$$.second->parameters.push_back(type);
}
$$ = std::make_shared<elna::boot::procedure_type_expression>(elna::boot::make_position(@1));
std::swap($1, $$->parameters);
}
procedure_declaration:
"proc" identifier_definition procedure_heading ";" block ";"
| formal_parameter_list "->" "!"
{
$$ = new boot::procedure_declaration(boot::make_position(@1), std::move($2), $3.second, std::move(*$5));
std::swap($3.first, $$->parameter_names);
$$ = std::make_shared<elna::boot::procedure_type_expression>(elna::boot::make_position(@1),
elna::boot::no_return);
std::swap($1, $$->parameters);
}
| "proc" identifier_definition procedure_heading ";" "extern" ";"
| formal_parameter_list "->" type_expression
{
$$ = new boot::procedure_declaration(boot::make_position(@1), std::move($2), $3.second);
std::swap($3.first, $$->parameter_names);
$$ = std::make_shared<elna::boot::procedure_type_expression>(elna::boot::make_position(@1), $3);
std::swap($1, $$->parameters);
}
procedure_declarations:
procedure_declaration procedure_declarations
procedure_definition:
"proc" identifier_definition procedure_heading ";" block
{
$$ = new elna::boot::procedure_definition(elna::boot::make_position(@1), $2.first, $2.second, $3, $5);
}
| "proc" identifier_definition procedure_heading ";" "extern"
{
$$ = new elna::boot::procedure_definition(elna::boot::make_position(@1), $2.first, $2.second, $3);
}
procedure_definitions:
procedure_definition procedure_definitions
{
std::swap($$, $2);
$$.emplace($$.cbegin(), std::move($1));
}
| procedure_declaration { $$.emplace_back(std::move($1)); }
| procedure_definition { $$.emplace_back(std::move($1)); }
procedure_part:
/* no procedure definitions */ {}
| procedure_declarations { std::swap($$, $1); }
| procedure_definitions { std::swap($$, $1); }
assign_statement: designator_expression ":=" expression
{
$$ = new elna::boot::assign_statement(elna::boot::make_position(@1), $1, $3);
}
call_expression: designator_expression actual_parameter_list
{
$$ = new boot::procedure_call(boot::make_position(@1), $1);
$$ = new elna::boot::procedure_call(elna::boot::make_position(@1), $1);
std::swap($$->arguments, $2);
}
cast_expression: "cast" "(" expression ":" type_expression ")"
{ $$ = new boot::cast_expression(boot::make_position(@1), $5, $3); }
{
$$ = new elna::boot::cast_expression(elna::boot::make_position(@1), $5, $3);
}
elsif_do_statements:
"elsif" expression "do" optional_statements elsif_do_statements
"elsif" expression "do" statements elsif_do_statements
{
boot::conditional_statements *branch = new boot::conditional_statements($2, std::move($4));
elna::boot::conditional_statements *branch = new elna::boot::conditional_statements($2);
std::swap(branch->statements, $4);
std::swap($5, $$);
$$.emplace($$.begin(), branch);
}
| {}
else_statements:
"else" optional_statements { $$ = new std::vector<boot::statement *>(std::move($2)); }
| { $$ = nullptr; }
while_statement: "while" expression "do" statements elsif_do_statements "end"
{
auto body = new elna::boot::conditional_statements($2);
std::swap($4, body->statements);
$$ = new elna::boot::while_statement(elna::boot::make_position(@1), body);
std::swap($5, $$->branches);
}
elsif_then_statements:
"elsif" expression "then" optional_statements elsif_then_statements
"elsif" expression "then" statements elsif_then_statements
{
boot::conditional_statements *branch = new boot::conditional_statements($2, std::move($4));
elna::boot::conditional_statements *branch = new elna::boot::conditional_statements($2);
std::swap(branch->statements, $4);
std::swap($5, $$);
$$.emplace($$.begin(), branch);
}
| {}
return_statement: "return" expression
{ $$ = new boot::return_statement(boot::make_position(@1), $2); }
literal:
INTEGER { $$ = new boot::literal<std::int32_t>(boot::make_position(@1), $1); }
| WORD { $$ = new boot::literal<std::uint32_t>(boot::make_position(@1), $1); }
| FLOAT { $$ = new boot::literal<double>(boot::make_position(@1), $1); }
| BOOLEAN { $$ = new boot::literal<bool>(boot::make_position(@1), $1); }
| CHARACTER { $$ = new boot::literal<unsigned char>(boot::make_position(@1), $1.at(0)); }
| "nil" { $$ = new boot::literal<std::nullptr_t>(boot::make_position(@1), nullptr); }
| STRING { $$ = new boot::literal<std::string>(boot::make_position(@1), $1); }
traits_expression:
TRAIT "(" type_expressions ")"
if_statement:
"if" expression "then" statements elsif_then_statements "end"
{
$$ = new boot::traits_expression(boot::make_position(@1), $1);
std::swap($3, $$->parameters);
auto then = new elna::boot::conditional_statements($2);
std::swap($4, then->statements);
$$ = new elna::boot::if_statement(elna::boot::make_position(@1), then);
std::swap($5, $$->branches);
}
simple_expression:
| "if" expression "then" statements elsif_then_statements "else" statements "end"
{
auto then = new elna::boot::conditional_statements($2);
std::swap($4, then->statements);
auto _else = new std::vector<elna::boot::statement *>(std::move($7));
$$ = new elna::boot::if_statement(elna::boot::make_position(@1), then, _else);
std::swap($5, $$->branches);
}
return_statement: "return" expression
{
$$ = new elna::boot::return_statement(elna::boot::make_position(@1), $2);
}
defer_statement: DEFER statements "end"
{
$$ = new elna::boot::defer_statement(elna::boot::make_position(@1));
std::swap($2, $$->statements);
}
literal:
INTEGER
{
$$ = new elna::boot::number_literal<std::int32_t>(elna::boot::make_position(@1), $1);
}
| WORD
{
$$ = new elna::boot::number_literal<std::uint32_t>(elna::boot::make_position(@1), $1);
}
| FLOAT
{
$$ = new elna::boot::number_literal<double>(elna::boot::make_position(@1), $1);
}
| BOOLEAN
{
$$ = new elna::boot::number_literal<bool>(elna::boot::make_position(@1), $1);
}
| CHARACTER
{
$$ = new elna::boot::number_literal<unsigned char>(elna::boot::make_position(@1), $1.at(0));
}
| "nil"
{
$$ = new elna::boot::number_literal<std::nullptr_t>(elna::boot::make_position(@1), nullptr);
}
| STRING
{
$$ = new elna::boot::number_literal<std::string>(elna::boot::make_position(@1), $1);
}
traits_expression:
TRAIT "(" type_expression ")"
{
$$ = new elna::boot::traits_expression(elna::boot::make_position(@1), $1, $3);
}
operand:
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; }
unary_expression { $$ = $1; }
| binary_expression { $$ = $1; }
| operand { $$ = $1; }
binary_expression:
expression "*" expression
{
$$ = new boot::binary_expression(boot::make_position(@2), $1, $3, boot::binary_operator::multiplication);
$$ = new elna::boot::binary_expression(elna::boot::make_position(@2), $1, $3,
elna::boot::binary_operator::multiplication);
}
| expression "/" expression
{
$$ = new boot::binary_expression(boot::make_position(@2), $1, $3, boot::binary_operator::division);
$$ = new elna::boot::binary_expression(elna::boot::make_position(@2), $1, $3,
elna::boot::binary_operator::division);
}
| expression "%" expression
{
$$ = new boot::binary_expression(boot::make_position(@2), $1, $3, boot::binary_operator::remainder);
$$ = new elna::boot::binary_expression(elna::boot::make_position(@2), $1, $3,
elna::boot::binary_operator::remainder);
}
| expression "+" expression
{
$$ = new boot::binary_expression(boot::make_position(@2), $1, $3, boot::binary_operator::sum);
$$ = new elna::boot::binary_expression(elna::boot::make_position(@2), $1, $3,
elna::boot::binary_operator::sum);
}
| expression "-" expression
{
$$ = new boot::binary_expression(boot::make_position(@2), $1, $3, boot::binary_operator::subtraction);
$$ = new elna::boot::binary_expression(elna::boot::make_position(@2), $1, $3,
elna::boot::binary_operator::subtraction);
}
| expression "=" expression
{
$$ = new boot::binary_expression(boot::make_position(@2), $1, $3, boot::binary_operator::equals);
$$ = new elna::boot::binary_expression(elna::boot::make_position(@2), $1, $3,
elna::boot::binary_operator::equals);
}
| expression "<>" expression
{
$$ = new boot::binary_expression(boot::make_position(@2), $1, $3, boot::binary_operator::not_equals);
$$ = new elna::boot::binary_expression(elna::boot::make_position(@2), $1, $3,
elna::boot::binary_operator::not_equals);
}
| expression "<" expression
{
$$ = new boot::binary_expression(boot::make_position(@2), $1, $3, boot::binary_operator::less);
$$ = new elna::boot::binary_expression(elna::boot::make_position(@2), $1, $3,
elna::boot::binary_operator::less);
}
| expression ">" expression
{
$$ = new boot::binary_expression(boot::make_position(@2), $1, $3, boot::binary_operator::greater);
$$ = new elna::boot::binary_expression(elna::boot::make_position(@2), $1, $3,
elna::boot::binary_operator::greater);
}
| expression "<=" expression
{
$$ = new boot::binary_expression(boot::make_position(@2), $1, $3,
boot::binary_operator::less_equal);
$$ = new elna::boot::binary_expression(elna::boot::make_position(@2), $1, $3,
elna::boot::binary_operator::less_equal);
}
| expression ">=" expression
{
$$ = new boot::binary_expression(boot::make_position(@2), $1, $3, boot::binary_operator::greater_equal);
$$ = new elna::boot::binary_expression(elna::boot::make_position(@2), $1, $3,
elna::boot::binary_operator::greater_equal);
}
| expression "&" expression
| expression "and" expression
{
$$ = new boot::binary_expression(boot::make_position(@2), $1, $3, boot::binary_operator::conjunction);
$$ = new elna::boot::binary_expression(elna::boot::make_position(@2), $1, $3,
elna::boot::binary_operator::conjunction);
}
| expression "or" expression
{
$$ = new boot::binary_expression(boot::make_position(@2), $1, $3, boot::binary_operator::disjunction);
$$ = new elna::boot::binary_expression(elna::boot::make_position(@2), $1, $3,
elna::boot::binary_operator::disjunction);
}
| expression "xor" expression
{
$$ = new boot::binary_expression(boot::make_position(@2), $1, $3,
boot::binary_operator::exclusive_disjunction);
$$ = new elna::boot::binary_expression(elna::boot::make_position(@2), $1, $3,
elna::boot::binary_operator::exclusive_disjunction);
}
| expression "<<" expression
{
$$ = new boot::binary_expression(boot::make_position(@2), $1, $3, boot::binary_operator::shift_left);
$$ = new elna::boot::binary_expression(elna::boot::make_position(@2), $1, $3,
elna::boot::binary_operator::shift_left);
}
| expression ">>" expression
{
$$ = new boot::binary_expression(boot::make_position(@2), $1, $3, boot::binary_operator::shift_right);
$$ = new elna::boot::binary_expression(elna::boot::make_position(@2), $1, $3,
elna::boot::binary_operator::shift_right);
}
unary_expression:
"@" operand
{
$$ = new boot::unary_expression(boot::make_position(@1), $2, boot::unary_operator::reference);
$$ = new elna::boot::unary_expression(elna::boot::make_position(@1), $2,
elna::boot::unary_operator::reference);
}
| "~" operand
| "not" operand
{
$$ = new boot::unary_expression(boot::make_position(@1), $2, boot::unary_operator::negation);
$$ = new elna::boot::unary_expression(elna::boot::make_position(@1), $2,
elna::boot::unary_operator::negation);
}
| "-" operand
{
$$ = new boot::unary_expression(boot::make_position(@1), $2, boot::unary_operator::minus);
$$ = new elna::boot::unary_expression(elna::boot::make_position(@1), $2,
elna::boot::unary_operator::minus);
}
expressions:
expression "," expressions
@@ -391,73 +438,45 @@ 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); }
| expression { $$.emplace_back(std::move($1)); }
designator_expression:
simple_expression "[" expression "]"
{ $$ = new boot::array_access_expression(boot::make_position(@2), $1, $3); }
| simple_expression "." IDENTIFIER
{ $$ = new boot::field_access_expression(boot::make_position(@2), $1, $3); }
| simple_expression "^"
{ $$ = new boot::dereference_expression(boot::make_position(@1), $1); }
operand "[" expression "]"
{
$$ = new elna::boot::array_access_expression(elna::boot::make_position(@2), $1, $3);
}
| operand "." IDENTIFIER
{
$$ = new elna::boot::field_access_expression(elna::boot::make_position(@2), $1, $3);
}
| operand "^"
{
$$ = new elna::boot::dereference_expression(elna::boot::make_position(@1), $1);
}
| IDENTIFIER
{ $$ = new boot::variable_expression(boot::make_position(@1), $1); }
{
$$ = new elna::boot::variable_expression(elna::boot::make_position(@1), $1);
}
statement:
designator_expression ":=" expression
{ $$ = new boot::assign_statement(boot::make_position(@1), $1, $3); }
| "while" expression "do" optional_statements elsif_do_statements "end"
{
boot::conditional_statements *body = new boot::conditional_statements($2, std::move($4));
$$ = new boot::while_statement(boot::make_position(@1), body, std::move($5));
}
| "if" expression "then" optional_statements elsif_then_statements else_statements "end"
{
boot::conditional_statements *then = new boot::conditional_statements($2, std::move($4));
$$ = new boot::if_statement(boot::make_position(@1), then, std::move($5), $6);
}
assign_statement { $$ = $1; }
| while_statement { $$ = $1; }
| if_statement { $$ = $1; }
| return_statement { $$ = $1; }
| call_expression { $$ = $1; }
| "defer" optional_statements "end"
{ $$ = new boot::defer_statement(boot::make_position(@1), std::move($2)); }
| "case" expression "of" switch_cases else_statements "end"
{ $$ = new boot::case_statement(boot::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
| defer_statement { $$ = $1; }
statements:
statement ";" statements
{
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);
$$.insert($$.cbegin(), $1);
}
| 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
field_declaration required_fields
{
std::swap($$, $3);
std::swap($$, $2);
$$.emplace($$.cbegin(), $1);
}
| field_declaration { $$.emplace_back($1); }
@@ -467,128 +486,91 @@ optional_fields:
type_expression:
"[" INTEGER "]" type_expression
{
$$ = new boot::array_type_expression(boot::make_position(@1), $4, $2);
$$ = std::make_shared<elna::boot::array_type_expression>(elna::boot::make_position(@1), $4, $2);
}
| "^" type_expression
{
$$ = new boot::pointer_type_expression(boot::make_position(@1), $2);
$$ = std::make_shared<elna::boot::pointer_type_expression>(elna::boot::make_position(@1), $2);
}
| "record" optional_fields "end"
{
$$ = new boot::record_type_expression(boot::make_position(@1), std::move($2));
$$ = std::make_shared<elna::boot::record_type_expression>(elna::boot::make_position(@1), std::move($2));
}
| "union" required_fields "end"
{
$$ = new boot::union_type_expression(boot::make_position(@1), std::move($2));
$$ = std::make_shared<elna::boot::union_type_expression>(elna::boot::make_position(@1), std::move($2));
}
| "proc" "(" type_expressions ")" return_declaration
| "proc" procedure_heading
{
auto result = new boot::procedure_type_expression(boot::make_position(@1), std::move($5));
std::swap(result->parameters, $3);
$$ = result;
}
| "(" identifiers ")"
{
$$ = new boot::enumeration_type_expression(boot::make_position(@1), std::move($2));
$$ = $2;
}
| IDENTIFIER
{
$$ = new boot::named_type_expression(boot::make_position(@1), $1);
$$ = std::make_shared<elna::boot::primitive_type_expression>(elna::boot::make_position(@1), $1);
}
identifiers:
IDENTIFIER "," identifiers
variable_declaration: identifier_definitions ":" type_expression
{
std::swap($$, $3);
$$.emplace($$.cbegin(), std::move($1));
}
| IDENTIFIER { $$.emplace_back(std::move($1)); }
variable_declaration:
identifier_definitions ":" type_expression ";"
{
std::shared_ptr<boot::type_expression> shared_type{ $3 };
$$ = new boot::variable_declaration( boot::make_position(@2), std::move($1), shared_type);
}
| identifier_definitions ":" type_expression ":=" "extern" ";"
{
std::shared_ptr<boot::type_expression> shared_type{ $3 };
$$ = new boot::variable_declaration( boot::make_position(@2), std::move($1), shared_type,
std::monostate{});
}
| identifier_definitions ":" type_expression ":=" expression ";"
{
std::shared_ptr<boot::type_expression> shared_type{ $3 };
$$ = new boot::variable_declaration( boot::make_position(@2), std::move($1), shared_type, $5);
for (const std::pair<std::string, bool>& identifier : $1)
{
elna::boot::variable_declaration *declaration = new elna::boot::variable_declaration(
elna::boot::make_position(@2), identifier.first, $3, identifier.second);
$$.push_back(declaration);
}
}
variable_declarations:
/* no variable declarations */ {}
| variable_declaration variable_declarations
variable_declaration variable_declarations
{
std::swap($$, $2);
$$.insert(std::cbegin($$), $1);
std::swap($$, $1);
$$.reserve($$.size() + $2.size());
$$.insert(std::end($$), std::begin($2), std::end($2));
}
| variable_declaration { std::swap($$, $1); }
variable_part:
/* no variable declarations */ {}
| "var" variable_declarations { std::swap($$, $2); }
constant_declaration: identifier_definition ":=" expression ";"
constant_definition: identifier_definition "=" literal
{
$$ = new boot::constant_declaration(boot::make_position(@1), std::move($1), $3);
$$ = new elna::boot::constant_definition(elna::boot::make_position(@1), $1.first, $1.second, $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 boot::import_declaration(boot::make_position(@1), std::move($1)));
}
| import_declaration
{
$$.emplace_back(new boot::import_declaration(boot::make_position(@1), std::move($1)));
}
import_part:
/* no import declarations */ {}
| "import" import_declarations ";" { std::swap($$, $2); }
type_declaration: identifier_definition "=" type_expression ";"
{
$$ = new boot::type_declaration(boot::make_position(@1), std::move($1), $3);
}
type_declarations:
type_declaration type_declarations
constant_definitions:
constant_definition constant_definitions
{
std::swap($$, $2);
$$.insert($$.cbegin(), $1);
}
| /* no type definitions */ {}
| /* no constant definitions */ {}
constant_part:
{}
| "const" constant_definitions { std::swap($$, $2); }
type_definition: identifier_definition "=" type_expression
{
$$ = new elna::boot::type_definition(elna::boot::make_position(@1), $1.first, $1.second, $3);
}
type_definitions:
type_definition type_definitions
{
std::swap($$, $2);
$$.insert($$.cbegin(), $1);
}
| type_definition { $$.push_back($1); }
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); }
| "type" {}
| "type" type_definitions { std::swap($$, $2); }
formal_parameter: IDENTIFIER ":" type_expression
{
$$ = new elna::boot::variable_declaration(elna::boot::make_position(@2), $1, $3);
}
formal_parameters:
formal_parameter "," formal_parameters
{
std::swap($$, $3);
$$.emplace($$.cbegin(), std::move($1));
$$.emplace($$.cbegin(), $1);
}
| formal_parameter { $$.emplace_back(std::move($1)); }
formal_parameter_list:
"(" ")" {}
| "(" formal_parameters ")" { std::swap($$, $2); }
actual_parameter_list:
"(" ")" {}
| "(" expressions ")" { std::swap($$, $2); }
@@ -596,5 +578,5 @@ actual_parameter_list:
void yy::parser::error(const location_type& loc, const std::string& message)
{
driver.add_error<boot::syntax_error>(message, driver.input_file, loc);
driver.error(loc, message);
}

View File

@@ -17,7 +17,9 @@ along with GCC; see the file COPYING3. If not see
#include "elna/boot/result.h"
namespace elna::boot
namespace elna
{
namespace boot
{
error::error(const char *path, const struct position position)
: position(position), path(path)
@@ -33,35 +35,5 @@ namespace elna::boot
{
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::boot::identifier_definition>::operator()(
const elna::boot::identifier_definition& key) const
{
return std::hash<std::string>{}(key.name);
}

View File

@@ -1,4 +1,4 @@
/* Name analysis.
/* Semantic analysis visitors.
Copyright (C) 2025 Free Software Foundation, Inc.
GCC is free software; you can redistribute it and/or modify
@@ -17,628 +17,17 @@ along with GCC; see the file COPYING3. If not see
#include "elna/boot/semantic.h"
#include <algorithm>
#include <set>
namespace elna::boot
namespace elna
{
undeclared_error::undeclared_error(const std::string& identifier, const char *path, const struct position position)
: error(path, position), identifier(identifier)
namespace boot
{
name_analysis_visitor::name_analysis_visitor(std::shared_ptr<symbol_table<>> table)
: table(table)
{
}
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 (boot::variable_declaration *const variable : definition->body.value().variables())
{
variable->accept(this);
}
}
}
}

View File

@@ -17,411 +17,16 @@ along with GCC; see the file COPYING3. If not see
#include "elna/boot/symbol.h"
namespace elna::boot
namespace elna
{
namespace boot
{
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);
}
}
}

View File

@@ -34,11 +34,6 @@ gelna$(exeext): $(ELNA_OBJS) $(EXTRA_GCC_OBJS) libcommon-target.a $(LIBDEPS)
$(ELNA_OBJS) $(EXTRA_GCC_OBJS) libcommon-target.a \
$(EXTRA_GCC_LIBS) $(LIBS)
# Create a version of the gelna driver which calls the cross-compiler.
gelna-cross$(exeext): gelna$(exeext)
-rm -f gelna-cross$(exeext)
cp gelna$(exeext) gelna-cross$(exeext)
# The compiler proper
elna_OBJS = \
@@ -48,7 +43,6 @@ elna_OBJS = \
elna/elna-tree.o \
elna/elna-builtins.o \
elna/ast.o \
elna/dependency.o \
elna/driver.o \
elna/lexer.o \
elna/parser.o \
@@ -61,7 +55,7 @@ elna1$(exeext): attribs.o $(elna_OBJS) $(BACKEND) $(LIBDEPS)
+$(LLINKER) $(ALL_LINKERFLAGS) $(LDFLAGS) -o $@ \
attribs.o $(elna_OBJS) $(BACKEND) $(LIBS) $(BACKENDLIBS)
elna.all.cross: gelna-cross$(exeext)
elna.all.cross:
elna.start.encap: gelna$(exeext)
elna.rest.encap:
@@ -80,15 +74,9 @@ ELNA_TEXI_FILES = \
elna.install-common: installdirs
-rm -f $(DESTDIR)$(bindir)/$(ELNA_INSTALL_NAME)$(exeext)
$(INSTALL_PROGRAM) gelna$(exeext) $(DESTDIR)$(bindir)/$(ELNA_INSTALL_NAME)$(exeext)
-if test -f elna1$(exeext); then \
if test -f gelna-cross$(exeext); then \
:; \
else \
rm -f $(DESTDIR)$(bindir)/$(ELNA_TARGET_INSTALL_NAME)$(exeext); \
( cd $(DESTDIR)$(bindir) && \
$(LN) $(ELNA_INSTALL_NAME)$(exeext) $(ELNA_TARGET_INSTALL_NAME)$(exeext) ); \
fi; \
fi
rm -f $(DESTDIR)$(bindir)/$(ELNA_TARGET_INSTALL_NAME)$(exeext); \
( cd $(DESTDIR)$(bindir) && \
$(LN) $(ELNA_INSTALL_NAME)$(exeext) $(ELNA_TARGET_INSTALL_NAME)$(exeext) ); \
$(build_htmldir)/gelna/index.html: $(ELNA_TEXI_FILES)
$(mkinstalldirs) $(@D)
@@ -148,7 +136,7 @@ elna.stagefeedback: stagefeedback-start
-mv elna/*$(objext) stagefeedback/elna
ELNA_INCLUDES = -I $(srcdir)/elna/include -I elna/generated
ELNA_CXXFLAGS = -std=c++17
ELNA_CXXFLAGS = -std=c++14
elna/%.o: elna/boot/%.cc elna/generated/parser.hh elna/generated/location.hh
$(COMPILE) $(ELNA_CXXFLAGS) $(ELNA_INCLUDES) $<

View File

@@ -15,22 +15,22 @@ 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 <algorithm>
#include "elna/gcc/elna-builtins.h"
#include "elna/gcc/elna1.h"
#include "stor-layout.h"
#include "stringpool.h"
#include "elna/gcc/elna-tree.h"
namespace elna::gcc
namespace elna
{
namespace gcc
{
void init_ttree()
{
elna_int_type_node = long_integer_type_node;
elna_word_type_node = size_type_node;
elna_char_type_node = unsigned_char_type_node;
elna_pointer_type_node = ptr_type_node;
elna_byte_type_node = make_unsigned_type(8);
elna_float_type_node = double_type_node;
elna_bool_type_node = boolean_type_node;
@@ -50,225 +50,5 @@ namespace elna::gcc
TYPE_FIELDS(elna_string_type_node) = chainon(elna_string_ptr_field_node, elna_string_length_field_node);
layout_type(elna_string_type_node);
}
static
tree declare_builtin_type(std::shared_ptr<symbol_table> symbol_table, const char *name, tree type)
{
tree identifier = get_identifier(name);
tree type_declaration = build_decl(UNKNOWN_LOCATION, TYPE_DECL, identifier, type);
symbol_table->enter(name, type_declaration);
return type_declaration;
}
std::shared_ptr<symbol_table> builtin_symbol_table()
{
std::shared_ptr<elna::gcc::symbol_table> symbol_table = std::make_shared<elna::gcc::symbol_table>();
declare_builtin_type(symbol_table, "Int", elna_int_type_node);
declare_builtin_type(symbol_table, "Word", elna_word_type_node);
declare_builtin_type(symbol_table, "Char", elna_char_type_node);
declare_builtin_type(symbol_table, "Bool", elna_bool_type_node);
declare_builtin_type(symbol_table, "Pointer", elna_pointer_type_node);
declare_builtin_type(symbol_table, "Float", elna_float_type_node);
tree string_declaration = declare_builtin_type(symbol_table, "String", elna_string_type_node);
TYPE_NAME(elna_string_type_node) = DECL_NAME(string_declaration);
TYPE_STUB_DECL(elna_string_type_node) = string_declaration;
return symbol_table;
}
tree build_composite_type(const std::vector<boot::type_field>& fields, tree composite_type_node,
std::shared_ptr<symbol_table> symbols)
{
for (auto& field : fields)
{
tree rewritten_field = get_inner_alias(field.second, symbols);
tree field_declaration = build_field(UNKNOWN_LOCATION,
composite_type_node, field.first, rewritten_field);
TYPE_FIELDS(composite_type_node) = chainon(TYPE_FIELDS(composite_type_node), field_declaration);
}
layout_type(composite_type_node);
return composite_type_node;
}
tree build_procedure_type(const boot::procedure_type& procedure, std::shared_ptr<symbol_table> symbols)
{
std::vector<tree> parameter_types(procedure.parameters.size());
for (std::size_t i = 0; i < procedure.parameters.size(); ++i)
{
parameter_types[i] = get_inner_alias(procedure.parameters.at(i), symbols);
}
tree return_type = void_type_node;
if (!procedure.return_type.proper_type.empty())
{
return_type = get_inner_alias(procedure.return_type.proper_type, symbols);
}
return build_function_type_array(return_type, procedure.parameters.size(), parameter_types.data());
}
tree get_inner_alias(const boot::type& type, std::shared_ptr<symbol_table> symbols)
{
if (auto reference = type.get<boot::primitive_type>())
{
auto looked_up = symbols->lookup(reference->identifier);
gcc_assert(looked_up != NULL_TREE);
return TREE_TYPE(looked_up);
}
else if (auto reference = type.get<boot::record_type>())
{
tree composite_type_node = make_node(RECORD_TYPE);
build_composite_type(reference->fields, composite_type_node, symbols);
return composite_type_node;
}
else if (auto reference = type.get<boot::union_type>())
{
tree composite_type_node = make_node(UNION_TYPE);
build_composite_type(reference->fields, composite_type_node, symbols);
return composite_type_node;
}
else if (auto reference = type.get<boot::enumeration_type>())
{
return build_enumeration_type(reference->members);
}
else if (auto reference = type.get<boot::pointer_type>())
{
return build_global_pointer_type(get_inner_alias(reference->base, symbols));
}
else if (auto reference = type.get<boot::array_type>())
{
tree base = get_inner_alias(reference->base, symbols);
return build_static_array_type(base, reference->size);
}
else if (auto reference = type.get<boot::procedure_type>())
{
auto procedure = build_procedure_type(*reference, symbols);
return build_global_pointer_type(procedure);
}
else if (auto reference = type.get<boot::alias_type>())
{
return TREE_TYPE(handle_symbol(reference->name, reference, symbols));
}
return error_mark_node;
}
tree handle_symbol(const std::string& symbol_name, std::shared_ptr<boot::alias_type> reference,
std::shared_ptr<symbol_table> symbols)
{
tree looked_up = symbols->lookup(symbol_name);
if (looked_up == NULL_TREE)
{
tree type_tree = get_inner_alias(reference->reference, symbols);
looked_up = build_decl(UNKNOWN_LOCATION, TYPE_DECL,
get_identifier(symbol_name.c_str()), type_tree);
TREE_PUBLIC(looked_up) = 1;
if (is_unique_type(type_tree))
{
TYPE_NAME(type_tree) = DECL_NAME(looked_up);
TYPE_STUB_DECL(type_tree) = looked_up;
}
else
{
TYPE_NAME(type_tree) = looked_up;
}
symbols->enter(symbol_name, looked_up);
}
return looked_up;
}
void declare_procedure(const std::string& name, const boot::procedure_info& info,
std::shared_ptr<symbol_table> symbols)
{
tree declaration_type = gcc::build_procedure_type(info.symbol, symbols);
tree fndecl = build_fn_decl(name.c_str(), declaration_type);
symbols->enter(name, fndecl);
if (info.symbol.return_type.no_return)
{
TREE_THIS_VOLATILE(fndecl) = 1;
}
tree resdecl = build_decl(UNKNOWN_LOCATION, RESULT_DECL, NULL_TREE, TREE_TYPE(declaration_type));
DECL_CONTEXT(resdecl) = fndecl;
DECL_RESULT(fndecl) = resdecl;
tree argument_chain = NULL_TREE;
function_args_iterator parameter_type;
function_args_iter_init(&parameter_type, declaration_type);
std::vector<std::string>::const_iterator parameter_name = info.names.cbegin();
for (boot::type parameter : info.symbol.parameters)
{
tree declaration_tree = build_decl(UNKNOWN_LOCATION, PARM_DECL,
get_identifier(parameter_name->c_str()), function_args_iter_cond(&parameter_type));
DECL_CONTEXT(declaration_tree) = fndecl;
DECL_ARG_TYPE(declaration_tree) = function_args_iter_cond(&parameter_type);
argument_chain = chainon(argument_chain, declaration_tree);
function_args_iter_next(&parameter_type);
++parameter_name;
}
DECL_ARGUMENTS(fndecl) = argument_chain;
TREE_ADDRESSABLE(fndecl) = 1;
DECL_EXTERNAL(fndecl) = info.is_extern();
TREE_PUBLIC(fndecl) = info.exported;
}
tree declare_variable(const std::string& name, const boot::variable_info& info,
std::shared_ptr<symbol_table> symbols)
{
auto variable_type = get_inner_alias(info.symbol, symbols);
tree declaration_tree = build_decl(UNKNOWN_LOCATION, VAR_DECL, get_identifier(name.c_str()), variable_type);
TREE_ADDRESSABLE(declaration_tree) = 1;
DECL_EXTERNAL(declaration_tree) = info.is_extern;
TREE_PUBLIC(declaration_tree) = info.exported;
symbols->enter(name, declaration_tree);
return declaration_tree;
}
void declare_type(const std::string& name, const boot::type_info& info, std::shared_ptr<symbol_table> symbols)
{
// The top level symbol table has basic (builtin) types in it which are not aliases.
if (auto alias_type = info.symbol.get<boot::alias_type>())
{
tree type_declaration = handle_symbol(name, alias_type, symbols);
TREE_PUBLIC(type_declaration) = info.exported;
}
}
void rewrite_symbol_table(std::shared_ptr<boot::symbol_table> info_table, std::shared_ptr<symbol_table> symbols)
{
for (auto& [symbol_name, symbol_info] : *info_table)
{
if (auto type_info = symbol_info->is_type())
{
declare_type(symbol_name, *type_info, symbols);
}
else if (auto variable_info = symbol_info->is_variable())
{
declare_variable(symbol_name, *variable_info, symbols);
}
else if (auto procedure_info = symbol_info->is_procedure())
{
declare_procedure(symbol_name, *procedure_info, symbols);
}
}
}
}
}

View File

@@ -19,18 +19,10 @@ along with GCC; see the file COPYING3. If not see
#include "elna/gcc/elna-tree.h"
#include "elna/gcc/elna1.h"
namespace elna::gcc
namespace elna
{
namespace gcc
{
linemap_guard::linemap_guard(const char *filename)
{
linemap_add(line_table, LC_ENTER, 0, filename, 1);
}
linemap_guard::~linemap_guard()
{
linemap_add(line_table, LC_LEAVE, 0, NULL, 0);
}
location_t get_location(const boot::position *position)
{
linemap_line_start(line_table, position->line, 0);
@@ -54,42 +46,39 @@ namespace elna::gcc
{
gcc_assert(TYPE_P(type));
tree unqualified_type = get_qualified_type(type, TYPE_UNQUALIFIED);
tree_code code = TREE_CODE(type);
if (unqualified_type == elna_int_type_node)
if (type == elna_int_type_node)
{
return "Int";
}
else if (unqualified_type == elna_word_type_node)
else if (type == elna_word_type_node)
{
return "Word";
}
else if (unqualified_type == elna_bool_type_node)
else if (type == elna_bool_type_node)
{
return "Bool";
}
else if (unqualified_type == elna_pointer_type_node)
else if (type == elna_byte_type_node)
{
return "Pointer";
return "Byte";
}
else if (unqualified_type == elna_float_type_node)
else if (type == elna_float_type_node)
{
return "Float";
}
else if (unqualified_type == elna_char_type_node)
else if (type == elna_char_type_node)
{
return "Char";
}
else if (unqualified_type == elna_string_type_node)
else if (type == elna_string_type_node)
{
return "String";
}
else if (is_void_type(unqualified_type)) // For procedures without a return type.
else if (is_void_type(type)) // For procedures without a return type.
{
return "()";
}
else if (POINTER_TYPE_P(unqualified_type))
else if (is_pointer_type(type))
{
tree pointer_target_type = TREE_TYPE(type);
@@ -102,7 +91,7 @@ namespace elna::gcc
return std::string("^" + print_type(pointer_target_type));
}
}
else if (code == FUNCTION_TYPE)
else if (TREE_CODE(type) == FUNCTION_TYPE)
{
std::string output = "proc(";
tree parameter_type = TYPE_ARG_TYPES(type);
@@ -120,29 +109,23 @@ namespace elna::gcc
}
}
output += ')';
tree return_type = TREE_TYPE(type);
if (!is_void_type(return_type))
if (!is_void_type(TREE_TYPE(type)))
{
output += " -> " + print_type(return_type);
output += " -> " + print_type(TREE_TYPE(type));
}
return output;
}
else if (code == ARRAY_TYPE)
else if (is_array_type(type))
{
return "array";
}
else if (code == RECORD_TYPE)
else if (TREE_CODE(type) == RECORD_TYPE)
{
return print_aggregate_name(unqualified_type, "record");
return print_aggregate_name(type, "record");
}
else if (code == UNION_TYPE)
else if (TREE_CODE(type) == UNION_TYPE)
{
return print_aggregate_name(unqualified_type, "union");
}
else if (code == ENUMERAL_TYPE)
{
return print_aggregate_name(unqualified_type, "enumeration");
return print_aggregate_name(type, "union");
}
else
{
@@ -150,18 +133,5 @@ namespace elna::gcc
}
gcc_unreachable();
}
void report_errors(const std::deque<std::unique_ptr<boot::error>>& errors)
{
for (const auto& error : errors)
{
location_t gcc_location{ UNKNOWN_LOCATION };
if (error->position.line != 0 || error->position.column != 0)
{
gcc_location = elna::gcc::get_location(&error->position);
}
error_at(gcc_location, error->what().c_str());
}
}
}
}

File diff suppressed because it is too large Load Diff

View File

@@ -21,14 +21,23 @@ along with GCC; see the file COPYING3. If not see
#include "function.h"
#include "stor-layout.h"
#include "fold-const.h"
#include "diagnostic-core.h"
namespace elna::gcc
namespace elna
{
namespace gcc
{
bool is_pointer_type(tree type)
{
gcc_assert(TYPE_P(type));
return TREE_CODE(type) == POINTER_TYPE;
}
bool is_integral_type(tree type)
{
gcc_assert(TYPE_P(type));
return TREE_CODE(type) == INTEGER_TYPE && type != elna_char_type_node;
return TREE_CODE(type) == INTEGER_TYPE;
}
bool is_numeric_type(tree type)
@@ -36,10 +45,10 @@ namespace elna::gcc
return is_integral_type(type) || type == elna_float_type_node;
}
bool is_unique_type(tree type)
bool is_array_type(tree type)
{
gcc_assert(TYPE_P(type));
return RECORD_OR_UNION_TYPE_P(type) || TREE_CODE(type) == ENUMERAL_TYPE;
return TREE_CODE(type) == ARRAY_TYPE;
}
bool is_void_type(tree type)
@@ -47,10 +56,10 @@ namespace elna::gcc
return type == NULL_TREE || type == void_type_node;
}
bool is_castable_type(tree type)
bool is_aggregate_type(tree type)
{
gcc_assert(TYPE_P(type));
return INTEGRAL_TYPE_P(type) || POINTER_TYPE_P(type) || TREE_CODE(type) == REAL_TYPE;
return TREE_CODE(type) == RECORD_TYPE || TREE_CODE(type) == UNION_TYPE;
}
bool are_compatible_pointers(tree lhs_type, tree rhs)
@@ -58,8 +67,8 @@ namespace elna::gcc
gcc_assert(TYPE_P(lhs_type));
tree rhs_type = TREE_TYPE(rhs);
return (POINTER_TYPE_P(lhs_type) && rhs == elna_pointer_nil_node)
|| (POINTER_TYPE_P(lhs_type) && lhs_type == rhs_type);
return (is_pointer_type(lhs_type) && rhs == elna_pointer_nil_node)
|| (is_pointer_type(lhs_type) && lhs_type == rhs_type);
}
tree prepare_rvalue(tree rvalue)
@@ -128,8 +137,7 @@ namespace elna::gcc
return field_declaration;
}
tree do_pointer_arithmetic(boot::binary_operator binary_operator,
tree left, tree right, location_t operation_location)
tree do_pointer_arithmetic(boot::binary_operator binary_operator, tree left, tree right)
{
tree left_type = get_qualified_type(TREE_TYPE(left), TYPE_UNQUALIFIED);
tree right_type = get_qualified_type(TREE_TYPE(right), TYPE_UNQUALIFIED);
@@ -137,36 +145,31 @@ namespace elna::gcc
{
tree pointer{ NULL_TREE };
tree offset{ NULL_TREE };
tree pointer_type{ NULL_TREE };
if (POINTER_TYPE_P(left_type) && is_integral_type(right_type))
if (is_pointer_type(left_type) && is_integral_type(right_type))
{
pointer = left;
offset = right;
pointer_type = left_type;
}
else if (is_integral_type(left_type) && POINTER_TYPE_P(right_type))
else if (is_integral_type(left_type) && is_pointer_type(right_type))
{
pointer = right;
offset = left;
pointer_type = right_type;
}
else
{
return error_mark_node;
}
tree size_exp = pointer_type == elna_pointer_type_node
? size_one_node
: fold_convert(TREE_TYPE(offset), size_in_bytes(TREE_TYPE(TREE_TYPE(pointer))));
tree size_exp = fold_convert(TREE_TYPE(offset), size_in_bytes(TREE_TYPE(TREE_TYPE(pointer))));
offset = fold_build2(MULT_EXPR, TREE_TYPE(offset), offset, size_exp);
offset = fold_convert(sizetype, offset);
return fold_build2_loc(operation_location, POINTER_PLUS_EXPR, TREE_TYPE(pointer), pointer, offset);
return fold_build2(POINTER_PLUS_EXPR, TREE_TYPE(pointer), pointer, offset);
}
else if (binary_operator == boot::binary_operator::subtraction)
{
if (POINTER_TYPE_P(left_type) && is_integral_type(right_type))
if (is_pointer_type(left_type) && is_integral_type(right_type))
{
tree pointer_type = left_type;
tree offset_type = right_type;
@@ -176,11 +179,11 @@ namespace elna::gcc
convert_expression = fold_convert(sizetype, convert_expression);
convert_expression = fold_build1(NEGATE_EXPR, sizetype, convert_expression);
return fold_build2_loc(operation_location, POINTER_PLUS_EXPR, pointer_type, left, convert_expression);
return fold_build2(POINTER_PLUS_EXPR, pointer_type, left, convert_expression);
}
else if (POINTER_TYPE_P(left_type) && POINTER_TYPE_P(right_type) && left_type == right_type)
else if (is_pointer_type(left_type) && is_pointer_type(right_type) && left_type == right_type)
{
return fold_build2_loc(operation_location, POINTER_DIFF_EXPR, ssizetype, left, right);
return fold_build2(POINTER_DIFF_EXPR, ssizetype, left, right);
}
}
gcc_unreachable();
@@ -195,7 +198,7 @@ namespace elna::gcc
if (condition)
{
return fold_build2_loc(expression_location, operator_code, target_type, left, right);
return build2_loc(expression_location, operator_code, target_type, left, right);
}
else
{
@@ -206,110 +209,5 @@ namespace elna::gcc
return error_mark_node;
}
}
tree find_field_by_name(location_t expression_location, tree type, const std::string& field_name)
{
if (type == error_mark_node)
{
return type;
}
tree field_declaration = TYPE_FIELDS(type);
if (!RECORD_OR_UNION_TYPE_P(type))
{
error_at(expression_location, "Type '%s' does not have a field named '%s'",
print_type(type).c_str(), field_name.c_str());
return error_mark_node;
}
while (field_declaration != NULL_TREE)
{
tree declaration_name = DECL_NAME(field_declaration);
const char *identifier_pointer = IDENTIFIER_POINTER(declaration_name);
if (field_name == identifier_pointer)
{
break;
}
field_declaration = TREE_CHAIN(field_declaration);
}
if (field_declaration == NULL_TREE)
{
error_at(expression_location, "Aggregate type does not have a field '%s'", field_name.c_str());
return error_mark_node;
}
return field_declaration;
}
tree build_global_pointer_type(tree type)
{
return build_pointer_type_for_mode(type, VOIDmode, true);
}
tree build_static_array_type(tree type, const std::uint64_t size)
{
tree upper_bound = build_int_cst_type(integer_type_node, size);
tree range_type = build_range_type(integer_type_node, size_one_node, upper_bound);
return build_array_type(type, range_type);
}
tree build_enumeration_type(const std::vector<std::string>& members)
{
tree composite_type_node = make_node(ENUMERAL_TYPE);
const tree base_type = integer_type_node;
TREE_TYPE(composite_type_node) = base_type;
ENUM_IS_SCOPED(composite_type_node) = 1;
tree *pp = &TYPE_VALUES(composite_type_node);
std::size_t order{ 1 };
for (const std::string& member : members)
{
tree member_name = get_identifier(member.c_str());
tree member_declaration = build_decl(UNKNOWN_LOCATION, CONST_DECL, member_name, composite_type_node);
DECL_CONTEXT(member_declaration) = composite_type_node;
DECL_INITIAL(member_declaration) = build_int_cst_type(composite_type_node, order++);
TREE_CONSTANT(member_declaration) = 1;
TREE_READONLY(member_declaration) = 1;
TYPE_MAX_VALUE(composite_type_node) = DECL_INITIAL(member_declaration);
*pp = build_tree_list(member_name, member_declaration);
pp = &TREE_CHAIN(*pp);
}
TYPE_MIN_VALUE(composite_type_node) = DECL_INITIAL(TREE_VALUE(TYPE_VALUES(composite_type_node)));
TYPE_UNSIGNED(composite_type_node) = TYPE_UNSIGNED(base_type);
SET_TYPE_ALIGN(composite_type_node, TYPE_ALIGN(base_type));
TYPE_SIZE(composite_type_node) = NULL_TREE;
TYPE_PRECISION(composite_type_node) = TYPE_PRECISION(base_type);
layout_type(composite_type_node);
return composite_type_node;
}
tree build_label_decl(const char *name, location_t loc)
{
auto label_decl = build_decl(loc, LABEL_DECL, get_identifier(name), void_type_node);
DECL_CONTEXT(label_decl) = current_function_decl;
return label_decl;
}
tree extract_constant(tree expression)
{
int code = TREE_CODE(expression);
if (code == CONST_DECL)
{
return DECL_INITIAL(expression);
}
else if (TREE_CODE_CLASS(code) == tcc_constant)
{
return expression;
}
return NULL_TREE;
}
}
}

View File

@@ -29,11 +29,13 @@ along with GCC; see the file COPYING3. If not see
#include "langhooks-def.h"
#include <fstream>
#include "elna/boot/dependency.h"
#include "elna/boot/driver.h"
#include "elna/boot/semantic.h"
#include "elna/gcc/elna-tree.h"
#include "elna/gcc/elna-generic.h"
#include "elna/gcc/elna-diagnostic.h"
#include "elna/gcc/elna-builtins.h"
#include "parser.hh"
tree elna_global_trees[ELNA_TI_MAX];
hash_map<nofree_string_hash, tree> *elna_global_decls = nullptr;
@@ -62,64 +64,45 @@ static bool elna_langhook_init(void)
return true;
}
using dependency_state = elna::boot::dependency_state<std::shared_ptr<elna::gcc::symbol_table>>;
static elna::boot::dependency elna_parse_file(dependency_state& state, const char *filename)
static void elna_parse_file(const char *filename)
{
std::ifstream entry_point{ filename, std::ios::in };
std::ifstream file{ filename, std::ios::in };
if (!entry_point)
if (!file)
{
fatal_error(UNKNOWN_LOCATION, "Cannot open filename %s: %m", filename);
fatal_error(UNKNOWN_LOCATION, "cannot open filename %s: %m", filename);
}
elna::gcc::linemap_guard{ filename };
elna::boot::dependency outcome = elna::boot::read_source(entry_point, filename);
if (outcome.has_errors())
elna::boot::driver driver{ filename };
elna::boot::lexer lexer(file);
yy::parser parser(lexer, driver);
linemap_add(line_table, LC_ENTER, 0, filename, 1);
if (parser())
{
elna::gcc::report_errors(outcome.errors());
return outcome;
}
elna::boot::symbol_bag outcome_bag = elna::boot::symbol_bag{ std::move(outcome.unresolved), state.globals };
for (const auto& sub_tree : outcome.tree->imports)
{
std::filesystem::path sub_path = "source" / elna::boot::build_path(sub_tree->segments);
std::unordered_map<std::filesystem::path, elna::boot::symbol_bag>::const_iterator cached_import =
state.cache.find(sub_path);
if (cached_import == state.cache.end())
for (const auto& error : driver.errors())
{
elna_parse_file(state, sub_path.c_str());
cached_import = state.cache.find(sub_path);
auto gcc_location = elna::gcc::get_location(&error->position);
error_at(gcc_location, error->what().c_str());
}
outcome_bag.add_import(cached_import->second);
}
outcome.errors() = analyze_semantics(filename, outcome.tree, outcome_bag);
if (outcome.has_errors())
else
{
elna::gcc::report_errors(outcome.errors());
return outcome;
}
state.cache.insert({ filename, outcome_bag });
elna::gcc::rewrite_symbol_table(outcome_bag.leave(), state.custom);
elna::boot::name_analysis_visitor name_analysis_visitor{ std::make_shared<elna::boot::symbol_table<>>() };
elna::gcc::generic_visitor generic_visitor{ std::make_shared<elna::gcc::symbol_table>() };
return outcome;
name_analysis_visitor.visit(driver.tree.get());
generic_visitor.visit(driver.tree.get());
}
linemap_add(line_table, LC_LEAVE, 0, NULL, 0);
}
static void elna_langhook_parse_file(void)
{
dependency_state state{ elna::gcc::builtin_symbol_table() };
for (unsigned int i = 0; i < num_in_fnames; i++)
{
elna::boot::dependency outcome = elna_parse_file(state, in_fnames[i]);
linemap_add(line_table, LC_ENTER, 0, in_fnames[i], 1);
elna::gcc::generic_visitor generic_visitor{ state.custom, state.cache.find(in_fnames[i])->second };
outcome.tree->accept(&generic_visitor);
linemap_add(line_table, LC_LEAVE, 0, NULL, 0);
elna_parse_file(in_fnames[i]);
}
}

View File

@@ -21,11 +21,11 @@ along with GCC; see the file COPYING3. If not see
#include <memory>
#include <string>
#include <vector>
#include <optional>
#include "elna/boot/symbol.h"
#include "elna/boot/result.h"
namespace elna::boot
namespace elna
{
namespace boot
{
enum class binary_operator
{
@@ -55,37 +55,32 @@ namespace elna::boot
};
class variable_declaration;
class constant_declaration;
class procedure_declaration;
class type_declaration;
class constant_definition;
class procedure_definition;
class type_definition;
class procedure_call;
class cast_expression;
class assign_statement;
class if_statement;
class import_declaration;
class while_statement;
class return_statement;
class case_statement;
class traits_expression;
class unit;
class block;
class program;
class binary_expression;
class unary_expression;
class named_type_expression;
class primitive_type_expression;
class array_type_expression;
class pointer_type_expression;
class record_type_expression;
class union_type_expression;
class procedure_type_expression;
class enumeration_type_expression;
class variable_expression;
class array_access_expression;
class field_access_expression;
class dereference_expression;
class designator_expression;
class literal_expression;
template<typename T>
class literal;
class number_literal;
class defer_statement;
/**
@@ -94,88 +89,78 @@ namespace elna::boot
struct parser_visitor
{
virtual void visit(variable_declaration *) = 0;
virtual void visit(constant_declaration *) = 0;
virtual void visit(procedure_declaration *) = 0;
virtual void visit(type_declaration *) = 0;
virtual void visit(constant_definition *) = 0;
virtual void visit(procedure_definition *) = 0;
virtual void visit(type_definition *) = 0;
virtual void visit(procedure_call *) = 0;
virtual void visit(cast_expression *) = 0;
virtual void visit(traits_expression *) = 0;
virtual void visit(assign_statement *) = 0;
virtual void visit(if_statement *) = 0;
virtual void visit(import_declaration *) = 0;
virtual void visit(while_statement *) = 0;
virtual void visit(return_statement *) = 0;
virtual void visit(defer_statement *) = 0;
virtual void visit(case_statement *) = 0;
virtual void visit(unit *) = 0;
virtual void visit(block *) = 0;
virtual void visit(program *) = 0;
virtual void visit(binary_expression *) = 0;
virtual void visit(unary_expression *) = 0;
virtual void visit(named_type_expression *) = 0;
virtual void visit(primitive_type_expression *) = 0;
virtual void visit(array_type_expression *) = 0;
virtual void visit(pointer_type_expression *) = 0;
virtual void visit(record_type_expression *) = 0;
virtual void visit(union_type_expression *) = 0;
virtual void visit(procedure_type_expression *) = 0;
virtual void visit(enumeration_type_expression *) = 0;
virtual void visit(variable_expression *) = 0;
virtual void visit(array_access_expression *) = 0;
virtual void visit(field_access_expression *) = 0;
virtual void visit(dereference_expression *) = 0;
virtual void visit(literal<std::int32_t> *) = 0;
virtual void visit(literal<std::uint32_t> *) = 0;
virtual void visit(literal<double> *) = 0;
virtual void visit(literal<bool> *) = 0;
virtual void visit(literal<unsigned char> *) = 0;
virtual void visit(literal<std::nullptr_t> *) = 0;
virtual void visit(literal<std::string> *) = 0;
virtual void visit(number_literal<std::int32_t> *) = 0;
virtual void visit(number_literal<std::uint32_t> *) = 0;
virtual void visit(number_literal<double> *) = 0;
virtual void visit(number_literal<bool> *) = 0;
virtual void visit(number_literal<unsigned char> *) = 0;
virtual void visit(number_literal<std::nullptr_t> *) = 0;
virtual void visit(number_literal<std::string> *) = 0;
};
/**
* Abstract visitor that doesn't visit any nodes by default.
* A visitor which visits all nodes but does nothing.
*/
class empty_visitor : public parser_visitor
struct empty_visitor : parser_visitor
{
[[noreturn]] void not_implemented();
public:
[[noreturn]] virtual void visit(named_type_expression *) override;
[[noreturn]] virtual void visit(array_type_expression *) override;
[[noreturn]] virtual void visit(pointer_type_expression *) override;
[[noreturn]] virtual void visit(program *) override;
[[noreturn]] virtual void visit(type_declaration *) override;
[[noreturn]] virtual void visit(record_type_expression *) override;
[[noreturn]] virtual void visit(union_type_expression *) override;
[[noreturn]] virtual void visit(procedure_type_expression *) override;
[[noreturn]] virtual void visit(enumeration_type_expression *) override;
[[noreturn]] virtual void visit(variable_declaration *) override;
[[noreturn]] virtual void visit(constant_declaration *) override;
[[noreturn]] virtual void visit(procedure_declaration *) override;
[[noreturn]] virtual void visit(assign_statement *) override;
[[noreturn]] virtual void visit(if_statement *) override;
[[noreturn]] virtual void visit(import_declaration *) override;
[[noreturn]] virtual void visit(while_statement *) override;
[[noreturn]] virtual void visit(return_statement *) override;
[[noreturn]] virtual void visit(defer_statement *) override;
[[noreturn]] virtual void visit(case_statement *) override;
[[noreturn]] virtual void visit(procedure_call *) override;
[[noreturn]] virtual void visit(unit *) override;
[[noreturn]] virtual void visit(cast_expression *) override;
[[noreturn]] virtual void visit(traits_expression *) override;
[[noreturn]] virtual void visit(binary_expression *) override;
[[noreturn]] virtual void visit(unary_expression *) override;
[[noreturn]] virtual void visit(variable_expression *) override;
[[noreturn]] virtual void visit(array_access_expression *) override;
[[noreturn]] virtual void visit(field_access_expression *) override;
[[noreturn]] virtual void visit(dereference_expression *) override;
[[noreturn]] virtual void visit(literal<std::int32_t> *) override;
[[noreturn]] virtual void visit(literal<std::uint32_t> *) override;
[[noreturn]] virtual void visit(literal<double> *) override;
[[noreturn]] virtual void visit(literal<bool> *) override;
[[noreturn]] virtual void visit(literal<unsigned char> *) override;
[[noreturn]] virtual void visit(literal<std::nullptr_t> *) override;
[[noreturn]] virtual void visit(literal<std::string> *) override;
virtual void visit(variable_declaration *) override;
virtual void visit(constant_definition *) override;
virtual void visit(procedure_definition *) override;
virtual void visit(type_definition *) override;
virtual void visit(traits_expression *) override;
virtual void visit(procedure_call *) override;
virtual void visit(cast_expression *) override;
virtual void visit(assign_statement *) override;
virtual void visit(if_statement *) override;
virtual void visit(while_statement *) override;
virtual void visit(return_statement *) override;
virtual void visit(defer_statement *) override;
virtual void visit(block *) override;
virtual void visit(program *) override;
virtual void visit(binary_expression *) override;
virtual void visit(unary_expression *) override;
virtual void visit(primitive_type_expression *) override;
virtual void visit(array_type_expression *) override;
virtual void visit(pointer_type_expression *) override;
virtual void visit(record_type_expression *) override;
virtual void visit(union_type_expression *) override;
virtual void visit(procedure_type_expression *) override;
virtual void visit(variable_expression *) override;
virtual void visit(array_access_expression *) override;
virtual void visit(field_access_expression *) override;
virtual void visit(dereference_expression *) override;
virtual void visit(number_literal<std::int32_t> *) override;
virtual void visit(number_literal<std::uint32_t> *) override;
virtual void visit(number_literal<double> *) override;
virtual void visit(number_literal<bool> *) override;
virtual void visit(number_literal<unsigned char> *) override;
virtual void visit(number_literal<std::nullptr_t> *) override;
virtual void visit(number_literal<std::string> *) override;
};
/**
@@ -192,8 +177,8 @@ namespace elna::boot
explicit node(const position position);
public:
virtual void accept(parser_visitor *visitor) = 0;
virtual ~node() = 0;
virtual ~node() = default;
virtual void accept(parser_visitor *) = 0;
/**
* \return Node position in the source code.
@@ -203,264 +188,207 @@ namespace elna::boot
class statement : public virtual node
{
protected:
statement();
};
class expression : public virtual node
{
public:
virtual cast_expression *is_cast();
virtual traits_expression *is_traits();
virtual binary_expression *is_binary();
virtual unary_expression *is_unary();
virtual designator_expression *is_designator();
virtual procedure_call *is_call_expression();
virtual literal_expression *is_literal();
protected:
expression();
};
/**
* Symbol definition.
*/
class declaration : public node
class definition : public node
{
protected:
declaration(const struct position position, identifier_definition identifier);
definition(const struct position position, const std::string& identifier, const bool exported);
public:
const identifier_definition identifier;
const std::string identifier;
const bool exported;
};
/**
* Some type expression.
*/
class type_expression : public node
class type_expression : public node, public std::enable_shared_from_this<type_expression>
{
public:
virtual named_type_expression *is_named();
virtual array_type_expression *is_array();
virtual pointer_type_expression *is_pointer();
virtual record_type_expression *is_record();
virtual union_type_expression *is_union();
virtual procedure_type_expression *is_procedure();
virtual enumeration_type_expression *is_enumeration();
protected:
type_expression(const struct position position);
};
/**
* Expression refering to a type by its name.
* Expression defining a basic type.
*/
class named_type_expression : public type_expression
class primitive_type_expression : public type_expression
{
public:
const std::string name;
const std::string m_name;
named_type_expression(const struct position position, const std::string& name);
void accept(parser_visitor *visitor) override;
named_type_expression *is_named() override;
public:
/**
* \param position Source code position.
* \param name Type name.
*/
primitive_type_expression(const struct position position, const std::string& name);
virtual void accept(parser_visitor *visitor) override;
const std::string& base_name();
};
class array_type_expression : public type_expression
{
type_expression *m_base;
std::shared_ptr<type_expression> m_base;
public:
const std::uint32_t size;
array_type_expression(const struct position position,
type_expression *base, const std::uint32_t size);
~array_type_expression();
void accept(parser_visitor *visitor) override;
array_type_expression *is_array() override;
std::shared_ptr<type_expression> base, const std::uint32_t size);
virtual void accept(parser_visitor *visitor) override;
type_expression& base();
};
class pointer_type_expression : public type_expression
{
type_expression *m_base;
std::shared_ptr<type_expression> m_base;
public:
pointer_type_expression(const struct position position, type_expression *base);
~pointer_type_expression();
void accept(parser_visitor *visitor) override;
pointer_type_expression *is_pointer() override;
pointer_type_expression(const struct position position, std::shared_ptr<type_expression> base);
virtual void accept(parser_visitor *visitor) override;
type_expression& base();
};
using field_declaration = std::pair<std::string, type_expression *>;
using field_t = std::pair<std::string, std::shared_ptr<type_expression>>;
using fields_t = std::vector<field_t>;
class record_type_expression : public type_expression
{
public:
const std::vector<field_declaration> fields;
fields_t fields;
record_type_expression(const struct position position, std::vector<field_declaration>&& fields);
~record_type_expression();
record_type_expression(const struct position position, fields_t&& fields);
void accept(parser_visitor *visitor) override;
record_type_expression *is_record() override;
virtual void accept(parser_visitor *visitor) override;
};
class union_type_expression : public type_expression
{
public:
std::vector<field_declaration> fields;
fields_t fields;
union_type_expression(const struct position position, std::vector<field_declaration>&& fields);
~union_type_expression();
union_type_expression(const struct position position, fields_t&& fields);
void accept(parser_visitor *visitor) override;
union_type_expression *is_union() override;
};
/**
* Enumeration type.
*/
class enumeration_type_expression : public type_expression
{
public:
const std::vector<std::string> members;
enumeration_type_expression(const struct position, std::vector<std::string>&& members);
void accept(parser_visitor *visitor) override;
enumeration_type_expression *is_enumeration() override;
virtual void accept(parser_visitor *visitor) override;
};
/**
* Variable declaration.
*/
class variable_declaration : public node
class variable_declaration : public definition
{
std::shared_ptr<type_expression> m_variable_type;
std::shared_ptr<type_expression> m_type;
public:
variable_declaration(const struct position position,
std::vector<identifier_definition>&& identifier, std::shared_ptr<type_expression> variable_type,
expression *body = nullptr);
variable_declaration(const struct position position,
std::vector<identifier_definition>&& identifier, std::shared_ptr<type_expression> variable_type,
std::monostate);
variable_declaration(const struct position position, const std::string& identifier,
std::shared_ptr<type_expression> type, const bool exported = false);
virtual void accept(parser_visitor *visitor) override;
void accept(parser_visitor *visitor) override;
bool has_initializer() const;
const std::vector<identifier_definition> identifiers;
type_expression& variable_type();
expression *const body{ nullptr };
const bool is_extern{ false };
};
/**
* Literal expression.
*/
class literal_expression : public expression
class literal : public expression
{
public:
literal_expression *is_literal() override;
protected:
literal_expression();
literal();
};
/**
* Constant definition.
*/
class constant_declaration : public declaration
class constant_definition : public definition
{
expression *m_body;
literal *m_body;
public:
constant_declaration(const struct position position, identifier_definition identifier,
expression *body);
void accept(parser_visitor *visitor) override;
/**
* \param position Source code position.
* \param identifier Constant name.
* \param body Constant value.
*/
constant_definition(const struct position position, const std::string& identifier,
const bool exported, literal *body);
virtual void accept(parser_visitor *visitor) override;
expression& body();
literal& body();
virtual ~constant_declaration() override;
virtual ~constant_definition() override;
};
/**
* Tags a procedure type as never returning.
*/
struct no_return_t
{
};
constexpr no_return_t no_return{};
/**
* Procedure type.
*/
class procedure_type_expression : public type_expression
{
public:
using return_t = return_declaration<type_expression *>;
const std::shared_ptr<type_expression> return_type;
const bool no_return;
std::vector<variable_declaration *> parameters;
const return_t return_type;
std::vector<type_expression *> parameters;
procedure_type_expression(const struct position position,
std::shared_ptr<type_expression> return_type = nullptr);
procedure_type_expression(const struct position position, no_return_t);
procedure_type_expression(const struct position position, return_t return_type = return_t());
~procedure_type_expression();
void accept(parser_visitor *visitor) override;
procedure_type_expression *is_procedure() override;
};
struct block
{
block(std::vector<constant_declaration*>&& constants, std::vector<variable_declaration *>&& variables,
std::vector<statement *>&& body);
block(const block&) = delete;
block(block&& that);
block& operator=(const block&) = delete;
block& operator=(block&& that);
const std::vector<variable_declaration *>& variables();
const std::vector<constant_declaration *>& constants();
const std::vector<statement *>& body();
virtual ~block();
private:
std::vector<variable_declaration *> m_variables;
std::vector<constant_declaration *> m_constants;
std::vector<statement *> m_body;
virtual void accept(parser_visitor *visitor) override;
virtual ~procedure_type_expression() override;
};
/**
* Procedure definition.
*/
class procedure_declaration : public declaration
class procedure_definition : public definition
{
procedure_type_expression *m_heading;
std::shared_ptr<procedure_type_expression> m_heading;
public:
std::optional<block> body;
std::vector<std::string> parameter_names;
block *const body;
procedure_declaration(const struct position position, identifier_definition identifier,
procedure_type_expression *heading, block&& body);
procedure_declaration(const struct position position, identifier_definition identifier,
procedure_type_expression *heading);
void accept(parser_visitor *visitor) override;
procedure_definition(const struct position position, const std::string& identifier,
const bool exported, std::shared_ptr<procedure_type_expression> heading, block *body = nullptr);
virtual void accept(parser_visitor *visitor) override;
procedure_type_expression& heading();
virtual ~procedure_declaration() override;
virtual ~procedure_definition() override;
};
/**
* Type definition.
*/
class type_declaration : public declaration
class type_definition : public definition
{
type_expression *m_body;
std::shared_ptr<type_expression> m_body;
public:
type_declaration(const struct position position, identifier_definition identifier,
type_expression *expression);
~type_declaration();
void accept(parser_visitor *visitor) override;
type_definition(const struct position position, const std::string& identifier,
const bool exported, std::shared_ptr<type_expression> expression);
virtual void accept(parser_visitor *visitor) override;
type_expression& body();
};
@@ -470,15 +398,12 @@ namespace elna::boot
*/
class cast_expression : public expression
{
type_expression *m_target;
std::shared_ptr<type_expression> m_target;
expression *m_value;
public:
type expression_type;
cast_expression(const struct position position, type_expression *target, expression *value);
void accept(parser_visitor *visitor) override;
cast_expression *is_cast() override;
cast_expression(const struct position position, std::shared_ptr<type_expression> target, expression *value);
virtual void accept(parser_visitor *visitor) override;
type_expression& target();
expression& value();
@@ -488,16 +413,16 @@ namespace elna::boot
class traits_expression : public expression
{
std::shared_ptr<type_expression> m_type;
public:
std::vector<type_expression *> parameters;
const std::string name;
std::vector<type> types;
traits_expression(const struct position position, const std::string& name);
~traits_expression();
traits_expression(const struct position position, const std::string& name,
std::shared_ptr<type_expression> type);
virtual void accept(parser_visitor *visitor) override;
void accept(parser_visitor *visitor) override;
traits_expression *is_traits() override;
type_expression& type();
};
/**
@@ -508,9 +433,9 @@ namespace elna::boot
expression *m_prerequisite;
public:
const std::vector<statement *> statements;
std::vector<statement *> statements;
conditional_statements(expression *prerequisite, std::vector<statement *>&& statements);
conditional_statements(expression *prerequisite);
expression& prerequisite();
@@ -519,37 +444,17 @@ namespace elna::boot
class return_statement : public statement
{
expression *m_return_expression{ nullptr };
public:
expression *m_return_expression;
return_statement(const struct position position, expression *return_expression);
void accept(parser_visitor *visitor) override;
virtual void accept(parser_visitor *visitor) override;
expression& return_expression();
expression *return_expression();
virtual ~return_statement() override;
};
struct switch_case
{
std::vector<expression *> labels;
std::vector<statement *> statements;
};
class case_statement : public statement
{
expression *m_condition;
public:
const std::vector<switch_case> cases;
const std::vector<statement *> *alternative;
case_statement(const struct position position, expression *condition,
std::vector<switch_case>&& cases, std::vector<statement *> *alternative = nullptr);
void accept(parser_visitor *visitor) override;
expression& condition();
};
class designator_expression : public expression
{
public:
@@ -558,21 +463,17 @@ namespace elna::boot
virtual field_access_expression *is_field_access();
virtual dereference_expression *is_dereference();
designator_expression *is_designator() override;
void accept(parser_visitor *visitor);
~designator_expression() = 0;
protected:
designator_expression();
};
class variable_expression : public designator_expression, public literal_expression
class variable_expression : public designator_expression
{
public:
const std::string name;
variable_expression(const struct position position, const std::string& name);
void accept(parser_visitor *visitor) override;
virtual void accept(parser_visitor *visitor) override;
variable_expression *is_variable() override;
};
@@ -584,7 +485,7 @@ namespace elna::boot
public:
array_access_expression(const struct position position, expression *base, expression *index);
void accept(parser_visitor *visitor) override;
virtual void accept(parser_visitor *visitor) override;
expression& base();
expression& index();
@@ -602,7 +503,7 @@ namespace elna::boot
public:
field_access_expression(const struct position position, expression *base,
const std::string& field);
void accept(parser_visitor *visitor) override;
virtual void accept(parser_visitor *visitor) override;
expression& base();
std::string& field();
@@ -618,7 +519,7 @@ namespace elna::boot
public:
dereference_expression(const struct position position, expression *base);
void accept(parser_visitor *visitor) override;
virtual void accept(parser_visitor *visitor) override;
expression& base();
@@ -638,8 +539,7 @@ namespace elna::boot
std::vector<expression *> arguments;
procedure_call(const struct position position, designator_expression *callable);
void accept(parser_visitor *visitor) override;
virtual procedure_call *is_call_expression() override;
virtual void accept(parser_visitor *visitor) override;
designator_expression& callable();
@@ -659,7 +559,7 @@ namespace elna::boot
*/
assign_statement(const struct position position, designator_expression *lvalue,
expression *rvalue);
void accept(parser_visitor *visitor) override;
virtual void accept(parser_visitor *visitor) override;
designator_expression& lvalue();
expression& rvalue();
@@ -673,33 +573,21 @@ namespace elna::boot
class if_statement : public statement
{
conditional_statements *m_body;
std::vector<statement *> *m_alternative;
public:
const std::vector<conditional_statements *> branches;
const std::vector<statement *> *alternative;
std::vector<conditional_statements *> branches;
if_statement(const struct position position, conditional_statements *body,
std::vector<conditional_statements *>&& branches,
std::vector<statement *> *alternative = nullptr);
void accept(parser_visitor *visitor) override;
virtual void accept(parser_visitor *visitor) override;
conditional_statements& body();
std::vector<statement *> *alternative();
virtual ~if_statement() override;
};
/**
* Import statement.
*/
class import_declaration : public node
{
public:
const std::vector<std::string> segments;
import_declaration(const struct position position, std::vector<std::string>&& segments);
void accept(parser_visitor *visitor) override;
};
/**
* While-statement.
*/
@@ -708,55 +596,52 @@ namespace elna::boot
conditional_statements *m_body;
public:
const std::vector<conditional_statements *> branches;
while_statement(const struct position position, conditional_statements *body,
std::vector<conditional_statements *>&& branches);
void accept(parser_visitor *visitor) override;
std::vector<conditional_statements *> branches;
while_statement(const struct position position, conditional_statements *body);
virtual void accept(parser_visitor *visitor) override;
conditional_statements& body();
virtual ~while_statement() override;
};
class unit : public node
class block : public node
{
public:
std::vector<import_declaration *> imports;
std::vector<constant_declaration *> constants;
std::vector<type_declaration *> types;
std::vector<variable_declaration *> variables;
std::vector<procedure_declaration *> procedures;
unit(const struct position position);
virtual void accept(parser_visitor *visitor) override;
virtual ~unit() override;
};
class program : public unit
{
public:
std::vector<constant_definition *> constants;
std::vector<statement *> body;
block(const struct position position);
virtual void accept(parser_visitor *visitor) override;
virtual ~block() override;
};
class program : public block
{
public:
std::vector<type_definition *> types;
std::vector<procedure_definition *> procedures;
program(const struct position position);
void accept(parser_visitor *visitor) override;
virtual void accept(parser_visitor *visitor) override;
virtual ~program() override;
};
template<typename T>
class literal : public literal_expression
class number_literal : public literal
{
public:
T value;
literal(const struct position position, const T& value)
number_literal(const struct position position, const T& value)
: node(position), value(value)
{
}
void accept(parser_visitor *visitor) override
virtual void accept(parser_visitor *visitor) override
{
visitor->visit(this);
}
@@ -765,10 +650,10 @@ namespace elna::boot
class defer_statement : public statement
{
public:
const std::vector<statement *> statements;
std::vector<statement *> statements;
defer_statement(const struct position position, std::vector<statement *>&& statements);
void accept(parser_visitor *visitor) override;
defer_statement(const struct position position);
virtual void accept(parser_visitor *visitor) override;
virtual ~defer_statement() override;
};
@@ -783,9 +668,7 @@ namespace elna::boot
binary_expression(const struct position position, expression *lhs,
expression *rhs, const binary_operator operation);
void accept(parser_visitor *visitor) override;
binary_expression *is_binary() override;
virtual void accept(parser_visitor *visitor) override;
expression& lhs();
expression& rhs();
binary_operator operation() const;
@@ -802,9 +685,7 @@ namespace elna::boot
unary_expression(const struct position position, expression *operand,
const unary_operator operation);
void accept(parser_visitor *visitor) override;
unary_expression *is_unary() override;
virtual void accept(parser_visitor *visitor) override;
expression& operand();
unary_operator operation() const;
@@ -813,3 +694,4 @@ namespace elna::boot
const char *print_binary_operator(const binary_operator operation);
}
}

View File

@@ -1,55 +0,0 @@
/* 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/>. */
#pragma once
#include <filesystem>
#include <fstream>
#include "elna/boot/result.h"
#include "elna/boot/ast.h"
#include "elna/boot/symbol.h"
namespace elna::boot
{
class dependency : public error_container
{
error_list m_errors;
public:
std::unique_ptr<unit> tree;
forward_table unresolved;
explicit dependency(const char *path);
};
dependency read_source(std::istream& entry_point, const char *entry_path);
std::filesystem::path build_path(const std::vector<std::string>& segments);
error_list analyze_semantics(const char *path, std::unique_ptr<unit>& tree, symbol_bag bag);
template<typename T>
struct dependency_state
{
const std::shared_ptr<symbol_table> globals;
T custom;
std::unordered_map<std::filesystem::path, symbol_bag> cache;
explicit dependency_state(T custom)
: globals(elna::boot::builtin_symbol_table()), custom(custom)
{
}
};
}

View File

@@ -17,11 +17,14 @@ along with GCC; see the file COPYING3. If not see
#pragma once
#include <list>
#include <optional>
#include "elna/boot/ast.h"
#include "location.hh"
namespace elna::boot
namespace elna
{
namespace boot
{
position make_position(const yy::location& location);
@@ -36,16 +39,22 @@ namespace elna::boot
virtual std::string what() const override;
};
class driver : public error_container
class driver
{
std::list<std::unique_ptr<struct error>> m_errors;
const char *input_file;
public:
std::unique_ptr<unit> tree;
std::unique_ptr<program> tree;
driver(const char *input_file);
void error(const yy::location& loc, const std::string& message);
const std::list<std::unique_ptr<struct error>>& errors() const noexcept;
};
constexpr char escape_invalid_char = '\xff';
char escape_char(char escape);
std::optional<std::string> escape_string(const char *escape);
}
}

View File

@@ -19,11 +19,10 @@ along with GCC; see the file COPYING3. If not see
#include <cstddef>
#include <string>
#include <deque>
#include <memory>
#include <variant>
namespace elna::boot
namespace elna
{
namespace boot
{
/**
* Position in the source text.
@@ -43,82 +42,28 @@ namespace elna::boot
class error
{
protected:
/**
* Constructs an error.
*
* \param path Source file name.
* \param position Error position in the source text.
*/
error(const char *path, const struct position position);
public:
const struct position position;
const char *path;
virtual ~error() = default;
virtual ~error() noexcept = default;
/// Error text.
virtual std::string what() const = 0;
/// Error line in the source text.
std::size_t line() const;
std::size_t line() const noexcept;
/// Error column in the source text.
std::size_t column() const;
};
using error_list = typename std::deque<std::unique_ptr<error>>;
class error_container
{
protected:
error_list m_errors;
error_container(const char *input_file);
public:
const char *input_file;
error_list& errors();
template<typename T, typename... Args>
void add_error(Args... arguments)
{
auto new_error = std::make_unique<T>(arguments...);
m_errors.emplace_back(std::move(new_error));
}
bool has_errors() const;
};
/**
* Tags a procedure type as never returning.
*/
template<typename T>
struct return_declaration
{
return_declaration() = default;
explicit return_declaration(T type)
: proper_type(type)
{
}
explicit return_declaration(std::monostate)
: no_return(true)
{
}
T proper_type{};
bool no_return{ false };
};
struct identifier_definition
{
std::string name;
bool exported;
bool operator==(const identifier_definition& that) const;
bool operator==(const std::string& that) const;
std::size_t column() const noexcept;
};
}
template<>
struct std::hash<elna::boot::identifier_definition>
{
std::size_t operator()(const elna::boot::identifier_definition& key) const noexcept;
};
}

View File

@@ -1,4 +1,4 @@
/* Name analysis.
/* Semantic analysis visitors.
Copyright (C) 2025 Free Software Foundation, Inc.
GCC is free software; you can redistribute it and/or modify
@@ -17,174 +17,21 @@ along with GCC; see the file COPYING3. If not see
#pragma once
#include <string>
#include <unordered_map>
#include <memory>
#include <deque>
#include "elna/boot/ast.h"
#include "elna/boot/result.h"
#include "elna/boot/symbol.h"
namespace elna::boot
namespace elna
{
class undeclared_error : public error
namespace boot
{
class name_analysis_visitor : public empty_visitor
{
const std::string identifier;
std::shared_ptr<symbol_table<>> table;
public:
undeclared_error(const std::string& identifier, const char *path, const struct position position);
std::string what() const override;
};
class already_declared_error : public error
{
const std::string identifier;
public:
already_declared_error(const std::string& identifier, const char *path, const struct position position);
std::string what() const override;
};
class field_duplication_error : public error
{
const std::string field_name;
public:
field_duplication_error(const std::string& field_name, const char *path, const struct position position);
std::string what() const override;
};
class cyclic_declaration_error : public error
{
const std::vector<std::string> cycle;
public:
cyclic_declaration_error(const std::vector<std::string>& cycle,
const char *path, const struct position position);
std::string what() const override;
};
class return_error : public error
{
const std::string identifier;
public:
return_error(const std::string& identifier, const char *path, const struct position position);
std::string what() const override;
};
class variable_initializer_error : public error
{
public:
variable_initializer_error(const char *path, const struct position position);
std::string what() const override;
};
/**
* Checks types.
*/
class type_analysis_visitor final : public empty_visitor, public error_container
{
bool returns;
symbol_bag bag;
bool check_unresolved_symbol(std::shared_ptr<alias_type> alias,
std::vector<std::string>& path);
public:
explicit type_analysis_visitor(const char *path, symbol_bag bag);
name_analysis_visitor(std::shared_ptr<symbol_table<>> table);
void visit(program *program) override;
void visit(procedure_declaration *definition) override;
void visit(assign_statement *) override;
void visit(if_statement *) override;
void visit(while_statement *) override;
void visit(return_statement *) override;
void visit(defer_statement *) override;
void visit(case_statement *) override;
void visit(procedure_call *) override;
void visit(unit *unit) override;
void visit(type_declaration *definition) override;
};
/**
* Performs name analysis.
*/
class name_analysis_visitor final : public parser_visitor, public error_container
{
type current_type;
constant_info::variant current_literal;
symbol_bag bag;
procedure_type build_procedure(procedure_type_expression& type_expression);
std::vector<type_field> build_composite_type(const std::vector<field_declaration>& fields);
public:
name_analysis_visitor(const char *path, symbol_bag bag);
void visit(named_type_expression *type_expression) override;
void visit(array_type_expression *type_expression) override;
void visit(pointer_type_expression *type_expression) override;
void visit(program *program) override;
void visit(type_declaration *definition) override;
void visit(record_type_expression *type_expression) override;
void visit(union_type_expression *type_expression) override;
void visit(procedure_type_expression *type_expression) override;
void visit(enumeration_type_expression *type_expression) override;
void visit(variable_declaration *declaration) override;
void visit(constant_declaration *definition) override;
void visit(procedure_declaration *definition) override;
void visit(assign_statement *statement) override;
void visit(if_statement *statement) override;
void visit(import_declaration *) override;
void visit(while_statement *statement) override;
void visit(return_statement *statement) override;
void visit(defer_statement *statement) override;
void visit(case_statement *statement) override;
void visit(procedure_call *call) override;
void visit(unit *unit) override;
void visit(cast_expression *expression) override;
void visit(traits_expression *trait) override;
void visit(binary_expression *expression) override;
void visit(unary_expression *expression) override;
void visit(variable_expression *) override;
void visit(array_access_expression *expression) override;
void visit(field_access_expression *expression) override;
void visit(dereference_expression *expression) override;
void visit(literal<std::int32_t> *literal) override;
void visit(literal<std::uint32_t> *literal) override;
void visit(literal<double> *literal) override;
void visit(literal<bool> *literal) override;
void visit(literal<unsigned char> *literal) override;
void visit(literal<std::nullptr_t> *literal) override;
void visit(literal<std::string> *literal) override;
};
/**
* Collects global declarations without resolving any symbols.
*/
class declaration_visitor final : public empty_visitor, public error_container
{
public:
forward_table unresolved;
explicit declaration_visitor(const char *path);
void visit(program *program) override;
void visit(import_declaration *) override;
void visit(unit *unit) override;
void visit(type_declaration *definition) override;
void visit(variable_declaration *declaration) override;
void visit(procedure_declaration *definition) override;
};
}
}

View File

@@ -21,173 +21,42 @@ along with GCC; see the file COPYING3. If not see
#include <unordered_map>
#include <string>
#include <memory>
#include <vector>
#include <forward_list>
#include "elna/boot/result.h"
namespace elna::boot
namespace elna
{
namespace boot
{
class alias_type;
class primitive_type;
class record_type;
class union_type;
class pointer_type;
class array_type;
class procedure_type;
class enumeration_type;
class type
{
enum class type_tag
{
empty,
alias,
primitive,
record,
_union,
pointer,
array,
procedure,
enumeration
};
type_tag tag{ type_tag::empty };
union
{
std::weak_ptr<alias_type> alias;
std::shared_ptr<primitive_type> primitive;
std::shared_ptr<record_type> record;
std::shared_ptr<union_type> _union;
std::shared_ptr<pointer_type> pointer;
std::shared_ptr<array_type> array;
std::shared_ptr<procedure_type> procedure;
std::shared_ptr<enumeration_type> enumeration;
};
virtual ~type() = 0;
void copy(const type& other);
void move(type&& other);
public:
type();
explicit type(std::shared_ptr<alias_type> alias);
explicit type(std::shared_ptr<primitive_type> primitive);
explicit type(std::shared_ptr<record_type> record);
explicit type(std::shared_ptr<union_type> _union);
explicit type(std::shared_ptr<pointer_type> pointer);
explicit type(std::shared_ptr<array_type> array);
explicit type(std::shared_ptr<procedure_type> procedure);
explicit type(std::shared_ptr<enumeration_type> enumeration);
type(const type& other);
type& operator=(const type& other);
type(type&& other);
type& operator=(type&& other);
bool operator==(const std::nullptr_t&);
~type();
template<typename T>
std::shared_ptr<T> get() const;
bool empty() const;
protected:
type() = default;
};
struct alias_type
{
const std::string name;
type reference;
explicit alias_type(const std::string& name);
};
struct pointer_type
{
const type base;
explicit pointer_type(type base);
};
struct array_type
{
const type base;
const std::uint64_t size;
array_type(type base, std::uint64_t size);
};
struct primitive_type
{
const std::string identifier;
explicit primitive_type(const std::string& identifier);
};
using type_field = std::pair<std::string, type>;
struct record_type
{
std::vector<type_field> fields;
};
struct union_type
{
std::vector<type_field> fields;
};
struct procedure_type
{
using return_t = return_declaration<type>;
std::vector<type> parameters;
const return_t return_type;
procedure_type(return_t return_type = return_t());
};
struct enumeration_type
{
std::vector<std::string> members;
explicit enumeration_type(const std::vector<std::string>& members);
};
class type_info;
class procedure_info;
class constant_info;
class variable_info;
class info : public std::enable_shared_from_this<info>
class info
{
public:
bool exported{ false };
virtual ~info() = 0;
virtual std::shared_ptr<type_info> is_type();
virtual std::shared_ptr<procedure_info> is_procedure();
virtual std::shared_ptr<constant_info> is_constant();
virtual std::shared_ptr<variable_info> is_variable();
protected:
info() = default;
};
/**
* Symbol table.
*/
template<typename T, typename U, U nothing>
class symbol_map
template<typename T = std::shared_ptr<info>, typename U = std::nullptr_t, U nothing = nullptr>
class symbol_table
{
public:
using symbol_ptr = typename std::enable_if<
std::is_convertible<U, T>::value || std::is_assignable<T, U>::value,
T
>::type;
using symbol_ptr = T;
using iterator = typename std::unordered_map<std::string, symbol_ptr>::iterator;
using const_iterator = typename std::unordered_map<std::string, symbol_ptr>::const_iterator;
private:
std::unordered_map<std::string, symbol_ptr> entries;
std::shared_ptr<symbol_map> outer_scope;
std::shared_ptr<symbol_table> outer_scope;
public:
/**
@@ -195,37 +64,29 @@ namespace elna::boot
*
* \param scope Outer scope.
*/
explicit symbol_map(std::shared_ptr<symbol_map> scope = nullptr)
explicit symbol_table(std::shared_ptr<symbol_table> scope = nullptr)
: outer_scope(scope)
{
}
iterator begin()
{
return this->entries.begin();
return entries.begin();
}
iterator end()
{
return this->entries.end();
return entries.end();
}
const_iterator cbegin() const
{
return this->entries.cbegin();
return entries.cbegin();
}
const_iterator cend() const
{
return this->entries.cend();
}
/**
* \return Symbol count in the current scope.
*/
std::size_t size() const
{
return this->entries.size();
return entries.cend();
}
/**
@@ -233,7 +94,6 @@ namespace elna::boot
* can not be found.
*
* \param name Symbol name.
*
* \return Symbol from the table if found.
*/
symbol_ptr lookup(const std::string& name)
@@ -251,16 +111,6 @@ namespace elna::boot
return nothing;
}
/**
* \param name Symbol name.
*
* \return Whether the table contains a symbol with the given name.
*/
bool contains(const std::string& name)
{
return lookup(name) != nothing;
}
/**
* Registers new symbol.
*
@@ -271,7 +121,14 @@ namespace elna::boot
*/
bool enter(const std::string& name, symbol_ptr entry)
{
return lookup(name) == nothing && entries.insert({ name, entry }).second;
if (lookup(name) == nothing)
{
return entries.insert({ name, entry }).second;
}
else
{
return nothing;
}
}
/**
@@ -279,179 +136,10 @@ namespace elna::boot
*
* \return Outer scope.
*/
std::shared_ptr<symbol_map> scope()
std::shared_ptr<symbol_table> scope()
{
return this->outer_scope;
}
};
using symbol_table = symbol_map<std::shared_ptr<info>, std::nullptr_t, nullptr>;
using forward_table = std::unordered_map<std::string, std::shared_ptr<alias_type>>;
class type_info : public info
{
public:
const type symbol;
explicit type_info(const type symbol);
std::shared_ptr<type_info> is_type() override;
};
/**
* Procedure symbol information.
*/
class procedure_info : public info
{
public:
/// Procedure type.
const procedure_type symbol;
/// Parameter names.
const std::vector<std::string> names;
/// Local definitions.
std::shared_ptr<symbol_table> scope;
/**
* Constructs procedure symbol information.
*
* \param symbol Procedure type.
* \param names Parameter names.
* \param scope Local definition (is `nullptr` for extern symbols).
*/
procedure_info(const procedure_type symbol, const std::vector<std::string> names,
std::shared_ptr<symbol_table> scope = nullptr);
std::shared_ptr<procedure_info> is_procedure() override;
/**
* \return Whether this is an extern symbol.
*/
bool is_extern() const;
};
class constant_info : public info
{
public:
using variant = typename
std::variant<std::int32_t, std::uint32_t, double, bool, unsigned char, std::nullptr_t, std::string>;
const variant symbol;
explicit constant_info(const variant& symbol);
std::shared_ptr<constant_info> is_constant() override;
};
/**
* Variable symbol information.
*/
class variable_info : public info
{
public:
/// Variable type.
const type symbol;
/// Whether this is an extern symbol.
const bool is_extern;
/**
* Constructs a variable symbol information.
*
* \param symbol Variable type.
* \param is_extern Whether this is an extern symbol.
*/
variable_info(const type symbol, bool is_extern);
std::shared_ptr<variable_info> is_variable() override;
};
std::shared_ptr<symbol_table> builtin_symbol_table();
/**
* Symbol bag contains:
*
* - the symbol table of a module itself
* - symbol tables of imported modules
* - forward declarations
*/
class symbol_bag
{
std::shared_ptr<symbol_table> symbols;
std::forward_list<std::shared_ptr<symbol_table>> imports;
forward_table unresolved;
public:
/**
* \param unresolved Forward declarations collected in the previous step.
* \param global_table Global symbols.
*/
symbol_bag(forward_table&& unresolved, std::shared_ptr<symbol_table> global_table);
/**
* Looks up a symbol in the current and imported modules.
*
* \param name Symbol name to look up.
*
* \return Symbol from one of the symbol tables if found.
*/
std::shared_ptr<info> lookup(const std::string& name);
/**
* Inserts a symbol into the current scope.
*
* \param name Symbol name.
* \param entry Symbol info.
*
* \return Whether the insertion took place.
*/
bool enter(const std::string& name, std::shared_ptr<info> entry);
/**
* Enters a new scope.
*
* \return Reference to the allocated scope.
*/
std::shared_ptr<symbol_table> enter();
/**
* Sets the current scope to \a child.
*
* \param child New scope.
*/
void enter(std::shared_ptr<symbol_table> child);
/**
* Leave the current scope.
*
* \return Left scope.
*/
std::shared_ptr<symbol_table> leave();
/**
* Checks whether there is a forward declaration \a symbol_name and
* returns it if so.
*
* \param symbol_name Type name to look up.
* \return Forward declaration or `nullptr` if the symbol is not declared.
*/
std::shared_ptr<alias_type> declared(const std::string& symbol_name);
/**
* Completes the forward-declared type \a symbol_name and defines it to
* be \a resolution.
*
* \param symbol_name Type name.
* \param resolution Type definition.
* \return Alias to the defined type.
*/
std::shared_ptr<alias_type> resolve(const std::string& symbol_name, type& resolution);
/**
* Add imported symbols to the scope.
*
* \param bag Symbol bag of another module.
*/
void add_import(const symbol_bag& bag);
};
}
}

View File

@@ -15,27 +15,16 @@ 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 <memory>
#include "config.h"
#include "system.h"
#include "coretypes.h"
#include "tree.h"
#include "tree-iterator.h"
#include "elna/gcc/elna-tree.h"
namespace elna::gcc
namespace elna
{
namespace gcc
{
void init_ttree();
std::shared_ptr<symbol_table> builtin_symbol_table();
void rewrite_symbol_table(std::shared_ptr<boot::symbol_table> info_table, std::shared_ptr<symbol_table> symbols);
tree handle_symbol(const std::string& symbol_name, std::shared_ptr<boot::alias_type> reference,
std::shared_ptr<symbol_table> symbols);
tree get_inner_alias(const boot::type& type, std::shared_ptr<symbol_table> symbols);
void declare_procedure(const std::string& name, const boot::procedure_info& info,
std::shared_ptr<symbol_table> symbols);
tree declare_variable(const std::string& name, const boot::variable_info& info,
std::shared_ptr<symbol_table> symbols);
}
}

View File

@@ -22,25 +22,15 @@ along with GCC; see the file COPYING3. If not see
#include "coretypes.h"
#include "input.h"
#include "tree.h"
#include "diagnostic.h"
#include <deque>
#include <memory>
#include "elna/boot/result.h"
namespace elna::gcc
namespace elna
{
namespace gcc
{
struct linemap_guard
{
explicit linemap_guard(const char *filename);
linemap_guard(const linemap_guard&) = delete;
linemap_guard(linemap_guard&&) = delete;
~linemap_guard();
};
location_t get_location(const boot::position *position);
std::string print_type(tree type);
void report_errors(const std::deque<std::unique_ptr<boot::error>>& errors);
}
}

View File

@@ -19,7 +19,6 @@ along with GCC; see the file COPYING3. If not see
#include "elna/boot/ast.h"
#include "elna/boot/symbol.h"
#include "elna/boot/semantic.h"
#include "elna/gcc/elna-tree.h"
#include "config.h"
@@ -28,20 +27,26 @@ along with GCC; see the file COPYING3. If not see
#include "tree.h"
#include "tree-iterator.h"
#include <string>
#include <forward_list>
#include <string>
namespace elna::gcc
namespace elna
{
namespace gcc
{
class generic_visitor final : public boot::empty_visitor
{
tree current_expression{ NULL_TREE };
elna::boot::symbol_bag bag;
std::shared_ptr<symbol_table> symbols;
std::shared_ptr<symbol_table> symbol_map;
tree build_label_decl(const char *name, location_t loc);
tree build_procedure_type(boot::procedure_type_expression& type);
void enter_scope();
tree leave_scope();
tree lookup(const std::string& name);
void make_if_branch(boot::conditional_statements& branch, tree goto_endif);
tree build_arithmetic_operation(boot::binary_expression *expression,
@@ -54,44 +59,44 @@ namespace elna::gcc
tree procedure_address, const std::vector<boot::expression *>& arguments);
void build_record_call(location_t call_location,
tree symbol, const std::vector<boot::expression *>& arguments);
bool build_builtin_procedures(boot::procedure_call *call);
void build_assert_builtin(location_t call_location, const std::vector<boot::expression *>& arguments);
bool expect_trait_type_only(boot::traits_expression *trait);
bool expect_trait_for_integral_type(boot::traits_expression *trait);
void visit_statements(const std::vector<boot::statement *>& statements);
bool assert_constant(location_t expression_location);
public:
generic_visitor(std::shared_ptr<symbol_table> symbol_table, elna::boot::symbol_bag bag);
generic_visitor(std::shared_ptr<symbol_table> symbol_table);
void visit(boot::program *program) override;
void visit(boot::procedure_declaration *definition) override;
void visit(boot::procedure_definition *definition) override;
void visit(boot::procedure_call *call) override;
void visit(boot::cast_expression *expression) override;
void visit(boot::traits_expression *trait) override;
void visit(boot::literal<std::int32_t> *literal) override;
void visit(boot::literal<std::uint32_t> *literal) override;
void visit(boot::literal<double> *literal) override;
void visit(boot::literal<bool> *boolean) override;
void visit(boot::literal<unsigned char> *character) override;
void visit(boot::literal<std::nullptr_t> *) override;
void visit(boot::literal<std::string> *string) override;
void visit(boot::number_literal<std::int32_t> *literal) override;
void visit(boot::number_literal<std::uint32_t> *literal) override;
void visit(boot::number_literal<double> *literal) override;
void visit(boot::number_literal<bool> *boolean) override;
void visit(boot::number_literal<unsigned char> *character) override;
void visit(boot::number_literal<std::nullptr_t> *) override;
void visit(boot::number_literal<std::string> *string) override;
void visit(boot::binary_expression *expression) override;
void visit(boot::unary_expression *expression) override;
void visit(boot::constant_declaration *definition) override;
void visit(boot::constant_definition *definition) override;
void visit(boot::type_definition *definition) override;
void visit(boot::variable_declaration *declaration) override;
void visit(boot::variable_expression *expression) override;
void visit(boot::array_access_expression *expression) override;
void visit(boot::field_access_expression *expression) override;
void visit(boot::dereference_expression *expression) override;
void visit(boot::unit *unit) override;
void visit(boot::block *block) override;
void visit(boot::assign_statement *statement) override;
void visit(boot::if_statement *statement) override;
void visit(boot::import_declaration *) override;
void visit(boot::while_statement *statement) override;
void visit(boot::primitive_type_expression *type) override;
void visit(boot::array_type_expression *type) override;
void visit(boot::pointer_type_expression *type) override;
void visit(boot::record_type_expression *type) override;
void visit(boot::union_type_expression *type) override;
void visit(boot::procedure_type_expression *type) override;
void visit(boot::return_statement *statement) override;
void visit(boot::defer_statement *statement) override;
void visit(boot::case_statement *statement) override;
};
}
}

View File

@@ -25,26 +25,27 @@ along with GCC; see the file COPYING3. If not see
#include "tree.h"
#include "tree-iterator.h"
#include "stringpool.h"
#include "fold-const.h"
#include "elna/boot/ast.h"
#include "elna/boot/symbol.h"
#include "elna/gcc/elna1.h"
namespace elna::gcc
namespace elna
{
using symbol_table = boot::symbol_map<tree, tree, NULL_TREE>;
namespace gcc
{
using symbol_table = boot::symbol_table<tree, tree, NULL_TREE>;
bool is_pointer_type(tree type);
bool is_integral_type(tree type);
bool is_numeric_type(tree type);
bool is_unique_type(tree type);
bool is_array_type(tree type);
bool is_void_type(tree type);
/**
* \param type The type to evaluate.
* \return Whether this type can be converted to another type.
* \return Whether the given type is record or union.
*/
bool is_castable_type(tree type);
bool is_aggregate_type(tree type);
/**
* \param lhs Left hand value.
@@ -74,32 +75,11 @@ namespace elna::gcc
void defer(tree statement_tree);
tree chain_defer();
tree do_pointer_arithmetic(boot::binary_operator binary_operator,
tree left, tree right, location_t expression_location);
tree do_pointer_arithmetic(boot::binary_operator binary_operator, tree left, tree right);
tree build_binary_operation(bool condition, boot::binary_expression *expression,
tree_code operator_code, tree left, tree right, tree target_type);
tree build_arithmetic_operation(boot::binary_expression *expression,
tree_code operator_code, tree left, tree right);
tree build_field(location_t location, tree record_type, const std::string name, tree type);
tree find_field_by_name(location_t expression_location, tree type, const std::string& field_name);
tree build_global_pointer_type(tree type);
tree build_static_array_type(tree type, const std::uint64_t size);
tree build_enumeration_type(const std::vector<std::string>& members);
tree build_label_decl(const char *name, location_t loc);
tree extract_constant(tree expression);
template<typename... Args>
tree call_built_in(location_t call_location, const char *name, tree return_type, Args... arguments)
{
tree *builtin = elna_global_decls->get(name);
gcc_assert(builtin != nullptr);
tree fndecl_type = build_function_type(return_type, TYPE_ARG_TYPES(*builtin));
tree builtin_addr = build1_loc(call_location, ADDR_EXPR, build_pointer_type(fndecl_type), *builtin);
tree argument_trees[sizeof...(Args)] = {arguments...};
return fold_build_call_array(return_type, builtin_addr, sizeof...(Args), argument_trees);
}
}
}

View File

@@ -15,15 +15,13 @@ 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/>. */
#pragma once
enum elna_tree_index
{
ELNA_TI_INT_TYPE,
ELNA_TI_WORD_TYPE,
ELNA_TI_CHAR_TYPE,
ELNA_TI_BOOL_TYPE,
ELNA_TI_POINTER_TYPE,
ELNA_TI_BYTE_TYPE,
ELNA_TI_FLOAT_TYPE,
ELNA_TI_STRING_TYPE,
ELNA_TI_BOOL_TRUE,
@@ -41,7 +39,7 @@ extern GTY(()) hash_map<nofree_string_hash, tree> *elna_global_decls;
#define elna_word_type_node elna_global_trees[ELNA_TI_WORD_TYPE]
#define elna_char_type_node elna_global_trees[ELNA_TI_CHAR_TYPE]
#define elna_bool_type_node elna_global_trees[ELNA_TI_BOOL_TYPE]
#define elna_pointer_type_node elna_global_trees[ELNA_TI_POINTER_TYPE]
#define elna_byte_type_node elna_global_trees[ELNA_TI_BYTE_TYPE]
#define elna_float_type_node elna_global_trees[ELNA_TI_FLOAT_TYPE]
#define elna_string_type_node elna_global_trees[ELNA_TI_STRING_TYPE]
#define elna_bool_true_node elna_global_trees[ELNA_TI_BOOL_TRUE]

View File

@@ -1,110 +0,0 @@
# This Source Code Form is subject to the terms of the Mozilla Public License,
# v. 2.0. If a copy of the MPL was not distributed with this file, You can
# obtain one at https://mozilla.org/MPL/2.0/. -}
# frozen_string_literal: true
require 'uri'
require 'net/http'
require 'open3'
def gcc_verbose(gcc_binary)
read, write = IO.pipe
sh({'LANG' => 'C'}, gcc_binary, '--verbose', err: write)
write.close
output = read.read
read.close
output
end
def find_build_target
gcc_verbose(ENV.fetch 'CC', 'gcc')
.lines
.find { |line| line.start_with? 'Target: ' }
.split(' ')
.last
.strip
end
def download_and_pipe(url, target, command)
target.mkpath
Net::HTTP.start(url.host, url.port, use_ssl: url.scheme == 'https') do |http|
request = Net::HTTP::Get.new url.request_uri
http.request request do |response|
case response
when Net::HTTPRedirection
download_and_pipe URI.parse(response['location']), target, command
when Net::HTTPSuccess
Dir.chdir target.to_path do
Open3.popen2(*command) do |stdin, stdout, wait_thread|
Thread.new do
stdout.each { |line| puts line }
end
response.read_body do |chunk|
stdin.write chunk
end
stdin.close
wait_thread.value
end
end
else
response.error!
end
end
end
end
namespace :boot do
# Dependencies.
GCC_VERSION = "15.1.0"
HOST_GCC = TMP + 'host/gcc'
GCC_PATCH = 'https://raw.githubusercontent.com/Homebrew/formula-patches/575ffcaed6d3112916fed77d271dd3799a7255c4/gcc/gcc-15.1.0.diff'
directory HOST_GCC
directory(TMP + 'tools')
desc 'Download and configure the bootstrap compiler'
task configure: [TMP + 'tools', HOST_GCC, HOST_INSTALL] do
url = URI.parse "https://gcc.gnu.org/pub/gcc/releases/gcc-#{GCC_VERSION}/gcc-#{GCC_VERSION}.tar.xz"
build_target = find_build_target
source_directory = TMP + "tools/gcc-#{GCC_VERSION}"
frontend_link = source_directory + 'gcc'
download_and_pipe url, source_directory.dirname, ['tar', '-Jxv']
download_and_pipe URI.parse(GCC_PATCH), source_directory, ['patch', '-p1']
sh 'contrib/download_prerequisites', chdir: source_directory.to_path
File.symlink Pathname.new('.').relative_path_from(frontend_link), (frontend_link + 'elna')
configure_options = [
"--prefix=#{HOST_INSTALL.realpath}",
'--enable-languages=c,c++,elna',
'--disable-bootstrap',
'--disable-multilib',
'--with-system-zlib',
"--target=#{build_target}",
"--build=#{build_target}",
"--host=#{build_target}"
]
if File.symlink? '/Library/Developer/CommandLineTools/SDKs/MacOSX.sdk'
configure_options << '--with-sysroot=/Library/Developer/CommandLineTools/SDKs/MacOSX.sdk'
end
env = ENV.slice 'CC', 'CXX'
env['CFLAGS'] = env['CXXFLAGS'] = '-O0 -g -fPIC -I/opt/homebrew/opt/flex/include'
configure = source_directory.relative_path_from(HOST_GCC) + 'configure'
sh env, configure.to_path, *configure_options, chdir: HOST_GCC
end
desc 'Make and install the bootstrap compiler'
task :make do
sh 'make', '-j', Etc.nprocessors.to_s, chdir: HOST_GCC
sh 'make', 'install', chdir: HOST_GCC
end
end
desc 'Build the bootstrap compiler'
task boot: %w[boot:configure boot:make]

990
source.elna Normal file
View File

@@ -0,0 +1,990 @@
const
SEEK_SET* = 0
SEEK_CUR* = 1
SEEK_END* = 2
TOKEN_IDENTIFIER* = 1
TOKEN_IF* = 2
TOKEN_THEN* = 3
TOKEN_ELSE* = 4
TOKEN_ELSIF* = 5
TOKEN_WHILE* = 6
TOKEN_DO* = 7
TOKEN_PROC* = 8
TOKEN_BEGIN* = 9
TOKEN_END* = 10
TOKEN_EXTERN* = 11
TOKEN_CONST* = 12
TOKEN_VAR* = 13
TOKEN_ARRAY* = 14
TOKEN_OF* = 15
TOKEN_TYPE* = 16
TOKEN_RECORD* = 17
TOKEN_UNION* = 18
TOKEN_POINTER* = 19
TOKEN_TO* = 20
TOKEN_BOOLEAN* = 21
TOKEN_NIL* = 22
TOKEN_AND* = 23
TOKEN_OR* = 24
TOKEN_NOT* = 25
TOKEN_RETURN* = 26
TOKEN_CAST* = 27
TOKEN_SHIFT_LEFT* = 28
TOKEN_SHIFT_RIGHT* = 29
TOKEN_LEFT_PAREN* = 30
TOKEN_RIGHT_PAREN* = 31
TOKEN_LEFT_SQUARE* = 32
TOKEN_RIGHT_SQUARE* = 33
TOKEN_GREATER_EQUAL* = 34
TOKEN_LESS_EQUAL* = 35
TOKEN_GREATER_THAN* = 36
TOKEN_LESS_THAN* = 37
TOKEN_NOT_EQUAL* = 38
TOKEN_EQUAL* = 39
TOKEN_SEMICOLON* = 40
TOKEN_DOT* = 41
TOKEN_COMMA* = 42
TOKEN_PLUS* = 43
TOKEN_MINUS* = 44
TOKEN_MULTIPLICATION* = 45
TOKEN_DIVISION* = 46
TOKEN_REMAINDER* = 47
TOKEN_ASSIGNMENT* = 48
TOKEN_COLON* = 49
TOKEN_HAT* = 50
TOKEN_AT* = 51
TOKEN_COMMENT* = 52
TOKEN_INTEGER* = 53
TOKEN_WORD* = 54
TOKEN_CHARACTER* = 55
TOKEN_STRING* = 56
TOKEN_DEFER* = 57
TOKEN_EXCLAMATION* = 58
TOKEN_ARROW = 59
type
Position* = record
line: Word
column: Word
end
Location* = record
first: Position
last: Position
end
FILE* = record end
SourceFile* = record
buffer: [1024]Char
handle: ^FILE
size: Word
index: Word
end
StringBuffer* = record
data: ^Byte
size: Word
capacity: Word
end
SourceCode = record
position: Position
input: ^Byte
empty: proc(data: ^Byte) -> Bool
advance: proc(data: ^Byte)
head: proc(data: ^Byte) -> Char
end
TokenValue* = union
int_value: Int
string: String
boolean_value: Bool
char_value: Char
end
Token* = record
kind: Int
value: TokenValue
location: Location
end
CommandLine* = record
input: ^Char
tokenize: Bool
syntax_tree: Bool
end
(*
External procedures.
*)
proc fopen(pathname: ^Char, mode: ^Char) -> ^FILE; extern
proc fclose(stream: ^FILE) -> Int; extern
proc fseek(stream: ^FILE, off: Int, whence: Int) -> Int; extern
proc rewind(stream: ^FILE); extern
proc ftell(stream: ^FILE) -> Int; extern
proc fread(ptr: ^Byte, size: Word, nmemb: Word, stream: ^FILE) -> Word; extern
proc write(fd: Int, buf: ^Byte, Word: Int) -> Int; extern
proc malloc(size: Word) -> ^Byte; extern
proc free(ptr: ^Byte); extern
proc calloc(nmemb: Word, size: Word) -> ^Byte; extern
proc realloc(ptr: ^Byte, size: Word) -> ^Byte; extern
proc memset(ptr: ^Char, c: Int, n: Int) -> ^Char; extern
proc strcmp(s1: ^Char, s2: ^Char) -> Int; extern
proc strncmp(s1: ^Char, s2: ^Char, n: Word) -> Int; extern
proc strncpy(dst: ^Char, src: ^Char, dsize: Word) -> ^Char; extern
proc strcpy(dst: ^Char, src: ^Char) -> ^Char; extern
proc strlen(ptr: ^Char) -> Word; extern
proc perror(s: ^Char); extern
proc exit(code: Int) -> !; extern
(*
Standard procedures.
*)
proc reallocarray(ptr: ^Byte, n: Word, size: Word) -> ^Byte;
begin
return realloc(ptr, n * size)
end
proc write_s(value: String);
begin
write(0, cast(value.ptr: ^Byte), cast(value.length: Int))
end
proc write_z(value: ^Char);
begin
write(0, cast(value: ^Byte), cast(strlen(value): Int))
end
proc write_b(value: Bool);
begin
if value then
write_s("true")
else
write_s("false")
end
end
proc write_c(value: Char);
begin
write(0, cast(@value: ^Byte), 1)
end
proc write_i(value: Int);
var
digit: Int
n: Word
buffer: [10]Char
begin
n := 10u;
if value = 0 then
write_c('0')
end;
while value <> 0 do
digit := value % 10;
value := value / 10;
buffer[n] := cast(cast('0': Int) + digit: Char);
n := n - 1u
end;
while n < 10u do
n := n + 1u;
write_c(buffer[n])
end
end
proc write_u(value: Word);
begin
write_i(cast(value: Int))
end
proc is_digit(c: Char) -> Bool;
begin
return cast(c: Int) >= cast('0': Int) and cast(c: Int) <= cast('9': Int)
end
proc is_alpha(c: Char) -> Bool;
begin
return cast(c: Int) >= cast('A': Int) and cast(c: Int) <= cast('z': Int)
end
proc is_alnum(c: Char) -> Bool;
begin
return is_digit(c) or is_alpha(c)
end
proc is_space(c: Char) -> Bool;
begin
return c = ' ' or c = '\n' or c = '\t'
end
proc substring(string: String, start: Word, count: Word) -> String;
begin
return String(string.ptr + start, count)
end
proc open_substring(string: String, start: Word) -> String;
begin
return substring(string, start, string.length - start)
end
proc string_dup(origin: String) -> String;
var
copy: ^Char
begin
copy := cast(malloc(origin.length): ^Char);
strncpy(copy, origin.ptr, origin.length);
return String(copy, origin.length)
end
proc string_buffer_new() -> StringBuffer;
var
result: StringBuffer
begin
result.capacity := 64u;
result.data := malloc(result.capacity);
result.size := 0u;
return result
end
proc string_buffer_push(buffer: ^StringBuffer, char: Char);
begin
if buffer^.size >= buffer^.capacity then
buffer^.capacity := buffer^.capacity + 1024u;
buffer^.data := realloc(buffer^.data, buffer^.capacity)
end;
(buffer^.data + buffer^.size)^ := cast(char: Byte);
buffer^.size := buffer^.size + 1u
end
proc string_buffer_pop(buffer: ^StringBuffer, count: Word);
begin
buffer^.size := buffer^.size - count
end
proc string_buffer_clear(buffer: ^StringBuffer) -> String;
var
result: String
begin
result := String(cast(buffer^.data: ^Char), buffer^.size);
buffer^.size := 0u;
return result
end
(*
End of standard procedures.
*)
proc make_position() -> Position;
begin
return Position(1u, 1u)
end
proc read_source(filename: ^Char) -> ^SourceFile;
var
result: ^SourceFile
file_handle: ^FILE
begin
file_handle := fopen(filename, "rb\0".ptr);
if file_handle <> nil then
result := cast(malloc(#size(SourceFile)): ^SourceFile);
result^.handle := file_handle;
result^.size := 0u;
result^.index := 1u
end;
return result
end
proc escape_char(escape: Char, result: ^Char) -> Bool;
var
successful: Bool
begin
if escape = 'n' then
result^ := '\n';
successful := true;
elsif escape = 'a' then
result^ := '\a';
successful := true
elsif escape = 'b' then
result^ := '\b';
successful := true
elsif escape = 't' then
result^ := '\t';
successful := true
elsif escape = 'f' then
result^ := '\f';
successful := true
elsif escape = 'r' then
result^ := '\r';
successful := true
elsif escape = 'v' then
result^ := '\v';
successful := true
elsif escape = '\\' then
result^ := '\\';
successful := true
elsif escape = '\'' then
result^ := '\'';
successful := true
elsif escape = '"' then
result^ := '"';
successful := true
elsif escape = '?' then
result^ := '\?';
successful := true
elsif escape = '0' then
result^ := '\0';
successful := true
else
successful := false
end;
return successful
end
proc source_file_empty(source_input: ^Byte) -> Bool;
var
source_file: ^SourceFile
begin
source_file := cast(source_input: ^SourceFile);
if source_file^.index > source_file^.size then
source_file^.size := fread(cast(@source_file^.buffer: ^Byte), 1u, 1024u, source_file^.handle);
source_file^.index := 1u
end;
return source_file^.size = 0u
end
proc source_file_head(source_input: ^Byte) -> Char;
var
source_file: ^SourceFile
begin
source_file := cast(source_input: ^SourceFile);
return source_file^.buffer[source_file^.index]
end
proc source_file_advance(source_input: ^Byte);
var
source_file: ^SourceFile
begin
source_file := cast(source_input: ^SourceFile);
source_file^.index := source_file^.index + 1u
end
proc source_code_empty(source_code: ^SourceCode) -> Bool;
begin
return source_code^.empty(source_code^.input)
end
proc source_code_head(source_code: SourceCode) -> Char;
begin
return source_code.head(source_code.input)
end
proc source_code_advance(source_code: ^SourceCode);
begin
source_code^.advance(source_code^.input);
source_code^.position.column := source_code^.position.column
end
proc source_code_break(source_code: ^SourceCode);
begin
source_code^.position.line := source_code^.position.line + 1u;
source_code^.position.column := 0u
end
proc source_code_expect(source_code: ^SourceCode, expected: Char) -> Bool;
begin
return not source_code_empty(source_code) and source_code_head(source_code^) = expected
end
proc skip_spaces(source_code: ^SourceCode);
begin
while not source_code_empty(source_code) and is_space(source_code_head(source_code^)) do
if source_code_head(source_code^) = '\n' then
source_code_break(source_code)
end;
source_code_advance(source_code)
end
end
proc is_ident(char: Char) -> Bool;
begin
return is_alnum(char) or char = '_'
end
proc lex_identifier(source_code: ^SourceCode, token_content: ^StringBuffer);
var
content_length: Word
begin
while not source_code_empty(source_code) and is_ident(source_code_head(source_code^)) do
string_buffer_push(token_content, source_code_head(source_code^));
source_code_advance(source_code)
end
end
proc lex_comment(source_code: ^SourceCode, token_content: ^StringBuffer) -> Bool;
var
trailing: Word
begin
trailing := 0u;
while not source_code_empty(source_code) and trailing < 2u do
if source_code_head(source_code^) = '*' then
string_buffer_push(token_content, '*');
trailing := 1u
elsif source_code_head(source_code^) = ')' and trailing = 1u then
string_buffer_pop(token_content, 1u);
trailing := 2u
else
string_buffer_push(token_content, source_code_head(source_code^));
trailing := 0u
end;
source_code_advance(source_code)
end;
return trailing = 2u
end
proc lex_character(source_code: ^SourceCode, token_content: ^Char) -> Bool;
var
successful: Bool
begin
successful := not source_code_empty(source_code);
if successful then
if source_code_head(source_code^) = '\\' then
source_code_advance(source_code);
successful := not source_code_empty(source_code) and escape_char(source_code_head(source_code^), token_content)
else
token_content^ := source_code_head(source_code^);
successful := true
end
end;
if successful then
source_code_advance(source_code)
end;
return successful
end
proc lex_string(source_code: ^SourceCode, token_content: ^StringBuffer) -> Bool;
var
token_end, constructed_string: ^Char
token_length: Word
is_valid: Bool
next_char: Char
begin
is_valid := true;
while is_valid and not source_code_empty(source_code) and source_code_head(source_code^) <> '"' do
is_valid := lex_character(source_code, @next_char);
if is_valid then
string_buffer_push(token_content, next_char)
end
end;
if is_valid and source_code_expect(source_code, '"') then
source_code_advance(source_code)
else
is_valid := false
end;
return is_valid
end
proc lex_number(source_code: ^SourceCode, token_content: ^Int);
begin
token_content^ := 0;
while not source_code_empty(source_code) and is_digit(source_code_head(source_code^)) do
token_content^ := token_content^ * 10 + (cast(source_code_head(source_code^): Int) - cast('0': Int));
source_code_advance(source_code)
end
end
proc print_tokens(tokens: ^Token, tokens_size: Word);
var
current_token: ^Token
i: Word
begin
i := 0u;
while i < tokens_size do
current_token := tokens + i;
if current_token^.kind = TOKEN_IF then
write_s("IF")
elsif current_token^.kind = TOKEN_THEN then
write_s("THEN")
elsif current_token^.kind = TOKEN_ELSE then
write_s("ELSE")
elsif current_token^.kind = TOKEN_ELSIF then
write_s("ELSIF")
elsif current_token^.kind = TOKEN_WHILE then
write_s("WHILE")
elsif current_token^.kind = TOKEN_DO then
write_s("DO")
elsif current_token^.kind = TOKEN_PROC then
write_s("PROC")
elsif current_token^.kind = TOKEN_BEGIN then
write_s("BEGIN")
elsif current_token^.kind = TOKEN_END then
write_s("END")
elsif current_token^.kind = TOKEN_EXTERN then
write_s("EXTERN")
elsif current_token^.kind = TOKEN_CONST then
write_s("CONST")
elsif current_token^.kind = TOKEN_VAR then
write_s("VAR")
elsif current_token^.kind = TOKEN_ARRAY then
write_s("ARRAY")
elsif current_token^.kind = TOKEN_OF then
write_s("OF")
elsif current_token^.kind = TOKEN_TYPE then
write_s("TYPE")
elsif current_token^.kind = TOKEN_RECORD then
write_s("RECORD")
elsif current_token^.kind = TOKEN_UNION then
write_s("UNION")
elsif current_token^.kind = TOKEN_POINTER then
write_s("POINTER")
elsif current_token^.kind = TOKEN_TO then
write_s("TO")
elsif current_token^.kind = TOKEN_BOOLEAN then
write_s("BOOLEAN<");
write_b(current_token^.value.boolean_value);
write_c('>')
elsif current_token^.kind = TOKEN_NIL then
write_s("NIL")
elsif current_token^.kind = TOKEN_AND then
write_s("AND")
elsif current_token^.kind = TOKEN_OR then
write_s("OR")
elsif current_token^.kind = TOKEN_NOT then
write_s("NOT")
elsif current_token^.kind = TOKEN_RETURN then
write_s("RETURN")
elsif current_token^.kind = TOKEN_CAST then
write_s("CAST")
elsif current_token^.kind = TOKEN_SHIFT_LEFT then
write_s("<<")
elsif current_token^.kind = TOKEN_SHIFT_RIGHT then
write_s(">>")
elsif current_token^.kind = TOKEN_IDENTIFIER then
write_c('<');
write_s(current_token^.value.string);
write_c('>')
elsif current_token^.kind = TOKEN_LEFT_PAREN then
write_s("(")
elsif current_token^.kind = TOKEN_RIGHT_PAREN then
write_s(")")
elsif current_token^.kind = TOKEN_LEFT_SQUARE then
write_s("[")
elsif current_token^.kind = TOKEN_RIGHT_SQUARE then
write_s("]")
elsif current_token^.kind = TOKEN_GREATER_EQUAL then
write_s(">=")
elsif current_token^.kind = TOKEN_LESS_EQUAL then
write_s("<=")
elsif current_token^.kind = TOKEN_GREATER_THAN then
write_s(">")
elsif current_token^.kind = TOKEN_LESS_THAN then
write_s("<")
elsif current_token^.kind = TOKEN_EQUAL then
write_s("=")
elsif current_token^.kind = TOKEN_NOT_EQUAL then
write_s("<>")
elsif current_token^.kind = TOKEN_SEMICOLON then
write_c(';')
elsif current_token^.kind = TOKEN_DOT then
write_c('.')
elsif current_token^.kind = TOKEN_COMMA then
write_c(',')
elsif current_token^.kind = TOKEN_PLUS then
write_c('+')
elsif current_token^.kind = TOKEN_MINUS then
write_c('-')
elsif current_token^.kind = TOKEN_MULTIPLICATION then
write_c('*')
elsif current_token^.kind = TOKEN_DIVISION then
write_c('/')
elsif current_token^.kind = TOKEN_REMAINDER then
write_c('%')
elsif current_token^.kind = TOKEN_ASSIGNMENT then
write_s(":=")
elsif current_token^.kind = TOKEN_COLON then
write_c(':')
elsif current_token^.kind = TOKEN_HAT then
write_c('^')
elsif current_token^.kind = TOKEN_AT then
write_c('@')
elsif current_token^.kind = TOKEN_COMMENT then
write_s("(* COMMENT *)")
elsif current_token^.kind = TOKEN_INTEGER then
write_c('<');
write_i(current_token^.value.int_value);
write_c('>')
elsif current_token^.kind = TOKEN_WORD then
write_c('<');
write_i(current_token^.value.int_value);
write_s("u>")
elsif current_token^.kind = TOKEN_CHARACTER then
write_c('<');
write_i(cast(current_token^.value.char_value: Int));
write_s("c>")
elsif current_token^.kind = TOKEN_STRING then
write_s("\"...\"")
elsif current_token^.kind = TOKEN_DEFER then
write_s("DEFER")
elsif current_token^.kind = TOKEN_EXCLAMATION then
write_c('!')
elsif current_token^.kind = TOKEN_ARROW then
write_s("->")
else
write_s("UNKNOWN<");
write_i(current_token^.kind);
write_c('>')
end;
write_c(' ');
i := i + 1u;
end;
write_c('\n')
end
proc categorize_identifier(token_content: String) -> Token;
var
current_token: Token
begin
if "if" = token_content then
current_token.kind := TOKEN_IF
elsif "then" = token_content then
current_token.kind := TOKEN_THEN
elsif "else" = token_content then
current_token.kind := TOKEN_ELSE
elsif "elsif" = token_content then
current_token.kind := TOKEN_ELSIF
elsif "while" = token_content then
current_token.kind := TOKEN_WHILE
elsif "do" = token_content then
current_token.kind := TOKEN_DO
elsif "proc" = token_content then
current_token.kind := TOKEN_PROC
elsif "begin" = token_content then
current_token.kind := TOKEN_BEGIN
elsif "end" = token_content then
current_token.kind := TOKEN_END
elsif "extern" = token_content then
current_token.kind := TOKEN_EXTERN
elsif "const" = token_content then
current_token.kind := TOKEN_CONST
elsif "var" = token_content then
current_token.kind := TOKEN_VAR
elsif "array" = token_content then
current_token.kind := TOKEN_ARRAY
elsif "of" = token_content then
current_token.kind := TOKEN_OF
elsif "type" = token_content then
current_token.kind := TOKEN_TYPE
elsif "record" = token_content then
current_token.kind := TOKEN_RECORD
elsif "union" = token_content then
current_token.kind := TOKEN_UNION
elsif "pointer" = token_content then
current_token.kind := TOKEN_POINTER
elsif "to" = token_content then
current_token.kind := TOKEN_TO
elsif "true" = token_content then
current_token.kind := TOKEN_BOOLEAN;
current_token.value.boolean_value := true
elsif "false" = token_content then
current_token.kind := TOKEN_BOOLEAN;
current_token.value.boolean_value := false
elsif "nil" = token_content then
current_token.kind := TOKEN_NIL
elsif "and" = token_content then
current_token.kind := TOKEN_AND
elsif "or" = token_content then
current_token.kind := TOKEN_OR
elsif "not" = token_content then
current_token.kind := TOKEN_NOT
elsif "return" = token_content then
current_token.kind := TOKEN_RETURN
elsif "cast" = token_content then
current_token.kind := TOKEN_CAST
elsif "defer" = token_content then
current_token.kind := TOKEN_DEFER
else
current_token.kind := TOKEN_IDENTIFIER;
current_token.value.string := string_dup(token_content)
end;
return current_token
end
proc tokenize(source_code: SourceCode, tokens_size: ^Word) -> ^Token;
var
tokens, current_token: ^Token
first_char: Char
token_buffer: StringBuffer
begin
tokens_size^ := 0u;
tokens := nil;
token_buffer := string_buffer_new();
skip_spaces(@source_code);
while not source_code_empty(@source_code) do
tokens := cast(reallocarray(cast(tokens: ^Byte), tokens_size^ + 1u, #size(Token)): ^Token);
current_token := tokens + tokens_size^;
first_char := source_code_head(source_code);
if is_alpha(first_char) or first_char = '_' then
lex_identifier(@source_code, @token_buffer);
current_token^ := categorize_identifier(string_buffer_clear(@token_buffer))
elsif is_digit(first_char) then
lex_number(@source_code, @current_token^.value.int_value);
if source_code_expect(@source_code, 'u') then
current_token^.kind := TOKEN_WORD;
source_code_advance(@source_code)
else
current_token^.kind := TOKEN_INTEGER
end
elsif first_char = '(' then
source_code_advance(@source_code);
if source_code_empty(@source_code) then
current_token^.kind := TOKEN_LEFT_PAREN
elsif source_code_head(source_code) = '*' then
source_code_advance(@source_code);
if lex_comment(@source_code, @token_buffer) then
current_token^.value.string := string_dup(string_buffer_clear(@token_buffer));
current_token^.kind := TOKEN_COMMENT
else
current_token^.kind := 0
end
else
current_token^.kind := TOKEN_LEFT_PAREN
end
elsif first_char = ')' then
current_token^.kind := TOKEN_RIGHT_PAREN;
source_code_advance(@source_code)
elsif first_char = '\'' then
source_code_advance(@source_code);
if lex_character(@source_code, @current_token^.value.char_value) and source_code_expect(@source_code, '\'') then
current_token^.kind := TOKEN_CHARACTER;
source_code_advance(@source_code)
else
current_token^.kind := 0
end
elsif first_char = '"' then
source_code_advance(@source_code);
if lex_string(@source_code, @token_buffer) then
current_token^.kind := TOKEN_STRING;
current_token^.value.string := string_dup(string_buffer_clear(@token_buffer))
else
current_token^.kind := 0
end
elsif first_char = '[' then
current_token^.kind := TOKEN_LEFT_SQUARE;
source_code_advance(@source_code)
elsif first_char = ']' then
current_token^.kind := TOKEN_RIGHT_SQUARE;
source_code_advance(@source_code)
elsif first_char = '>' then
source_code_advance(@source_code);
if source_code_empty(@source_code) then
current_token^.kind := TOKEN_GREATER_THAN
elsif source_code_head(source_code) = '=' then
current_token^.kind := TOKEN_GREATER_EQUAL;
source_code_advance(@source_code)
elsif source_code_head(source_code) = '>' then
current_token^.kind := TOKEN_SHIFT_RIGHT;
source_code_advance(@source_code)
else
current_token^.kind := TOKEN_GREATER_THAN
end
elsif first_char = '<' then
source_code_advance(@source_code);
if source_code_empty(@source_code) then
current_token^.kind := TOKEN_LESS_THAN
elsif source_code_head(source_code) = '=' then
current_token^.kind := TOKEN_LESS_EQUAL;
source_code_advance(@source_code)
elsif source_code_head(source_code) = '<' then
current_token^.kind := TOKEN_SHIFT_LEFT;
source_code_advance(@source_code)
elsif source_code_head(source_code) = '>' then
current_token^.kind := TOKEN_NOT_EQUAL;
source_code_advance(@source_code)
else
current_token^.kind := TOKEN_LESS_THAN
end
elsif first_char = '=' then
current_token^.kind := TOKEN_EQUAL;
source_code_advance(@source_code)
elsif first_char = ';' then
current_token^.kind := TOKEN_SEMICOLON;
source_code_advance(@source_code)
elsif first_char = '.' then
current_token^.kind := TOKEN_DOT;
source_code_advance(@source_code)
elsif first_char = ',' then
current_token^.kind := TOKEN_COMMA;
source_code_advance(@source_code)
elsif first_char = '+' then
current_token^.kind := TOKEN_PLUS;
source_code_advance(@source_code)
elsif first_char = '-' then
source_code_advance(@source_code);
if source_code_empty(@source_code) then
current_token^.kind := TOKEN_MINUS
elsif source_code_head(source_code) = '>' then
current_token^.kind := TOKEN_ARROW;
source_code_advance(@source_code)
else
current_token^.kind := TOKEN_MINUS
end
elsif first_char = '*' then
current_token^.kind := TOKEN_MULTIPLICATION;
source_code_advance(@source_code)
elsif first_char = '/' then
current_token^.kind := TOKEN_DIVISION;
source_code_advance(@source_code)
elsif first_char = '%' then
current_token^.kind := TOKEN_REMAINDER;
source_code_advance(@source_code)
elsif first_char = ':' then
source_code_advance(@source_code);
if source_code_empty(@source_code) then
current_token^.kind := TOKEN_COLON
elsif source_code_head(source_code) = '=' then
current_token^.kind := TOKEN_ASSIGNMENT;
source_code_advance(@source_code)
else
current_token^.kind := TOKEN_COLON
end
elsif first_char = '^' then
current_token^.kind := TOKEN_HAT;
source_code_advance(@source_code)
elsif first_char = '@' then
current_token^.kind := TOKEN_AT;
source_code_advance(@source_code)
elsif first_char = '!' then
current_token^.kind := TOKEN_EXCLAMATION;
source_code_advance(@source_code)
else
current_token^.kind := 0;
source_code_advance(@source_code)
end;
if current_token^.kind <> 0 then
tokens_size^ := tokens_size^ + 1u;
skip_spaces(@source_code)
else
write_s("Lexical analysis error on \"");
write_c(first_char);
write_s("\".\n")
end
end;
return tokens
end
proc parse_command_line*(argc: Int, argv: ^^Char) -> ^CommandLine;
var
parameter: ^^Char
i: Int
result: ^CommandLine
begin
i := 1;
result := cast(malloc(#size(CommandLine)): ^CommandLine);
result^.tokenize := false;
result^.syntax_tree := false;
result^.input := nil;
while i < argc do
parameter := argv + i;
if strcmp(parameter^, "--tokenize\0".ptr) = 0 then
result^.tokenize := true
elsif strcmp(parameter^, "--syntax-tree\0".ptr) = 0 then
result^.syntax_tree := true
elsif parameter^^ <> '-' then
result^.input := parameter^
else
write_s("Fatal error: Unknown command line options:");
write_c(' ');
write_z(parameter^);
write_s(".\n");
return nil
end;
i := i + 1
end;
if result^.input = nil then
write_s("Fatal error: no input files.\n");
return nil
end;
return result
end
proc process(argc: Int, argv: ^^Char) -> Int;
var
tokens: ^Token
tokens_size: Word
source_code: SourceCode
command_line: ^CommandLine
return_code: Int
begin
return_code := 0;
command_line := parse_command_line(argc, argv);
if command_line = nil then
return_code := 2
end;
if return_code = 0 then
source_code.position := make_position();
source_code.input := cast(read_source(command_line^.input): ^Byte);
source_code.empty := source_file_empty;
source_code.head := source_file_head;
source_code.advance := source_file_advance;
if source_code.input = nil then
perror(command_line^.input);
return_code := 3
end
end;
if return_code = 0 then
tokens := tokenize(source_code, @tokens_size);
fclose(cast(source_code.input: ^SourceFile)^.handle);
if command_line^.tokenize then
print_tokens(tokens, tokens_size)
end
end;
return return_code
end
begin
exit(process(cast(count: Int), cast(parameters: ^^Char)))
end.

File diff suppressed because it is too large Load Diff

View File

@@ -1,631 +0,0 @@
(* This Source Code Form is subject to the terms of the Mozilla Public License,
v. 2.0. If a copy of the MPL was not distributed with this file, You can
obtain one at https://mozilla.org/MPL/2.0/. *)
module;
from FIO import File, WriteNBytes, WriteLine, WriteChar, WriteString;
from NumberIO import IntToStr;
import common, Parser;
type
TranspilerContext* = record
input_name: String;
output: File;
definition: File;
indentation: Word
end;
proc indent(context: ^TranspilerContext);
var
count: Word;
begin
count := 0;
while count < context^.indentation do
WriteString(context^.output, " ");
count := count + 1u
end
end;
(* Write a semicolon followed by a newline. *)
proc write_semicolon(output: File);
begin
WriteChar(output, ';');
WriteLine(output)
end;
proc transpile_import_statement(context: ^TranspilerContext, import_statement: ^AstImportStatement);
var
current_symbol: ^Identifier;
begin
WriteString(context^.output, "FROM ");
transpile_identifier(context, import_statement^.package);
WriteString(context^.output, " IMPORT ");
current_symbol := import_statement^.symbols;
transpile_identifier(context, current_symbol^);
current_symbol := current_symbol + 1;
while current_symbol^[1] <> '\0' do
WriteString(context^.output, ", ");
transpile_identifier(context, current_symbol^);
current_symbol := current_symbol + 1;
end;
write_semicolon(context^.output)
end;
proc transpile_import_part(context: ^TranspilerContext, imports: ^^AstImportStatement);
var
import_statement: ^AstImportStatement;
begin
while imports^ <> nil do
transpile_import_statement(context, imports^);
imports := imports + 1
end;
WriteLine(context^.output)
end;
proc transpile_constant_declaration(context: ^TranspilerContext, declaration: ^AstConstantDeclaration);
var
buffer: [20]Char;
begin
WriteString(context^.output, " ");
transpile_identifier(context, declaration^.constant_name);
WriteString(context^.output, " = ");
IntToStr(declaration^.constant_value, 0, buffer);
WriteString(context^.output, buffer);
write_semicolon(context^.output)
end;
proc transpile_constant_part(context: ^TranspilerContext, declarations: ^^AstConstantDeclaration, extra_newline: Bool);
var
current_declaration: ^^AstConstantDeclaration;
begin
if declarations^ <> nil then
WriteString(context^.output, "CONST");
WriteLine(context^.output);
current_declaration := declarations;
while current_declaration^ <> nil do
transpile_constant_declaration(context, current_declaration^);
current_declaration := current_declaration + 1
end;
if extra_newline then
WriteLine(context^.output)
end
end
end;
proc transpile_module(context: ^TranspilerContext, result: ^AstModule);
begin
if result^.main = false then
WriteString(context^.output, "IMPLEMENTATION ")
end;
WriteString(context^.output, "MODULE ");
(* Write the module name and end the line with a semicolon and newline. *)
transpile_module_name(context);
write_semicolon(context^.output);
WriteLine(context^.output);
(* Write the module body. *)
transpile_import_part(context, result^.imports);
transpile_constant_part(context, result^.constants, true);
transpile_type_part(context, result^.types);
transpile_variable_part(context, result^.variables, true);
transpile_procedure_part(context, result^.procedures);
transpile_statement_part(context, result^.statements);
WriteString(context^.output, "END ");
transpile_module_name(context);
WriteChar(context^.output, ".");
WriteLine(context^.output)
end;
proc transpile_type_fields(context: ^TranspilerContext, fields: ^AstFieldDeclaration);
var
current_field: ^AstFieldDeclaration;
begin
current_field := fields;
while current_field^.field_name[1] <> '\0' do
WriteString(context^.output, " ");
transpile_identifier(context, current_field^.field_name);
WriteString(context^.output, ": ");
transpile_type_expression(context, current_field^.field_type);
current_field := current_field + 1;
if current_field^.field_name[1] <> '\0' then
WriteChar(context^.output, ';')
end;
WriteLine(context^.output)
end
end;
proc transpile_record_type(context: ^TranspilerContext, type_expression: ^AstTypeExpression);
begin
WriteString(context^.output, "RECORD");
WriteLine(context^.output);
transpile_type_fields(context, type_expression^.fields);
WriteString(context^.output, " END")
end;
proc transpile_pointer_type(context: ^TranspilerContext, type_expression: ^AstTypeExpression);
begin
WriteString(context^.output, "POINTER TO ");
transpile_type_expression(context, type_expression^.target)
end;
proc transpile_array_type(context: ^TranspilerContext, type_expression: ^AstTypeExpression);
var
buffer: [20]Char;
begin
WriteString(context^.output, "ARRAY");
if type_expression^.length <> 0 then
WriteString(context^.output, "[1..");
IntToStr(type_expression^.length, 0, buffer);
WriteString(context^.output, buffer);
WriteChar(context^.output, ']')
end;
WriteString(context^.output, " OF ");
transpile_type_expression(context, type_expression^.base)
end;
proc transpile_enumeration_type(context: ^TranspilerContext, type_expression: ^AstTypeExpression);
var
current_case: ^Identifier;
begin
current_case := type_expression^.cases;
WriteString(context^.output, "(");
WriteLine(context^.output);
WriteString(context^.output, " ");
transpile_identifier(context, current_case^);
current_case := current_case + 1;
while current_case^[1] <> '\0' do
WriteChar(context^.output, ',');
WriteLine(context^.output);
WriteString(context^.output, " ");
transpile_identifier(context, current_case^);
current_case := current_case + 1
end;
WriteLine(context^.output);
WriteString(context^.output, " )")
end;
proc transpile_identifier(context: ^TranspilerContext, identifier: Identifier);
var
written_bytes: Word;
begin
written_bytes := WriteNBytes(context^.output, cast(identifier[1]: Word), @identifier[2])
end;
proc transpile_procedure_type(context: ^TranspilerContext, type_expression: ^AstTypeExpression);
var
result: ^AstTypeExpression;
current_parameter: ^^AstTypeExpression;
parameter_count: Word;
begin
WriteString(context^.output, "PROCEDURE(");
current_parameter := type_expression^.parameters;
while current_parameter^ <> nil do
transpile_type_expression(context, current_parameter^);
current_parameter := current_parameter + 1;
if current_parameter^ <> nil then
WriteString(context^.output, ", ")
end
end;
WriteChar(context^.output, ')')
end;
proc transpile_type_expression(context: ^TranspilerContext, type_expression: ^AstTypeExpression);
begin
if type_expression^.kind = astTypeExpressionKindRecord then
transpile_record_type(context, type_expression)
end;
if type_expression^.kind = astTypeExpressionKindEnumeration then
transpile_enumeration_type(context, type_expression)
end;
if type_expression^.kind = astTypeExpressionKindArray then
transpile_array_type(context, type_expression)
end;
if type_expression^.kind = astTypeExpressionKindPointer then
transpile_pointer_type(context, type_expression)
end;
if type_expression^.kind = astTypeExpressionKindProcedure then
transpile_procedure_type(context, type_expression)
end;
if type_expression^.kind = astTypeExpressionKindNamed then
transpile_identifier(context, type_expression^.name)
end
end;
proc transpile_type_declaration(context: ^TranspilerContext, declaration: ^AstTypedDeclaration);
var
written_bytes: Word;
begin
WriteString(context^.output, " ");
transpile_identifier(context^.output, declaration^.identifier);
WriteString(context^.output, " = ");
transpile_type_expression(context, declaration^.type_expression);
write_semicolon(context^.output)
end;
proc transpile_type_part(context: ^TranspilerContext, declarations: ^^AstTypedDeclaration);
var
current_declaration: ^^AstTypedDeclaration;
begin
if declarations^ <> nil then
WriteString(context^.output, "TYPE");
WriteLine(context^.output);
current_declaration := declarations;
while current_declaration^ <> nil do
transpile_type_declaration(context, current_declaration^);
current_declaration := current_declaration + 1
end;
WriteLine(context^.output)
end
end;
proc transpile_variable_declaration(context: ^TranspilerContext, declaration: ^AstVariableDeclaration);
begin
WriteString(context^.output, " ");
transpile_identifier(context, declaration^.variable_name);
WriteString(context^.output, ": ");
transpile_type_expression(context, declaration^.variable_type);
write_semicolon(context^.output)
end;
proc transpile_variable_part(context: ^TranspilerContext, declarations: ^^AstVariableDeclaration, extra_newline: Bool);
var
current_declaration: ^^AstVariableDeclaration;
begin
if declarations^ <> nil then
WriteString(context^.output, "VAR");
WriteLine(context^.output);
current_declaration := declarations;
while current_declaration^ <> nil do
transpile_variable_declaration(context, current_declaration^);
current_declaration := current_declaration + 1
end;
if extra_newline then
WriteLine(context^.output)
end
end
end;
proc transpile_procedure_heading(context: ^TranspilerContext, declaration: ^AstProcedureDeclaration);
var
parameter_index: Word;
current_parameter: ^AstTypedDeclaration;
begin
WriteString(context^.output, "PROCEDURE ");
transpile_identifier(context, declaration^.name);
WriteChar(context^.output, '(');
parameter_index := 0;
current_parameter := declaration^.parameters;
while parameter_index < declaration^.parameter_count do
transpile_identifier(context, current_parameter^.identifier);
WriteString(context^.output, ": ");
transpile_type_expression(context, current_parameter^.type_expression);
parameter_index := parameter_index + 1u;
current_parameter := current_parameter + 1;
if parameter_index <> declaration^.parameter_count then
WriteString(context^.output, "; ")
end
end;
WriteString(context^.output, ")");
(* Check for the return type and write it. *)
if declaration^.return_type <> nil then
WriteString(context^.output, ": ");
transpile_type_expression(context, declaration^.return_type)
end;
write_semicolon(context^.output)
end;
proc transpile_unary_operator(context: ^TranspilerContext, operator: AstUnaryOperator);
begin
if operator = AstUnaryOperator.minus then
WriteChar(context^.output, '-')
end;
if operator = AstUnaryOperator.not then
WriteChar(context^.output, '~')
end
end;
proc transpile_binary_operator(context: ^TranspilerContext, operator: AstBinaryOperator);
begin
case operator of
AstBinaryOperator.sum: WriteChar(context^.output, '+')
| AstBinaryOperator.subtraction: WriteChar(context^.output, '-')
| AstBinaryOperator.multiplication: WriteChar(context^.output, '*')
| AstBinaryOperator.equals: WriteChar(context^.output, '=')
| AstBinaryOperator.not_equals: WriteChar(context^.output, '#')
| AstBinaryOperator.less: WriteChar(context^.output, '<')
| AstBinaryOperator.greater: WriteChar(context^.output, '>')
| AstBinaryOperator.less_equal: WriteString(context^.output, "<=")
| AstBinaryOperator.greater_equal: WriteString(context^.output, ">=")
| AstBinaryOperator.disjunction: WriteString(context^.output, "OR")
| AstBinaryOperatorConjunction: WriteString(context^.output, "AND")
end
end;
proc transpile_expression(context: ^TranspilerContext, expression: ^AstExpression);
var
literal: ^AstLiteral;
buffer: [20]Char;
argument_index: Word;
current_argument: ^^AstExpression;
begin
if expression^.kind = astExpressionKindLiteral then
literal := expression^.literal;
if literal^.kind = AstLiteralKind.integer then
IntToStr(literal^.integer, 0, buffer);
WriteString(context^.output, buffer)
end;
if literal^.kind = AstLiteralKind.string then
WriteString(context^.output, literal^.string)
end;
if literal^.kind = AstLiteralKind.null then
WriteString(context^.output, "NIL")
end;
if (literal^.kind = AstLiteralKind.boolean) & literal^.boolean then
WriteString(context^.output, "TRUE")
end;
if (literal^.kind = AstLiteralKind.boolean) & (literal^.boolean = false) then
WriteString(context^.output, "FALSE")
end
end;
if expression^.kind = astExpressionKindIdentifier then
transpile_identifier(context, expression^.identifier)
end;
if expression^.kind = astExpressionKindDereference then
transpile_expression(context, expression^.reference);
WriteChar(context^.output, '^')
end;
if expression^.kind = astExpressionKindArrayAccess then
transpile_expression(context, expression^.array);
WriteChar(context^.output, '[');
transpile_expression(context, expression^.index);
WriteChar(context^.output, ']')
end;
if expression^.kind = astExpressionKindFieldAccess then
transpile_expression(context, expression^.aggregate);
WriteChar(context^.output, '.');
transpile_identifier(contextexpression^.field)
end;
if expression^.kind = astExpressionKindUnary then
transpile_unary_operator(context, expression^.unary_operator);
transpile_expression(context, expression^.unary_operand)
end;
if expression^.kind = astExpressionKindBinary then
WriteChar(context^.output, '(');
transpile_expression(context, expression^.lhs);
WriteChar(context^.output, ' ');
transpile_binary_operator(context, expression^.binary_operator);
WriteChar(context^.output, ' ');
transpile_expression(context, expression^.rhs);
WriteChar(context^.output, ')')
end;
if expression^.kind = astExpressionKindCall then
transpile_expression(context, expression^.callable);
WriteChar(context^.output, '(');
current_argument := expression^.arguments;
if expression^.argument_count > 0 then
transpile_expression(context, current_argument^);
argument_index := 1u;
current_argument := current_argument + 1;
while argument_index < expression^.argument_count do
WriteString(context^.output, ", ");
transpile_expression(context, current_argument^);
current_argument := current_argument + 1;
argument_index := argument_index + 1u
end
end;
WriteChar(context^.output, ')')
end
end;
proc transpile_if_statement(context: ^TranspilerContext, statement: ^AstStatement);
begin
WriteString(context^.output, "IF ");
transpile_expression(context, statement^.if_condition);
WriteString(context^.output, " THEN");
WriteLine(context^.output);
context^.indentation := context^.indentation + 1u;
transpile_compound_statement(context, statement^.if_branch);
context^.indentation := context^.indentation - 1u;
indent(context);
WriteString(context^.output, "END")
end;
proc transpile_while_statement(context: ^TranspilerContext, statement: ^AstStatement);
begin
WriteString(context^.output, "WHILE ");
transpile_expression(context, statement^.while_condition);
WriteString(context^.output, " DO");
WriteLine(context^.output);
context^.indentation := context^.indentation + 1u;
transpile_compound_statement(context, statement^.while_body);
context^.indentation := context^.indentation - 1u;
indent(context);
WriteString(context^.output, "END")
end;
proc transpile_assignment_statement(context: ^TranspilerContext, statement: ^AstStatement);
begin
transpile_expression(context, statement^.assignee);
WriteString(context^.output, " := ");
transpile_expression(context, statement^.assignment)
end;
proc transpile_return_statement(context: ^TranspilerContext, statement: ^AstStatement);
begin
WriteString(context^.output, "RETURN ");
transpile_expression(context, statement^.returned)
end;
proc transpile_compound_statement(context: ^TranspilerContext, statement: AstCompoundStatement);
var
current_statement: ^^AstStatement;
index: Word;
begin
index := 0;
current_statement := statement.statements;
while index < statement.count do
transpile_statement(context, current_statement^);
current_statement := current_statement + 1;
index := index + 1u;
if index <> statement.count then
WriteChar(context^.output, ';')
end;
WriteLine(context^.output)
end
end;
proc transpile_statement(context: ^TranspilerContext, statement: ^AstStatement);
begin
indent(context);
if statement^.kind = astStatementKindIf then
transpile_if_statement(context, statement)
end;
if statement^.kind = astStatementKindWhile then
transpile_while_statement(context, statement)
end;
if statement^.kind = astStatementKindReturn then
transpile_return_statement(context, statement)
end;
if statement^.kind = astStatementKindAssignment then
transpile_assignment_statement(context, statement)
end;
if statement^.kind = astStatementKindCall then
transpile_expression(context, statement^.call)
end
end;
proc transpile_statement_part(context: ^TranspilerContext, compound: AstCompoundStatement);
begin
if compound.count > 0 then
WriteString(context^.output, "BEGIN");
WriteLine(context^.output);
context^.indentation := context^.indentation + 1u;
transpile_compound_statement(context, compound);
context^.indentation := context^.indentation - 1u;
end
end;
proc transpile_procedure_declaration(context: ^TranspilerContext, declaration: ^AstProcedureDeclaration);
begin
transpile_procedure_heading(context, declaration);
transpile_constant_part(context, declaration^.constants, false);
transpile_variable_part(context, declaration^.variables, false);
transpile_statement_part(context, declaration^.statements);
WriteString(context^.output, "END ");
transpile_identifier(context^.output, declaration^.name);
write_semicolon(context^.output)
end;
proc transpile_procedure_part(context: ^TranspilerContext, declaration: ^^AstProcedureDeclaration);
begin
while declaration^ <> nil do
transpile_procedure_declaration(context, declaration^);
WriteLine(context^.output);
declaration := declaration + 1
end
end;
proc transpile_module_name(context: ^TranspilerContext);
var
counter: Word;
last_slash: Word;
begin
counter := 1u;
last_slash := 0u;
while context^.input_name[counter] <> '.' & context^.input_name[counter] <> '\0' do
if context^.input_name[counter] = '/' then
last_slash := counter
end;
counter := counter + 1u
end;
if last_slash = 0u then
counter := 1u
end;
if last_slash <> 0u then
counter := last_slash + 1u
end;
while context^.input_name[counter] <> '.' & context^.input_name[counter] <> '\0' do
WriteChar(context^.output, context^.input_name[counter]);
counter := counter + 1u
end
end;
proc transpile*(ast_module: ^AstModule, output: File, definition: File, input_name: String);
var
context: TranspilerContext;
begin
context.input_name := input_name;
context.output := output;
context.definition := definition;
context.indentation := 0u;
transpile_module(@context, ast_module)
end;
end.

View File

@@ -1,14 +0,0 @@
(* This Source Code Form is subject to the terms of the Mozilla Public License,
v. 2.0. If a copy of the MPL was not distributed with this file, You can
obtain one at https://mozilla.org/MPL/2.0/. *)
module;
proc isdigit*(c: Int ) -> Int; extern;
proc isalnum*(c: Int) -> Int; extern;
proc isalpha*(c: Int) -> Int; extern;
proc isspace*(c: Int) -> Int; extern;
proc tolower*(c: Int) -> Int; extern;
proc toupper*(c: Int) -> Int; extern;
end.

View File

@@ -1,93 +0,0 @@
(* This Source Code Form is subject to the terms of the Mozilla Public License,
v. 2.0. If a copy of the MPL was not distributed with this file, You can
obtain one at https://mozilla.org/MPL/2.0/. *)
(*
Command line handling.
*)
module;
import cstdlib, cstring, common;
type
CommandLine* = record
input: ^Char;
output: ^Char;
lex: Bool;
parse: Bool
end;
proc parse_command_line*(argc: Int, argv: ^^Char) -> ^CommandLine;
var
parameter: ^Char;
i: Int;
result: ^CommandLine;
parsed: Bool;
begin
i := 1;
result := cast(malloc(#size(CommandLine)): ^CommandLine);
result^.lex := false;
result^.parse := false;
result^.input := nil;
result^.output := nil;
while i < argc & result <> nil do
parameter := (argv + i)^;
parsed := false;
if strcmp(parameter, "--lex\0".ptr) = 0 then
parsed := true;
result^.lex := true
end;
if strcmp(parameter, "--parse\0".ptr) = 0 then
parsed := true;
result^.parse := true
end;
if strcmp(parameter, "-o\0".ptr) = 0 then
i := i + 1;
if i = argc then
write_s("Fatal error: expecting a file name following -o.");
result := nil
end;
if i < argc then
parameter := (argv + i)^;
result^.output := parameter
end;
parsed := true
end;
if (parameter^ <> '-') & ~parsed then
parsed := true;
if result^.input <> nil then
write_s("Fatal error: only one source file can be compiled at once. First given \"");
write_z(result^.input);
write_s("\", then \"");
write_z(parameter);
write_s("\".\n");
result := nil
end;
if result <> nil then
result^.input := parameter
end
end;
if ~parsed then
write_s("Fatal error: unknown command line options: ");
write_z(parameter);
write_s(".\n");
result := nil
end;
i := i + 1
end;
if result <> nil & result^.input = nil then
write_s("Fatal error: no input files.\n");
result := nil
end;
return result
end;
end.

View File

@@ -1,72 +0,0 @@
(* This Source Code Form is subject to the terms of the Mozilla Public License,
v. 2.0. If a copy of the MPL was not distributed with this file, You can
obtain one at https://mozilla.org/MPL/2.0/. *)
module;
import cstring, cstdio;
type
Identifier = [256]Char;
TextLocation* = record
line: Word;
column: Word
end;
proc write*(fd: Int, buf: Pointer, Word: Int) -> Int; extern;
proc write_s*(value: String);
begin
(* fwrite(cast(value.ptr: Pointer), value.length, 1u, stdout) *)
write(1, cast(value.ptr: Pointer), cast(value.length: Int))
end;
proc write_z*(value: ^Char);
begin
write(1, cast(value: Pointer), cast(strlen(value): Int))
end;
proc write_b*(value: Bool);
begin
if value then
write_s("true")
else
write_s("false")
end
end;
proc write_c*(value: Char);
begin
putchar(cast(value: Int));
fflush(nil)
end;
proc write_i*(value: Int);
var
digit: Int;
n: Word;
buffer: [10]Char;
begin
n := 10u;
if value = 0 then
write_c('0')
end;
while value <> 0 do
digit := value % 10;
value := value / 10;
buffer[n] := cast(cast('0': Int) + digit: Char);
n := n - 1u
end;
while n < 10u do
n := n + 1u;
write_c(buffer[n])
end
end;
proc write_u*(value: Word);
begin
write_i(cast(value: Int))
end;
end.

View File

@@ -1,29 +0,0 @@
(* This Source Code Form is subject to the terms of the Mozilla Public License,
v. 2.0. If a copy of the MPL was not distributed with this file, You can
obtain one at https://mozilla.org/MPL/2.0/. *)
module;
type
FILE* = record end;
var
stdin*: ^FILE := extern;
stdout*: ^FILE := extern;
stderr*: ^FILE := extern;
proc fopen*(pathname: ^Char, mode: ^Char) -> ^FILE; extern;
proc fclose*(stream: ^FILE) -> Int; extern;
proc fseek*(stream: ^FILE, off: Int, whence: Int) -> Int; extern;
proc rewind*(stream: ^FILE); extern;
proc ftell*(stream: ^FILE) -> Int; extern;
proc fflush*(stream: ^FILE) -> Int; extern;
proc fread*(ptr: Pointer, size: Word, nmemb: Word, stream: ^FILE) -> Word; extern;
proc fwrite*(ptr: Pointer, size: Word, nitems: Word, stream: ^FILE) -> Word; extern;
proc perror(s: ^Char); extern;
proc puts(s: ^Char) -> Int; extern;
proc putchar(c: Int) -> Int; extern;
end.

View File

@@ -1,15 +0,0 @@
(* This Source Code Form is subject to the terms of the Mozilla Public License,
v. 2.0. If a copy of the MPL was not distributed with this file, You can
obtain one at https://mozilla.org/MPL/2.0/. *)
module;
proc malloc(size: Word) -> Pointer; extern;
proc free(ptr: Pointer); extern;
proc calloc(nmemb: Word, size: Word) -> Pointer; extern;
proc realloc(ptr: Pointer, size: Word) -> Pointer; extern;
proc atoi(str: ^Char) -> Int; extern;
proc exit(code: Int) -> !; extern;
end.

View File

@@ -1,15 +0,0 @@
(* This Source Code Form is subject to the terms of the Mozilla Public License,
v. 2.0. If a copy of the MPL was not distributed with this file, You can
obtain one at https://mozilla.org/MPL/2.0/. *)
module;
proc memset(ptr: Pointer, c: Int, n: Word) -> ^Char; extern;
proc memcpy(dst: Pointer, src: Pointer, n: Word); extern;
proc strcmp(s1: ^Char, s2: ^Char) -> Int; extern;
proc strncmp(s1: ^Char, s2: ^Char, n: Word) -> Int; extern;
proc strncpy(dst: ^Char, src: ^Char, dsize: Word) -> ^Char; extern;
proc strcpy(dst: ^Char, src: ^Char) -> ^Char; extern;
proc strlen(ptr: ^Char) -> Word; extern;
end.

View File

@@ -1,952 +0,0 @@
(* This Source Code Form is subject to the terms of the Mozilla Public License,
v. 2.0. If a copy of the MPL was not distributed with this file, You can
obtain one at https://mozilla.org/MPL/2.0/. *)
module;
import cstdio, cstring, cctype, cstdlib, common;
const
CHUNK_SIZE := 85536u;
type
(*
* Classification table assigns each possible character to a group (class). All
* characters of the same group are handled equivalently.
*
* Classification:
*)
TransitionClass = (
invalid,
digit,
alpha,
space,
colon,
equals,
left_paren,
right_paren,
asterisk,
underscore,
single,
hex,
zero,
x,
eof,
dot,
minus,
single_quote,
double_quote,
greater,
less,
other
);
TransitionState = (
start,
colon,
identifier,
decimal,
greater,
minus,
left_paren,
less,
dot,
comment,
closing_comment,
character,
string,
leading_zero,
decimal_suffix,
finish
);
LexerToken = record
kind: LexerKind;
value: union
booleanKind: Bool;
identifierKind: Identifier;
integerKind: Int;
stringKind: String
end;
start_location: TextLocation;
end_location: TextLocation
end;
TransitionAction = proc(^Lexer, ^LexerToken);
Transition = record
action: TransitionAction;
next_state: TransitionState
end;
TransitionClasses = [22]Transition;
BufferPosition* = record
iterator: ^Char;
location: TextLocation
end;
Lexer* = record
input: ^FILE;
buffer: ^Char;
size: Word;
length: Word;
start: BufferPosition;
current: BufferPosition
end;
LexerKind* = (
unknown,
identifier,
_if,
_then,
_else,
_elsif,
_while,
_do,
_proc,
_begin,
_end,
_extern,
_const,
_var,
_case,
_of,
_type,
_record,
_union,
pipe,
to,
boolean,
null,
and,
_or,
_xor,
not,
_return,
_cast,
shift_left,
shift_right,
left_paren,
right_paren,
left_square,
right_square,
greater_equal,
less_equal,
greater_than,
less_than,
not_equal,
equal,
semicolon,
dot,
comma,
plus,
minus,
multiplication,
division,
remainder,
assignment,
colon,
hat,
at,
comment,
integer,
word,
character,
string,
_defer,
exclamation,
arrow,
trait,
_program,
_module,
_import
);
var
classification: [128]TransitionClass;
transitions: [16]TransitionClasses;
proc initialize_classification();
var
i: Word;
begin
classification[1] := TransitionClass.eof; (* NUL *)
classification[2] := TransitionClass.invalid; (* SOH *)
classification[3] := TransitionClass.invalid; (* STX *)
classification[4] := TransitionClass.invalid; (* ETX *)
classification[5] := TransitionClass.invalid; (* EOT *)
classification[6] := TransitionClass.invalid; (* EMQ *)
classification[7] := TransitionClass.invalid; (* ACK *)
classification[8] := TransitionClass.invalid; (* BEL *)
classification[9] := TransitionClass.invalid; (* BS *)
classification[10] := TransitionClass.space; (* HT *)
classification[11] := TransitionClass.space; (* LF *)
classification[12] := TransitionClass.invalid; (* VT *)
classification[13] := TransitionClass.invalid; (* FF *)
classification[14] := TransitionClass.space; (* CR *)
classification[15] := TransitionClass.invalid; (* SO *)
classification[16] := TransitionClass.invalid; (* SI *)
classification[17] := TransitionClass.invalid; (* DLE *)
classification[18] := TransitionClass.invalid; (* DC1 *)
classification[19] := TransitionClass.invalid; (* DC2 *)
classification[20] := TransitionClass.invalid; (* DC3 *)
classification[21] := TransitionClass.invalid; (* DC4 *)
classification[22] := TransitionClass.invalid; (* NAK *)
classification[23] := TransitionClass.invalid; (* SYN *)
classification[24] := TransitionClass.invalid; (* ETB *)
classification[25] := TransitionClass.invalid; (* CAN *)
classification[26] := TransitionClass.invalid; (* EM *)
classification[27] := TransitionClass.invalid; (* SUB *)
classification[28] := TransitionClass.invalid; (* ESC *)
classification[29] := TransitionClass.invalid; (* FS *)
classification[30] := TransitionClass.invalid; (* GS *)
classification[31] := TransitionClass.invalid; (* RS *)
classification[32] := TransitionClass.invalid; (* US *)
classification[33] := TransitionClass.space; (* Space *)
classification[34] := TransitionClass.single; (* ! *)
classification[35] := TransitionClass.double_quote; (* " *)
classification[36] := TransitionClass.other; (* # *)
classification[37] := TransitionClass.other; (* $ *)
classification[38] := TransitionClass.single; (* % *)
classification[39] := TransitionClass.single; (* & *)
classification[40] := TransitionClass.single_quote; (* ' *)
classification[41] := TransitionClass.left_paren; (* ( *)
classification[42] := TransitionClass.right_paren; (* ) *)
classification[43] := TransitionClass.asterisk; (* * *)
classification[44] := TransitionClass.single; (* + *)
classification[45] := TransitionClass.single; (* , *)
classification[46] := TransitionClass.minus; (* - *)
classification[47] := TransitionClass.dot; (* . *)
classification[48] := TransitionClass.single; (* / *)
classification[49] := TransitionClass.zero; (* 0 *)
classification[50] := TransitionClass.digit; (* 1 *)
classification[51] := TransitionClass.digit; (* 2 *)
classification[52] := TransitionClass.digit; (* 3 *)
classification[53] := TransitionClass.digit; (* 4 *)
classification[54] := TransitionClass.digit; (* 5 *)
classification[55] := TransitionClass.digit; (* 6 *)
classification[56] := TransitionClass.digit; (* 7 *)
classification[57] := TransitionClass.digit; (* 8 *)
classification[58] := TransitionClass.digit; (* 9 *)
classification[59] := TransitionClass.colon; (* : *)
classification[60] := TransitionClass.single; (* ; *)
classification[61] := TransitionClass.less; (* < *)
classification[62] := TransitionClass.equals; (* = *)
classification[63] := TransitionClass.greater; (* > *)
classification[64] := TransitionClass.other; (* ? *)
classification[65] := TransitionClass.single; (* @ *)
classification[66] := TransitionClass.alpha; (* A *)
classification[67] := TransitionClass.alpha; (* B *)
classification[68] := TransitionClass.alpha; (* C *)
classification[69] := TransitionClass.alpha; (* D *)
classification[70] := TransitionClass.alpha; (* E *)
classification[71] := TransitionClass.alpha; (* F *)
classification[72] := TransitionClass.alpha; (* G *)
classification[73] := TransitionClass.alpha; (* H *)
classification[74] := TransitionClass.alpha; (* I *)
classification[75] := TransitionClass.alpha; (* J *)
classification[76] := TransitionClass.alpha; (* K *)
classification[77] := TransitionClass.alpha; (* L *)
classification[78] := TransitionClass.alpha; (* M *)
classification[79] := TransitionClass.alpha; (* N *)
classification[80] := TransitionClass.alpha; (* O *)
classification[81] := TransitionClass.alpha; (* P *)
classification[82] := TransitionClass.alpha; (* Q *)
classification[83] := TransitionClass.alpha; (* R *)
classification[84] := TransitionClass.alpha; (* S *)
classification[85] := TransitionClass.alpha; (* T *)
classification[86] := TransitionClass.alpha; (* U *)
classification[87] := TransitionClass.alpha; (* V *)
classification[88] := TransitionClass.alpha; (* W *)
classification[89] := TransitionClass.alpha; (* X *)
classification[90] := TransitionClass.alpha; (* Y *)
classification[91] := TransitionClass.alpha; (* Z *)
classification[92] := TransitionClass.single; (* [ *)
classification[93] := TransitionClass.other; (* \ *)
classification[94] := TransitionClass.single; (* ] *)
classification[95] := TransitionClass.single; (* ^ *)
classification[96] := TransitionClass.underscore; (* _ *)
classification[97] := TransitionClass.other; (* ` *)
classification[98] := TransitionClass.hex; (* a *)
classification[99] := TransitionClass.hex; (* b *)
classification[100] := TransitionClass.hex; (* c *)
classification[101] := TransitionClass.hex; (* d *)
classification[102] := TransitionClass.hex; (* e *)
classification[103] := TransitionClass.hex; (* f *)
classification[104] := TransitionClass.alpha; (* g *)
classification[105] := TransitionClass.alpha; (* h *)
classification[106] := TransitionClass.alpha; (* i *)
classification[107] := TransitionClass.alpha; (* j *)
classification[108] := TransitionClass.alpha; (* k *)
classification[109] := TransitionClass.alpha; (* l *)
classification[110] := TransitionClass.alpha; (* m *)
classification[111] := TransitionClass.alpha; (* n *)
classification[112] := TransitionClass.alpha; (* o *)
classification[113] := TransitionClass.alpha; (* p *)
classification[114] := TransitionClass.alpha; (* q *)
classification[115] := TransitionClass.alpha; (* r *)
classification[116] := TransitionClass.alpha; (* s *)
classification[117] := TransitionClass.alpha; (* t *)
classification[118] := TransitionClass.alpha; (* u *)
classification[119] := TransitionClass.alpha; (* v *)
classification[120] := TransitionClass.alpha; (* w *)
classification[121] := TransitionClass.x; (* x *)
classification[122] := TransitionClass.alpha; (* y *)
classification[123] := TransitionClass.alpha; (* z *)
classification[124] := TransitionClass.other; (* { *)
classification[125] := TransitionClass.single; (* | *)
classification[126] := TransitionClass.other; (* } *)
classification[127] := TransitionClass.single; (* ~ *)
classification[128] := TransitionClass.invalid; (* DEL *)
i := 129u;
while i <= 256u do
classification[i] := TransitionClass.other;
i := i + 1u
end
end;
proc compare_keyword(keyword: String, token_start: BufferPosition, token_end: ^Char) -> Bool;
var
result: Bool;
index: Word;
continue: Bool;
begin
index := 0u;
result := true;
continue := (index < keyword.length) & (token_start.iterator <> token_end);
while continue & result do
result := keyword[index] = token_start.iterator^
or cast(tolower(cast(keyword[index]: Int)): Char) = token_start.iterator^;
token_start.iterator := token_start.iterator + 1;
index := index + 1u;
continue := (index < keyword.length) & (token_start.iterator <> token_end)
end;
result := result & index = keyword.length;
return result & (token_start.iterator = token_end)
end;
(* Reached the end of file. *)
proc transition_action_eof(lexer: ^Lexer, token: ^LexerToken);
begin
token^.kind := LexerKind.unknown
end;
proc increment(position: ^BufferPosition);
begin
position^.iterator := position^.iterator + 1
end;
(* Add the character to the token currently read and advance to the next character. *)
proc transition_action_accumulate(lexer: ^Lexer, token: ^LexerToken);
begin
increment(@lexer^.current)
end;
(* The current character is not a part of the token. Finish the token already
* read. Don't advance to the next character. *)
proc transition_action_finalize(lexer: ^Lexer, token: ^LexerToken);
begin
if lexer^.start.iterator^ = ':' then
token^.kind := LexerKind.colon
end;
if lexer^.start.iterator^ = '>' then
token^.kind := LexerKind.greater_than
end;
if lexer^.start.iterator^ = '<' then
token^.kind := LexerKind.less_than
end;
if lexer^.start.iterator^ = '(' then
token^.kind := LexerKind.left_paren
end;
if lexer^.start.iterator^ = '-' then
token^.kind := LexerKind.minus
end;
if lexer^.start.iterator^ = '.' then
token^.kind := LexerKind.dot
end
end;
(* An action for tokens containing multiple characters. *)
proc transition_action_composite(lexer: ^Lexer, token: ^LexerToken);
begin
if lexer^.start.iterator^ = '<' then
if lexer^.current.iterator^ = '>' then
token^.kind := LexerKind.not_equal
end;
if lexer^.current.iterator^ = '=' then
token^.kind := LexerKind.less_equal
end
end;
if (lexer^.start.iterator^ = '>') & (lexer^.current.iterator^ = '=') then
token^.kind := LexerKind.greater_equal
end;
if (lexer^.start.iterator^ = ':') & (lexer^.current.iterator^ = '=') then
token^.kind := LexerKind.assignment
end;
if (lexer^.start.iterator^ = '-') & (lexer^.current.iterator^ = '>') then
token^.kind := LexerKind.arrow
end;
increment(@lexer^.current)
end;
(* Skip a space. *)
proc transition_action_skip(lexer: ^Lexer, token: ^LexerToken);
begin
increment(@lexer^.start);
if lexer^.start.iterator^ = '\n' then
lexer^.start.location.line := lexer^.start.location.line + 1u;
lexer^.start.location.column := 1u
end;
lexer^.current := lexer^.start
end;
(* Delimited string action. *)
proc transition_action_delimited(lexer: ^Lexer, token: ^LexerToken);
var
text_length: Word;
begin
if lexer^.start.iterator^ = '(' then
token^.kind := LexerKind.comment
end;
if lexer^.start.iterator^ = '"' then
text_length := cast(lexer^.current.iterator - lexer^.start.iterator + 1: Word);
token^.value.stringKind := String(cast(malloc(text_length): ^Char), text_length);
memcpy(cast(token^.value.stringKind.ptr: Pointer), cast(lexer^.start.iterator: Pointer), text_length);
token^.kind := LexerKind.character
end;
if lexer^.start.iterator^ = '\'' then
text_length := cast(lexer^.current.iterator - lexer^.start.iterator + 1: Word);
token^.value.stringKind := String(cast(malloc(text_length): ^Char), text_length);
memcpy(cast(token^.value.stringKind.ptr: Pointer), cast(lexer^.start.iterator: Pointer), text_length);
token^.kind := LexerKind.string
end;
increment(@lexer^.current)
end;
(* Finalize keyword or identifier. *)
proc transition_action_key_id(lexer: ^Lexer, token: ^LexerToken);
begin
token^.kind := LexerKind.identifier;
token^.value.identifierKind[1] := cast(lexer^.current.iterator - lexer^.start.iterator: Char);
memcpy(cast(@token^.value.identifierKind[2]: Pointer), cast(lexer^.start.iterator: Pointer), cast(token^.value.identifierKind[1]: Word));
if compare_keyword("program", lexer^.start, lexer^.current.iterator) then
token^.kind := LexerKind._program
end;
if compare_keyword("import", lexer^.start, lexer^.current.iterator) then
token^.kind := LexerKind._import
end;
if compare_keyword("const", lexer^.start, lexer^.current.iterator) then
token^.kind := LexerKind._const
end;
if compare_keyword("var", lexer^.start, lexer^.current.iterator) then
token^.kind := LexerKind._var
end;
if compare_keyword("if", lexer^.start, lexer^.current.iterator) then
token^.kind := LexerKind._if
end;
if compare_keyword("then", lexer^.start, lexer^.current.iterator) then
token^.kind := LexerKind._then
end;
if compare_keyword("elsif", lexer^.start, lexer^.current.iterator) then
token^.kind := LexerKind._elsif
end;
if compare_keyword("else", lexer^.start, lexer^.current.iterator) then
token^.kind := LexerKind._else
end;
if compare_keyword("while", lexer^.start, lexer^.current.iterator) then
token^.kind := LexerKind._while
end;
if compare_keyword("do", lexer^.start, lexer^.current.iterator) then
token^.kind := LexerKind._do
end;
if compare_keyword("proc", lexer^.start, lexer^.current.iterator) then
token^.kind := LexerKind._proc
end;
if compare_keyword("begin", lexer^.start, lexer^.current.iterator) then
token^.kind := LexerKind._begin
end;
if compare_keyword("end", lexer^.start, lexer^.current.iterator) then
token^.kind := LexerKind._end
end;
if compare_keyword("type", lexer^.start, lexer^.current.iterator) then
token^.kind := LexerKind._type
end;
if compare_keyword("record", lexer^.start, lexer^.current.iterator) then
token^.kind := LexerKind._record
end;
if compare_keyword("union", lexer^.start, lexer^.current.iterator) then
token^.kind := LexerKind._union
end;
if compare_keyword("NIL", lexer^.start, lexer^.current.iterator) then
token^.kind := LexerKind.null
end;
if compare_keyword("or", lexer^.start, lexer^.current.iterator) then
token^.kind := LexerKind._or
end;
if compare_keyword("return", lexer^.start, lexer^.current.iterator) then
token^.kind := LexerKind._return
end;
if compare_keyword("defer", lexer^.start, lexer^.current.iterator) then
token^.kind := LexerKind._defer
end;
if compare_keyword("TO", lexer^.start, lexer^.current.iterator) then
token^.kind := LexerKind.to
end;
if compare_keyword("CASE", lexer^.start, lexer^.current.iterator) then
token^.kind := LexerKind._case
end;
if compare_keyword("OF", lexer^.start, lexer^.current.iterator) then
token^.kind := LexerKind._of
end;
if compare_keyword("module", lexer^.start, lexer^.current.iterator) then
token^.kind := LexerKind._module
end;
if compare_keyword("xor", lexer^.start, lexer^.current.iterator) then
token^.kind := LexerKind._xor
end;
if compare_keyword("TRUE", lexer^.start, lexer^.current.iterator) then
token^.kind := LexerKind.boolean;
token^.value.booleanKind := true
end;
if compare_keyword("FALSE", lexer^.start, lexer^.current.iterator) then
token^.kind := LexerKind.boolean;
token^.value.booleanKind := false
end
end;
(* Action for tokens containing only one character. The character cannot be
* followed by other characters forming a composite token. *)
proc transition_action_single(lexer: ^Lexer, token: ^LexerToken);
begin
if lexer^.current.iterator^ = '&' then
token^.kind := LexerKind.and
end;
if lexer^.current.iterator^ = ';' then
token^.kind := LexerKind.semicolon
end;
if lexer^.current.iterator^ = ',' then
token^.kind := LexerKind.comma
end;
if lexer^.current.iterator^ = '~' then
token^.kind := LexerKind.not
end;
if lexer^.current.iterator^ = ')' then
token^.kind := LexerKind.right_paren
end;
if lexer^.current.iterator^ = '[' then
token^.kind := LexerKind.left_square
end;
if lexer^.current.iterator^ = ']' then
token^.kind := LexerKind.right_square
end;
if lexer^.current.iterator^ = '^' then
token^.kind := LexerKind.hat
end;
if lexer^.current.iterator^ = '=' then
token^.kind := LexerKind.equal
end;
if lexer^.current.iterator^ = '+' then
token^.kind := LexerKind.plus
end;
if lexer^.current.iterator^ = '*' then
token^.kind := LexerKind.multiplication
end;
if lexer^.current.iterator^ = '/' then
token^.kind := LexerKind.division
end;
if lexer^.current.iterator^ = '%' then
token^.kind := LexerKind.remainder
end;
if lexer^.current.iterator^ = '@' then
token^.kind := LexerKind.at
end;
if lexer^.current.iterator^ = '|' then
token^.kind := LexerKind.pipe
end;
increment(@lexer^.current)
end;
(* Handle an integer literal. *)
proc transition_action_integer(lexer: ^Lexer, token: ^LexerToken);
var
buffer: String;
integer_length: Word;
found: Bool;
begin
token^.kind := LexerKind.integer;
integer_length := cast(lexer^.current.iterator - lexer^.start.iterator: Word);
memset(cast(token^.value.identifierKind.ptr: Pointer), 0, #size(Identifier));
memcpy(cast(@token^.value.identifierKind[1]: Pointer), cast(lexer^.start.iterator: Pointer), integer_length);
token^.value.identifierKind[cast(token^.value.identifierKind[1]: Int) + 2] := '\0';
token^.value.integerKind := atoi(@token^.value.identifierKind[2])
end;
proc set_default_transition(current_state: TransitionState, default_action: TransitionAction, next_state: TransitionState) -> Int;
var
default_transition: Transition;
state_index: Int;
begin
default_transition.action := default_action;
default_transition.next_state := next_state;
state_index := cast(current_state: Int) + 1;
transitions[state_index][cast(TransitionClass.invalid: Int) + 1] := default_transition;
transitions[state_index][cast(TransitionClass.digit: Int) + 1] := default_transition;
transitions[state_index][cast(TransitionClass.alpha: Int) + 1] := default_transition;
transitions[state_index][cast(TransitionClass.space: Int) + 1] := default_transition;
transitions[state_index][cast(TransitionClass.colon: Int) + 1] := default_transition;
transitions[state_index][cast(TransitionClass.equals: Int) + 1] := default_transition;
transitions[state_index][cast(TransitionClass.left_paren: Int) + 1] := default_transition;
transitions[state_index][cast(TransitionClass.right_paren: Int) + 1] := default_transition;
transitions[state_index][cast(TransitionClass.asterisk: Int) + 1] := default_transition;
transitions[state_index][cast(TransitionClass.underscore: Int) + 1] := default_transition;
transitions[state_index][cast(TransitionClass.single: Int) + 1] := default_transition;
transitions[state_index][cast(TransitionClass.hex: Int) + 1] := default_transition;
transitions[state_index][cast(TransitionClass.zero: Int) + 1] := default_transition;
transitions[state_index][cast(TransitionClass.x: Int) + 1] := default_transition;
transitions[state_index][cast(TransitionClass.eof: Int) + 1] := default_transition;
transitions[state_index][cast(TransitionClass.dot: Int) + 1] := default_transition;
transitions[state_index][cast(TransitionClass.minus: Int) + 1] := default_transition;
transitions[state_index][cast(TransitionClass.single_quote: Int) + 1] := default_transition;
transitions[state_index][cast(TransitionClass.double_quote: Int) + 1] := default_transition;
transitions[state_index][cast(TransitionClass.greater: Int) + 1] := default_transition;
transitions[state_index][cast(TransitionClass.less: Int) + 1] := default_transition;
transitions[state_index][cast(TransitionClass.other: Int) + 1] := default_transition;
return state_index
end;
(*
* The transition table describes transitions from one state to another, given
* a symbol (character class).
*
* The table has m rows and n columns, where m is the amount of states and n is
* the amount of classes. So given the current state and a classified character
* the table can be used to look up the next state.
*
* Each cell is a word long.
* - The least significant byte of the word is a row number (beginning with 0).
* It specifies the target state. "ff" means that this is an end state and no
* transition is possible.
* - The next byte is the action that should be performed when transitioning.
* For the meaning of actions see labels in the lex_next function, which
* handles each action.
*)
proc initialize_transitions();
var
state_index: Int;
begin
(* Start state. *)
state_index := cast(TransitionState.start: Int) + 1;
transitions[state_index][cast(TransitionClass.invalid: Int) + 1].action := nil;
transitions[state_index][cast(TransitionClass.invalid: Int) + 1].next_state := TransitionState.finish;
transitions[state_index][cast(TransitionClass.digit: Int) + 1].action := transition_action_accumulate;
transitions[state_index][cast(TransitionClass.digit: Int) + 1].next_state := TransitionState.decimal;
transitions[state_index][cast(TransitionClass.alpha: Int) + 1].action := transition_action_accumulate;
transitions[state_index][cast(TransitionClass.alpha: Int) + 1].next_state := TransitionState.identifier;
transitions[state_index][cast(TransitionClass.space: Int) + 1].action := transition_action_skip;
transitions[state_index][cast(TransitionClass.space: Int) + 1].next_state := TransitionState.start;
transitions[state_index][cast(TransitionClass.colon: Int) + 1].action := transition_action_accumulate;
transitions[state_index][cast(TransitionClass.colon: Int) + 1].next_state := TransitionState.colon;
transitions[state_index][cast(TransitionClass.equals: Int) + 1].action := transition_action_single;
transitions[state_index][cast(TransitionClass.equals: Int) + 1].next_state := TransitionState.finish;
transitions[state_index][cast(TransitionClass.left_paren: Int) + 1].action := transition_action_accumulate;
transitions[state_index][cast(TransitionClass.left_paren: Int) + 1].next_state := TransitionState.left_paren;
transitions[state_index][cast(TransitionClass.right_paren: Int) + 1].action := transition_action_single;
transitions[state_index][cast(TransitionClass.right_paren: Int) + 1].next_state := TransitionState.finish;
transitions[state_index][cast(TransitionClass.asterisk: Int) + 1].action := transition_action_single;
transitions[state_index][cast(TransitionClass.asterisk: Int) + 1].next_state := TransitionState.finish;
transitions[state_index][cast(TransitionClass.underscore: Int) + 1].action := transition_action_accumulate;
transitions[state_index][cast(TransitionClass.underscore: Int) + 1].next_state := TransitionState.identifier;
transitions[state_index][cast(TransitionClass.single: Int) + 1].action := transition_action_single;
transitions[state_index][cast(TransitionClass.single: Int) + 1].next_state := TransitionState.finish;
transitions[state_index][cast(TransitionClass.hex: Int) + 1].action := transition_action_accumulate;
transitions[state_index][cast(TransitionClass.hex: Int) + 1].next_state := TransitionState.identifier;
transitions[state_index][cast(TransitionClass.zero: Int) + 1].action := transition_action_accumulate;
transitions[state_index][cast(TransitionClass.zero: Int) + 1].next_state := TransitionState.leading_zero;
transitions[state_index][cast(TransitionClass.x: Int) + 1].action := transition_action_accumulate;
transitions[state_index][cast(TransitionClass.x: Int) + 1].next_state := TransitionState.identifier;
transitions[state_index][cast(TransitionClass.eof: Int) + 1].action := transition_action_eof;
transitions[state_index][cast(TransitionClass.eof: Int) + 1].next_state := TransitionState.finish;
transitions[state_index][cast(TransitionClass.dot: Int) + 1].action := transition_action_accumulate;
transitions[state_index][cast(TransitionClass.dot: Int) + 1].next_state := TransitionState.dot;
transitions[state_index][cast(TransitionClass.minus: Int) + 1].action := transition_action_accumulate;
transitions[state_index][cast(TransitionClass.minus: Int) + 1].next_state := TransitionState.minus;
transitions[state_index][cast(TransitionClass.single_quote: Int) + 1].action := transition_action_accumulate;
transitions[state_index][cast(TransitionClass.single_quote: Int) + 1].next_state := TransitionState.character;
transitions[state_index][cast(TransitionClass.double_quote: Int) + 1].action := transition_action_accumulate;
transitions[state_index][cast(TransitionClass.double_quote: Int) + 1].next_state := TransitionState.string;
transitions[state_index][cast(TransitionClass.greater: Int) + 1].action := transition_action_accumulate;
transitions[state_index][cast(TransitionClass.greater: Int) + 1].next_state := TransitionState.greater;
transitions[state_index][cast(TransitionClass.less: Int) + 1].action := transition_action_accumulate;
transitions[state_index][cast(TransitionClass.less: Int) + 1].next_state := TransitionState.less;
transitions[state_index][cast(TransitionClass.other: Int) + 1].action := nil;
transitions[state_index][cast(TransitionClass.other: Int) + 1].next_state := TransitionState.finish;
(* Colon state. *)
state_index := set_default_transition(TransitionState.colon, transition_action_finalize, TransitionState.finish);
transitions[state_index][cast(TransitionClass.equals: Int) + 1].action := transition_action_composite;
transitions[state_index][cast(TransitionClass.equals: Int) + 1].next_state := TransitionState.finish;
(* Identifier state. *)
state_index := set_default_transition(TransitionState.identifier, transition_action_key_id, TransitionState.finish);
transitions[state_index][cast(TransitionClass.digit: Int) + 1].action := transition_action_accumulate;
transitions[state_index][cast(TransitionClass.digit: Int) + 1].next_state := TransitionState.identifier;
transitions[state_index][cast(TransitionClass.alpha: Int) + 1].action := transition_action_accumulate;
transitions[state_index][cast(TransitionClass.alpha: Int) + 1].next_state := TransitionState.identifier;
transitions[state_index][cast(TransitionClass.underscore: Int) + 1].action := transition_action_accumulate;
transitions[state_index][cast(TransitionClass.underscore: Int) + 1].next_state := TransitionState.identifier;
transitions[state_index][cast(TransitionClass.hex: Int) + 1].action := transition_action_accumulate;
transitions[state_index][cast(TransitionClass.hex: Int) + 1].next_state := TransitionState.identifier;
transitions[state_index][cast(TransitionClass.zero: Int) + 1].action := transition_action_accumulate;
transitions[state_index][cast(TransitionClass.zero: Int) + 1].next_state := TransitionState.identifier;
transitions[state_index][cast(TransitionClass.x: Int) + 1].action := transition_action_accumulate;
transitions[state_index][cast(TransitionClass.x: Int) + 1].next_state := TransitionState.identifier;
(* Decimal state. *)
state_index := set_default_transition(TransitionState.decimal, transition_action_integer, TransitionState.finish);
transitions[state_index][cast(TransitionClass.digit: Int) + 1].action := transition_action_accumulate;
transitions[state_index][cast(TransitionClass.digit: Int) + 1].next_state := TransitionState.decimal;
transitions[state_index][cast(TransitionClass.alpha: Int) + 1].action := transition_action_accumulate;
transitions[state_index][cast(TransitionClass.alpha: Int) + 1].next_state := TransitionState.decimal_suffix;
transitions[state_index][cast(TransitionClass.underscore: Int) + 1].action := nil;
transitions[state_index][cast(TransitionClass.underscore: Int) + 1].next_state := TransitionState.finish;
transitions[state_index][cast(TransitionClass.hex: Int) + 1].action := transition_action_accumulate;
transitions[state_index][cast(TransitionClass.hex: Int) + 1].next_state := TransitionState.decimal_suffix;
transitions[state_index][cast(TransitionClass.zero: Int) + 1].action := transition_action_accumulate;
transitions[state_index][cast(TransitionClass.zero: Int) + 1].next_state := TransitionState.decimal;
transitions[state_index][cast(TransitionClass.x: Int) + 1].action := transition_action_accumulate;
transitions[state_index][cast(TransitionClass.x: Int) + 1].next_state := TransitionState.decimal_suffix;
(* Greater state. *)
state_index := set_default_transition(TransitionState.greater, transition_action_finalize, TransitionState.finish);
transitions[state_index][cast(TransitionClass.equals: Int) + 1].action := transition_action_composite;
transitions[state_index][cast(TransitionClass.equals: Int) + 1].next_state := TransitionState.finish;
(* Minus state. *)
state_index := set_default_transition(TransitionState.minus, transition_action_finalize, TransitionState.finish);
transitions[state_index][cast(TransitionClass.greater: Int) + 1].action := transition_action_composite;
transitions[state_index][cast(TransitionClass.greater: Int) + 1].next_state := TransitionState.finish;
(* Left paren state. *)
state_index := set_default_transition(TransitionState.left_paren, transition_action_finalize, TransitionState.finish);
transitions[state_index][cast(TransitionClass.asterisk: Int) + 1].action := transition_action_accumulate;
transitions[state_index][cast(TransitionClass.asterisk: Int) + 1].next_state := TransitionState.comment;
(* Less state. *)
state_index := set_default_transition(TransitionState.less, transition_action_finalize, TransitionState.finish);
transitions[state_index][cast(TransitionClass.equals: Int) + 1].action := transition_action_composite;
transitions[state_index][cast(TransitionClass.equals: Int) + 1].next_state := TransitionState.finish;
transitions[state_index][cast(TransitionClass.greater: Int) + 1].action := transition_action_composite;
transitions[state_index][cast(TransitionClass.greater: Int) + 1].next_state := TransitionState.finish;
(* Hexadecimal after 0x. *)
state_index := set_default_transition(TransitionState.dot, transition_action_finalize, TransitionState.finish);
transitions[state_index][cast(TransitionClass.dot: Int) + 1].action := transition_action_composite;
transitions[state_index][cast(TransitionClass.dot: Int) + 1].next_state := TransitionState.finish;
(* Comment. *)
state_index := set_default_transition(TransitionState.comment, transition_action_accumulate, TransitionState.comment);
transitions[state_index][cast(TransitionClass.asterisk: Int) + 1].action := transition_action_accumulate;
transitions[state_index][cast(TransitionClass.asterisk: Int) + 1].next_state := TransitionState.closing_comment;
transitions[state_index][cast(TransitionClass.eof: Int) + 1].action := nil;
transitions[state_index][cast(TransitionClass.eof: Int) + 1].next_state := TransitionState.finish;
(* Closing comment. *)
state_index := set_default_transition(TransitionState.closing_comment, transition_action_accumulate, TransitionState.comment);
transitions[state_index][cast(TransitionClass.invalid: Int) + 1].action := nil;
transitions[state_index][cast(TransitionClass.invalid: Int) + 1].next_state := TransitionState.finish;
transitions[state_index][cast(TransitionClass.right_paren: Int) + 1].action := transition_action_delimited;
transitions[state_index][cast(TransitionClass.right_paren: Int) + 1].next_state := TransitionState.finish;
transitions[state_index][cast(TransitionClass.asterisk: Int) + 1].action := transition_action_accumulate;
transitions[state_index][cast(TransitionClass.asterisk: Int) + 1].next_state := TransitionState.closing_comment;
transitions[state_index][cast(TransitionClass.eof: Int) + 1].action := nil;
transitions[state_index][cast(TransitionClass.eof: Int) + 1].next_state := TransitionState.finish;
(* Character. *)
state_index := set_default_transition(TransitionState.character, transition_action_accumulate, TransitionState.character);
transitions[state_index][cast(TransitionClass.invalid: Int) + 1].action := nil;
transitions[state_index][cast(TransitionClass.invalid: Int) + 1].next_state := TransitionState.finish;
transitions[state_index][cast(TransitionClass.eof: Int) + 1].action := nil;
transitions[state_index][cast(TransitionClass.eof: Int) + 1].next_state := TransitionState.finish;
transitions[state_index][cast(TransitionClass.single_quote: Int) + 1].action := transition_action_delimited;
transitions[state_index][cast(TransitionClass.single_quote: Int) + 1].next_state := TransitionState.finish;
(* String. *)
state_index := set_default_transition(TransitionState.string, transition_action_accumulate, TransitionState.string);
transitions[state_index][cast(TransitionClass.invalid: Int) + 1].action := nil;
transitions[state_index][cast(TransitionClass.invalid: Int) + 1].next_state := TransitionState.finish;
transitions[state_index][cast(TransitionClass.eof: Int) + 1].action := nil;
transitions[state_index][cast(TransitionClass.eof: Int) + 1].next_state := TransitionState.finish;
transitions[state_index][cast(TransitionClass.double_quote: Int) + 1].action := transition_action_delimited;
transitions[state_index][cast(TransitionClass.double_quote: Int) + 1].next_state := TransitionState.finish;
(* Leading zero. *)
state_index := set_default_transition(TransitionState.leading_zero, transition_action_integer, TransitionState.finish);
transitions[state_index][cast(TransitionClass.digit: Int) + 1].action := nil;
transitions[state_index][cast(TransitionClass.digit: Int) + 1].next_state := TransitionState.finish;
transitions[state_index][cast(TransitionClass.alpha: Int) + 1].action := nil;
transitions[state_index][cast(TransitionClass.alpha: Int) + 1].next_state := TransitionState.finish;
transitions[state_index][cast(TransitionClass.underscore: Int) + 1].action := nil;
transitions[state_index][cast(TransitionClass.underscore: Int) + 1].next_state := TransitionState.finish;
transitions[state_index][cast(TransitionClass.hex: Int) + 1].action := nil;
transitions[state_index][cast(TransitionClass.hex: Int) + 1].next_state := TransitionState.finish;
transitions[state_index][cast(TransitionClass.zero: Int) + 1].action := nil;
transitions[state_index][cast(TransitionClass.zero: Int) + 1].next_state := TransitionState.finish;
transitions[state_index][cast(TransitionClass.x: Int) + 1].action := nil;
transitions[state_index][cast(TransitionClass.x: Int) + 1].next_state := TransitionState.finish;
(* Digit with a character suffix. *)
state_index := set_default_transition(TransitionState.decimal_suffix, transition_action_integer, TransitionState.finish);
transitions[state_index][cast(TransitionClass.alpha: Int) + 1].action := nil;
transitions[state_index][cast(TransitionClass.alpha: Int) + 1].next_state := TransitionState.finish;
transitions[state_index][cast(TransitionClass.digit: Int) + 1].action := nil;
transitions[state_index][cast(TransitionClass.digit: Int) + 1].next_state := TransitionState.finish;
transitions[state_index][cast(TransitionClass.hex: Int) + 1].action := nil;
transitions[state_index][cast(TransitionClass.hex: Int) + 1].next_state := TransitionState.finish;
transitions[state_index][cast(TransitionClass.zero: Int) + 1].action := nil;
transitions[state_index][cast(TransitionClass.zero: Int) + 1].next_state := TransitionState.finish;
transitions[state_index][cast(TransitionClass.x: Int) + 1].action := nil;
transitions[state_index][cast(TransitionClass.x: Int) + 1].next_state := TransitionState.finish
end;
proc lexer_make*(lexer: ^Lexer, input: ^FILE);
begin
lexer^.input := input;
lexer^.length := 0u;
lexer^.buffer := cast(malloc(CHUNK_SIZE): ^Char);
memset(cast(lexer^.buffer: Pointer), 0, CHUNK_SIZE);
lexer^.size := CHUNK_SIZE
end;
(* Returns the last read token. *)
proc lexer_current*(lexer: ^Lexer) -> LexerToken;
var
current_class: TransitionClass;
current_state: TransitionState;
current_transition: Transition;
result: LexerToken;
index1: Word;
index2: Word;
begin
lexer^.current := lexer^.start;
current_state := TransitionState.start;
while current_state <> TransitionState.finish do
index1 := cast(lexer^.current.iterator^: Word) + 1u;
current_class := classification[index1];
index1 := cast(current_state: Word) + 1u;
index2 := cast(current_class: Word) + 1u;
current_transition := transitions[index1][index2];
if current_transition.action <> nil then
current_transition.action(lexer, @result)
end;
current_state := current_transition.next_state
end;
result.start_location := lexer^.start.location;
result.end_location := lexer^.current.location;
return result
end;
(* Read and return the next token. *)
proc lexer_lex*(lexer: ^Lexer) -> LexerToken;
var
result: LexerToken;
begin
if lexer^.length = 0u then
lexer^.length := fread(cast(lexer^.buffer: Pointer), CHUNK_SIZE, 1u, lexer^.input);
lexer^.current.location.column := 1u;
lexer^.current.location.line := 1u;
lexer^.current.iterator := lexer^.buffer
end;
lexer^.start := lexer^.current;
result := lexer_current(lexer);
return result
end;
proc lexer_destroy*(lexer: ^Lexer);
begin
free(cast(lexer^.buffer: Pointer))
end;
proc lexer_initialize();
begin
initialize_classification();
initialize_transitions()
end;
end.

View File

@@ -1,841 +0,0 @@
(* This Source Code Form is subject to the terms of the Mozilla Public License,
v. 2.0. If a copy of the MPL was not distributed with this file, You can
obtain one at https://mozilla.org/MPL/2.0/. *)
program;
import cstdio, cctype, common, command_line_interface, lexer;
type
SourceFile* = record
buffer: [1024]Char;
handle: ^FILE;
size: Word;
index: Word
end;
StringBuffer* = record
data: Pointer;
size: Word;
capacity: Word
end;
SourceCode = record
position: TextLocation;
input: Pointer;
empty: proc(Pointer) -> Bool;
advance: proc(Pointer);
head: proc(Pointer) -> Char
end;
Token* = record
kind: LexerKind;
value: union
int_value: Int;
string: String;
boolean_value: Bool;
char_value: Char
end
end;
Tokenizer* = record
length: Word;
data: ^Token
end;
(*
Standard procedures.
*)
proc reallocarray(ptr: Pointer, n: Word, size: Word) -> Pointer;
return realloc(ptr, n * size)
end;
proc substring(string: String, start: Word, count: Word) -> String;
return String(string.ptr + start, count)
end;
proc open_substring(string: String, start: Word) -> String;
return substring(string, start, string.length - start)
end;
proc string_dup(origin: String) -> String;
var
copy: ^Char;
begin
copy := cast(malloc(origin.length): ^Char);
strncpy(copy, origin.ptr, origin.length);
return String(copy, origin.length)
end;
proc string_buffer_new() -> StringBuffer;
var
result: StringBuffer;
begin
result.capacity := 64u;
result.data := malloc(result.capacity);
result.size := 0u;
return result
end;
proc string_buffer_push(buffer: ^StringBuffer, char: Char);
begin
if buffer^.size >= buffer^.capacity then
buffer^.capacity := buffer^.capacity + 1024u;
buffer^.data := realloc(buffer^.data, buffer^.capacity)
end;
cast(buffer^.data + buffer^.size: ^Char)^ := cast(char: Char);
buffer^.size := buffer^.size + 1u
end;
proc string_buffer_pop(buffer: ^StringBuffer, count: Word);
begin
buffer^.size := buffer^.size - count
end;
proc string_buffer_clear(buffer: ^StringBuffer) -> String;
var
result: String;
begin
result := String(cast(buffer^.data: ^Char), buffer^.size);
buffer^.size := 0u;
return result
end;
(*
Source code stream procedures.
*)
proc read_source(filename: ^Char) -> ^SourceFile;
var
result: ^SourceFile;
file_handle: ^FILE;
begin
file_handle := fopen(filename, "rb\0".ptr);
if file_handle <> nil then
result := cast(malloc(#size(SourceFile)): ^SourceFile);
result^.handle := file_handle;
result^.size := 0u;
result^.index := 1u
end;
return result
end;
proc source_file_empty(source_input: Pointer) -> Bool;
var
source_file: ^SourceFile;
begin
source_file := cast(source_input: ^SourceFile);
if source_file^.index > source_file^.size then
source_file^.size := fread(cast(@source_file^.buffer: Pointer), 1u, 1024u, source_file^.handle);
source_file^.index := 1u
end;
return source_file^.size = 0u
end;
proc source_file_head(source_input: Pointer) -> Char;
var
source_file: ^SourceFile;
begin
source_file := cast(source_input: ^SourceFile);
return source_file^.buffer[source_file^.index]
end;
proc source_file_advance(source_input: Pointer);
var
source_file: ^SourceFile;
begin
source_file := cast(source_input: ^SourceFile);
source_file^.index := source_file^.index + 1u
end;
proc source_code_empty(source_code: ^SourceCode) -> Bool;
return source_code^.empty(source_code^.input)
end;
proc source_code_head(source_code: SourceCode) -> Char;
return source_code.head(source_code.input)
end;
proc source_code_advance(source_code: ^SourceCode);
begin
source_code^.advance(source_code^.input);
source_code^.position.column := source_code^.position.column
end;
proc source_code_break(source_code: ^SourceCode);
begin
source_code^.position.line := source_code^.position.line + 1u;
source_code^.position.column := 0u
end;
proc source_code_expect(source_code: ^SourceCode, expected: Char) -> Bool;
return ~source_code_empty(source_code) & source_code_head(source_code^) = expected
end;
(*
Token procedures.
*)
proc lexer_escape(escape: Char, result: ^Char) -> Bool;
var
successful: Bool;
begin
case escape of
'n':
result^ := '\n';
successful := true
| 'a':
result^ := '\a';
successful := true
| 'b':
result^ := '\b';
successful := true
| 't':
result^ := '\t';
successful := true
| 'f':
result^ := '\f';
successful := true
| 'r':
result^ := '\r';
successful := true
| 'v':
result^ := '\v';
successful := true
| '\\':
result^ := '\\';
successful := true
| '\'':
result^ := '\'';
successful := true
| '"':
result^ := '"';
successful := true
| '?':
result^ := '\?';
successful := true
| '0':
result^ := '\0';
successful := true
else
successful := false
end;
return successful
end;
(* Skip spaces. *)
proc lexer_spaces(source_code: ^SourceCode);
var
current: Char;
begin
while ~source_code_empty(source_code) & isspace(cast(source_code_head(source_code^): Int)) <> 0 do
current := source_code_head(source_code^);
if current = '\n' then
source_code_break(source_code)
end;
source_code_advance(source_code)
end
end;
(* Checker whether the character is allowed in an identificator. *)
proc lexer_is_ident(char: Char) -> Bool;
return isalnum(cast(char: Int)) <> 0 or char = '_'
end;
proc lexer_identifier(source_code: ^SourceCode, token_content: ^StringBuffer);
var
content_length: Word;
begin
while ~source_code_empty(source_code) & lexer_is_ident(source_code_head(source_code^)) do
string_buffer_push(token_content, source_code_head(source_code^));
source_code_advance(source_code)
end
end;
proc lexer_comment(source_code: ^SourceCode, token_content: ^StringBuffer) -> Bool;
var
trailing: Word;
begin
trailing := 0u;
while ~source_code_empty(source_code) & trailing < 2u do
if source_code_head(source_code^) = '*' then
string_buffer_push(token_content, '*');
trailing := 1u
elsif source_code_head(source_code^) = ')' & trailing = 1u then
string_buffer_pop(token_content, 1u);
trailing := 2u
else
string_buffer_push(token_content, source_code_head(source_code^));
trailing := 0u
end;
source_code_advance(source_code)
end;
return trailing = 2u
end;
proc lexer_character(source_code: ^SourceCode, token_content: ^Char) -> Bool;
var
successful: Bool;
begin
successful := ~source_code_empty(source_code);
if successful then
if source_code_head(source_code^) = '\\' then
source_code_advance(source_code);
successful := ~source_code_empty(source_code) & lexer_escape(source_code_head(source_code^), token_content)
else
token_content^ := source_code_head(source_code^);
successful := true
end
end;
if successful then
source_code_advance(source_code)
end;
return successful
end;
proc lexer_string(source_code: ^SourceCode, token_content: ^StringBuffer) -> Bool;
var
token_end, constructed_string: ^Char;
token_length: Word;
is_valid: Bool := true;
next_char: Char;
begin
while is_valid & ~source_code_empty(source_code) & source_code_head(source_code^) <> '"' do
is_valid := lexer_character(source_code, @next_char);
if is_valid then
string_buffer_push(token_content, next_char)
end
end;
if is_valid & source_code_expect(source_code, '"') then
source_code_advance(source_code)
else
is_valid := false
end;
return is_valid
end;
proc lexer_number(source_code: ^SourceCode, token_content: ^Int);
begin
token_content^ := 0;
while ~source_code_empty(source_code) & isdigit(cast(source_code_head(source_code^): Int)) <> 0 do
token_content^ := token_content^ * 10 + (cast(source_code_head(source_code^): Int) - cast('0': Int));
source_code_advance(source_code)
end
end;
(* Categorize an identifier. *)
proc lexer_categorize(token_content: String) -> Token;
var
current_token: Token;
begin
if token_content = "if" then
current_token.kind := LexerKind._if
elsif token_content = "then" then
current_token.kind := LexerKind._then
elsif token_content = "else" then
current_token.kind := LexerKind._else
elsif token_content = "elsif" then
current_token.kind := LexerKind._elsif
elsif token_content = "while" then
current_token.kind := LexerKind._while
elsif token_content = "do" then
current_token.kind := LexerKind._do
elsif token_content = "proc" then
current_token.kind := LexerKind._proc
elsif token_content = "begin" then
current_token.kind := LexerKind._begin
elsif token_content = "end" then
current_token.kind := LexerKind._end
elsif token_content = "extern" then
current_token.kind := LexerKind._extern
elsif token_content = "const" then
current_token.kind := LexerKind._const
elsif token_content = "var" then
current_token.kind := LexerKind._var
elsif token_content = "case" then
current_token.kind := LexerKind._case
elsif token_content = "of" then
current_token.kind := LexerKind._of
elsif token_content = "type" then
current_token.kind := LexerKind._type
elsif token_content = "record" then
current_token.kind := LexerKind._record
elsif token_content = "union" then
current_token.kind := LexerKind._union
elsif token_content = "true" then
current_token.kind := LexerKind.boolean;
current_token.value.boolean_value := true
elsif token_content = "false" then
current_token.kind := LexerKind.boolean;
current_token.value.boolean_value := false
elsif token_content = "nil" then
current_token.kind := LexerKind.null
elsif token_content = "or" then
current_token.kind := LexerKind._or
elsif token_content = "return" then
current_token.kind := LexerKind._return
elsif token_content = "cast" then
current_token.kind := LexerKind._cast
elsif token_content = "defer" then
current_token.kind := LexerKind._defer
elsif token_content = "program" then
current_token.kind := LexerKind._program
elsif token_content = "module" then
current_token.kind := LexerKind._module
elsif token_content = "import" then
current_token.kind := LexerKind._import
else
current_token.kind := LexerKind.identifier;
current_token.value.string := string_dup(token_content)
end;
return current_token
end;
proc lexer_add_token(lexer: ^Tokenizer, token: Token);
var
new_length: Word;
begin
new_length := lexer^.length + 1u;
lexer^.data := cast(reallocarray(cast(lexer^.data: Pointer), new_length, #size(Token)): ^Token);
(lexer^.data + lexer^.length)^ := token;
lexer^.length := new_length
end;
(* Read the next token from the input. *)
proc lexer_next(source_code: SourceCode, token_buffer: ^StringBuffer) -> Token;
var
current_token: Token;
first_char: Char;
begin
current_token.kind := LexerKind.unknown;
first_char := source_code_head(source_code);
if isalpha(cast(first_char: Int)) <> 0 or first_char = '_' then
lexer_identifier(@source_code, token_buffer);
current_token := lexer_categorize(string_buffer_clear(token_buffer))
elsif first_char = '#' then
source_code_advance(@source_code);
lexer_identifier(@source_code, token_buffer);
current_token.kind := LexerKind.trait;
current_token.value.string := string_dup(string_buffer_clear(token_buffer))
elsif isdigit(cast(first_char: Int)) <> 0 then
lexer_number(@source_code, @current_token.value.int_value);
if source_code_expect(@source_code, 'u') then
current_token.kind := LexerKind.word;
source_code_advance(@source_code)
else
current_token.kind := LexerKind.integer
end
elsif first_char = '(' then
source_code_advance(@source_code);
if source_code_empty(@source_code) then
current_token.kind := LexerKind.left_paren
elsif source_code_head(source_code) = '*' then
source_code_advance(@source_code);
if lexer_comment(@source_code, token_buffer) then
current_token.value.string := string_dup(string_buffer_clear(token_buffer));
current_token.kind := LexerKind.comment
else
current_token.kind := LexerKind.unknown
end
else
current_token.kind := LexerKind.left_paren
end
elsif first_char = ')' then
current_token.kind := LexerKind.right_paren;
source_code_advance(@source_code)
elsif first_char = '\'' then
source_code_advance(@source_code);
if lexer_character(@source_code, @current_token.value.char_value) & source_code_expect(@source_code, '\'') then
current_token.kind := LexerKind.character;
source_code_advance(@source_code)
else
current_token.kind := LexerKind.unknown
end
elsif first_char = '"' then
source_code_advance(@source_code);
if lexer_string(@source_code, token_buffer) then
current_token.kind := LexerKind.string;
current_token.value.string := string_dup(string_buffer_clear(token_buffer))
else
current_token.kind := LexerKind.unknown
end
elsif first_char = '[' then
current_token.kind := LexerKind.left_square;
source_code_advance(@source_code)
elsif first_char = ']' then
current_token.kind := LexerKind.right_square;
source_code_advance(@source_code)
elsif first_char = '>' then
source_code_advance(@source_code);
if source_code_empty(@source_code) then
current_token.kind := LexerKind.greater_than
elsif source_code_head(source_code) = '=' then
current_token.kind := LexerKind.greater_equal;
source_code_advance(@source_code)
elsif source_code_head(source_code) = '>' then
current_token.kind := LexerKind.shift_right;
source_code_advance(@source_code)
else
current_token.kind := LexerKind.greater_than
end
elsif first_char = '<' then
source_code_advance(@source_code);
if source_code_empty(@source_code) then
current_token.kind := LexerKind.less_than
elsif source_code_head(source_code) = '=' then
current_token.kind := LexerKind.less_equal;
source_code_advance(@source_code)
elsif source_code_head(source_code) = '<' then
current_token.kind := LexerKind.shift_left;
source_code_advance(@source_code)
elsif source_code_head(source_code) = '>' then
current_token.kind := LexerKind.not_equal;
source_code_advance(@source_code)
else
current_token.kind := LexerKind.less_than
end
elsif first_char = '=' then
current_token.kind := LexerKind.equal;
source_code_advance(@source_code)
elsif first_char = ';' then
current_token.kind := LexerKind.semicolon;
source_code_advance(@source_code)
elsif first_char = '.' then
current_token.kind := LexerKind.dot;
source_code_advance(@source_code)
elsif first_char = ',' then
current_token.kind := LexerKind.comma;
source_code_advance(@source_code)
elsif first_char = '+' then
current_token.kind := LexerKind.plus;
source_code_advance(@source_code)
elsif first_char = '-' then
source_code_advance(@source_code);
if source_code_empty(@source_code) then
current_token.kind := LexerKind.minus
elsif source_code_head(source_code) = '>' then
current_token.kind := LexerKind.arrow;
source_code_advance(@source_code)
else
current_token.kind := LexerKind.minus
end
elsif first_char = '*' then
current_token.kind := LexerKind.multiplication;
source_code_advance(@source_code)
elsif first_char = '/' then
current_token.kind := LexerKind.division;
source_code_advance(@source_code)
elsif first_char = '%' then
current_token.kind := LexerKind.remainder;
source_code_advance(@source_code)
elsif first_char = ':' then
source_code_advance(@source_code);
if source_code_empty(@source_code) then
current_token.kind := LexerKind.colon
elsif source_code_head(source_code) = '=' then
current_token.kind := LexerKind.assignment;
source_code_advance(@source_code)
else
current_token.kind := LexerKind.colon
end
elsif first_char = '^' then
current_token.kind := LexerKind.hat;
source_code_advance(@source_code)
elsif first_char = '@' then
current_token.kind := LexerKind.at;
source_code_advance(@source_code)
elsif first_char = '!' then
current_token.kind := LexerKind.exclamation;
source_code_advance(@source_code)
elsif first_char = '&' then
current_token.kind := LexerKind.and;
source_code_advance(@source_code)
elsif first_char = '~' then
current_token.kind := LexerKind.not;
source_code_advance(@source_code)
elsif first_char = '|' then
current_token.kind := LexerKind.pipe;
source_code_advance(@source_code)
else
current_token.kind := LexerKind.unknown;
source_code_advance(@source_code)
end;
return current_token
end;
(* Split the source text into tokens. *)
proc lexer_text(source_code: SourceCode) -> Tokenizer;
var
current_token: Token;
token_buffer: StringBuffer;
lexer: Tokenizer;
begin
lexer := Tokenizer(0u, nil);
token_buffer := string_buffer_new();
lexer_spaces(@source_code);
while ~source_code_empty(@source_code) do
current_token := lexer_next(source_code, @token_buffer);
if current_token.kind <> LexerKind.unknown then
lexer_add_token(@lexer, current_token);
lexer_spaces(@source_code)
else
write_s("Lexical analysis error on \"");
write_c(source_code_head(source_code));
write_s("\".\n")
end
end;
return lexer
end;
(*
Parser.
*)
proc parse(tokens: ^Token, tokens_size: Word);
var
current_token: ^Token;
i: Word := 0u;
begin
while i < tokens_size do
current_token := tokens + i;
case current_token^.kind of
LexerKind._if:
write_s("IF")
| LexerKind._then:
write_s("THEN")
| LexerKind._else:
write_s("ELSE")
| LexerKind._elsif:
write_s("ELSIF")
| LexerKind._while:
write_s("WHILE")
| LexerKind._do:
write_s("DO")
| LexerKind._proc:
write_s("PROC")
| LexerKind._begin:
write_s("BEGIN")
| LexerKind._end:
write_s("END")
| LexerKind._extern:
write_s("EXTERN")
| LexerKind._const:
write_s("CONST")
| LexerKind._var:
write_s("VAR")
| LexerKind._case:
write_s("CASE")
| LexerKind._of:
write_s("OF")
| LexerKind._type:
write_s("TYPE")
| LexerKind._record:
write_s("RECORD")
| LexerKind._union:
write_s("UNION")
| LexerKind.pipe:
write_s("|")
| LexerKind.to:
write_s("TO")
| LexerKind.boolean:
write_s("BOOLEAN<");
write_b(current_token^.value.boolean_value);
write_c('>')
| LexerKind.null:
write_s("NIL")
| LexerKind.and:
write_s("&")
| LexerKind._or:
write_s("OR")
| LexerKind.not:
write_s("~")
| LexerKind._return:
write_s("RETURN")
| LexerKind._cast:
write_s("CAST")
| LexerKind.shift_left:
write_s("<<")
| LexerKind.shift_right:
write_s(">>")
| LexerKind.identifier:
write_c('<');
write_s(current_token^.value.string);
write_c('>')
| LexerKind.trait:
write_c('#');
write_s(current_token^.value.string)
| LexerKind.left_paren:
write_s("(")
| LexerKind.right_paren:
write_s(")")
| LexerKind.left_square:
write_s("[")
| LexerKind.right_square:
write_s("]")
| LexerKind.greater_equal:
write_s(">=")
| LexerKind.less_equal:
write_s("<=")
| LexerKind.greater_than:
write_s(">")
| LexerKind.less_than:
write_s("<")
| LexerKind.equal:
write_s("=")
| LexerKind.not_equal:
write_s("<>")
| LexerKind.semicolon:
write_c(';')
| LexerKind.dot:
write_c('.')
| LexerKind.comma:
write_c(',')
| LexerKind.plus:
write_c('+')
| LexerKind.minus:
write_c('-')
| LexerKind.multiplication:
write_c('*')
| LexerKind.division:
write_c('/')
| LexerKind.remainder:
write_c('%')
| LexerKind.assignment:
write_s(":=")
| LexerKind.colon:
write_c(':')
| LexerKind.hat:
write_c('^')
| LexerKind.at:
write_c('@')
| LexerKind.comment:
write_s("(* COMMENT *)")
| LexerKind.integer:
write_c('<');
write_i(current_token^.value.int_value);
write_c('>')
| LexerKind.word:
write_c('<');
write_i(current_token^.value.int_value);
write_s("u>")
| LexerKind.character:
write_c('<');
write_i(cast(current_token^.value.char_value: Int));
write_s("c>")
| LexerKind.string:
write_s("\"...\"")
| LexerKind._defer:
write_s("DEFER")
| LexerKind.exclamation:
write_c('!')
| LexerKind.arrow:
write_s("->")
| LexerKind._program:
write_s("PROGRAM")
| LexerKind._module:
write_s("MODULE")
| LexerKind._import:
write_s("IMPORT")
else
write_s("UNKNOWN<");
write_i(cast(current_token^.kind: Int));
write_c('>')
end;
write_c(' ');
i := i + 1u
end;
write_c('\n')
end;
(*
Compilation entry.
*)
proc compile_in_stages(command_line: ^CommandLine, source_code: SourceCode) -> Int;
var
return_code: Int := 0;
lexer: Tokenizer;
begin
if command_line^.lex or command_line^.parse then
lexer := lexer_text(source_code)
end;
if command_line^.parse then
parse(lexer.data, lexer.length)
end;
return return_code
end;
proc process(argc: Int, argv: ^^Char) -> Int;
var
tokens: ^Token;
tokens_size: Word;
source_code: SourceCode;
command_line: ^CommandLine;
return_code: Int := 0;
source_file: ^SourceFile;
begin
command_line := parse_command_line(argc, argv);
if command_line = nil then
return_code := 2
end;
if return_code = 0 then
source_file := read_source(command_line^.input);
if source_file = nil then
perror(command_line^.input);
return_code := 3
end
end;
if return_code = 0 then
defer
fclose(source_file^.handle)
end;
source_code.position := TextLocation(1u, 1u);
source_code.input := cast(source_file: Pointer);
source_code.empty := source_file_empty;
source_code.head := source_file_head;
source_code.advance := source_file_advance;
return_code := compile_in_stages(command_line, source_code)
end;
return return_code
end;
return process(count, parameters)
end.

204
tools/init.c Normal file
View File

@@ -0,0 +1,204 @@
#include <stdio.h>
#include <dirent.h>
#include <fcntl.h>
#include <unistd.h>
#include <sys/stat.h>
#include <sys/wait.h>
#include <sys/reboot.h>
#define FILENAME_BUFFER_SIZE 256
size_t read_command(int descriptor, char *command_buffer)
{
ssize_t bytes_read = 0;
size_t read_so_far = 0;
while ((bytes_read = read(descriptor, command_buffer + read_so_far, FILENAME_BUFFER_SIZE - read_so_far - 1)) > 0)
{
read_so_far += bytes_read;
if (read_so_far >= FILENAME_BUFFER_SIZE - 1)
{
break;
}
}
command_buffer[read_so_far] = 0;
return read_so_far;
}
enum status
{
status_success,
status_failure,
status_warning,
status_fatal
};
unsigned int make_path(char *destination, const char *directory, const char *filename, const char *extension)
{
unsigned int i = 0;
for (; i < FILENAME_BUFFER_SIZE; i++)
{
if (directory[i] == 0)
{
break;
}
destination[i] = directory[i];
}
for (int j = 0; i < FILENAME_BUFFER_SIZE; i++, j++)
{
if (filename[j] == 0)
{
break;
}
destination[i] = filename[j];
}
if (extension == NULL)
{
goto done;
}
for (int j = 0; i < FILENAME_BUFFER_SIZE; i++, j++)
{
if (extension[j] == 0)
{
break;
}
destination[i] = extension[j];
}
done:
destination[i] = 0;
return i;
}
enum status run_test(const char *file_entry_name)
{
printf("Running %s. ", file_entry_name);
char filename[FILENAME_BUFFER_SIZE];
char command_buffer[FILENAME_BUFFER_SIZE];
char file_buffer[256];
int pipe_ends[2];
if (pipe(pipe_ends) == -1)
{
perror("pipe");
return status_fatal;
}
make_path(filename, "./tests/", file_entry_name, NULL);
int child_pid = fork();
if (child_pid == -1)
{
return status_fatal;
}
else if (child_pid == 0)
{
close(STDIN_FILENO);
close(STDERR_FILENO);
close(pipe_ends[0]); // Close the read end.
if (dup2(pipe_ends[1], STDOUT_FILENO) == -1)
{
perror("dup2");
}
else
{
execl(filename, filename);
perror("execl");
}
close(STDOUT_FILENO);
close(pipe_ends[1]);
_exit(1);
}
else
{
close(pipe_ends[1]); // Close the write end.
read_command(pipe_ends[0], command_buffer);
close(pipe_ends[0]);
int wait_status = 0;
make_path(filename, "./expectations/", file_entry_name, ".txt");
FILE *expectation_descriptor = fopen(filename, "r");
if (expectation_descriptor == NULL)
{
return status_warning;
}
size_t read_from_file = fread(file_buffer, 1, sizeof(file_buffer) - 1, expectation_descriptor);
fclose(expectation_descriptor);
file_buffer[read_from_file] = 0;
for (unsigned int i = 0; ; ++i)
{
if (command_buffer[i] == 0 && file_buffer[i] == 0)
{
fwrite("\n", 1, 1, stdout);
return status_success;
}
else if (command_buffer[i] != file_buffer[i])
{
printf("Failed. Got:\n%s", command_buffer);
return status_failure;
}
}
}
}
struct summary
{
size_t total;
size_t failure;
size_t success;
};
void walk()
{
DIR *directory_stream = opendir("./tests");
struct dirent *file_entry;
struct summary test_summary = { .total = 0, .failure = 0, .success = 0 };
while ((file_entry = readdir(directory_stream)) != NULL)
{
if (file_entry->d_name[0] == '.')
{
continue;
}
++test_summary.total;
switch (run_test(file_entry->d_name))
{
case status_failure:
++test_summary.failure;
break;
case status_success:
++test_summary.success;
break;
case status_warning:
break;
case status_fatal:
goto end_walk;
}
}
printf("Successful: %lu, Failed: %lu, Total: %lu.\n",
test_summary.success, test_summary.failure, test_summary.total);
end_walk:
closedir(directory_stream);
}
int main()
{
int dev_console = open("/dev/console", O_WRONLY);
if (dev_console != -1)
{
dup2(dev_console, STDOUT_FILENO);
walk();
close(dev_console);
}
sync();
reboot(RB_POWER_OFF);
return 1;
}

92
tools/support.rb Normal file
View File

@@ -0,0 +1,92 @@
# This Source Code Form is subject to the terms of the Mozilla Public License,
# v. 2.0. If a copy of the MPL was not distributed with this file, You can
# obtain one at https://mozilla.org/MPL/2.0/. -}
require 'uri'
require 'net/http'
require 'open3'
LINKER = 'build/rootfs/riscv32-unknown-linux-gnu/bin/ld'
AS = 'build/rootfs/riscv32-unknown-linux-gnu/bin/as'
TMP = Pathname.new('./build')
class BuildTarget
attr_accessor(:build, :gcc, :sysroot, :tmp)
def initialize
@sysroot = Pathname.new '/'
end
def gxx
@gcc.gsub 'c', '+'
end
def rootfs
tmp + 'rootfs'
end
end
def gcc_verbose(gcc_binary)
read, write = IO.pipe
sh({'LANG' => 'C'}, gcc_binary, '--verbose', err: write)
write.close
output = read.read
read.close
output
end
def find_build_target(gcc_version)
gcc_binary = 'gcc'
output = gcc_verbose gcc_binary
if output.start_with? 'Apple clang'
gcc_binary = "gcc-#{gcc_version.split('.').first}"
output = gcc_verbose gcc_binary
sdk = Pathname.new '/Library/Developer/CommandLineTools/SDKs/MacOSX15.sdk'
end
result = output
.lines
.each_with_object(BuildTarget.new) do |line, accumulator|
if line.start_with? 'Target: '
accumulator.build = line.split(' ').last.strip
elsif line.start_with? 'COLLECT_GCC'
accumulator.gcc = line.split('=').last.strip
end
end
result.tmp = TMP
result.sysroot = sdk unless sdk.nil?
result
end
def download_and_pipe(url, target, command)
target.mkpath
Net::HTTP.start(url.host, url.port, use_ssl: url.scheme == 'https') do |http|
request = Net::HTTP::Get.new url.request_uri
http.request request do |response|
case response
when Net::HTTPRedirection
download_and_pipe URI.parse(response['location']), target, command
when Net::HTTPSuccess
Dir.chdir target.to_path do
Open3.popen2(*command) do |stdin, stdout, wait_thread|
Thread.new do
stdout.each { |line| puts line }
end
response.read_body do |chunk|
stdin.write chunk
end
stdin.close
wait_thread.value
end
end
else
response.error!
end
end
end
end