1 Commits

Author SHA1 Message Date
ec0b4be1f7 Use colon instead of as to cast 2025-02-14 19:56:46 +01:00
48 changed files with 3263 additions and 8850 deletions

1
.gitignore vendored
View File

@@ -1,2 +1 @@
/build/ /build/
a.out

1
.ruby-version Normal file
View File

@@ -0,0 +1 @@
3.3.6

View File

@@ -24,7 +24,7 @@ in the `boot/` directory.
## Build ## 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` Download the GCC source. Copy the contents of this repository into `gcc/elna`
inside GCC. Finally build GCC enabling the frontend with 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 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 See `rake -T` for more tasks. The GCC source is under `build/tools`. The
installation path is `build/host/install`. 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, # 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 # 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 'pathname'
require 'open3'
require 'rake/clean' 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' HOST_INSTALL = TMP + 'host/install'
CLOBBER.include TMP CLOBBER.include TMP
CLEAN.include(TMP + 'boot')
directory(TMP + 'tools')
directory HOST_GCC
directory HOST_INSTALL directory HOST_INSTALL
task default: ['source/main.elna', TMP + 'boot/elna'] do |t| task default: [TMP + 'elna'] do
sources, compiler = t.prerequisites.partition { |f| f.end_with? '.elna' } sh (TMP + 'elna').to_path, '--tokenize', 'source.elna'
sh *compiler, '--parse', *sources
end end
rule(/boot\/.+\.o$/ => ->(file) { namespace :boot do
source = Pathname.new('source') + desc 'Download and configure the bootstrap compiler'
Pathname.new(file).relative_path_from(TMP + 'boot').sub_ext('.elna') 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] download_and_pipe url, source_directory.dirname, ['tar', '-Jxv']
}) do |t| download_and_pipe URI.parse(GCC_PATCH), source_directory, ['patch', '-p1']
Pathname.new(t.name).dirname.mkpath
sources, compiler = t.prerequisites.partition { |source| source.end_with? '.elna' }
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 = '-O2 -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 end
file TMP + 'boot/elna' => FileList['source/**/*.elna'].reject { |file| desc 'Build the bootstrap compiler'
file != file.downcase task boot: %w[boot:configure boot:make]
}.map { |file|
TMP + 'boot' +
Pathname.new(file).relative_path_from('source').sub_ext('.o')
} do |t|
compiler = HOST_INSTALL + 'bin/gcc'
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 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" #include "elna/boot/driver.h"
namespace elna::boot namespace elna
{
namespace boot
{ {
position make_position(const yy::location& location) position make_position(const yy::location& location)
{ {
@@ -40,10 +42,20 @@ namespace elna::boot
} }
driver::driver(const char *input_file) 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) char escape_char(char escape)
{ {
switch (escape) switch (escape)
@@ -76,49 +88,5 @@ namespace elna::boot
return escape_invalid_char; 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,30 +23,25 @@ along with GCC; see the file COPYING3. If not see
#include "parser.hh" #include "parser.hh"
#undef YY_DECL #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) #define yyterminate() return yy::parser::make_YYEOF(this->location)
%} %}
%option c++ noyywrap never-interactive %option c++ noyywrap never-interactive
%option yyclass="lexer" %option yyclass="elna::boot::lexer"
%x IN_COMMENT %x IN_COMMENT
ID1 [A-Za-z_]
ID2 [A-Za-z0-9_]
HIGIT [0-9a-fA-F]
BIGIT [01]
%% %%
%{ %{
this->location.step(); this->location.step();
%} %}
<IN_COMMENT>{ <IN_COMMENT>{
\*\) BEGIN(INITIAL); \*\) BEGIN(INITIAL);
[^*\n]+ ; /* Eat comment in chunks. */ [^*\n]+ ; /* Eat comment in chunks. */
\* ; /* Eat the lone star. */ \* ; /* Eat the lone star. */
\n+ { \n+ {
this->location.lines(yyleng); this->location.lines(yyleng);
this->location.step(); this->location.step();
} }
@@ -54,267 +49,250 @@ BIGIT [01]
\(\* BEGIN(IN_COMMENT); \(\* BEGIN(IN_COMMENT);
[ \t\r] { [ \t\r] {
this->location.step(); this->location.step();
} }
\n+ { \n+ {
this->location.lines(yyleng); this->location.lines(yyleng);
} }
if { if {
return yy::parser::make_IF(this->location); return yy::parser::make_IF(this->location);
} }
then { then {
return yy::parser::make_THEN(this->location); return yy::parser::make_THEN(this->location);
} }
else { else {
return yy::parser::make_ELSE(this->location); return yy::parser::make_ELSE(this->location);
} }
elsif { elsif {
return yy::parser::make_ELSIF(this->location); return yy::parser::make_ELSIF(this->location);
} }
while { while {
return yy::parser::make_WHILE(this->location); return yy::parser::make_WHILE(this->location);
} }
do { do {
return yy::parser::make_DO(this->location); return yy::parser::make_DO(this->location);
} }
proc { proc {
return yy::parser::make_PROCEDURE(this->location); return yy::parser::make_PROCEDURE(this->location);
} }
begin { begin {
return yy::parser::make_BEGIN_BLOCK(this->location); return yy::parser::make_BEGIN_BLOCK(this->location);
} }
end { end {
return yy::parser::make_END_BLOCK(this->location); return yy::parser::make_END_BLOCK(this->location);
} }
extern { extern {
return yy::parser::make_EXTERN(this->location); return yy::parser::make_EXTERN(this->location);
} }
const { const {
return yy::parser::make_CONST(this->location); return yy::parser::make_CONST(this->location);
} }
var { var {
return yy::parser::make_VAR(this->location); return yy::parser::make_VAR(this->location);
} }
type { array {
return yy::parser::make_TYPE(this->location); return yy::parser::make_ARRAY(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 { of {
return yy::parser::make_OF(this->location); return yy::parser::make_OF(this->location);
} }
{ID1}{ID2}* { type {
return yy::parser::make_TYPE(this->location);
}
record {
return yy::parser::make_RECORD(this->location);
}
union {
return yy::parser::make_UNION(this->location);
}
pointer {
return yy::parser::make_POINTER(this->location);
}
to {
return yy::parser::make_TO(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);
}
sizeof {
return yy::parser::make_SIZEOF(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); return yy::parser::make_IDENTIFIER(yytext, this->location);
} }
#{ID1}{ID2}* { [0-9]+u {
return yy::parser::make_TRAIT(yytext + 1, this->location); return yy::parser::make_WORD(strtoul(yytext, NULL, 10), this->location);
} }
[[:digit:]]+u { [0-9]+ {
unsigned long result = strtoul(yytext, NULL, 10); return yy::parser::make_INTEGER(strtol(yytext, NULL, 10), this->location);
}
if (errno == ERANGE) [0-9]+\.[0-9] {
return yy::parser::make_FLOAT(strtof(yytext, NULL), this->location);
}
'[[:print:]]' {
if (yytext[1] == '\\' || yytext[1] == '\'')
{ {
REJECT; REJECT;
} }
else else
{ {
return yy::parser::make_WORD(result, this->location); return yy::parser::make_CHARACTER(std::string(yytext, 1, 1), this->location);
} }
} }
[[:digit:]]+ { '\\x[0-9a-fA-F]{1,2}' {
long result = strtol(yytext, NULL, 10); char character = static_cast<char>(std::stoi(yytext + 3, nullptr, 16));
if (errno == ERANGE) 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; REJECT;
} }
else return yy::parser::make_CHARACTER(std::string(&escape, 1), this->location);
{
return yy::parser::make_INTEGER(result, this->location);
} }
}
0x{HIGIT}+ {
unsigned long result = strtoul(yytext, NULL, 16);
if (errno == ERANGE)
{
REJECT;
}
else
{
return yy::parser::make_WORD(result, this->location);
}
}
0b{BIGIT}+ {
unsigned long result = strtoul(yytext, NULL, 2);
if (errno == ERANGE)
{
REJECT;
}
else
{
return yy::parser::make_WORD(result, this->location);
}
}
[[:digit:]]+\.[[:digit:]]+ {
float result = strtof(yytext, NULL);
if (errno == ERANGE)
{
REJECT;
}
else
{
return yy::parser::make_FLOAT(result, this->location);
}
}
'[[:print:]]+' {
std::optional<std::string> result = escape_string(yytext);
if (!result.has_value() || result.value().size() != 1)
{
REJECT;
}
return yy::parser::make_CHARACTER(result.value(), this->location);
}
\"[[:print:]]*\" { \"[[:print:]]*\" {
std::optional<std::string> result = escape_string(yytext); std::string result;
if (!result.has_value()) const char *current_position = yytext + 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)
{ {
REJECT; REJECT;
} }
return yy::parser::make_STRING(result.value(), this->location); else
} {
current_position += processed - 1;
result.push_back(character);
}
}
else if (*current_position == '\\')
{
++current_position;
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_LEFT_PAREN(this->location);
} }
\) { \) {
return yy::parser::make_RIGHT_PAREN(this->location); return yy::parser::make_RIGHT_PAREN(this->location);
} }
\[ { \[ {
return yy::parser::make_LEFT_SQUARE(this->location); return yy::parser::make_LEFT_SQUARE(this->location);
} }
\] { \] {
return yy::parser::make_RIGHT_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_GREATER_EQUAL(this->location);
} }
\<= { \<= {
return yy::parser::make_LESS_EQUAL(this->location); return yy::parser::make_LESS_EQUAL(this->location);
} }
\> { \> {
return yy::parser::make_GREATER_THAN(this->location); return yy::parser::make_GREATER_THAN(this->location);
} }
\< { \< {
return yy::parser::make_LESS_THAN(this->location); return yy::parser::make_LESS_THAN(this->location);
} }
\<\> { \<\> {
return yy::parser::make_NOT_EQUAL(this->location); return yy::parser::make_NOT_EQUAL(this->location);
} }
= { = {
return yy::parser::make_EQUALS(this->location); return yy::parser::make_EQUALS(this->location);
} }
; { ; {
return yy::parser::make_SEMICOLON(this->location); return yy::parser::make_SEMICOLON(this->location);
} }
\. { \. {
return yy::parser::make_DOT(this->location); return yy::parser::make_DOT(this->location);
} }
, { , {
return yy::parser::make_COMMA(this->location); return yy::parser::make_COMMA(this->location);
} }
\+ { \+ {
return yy::parser::make_PLUS(this->location); return yy::parser::make_PLUS(this->location);
} }
\-> { \-> {
return yy::parser::make_ARROW(this->location); return yy::parser::make_ARROW(this->location);
} }
\- { \- {
return yy::parser::make_MINUS(this->location); return yy::parser::make_MINUS(this->location);
} }
\* { \* {
return yy::parser::make_MULTIPLICATION(this->location); return yy::parser::make_MULTIPLICATION(this->location);
} }
\/ { \/ {
return yy::parser::make_DIVISION(this->location); return yy::parser::make_DIVISION(this->location);
} }
% { % {
return yy::parser::make_REMAINDER(this->location); return yy::parser::make_REMAINDER(this->location);
} }
:= { := {
return yy::parser::make_ASSIGNMENT(this->location); return yy::parser::make_ASSIGNMENT(this->location);
} }
: { : {
return yy::parser::make_COLON(this->location); return yy::parser::make_COLON(this->location);
} }
\^ { \^ {
return yy::parser::make_HAT(this->location); return yy::parser::make_HAT(this->location);
} }
@ { @ {
return yy::parser::make_AT(this->location); return yy::parser::make_AT(this->location);
} }
! {
return yy::parser::make_EXCLAMATION(this->location);
}
. { . {
std::stringstream ss; std::stringstream ss;
ss << "Illegal character 0x" << std::hex << static_cast<unsigned int>(yytext[0]); ss << "Illegal character 0x" << std::hex << static_cast<unsigned int>(yytext[0]);
driver.add_error<syntax_error>(ss.str(), driver.input_file, this->location); 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" %require "3.4"
%language "c++" %language "c++"
%code {
using namespace elna;
}
%code requires { %code requires {
#include <cstdint> #include <cstdint>
#include <iostream> #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);
}; };
} }
@@ -78,523 +74,420 @@ along with GCC; see the file COPYING3. If not see
} }
%start program; %start program;
%token <std::string> IDENTIFIER %token <std::string> IDENTIFIER "identifier"
%token <std::string> TRAIT %token <std::int32_t> INTEGER "integer"
%token <std::int32_t> INTEGER %token <std::uint32_t> WORD "word"
%token <std::uint32_t> WORD %token <float> FLOAT "float"
%token <float> FLOAT %token <std::string> CHARACTER "character"
%token <std::string> CHARACTER %token <std::string> STRING "string"
%token <std::string> STRING
%token <bool> BOOLEAN %token <bool> BOOLEAN
%token LEFT_PAREN "(" RIGHT_PAREN ")" LEFT_SQUARE "[" RIGHT_SQUARE "]" %token IF WHILE DO THEN ELSE ELSIF RETURN
%token ASSIGNMENT ":=" %token CONST VAR PROCEDURE ARRAY OF TYPE RECORD POINTER TO UNION
ARROW "->" EXCLAMATION "!" %token BEGIN_BLOCK END_BLOCK EXTERN DEFER
AT "@" HAT "^" %token LEFT_PAREN RIGHT_PAREN LEFT_SQUARE RIGHT_SQUARE SEMICOLON DOT COMMA
COLON ":" SEMICOLON ";" DOT "." COMMA "," %token AND OR NOT CAST SIZEOF
%token NOT "~" %token GREATER_EQUAL LESS_EQUAL LESS_THAN GREATER_THAN NOT_EQUAL EQUALS
CAST "cast" %token PLUS MINUS MULTIPLICATION DIVISION REMAINDER
NIL "nil" %token ASSIGNMENT COLON HAT AT NIL ARROW
CONST "const"
VAR "var"
PROCEDURE "proc"
TYPE "type"
RECORD "record"
UNION "union"
EXTERN "extern"
IF "if"
WHILE "while"
DO "do"
THEN "then"
ELSE "else"
ELSIF "elsif"
RETURN "return"
PROGRAM "program"
MODULE "module"
IMPORT "import"
BEGIN_BLOCK "begin"
END_BLOCK "end"
DEFER "defer"
CASE "case"
OF "of"
PIPE "|"
%token OR "or" AND "&" XOR "xor"
EQUALS "=" NOT_EQUAL "<>" LESS_THAN "<" GREATER_THAN ">" LESS_EQUAL "<=" GREATER_EQUAL ">="
SHIFT_LEFT "<<" SHIFT_RIGHT ">>"
PLUS "+" MINUS "-"
MULTIPLICATION "*" DIVISION "/" REMAINDER "%"
%left "or" "&" "xor" %left OR AND XOR
%left "=" "<>" "<" ">" "<=" ">=" %left EQUALS NOT_EQUAL LESS_THAN GREATER_THAN LESS_EQUAL GREATER_EQUAL
%left "<<" ">>" %left PLUS MINUS
%left "+" "-" %left MULTIPLICATION DIVISION REMAINDER
%left "*" "/" "%"
%type <elna::boot::literal_expression *> literal; %type <elna::boot::literal *> literal;
%type <std::vector<elna::boot::expression *>> case_labels; %type <elna::boot::constant_definition *> constant_definition;
%type <elna::boot::switch_case> switch_case; %type <std::vector<elna::boot::constant_definition *>> constant_part constant_definitions;
%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 <elna::boot::variable_declaration *> variable_declaration;
%type <std::vector<elna::boot::variable_declaration *>> variable_declarations variable_part; %type <std::vector<elna::boot::variable_declaration *>> variable_declarations variable_part
formal_parameter_list;
%type <elna::boot::type_expression *> type_expression; %type <elna::boot::type_expression *> type_expression;
%type <std::vector<elna::boot::type_expression *>> type_expressions; %type <elna::boot::expression *> expression operand unary;
%type <elna::boot::traits_expression *> traits_expression;
%type <elna::boot::expression *> expression operand simple_expression;
%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 <std::vector<elna::boot::expression *>> expressions actual_parameter_list;
%type <elna::boot::designator_expression *> designator_expression; %type <elna::boot::designator_expression *> designator_expression;
%type <elna::boot::procedure_call*> call_expression; %type <elna::boot::assign_statement *> assign_statement;
%type <elna::boot::call_expression *> 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::return_statement *> return_statement;
%type <elna::boot::statement *> statement; %type <elna::boot::statement *> statement;
%type <std::vector<elna::boot::statement *>> required_statements optional_statements statement_part; %type <std::vector<elna::boot::statement *>> statements optional_statements;
%type <elna::boot::procedure_declaration *> procedure_declaration; %type <elna::boot::procedure_definition *> procedure_definition procedure_heading;
%type <std::pair<std::vector<std::string>, elna::boot::procedure_type_expression *>> procedure_heading; %type <std::vector<elna::boot::procedure_definition *>> procedure_definitions procedure_part;
%type <elna::boot::procedure_type_expression::return_t> return_declaration; %type <elna::boot::type_definition *> type_definition;
%type <std::vector<elna::boot::procedure_declaration *>> procedure_declarations procedure_part; %type <std::vector<elna::boot::type_definition *>> type_definitions type_part;
%type <elna::boot::type_declaration *> type_declaration; %type <elna::boot::block *> block;
%type <std::vector<elna::boot::type_declaration *>> type_declarations type_part; %type <std::pair<std::string, elna::boot::type_expression *>> field_declaration;
%type <std::unique_ptr<elna::boot::block>> block; %type <std::vector<std::pair<std::string, elna::boot::type_expression *>>> field_list;
%type <elna::boot::field_declaration> field_declaration formal_parameter; %type <std::vector<elna::boot::conditional_statements *>> elsif_statement_list;
%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::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::cast_expression *> cast_expression;
%type <elna::boot::identifier_definition> identifier_definition; %type <elna::boot::defer_statement *> defer_statement;
%type <std::vector<elna::boot::identifier_definition>> identifier_definitions; %type <std::pair<std::string, bool>> identifier_definition;
%type <std::vector<std::string>> identifiers import_declaration;
%type <std::vector<elna::boot::import_declaration *>> import_declarations import_part;
%% %%
program: program:
"program" ";" import_part constant_part type_part variable_part procedure_part statement_part "end" "." constant_part type_part variable_part procedure_part BEGIN_BLOCK optional_statements END_BLOCK DOT
{ {
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, $1);
std::swap(tree->constants, $4); std::swap(tree->types , $2);
std::swap(tree->types , $5); std::swap(tree->variables, $3);
std::swap(tree->variables, $6); std::swap(tree->procedures, $4);
std::swap(tree->procedures, $7); std::swap(tree->body, $6);
std::swap(tree->body, $8);
driver.tree.reset(tree); driver.tree.reset(tree);
} }
| "module" ";" import_part constant_part type_part variable_part procedure_part "end" "." block: constant_part variable_part BEGIN_BLOCK optional_statements END_BLOCK
{ {
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($$->constants, $1);
std::swap(tree->constants, $4); std::swap($$->variables, $2);
std::swap(tree->types , $5); std::swap($$->body, $4);
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);
} }
identifier_definition: identifier_definition:
IDENTIFIER "*" { $$ = boot::identifier_definition{ $1, true }; } IDENTIFIER MULTIPLICATION
| IDENTIFIER { $$ = boot::identifier_definition{ $1, false }; }
identifier_definitions:
identifier_definition "," identifier_definitions
{ {
std::swap($$, $3); $$ = std::make_pair($1, true);
$$.emplace($$.cbegin(), $1);
} }
| identifier_definition { $$.emplace_back(std::move($1)); } | IDENTIFIER
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
{ {
$$.second = new boot::procedure_type_expression(boot::make_position(@1), std::move($2)); $$ = std::make_pair($1, false);
for (auto& [name, type] : $1) }
procedure_heading:
PROCEDURE identifier_definition formal_parameter_list SEMICOLON
{ {
$$.first.emplace_back(std::move(name)); $$ = new elna::boot::procedure_definition(elna::boot::make_position(@1),
$$.second->parameters.push_back(type); $2.first, $2.second);
std::swap($3, $$->parameters);
} }
} | PROCEDURE identifier_definition formal_parameter_list ARROW type_expression SEMICOLON
procedure_declaration:
"proc" identifier_definition procedure_heading ";" block ";"
{ {
$$ = new boot::procedure_declaration(boot::make_position(@1), std::move($2), $3.second, std::move(*$5)); $$ = new elna::boot::procedure_definition(elna::boot::make_position(@1),
std::swap($3.first, $$->parameter_names); $2.first, $2.second, $5);
std::swap($3, $$->parameters);
} }
| "proc" identifier_definition procedure_heading ";" "extern" ";" procedure_definition:
{ procedure_heading block { $$ = $1->add_body($2); }
$$ = new boot::procedure_declaration(boot::make_position(@1), std::move($2), $3.second); | procedure_heading EXTERN { $$ = $1; }
std::swap($3.first, $$->parameter_names); procedure_definitions:
} procedure_definition procedure_definitions
procedure_declarations:
procedure_declaration procedure_declarations
{ {
std::swap($$, $2); std::swap($$, $2);
$$.emplace($$.cbegin(), std::move($1)); $$.emplace($$.cbegin(), std::move($1));
} }
| procedure_declaration { $$.emplace_back(std::move($1)); } | procedure_definition { $$.emplace_back(std::move($1)); }
procedure_part: procedure_part:
/* no procedure definitions */ {} /* no procedure definitions */ {}
| procedure_declarations { std::swap($$, $1); } | procedure_definitions { std::swap($$, $1); }
call_expression: designator_expression actual_parameter_list assign_statement: designator_expression ASSIGNMENT expression
{ {
$$ = new boot::procedure_call(boot::make_position(@1), $1); $$ = new elna::boot::assign_statement(elna::boot::make_position(@1), $1, $3);
std::swap($$->arguments, $2);
} }
cast_expression: "cast" "(" expression ":" type_expression ")" call_expression: IDENTIFIER actual_parameter_list
{ $$ = new boot::cast_expression(boot::make_position(@1), $5, $3); }
elsif_do_statements:
"elsif" expression "do" optional_statements elsif_do_statements
{ {
boot::conditional_statements *branch = new boot::conditional_statements($2, std::move($4)); $$ = new elna::boot::call_expression(elna::boot::make_position(@1), $1);
std::swap($$->arguments(), $2);
}
cast_expression: CAST LEFT_PAREN expression COLON type_expression RIGHT_PAREN
{
$$ = new elna::boot::cast_expression(elna::boot::make_position(@1), $5, $3);
}
while_statement: WHILE expression DO optional_statements END_BLOCK
{
auto body = new elna::boot::conditional_statements($2);
std::swap($4, body->statements);
$$ = new elna::boot::while_statement(elna::boot::make_position(@1), body);
}
elsif_statement_list:
ELSIF expression THEN optional_statements elsif_statement_list
{
elna::boot::conditional_statements *branch = new elna::boot::conditional_statements($2);
std::swap(branch->statements, $4);
std::swap($5, $$); std::swap($5, $$);
$$.emplace($$.begin(), branch); $$.emplace($$.begin(), branch);
} }
| {} | {}
else_statements: if_statement:
"else" optional_statements { $$ = new std::vector<boot::statement *>(std::move($2)); } IF expression THEN optional_statements elsif_statement_list END_BLOCK
| { $$ = nullptr; }
elsif_then_statements:
"elsif" expression "then" optional_statements elsif_then_statements
{ {
boot::conditional_statements *branch = new boot::conditional_statements($2, std::move($4)); auto then = new elna::boot::conditional_statements($2);
std::swap($5, $$); std::swap($4, then->statements);
$$.emplace($$.begin(), branch); $$ = new elna::boot::if_statement(elna::boot::make_position(@1), then);
std::swap($5, $$->branches);
}
| IF expression THEN optional_statements elsif_statement_list ELSE optional_statements END_BLOCK
{
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 optional_statements END_BLOCK
{
$$ = new elna::boot::defer_statement(elna::boot::make_position(@1));
std::swap($2, $$->statements);
} }
| {}
return_statement: "return" expression
{ $$ = new boot::return_statement(boot::make_position(@1), $2); }
literal: literal:
INTEGER { $$ = new boot::literal<std::int32_t>(boot::make_position(@1), $1); } INTEGER
| 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 ")"
{ {
$$ = new boot::traits_expression(boot::make_position(@1), $1); $$ = new elna::boot::number_literal<std::int32_t>(elna::boot::make_position(@1), $1);
std::swap($3, $$->parameters);
} }
simple_expression: | 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);
}
operand:
literal { $$ = $1; } literal { $$ = $1; }
| designator_expression { $$ = $1; } | designator_expression { $$ = $1; }
| traits_expression { $$ = $1; } | SIZEOF LEFT_PAREN type_expression RIGHT_PAREN
{
$$ = new elna::boot::size_of_expression(elna::boot::make_position(@1), $3);
}
| cast_expression { $$ = $1; } | cast_expression { $$ = $1; }
| call_expression { $$ = $1; } | call_expression { $$ = $1; }
| "(" expression ")" { $$ = $2; } | LEFT_PAREN expression RIGHT_PAREN { $$ = $2; }
operand:
unary_expression { $$ = $1; }
| simple_expression { $$ = $1; }
expression: expression:
binary_expression { $$ = $1; } unary { $$ = $1; }
| expression MULTIPLICATION expression
{
$$ = new elna::boot::binary_expression(elna::boot::make_position(@2), $1, $3,
elna::boot::binary_operator::multiplication);
}
| expression DIVISION expression
{
$$ = new elna::boot::binary_expression(elna::boot::make_position(@2), $1, $3,
elna::boot::binary_operator::division);
}
| expression REMAINDER expression
{
$$ = new elna::boot::binary_expression(elna::boot::make_position(@2), $1, $3,
elna::boot::binary_operator::remainder);
}
| expression PLUS expression
{
$$ = new elna::boot::binary_expression(elna::boot::make_position(@2), $1, $3,
elna::boot::binary_operator::sum);
}
| expression MINUS expression
{
$$ = new elna::boot::binary_expression(elna::boot::make_position(@2), $1, $3,
elna::boot::binary_operator::subtraction);
}
| expression EQUALS expression
{
$$ = new elna::boot::binary_expression(elna::boot::make_position(@2), $1, $3,
elna::boot::binary_operator::equals);
}
| expression NOT_EQUAL expression
{
$$ = new elna::boot::binary_expression(elna::boot::make_position(@2), $1, $3,
elna::boot::binary_operator::not_equals);
}
| expression LESS_THAN expression
{
$$ = new elna::boot::binary_expression(elna::boot::make_position(@2), $1, $3,
elna::boot::binary_operator::less);
}
| expression GREATER_THAN expression
{
$$ = new elna::boot::binary_expression(elna::boot::make_position(@2), $1, $3,
elna::boot::binary_operator::greater);
}
| expression LESS_EQUAL expression
{
$$ = new elna::boot::binary_expression(elna::boot::make_position(@2), $1, $3,
elna::boot::binary_operator::less_equal);
}
| expression GREATER_EQUAL expression
{
$$ = new elna::boot::binary_expression(elna::boot::make_position(@2), $1, $3,
elna::boot::binary_operator::greater_equal);
}
| expression AND expression
{
$$ = new elna::boot::binary_expression(elna::boot::make_position(@2), $1, $3,
elna::boot::binary_operator::conjunction);
}
| expression OR expression
{
$$ = new elna::boot::binary_expression(elna::boot::make_position(@2), $1, $3,
elna::boot::binary_operator::disjunction);
}
| expression XOR expression
{
$$ = new elna::boot::binary_expression(elna::boot::make_position(@2), $1, $3,
elna::boot::binary_operator::exclusive_disjunction);
}
unary:
AT operand
{
$$ = new elna::boot::unary_expression(elna::boot::make_position(@1), $2,
elna::boot::unary_operator::reference);
}
| NOT operand
{
$$ = new elna::boot::unary_expression(elna::boot::make_position(@1), $2,
elna::boot::unary_operator::negation);
}
| MINUS operand
{
$$ = new elna::boot::unary_expression(elna::boot::make_position(@1), $2,
elna::boot::unary_operator::minus);
}
| operand { $$ = $1; } | operand { $$ = $1; }
binary_expression:
expression "*" expression
{
$$ = new boot::binary_expression(boot::make_position(@2), $1, $3, boot::binary_operator::multiplication);
}
| expression "/" expression
{
$$ = new boot::binary_expression(boot::make_position(@2), $1, $3, boot::binary_operator::division);
}
| expression "%" expression
{
$$ = new boot::binary_expression(boot::make_position(@2), $1, $3, boot::binary_operator::remainder);
}
| expression "+" expression
{
$$ = new boot::binary_expression(boot::make_position(@2), $1, $3, boot::binary_operator::sum);
}
| expression "-" expression
{
$$ = new boot::binary_expression(boot::make_position(@2), $1, $3, boot::binary_operator::subtraction);
}
| expression "=" expression
{
$$ = new boot::binary_expression(boot::make_position(@2), $1, $3, boot::binary_operator::equals);
}
| expression "<>" expression
{
$$ = new boot::binary_expression(boot::make_position(@2), $1, $3, boot::binary_operator::not_equals);
}
| expression "<" expression
{
$$ = new boot::binary_expression(boot::make_position(@2), $1, $3, boot::binary_operator::less);
}
| expression ">" expression
{
$$ = new boot::binary_expression(boot::make_position(@2), $1, $3, boot::binary_operator::greater);
}
| expression "<=" expression
{
$$ = new boot::binary_expression(boot::make_position(@2), $1, $3,
boot::binary_operator::less_equal);
}
| expression ">=" expression
{
$$ = new boot::binary_expression(boot::make_position(@2), $1, $3, boot::binary_operator::greater_equal);
}
| expression "&" expression
{
$$ = new boot::binary_expression(boot::make_position(@2), $1, $3, boot::binary_operator::conjunction);
}
| expression "or" expression
{
$$ = new boot::binary_expression(boot::make_position(@2), $1, $3, boot::binary_operator::disjunction);
}
| expression "xor" expression
{
$$ = new boot::binary_expression(boot::make_position(@2), $1, $3,
boot::binary_operator::exclusive_disjunction);
}
| expression "<<" expression
{
$$ = new boot::binary_expression(boot::make_position(@2), $1, $3, boot::binary_operator::shift_left);
}
| expression ">>" expression
{
$$ = new boot::binary_expression(boot::make_position(@2), $1, $3, boot::binary_operator::shift_right);
}
unary_expression:
"@" operand
{
$$ = new boot::unary_expression(boot::make_position(@1), $2, boot::unary_operator::reference);
}
| "~" operand
{
$$ = new boot::unary_expression(boot::make_position(@1), $2, boot::unary_operator::negation);
}
| "-" operand
{
$$ = new boot::unary_expression(boot::make_position(@1), $2, boot::unary_operator::minus);
}
expressions: expressions:
expression "," expressions expression COMMA expressions
{ {
std::swap($$, $3); std::swap($$, $3);
$$.emplace($$.cbegin(), $1); $$.emplace($$.cbegin(), $1);
} }
| expression { $$.push_back($1); } | expression { $$.emplace_back(std::move($1)); }
type_expressions:
type_expression "," type_expressions
{
std::swap($$, $3);
$$.emplace($$.cbegin(), $1);
}
| type_expression { $$.push_back($1); }
designator_expression: designator_expression:
simple_expression "[" expression "]" operand LEFT_SQUARE expression RIGHT_SQUARE
{ $$ = new boot::array_access_expression(boot::make_position(@2), $1, $3); } {
| simple_expression "." IDENTIFIER $$ = new elna::boot::array_access_expression(elna::boot::make_position(@2), $1, $3);
{ $$ = new boot::field_access_expression(boot::make_position(@2), $1, $3); } }
| simple_expression "^" | operand DOT IDENTIFIER
{ $$ = new boot::dereference_expression(boot::make_position(@1), $1); } {
$$ = new elna::boot::field_access_expression(elna::boot::make_position(@2), $1, $3);
}
| operand HAT
{
$$ = new elna::boot::dereference_expression(elna::boot::make_position(@1), $1);
}
| IDENTIFIER | IDENTIFIER
{ $$ = new boot::variable_expression(boot::make_position(@1), $1); } {
$$ = new elna::boot::variable_expression(elna::boot::make_position(@1), $1);
}
statement: statement:
designator_expression ":=" expression assign_statement { $$ = $1; }
{ $$ = new boot::assign_statement(boot::make_position(@1), $1, $3); } | while_statement { $$ = $1; }
| "while" expression "do" optional_statements elsif_do_statements "end" | if_statement { $$ = $1; }
| return_statement { $$ = $1; }
| call_expression
{ {
boot::conditional_statements *body = new boot::conditional_statements($2, std::move($4)); $$ = new elna::boot::call_statement(elna::boot::make_position(@1), $1);
$$ = new boot::while_statement(boot::make_position(@1), body, std::move($5));
} }
| "if" expression "then" optional_statements elsif_then_statements else_statements "end" | defer_statement { $$ = $1; }
{ statements:
boot::conditional_statements *then = new boot::conditional_statements($2, std::move($4)); statement SEMICOLON statements
$$ = new boot::if_statement(boot::make_position(@1), then, std::move($5), $6);
}
| 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
{ {
std::swap($$, $3); std::swap($$, $3);
$$.emplace($$.cbegin(), $1); $$.emplace($$.cbegin(), $1);
} }
| switch_case { $$.push_back($1); }
case_labels:
expression "," case_labels
{
std::swap($$, $3);
$$.emplace($$.cbegin(), $1);
}
| expression { $$.push_back($1); }
required_statements:
required_statements ";" statement
{
std::swap($$, $1);
$$.insert($$.cend(), $3);
}
| statement { $$.push_back($1); } | statement { $$.push_back($1); }
optional_statements: optional_statements:
required_statements { std::swap($$, $1); } statements { std::swap($$, $1); }
| /* no statements */ {} | /* no statements */ {}
field_declaration: field_declaration:
IDENTIFIER ":" type_expression { $$ = std::make_pair($1, $3); } IDENTIFIER COLON type_expression { $$ = std::make_pair($1, $3); }
required_fields: field_list:
field_declaration ";" required_fields field_declaration field_list
{
std::swap($$, $2);
$$.emplace($$.cbegin(), $1);
}
| field_declaration { $$.emplace_back($1); }
type_expression:
ARRAY INTEGER OF type_expression
{
$$ = new elna::boot::array_type_expression(elna::boot::make_position(@1), $4, $2);
}
| POINTER TO type_expression
{
$$ = new elna::boot::pointer_type_expression(elna::boot::make_position(@1), $3);
}
| RECORD field_list END_BLOCK
{
$$ = new elna::boot::record_type_expression(elna::boot::make_position(@1), std::move($2));
}
| UNION field_list END_BLOCK
{
$$ = new elna::boot::union_type_expression(elna::boot::make_position(@1), std::move($2));
}
| IDENTIFIER
{
$$ = new elna::boot::basic_type_expression(elna::boot::make_position(@1), $1);
}
variable_declaration: identifier_definition COLON type_expression
{
$$ = new elna::boot::variable_declaration(elna::boot::make_position(@2), $1.first, $1.second, $3);
}
variable_declarations:
variable_declaration COMMA variable_declarations
{ {
std::swap($$, $3); std::swap($$, $3);
$$.emplace($$.cbegin(), $1); $$.emplace($$.cbegin(), $1);
} }
| field_declaration { $$.emplace_back($1); } | variable_declaration { $$.emplace_back(std::move($1)); }
optional_fields:
required_fields { std::swap($$, $1); }
| /* no fields */ {}
type_expression:
"[" INTEGER "]" type_expression
{
$$ = new boot::array_type_expression(boot::make_position(@1), $4, $2);
}
| "^" type_expression
{
$$ = new boot::pointer_type_expression(boot::make_position(@1), $2);
}
| "record" optional_fields "end"
{
$$ = new boot::record_type_expression(boot::make_position(@1), std::move($2));
}
| "union" required_fields "end"
{
$$ = new boot::union_type_expression(boot::make_position(@1), std::move($2));
}
| "proc" "(" type_expressions ")" return_declaration
{
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));
}
| IDENTIFIER
{
$$ = new boot::named_type_expression(boot::make_position(@1), $1);
}
identifiers:
IDENTIFIER "," identifiers
{
std::swap($$, $3);
$$.emplace($$.cbegin(), std::move($1));
}
| IDENTIFIER { $$.emplace_back(std::move($1)); }
variable_declaration:
identifier_definitions ":" type_expression ";"
{
std::shared_ptr<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);
}
variable_declarations:
/* no variable declarations */ {}
| variable_declaration variable_declarations
{
std::swap($$, $2);
$$.insert(std::cbegin($$), $1);
}
variable_part: variable_part:
/* no variable declarations */ {} /* no variable declarations */ {}
| "var" variable_declarations { std::swap($$, $2); } | VAR variable_declarations SEMICOLON { std::swap($$, $2); }
constant_declaration: identifier_definition ":=" expression ";" constant_definition: identifier_definition EQUALS 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_definitions:
constant_declaration constant_declarations constant_definition constant_definitions
{ {
std::swap($$, $2); std::swap($$, $2);
$$.insert(std::cbegin($$), $1); $$.emplace($$.cbegin(), std::move($1));
} }
| /* no constant definitions */ {} | constant_definition { $$.emplace_back(std::move($1)); }
constant_part: constant_part:
/* no constant definitions */ {} /* no constant definitions */ {}
| "const" constant_declarations { std::swap($$, $2); } | CONST {}
import_declaration: | CONST constant_definitions { std::swap($$, $2); }
IDENTIFIER "." import_declaration type_definition: identifier_definition EQUALS type_expression
{ {
std::swap($$, $3); $$ = new elna::boot::type_definition(elna::boot::make_position(@1), $1.first, $1.second, $3);
$$.emplace($$.cbegin(), std::move($1));
} }
| IDENTIFIER { $$.emplace_back(std::move($1)); } type_definitions:
import_declarations: type_definition type_definitions
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
{ {
std::swap($$, $2); std::swap($$, $2);
$$.insert($$.cbegin(), $1);
}
| /* no type definitions */ {}
type_part:
/* no type definitions */ {}
| "type" type_declarations { std::swap($$, $2); }
formal_parameter:
IDENTIFIER ":" type_expression { $$ = std::make_pair($1, $3); }
formal_parameter_list:
"(" ")" {}
| "(" formal_parameters ")" { std::swap($$, $2); }
formal_parameters:
formal_parameter "," formal_parameters
{
std::swap($$, $3);
$$.emplace($$.cbegin(), std::move($1)); $$.emplace($$.cbegin(), std::move($1));
} }
| formal_parameter { $$.emplace_back(std::move($1)); } | type_definition { $$.emplace_back(std::move($1)); }
type_part:
/* no type definitions */ {}
| TYPE {}
| TYPE type_definitions { std::swap($$, $2); }
formal_parameter_list:
LEFT_PAREN RIGHT_PAREN {}
| LEFT_PAREN variable_declarations RIGHT_PAREN { std::swap($$, $2); }
actual_parameter_list: actual_parameter_list:
"(" ")" {} LEFT_PAREN RIGHT_PAREN {}
| "(" expressions ")" { std::swap($$, $2); } | LEFT_PAREN expressions RIGHT_PAREN { std::swap($$, $2); }
%% %%
void yy::parser::error(const location_type& loc, const std::string& message) 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" #include "elna/boot/result.h"
namespace elna::boot namespace elna
{
namespace boot
{ {
error::error(const char *path, const struct position position) error::error(const char *path, const struct position position)
: position(position), path(path) : position(position), path(path)
@@ -33,35 +35,5 @@ namespace elna::boot
{ {
return this->position.column; 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,644 +0,0 @@
/* Name analysis.
Copyright (C) 2025 Free Software Foundation, Inc.
GCC is free software; you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation; either version 3, or (at your option)
any later version.
GCC is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with GCC; see the file COPYING3. If not see
<http://www.gnu.org/licenses/>. */
#include "elna/boot/semantic.h"
#include <algorithm>
#include <set>
namespace elna::boot
{
undeclared_error::undeclared_error(const std::string& identifier, const char *path, const struct position position)
: error(path, position), identifier(identifier)
{
}
std::string undeclared_error::what() const
{
return "Type '" + identifier + "' not declared";
}
already_declared_error::already_declared_error(const std::string& identifier,
const char *path, const struct position position)
: error(path, position), identifier(identifier)
{
}
std::string already_declared_error::what() const
{
return "Symbol '" + identifier + "' has been already declared";
}
field_duplication_error::field_duplication_error(const std::string& field_name,
const char *path, const struct position position)
: error(path, position), field_name(field_name)
{
}
std::string field_duplication_error::what() const
{
return "Repeated field name '" + field_name + "'";
}
cyclic_declaration_error::cyclic_declaration_error(const std::vector<std::string>& cycle,
const char *path, const struct position position)
: error(path, position), cycle(cycle)
{
}
std::string cyclic_declaration_error::what() const
{
auto segment = std::cbegin(this->cycle);
std::string message = "Type declaration forms a cycle: " + *segment;
++segment;
for (; segment != std::cend(this->cycle); ++segment)
{
message += " -> " + *segment;
}
return message;
}
return_error::return_error(const std::string& identifier, const char *path, const struct position position)
: error(path, position), identifier(identifier)
{
}
std::string return_error::what() const
{
return "Procedure '" + identifier + "' is expected to return, but does not have a return statement";
}
variable_initializer_error::variable_initializer_error(const char *path, const struct position position)
: error(path, position)
{
}
std::string variable_initializer_error::what() const
{
return "Only one variable can be initialized";
}
type_analysis_visitor::type_analysis_visitor(const char *path, symbol_bag bag)
: error_container(path), bag(bag)
{
}
void type_analysis_visitor::visit(program *program)
{
visit(static_cast<unit *>(program));
}
void type_analysis_visitor::visit(procedure_declaration *definition)
{
if (definition->body.has_value() && definition->heading().return_type.proper_type != nullptr)
{
for (statement *const statement : definition->body.value().body())
{
statement->accept(this);
}
if (!this->returns)
{
add_error<return_error>(definition->identifier.name, this->input_file, definition->position());
}
}
}
void type_analysis_visitor::visit(assign_statement *)
{
}
void type_analysis_visitor::visit(if_statement *)
{
}
void type_analysis_visitor::visit(while_statement *)
{
}
void type_analysis_visitor::visit(return_statement *)
{
this->returns = true;
}
void type_analysis_visitor::visit(defer_statement *)
{
}
void type_analysis_visitor::visit(case_statement *)
{
}
void type_analysis_visitor::visit(procedure_call *)
{
}
bool type_analysis_visitor::check_unresolved_symbol(std::shared_ptr<alias_type> alias,
std::vector<std::string>& alias_path)
{
if (std::find(std::cbegin(alias_path), std::cend(alias_path), alias->name) != std::cend(alias_path))
{
return false;
}
alias_path.push_back(alias->name);
if (auto another_alias = alias->reference.get<alias_type>())
{
return check_unresolved_symbol(another_alias, alias_path);
}
return true;
}
void type_analysis_visitor::visit(unit *unit)
{
for (type_declaration *const type : unit->types)
{
type->accept(this);
}
for (procedure_declaration *const procedure : unit->procedures)
{
this->returns = false;
procedure->accept(this);
}
}
void type_analysis_visitor::visit(type_declaration *definition)
{
std::vector<std::string> alias_path;
auto unresolved_type = this->bag.lookup(definition->identifier.name)->is_type()->symbol.get<alias_type>();
if (!check_unresolved_symbol(unresolved_type, alias_path))
{
add_error<cyclic_declaration_error>(alias_path, this->input_file, definition->position());
}
}
name_analysis_visitor::name_analysis_visitor(const char *path, symbol_bag bag)
: error_container(path), bag(bag)
{
}
procedure_type name_analysis_visitor::build_procedure(procedure_type_expression& type_expression)
{
procedure_type::return_t result_return;
if (type_expression.return_type.no_return)
{
result_return = procedure_type::return_t(std::monostate{});
}
else if (type_expression.return_type.proper_type != nullptr)
{
type_expression.return_type.proper_type->accept(this);
result_return = procedure_type::return_t(this->current_type);
}
else
{
result_return = procedure_type::return_t();
}
procedure_type result_type = procedure_type(result_return);
for (struct type_expression *parameter : type_expression.parameters)
{
parameter->accept(this);
result_type.parameters.push_back(this->current_type);
}
return result_type;
}
void name_analysis_visitor::visit(program *program)
{
visit(static_cast<unit *>(program));
for (statement *const statement : program->body)
{
statement->accept(this);
}
}
void name_analysis_visitor::visit(type_declaration *definition)
{
definition->body().accept(this);
auto resolved = this->bag.resolve(definition->identifier.name, this->current_type);
auto info = std::make_shared<type_info>(type(resolved));
info->exported = definition->identifier.exported;
this->bag.enter(definition->identifier.name, info);
}
void name_analysis_visitor::visit(named_type_expression *type_expression)
{
auto unresolved_alias = this->bag.declared(type_expression->name);
if (unresolved_alias != nullptr)
{
this->current_type = type(unresolved_alias);
}
else if (auto from_symbol_table = this->bag.lookup(type_expression->name))
{
this->current_type = from_symbol_table->is_type()->symbol;
}
else
{
add_error<undeclared_error>(type_expression->name, this->input_file, type_expression->position());
this->current_type = type();
}
}
void name_analysis_visitor::visit(pointer_type_expression *type_expression)
{
type_expression->base().accept(this);
this->current_type = type(std::make_shared<pointer_type>(this->current_type));
}
void name_analysis_visitor::visit(array_type_expression *type_expression)
{
type_expression->base().accept(this);
this->current_type = type(std::make_shared<array_type>(this->current_type, type_expression->size));
}
std::vector<type_field> name_analysis_visitor::build_composite_type(const std::vector<field_declaration>& fields)
{
std::vector<type_field> result;
std::set<std::string> field_names;
for (auto& field : fields)
{
if (field_names.find(field.first) != field_names.cend())
{
add_error<field_duplication_error>(field.first, this->input_file, field.second->position());
}
else
{
field_names.insert(field.first);
field.second->accept(this);
result.push_back(std::make_pair(field.first, this->current_type));
}
}
return result;
}
void name_analysis_visitor::visit(record_type_expression *type_expression)
{
auto result_type = std::make_shared<record_type>();
result_type->fields = build_composite_type(type_expression->fields);
this->current_type = type(result_type);
}
void name_analysis_visitor::visit(union_type_expression *type_expression)
{
auto result_type = std::make_shared<union_type>();
result_type->fields = build_composite_type(type_expression->fields);
this->current_type = type(result_type);
}
void name_analysis_visitor::visit(procedure_type_expression *type_expression)
{
std::shared_ptr<procedure_type> result_type =
std::make_shared<procedure_type>(std::move(build_procedure(*type_expression)));
this->current_type = type(result_type);
}
void name_analysis_visitor::visit(enumeration_type_expression *type_expression)
{
std::shared_ptr<enumeration_type> result_type = std::make_shared<enumeration_type>(type_expression->members);
this->current_type = type(result_type);
}
void name_analysis_visitor::visit(variable_declaration *declaration)
{
declaration->variable_type().accept(this);
for (const auto& variable_identifier : declaration->identifiers)
{
auto variable_symbol = std::make_shared<variable_info>(this->current_type, declaration->is_extern);
variable_symbol->exported = variable_identifier.exported;
if (!this->bag.enter(variable_identifier.name, variable_symbol))
{
add_error<already_declared_error>(variable_identifier.name, this->input_file,
declaration->position());
}
}
}
void name_analysis_visitor::visit(constant_declaration *definition)
{
definition->body().accept(this);
auto constant_symbol = std::make_shared<constant_info>(this->current_literal);
constant_symbol->exported = definition->identifier.exported;
this->bag.enter(definition->identifier.name, constant_symbol);
}
void name_analysis_visitor::visit(procedure_declaration *definition)
{
std::shared_ptr<procedure_info> info;
auto heading = build_procedure(definition->heading());
if (definition->body.has_value())
{
info = std::make_shared<procedure_info>(heading, definition->parameter_names, this->bag.enter());
for (constant_declaration *const constant : definition->body.value().constants())
{
constant->accept(this);
}
for (variable_declaration *const variable : definition->body.value().variables())
{
variable->accept(this);
}
for (statement *const statement : definition->body.value().body())
{
statement->accept(this);
}
this->bag.leave();
}
else
{
info = std::make_shared<procedure_info>(heading, definition->parameter_names);
}
info->exported = definition->identifier.exported;
this->bag.enter(definition->identifier.name, info);
}
void name_analysis_visitor::visit(assign_statement *statement)
{
statement->lvalue().accept(this);
statement->rvalue().accept(this);
}
void name_analysis_visitor::visit(if_statement *statement)
{
statement->body().prerequisite().accept(this);
for (struct statement *const statement : statement->body().statements)
{
statement->accept(this);
}
for (const auto branch : statement->branches)
{
branch->prerequisite().accept(this);
for (struct statement *const statement : branch->statements)
{
statement->accept(this);
}
}
if (statement->alternative != nullptr)
{
for (struct statement *const statement : *statement->alternative)
{
statement->accept(this);
}
}
}
void name_analysis_visitor::visit(import_declaration *)
{
}
void name_analysis_visitor::visit(while_statement *statement)
{
statement->body().prerequisite().accept(this);
for (struct statement *const statement : statement->body().statements)
{
statement->accept(this);
}
for (const auto branch : statement->branches)
{
branch->prerequisite().accept(this);
for (struct statement *const statement : branch->statements)
{
statement->accept(this);
}
}
}
void name_analysis_visitor::visit(return_statement *statement)
{
statement->return_expression().accept(this);
}
void name_analysis_visitor::visit(defer_statement *statement)
{
for (struct statement *const statement : statement->statements)
{
statement->accept(this);
}
}
void name_analysis_visitor::visit(case_statement *statement)
{
statement->condition().accept(this);
for (const switch_case& case_block : statement->cases)
{
for (expression *const case_label : case_block.labels)
{
case_label->accept(this);
}
for (struct statement *const statement : case_block.statements)
{
statement->accept(this);
}
}
if (statement->alternative != nullptr)
{
for (struct statement *const statement : *statement->alternative)
{
statement->accept(this);
}
}
}
void name_analysis_visitor::visit(procedure_call *call)
{
call->callable().accept(this);
for (expression *const argument: call->arguments)
{
argument->accept(this);
}
}
void name_analysis_visitor::visit(unit *unit)
{
for (type_declaration *const type : unit->types)
{
type->accept(this);
}
for (variable_declaration *const variable : unit->variables)
{
variable->accept(this);
}
for (procedure_declaration *const procedure : unit->procedures)
{
procedure->accept(this);
}
}
void name_analysis_visitor::visit(traits_expression *trait)
{
if (!trait->parameters.empty())
{
trait->parameters.front()->accept(this);
trait->types.push_back(this->current_type);
}
}
void name_analysis_visitor::visit(cast_expression *expression)
{
expression->value().accept(this);
expression->target().accept(this);
expression->expression_type = this->current_type;
}
void name_analysis_visitor::visit(binary_expression *expression)
{
expression->lhs().accept(this);
expression->rhs().accept(this);
}
void name_analysis_visitor::visit(unary_expression *expression)
{
expression->operand().accept(this);
}
void name_analysis_visitor::visit(variable_expression *)
{
}
void name_analysis_visitor::visit(array_access_expression *expression)
{
expression->base().accept(this);
expression->index().accept(this);
}
void name_analysis_visitor::visit(field_access_expression *expression)
{
expression->base().accept(this);
}
void name_analysis_visitor::visit(dereference_expression *expression)
{
expression->base().accept(this);
}
void name_analysis_visitor::visit(literal<std::int32_t> *literal)
{
this->current_literal = literal->value;
}
void name_analysis_visitor::visit(literal<std::uint32_t> *literal)
{
this->current_literal = literal->value;
}
void name_analysis_visitor::visit(literal<double> *literal)
{
this->current_literal = literal->value;
}
void name_analysis_visitor::visit(literal<bool> *literal)
{
this->current_literal = literal->value;
}
void name_analysis_visitor::visit(literal<unsigned char> *literal)
{
this->current_literal = literal->value;
}
void name_analysis_visitor::visit(literal<std::nullptr_t> *literal)
{
this->current_literal = literal->value;
}
void name_analysis_visitor::visit(literal<std::string> *literal)
{
this->current_literal = literal->value;
}
declaration_visitor::declaration_visitor(const char *path)
: error_container(path)
{
}
void declaration_visitor::visit(program *program)
{
visit(static_cast<unit *>(program));
}
void declaration_visitor::visit(import_declaration *)
{
}
void declaration_visitor::visit(unit *unit)
{
for (import_declaration *const _import : unit->imports)
{
_import->accept(this);
}
for (type_declaration *const type : unit->types)
{
type->accept(this);
}
for (variable_declaration *const variable : unit->variables)
{
variable->accept(this);
}
for (procedure_declaration *const procedure : unit->procedures)
{
procedure->accept(this);
}
}
void declaration_visitor::visit(type_declaration *definition)
{
const std::string& type_identifier = definition->identifier.name;
if (!this->unresolved.insert({ type_identifier, std::make_shared<alias_type>(type_identifier) }).second)
{
add_error<already_declared_error>(definition->identifier.name, this->input_file,
definition->position());
}
}
void declaration_visitor::visit(variable_declaration *declaration)
{
if (declaration->has_initializer() && declaration->identifiers.size() > 1)
{
add_error<variable_initializer_error>(this->input_file, declaration->position());
}
}
void declaration_visitor::visit(procedure_declaration *definition)
{
if (!definition->body.has_value())
{
return;
}
for (boot::variable_declaration *const variable : definition->body.value().variables())
{
variable->accept(this);
}
}
}

View File

@@ -1,427 +0,0 @@
/* Symbol definitions.
Copyright (C) 2025 Free Software Foundation, Inc.
GCC is free software; you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation; either version 3, or (at your option)
any later version.
GCC is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with GCC; see the file COPYING3. If not see
<http://www.gnu.org/licenses/>. */
#include "elna/boot/symbol.h"
namespace elna::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 \ $(ELNA_OBJS) $(EXTRA_GCC_OBJS) libcommon-target.a \
$(EXTRA_GCC_LIBS) $(LIBS) $(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 # The compiler proper
elna_OBJS = \ elna_OBJS = \
@@ -48,12 +43,9 @@ elna_OBJS = \
elna/elna-tree.o \ elna/elna-tree.o \
elna/elna-builtins.o \ elna/elna-builtins.o \
elna/ast.o \ elna/ast.o \
elna/dependency.o \
elna/driver.o \ elna/driver.o \
elna/lexer.o \ elna/lexer.o \
elna/parser.o \ elna/parser.o \
elna/semantic.o \
elna/symbol.o \
elna/result.o \ elna/result.o \
$(END) $(END)
@@ -61,7 +53,7 @@ elna1$(exeext): attribs.o $(elna_OBJS) $(BACKEND) $(LIBDEPS)
+$(LLINKER) $(ALL_LINKERFLAGS) $(LDFLAGS) -o $@ \ +$(LLINKER) $(ALL_LINKERFLAGS) $(LDFLAGS) -o $@ \
attribs.o $(elna_OBJS) $(BACKEND) $(LIBS) $(BACKENDLIBS) attribs.o $(elna_OBJS) $(BACKEND) $(LIBS) $(BACKENDLIBS)
elna.all.cross: gelna-cross$(exeext) elna.all.cross:
elna.start.encap: gelna$(exeext) elna.start.encap: gelna$(exeext)
elna.rest.encap: elna.rest.encap:
@@ -69,59 +61,23 @@ elna.rest.encap:
# No elna-specific selftests. # No elna-specific selftests.
selftest-elna: selftest-elna:
ELNA_TEXI_FILES = \
elna/gcc/gelna.texi \
$(srcdir)/doc/include/fdl.texi \
$(srcdir)/doc/include/gpl_v3.texi \
$(srcdir)/doc/include/funding.texi \
$(srcdir)/doc/include/gcc-common.texi \
gcc-vers.texi
elna.install-common: installdirs elna.install-common: installdirs
-rm -f $(DESTDIR)$(bindir)/$(ELNA_INSTALL_NAME)$(exeext) -rm -f $(DESTDIR)$(bindir)/$(ELNA_INSTALL_NAME)$(exeext)
$(INSTALL_PROGRAM) gelna$(exeext) $(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); \ rm -f $(DESTDIR)$(bindir)/$(ELNA_TARGET_INSTALL_NAME)$(exeext); \
( cd $(DESTDIR)$(bindir) && \ ( cd $(DESTDIR)$(bindir) && \
$(LN) $(ELNA_INSTALL_NAME)$(exeext) $(ELNA_TARGET_INSTALL_NAME)$(exeext) ); \ $(LN) $(ELNA_INSTALL_NAME)$(exeext) $(ELNA_TARGET_INSTALL_NAME)$(exeext) ); \
fi; \
fi
$(build_htmldir)/gelna/index.html: $(ELNA_TEXI_FILES)
$(mkinstalldirs) $(@D)
rm -f $(@D)/*
$(TEXI2HTML) -I $(gcc_docdir)/include -I $(srcdir)/elna -o $(@D) $<
# Required goals, they still do nothing # Required goals, they still do nothing
elna.install-man: elna.install-man:
elna.install-info: elna.install-info:
elna.install-pdf: elna.install-pdf:
elna.install-plugin: elna.install-plugin:
elna.install-html:
elna.install-html: $(build_htmldir)/gelna
@$(NORMAL_INSTALL)
test -z "$(htmldir)" || $(mkinstalldirs) "$(DESTDIR)$(htmldir)"
@for p in $(build_htmldir)/gelna; do \
if test -f "$$p" || test -d "$$p"; then d=""; else d="$(srcdir)/"; fi; \
f=$(html__strip_dir) \
if test -d "$$d$$p"; then \
echo " $(mkinstalldirs) '$(DESTDIR)$(htmldir)/$$f'"; \
$(mkinstalldirs) "$(DESTDIR)$(htmldir)/$$f" || exit 1; \
echo " $(INSTALL_DATA) '$$d$$p'/* '$(DESTDIR)$(htmldir)/$$f'"; \
$(INSTALL_DATA) "$$d$$p"/* "$(DESTDIR)$(htmldir)/$$f"; \
else \
echo " $(INSTALL_DATA) '$$d$$p' '$(DESTDIR)$(htmldir)/$$f'"; \
$(INSTALL_DATA) "$$d$$p" "$(DESTDIR)$(htmldir)/$$f"; \
fi; \
done
elna.info: elna.info:
elna.dvi: elna.dvi:
elna.pdf: elna.pdf:
elna.html: $(build_htmldir)/gelna/index.html elna.html:
elna.man: elna.man:
elna.mostlyclean: elna.mostlyclean:
elna.clean: elna.clean:
@@ -148,7 +104,7 @@ elna.stagefeedback: stagefeedback-start
-mv elna/*$(objext) stagefeedback/elna -mv elna/*$(objext) stagefeedback/elna
ELNA_INCLUDES = -I $(srcdir)/elna/include -I elna/generated ELNA_INCLUDES = -I $(srcdir)/elna/include -I elna/generated
ELNA_CXXFLAGS = -std=c++17 ELNA_CXXFLAGS = -std=c++11
elna/%.o: elna/boot/%.cc elna/generated/parser.hh elna/generated/location.hh elna/%.o: elna/boot/%.cc elna/generated/parser.hh elna/generated/location.hh
$(COMPILE) $(ELNA_CXXFLAGS) $(ELNA_INCLUDES) $< $(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 along with GCC; see the file COPYING3. If not see
<http://www.gnu.org/licenses/>. */ <http://www.gnu.org/licenses/>. */
#include <algorithm>
#include "elna/gcc/elna-builtins.h" #include "elna/gcc/elna-builtins.h"
#include "elna/gcc/elna1.h" #include "elna/gcc/elna1.h"
#include "stor-layout.h" #include "stor-layout.h"
#include "stringpool.h" #include "stringpool.h"
#include "elna/gcc/elna-tree.h" #include "elna/gcc/elna-tree.h"
namespace elna::gcc namespace elna
{
namespace gcc
{ {
void init_ttree() void init_ttree()
{ {
elna_int_type_node = long_integer_type_node; elna_int_type_node = long_integer_type_node;
elna_word_type_node = size_type_node; elna_word_type_node = size_type_node;
elna_char_type_node = unsigned_char_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_float_type_node = double_type_node;
elna_bool_type_node = boolean_type_node; elna_bool_type_node = boolean_type_node;
@@ -43,232 +43,12 @@ namespace elna::gcc
tree string_ptr_type = build_pointer_type_for_mode(elna_char_type_node, VOIDmode, true); tree string_ptr_type = build_pointer_type_for_mode(elna_char_type_node, VOIDmode, true);
elna_string_length_field_node = build_field(UNKNOWN_LOCATION, elna_string_length_field_node = build_field(UNKNOWN_LOCATION,
elna_string_type_node, "length", build_qualified_type(elna_word_type_node, TYPE_QUAL_CONST)); elna_string_type_node, "length", elna_word_type_node);
elna_string_ptr_field_node = build_field(UNKNOWN_LOCATION, elna_string_ptr_field_node = build_field(UNKNOWN_LOCATION,
elna_string_type_node, "ptr", build_qualified_type(string_ptr_type, TYPE_QUAL_CONST)); elna_string_type_node, "ptr", string_ptr_type);
TYPE_FIELDS(elna_string_type_node) = chainon(elna_string_ptr_field_node, elna_string_length_field_node); TYPE_FIELDS(elna_string_type_node) = chainon(elna_string_ptr_field_node, elna_string_length_field_node);
layout_type(elna_string_type_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/elna-tree.h"
#include "elna/gcc/elna1.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) location_t get_location(const boot::position *position)
{ {
linemap_line_start(line_table, position->line, 0); linemap_line_start(line_table, position->line, 0);
@@ -38,75 +30,51 @@ namespace elna::gcc
return linemap_position_for_column(line_table, position->column); return linemap_position_for_column(line_table, position->column);
} }
std::string print_aggregate_name(tree type, const std::string& kind_name)
{
if (TYPE_IDENTIFIER(type) == NULL_TREE)
{
return kind_name;
}
else
{
return std::string(IDENTIFIER_POINTER(TYPE_IDENTIFIER(type)));
}
}
std::string print_type(tree type) std::string print_type(tree type)
{ {
gcc_assert(TYPE_P(type)); gcc_assert(TYPE_P(type));
tree unqualified_type = get_qualified_type(type, TYPE_UNQUALIFIED); if (type == elna_int_type_node)
tree_code code = TREE_CODE(type);
if (unqualified_type == elna_int_type_node)
{ {
return "Int"; return "Int";
} }
else if (unqualified_type == elna_word_type_node) else if (type == elna_word_type_node)
{ {
return "Word"; return "Word";
} }
else if (unqualified_type == elna_bool_type_node) else if (type == elna_bool_type_node)
{ {
return "Bool"; 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"; return "Float";
} }
else if (unqualified_type == elna_char_type_node) else if (type == elna_char_type_node)
{ {
return "Char"; return "Char";
} }
else if (unqualified_type == elna_string_type_node) else if (type == elna_string_type_node)
{ {
return "String"; 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 "()"; return "()";
} }
else if (POINTER_TYPE_P(unqualified_type)) else if (is_pointer_type(type))
{ {
tree pointer_target_type = TREE_TYPE(type); return std::string("pointer to " + print_type(TREE_TYPE(type)));
if (TREE_CODE(pointer_target_type) == FUNCTION_TYPE)
{
return print_type(pointer_target_type);
} }
else else if (is_procedure_type(type))
{
return std::string("^" + print_type(pointer_target_type));
}
}
else if (code == FUNCTION_TYPE)
{ {
std::string output = "proc("; std::string output = "proc(";
tree parameter_type = TYPE_ARG_TYPES(type); tree parameter_type = TYPE_ARG_TYPES(type);
while (TREE_VALUE(parameter_type) != void_type_node) while (parameter_type != NULL_TREE)
{ {
output += print_type(TREE_VALUE(parameter_type)); output += print_type(TREE_VALUE(parameter_type));
parameter_type = TREE_CHAIN(parameter_type); parameter_type = TREE_CHAIN(parameter_type);
@@ -120,29 +88,23 @@ namespace elna::gcc
} }
} }
output += ')'; output += ')';
tree return_type = TREE_TYPE(type); if (!is_void_type(TREE_TYPE(type)))
if (!is_void_type(return_type))
{ {
output += " -> " + print_type(return_type); output += " -> " + print_type(TREE_TYPE(type));
} }
return output; return output;
} }
else if (code == ARRAY_TYPE) else if (is_array_type(type))
{ {
return "array"; return "array";
} }
else if (code == RECORD_TYPE) else if (TREE_CODE(type) == RECORD_TYPE)
{ {
return print_aggregate_name(unqualified_type, "record"); return "record";
} }
else if (code == UNION_TYPE) else if (TREE_CODE(type) == UNION_TYPE)
{ {
return print_aggregate_name(unqualified_type, "union"); return "union";
}
else if (code == ENUMERAL_TYPE)
{
return print_aggregate_name(unqualified_type, "enumeration");
} }
else else
{ {
@@ -150,18 +112,5 @@ namespace elna::gcc
} }
gcc_unreachable(); 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

@@ -22,7 +22,7 @@ void lang_specific_driver(struct cl_decoded_option ** /* in_decoded_options */,
} }
/* Called before linking. Returns 0 on success and -1 on failure. */ /* Called before linking. Returns 0 on success and -1 on failure. */
int lang_specific_pre_link(void) int lang_specific_pre_link (void)
{ {
return 0; return 0;
} }

View File

@@ -21,14 +21,23 @@ along with GCC; see the file COPYING3. If not see
#include "function.h" #include "function.h"
#include "stor-layout.h" #include "stor-layout.h"
#include "fold-const.h"
#include "diagnostic-core.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) bool is_integral_type(tree type)
{ {
gcc_assert(TYPE_P(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) bool is_numeric_type(tree type)
@@ -36,10 +45,16 @@ namespace elna::gcc
return is_integral_type(type) || type == elna_float_type_node; 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)); 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_procedure_type(tree type)
{
gcc_assert(TYPE_P(type));
return TREE_CODE(type) == FUNCTION_TYPE;
} }
bool is_void_type(tree type) bool is_void_type(tree type)
@@ -47,10 +62,9 @@ namespace elna::gcc
return type == NULL_TREE || type == void_type_node; return type == NULL_TREE || type == void_type_node;
} }
bool is_castable_type(tree type) bool is_record_type(tree type)
{ {
gcc_assert(TYPE_P(type)); return TREE_CODE(type) == RECORD_TYPE;
return INTEGRAL_TYPE_P(type) || POINTER_TYPE_P(type) || TREE_CODE(type) == REAL_TYPE;
} }
bool are_compatible_pointers(tree lhs_type, tree rhs) bool are_compatible_pointers(tree lhs_type, tree rhs)
@@ -58,25 +72,13 @@ namespace elna::gcc
gcc_assert(TYPE_P(lhs_type)); gcc_assert(TYPE_P(lhs_type));
tree rhs_type = TREE_TYPE(rhs); tree rhs_type = TREE_TYPE(rhs);
return (POINTER_TYPE_P(lhs_type) && rhs == elna_pointer_nil_node) return (is_pointer_type(lhs_type) && rhs == elna_pointer_nil_node)
|| (POINTER_TYPE_P(lhs_type) && lhs_type == rhs_type); || (is_pointer_type(lhs_type) && lhs_type == rhs_type);
}
tree prepare_rvalue(tree rvalue)
{
if (DECL_P(rvalue) && TREE_CODE(TREE_TYPE(rvalue)) == FUNCTION_TYPE)
{
return build1(ADDR_EXPR, build_pointer_type_for_mode(TREE_TYPE(rvalue), VOIDmode, true), rvalue);
}
else
{
return rvalue;
}
} }
bool is_assignable_from(tree assignee, tree assignment) bool is_assignable_from(tree assignee, tree assignment)
{ {
return get_qualified_type(TREE_TYPE(assignment), TYPE_UNQUALIFIED) == assignee return TREE_TYPE(assignment) == assignee
|| are_compatible_pointers(assignee, assignment); || are_compatible_pointers(assignee, assignment);
} }
@@ -128,59 +130,51 @@ namespace elna::gcc
return field_declaration; return field_declaration;
} }
tree do_pointer_arithmetic(boot::binary_operator binary_operator, tree do_pointer_arithmetic(boot::binary_operator binary_operator, tree left, tree right)
tree left, tree right, location_t operation_location)
{ {
tree left_type = get_qualified_type(TREE_TYPE(left), TYPE_UNQUALIFIED);
tree right_type = get_qualified_type(TREE_TYPE(right), TYPE_UNQUALIFIED);
if (binary_operator == boot::binary_operator::sum) if (binary_operator == boot::binary_operator::sum)
{ {
tree pointer{ NULL_TREE }; tree pointer{ NULL_TREE };
tree offset{ 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(TREE_TYPE(left)) && is_integral_type(TREE_TYPE(right)))
{ {
pointer = left; pointer = left;
offset = right; offset = right;
pointer_type = left_type;
} }
else if (is_integral_type(left_type) && POINTER_TYPE_P(right_type)) else if (is_integral_type(TREE_TYPE(left)) && is_pointer_type(TREE_TYPE(right)))
{ {
pointer = right; pointer = right;
offset = left; offset = left;
pointer_type = right_type;
} }
else else
{ {
return error_mark_node; return error_mark_node;
} }
tree size_exp = pointer_type == elna_pointer_type_node tree size_exp = fold_convert(TREE_TYPE(offset), size_in_bytes(TREE_TYPE(TREE_TYPE(pointer))));
? size_one_node
: 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_build2(MULT_EXPR, TREE_TYPE(offset), offset, size_exp);
offset = fold_convert(sizetype, offset); 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) else if (binary_operator == boot::binary_operator::subtraction)
{ {
if (POINTER_TYPE_P(left_type) && is_integral_type(right_type)) if (is_pointer_type(TREE_TYPE(left)) && is_integral_type(TREE_TYPE(right)))
{ {
tree pointer_type = left_type; tree pointer_type = TREE_TYPE(left);
tree offset_type = right_type; tree offset_type = TREE_TYPE(right);
tree size_exp = fold_convert(offset_type, size_in_bytes(TREE_TYPE(pointer_type))); tree size_exp = fold_convert(offset_type, size_in_bytes(TREE_TYPE(pointer_type)));
tree convert_expression = fold_build2(MULT_EXPR, offset_type, right, size_exp); tree convert_expression = fold_build2(MULT_EXPR, offset_type, right, size_exp);
convert_expression = fold_convert(sizetype, convert_expression); convert_expression = fold_convert(sizetype, convert_expression);
convert_expression = fold_build1(NEGATE_EXPR, 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 (is_pointer_type(TREE_TYPE(left)) && is_pointer_type(TREE_TYPE(right))
else if (POINTER_TYPE_P(left_type) && POINTER_TYPE_P(right_type) && left_type == right_type) && TREE_TYPE(left) == TREE_TYPE(right))
{ {
return fold_build2_loc(operation_location, POINTER_DIFF_EXPR, ssizetype, left, right); return fold_build2(POINTER_DIFF_EXPR, ssizetype, left, right);
} }
} }
gcc_unreachable(); gcc_unreachable();
@@ -190,126 +184,21 @@ namespace elna::gcc
tree_code operator_code, tree left, tree right, tree target_type) tree_code operator_code, tree left, tree right, tree target_type)
{ {
location_t expression_location = get_location(&expression->position()); location_t expression_location = get_location(&expression->position());
tree left_type = get_qualified_type(TREE_TYPE(left), TYPE_UNQUALIFIED); tree left_type = TREE_TYPE(left);
tree right_type = get_qualified_type(TREE_TYPE(right), TYPE_UNQUALIFIED); tree right_type = TREE_TYPE(right);
if (condition) 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 else
{ {
error_at(expression_location, error_at(expression_location,
"invalid operands of type '%s' and '%s' for operator %s", "invalid operands of type %s and %s for operator %s",
print_type(left_type).c_str(), print_type(right_type).c_str(), print_type(left_type).c_str(), print_type(right_type).c_str(),
elna::boot::print_binary_operator(expression->operation())); elna::boot::print_binary_operator(expression->operation()));
return error_mark_node; 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,12 @@ along with GCC; see the file COPYING3. If not see
#include "langhooks-def.h" #include "langhooks-def.h"
#include <fstream> #include <fstream>
#include "elna/boot/dependency.h" #include "elna/boot/driver.h"
#include "elna/gcc/elna-tree.h" #include "elna/gcc/elna-tree.h"
#include "elna/gcc/elna-generic.h" #include "elna/gcc/elna-generic.h"
#include "elna/gcc/elna-diagnostic.h" #include "elna/gcc/elna-diagnostic.h"
#include "elna/gcc/elna-builtins.h" #include "elna/gcc/elna-builtins.h"
#include "parser.hh"
tree elna_global_trees[ELNA_TI_MAX]; tree elna_global_trees[ELNA_TI_MAX];
hash_map<nofree_string_hash, tree> *elna_global_decls = nullptr; hash_map<nofree_string_hash, tree> *elna_global_decls = nullptr;
@@ -62,64 +63,43 @@ static bool elna_langhook_init(void)
return true; return true;
} }
using dependency_state = elna::boot::dependency_state<std::shared_ptr<elna::gcc::symbol_table>>; static void elna_parse_file(const char *filename)
static elna::boot::dependency elna_parse_file(dependency_state& state, 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()); for (const auto& error : driver.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); auto gcc_location = elna::gcc::get_location(&error->position);
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()) error_at(gcc_location, error->what().c_str());
}
}
else
{ {
elna_parse_file(state, sub_path.c_str()); elna::gcc::generic_visitor generic_visitor{ std::make_shared<elna::boot::symbol_table<tree>>() };
cached_import = state.cache.find(sub_path);
}
outcome_bag.add_import(cached_import->second);
}
outcome.errors() = analyze_semantics(filename, outcome.tree, outcome_bag);
if (outcome.has_errors()) generic_visitor.visit(driver.tree.get());
{
elna::gcc::report_errors(outcome.errors());
return outcome;
} }
state.cache.insert({ filename, outcome_bag }); linemap_add(line_table, LC_LEAVE, 0, NULL, 0);
elna::gcc::rewrite_symbol_table(outcome_bag.leave(), state.custom);
return outcome;
} }
static void elna_langhook_parse_file(void) 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++) for (unsigned int i = 0; i < num_in_fnames; i++)
{ {
elna::boot::dependency outcome = elna_parse_file(state, in_fnames[i]); elna_parse_file(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);
} }
} }
@@ -206,11 +186,6 @@ static tree elna_langhook_builtin_function(tree decl)
return decl; return decl;
} }
static unsigned int elna_langhook_option_lang_mask(void)
{
return CL_Elna;
}
/* Creates an expression whose value is that of EXPR, converted to type TYPE. /* Creates an expression whose value is that of EXPR, converted to type TYPE.
This function implements all reasonable scalar conversions. */ This function implements all reasonable scalar conversions. */
tree convert(tree type, tree expr) tree convert(tree type, tree expr)
@@ -247,9 +222,6 @@ tree convert(tree type, tree expr)
#undef LANG_HOOKS_IDENTIFIER_SIZE #undef LANG_HOOKS_IDENTIFIER_SIZE
#define LANG_HOOKS_IDENTIFIER_SIZE sizeof(struct tree_identifier) #define LANG_HOOKS_IDENTIFIER_SIZE sizeof(struct tree_identifier)
#undef LANG_HOOKS_OPTION_LANG_MASK
#define LANG_HOOKS_OPTION_LANG_MASK elna_langhook_option_lang_mask
struct lang_hooks lang_hooks = LANG_HOOKS_INITIALIZER; struct lang_hooks lang_hooks = LANG_HOOKS_INITIALIZER;
#include "gt-elna-elna1.h" #include "gt-elna-elna1.h"

View File

@@ -1,135 +0,0 @@
\input texinfo @c -*-texinfo-*-
@setfilename gelna.info
@settitle The GNU Elna Compiler
@c Create a separate index for command line options
@defcodeindex op
@c Merge the standard indexes into a single one.
@syncodeindex fn cp
@syncodeindex vr cp
@syncodeindex ky cp
@syncodeindex pg cp
@syncodeindex tp cp
@include gcc-common.texi
@c Copyright years for this manual.
@set copyrights-elna 2025
@copying
@c man begin COPYRIGHT
Copyright @copyright{} @value{copyrights-elna} Free Software Foundation, Inc.
Permission is granted to copy, distribute and/or modify this document
under the terms of the GNU Free Documentation License, Version 1.3 or
any later version published by the Free Software Foundation; with no
Invariant Sections, no Front-Cover Texts, and no Back-Cover Texts.
A copy of the license is included in the
@c man end
section entitled ``GNU Free Documentation License''.
@ignore
@c man begin COPYRIGHT
man page gfdl(7).
@c man end
@end ignore
@end copying
@ifinfo
@format
@dircategory Software development
@direntry
* Gelna: (gelna). A GCC-based compiler for the Elna language
@end direntry
@end format
@insertcopying
@end ifinfo
@titlepage
@title The GNU Elna Compiler
@versionsubtitle
@author Eugen Wissner
@page
@vskip 0pt plus 1filll
Published by the Free Software Foundation @*
51 Franklin Street, Fifth Floor@*
Boston, MA 02110-1301, USA@*
@sp 1
@insertcopying
@end titlepage
@contents
@page
@node Top
@top Introduction
This manual describes how to use @command{gelna}, the GNU compiler for
the Elna programming language. This manual is specifically about how to
invoke @command{gelna}.
@menu
* Copying:: The GNU General Public License.
* GNU Free Documentation License::
How you can share and copy this manual.
* Invoking gelna:: How to run gelna.
* Option Index:: Index of command line options.
* Keyword Index:: Index of concepts.
@end menu
@include gpl_v3.texi
@include fdl.texi
@node Invoking gelna
@chapter Invoking gelna
@c man title gelna A GCC-based compiler for the Elna language
@ignore
@c man begin SYNOPSIS gelna
gelna [@option{-c}|@option{-S}]
[@option{-g}] [@option{-pg}]
[@option{-o} @var{outfile}] @var{infile}@dots{}
Only the most useful options are listed here; see below for the
remainder.
@c man end
@c man begin SEEALSO
gpl(7), gfdl(7), fsf-funding(7), gcc(1)
and the Info entries for @file{gelna} and @file{gcc}.
@c man end
@end ignore
@c man begin DESCRIPTION gelna
The @command{gelna} command is a frontend to @command{gcc} and
supports many of the same options. @xref{Option Summary, , Option
Summary, gcc, Using the GNU Compiler Collection (GCC)}. This manual
only documents the options specific to @command{gelna}.
@c man end
@c man begin OPTIONS gelna
@c man end
@node Option Index
@unnumbered Option Index
@command{gelna}'s command line options are indexed here without any
initial @samp{-} or @samp{--}. Where an option has both positive and
negative forms (such as -foption and -fno-option), relevant entries in
the manual are indexed under the most appropriate form; it may sometimes
be useful to look up both forms.
@printindex op
@node Keyword Index
@unnumbered Keyword Index
@printindex cp
@bye

View File

@@ -19,9 +19,7 @@ along with GCC; see the file COPYING3. If not see
{".elna", "@elna", nullptr, 0, 0}, {".elna", "@elna", nullptr, 0, 0},
{"@elna", {"@elna",
"elna1 %i \ "elna1 %i \
%{!Q:-quiet} " DUMPS_OPTIONS("") " %{m*} %{aux-info*} \ %{!Q:-quiet} \
%{g*} %{O*} %{W*&pedantic*} %{w} %{std*&ansi&trigraphs} \
%{pg:-p} %{p} %{f*} %{undef} \
%{!fsyntax-only:%{S:%W{o*}%{!o*:-o %w%b.s}}} \ %{!fsyntax-only:%{S:%W{o*}%{!o*:-o %w%b.s}}} \
%{fsyntax-only:-o %j} %{-param*} \ %{fsyntax-only:-o %j} %{-param*} \
%{!fsyntax-only:%(invoke_as)}", %{!fsyntax-only:%(invoke_as)}",

View File

@@ -1,23 +0,0 @@
; lang.opt -- Options for the Elna front end.
; 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/>.
; See the GCC internals manual for a description of this file's format.
; Please try to keep this file in ASCII collating order.
Language
Elna

View File

@@ -1,2 +0,0 @@
; Autogenerated by regenerate-opt-urls.py from gcc/lang.opt and generated HTML

View File

@@ -21,11 +21,11 @@ along with GCC; see the file COPYING3. If not see
#include <memory> #include <memory>
#include <string> #include <string>
#include <vector> #include <vector>
#include <optional>
#include "elna/boot/symbol.h"
#include "elna/boot/result.h" #include "elna/boot/result.h"
namespace elna::boot namespace elna
{
namespace boot
{ {
enum class binary_operator enum class binary_operator
{ {
@@ -42,9 +42,7 @@ namespace elna::boot
greater_equal, greater_equal,
disjunction, disjunction,
conjunction, conjunction,
exclusive_disjunction, exclusive_disjunction
shift_left,
shift_right
}; };
enum class unary_operator enum class unary_operator
@@ -55,37 +53,32 @@ namespace elna::boot
}; };
class variable_declaration; class variable_declaration;
class constant_declaration; class constant_definition;
class procedure_declaration; class procedure_definition;
class type_declaration; class type_definition;
class procedure_call; class call_expression;
class cast_expression; class cast_expression;
class size_of_expression;
class assign_statement; class assign_statement;
class if_statement; class if_statement;
class import_declaration;
class while_statement; class while_statement;
class return_statement; class return_statement;
class case_statement; class call_statement;
class traits_expression; class block;
class unit;
class program; class program;
class binary_expression; class binary_expression;
class unary_expression; class unary_expression;
class named_type_expression; class basic_type_expression;
class array_type_expression; class array_type_expression;
class pointer_type_expression; class pointer_type_expression;
class record_type_expression; class record_type_expression;
class union_type_expression; class union_type_expression;
class procedure_type_expression;
class enumeration_type_expression;
class variable_expression; class variable_expression;
class array_access_expression; class array_access_expression;
class field_access_expression; class field_access_expression;
class dereference_expression; class dereference_expression;
class designator_expression;
class literal_expression;
template<typename T> template<typename T>
class literal; class number_literal;
class defer_statement; class defer_statement;
/** /**
@@ -94,88 +87,78 @@ namespace elna::boot
struct parser_visitor struct parser_visitor
{ {
virtual void visit(variable_declaration *) = 0; virtual void visit(variable_declaration *) = 0;
virtual void visit(constant_declaration *) = 0; virtual void visit(constant_definition *) = 0;
virtual void visit(procedure_declaration *) = 0; virtual void visit(procedure_definition *) = 0;
virtual void visit(type_declaration *) = 0; virtual void visit(type_definition *) = 0;
virtual void visit(procedure_call *) = 0; virtual void visit(call_expression *) = 0;
virtual void visit(cast_expression *) = 0; virtual void visit(cast_expression *) = 0;
virtual void visit(traits_expression *) = 0; virtual void visit(size_of_expression *) = 0;
virtual void visit(call_statement *) = 0;
virtual void visit(assign_statement *) = 0; virtual void visit(assign_statement *) = 0;
virtual void visit(if_statement *) = 0; virtual void visit(if_statement *) = 0;
virtual void visit(import_declaration *) = 0;
virtual void visit(while_statement *) = 0; virtual void visit(while_statement *) = 0;
virtual void visit(return_statement *) = 0; virtual void visit(return_statement *) = 0;
virtual void visit(defer_statement *) = 0; virtual void visit(defer_statement *) = 0;
virtual void visit(case_statement *) = 0; virtual void visit(block *) = 0;
virtual void visit(unit *) = 0;
virtual void visit(program *) = 0; virtual void visit(program *) = 0;
virtual void visit(binary_expression *) = 0; virtual void visit(binary_expression *) = 0;
virtual void visit(unary_expression *) = 0; virtual void visit(unary_expression *) = 0;
virtual void visit(named_type_expression *) = 0; virtual void visit(basic_type_expression *) = 0;
virtual void visit(array_type_expression *) = 0; virtual void visit(array_type_expression *) = 0;
virtual void visit(pointer_type_expression *) = 0; virtual void visit(pointer_type_expression *) = 0;
virtual void visit(record_type_expression *) = 0; virtual void visit(record_type_expression *) = 0;
virtual void visit(union_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(variable_expression *) = 0;
virtual void visit(array_access_expression *) = 0; virtual void visit(array_access_expression *) = 0;
virtual void visit(field_access_expression *) = 0; virtual void visit(field_access_expression *is_field_access) = 0;
virtual void visit(dereference_expression *) = 0; virtual void visit(dereference_expression *is_dereference) = 0;
virtual void visit(literal<std::int32_t> *) = 0; virtual void visit(number_literal<std::int32_t> *) = 0;
virtual void visit(literal<std::uint32_t> *) = 0; virtual void visit(number_literal<std::uint32_t> *) = 0;
virtual void visit(literal<double> *) = 0; virtual void visit(number_literal<double> *) = 0;
virtual void visit(literal<bool> *) = 0; virtual void visit(number_literal<bool> *) = 0;
virtual void visit(literal<unsigned char> *) = 0; virtual void visit(number_literal<unsigned char> *) = 0;
virtual void visit(literal<std::nullptr_t> *) = 0; virtual void visit(number_literal<std::nullptr_t> *) = 0;
virtual void visit(literal<std::string> *) = 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(); virtual void visit(variable_declaration *) override;
virtual void visit(constant_definition *definition) override;
public: virtual void visit(procedure_definition *definition) override;
[[noreturn]] virtual void visit(named_type_expression *) override; virtual void visit(type_definition *definition) override;
[[noreturn]] virtual void visit(array_type_expression *) override; virtual void visit(call_expression *expression) override;
[[noreturn]] virtual void visit(pointer_type_expression *) override; virtual void visit(cast_expression *expression) override;
[[noreturn]] virtual void visit(program *) override; virtual void visit(size_of_expression *expression) override;
[[noreturn]] virtual void visit(type_declaration *) override; virtual void visit(call_statement *statement) override;
[[noreturn]] virtual void visit(record_type_expression *) override; virtual void visit(assign_statement *statement) override;
[[noreturn]] virtual void visit(union_type_expression *) override; virtual void visit(if_statement *) override;
[[noreturn]] virtual void visit(procedure_type_expression *) override; virtual void visit(while_statement *) override;
[[noreturn]] virtual void visit(enumeration_type_expression *) override; virtual void visit(return_statement *) override;
virtual void visit(defer_statement *defer) override;
[[noreturn]] virtual void visit(variable_declaration *) override; virtual void visit(block *block) override;
[[noreturn]] virtual void visit(constant_declaration *) override; virtual void visit(program *program) override;
[[noreturn]] virtual void visit(procedure_declaration *) override; virtual void visit(binary_expression *expression) override;
[[noreturn]] virtual void visit(assign_statement *) override; virtual void visit(unary_expression *expression) override;
[[noreturn]] virtual void visit(if_statement *) override; virtual void visit(basic_type_expression *) override;
[[noreturn]] virtual void visit(import_declaration *) override; virtual void visit(array_type_expression *expression) override;
[[noreturn]] virtual void visit(while_statement *) override; virtual void visit(pointer_type_expression *) override;
[[noreturn]] virtual void visit(return_statement *) override; virtual void visit(record_type_expression *expression) override;
[[noreturn]] virtual void visit(defer_statement *) override; virtual void visit(union_type_expression *expression) override;
[[noreturn]] virtual void visit(case_statement *) override; virtual void visit(variable_expression *) override;
[[noreturn]] virtual void visit(procedure_call *) override; virtual void visit(array_access_expression *expression) override;
[[noreturn]] virtual void visit(unit *) override; virtual void visit(field_access_expression *expression) override;
[[noreturn]] virtual void visit(cast_expression *) override; virtual void visit(dereference_expression *expression) override;
[[noreturn]] virtual void visit(traits_expression *) override; virtual void visit(number_literal<std::int32_t> *) override;
[[noreturn]] virtual void visit(binary_expression *) override; virtual void visit(number_literal<std::uint32_t> *) override;
[[noreturn]] virtual void visit(unary_expression *) override; virtual void visit(number_literal<double> *) override;
[[noreturn]] virtual void visit(variable_expression *) override; virtual void visit(number_literal<bool> *) override;
[[noreturn]] virtual void visit(array_access_expression *) override; virtual void visit(number_literal<unsigned char> *) override;
[[noreturn]] virtual void visit(field_access_expression *) override; virtual void visit(number_literal<std::nullptr_t> *) override;
[[noreturn]] virtual void visit(dereference_expression *) override; virtual void visit(number_literal<std::string> *) 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;
}; };
/** /**
@@ -192,8 +175,8 @@ namespace elna::boot
explicit node(const position position); explicit node(const position position);
public: public:
virtual void accept(parser_visitor *visitor) = 0; virtual ~node() = default;
virtual ~node() = 0; virtual void accept(parser_visitor *) = 0;
/** /**
* \return Node position in the source code. * \return Node position in the source code.
@@ -201,32 +184,35 @@ namespace elna::boot
const struct position& position() const; const struct position& position() const;
}; };
class statement : public virtual node class statement : public node
{ {
protected:
/**
* \param position Source code position.
*/
explicit statement(const struct position position);
}; };
class expression : public virtual node class expression : public node
{ {
public: protected:
virtual cast_expression *is_cast(); /**
virtual traits_expression *is_traits(); * \param position Source code position.
virtual binary_expression *is_binary(); */
virtual unary_expression *is_unary(); explicit expression(const struct position position);
virtual designator_expression *is_designator();
virtual procedure_call *is_call_expression();
virtual literal_expression *is_literal();
}; };
/** /**
* Symbol definition. * Symbol definition.
*/ */
class declaration : public node class definition : public node
{ {
protected: protected:
declaration(const struct position position, identifier_definition identifier); definition(const struct position position, const std::string& identifier, const bool exported);
public: public:
const identifier_definition identifier; const std::string identifier;
const bool exported;
}; };
/** /**
@@ -235,234 +221,208 @@ namespace elna::boot
class type_expression : public node class type_expression : public node
{ {
public: public:
virtual named_type_expression *is_named(); virtual basic_type_expression *is_basic();
virtual array_type_expression *is_array(); virtual array_type_expression *is_array();
virtual pointer_type_expression *is_pointer(); virtual pointer_type_expression *is_pointer();
virtual record_type_expression *is_record(); virtual record_type_expression *is_record();
virtual union_type_expression *is_union(); virtual union_type_expression *is_union();
virtual procedure_type_expression *is_procedure();
virtual enumeration_type_expression *is_enumeration();
protected: protected:
type_expression(const struct position position); 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 basic_type_expression final : public type_expression
{ {
public: const std::string m_name;
const std::string name;
named_type_expression(const struct position position, const std::string& name); public:
void accept(parser_visitor *visitor) override; /**
named_type_expression *is_named() override; * \param position Source code position.
* \param name Type name.
*/
basic_type_expression(const struct position position, const std::string& name);
virtual void accept(parser_visitor *visitor) override;
const std::string& base_name();
basic_type_expression *is_basic() override;
}; };
class array_type_expression : public type_expression class array_type_expression final : public type_expression
{ {
type_expression *m_base; type_expression *m_base;
public: public:
const std::uint32_t size; const std::uint32_t size;
array_type_expression(const struct position position, array_type_expression(const struct position position, type_expression *base, const std::uint32_t size);
type_expression *base, const std::uint32_t size); virtual void accept(parser_visitor *visitor) override;
~array_type_expression();
void accept(parser_visitor *visitor) override;
array_type_expression *is_array() override;
type_expression& base(); type_expression& base();
array_type_expression *is_array() override;
virtual ~array_type_expression() override;
}; };
class pointer_type_expression : public type_expression class pointer_type_expression final : public type_expression
{ {
type_expression *m_base; type_expression *m_base;
public: public:
pointer_type_expression(const struct position position, type_expression *base); pointer_type_expression(const struct position position, type_expression *base);
~pointer_type_expression(); virtual void accept(parser_visitor *visitor) override;
void accept(parser_visitor *visitor) override;
pointer_type_expression *is_pointer() override;
type_expression& base(); type_expression& base();
pointer_type_expression *is_pointer() override;
virtual ~pointer_type_expression() override;
}; };
using field_declaration = std::pair<std::string, type_expression *>; using field_t = std::pair<std::string, type_expression *>;
using fields_t = std::vector<field_t>;
class record_type_expression : public type_expression class composite_type_expression : public type_expression
{
protected:
composite_type_expression(const struct position position, fields_t&& fields);
public:
fields_t fields;
virtual ~composite_type_expression() override;
};
class record_type_expression final : public composite_type_expression
{ {
public: public:
const std::vector<field_declaration> fields; record_type_expression(const struct position position, fields_t&& fields);
record_type_expression(const struct position position, std::vector<field_declaration>&& fields); virtual void accept(parser_visitor *visitor) override;
~record_type_expression();
void accept(parser_visitor *visitor) override;
record_type_expression *is_record() override; record_type_expression *is_record() override;
}; };
class union_type_expression : public type_expression class union_type_expression final : public composite_type_expression
{ {
public: public:
std::vector<field_declaration> fields; union_type_expression(const struct position position, fields_t&& fields);
union_type_expression(const struct position position, std::vector<field_declaration>&& fields); virtual void accept(parser_visitor *visitor) override;
~union_type_expression();
void accept(parser_visitor *visitor) override;
union_type_expression *is_union() 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;
};
/** /**
* Variable declaration. * Variable declaration.
*/ */
class variable_declaration : public node class variable_declaration : public definition
{ {
std::shared_ptr<type_expression> m_variable_type; type_expression *m_type;
public: public:
variable_declaration(const struct position position, variable_declaration(const struct position position, const std::string& identifier,
std::vector<identifier_definition>&& identifier, std::shared_ptr<type_expression> variable_type, const bool exported, type_expression *type);
expression *body = nullptr); virtual void accept(parser_visitor *visitor) override;
variable_declaration(const struct position position,
std::vector<identifier_definition>&& identifier, std::shared_ptr<type_expression> variable_type,
std::monostate);
void accept(parser_visitor *visitor) override; type_expression& type();
bool has_initializer() const; virtual ~variable_declaration() override;
const std::vector<identifier_definition> identifiers;
type_expression& variable_type();
expression *const body{ nullptr };
const bool is_extern{ false };
}; };
/** /**
* Literal expression. * Literal expression.
*/ */
class literal_expression : public expression class literal : public expression
{ {
public:
literal_expression *is_literal() override;
protected: protected:
literal_expression(); explicit literal(const struct position position);
}; };
/** /**
* Constant definition. * Constant definition.
*/ */
class constant_declaration : public declaration class constant_definition : public definition
{ {
expression *m_body; literal *m_body;
public: public:
constant_declaration(const struct position position, identifier_definition identifier,
expression *body);
void accept(parser_visitor *visitor) override;
expression& body();
virtual ~constant_declaration() override;
};
/** /**
* Procedure type. * \param position Source code position.
* \param identifier Constant name.
* \param body Constant value.
*/ */
class procedure_type_expression : public type_expression constant_definition(const struct position position, const std::string& identifier,
{ const bool exported, literal *body);
public: virtual void accept(parser_visitor *visitor) override;
using return_t = return_declaration<type_expression *>;
const return_t return_type; literal& body();
std::vector<type_expression *> parameters;
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 ~constant_definition() override;
}; };
/** /**
* Procedure definition. * Procedure definition.
*/ */
class procedure_declaration : public declaration class procedure_definition : public definition
{ {
procedure_type_expression *m_heading; type_expression *m_return_type{ nullptr };
block *m_body{ nullptr };
public: public:
std::optional<block> body; std::vector<variable_declaration *> parameters;
std::vector<std::string> parameter_names;
procedure_declaration(const struct position position, identifier_definition identifier, procedure_definition(const struct position position, const std::string& identifier,
procedure_type_expression *heading, block&& body); const bool exported, type_expression *return_type = nullptr);
procedure_declaration(const struct position position, identifier_definition identifier, virtual void accept(parser_visitor *visitor) override;
procedure_type_expression *heading);
void accept(parser_visitor *visitor) override;
procedure_type_expression& heading(); type_expression *return_type();
virtual ~procedure_declaration() override; block *body();
procedure_definition *add_body(block *procedure_body);
virtual ~procedure_definition() override;
}; };
/** /**
* Type definition. * Type definition.
*/ */
class type_declaration : public declaration class type_definition : public definition
{ {
type_expression *m_body; type_expression *m_body;
public: public:
type_declaration(const struct position position, identifier_definition identifier, type_definition(const struct position position, const std::string& identifier,
type_expression *expression); const bool exported, type_expression *expression);
~type_declaration(); virtual void accept(parser_visitor *visitor) override;
void accept(parser_visitor *visitor) override;
type_expression& body(); type_expression& body();
virtual ~type_definition() override;
};
/**
* Procedure call expression.
*/
class call_expression : public expression
{
std::string m_name;
std::vector<expression *> m_arguments;
public:
/**
* \param position Source code position.
* \param name Callable's name.
*/
call_expression(const struct position position, const std::string& name);
virtual void accept(parser_visitor *visitor) override;
std::string& name();
std::vector<expression *>& arguments();
virtual ~call_expression() override;
}; };
/** /**
@@ -474,11 +434,8 @@ namespace elna::boot
expression *m_value; expression *m_value;
public: public:
type expression_type;
cast_expression(const struct position position, type_expression *target, expression *value); cast_expression(const struct position position, type_expression *target, expression *value);
void accept(parser_visitor *visitor) override; virtual void accept(parser_visitor *visitor) override;
cast_expression *is_cast() override;
type_expression& target(); type_expression& target();
expression& value(); expression& value();
@@ -486,18 +443,33 @@ namespace elna::boot
virtual ~cast_expression() override; virtual ~cast_expression() override;
}; };
class traits_expression : public expression /**
* sizeOf operator.
*/
class size_of_expression : public expression
{ {
type_expression *m_body;
public: public:
std::vector<type_expression *> parameters; size_of_expression(const struct position position, type_expression *body);
const std::string name; virtual void accept(parser_visitor *visitor) override;
std::vector<type> types;
traits_expression(const struct position position, const std::string& name); type_expression& body();
~traits_expression();
void accept(parser_visitor *visitor) override; virtual ~size_of_expression() override;
traits_expression *is_traits() override; };
class call_statement : public statement
{
call_expression *m_body;
public:
call_statement(const struct position position, call_expression *body);
virtual void accept(parser_visitor *visitor) override;
call_expression& body();
virtual ~call_statement() override;
}; };
/** /**
@@ -508,9 +480,9 @@ namespace elna::boot
expression *m_prerequisite; expression *m_prerequisite;
public: public:
const std::vector<statement *> statements; std::vector<statement *> statements;
conditional_statements(expression *prerequisite, std::vector<statement *>&& statements); conditional_statements(expression *prerequisite);
expression& prerequisite(); expression& prerequisite();
@@ -519,37 +491,17 @@ namespace elna::boot
class return_statement : public statement class return_statement : public statement
{ {
expression *m_return_expression{ nullptr };
public: public:
expression *m_return_expression;
return_statement(const struct position position, expression *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; 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 class designator_expression : public expression
{ {
public: public:
@@ -558,21 +510,19 @@ namespace elna::boot
virtual field_access_expression *is_field_access(); virtual field_access_expression *is_field_access();
virtual dereference_expression *is_dereference(); virtual dereference_expression *is_dereference();
designator_expression *is_designator() override;
void accept(parser_visitor *visitor);
~designator_expression() = 0;
protected: protected:
designator_expression(); designator_expression(const struct position position);
}; };
class variable_expression : public designator_expression, public literal_expression class variable_expression : public designator_expression
{ {
public: std::string m_name;
const std::string name;
public:
variable_expression(const struct position position, 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;
const std::string& name() const;
variable_expression *is_variable() override; variable_expression *is_variable() override;
}; };
@@ -584,7 +534,7 @@ namespace elna::boot
public: public:
array_access_expression(const struct position position, expression *base, expression *index); 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& base();
expression& index(); expression& index();
@@ -602,7 +552,7 @@ namespace elna::boot
public: public:
field_access_expression(const struct position position, expression *base, field_access_expression(const struct position position, expression *base,
const std::string& field); const std::string& field);
void accept(parser_visitor *visitor) override; virtual void accept(parser_visitor *visitor) override;
expression& base(); expression& base();
std::string& field(); std::string& field();
@@ -618,7 +568,7 @@ namespace elna::boot
public: public:
dereference_expression(const struct position position, expression *base); dereference_expression(const struct position position, expression *base);
void accept(parser_visitor *visitor) override; virtual void accept(parser_visitor *visitor) override;
expression& base(); expression& base();
@@ -627,25 +577,6 @@ namespace elna::boot
~dereference_expression() override; ~dereference_expression() override;
}; };
/**
* Procedure call expression.
*/
class procedure_call : public expression, public statement
{
designator_expression *m_callable;
public:
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;
designator_expression& callable();
virtual ~procedure_call() override;
};
class assign_statement : public statement class assign_statement : public statement
{ {
designator_expression *m_lvalue; designator_expression *m_lvalue;
@@ -659,7 +590,7 @@ namespace elna::boot
*/ */
assign_statement(const struct position position, designator_expression *lvalue, assign_statement(const struct position position, designator_expression *lvalue,
expression *rvalue); expression *rvalue);
void accept(parser_visitor *visitor) override; virtual void accept(parser_visitor *visitor) override;
designator_expression& lvalue(); designator_expression& lvalue();
expression& rvalue(); expression& rvalue();
@@ -673,33 +604,21 @@ namespace elna::boot
class if_statement : public statement class if_statement : public statement
{ {
conditional_statements *m_body; conditional_statements *m_body;
std::vector<statement *> *m_alternative;
public: public:
const std::vector<conditional_statements *> branches; std::vector<conditional_statements *> branches;
const std::vector<statement *> *alternative;
if_statement(const struct position position, conditional_statements *body, if_statement(const struct position position, conditional_statements *body,
std::vector<conditional_statements *>&& branches,
std::vector<statement *> *alternative = nullptr); std::vector<statement *> *alternative = nullptr);
void accept(parser_visitor *visitor) override; virtual void accept(parser_visitor *visitor) override;
conditional_statements& body(); conditional_statements& body();
std::vector<statement *> *alternative();
virtual ~if_statement() override; 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. * While-statement.
*/ */
@@ -708,55 +627,51 @@ namespace elna::boot
conditional_statements *m_body; conditional_statements *m_body;
public: public:
const std::vector<conditional_statements *> branches; while_statement(const struct position position, conditional_statements *body);
virtual void accept(parser_visitor *visitor) override;
while_statement(const struct position position, conditional_statements *body,
std::vector<conditional_statements *>&& branches);
void accept(parser_visitor *visitor) override;
conditional_statements& body(); conditional_statements& body();
virtual ~while_statement() override; virtual ~while_statement() override;
}; };
class unit : public node class block : public node
{ {
public: public:
std::vector<import_declaration *> imports;
std::vector<constant_declaration *> constants;
std::vector<type_declaration *> types;
std::vector<variable_declaration *> variables; std::vector<variable_declaration *> variables;
std::vector<procedure_declaration *> procedures; std::vector<constant_definition *> constants;
unit(const struct position position);
virtual void accept(parser_visitor *visitor) override;
virtual ~unit() override;
};
class program : public unit
{
public:
std::vector<statement *> body; 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); program(const struct position position);
void accept(parser_visitor *visitor) override; virtual void accept(parser_visitor *visitor) override;
virtual ~program() override; virtual ~program() override;
}; };
template<typename T> template<typename T>
class literal : public literal_expression class number_literal : public literal
{ {
public: public:
T value; T value;
literal(const struct position position, const T& value) number_literal(const struct position position, const T& value)
: node(position), value(value) : literal(position), value(value)
{ {
} }
void accept(parser_visitor *visitor) override virtual void accept(parser_visitor *visitor) override
{ {
visitor->visit(this); visitor->visit(this);
} }
@@ -765,10 +680,10 @@ namespace elna::boot
class defer_statement : public statement class defer_statement : public statement
{ {
public: public:
const std::vector<statement *> statements; std::vector<statement *> statements;
defer_statement(const struct position position, std::vector<statement *>&& statements); defer_statement(const struct position position);
void accept(parser_visitor *visitor) override; virtual void accept(parser_visitor *visitor) override;
virtual ~defer_statement() override; virtual ~defer_statement() override;
}; };
@@ -783,9 +698,7 @@ namespace elna::boot
binary_expression(const struct position position, expression *lhs, binary_expression(const struct position position, expression *lhs,
expression *rhs, const binary_operator operation); expression *rhs, const binary_operator operation);
void accept(parser_visitor *visitor) override; virtual void accept(parser_visitor *visitor) override;
binary_expression *is_binary() override;
expression& lhs(); expression& lhs();
expression& rhs(); expression& rhs();
binary_operator operation() const; binary_operator operation() const;
@@ -802,9 +715,7 @@ namespace elna::boot
unary_expression(const struct position position, expression *operand, unary_expression(const struct position position, expression *operand,
const unary_operator operation); const unary_operator operation);
void accept(parser_visitor *visitor) override; virtual void accept(parser_visitor *visitor) override;
unary_expression *is_unary() override;
expression& operand(); expression& operand();
unary_operator operation() const; unary_operator operation() const;
@@ -813,3 +724,4 @@ namespace elna::boot
const char *print_binary_operator(const binary_operator operation); 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 #pragma once
#include <list>
#include <optional> #include <optional>
#include "elna/boot/ast.h" #include "elna/boot/ast.h"
#include "location.hh" #include "location.hh"
namespace elna::boot namespace elna
{
namespace boot
{ {
position make_position(const yy::location& location); position make_position(const yy::location& location);
@@ -36,16 +39,22 @@ namespace elna::boot
virtual std::string what() const override; 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: public:
std::unique_ptr<unit> tree; std::unique_ptr<program> tree;
driver(const char *input_file); 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'; constexpr char escape_invalid_char = '\xff';
char escape_char(char escape); 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 <cstddef>
#include <string> #include <string>
#include <deque>
#include <memory>
#include <variant>
namespace elna::boot namespace elna
{
namespace boot
{ {
/** /**
* Position in the source text. * Position in the source text.
@@ -43,82 +42,28 @@ namespace elna::boot
class error class error
{ {
protected: 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); error(const char *path, const struct position position);
public: public:
const struct position position; const struct position position;
const char *path; const char *path;
virtual ~error() = default; virtual ~error() noexcept = default;
/// Error text. /// Error text.
virtual std::string what() const = 0; virtual std::string what() const = 0;
/// Error line in the source text. /// Error line in the source text.
std::size_t line() const; std::size_t line() const noexcept;
/// Error column in the source text. /// Error column in the source text.
std::size_t column() const; std::size_t column() const noexcept;
};
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;
}; };
} }
}
template<>
struct std::hash<elna::boot::identifier_definition>
{
std::size_t operator()(const elna::boot::identifier_definition& key) const noexcept;
};

View File

@@ -1,190 +0,0 @@
/* Name analysis.
Copyright (C) 2025 Free Software Foundation, Inc.
GCC is free software; you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation; either version 3, or (at your option)
any later version.
GCC is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with GCC; see the file COPYING3. If not see
<http://www.gnu.org/licenses/>. */
#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
{
class undeclared_error : public error
{
const std::string identifier;
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);
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,25 @@ along with GCC; see the file COPYING3. If not see
#include <unordered_map> #include <unordered_map>
#include <string> #include <string>
#include <memory> #include <memory>
#include <vector>
#include <forward_list>
#include "elna/boot/result.h" namespace elna
{
namespace elna::boot 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;
};
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;
};
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>
{
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();
};
/** /**
* Symbol table. * Symbol table.
*/ */
template<typename T, typename U, U nothing> template<typename T>
class symbol_map class symbol_table
{ {
public: public:
using symbol_ptr = typename std::enable_if< using symbol_ptr = T;
std::is_convertible<U, T>::value || std::is_assignable<T, U>::value,
T
>::type;
using iterator = typename std::unordered_map<std::string, symbol_ptr>::iterator; using iterator = typename std::unordered_map<std::string, symbol_ptr>::iterator;
using const_iterator = typename std::unordered_map<std::string, symbol_ptr>::const_iterator; using const_iterator = typename std::unordered_map<std::string, symbol_ptr>::const_iterator;
private: private:
std::unordered_map<std::string, symbol_ptr> entries; std::unordered_map<std::string, symbol_ptr> entries;
std::shared_ptr<symbol_map> outer_scope; std::shared_ptr<symbol_table> outer_scope;
public: public:
/** /**
@@ -195,45 +47,36 @@ namespace elna::boot
* *
* \param scope Outer scope. * \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) : outer_scope(scope)
{ {
} }
iterator begin() iterator begin()
{ {
return this->entries.begin(); return entries.begin();
} }
iterator end() iterator end()
{ {
return this->entries.end(); return entries.end();
} }
const_iterator cbegin() const const_iterator cbegin() const
{ {
return this->entries.cbegin(); return entries.cbegin();
} }
const_iterator cend() const const_iterator cend() const
{ {
return this->entries.cend(); return entries.cend();
} }
/** /**
* \return Symbol count in the current scope. * Looks for symbol in the table by name. Returns nullptr if the symbol
*/
std::size_t size() const
{
return this->entries.size();
}
/**
* Looks for symbol in the table by name. Returns nothing if the symbol
* can not be found. * can not be found.
* *
* \param name Symbol name. * \param name Symbol name.
*
* \return Symbol from the table if found. * \return Symbol from the table if found.
*/ */
symbol_ptr lookup(const std::string& name) symbol_ptr lookup(const std::string& name)
@@ -248,17 +91,7 @@ namespace elna::boot
{ {
return this->outer_scope->lookup(name); return this->outer_scope->lookup(name);
} }
return nothing; return nullptr;
}
/**
* \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;
} }
/** /**
@@ -271,7 +104,7 @@ namespace elna::boot
*/ */
bool enter(const std::string& name, symbol_ptr entry) bool enter(const std::string& name, symbol_ptr entry)
{ {
return lookup(name) == nothing && entries.insert({ name, entry }).second; return entries.insert({ name, entry }).second;
} }
/** /**
@@ -279,179 +112,10 @@ namespace elna::boot
* *
* \return Outer scope. * \return Outer scope.
*/ */
std::shared_ptr<symbol_map> scope() std::shared_ptr<symbol_table> scope()
{ {
return this->outer_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 along with GCC; see the file COPYING3. If not see
<http://www.gnu.org/licenses/>. */ <http://www.gnu.org/licenses/>. */
#include <memory>
#include "config.h" #include "config.h"
#include "system.h" #include "system.h"
#include "coretypes.h" #include "coretypes.h"
#include "tree.h" #include "tree.h"
#include "tree-iterator.h" #include "tree-iterator.h"
#include "elna/gcc/elna-tree.h" namespace elna
{
namespace elna::gcc namespace gcc
{ {
void init_ttree(); 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 "coretypes.h"
#include "input.h" #include "input.h"
#include "tree.h" #include "tree.h"
#include "diagnostic.h"
#include <deque>
#include <memory>
#include "elna/boot/result.h" #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); location_t get_location(const boot::position *position);
std::string print_type(tree type); 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/ast.h"
#include "elna/boot/symbol.h" #include "elna/boot/symbol.h"
#include "elna/boot/semantic.h"
#include "elna/gcc/elna-tree.h" #include "elna/gcc/elna-tree.h"
#include "config.h" #include "config.h"
@@ -28,20 +27,26 @@ along with GCC; see the file COPYING3. If not see
#include "tree.h" #include "tree.h"
#include "tree-iterator.h" #include "tree-iterator.h"
#include <string>
#include <forward_list> #include <forward_list>
#include <string>
namespace elna::gcc namespace elna
{
namespace gcc
{ {
class generic_visitor final : public boot::empty_visitor class generic_visitor final : public boot::empty_visitor
{ {
tree current_expression{ NULL_TREE }; tree current_expression{ NULL_TREE };
elna::boot::symbol_bag bag; std::shared_ptr<boot::symbol_table<tree>> symbol_map;
std::shared_ptr<symbol_table> symbols;
tree build_label_decl(const char *name, location_t loc);
tree build_type(boot::type_expression& type);
void enter_scope(); void enter_scope();
tree leave_scope(); tree leave_scope();
tree lookup(const std::string& name);
void make_if_branch(boot::conditional_statements& branch, tree goto_endif); void make_if_branch(boot::conditional_statements& branch, tree goto_endif);
tree build_arithmetic_operation(boot::binary_expression *expression, tree build_arithmetic_operation(boot::binary_expression *expression,
@@ -51,47 +56,40 @@ namespace elna::gcc
tree build_bit_logic_operation(boot::binary_expression *expression, tree left, tree right); tree build_bit_logic_operation(boot::binary_expression *expression, tree left, tree right);
tree build_equality_operation(boot::binary_expression *expression, tree left, tree right); tree build_equality_operation(boot::binary_expression *expression, tree left, tree right);
void build_procedure_call(location_t call_location, void build_procedure_call(location_t call_location,
tree procedure_address, const std::vector<boot::expression *>& arguments); tree symbol, const std::vector<boot::expression *>& arguments);
void build_record_call(location_t call_location, void build_record_call(location_t call_location,
tree symbol, const std::vector<boot::expression *>& arguments); 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: public:
generic_visitor(std::shared_ptr<symbol_table> symbol_table, elna::boot::symbol_bag bag); generic_visitor(std::shared_ptr<boot::symbol_table<tree>> symbol_table);
void visit(boot::program *program) override; 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::call_expression *expression) override;
void visit(boot::cast_expression *expression) override; void visit(boot::cast_expression *expression) override;
void visit(boot::traits_expression *trait) override; void visit(boot::size_of_expression *expression) override;
void visit(boot::literal<std::int32_t> *literal) override; void visit(boot::number_literal<std::int32_t> *literal) override;
void visit(boot::literal<std::uint32_t> *literal) override; void visit(boot::number_literal<std::uint32_t> *literal) override;
void visit(boot::literal<double> *literal) override; void visit(boot::number_literal<double> *literal) override;
void visit(boot::literal<bool> *boolean) override; void visit(boot::number_literal<bool> *boolean) override;
void visit(boot::literal<unsigned char> *character) override; void visit(boot::number_literal<unsigned char> *character) override;
void visit(boot::literal<std::nullptr_t> *) override; void visit(boot::number_literal<std::nullptr_t> *) override;
void visit(boot::literal<std::string> *string) override; void visit(boot::number_literal<std::string> *string) override;
void visit(boot::binary_expression *expression) override; void visit(boot::binary_expression *expression) override;
void visit(boot::unary_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_declaration *declaration) override;
void visit(boot::variable_expression *expression) override; void visit(boot::variable_expression *expression) override;
void visit(boot::array_access_expression *expression) override; void visit(boot::array_access_expression *expression) override;
void visit(boot::field_access_expression *expression) override; void visit(boot::field_access_expression *expression) override;
void visit(boot::dereference_expression *expression) override; void visit(boot::dereference_expression *expression) override;
void visit(boot::unit *unit) override;
void visit(boot::assign_statement *statement) override; void visit(boot::assign_statement *statement) override;
void visit(boot::if_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::while_statement *statement) override;
void visit(boot::call_statement *statement) override;
void visit(boot::return_statement *statement) override; void visit(boot::return_statement *statement) override;
void visit(boot::defer_statement *statement) override; void visit(boot::defer_statement *statement) override;
void visit(boot::case_statement *statement) override;
}; };
} }
}

View File

@@ -25,26 +25,21 @@ along with GCC; see the file COPYING3. If not see
#include "tree.h" #include "tree.h"
#include "tree-iterator.h" #include "tree-iterator.h"
#include "stringpool.h" #include "stringpool.h"
#include "fold-const.h"
#include "elna/boot/ast.h" #include "elna/boot/ast.h"
#include "elna/boot/symbol.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
{
bool is_pointer_type(tree type);
bool is_integral_type(tree type); bool is_integral_type(tree type);
bool is_numeric_type(tree type); bool is_numeric_type(tree type);
bool is_unique_type(tree type); bool is_array_type(tree type);
bool is_procedure_type(tree type);
bool is_void_type(tree type); bool is_void_type(tree type);
bool is_record_type(tree type);
/**
* \param type The type to evaluate.
* \return Whether this type can be converted to another type.
*/
bool is_castable_type(tree type);
/** /**
* \param lhs Left hand value. * \param lhs Left hand value.
@@ -53,16 +48,6 @@ namespace elna::gcc
*/ */
bool are_compatible_pointers(tree lhs_type, tree rhs); bool are_compatible_pointers(tree lhs_type, tree rhs);
/**
* Prepares a value to be bound to a variable or parameter.
*
* If rvalue is a procedure declaration, builds a procedure pointer.
*
* \param rvalue Value to be assigned.
* \return Processed value.
*/
tree prepare_rvalue(tree rvalue);
/** /**
* \param assignee Assignee. * \param assignee Assignee.
* \param assignee Assignment. * \param assignee Assignment.
@@ -74,32 +59,11 @@ namespace elna::gcc
void defer(tree statement_tree); void defer(tree statement_tree);
tree chain_defer(); tree chain_defer();
tree do_pointer_arithmetic(boot::binary_operator binary_operator, tree do_pointer_arithmetic(boot::binary_operator binary_operator, tree left, tree right);
tree left, tree right, location_t expression_location);
tree build_binary_operation(bool condition, boot::binary_expression *expression, tree build_binary_operation(bool condition, boot::binary_expression *expression,
tree_code operator_code, tree left, tree right, tree target_type); tree_code operator_code, tree left, tree right, tree target_type);
tree build_arithmetic_operation(boot::binary_expression *expression, tree build_arithmetic_operation(boot::binary_expression *expression,
tree_code operator_code, tree left, tree right); tree_code operator_code, tree left, tree right);
tree build_field(location_t location, tree record_type, const std::string name, tree type); 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 along with GCC; see the file COPYING3. If not see
<http://www.gnu.org/licenses/>. */ <http://www.gnu.org/licenses/>. */
#pragma once
enum elna_tree_index enum elna_tree_index
{ {
ELNA_TI_INT_TYPE, ELNA_TI_INT_TYPE,
ELNA_TI_WORD_TYPE, ELNA_TI_WORD_TYPE,
ELNA_TI_CHAR_TYPE, ELNA_TI_CHAR_TYPE,
ELNA_TI_BOOL_TYPE, ELNA_TI_BOOL_TYPE,
ELNA_TI_POINTER_TYPE, ELNA_TI_BYTE_TYPE,
ELNA_TI_FLOAT_TYPE, ELNA_TI_FLOAT_TYPE,
ELNA_TI_STRING_TYPE, ELNA_TI_STRING_TYPE,
ELNA_TI_BOOL_TRUE, 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_word_type_node elna_global_trees[ELNA_TI_WORD_TYPE]
#define elna_char_type_node elna_global_trees[ELNA_TI_CHAR_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_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_float_type_node elna_global_trees[ELNA_TI_FLOAT_TYPE]
#define elna_string_type_node elna_global_trees[ELNA_TI_STRING_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] #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]

955
source.elna Normal file
View File

@@ -0,0 +1,955 @@
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_AS* = 28
TOKEN_SIZEOF* = 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
type
Position* = record
line: Word
column: Word
end
Location* = record
first: Position
last: Position
end
SourceCode = record
position: Position
text: String
end
TokenValue* = union
int_value: Int
string_value: pointer to Char
string: String
boolean_value: Bool
char_value: Char
end
Token* = record
kind: Int
value: TokenValue
location: Location
end
FILE* = record
dummy: Int
end
CommandLine* = record
input: pointer to Char
tokenize: Bool
syntax_tree: Bool
end
Literal* = record
value: Int
end
ConstantDefinition* = record
name: pointer to Char
body: pointer to Literal
end
ConstantPart* = record
elements: pointer to pointer to ConstantDefinition
count: Word
end
Program* = record
constants: ConstantPart
end
(*
External procedures.
*)
proc fopen(pathname: pointer to Char, mode: pointer to Char) -> pointer to FILE; extern
proc fclose(stream: pointer to FILE) -> Int; extern
proc fseek(stream: pointer to FILE, off: Int, whence: Int) -> Int; extern
proc rewind(stream: pointer to FILE); extern
proc ftell(stream: pointer to FILE) -> Int; extern
proc fread(ptr: pointer to Byte, size: Word, nmemb: Word, stream: pointer to FILE) -> Word; extern
proc write(fd: Int, buf: pointer to Byte, Word: Int) -> Int; extern
proc malloc(size: Word) -> pointer to Byte; extern
proc free(ptr: pointer to Byte); extern
proc calloc(nmemb: Word, size: Word) -> pointer to Byte; extern
proc realloc(ptr: pointer to Byte, size: Word) -> pointer to Byte; extern
proc memset(ptr: pointer to Char, c: Int, n: Int) -> pointer to Char; extern
proc strcmp(s1: pointer to Char, s2: pointer to Char) -> Int; extern
proc strncmp(s1: pointer to Char, s2: pointer to Char, n: Word) -> Int; extern
proc strncpy(dst: pointer to Char, src: pointer to Char, dsize: Word) -> pointer to Char; extern
proc strcpy(dst: pointer to Char, src: pointer to Char) -> pointer to Char; extern
proc strlen(ptr: pointer to Char) -> Word; extern
proc strtol(nptr: pointer to Char, endptr: pointer to pointer to Char, base: Int) -> Int; extern
proc perror(s: pointer to Char); extern
proc exit(code: Int); extern
(*
Standard procedures.
*)
proc reallocarray(ptr: pointer to Byte, n: Word, size: Word) -> pointer to Byte;
begin
return realloc(ptr, n * size)
end
proc write_s(value: String);
begin
write(0, cast(value.ptr: pointer to Byte), cast(value.length: Int))
end
proc write_z(value: pointer to Char);
begin
write(0, cast(value: pointer to 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: pointer to Byte), 1)
end
proc write_i(value: Int);
var
digit: Int, n: Word,
buffer: array 10 of 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: pointer to Char;
begin
copy := cast(malloc(origin.length): pointer to Char);
strncpy(copy, origin.ptr, origin.length);
return String(copy, origin.length)
end
(*
End of standard procedures.
*)
proc make_position() -> Position;
var
result: Position;
begin
return Position(1u, 1u)
end
proc read_source(filename: pointer to Char, result: pointer to String) -> Bool;
var
input_file: pointer to FILE,
source_size: Int,
input: pointer to Byte;
begin
input_file := fopen(filename, "rb\0".ptr);
if input_file = nil then
return false
end;
defer
fclose(input_file)
end;
if fseek(input_file, 0, SEEK_END) <> 0 then
return false
end;
source_size := ftell(input_file);
if source_size < 0 then
return false
end;
rewind(input_file);
input := malloc(cast(source_size: Word));
if fread(input, cast(source_size: Word), 1u, input_file) <> 1u then
return false
end;
result^.length := cast(source_size: Word);
result^.ptr := cast(input: pointer to Char);
return true
end
proc escape_char(escape: Char, result: pointer to Char) -> Bool;
begin
if escape = 'n' then
result^ := '\n';
return true
elsif escape = 'a' then
result^ := '\a';
return true
elsif escape = 'b' then
result^ := '\b';
return true
elsif escape = 't' then
result^ := '\t';
return true
elsif escape = 'f' then
result^ := '\f';
return true
elsif escape = 'r' then
result^ := '\r';
return true
elsif escape = 'v' then
result^ := '\v';
return true
elsif escape = '\\' then
result^ := '\\';
return true
elsif escape = '\'' then
result^ := '\'';
return true
elsif escape = '"' then
result^ := '"';
return true
elsif escape = '?' then
result^ := '\?';
return true
elsif escape = '0' then
result^ := '\0';
return true
else
return false
end
end
proc advance_source(source_code: SourceCode, length: Word) -> SourceCode;
begin
source_code.text := open_substring(source_code.text, length);
source_code.position.column := source_code.position.column + length;
return source_code
end
proc skip_spaces(source_code: SourceCode) -> SourceCode;
begin
while source_code.text.length > 0u and is_space(source_code.text[1u]) do
if source_code.text[1u] = '\n' then
source_code.position.line := source_code.position.line + 1u;
source_code.position.column := 1u
else
source_code.position.column := source_code.position.column + 1u
end;
source_code.text := open_substring(source_code.text, 1u)
end;
return source_code
end
proc lex_identifier(source_code: pointer to SourceCode, token_content: pointer to String);
var
content_length: Word;
begin
content_length := 0u;
token_content^ := source_code^.text;
while is_alnum(source_code^.text[1u]) or source_code^.text[1u] = '_' do
content_length := content_length + 1u;
source_code^ := advance_source(source_code^, 1u)
end;
token_content^ := substring(token_content^, 0u, content_length)
end
proc lex_comment(source_code: pointer to SourceCode, token_content: pointer to String) -> Bool;
var
content_length: Word;
begin
content_length := 0u;
token_content^ := source_code^.text;
while source_code^.text.length > 1u do
if source_code^.text[1u] = '*' and source_code^.text[2u] = ')' then
source_code^ := advance_source(source_code^, 2u);
token_content^ := substring(token_content^, 0u, content_length);
return true
end;
content_length := content_length + 1u;
source_code^ := advance_source(source_code^, 1u)
end;
return false
end
proc lex_character(input: pointer to Char, current_token: pointer to Token) -> pointer to Char;
begin
if input^ = '\\' then
input := input + 1;
if escape_char(input^, @current_token^.value.char_value) then
input := input + 1
end
elsif input^ <> '\0' then
current_token^.value.char_value := input^;
input := input + 1
end;
return input
end
proc lex_string(input: pointer to Char, current_token: pointer to Token) -> pointer to Char;
var
token_end: pointer to Char,
constructed_string: pointer to Char,
token_length: Word,
is_valid: Bool;
begin
token_end := input;
while token_end^ <> '\0' and not ((token_end - 1)^ <> '\\' and token_end^ = '"') do
token_end := token_end + 1
end;
if token_end^ <> '\"' then
return input
end;
token_length := cast(token_end - input: Word);
current_token^.value.string_value := cast(calloc(token_length, 1u): pointer to Char);
is_valid := true;
constructed_string := current_token^.value.string_value;
while input < token_end and is_valid do
if input^ = '\\' then
input := input + 1;
if escape_char(input^, constructed_string) then
input := input + 1
else
is_valid := false
end
elsif input^ <> '\0' then
constructed_string^ := input^;
input := input + 1
end;
constructed_string := constructed_string + 1
end;
return token_end
end
proc print_tokens(tokens: pointer to Token, tokens_size: Word);
var
current_token: pointer to 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_AS then
write_s("AS")
elsif current_token^.kind = TOKEN_SIZEOF then
write_s("SIZEOF")
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")
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 "as" = token_content then
current_token.kind := TOKEN_AS
elsif "sizeof" = token_content then
current_token.kind := TOKEN_SIZEOF
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: pointer to Word) -> pointer to Token;
var
token_end: pointer to Char,
tokens: pointer to Token,
current_token: pointer to Token,
token_length: Word,
first_char: Char,
token_content: String;
begin
tokens_size^ := 0u;
tokens := nil;
source_code := skip_spaces(source_code);
while source_code.text.length <> 0u do
tokens := cast(reallocarray(cast(tokens: pointer to Byte), tokens_size^ + 1u, sizeof(Token)): pointer to Token);
current_token := tokens + tokens_size^;
first_char := source_code.text[1u];
if is_alpha(first_char) or first_char = '_' then
lex_identifier(@source_code, @token_content);
current_token^ := categorize_identifier(token_content)
elsif is_digit(first_char) then
token_end := nil;
current_token^.value.int_value := strtol(source_code.text.ptr, @token_end, 10);
token_length := cast(token_end - source_code.text.ptr: Word);
if token_end^ = 'u' then
current_token^.kind := TOKEN_WORD;
source_code := advance_source(source_code, token_length + 1u)
else
current_token^.kind := TOKEN_INTEGER;
source_code := advance_source(source_code, token_length)
end
elsif first_char = '(' then
source_code := advance_source(source_code, 1u);
if source_code.text.length = 0u then
current_token^.kind := TOKEN_LEFT_PAREN
elsif source_code.text[1u] = '*' then
source_code := advance_source(source_code, 1u);
if lex_comment(@source_code, @token_content) then
current_token^.value.string := string_dup(token_content);
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(source_code, 1u)
elsif first_char = '\'' then
token_end := lex_character(source_code.text.ptr + 1, current_token);
token_length := cast(token_end - source_code.text.ptr: Word);
if token_end^ = '\'' then
current_token^.kind := TOKEN_CHARACTER;
source_code := advance_source(source_code, token_length + 1u)
else
source_code := advance_source(source_code, 1u)
end
elsif first_char = '"' then
token_end := lex_string(source_code.text.ptr + 1, current_token);
if token_end^ = '"' then
current_token^.kind := TOKEN_STRING;
token_length := cast(token_end - source_code.text.ptr: Word);
source_code := advance_source(source_code, token_length + 1u)
end
elsif first_char = '[' then
current_token^.kind := TOKEN_LEFT_SQUARE;
source_code := advance_source(source_code, 1u)
elsif first_char = ']' then
current_token^.kind := TOKEN_RIGHT_SQUARE;
source_code := advance_source(source_code, 1u)
elsif first_char = '>' then
source_code := advance_source(source_code, 1u);
if source_code.text.length = 0u then
current_token^.kind := TOKEN_GREATER_THAN
elsif source_code.text[1u] = '=' then
current_token^.kind := TOKEN_GREATER_EQUAL;
source_code := advance_source(source_code, 1u)
else
current_token^.kind := TOKEN_GREATER_THAN
end
elsif first_char = '<' then
source_code := advance_source(source_code, 1u);
if source_code.text.length = 0u then
current_token^.kind := TOKEN_LESS_THAN
elsif source_code.text[1u] = '=' then
current_token^.kind := TOKEN_LESS_EQUAL;
source_code := advance_source(source_code, 1u)
elsif source_code.text[1u] = '>' then
current_token^.kind := TOKEN_NOT_EQUAL;
source_code := advance_source(source_code, 1u)
else
current_token^.kind := TOKEN_LESS_THAN
end
elsif first_char = '=' then
current_token^.kind := TOKEN_EQUAL;
source_code := advance_source(source_code, 1u)
elsif first_char = ';' then
current_token^.kind := TOKEN_SEMICOLON;
source_code := advance_source(source_code, 1u)
elsif first_char = '.' then
current_token^.kind := TOKEN_DOT;
source_code := advance_source(source_code, 1u)
elsif first_char = ',' then
current_token^.kind := TOKEN_COMMA;
source_code := advance_source(source_code, 1u)
elsif first_char = '+' then
current_token^.kind := TOKEN_PLUS;
source_code := advance_source(source_code, 1u)
elsif first_char = '-' then
current_token^.kind := TOKEN_MINUS;
source_code := advance_source(source_code, 1u)
elsif first_char = '*' then
current_token^.kind := TOKEN_MULTIPLICATION;
source_code := advance_source(source_code, 1u)
elsif first_char = '/' then
current_token^.kind := TOKEN_DIVISION;
source_code := advance_source(source_code, 1u)
elsif first_char = '%' then
current_token^.kind := TOKEN_REMAINDER;
source_code := advance_source(source_code, 1u)
elsif first_char = ':' then
source_code := advance_source(source_code, 1u);
if source_code.text.length = 0u then
current_token^.kind := TOKEN_COLON
elsif source_code.text[1u] = '=' then
current_token^.kind := TOKEN_ASSIGNMENT;
source_code := advance_source(source_code, 1u)
else
current_token^.kind := TOKEN_COLON
end
elsif first_char = '^' then
current_token^.kind := TOKEN_HAT;
source_code := advance_source(source_code, 1u)
elsif first_char = '@' then
current_token^.kind := TOKEN_AT;
source_code := advance_source(source_code, 1u)
else
current_token^.kind := 0;
source_code := advance_source(source_code, 1u)
end;
if current_token^.kind <> 0 then
tokens_size^ := tokens_size^ + 1u;
source_code := 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_literal(tokens: pointer to pointer to Token, tokens_size: pointer to Word) -> pointer to Literal;
begin
return cast(calloc(1u, sizeof(Literal)): pointer to Literal)
end
proc parse_constant_definition(tokens: pointer to pointer to Token,
tokens_size: pointer to Word) -> pointer to ConstantDefinition;
var
result: pointer to ConstantDefinition;
begin
result := cast(calloc(1u, sizeof(ConstantDefinition)): pointer to ConstantDefinition);
result^.name := cast(malloc(strlen(tokens^^.value.string_value)): pointer to Char);
strcpy(result^.name, tokens^^.value.string_value);
tokens^ := tokens^ + 2u;
tokens_size := tokens_size - 2u;
write_z(result^.name);
write_c('\n');
result^.body := parse_literal(tokens, tokens_size);
tokens^ := tokens^ + 2u;
tokens_size := tokens_size - 2u;
return result
end
proc parse_program(tokens: pointer to pointer to Token, tokens_size: pointer to Word) -> pointer to Program;
var
result: pointer to Program,
current_constant: pointer to pointer to ConstantDefinition;
begin
result := cast(calloc(1u, sizeof(Program)): pointer to Program);
result^.constants.elements := nil;
result^.constants.count := 0u;
if tokens^^.kind = TOKEN_CONST then
tokens^ := tokens^ + 1;
tokens_size^ := tokens_size^ - 1u;
while tokens_size^ > 0u and tokens^^.kind = TOKEN_IDENTIFIER do
result^.constants.elements := cast(
reallocarray(
cast(result^.constants.elements: pointer to Byte),
result^.constants.count + 1u,
sizeof(pointer to ConstantDefinition)
) : pointer to pointer to ConstantDefinition);
current_constant := result^.constants.elements + result^.constants.count;
result^.constants.count := result^.constants.count + 1u;
current_constant^ := parse_constant_definition(tokens, tokens_size);
if current_constant^ = nil then
return nil
end
end
end
end
proc parse_command_line*(argc: Int, argv: pointer to pointer to Char) -> pointer to CommandLine;
var
parameter: pointer to pointer to Char,
i: Int,
result: pointer to CommandLine;
begin
i := 1;
result := cast(malloc(sizeof(CommandLine)): pointer to 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: pointer to pointer to Char) -> Int;
var
tokens: pointer to Token,
tokens_size: Word,
source_code: SourceCode,
command_line: pointer to CommandLine;
begin
command_line := parse_command_line(argc, argv);
if command_line = nil then
return 2
end;
source_code.position := make_position();
if not read_source(command_line^.input, @source_code.text) then
perror(command_line^.input);
return 3
end;
tokens := tokenize(source_code, @tokens_size);
if command_line^.tokenize then
print_tokens(tokens, tokens_size)
end;
if command_line^.syntax_tree then
parse_program(@tokens, @tokens_size)
end;
return 0
end
begin
exit(process(cast(count: Int), cast(parameters: pointer to pointer to 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