Compare commits
No commits in common. "dlang" and "cpp" have entirely different histories.
5
.gitignore
vendored
5
.gitignore
vendored
@ -1,3 +1,4 @@
|
||||
/.dub/
|
||||
/dub.selections.json
|
||||
/build/
|
||||
.cache/
|
||||
CMakeFiles/
|
||||
CMakeCache.txt
|
||||
|
1
.ruby-version
Normal file
1
.ruby-version
Normal file
@ -0,0 +1 @@
|
||||
3.3.6
|
34
CMakeLists.txt
Normal file
34
CMakeLists.txt
Normal file
@ -0,0 +1,34 @@
|
||||
cmake_minimum_required(VERSION 3.21)
|
||||
project(Elna)
|
||||
|
||||
set(CMAKE_EXPORT_COMPILE_COMMANDS 1)
|
||||
set(CMAKE_RUNTIME_OUTPUT_DIRECTORY ${CMAKE_BINARY_DIR}/bin)
|
||||
set(CMAKE_CXX_STANDARD 14)
|
||||
|
||||
find_package(Boost CONFIG COMPONENTS process program_options REQUIRED)
|
||||
find_package(FLEX REQUIRED)
|
||||
find_package(BISON REQUIRED)
|
||||
|
||||
FLEX_TARGET(lexer source/lexer.ll ${CMAKE_CURRENT_BINARY_DIR}/lexer.cc)
|
||||
BISON_TARGET(parser source/parser.yy ${CMAKE_CURRENT_BINARY_DIR}/parser.cc)
|
||||
add_flex_bison_dependency(lexer parser)
|
||||
|
||||
add_library(elna-frontend
|
||||
source/ast.cc include/elna/source/ast.h
|
||||
source/types.cc include/elna/source/types.h
|
||||
source/driver.cc include/elna/source/driver.h
|
||||
source/result.cc include/elna/source/result.h
|
||||
${BISON_parser_OUTPUTS} ${FLEX_lexer_OUTPUTS}
|
||||
)
|
||||
target_include_directories(elna-frontend PRIVATE ${CMAKE_CURRENT_BINARY_DIR} include)
|
||||
target_compile_options(elna-frontend PRIVATE
|
||||
$<$<COMPILE_LANGUAGE:CXX>:-fno-exceptions -fno-rtti>
|
||||
)
|
||||
|
||||
add_executable(elna cli/main.cc)
|
||||
target_link_libraries(elna PRIVATE elna-frontend)
|
||||
target_include_directories(elna PRIVATE ${CMAKE_CURRENT_BINARY_DIR} include ${Boost_INCLUDE_DIR})
|
||||
target_link_libraries(elna LINK_PUBLIC ${Boost_LIBRARIES})
|
||||
target_compile_options(elna PRIVATE
|
||||
$<$<COMPILE_LANGUAGE:CXX>:-fno-exceptions -fno-rtti>
|
||||
)
|
37
README
37
README
@ -1,37 +0,0 @@
|
||||
# Elna programming language
|
||||
|
||||
Elna compiles simple mathematical operations to machine code.
|
||||
The compiled program returns the result of the operation.
|
||||
|
||||
## File extension
|
||||
|
||||
.elna
|
||||
|
||||
## Grammar PL/0
|
||||
|
||||
program = block "." ;
|
||||
|
||||
block = [ "const" ident "=" number {"," ident "=" number} ";"]
|
||||
[ "var" ident {"," ident} ";"]
|
||||
{ "procedure" ident ";" block ";" } statement ;
|
||||
|
||||
statement = [ ident ":=" expression | "call" ident
|
||||
| "?" ident | "!" expression
|
||||
| "begin" statement {";" statement } "end"
|
||||
| "if" condition "then" statement
|
||||
| "while" condition "do" statement ];
|
||||
|
||||
condition = "odd" expression |
|
||||
expression ("="|"#"|"<"|"<="|">"|">=") expression ;
|
||||
|
||||
expression = [ "+"|"-"] term { ("+"|"-") term};
|
||||
|
||||
term = factor {("*"|"/") factor};
|
||||
|
||||
factor = ident | number | "(" expression ")";
|
||||
|
||||
## Operations
|
||||
|
||||
"!" - Write a line.
|
||||
"?" - Read user input.
|
||||
"odd" - The only function, returns whether a number is odd.
|
101
README.md
Normal file
101
README.md
Normal file
@ -0,0 +1,101 @@
|
||||
# Elna programming language
|
||||
|
||||
Elna is a simple, imperative, low-level programming language.
|
||||
|
||||
It is intendet to accompany other languages in the areas, where a high-level
|
||||
language doesn't fit well. It is also supposed to be an intermediate
|
||||
representation for a such high-level hypothetical programming language.
|
||||
|
||||
## File extension
|
||||
|
||||
.elna
|
||||
|
||||
## Current implementation
|
||||
|
||||
This repository contains a GCC frontend for Elna. After finishing the frontend
|
||||
I'm planning to rewrite the compiler in Elna itself with its own backend and
|
||||
a hand-written parser. So GCC gives a way to have a simple bootstrap compiler
|
||||
and a possbility to compile Elna programs for different platforms.
|
||||
|
||||
## Grammar
|
||||
|
||||
```ebnf
|
||||
digit = "0" | "1" | "2" | "3" | "4" | "5" | "6" | "7" | "8" | "9";
|
||||
letter = "A" | "B" | … | "Z" | "a" | "b" | … | "z";
|
||||
|
||||
ident = letter { letter | digit | "_" };
|
||||
integer = digit { digit };
|
||||
float = integer "." integer;
|
||||
boolean = "true" | "false";
|
||||
|
||||
literal = integer | float | boolean | "'" character "'" | """ { character } """;
|
||||
|
||||
program = [ "type" type_definitions ";" ]
|
||||
[ constant_part ]
|
||||
{ procedure_definition }
|
||||
[ variable_part ]
|
||||
"begin" [ statement_list ] "end" ".";
|
||||
|
||||
procedure_definition = "proc" ident formal_parameter_list ";" ( block | "extern" ) ";";
|
||||
|
||||
block = [ constant_part ]
|
||||
[ variable_part ]
|
||||
statement;
|
||||
|
||||
constant_part = "const" ident "=" integer { "," ident "=" integer } ";";
|
||||
variable_part = "var" variable_declarations ";";
|
||||
|
||||
statement = ident ":=" expression
|
||||
| ident actual_parameter_list
|
||||
| while_do
|
||||
| if_then_else;
|
||||
|
||||
|
||||
while_do = "while" condition "do" [ statement_list ] "end";
|
||||
if_then_else = "if" expression
|
||||
"then" [ statement_list ]
|
||||
[ else statement_list ] "end";
|
||||
|
||||
statement_list = statement {";" statement };
|
||||
|
||||
condition = "odd" expression |
|
||||
expression ("="|"#"|"<"|"<="|">"|">=") expression;
|
||||
|
||||
comparison_operator = "=", "/=", "<", ">", "<=", ">=";
|
||||
unary_prefix = "not", "@";
|
||||
|
||||
expression = logical_operand { ("and" | "or") logical_operand };
|
||||
logical_operand = comparand { comparison_operator comparand };
|
||||
comparand = summand { ("+" | "-") summand };
|
||||
summand = factor { ("*" | "/") factor };
|
||||
factor = pointer { unary_prefix pointer };
|
||||
|
||||
pointer = literal
|
||||
| designator_expression { $$ = $1; }
|
||||
| "(" expression ")";
|
||||
|
||||
designator_expression = designator_expression "[" expression "]"
|
||||
| designator_expression "." ident
|
||||
| designator_expression "^"
|
||||
| ident;
|
||||
|
||||
formal_parameter_list = "(" [ variable_declarations ] ")";
|
||||
|
||||
actual_parameter_list = "(" [ expressions ] ")";
|
||||
|
||||
expressions = expression { "," expression };
|
||||
|
||||
variable_declarations = variable_declaration { ";" variable_declaration };
|
||||
|
||||
variable_declaration = ident ":" type_expression;
|
||||
|
||||
type_expression = "array" integer "of" type_expression
|
||||
| "pointer" "to" type_expression
|
||||
| "record" field_list "end"
|
||||
| "union" field_list "end"
|
||||
| ident;
|
||||
|
||||
field_list = field_declaration { ";" field_declaration };
|
||||
|
||||
field_declaration = ident ":" type_expression;
|
||||
```
|
106
Rakefile
106
Rakefile
@ -1,95 +1,15 @@
|
||||
require 'pathname'
|
||||
require 'rake/clean'
|
||||
require 'open3'
|
||||
# MacOS:
|
||||
# ---
|
||||
# CC=gcc-14 CXX=g++-14 \
|
||||
# CFLAGS="-I/opt/homebrew/Cellar/flex/2.6.4_2/include" \
|
||||
# CXXFLAGS="-I/opt/homebrew/Cellar/flex/2.6.4_2/include" \
|
||||
# ../gcc-14.2.0/configure \
|
||||
# --disable-bootstrap \
|
||||
# --enable-languages=c,c++,elna \
|
||||
# --with-sysroot=/Library/Developer/CommandLineTools/SDKs/MacOSX15.2.sdk \
|
||||
# --prefix=$(realpath ../gcc-install)
|
||||
|
||||
DFLAGS = ['--warn-no-deprecated', '-L/usr/lib64/gcc-12']
|
||||
BINARY = 'build/bin/elna'
|
||||
TESTS = FileList['tests/*.eln'].flat_map do |test|
|
||||
build = Pathname.new 'build'
|
||||
test_basename = Pathname.new(test).basename('')
|
||||
|
||||
[build + 'riscv' + test_basename].map { |path| path.sub_ext('').to_path }
|
||||
end
|
||||
|
||||
SOURCES = FileList['source/**/*.d']
|
||||
|
||||
directory 'build'
|
||||
|
||||
CLEAN.include 'build'
|
||||
CLEAN.include '.dub'
|
||||
|
||||
rule(/build\/riscv\/[^\/\.]+$/ => ->(file) { test_for_out(file, '.o') }) do |t|
|
||||
sh '/opt/riscv/bin/riscv32-unknown-elf-ld',
|
||||
'-o', t.name,
|
||||
'-L/opt/riscv/lib/gcc/riscv32-unknown-elf/13.2.0/',
|
||||
'-L/opt/riscv/riscv32-unknown-elf/lib',
|
||||
'/opt/riscv/riscv32-unknown-elf/lib/crt0.o',
|
||||
'/opt/riscv/lib/gcc/riscv32-unknown-elf/13.2.0/crtbegin.o',
|
||||
t.source,
|
||||
'--start-group', '-lgcc', '-lc', '-lgloss', '--end-group',
|
||||
'/opt/riscv/lib/gcc/riscv32-unknown-elf/13.2.0/crtend.o'
|
||||
end
|
||||
|
||||
rule(/build\/riscv\/.+\.o$/ => ->(file) { test_for_object(file, '.eln') }) do |t|
|
||||
Pathname.new(t.name).dirname.mkpath
|
||||
sh BINARY, '-o', t.name, t.source
|
||||
end
|
||||
|
||||
file BINARY => SOURCES do |t|
|
||||
sh({ 'DFLAGS' => (DFLAGS * ' ') }, 'dub', 'build', '--compiler=gdc')
|
||||
end
|
||||
|
||||
task default: BINARY
|
||||
|
||||
desc 'Run all tests and check the results'
|
||||
task test: TESTS
|
||||
task test: BINARY do
|
||||
TESTS.each do |test|
|
||||
expected = Pathname
|
||||
.new(test)
|
||||
.sub_ext('.txt')
|
||||
.sub(/^build\/[[:alpha:]]+\//, 'tests/expectations/')
|
||||
.to_path
|
||||
|
||||
puts "Running #{test}"
|
||||
if test.include? '/riscv/'
|
||||
spike = [
|
||||
'/opt/riscv/bin/spike',
|
||||
'--isa=RV32IMAC',
|
||||
'/opt/riscv/riscv32-unknown-elf/bin/pk',
|
||||
test
|
||||
]
|
||||
diff = ['diff', '-Nur', '--color', expected, '-']
|
||||
tail = ['tail', '-n', '1']
|
||||
|
||||
last_stdout, wait_threads = Open3.pipeline_r spike, tail, diff
|
||||
else
|
||||
raise 'Unsupported test platform'
|
||||
end
|
||||
print last_stdout.read
|
||||
last_stdout.close
|
||||
|
||||
fail unless wait_threads.last.value.exitstatus.zero?
|
||||
end
|
||||
end
|
||||
|
||||
desc 'Run unittest blocks'
|
||||
task unittest: SOURCES do |t|
|
||||
sh('dub', 'test', '--compiler=gdc-12')
|
||||
end
|
||||
|
||||
def test_for_object(out_file, extension)
|
||||
test_source = Pathname
|
||||
.new(out_file)
|
||||
.sub_ext(extension)
|
||||
.sub(/^build\/[[:alpha:]]+\//, 'tests/')
|
||||
.to_path
|
||||
[test_source, BINARY]
|
||||
end
|
||||
|
||||
def test_for_out(out_file, extension)
|
||||
Pathname
|
||||
.new(out_file)
|
||||
.sub_ext(extension)
|
||||
.to_path
|
||||
task :default do
|
||||
sh 'make -C build'
|
||||
sh './build/bin/elna'
|
||||
end
|
||||
|
41
cli/main.cc
Normal file
41
cli/main.cc
Normal file
@ -0,0 +1,41 @@
|
||||
#include <elna/source/driver.h>
|
||||
#include "parser.hh"
|
||||
#include <sstream>
|
||||
|
||||
constexpr std::size_t pointer_size = 4;
|
||||
|
||||
int main()
|
||||
{
|
||||
elna::source::driver driver{ "-" };
|
||||
std::istringstream inp(R"(
|
||||
proc f();
|
||||
begin
|
||||
end;
|
||||
|
||||
var x: Int;
|
||||
|
||||
begin
|
||||
x := 4 + 2
|
||||
end.
|
||||
)");
|
||||
|
||||
elna::source::lexer lexer(inp);
|
||||
yy::parser parser(lexer, driver);
|
||||
|
||||
if (auto result = parser())
|
||||
{
|
||||
for (const auto& error : driver.errors())
|
||||
{
|
||||
std::cerr << error->path << ':'
|
||||
<< error->line() << ':' << error->column()
|
||||
<< ": error: " << error->what()
|
||||
<< '.' << std::endl;
|
||||
}
|
||||
return result;
|
||||
}
|
||||
for (auto& definition : driver.tree->definitions())
|
||||
{
|
||||
std::cout << "Definition identifier: " << definition->identifier() << std::endl;
|
||||
}
|
||||
return 0;
|
||||
}
|
4
config-lang.in
Normal file
4
config-lang.in
Normal file
@ -0,0 +1,4 @@
|
||||
language="elna"
|
||||
gcc_subdir="elna/gcc"
|
||||
|
||||
. ${srcdir}/elna/gcc/config-lang.in
|
9
dub.json
9
dub.json
@ -1,9 +0,0 @@
|
||||
{
|
||||
"dependencies": {
|
||||
"tanya": "~>0.19.0"
|
||||
},
|
||||
"name": "elna",
|
||||
"targetType": "executable",
|
||||
"targetPath": "build/bin",
|
||||
"mainSourceFile": "source/main.d"
|
||||
}
|
154
example.elna
Normal file
154
example.elna
Normal file
@ -0,0 +1,154 @@
|
||||
type
|
||||
T = array 5 of Int,
|
||||
R = record
|
||||
x: Int;
|
||||
y: Int
|
||||
end,
|
||||
U = union
|
||||
a: Int;
|
||||
b: Int
|
||||
end;
|
||||
|
||||
proc test_string();
|
||||
var s: String;
|
||||
begin
|
||||
s := "Test string.";
|
||||
|
||||
writei("");
|
||||
writei(s)
|
||||
end;
|
||||
|
||||
proc test_array();
|
||||
var a: T, x: Int;
|
||||
begin
|
||||
a[0] := 2;
|
||||
a[1] := 5;
|
||||
|
||||
writei("");
|
||||
writei("Test array:");
|
||||
|
||||
x := 0;
|
||||
while x < 2 do
|
||||
writei(a[x]);
|
||||
x := x + 1
|
||||
end
|
||||
end;
|
||||
|
||||
proc test_pointer();
|
||||
var x: Int, p: pointer to Int;
|
||||
begin
|
||||
x := 5;
|
||||
p := @x;
|
||||
|
||||
writei("");
|
||||
writei("Test pointer:");
|
||||
writei(p);
|
||||
writei(p^)
|
||||
end;
|
||||
|
||||
proc test_record();
|
||||
var r: R;
|
||||
begin
|
||||
writei("");
|
||||
writei("Test record:");
|
||||
|
||||
r.x := 4;
|
||||
r.y := 8;
|
||||
|
||||
writei(r.y)
|
||||
end;
|
||||
|
||||
proc test_union();
|
||||
var u: U;
|
||||
begin
|
||||
writei("");
|
||||
writei("Test union:");
|
||||
|
||||
u.a := 9;
|
||||
|
||||
writei(u.b)
|
||||
end;
|
||||
|
||||
proc test_primitive();
|
||||
var c: Char, z: Float;
|
||||
begin
|
||||
c := 'x';
|
||||
z := 8.2;
|
||||
|
||||
writei("");
|
||||
writei("Test primitives:");
|
||||
writei(c);
|
||||
writei(z)
|
||||
end;
|
||||
|
||||
proc test_const();
|
||||
const t = 5;
|
||||
var x: Int;
|
||||
begin
|
||||
x := t;
|
||||
|
||||
writei("");
|
||||
writei("Test const:");
|
||||
writei(x)
|
||||
end;
|
||||
|
||||
proc test_if();
|
||||
var x: Bool, y: Bool;
|
||||
begin
|
||||
x := true;
|
||||
y := false;
|
||||
|
||||
writei("");
|
||||
if x and y then
|
||||
writei("Test if: True")
|
||||
else
|
||||
writei("Test if: False")
|
||||
end
|
||||
end;
|
||||
|
||||
proc test_not();
|
||||
var x: Bool;
|
||||
begin
|
||||
x := false;
|
||||
|
||||
writei("");
|
||||
if not x then
|
||||
writei("Test not true.")
|
||||
else
|
||||
writei("Test not false")
|
||||
end
|
||||
end;
|
||||
|
||||
proc test_param(d: Int, e: Int);
|
||||
begin
|
||||
writei("");
|
||||
writei("Test param");
|
||||
writei(d);
|
||||
writei(e)
|
||||
end;
|
||||
|
||||
proc test_const_char();
|
||||
const x = 'u';
|
||||
begin
|
||||
writei("");
|
||||
writei("Test constant character");
|
||||
writei(x)
|
||||
end;
|
||||
|
||||
proc exit(code: Int); extern;
|
||||
|
||||
begin
|
||||
test_primitive();
|
||||
test_string();
|
||||
test_array();
|
||||
test_pointer();
|
||||
test_record();
|
||||
test_const();
|
||||
test_if();
|
||||
test_not();
|
||||
test_param(8, 7);
|
||||
test_const_char();
|
||||
test_union();
|
||||
|
||||
exit(0)
|
||||
end.
|
112
gcc/Make-lang.in
Normal file
112
gcc/Make-lang.in
Normal file
@ -0,0 +1,112 @@
|
||||
ELNA_INSTALL_NAME := $(shell echo gelna|sed '$(program_transform_name)')
|
||||
ELNA_TARGET_INSTALL_NAME := $(target_noncanonical)-$(shell echo gelna|sed '$(program_transform_name)')
|
||||
|
||||
elna: elna1$(exeext)
|
||||
|
||||
.PHONY: elna
|
||||
|
||||
# Driver
|
||||
|
||||
ELNA_OBJS = \
|
||||
$(GCC_OBJS) \
|
||||
elna/elna-spec.o \
|
||||
$(END)
|
||||
|
||||
gelna$(exeext): $(ELNA_OBJS) $(EXTRA_GCC_OBJS) libcommon-target.a $(LIBDEPS)
|
||||
+$(LINKER) $(ALL_LINKERFLAGS) $(LDFLAGS) -o $@ \
|
||||
$(ELNA_OBJS) $(EXTRA_GCC_OBJS) libcommon-target.a \
|
||||
$(EXTRA_GCC_LIBS) $(LIBS)
|
||||
|
||||
# The compiler proper
|
||||
|
||||
elna_OBJS = \
|
||||
elna/elna1.o \
|
||||
elna/elna-generic.o \
|
||||
elna/elna-convert.o \
|
||||
elna/elna-diagnostic.o \
|
||||
elna/elna-tree.o \
|
||||
elna/ast.o \
|
||||
elna/driver.o \
|
||||
elna/lexer.o \
|
||||
elna/parser.o \
|
||||
elna/result.o \
|
||||
$(END)
|
||||
|
||||
elna1$(exeext): attribs.o $(elna_OBJS) $(BACKEND) $(LIBDEPS)
|
||||
+$(LLINKER) $(ALL_LINKERFLAGS) $(LDFLAGS) -o $@ \
|
||||
attribs.o $(elna_OBJS) $(BACKEND) $(LIBS) $(BACKENDLIBS)
|
||||
|
||||
elna.all.cross:
|
||||
|
||||
elna.start.encap: gelna$(exeext)
|
||||
elna.rest.encap:
|
||||
|
||||
# No elna-specific selftests.
|
||||
selftest-elna:
|
||||
|
||||
elna.install-common: installdirs
|
||||
-rm -f $(DESTDIR)$(bindir)/$(ELNA_INSTALL_NAME)$(exeext)
|
||||
$(INSTALL_PROGRAM) gelna$(exeext) $(DESTDIR)$(bindir)/$(ELNA_INSTALL_NAME)$(exeext)
|
||||
rm -f $(DESTDIR)$(bindir)/$(ELNA_TARGET_INSTALL_NAME)$(exeext); \
|
||||
( cd $(DESTDIR)$(bindir) && \
|
||||
$(LN) $(ELNA_INSTALL_NAME)$(exeext) $(ELNA_TARGET_INSTALL_NAME)$(exeext) ); \
|
||||
|
||||
# Required goals, they still do nothing
|
||||
elna.install-man:
|
||||
elna.install-info:
|
||||
elna.install-pdf:
|
||||
elna.install-plugin:
|
||||
elna.install-html:
|
||||
elna.info:
|
||||
elna.dvi:
|
||||
elna.pdf:
|
||||
elna.html:
|
||||
elna.man:
|
||||
elna.mostlyclean:
|
||||
elna.clean:
|
||||
elna.distclean:
|
||||
elna.maintainer-clean:
|
||||
|
||||
# make uninstall
|
||||
elna.uninstall:
|
||||
-rm -f gelna$(exeext) elna1$(exeext)
|
||||
-rm -f $(elna_OBJS)
|
||||
|
||||
# Used for handling bootstrap
|
||||
elna.stage1: stage1-start
|
||||
-mv elna/*$(objext) stage1/elna
|
||||
elna.stage2: stage2-start
|
||||
-mv elna/*$(objext) stage2/elna
|
||||
elna.stage3: stage3-start
|
||||
-mv elna/*$(objext) stage3/elna
|
||||
elna.stage4: stage4-start
|
||||
-mv elna/*$(objext) stage4/elna
|
||||
elna.stageprofile: stageprofile-start
|
||||
-mv elna/*$(objext) stageprofile/elna
|
||||
elna.stagefeedback: stagefeedback-start
|
||||
-mv elna/*$(objext) stagefeedback/elna
|
||||
|
||||
ELNA_INCLUDES = -I $(srcdir)/elna/include -I elna/generated
|
||||
|
||||
elna/%.o: elna/source/%.cc elna/generated/parser.hh elna/generated/location.hh
|
||||
$(COMPILE) $(ELNA_INCLUDES) $<
|
||||
$(POSTCOMPILE)
|
||||
|
||||
elna/%.o: elna/generated/%.cc elna/generated/parser.hh elna/generated/location.hh
|
||||
$(COMPILE) $(ELNA_INCLUDES) $<
|
||||
$(POSTCOMPILE)
|
||||
|
||||
elna/%.o: elna/gcc/%.cc elna/generated/parser.hh elna/generated/location.hh
|
||||
$(COMPILE) $(ELNA_INCLUDES) $<
|
||||
$(POSTCOMPILE)
|
||||
|
||||
elna/generated/parser.cc: elna/source/parser.yy
|
||||
mkdir -p $(dir $@)
|
||||
$(BISON) -d -o $@ $<
|
||||
|
||||
elna/generated/parser.hh elna/generated/location.hh: elna/generated/parser.cc
|
||||
@touch $@
|
||||
|
||||
elna/generated/lexer.cc: elna/source/lexer.ll
|
||||
mkdir -p $(dir $@)
|
||||
$(FLEX) -o $@ $<
|
13
gcc/config-lang.in
Normal file
13
gcc/config-lang.in
Normal file
@ -0,0 +1,13 @@
|
||||
language="elna"
|
||||
gcc_subdir="elna/gcc"
|
||||
|
||||
compilers="elna1\$(exeext)"
|
||||
|
||||
target_libs=""
|
||||
|
||||
gtfiles="\$(srcdir)/elna/gcc/elna1.cc"
|
||||
|
||||
lang_requires_boot_languages=c++
|
||||
|
||||
# Do not build by default
|
||||
build_by_default="no"
|
14
gcc/elna-convert.cc
Normal file
14
gcc/elna-convert.cc
Normal file
@ -0,0 +1,14 @@
|
||||
#include "config.h"
|
||||
#include "system.h"
|
||||
#include "coretypes.h"
|
||||
#include "tree.h"
|
||||
#include "fold-const.h"
|
||||
#include "convert.h"
|
||||
|
||||
/* Creates an expression whose value is that of EXPR, converted to type TYPE.
|
||||
This function implements all reasonable scalar conversions. */
|
||||
|
||||
tree convert(tree /* type */, tree expr)
|
||||
{
|
||||
return expr;
|
||||
}
|
61
gcc/elna-diagnostic.cc
Normal file
61
gcc/elna-diagnostic.cc
Normal file
@ -0,0 +1,61 @@
|
||||
#include "elna/gcc/elna-diagnostic.h"
|
||||
#include "elna/gcc/elna-tree.h"
|
||||
|
||||
namespace elna
|
||||
{
|
||||
namespace gcc
|
||||
{
|
||||
location_t get_location(const elna::source::position *position)
|
||||
{
|
||||
linemap_line_start(line_table, position->line, 0);
|
||||
|
||||
return linemap_position_for_column(line_table, position->column);
|
||||
}
|
||||
|
||||
const char *print_type(tree type)
|
||||
{
|
||||
gcc_assert(TYPE_P(type));
|
||||
|
||||
if (type == integer_type_node)
|
||||
{
|
||||
return "Int";
|
||||
}
|
||||
else if (type == boolean_type_node)
|
||||
{
|
||||
return "Bool";
|
||||
}
|
||||
else if (type == double_type_node)
|
||||
{
|
||||
return "Float";
|
||||
}
|
||||
else if (type == elna_char_type_node)
|
||||
{
|
||||
return "Char";
|
||||
}
|
||||
else if (is_string_type(type))
|
||||
{
|
||||
return "String";
|
||||
}
|
||||
else if (is_pointer_type(type))
|
||||
{
|
||||
return "pointer";
|
||||
}
|
||||
else if (TREE_CODE(type) == ARRAY_TYPE)
|
||||
{
|
||||
return "array";
|
||||
}
|
||||
else if (TREE_CODE(type) == RECORD_TYPE)
|
||||
{
|
||||
return "record";
|
||||
}
|
||||
else if (TREE_CODE(type) == UNION_TYPE)
|
||||
{
|
||||
return "union";
|
||||
}
|
||||
else
|
||||
{
|
||||
return "<<unknown-type>>";
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
859
gcc/elna-generic.cc
Normal file
859
gcc/elna-generic.cc
Normal file
@ -0,0 +1,859 @@
|
||||
#include "elna/gcc/elna-generic.h"
|
||||
#include "elna/gcc/elna-diagnostic.h"
|
||||
|
||||
#include "input.h"
|
||||
#include "cgraph.h"
|
||||
#include "gimplify.h"
|
||||
#include "stringpool.h"
|
||||
#include "diagnostic.h"
|
||||
#include "realmpfr.h"
|
||||
#include "stor-layout.h"
|
||||
#include "varasm.h"
|
||||
#include <set>
|
||||
|
||||
namespace elna
|
||||
{
|
||||
namespace gcc
|
||||
{
|
||||
generic_visitor::generic_visitor(std::shared_ptr<source::symbol_table<tree>> symbol_table)
|
||||
{
|
||||
this->symbol_map = symbol_table;
|
||||
}
|
||||
|
||||
void generic_visitor::visit(source::call_expression *statement)
|
||||
{
|
||||
auto symbol = this->symbol_map->lookup(statement->name());
|
||||
|
||||
if (statement->name() == "writei")
|
||||
{
|
||||
if (statement->arguments().size() != 1)
|
||||
{
|
||||
error_at(get_location(&statement->position()),
|
||||
"procedure '%s' expects 1 argument, %lu given",
|
||||
statement->name().c_str(), statement->arguments().size());
|
||||
return;
|
||||
}
|
||||
auto& argument = statement->arguments().at(0);
|
||||
argument->accept(this);
|
||||
auto argument_type = TREE_TYPE(this->current_expression);
|
||||
|
||||
const char *format_number{ nullptr };
|
||||
if (argument_type == integer_type_node)
|
||||
{
|
||||
format_number = "%d\n";
|
||||
}
|
||||
else if (argument_type == double_type_node)
|
||||
{
|
||||
format_number = "%f\n";
|
||||
}
|
||||
else if (argument_type == elna_char_type_node)
|
||||
{
|
||||
format_number = "%c\n";
|
||||
}
|
||||
else if (is_string_type(argument_type))
|
||||
{
|
||||
format_number = "%s\n";
|
||||
}
|
||||
else if (is_pointer_type(argument_type))
|
||||
{
|
||||
format_number = "%p\n";
|
||||
}
|
||||
else
|
||||
{
|
||||
error_at(get_location(&argument->position()),
|
||||
"invalid argument of type %s for procedure %s",
|
||||
print_type(argument_type), statement->name().c_str());
|
||||
this->current_expression = error_mark_node;
|
||||
return;
|
||||
}
|
||||
tree args[] = {
|
||||
build_string_literal(strlen(format_number) + 1, format_number),
|
||||
this->current_expression
|
||||
};
|
||||
tree fndecl_type_param[] = {
|
||||
build_pointer_type(build_qualified_type(char_type_node, TYPE_QUAL_CONST)) /* const char* */
|
||||
};
|
||||
tree fndecl_type = build_varargs_function_type_array(integer_type_node, 1, fndecl_type_param);
|
||||
|
||||
tree printf_fn_decl = build_fn_decl("printf", fndecl_type);
|
||||
DECL_EXTERNAL(printf_fn_decl) = 1;
|
||||
|
||||
tree printf_fn = build1(ADDR_EXPR, build_pointer_type(fndecl_type), printf_fn_decl);
|
||||
|
||||
tree stmt = build_call_array(integer_type_node, printf_fn, 2, args);
|
||||
|
||||
append_to_statement_list(stmt, &this->current_statements);
|
||||
this->current_expression = NULL_TREE;
|
||||
}
|
||||
else if (symbol)
|
||||
{
|
||||
tree return_type = TREE_TYPE(TREE_TYPE(symbol->payload));
|
||||
tree fndecl_type = build_function_type(return_type, TYPE_ARG_TYPES(symbol->payload));
|
||||
tree printf_fn = build1(ADDR_EXPR, build_pointer_type(fndecl_type), symbol->payload);
|
||||
|
||||
std::vector<tree> arguments(statement->arguments().size());
|
||||
for (std::size_t i = 0; i < statement->arguments().size(); ++i)
|
||||
{
|
||||
statement->arguments().at(i)->accept(this);
|
||||
arguments[i] = this->current_expression;
|
||||
}
|
||||
tree stmt = build_call_array_loc(get_location(&statement->position()),
|
||||
return_type, printf_fn, arguments.size(), arguments.data());
|
||||
|
||||
if (return_type == void_type_node)
|
||||
{
|
||||
append_to_statement_list(stmt, &this->current_statements);
|
||||
this->current_expression = NULL_TREE;
|
||||
}
|
||||
else
|
||||
{
|
||||
this->current_expression = stmt;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
error_at(get_location(&statement->position()),
|
||||
"procedure '%s' not declared",
|
||||
statement->name().c_str());
|
||||
}
|
||||
}
|
||||
|
||||
void generic_visitor::visit(source::program *program)
|
||||
{
|
||||
for (const auto& constant : program->type_definitions)
|
||||
{
|
||||
constant->accept(this);
|
||||
}
|
||||
|
||||
tree parameter_types[] = {
|
||||
integer_type_node,
|
||||
build_pointer_type(build_pointer_type(char_type_node))
|
||||
};
|
||||
tree declaration_type = build_function_type_array(integer_type_node, 2, parameter_types);
|
||||
this->main_fndecl = build_fn_decl("main", declaration_type);
|
||||
tree resdecl = build_decl(UNKNOWN_LOCATION, RESULT_DECL, NULL_TREE, integer_type_node);
|
||||
DECL_CONTEXT(resdecl) = this->main_fndecl;
|
||||
DECL_RESULT(this->main_fndecl) = resdecl;
|
||||
|
||||
enter_scope();
|
||||
|
||||
for (const auto definition : program->value_definitions)
|
||||
{
|
||||
definition->accept(this);
|
||||
}
|
||||
for (const auto body_statement : program->body)
|
||||
{
|
||||
body_statement->accept(this);
|
||||
}
|
||||
tree set_result = build2(INIT_EXPR, void_type_node, DECL_RESULT(main_fndecl),
|
||||
build_int_cst_type(integer_type_node, 0));
|
||||
tree return_stmt = build1(RETURN_EXPR, void_type_node, set_result);
|
||||
append_to_statement_list(return_stmt, &this->current_statements);
|
||||
tree_symbol_mapping mapping = leave_scope();
|
||||
|
||||
BLOCK_SUPERCONTEXT(mapping.block()) = this->main_fndecl;
|
||||
DECL_INITIAL(this->main_fndecl) = mapping.block();
|
||||
DECL_SAVED_TREE(this->main_fndecl) = mapping.bind_expression();
|
||||
|
||||
DECL_EXTERNAL(this->main_fndecl) = 0;
|
||||
DECL_PRESERVE_P(this->main_fndecl) = 1;
|
||||
|
||||
gimplify_function_tree(this->main_fndecl);
|
||||
|
||||
cgraph_node::finalize_function(this->main_fndecl, true);
|
||||
}
|
||||
|
||||
void generic_visitor::visit(source::procedure_definition *definition)
|
||||
{
|
||||
std::vector<tree> parameter_types(definition->parameters.size());
|
||||
|
||||
for (std::size_t i = 0; i < definition->parameters.size(); ++i)
|
||||
{
|
||||
parameter_types[i] = build_type(definition->parameters.at(i)->type());
|
||||
}
|
||||
tree return_type = definition->return_type() == nullptr
|
||||
? void_type_node
|
||||
: build_type(*definition->return_type());
|
||||
tree declaration_type = build_function_type_array(return_type,
|
||||
definition->parameters.size(), parameter_types.data());
|
||||
this->main_fndecl = build_fn_decl(definition->identifier().c_str(), declaration_type);
|
||||
this->symbol_map->enter(definition->identifier(), source::make_info(this->main_fndecl));
|
||||
|
||||
if (definition->body() != nullptr)
|
||||
{
|
||||
tree resdecl = build_decl(UNKNOWN_LOCATION, RESULT_DECL, NULL_TREE, return_type);
|
||||
DECL_CONTEXT(resdecl) = this->main_fndecl;
|
||||
DECL_RESULT(this->main_fndecl) = resdecl;
|
||||
|
||||
enter_scope();
|
||||
}
|
||||
gcc::tree_chain argument_chain;
|
||||
for (std::size_t i = 0; i < definition->parameters.size(); ++i)
|
||||
{
|
||||
auto parameter = definition->parameters.at(i);
|
||||
|
||||
tree declaration_tree = build_decl(get_location(¶meter->position()), PARM_DECL,
|
||||
get_identifier(parameter->identifier().c_str()), parameter_types[i]);
|
||||
DECL_CONTEXT(declaration_tree) = this->main_fndecl;
|
||||
DECL_ARG_TYPE(declaration_tree) = parameter_types[i];
|
||||
|
||||
if (definition->body() != nullptr)
|
||||
{
|
||||
this->symbol_map->enter(parameter->identifier(), source::make_info(declaration_tree));
|
||||
}
|
||||
argument_chain.append(declaration_tree);
|
||||
}
|
||||
DECL_ARGUMENTS(this->main_fndecl) = argument_chain.head();
|
||||
|
||||
if (definition->body() != nullptr)
|
||||
{
|
||||
definition->body()->accept(this);
|
||||
tree_symbol_mapping mapping = leave_scope();
|
||||
|
||||
BLOCK_SUPERCONTEXT(mapping.block()) = this->main_fndecl;
|
||||
DECL_INITIAL(this->main_fndecl) = mapping.block();
|
||||
DECL_SAVED_TREE(this->main_fndecl) = mapping.bind_expression();
|
||||
|
||||
DECL_EXTERNAL(this->main_fndecl) = 0;
|
||||
DECL_PRESERVE_P(this->main_fndecl) = 1;
|
||||
|
||||
gimplify_function_tree(this->main_fndecl);
|
||||
|
||||
cgraph_node::finalize_function(this->main_fndecl, true);
|
||||
}
|
||||
else
|
||||
{
|
||||
DECL_EXTERNAL(this->main_fndecl) = 1;
|
||||
}
|
||||
}
|
||||
|
||||
void generic_visitor::enter_scope()
|
||||
{
|
||||
this->current_statements = alloc_stmt_list();
|
||||
this->variable_chain = tree_chain();
|
||||
this->symbol_map = std::make_shared<source::symbol_table<tree>>(this->symbol_map);
|
||||
}
|
||||
|
||||
tree_symbol_mapping generic_visitor::leave_scope()
|
||||
{
|
||||
tree new_block = build_block(variable_chain.head(),
|
||||
NULL_TREE, NULL_TREE, NULL_TREE);
|
||||
tree bind_expr = build3(BIND_EXPR, void_type_node, variable_chain.head(),
|
||||
this->current_statements, new_block);
|
||||
this->symbol_map = this->symbol_map->scope();
|
||||
|
||||
return tree_symbol_mapping{ bind_expr, new_block };
|
||||
}
|
||||
|
||||
void generic_visitor::visit(source::number_literal<std::int32_t> *literal)
|
||||
{
|
||||
this->current_expression = build_int_cst_type(integer_type_node, literal->number());
|
||||
}
|
||||
|
||||
void generic_visitor::visit(source::number_literal<double> *literal)
|
||||
{
|
||||
REAL_VALUE_TYPE real_value1;
|
||||
|
||||
mpfr_t number;
|
||||
mpfr_init2(number, SIGNIFICAND_BITS);
|
||||
mpfr_set_d(number, literal->number(), MPFR_RNDN);
|
||||
|
||||
real_from_mpfr(&real_value1, number, double_type_node, MPFR_RNDN);
|
||||
|
||||
this->current_expression = build_real(double_type_node, real_value1);
|
||||
|
||||
mpfr_clear(number);
|
||||
}
|
||||
|
||||
void generic_visitor::visit(source::number_literal<bool> *boolean)
|
||||
{
|
||||
this->current_expression = build_int_cst_type(boolean_type_node, boolean->number());
|
||||
}
|
||||
|
||||
void generic_visitor::visit(source::number_literal<unsigned char> *character)
|
||||
{
|
||||
this->current_expression = build_int_cstu(elna_char_type_node, character->number());
|
||||
}
|
||||
|
||||
void generic_visitor::visit(source::string_literal *string)
|
||||
{
|
||||
this->current_expression = build_string_literal(string->string().size() + 1, string->string().c_str());
|
||||
}
|
||||
|
||||
void generic_visitor::build_binary_operation(bool condition, source::binary_expression *expression,
|
||||
tree_code operator_code, tree left, tree right, tree target_type)
|
||||
{
|
||||
auto expression_location = get_location(&expression->position());
|
||||
auto left_type = TREE_TYPE(left);
|
||||
auto right_type = TREE_TYPE(right);
|
||||
|
||||
if (condition)
|
||||
{
|
||||
this->current_expression = build2_loc(expression_location,
|
||||
operator_code, target_type, left, right);
|
||||
}
|
||||
else
|
||||
{
|
||||
error_at(expression_location,
|
||||
"invalid operands of type %s and %s for operator %s",
|
||||
print_type(left_type), print_type(right_type),
|
||||
elna::source::print_binary_operator(expression->operation()));
|
||||
this->current_expression = error_mark_node;
|
||||
}
|
||||
}
|
||||
|
||||
void generic_visitor::visit(source::binary_expression *expression)
|
||||
{
|
||||
expression->lhs().accept(this);
|
||||
auto left = this->current_expression;
|
||||
auto left_type = TREE_TYPE(left);
|
||||
|
||||
expression->rhs().accept(this);
|
||||
auto right = this->current_expression;
|
||||
auto right_type = TREE_TYPE(right);
|
||||
|
||||
auto expression_location = get_location(&expression->position());
|
||||
tree_code operator_code = ERROR_MARK;
|
||||
tree target_type = error_mark_node;
|
||||
|
||||
if (left_type != right_type)
|
||||
{
|
||||
error_at(expression_location,
|
||||
"invalid operands of type %s and %s for operator %s",
|
||||
print_type(left_type), print_type(right_type),
|
||||
elna::source::print_binary_operator(expression->operation()));
|
||||
this->current_expression = error_mark_node;
|
||||
return;
|
||||
}
|
||||
switch (expression->operation())
|
||||
{
|
||||
case source::binary_operator::sum:
|
||||
operator_code = PLUS_EXPR;
|
||||
target_type = left_type;
|
||||
break;
|
||||
case source::binary_operator::subtraction:
|
||||
operator_code = MINUS_EXPR;
|
||||
target_type = left_type;
|
||||
break;
|
||||
case source::binary_operator::division:
|
||||
operator_code = TRUNC_DIV_EXPR;
|
||||
target_type = left_type;
|
||||
break;
|
||||
case source::binary_operator::multiplication:
|
||||
operator_code = MULT_EXPR;
|
||||
target_type = left_type;
|
||||
break;
|
||||
case source::binary_operator::less:
|
||||
operator_code = LT_EXPR;
|
||||
target_type = boolean_type_node;
|
||||
break;
|
||||
case source::binary_operator::greater:
|
||||
operator_code = GT_EXPR;
|
||||
target_type = boolean_type_node;
|
||||
break;
|
||||
case source::binary_operator::less_equal:
|
||||
operator_code = LE_EXPR;
|
||||
target_type = boolean_type_node;
|
||||
break;
|
||||
case source::binary_operator::greater_equal:
|
||||
operator_code = GE_EXPR;
|
||||
target_type = boolean_type_node;
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
if (operator_code != ERROR_MARK) // An arithmetic operation.
|
||||
{
|
||||
build_binary_operation(left_type == integer_type_node || left_type == double_type_node,
|
||||
expression, operator_code, left, right, target_type);
|
||||
return;
|
||||
}
|
||||
switch (expression->operation())
|
||||
{
|
||||
case source::binary_operator::conjunction:
|
||||
operator_code = TRUTH_ANDIF_EXPR;
|
||||
target_type = boolean_type_node;
|
||||
break;
|
||||
case source::binary_operator::disjunction:
|
||||
operator_code = TRUTH_ORIF_EXPR;
|
||||
target_type = boolean_type_node;
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
if (operator_code != ERROR_MARK) // A logical operation.
|
||||
{
|
||||
build_binary_operation(left_type == boolean_type_node,
|
||||
expression, operator_code, left, right, target_type);
|
||||
return;
|
||||
}
|
||||
switch (expression->operation())
|
||||
{
|
||||
case source::binary_operator::equals:
|
||||
operator_code = EQ_EXPR;
|
||||
target_type = boolean_type_node;
|
||||
break;
|
||||
case source::binary_operator::not_equals:
|
||||
operator_code = NE_EXPR;
|
||||
target_type = boolean_type_node;
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
gcc_assert(operator_code != ERROR_MARK);
|
||||
gcc_assert(target_type != error_mark_node);
|
||||
|
||||
this->current_expression = build2_loc(expression_location,
|
||||
operator_code, target_type, left, right);
|
||||
}
|
||||
|
||||
void generic_visitor::visit(source::unary_expression *expression)
|
||||
{
|
||||
expression->operand().accept(this);
|
||||
|
||||
switch (expression->operation())
|
||||
{
|
||||
case source::unary_operator::reference:
|
||||
this->current_expression = build1_loc(get_location(&expression->position()), ADDR_EXPR,
|
||||
build_pointer_type_for_mode(TREE_TYPE(this->current_expression), VOIDmode, true),
|
||||
this->current_expression);
|
||||
break;
|
||||
case source::unary_operator::negation:
|
||||
this->current_expression = build1_loc(get_location(&expression->position()), TRUTH_NOT_EXPR,
|
||||
boolean_type_node, this->current_expression);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
void generic_visitor::visit(source::constant_definition *definition)
|
||||
{
|
||||
location_t definition_location = get_location(&definition->position());
|
||||
definition->body().accept(this);
|
||||
|
||||
tree definition_tree = build_decl(definition_location, CONST_DECL,
|
||||
get_identifier(definition->identifier().c_str()), TREE_TYPE(this->current_expression));
|
||||
auto result = this->symbol_map->enter(definition->identifier(), source::make_info(definition_tree));
|
||||
|
||||
if (result)
|
||||
{
|
||||
DECL_INITIAL(definition_tree) = this->current_expression;
|
||||
TREE_CONSTANT(definition_tree) = 1;
|
||||
TREE_READONLY(definition_tree) = 1;
|
||||
|
||||
auto declaration_statement = build1_loc(definition_location, DECL_EXPR,
|
||||
void_type_node, definition_tree);
|
||||
append_to_statement_list(declaration_statement, &this->current_statements);
|
||||
}
|
||||
else
|
||||
{
|
||||
error_at(definition_location,
|
||||
"variable '%s' already declared in this scope",
|
||||
definition->identifier().c_str());
|
||||
}
|
||||
this->current_expression = NULL_TREE;
|
||||
}
|
||||
|
||||
void generic_visitor::visit(source::type_definition *definition)
|
||||
{
|
||||
tree tree_type = build_type(definition->body());
|
||||
|
||||
if (tree_type == NULL_TREE)
|
||||
{
|
||||
return;
|
||||
}
|
||||
location_t definition_location = get_location(&definition->position());
|
||||
tree definition_tree = build_decl(definition_location, TYPE_DECL,
|
||||
get_identifier(definition->identifier().c_str()), tree_type);
|
||||
auto result = this->symbol_map->enter(definition->identifier(), source::make_info(tree_type));
|
||||
|
||||
if (result)
|
||||
{
|
||||
DECL_CONTEXT(definition_tree) = this->main_fndecl;
|
||||
variable_chain.append(definition_tree);
|
||||
|
||||
auto declaration_statement = build1_loc(definition_location, DECL_EXPR,
|
||||
void_type_node, definition_tree);
|
||||
append_to_statement_list(declaration_statement, &this->current_statements);
|
||||
}
|
||||
else
|
||||
{
|
||||
error_at(definition_location,
|
||||
"type '%s' already declared in this scope",
|
||||
definition->identifier().c_str());
|
||||
}
|
||||
}
|
||||
|
||||
tree generic_visitor::build_type(source::type_expression& type)
|
||||
{
|
||||
if (source::basic_type_expression *basic_type = type.is_basic())
|
||||
{
|
||||
auto symbol = this->symbol_map->lookup(basic_type->base_name());
|
||||
|
||||
if (symbol && TYPE_P(symbol->payload))
|
||||
{
|
||||
return symbol->payload;
|
||||
}
|
||||
error_at(get_location(&basic_type->position()),
|
||||
"type '%s' not declared", basic_type->base_name().c_str());
|
||||
|
||||
return error_mark_node;
|
||||
}
|
||||
else if (source::array_type_expression *array_type = type.is_array())
|
||||
{
|
||||
tree lower_bound = build_int_cst_type(integer_type_node, 0);
|
||||
tree upper_bound = build_int_cst_type(integer_type_node, array_type->size);
|
||||
tree base_type = build_type(array_type->base());
|
||||
|
||||
if (base_type == NULL_TREE || base_type == error_mark_node)
|
||||
{
|
||||
return base_type;
|
||||
}
|
||||
tree range_type = build_range_type(integer_type_node, lower_bound, upper_bound);
|
||||
|
||||
return build_array_type(base_type, range_type);
|
||||
}
|
||||
else if (source::pointer_type_expression *pointer_type = type.is_pointer())
|
||||
{
|
||||
tree base_type = build_type(pointer_type->base());
|
||||
|
||||
if (base_type == NULL_TREE || base_type == error_mark_node)
|
||||
{
|
||||
return base_type;
|
||||
}
|
||||
return build_pointer_type_for_mode(base_type, VOIDmode, true);
|
||||
}
|
||||
else if (source::record_type_expression *record_type = type.is_record())
|
||||
{
|
||||
std::set<std::string> field_names;
|
||||
tree record_type_node = make_node(RECORD_TYPE);
|
||||
tree_chain record_chain;
|
||||
|
||||
for (auto& field : record_type->fields)
|
||||
{
|
||||
if (field_names.find(field.first) != field_names.cend())
|
||||
{
|
||||
error_at(get_location(&field.second->position()), "repeated field name");
|
||||
return error_mark_node;
|
||||
}
|
||||
field_names.insert(field.first);
|
||||
|
||||
tree field_type = build_type(*field.second);
|
||||
if (field_type == NULL_TREE || field_type == error_mark_node)
|
||||
{
|
||||
return field_type;
|
||||
}
|
||||
tree field_declaration = build_decl(get_location(&field.second->position()),
|
||||
FIELD_DECL, get_identifier(field.first.c_str()), field_type);
|
||||
TREE_ADDRESSABLE(field_declaration) = 1;
|
||||
DECL_CONTEXT(field_declaration) = record_type_node;
|
||||
|
||||
record_chain.append(field_declaration);
|
||||
}
|
||||
TYPE_FIELDS(record_type_node) = record_chain.head();
|
||||
layout_type(record_type_node);
|
||||
|
||||
return record_type_node;
|
||||
}
|
||||
else if (source::union_type_expression *union_type = type.is_union())
|
||||
{
|
||||
std::set<std::string> field_names;
|
||||
tree union_type_node = make_node(UNION_TYPE);
|
||||
tree_chain union_chain;
|
||||
|
||||
for (auto& field : union_type->fields)
|
||||
{
|
||||
if (field_names.find(field.first) != field_names.cend())
|
||||
{
|
||||
error_at(get_location(&field.second->position()), "repeated field name");
|
||||
return error_mark_node;
|
||||
}
|
||||
field_names.insert(field.first);
|
||||
|
||||
tree field_type = build_type(*field.second);
|
||||
if (field_type == NULL_TREE || field_type == error_mark_node)
|
||||
{
|
||||
return field_type;
|
||||
}
|
||||
tree field_declaration = build_decl(get_location(&field.second->position()),
|
||||
FIELD_DECL, get_identifier(field.first.c_str()), field_type);
|
||||
TREE_ADDRESSABLE(field_declaration) = 1;
|
||||
DECL_CONTEXT(field_declaration) = union_type_node;
|
||||
|
||||
union_chain.append(field_declaration);
|
||||
}
|
||||
TYPE_FIELDS(union_type_node) = union_chain.head();
|
||||
layout_type(union_type_node);
|
||||
|
||||
return union_type_node;
|
||||
}
|
||||
return NULL_TREE;
|
||||
}
|
||||
|
||||
void generic_visitor::visit(source::variable_declaration *declaration)
|
||||
{
|
||||
tree declaration_type = build_type(declaration->type());
|
||||
gcc_assert(declaration_type != NULL_TREE);
|
||||
|
||||
auto declaration_location = get_location(&declaration->position());
|
||||
tree declaration_tree = build_decl(declaration_location, VAR_DECL,
|
||||
get_identifier(declaration->identifier().c_str()), declaration_type);
|
||||
auto result = this->symbol_map->enter(declaration->identifier(), source::make_info(declaration_tree));
|
||||
|
||||
if (result)
|
||||
{
|
||||
DECL_CONTEXT(declaration_tree) = this->main_fndecl;
|
||||
variable_chain.append(declaration_tree);
|
||||
|
||||
auto declaration_statement = build1_loc(declaration_location, DECL_EXPR,
|
||||
void_type_node, declaration_tree);
|
||||
append_to_statement_list(declaration_statement, &this->current_statements);
|
||||
}
|
||||
else
|
||||
{
|
||||
error_at(declaration_location,
|
||||
"variable '%s' already declared in this scope",
|
||||
declaration->identifier().c_str());
|
||||
}
|
||||
}
|
||||
|
||||
void generic_visitor::visit(source::variable_expression *expression)
|
||||
{
|
||||
auto symbol = this->symbol_map->lookup(expression->name());
|
||||
|
||||
if (!symbol)
|
||||
{
|
||||
error_at(get_location(&expression->position()),
|
||||
"variable '%s' not declared in the current scope",
|
||||
expression->name().c_str());
|
||||
this->current_expression = error_mark_node;
|
||||
return;
|
||||
}
|
||||
this->current_expression = symbol->payload;
|
||||
}
|
||||
|
||||
void generic_visitor::visit(source::array_access_expression *expression)
|
||||
{
|
||||
expression->base().accept(this);
|
||||
tree designator = this->current_expression;
|
||||
|
||||
expression->index().accept(this);
|
||||
tree index = this->current_expression;
|
||||
|
||||
tree element_type = TREE_TYPE(TREE_TYPE(designator));
|
||||
|
||||
this->current_expression = build4_loc(get_location(&expression->position()),
|
||||
ARRAY_REF, element_type, designator, index, NULL_TREE, NULL_TREE);
|
||||
}
|
||||
|
||||
void generic_visitor::visit(source::field_access_expression *expression)
|
||||
{
|
||||
expression->base().accept(this);
|
||||
tree field_declaration = TYPE_FIELDS(TREE_TYPE(this->current_expression));
|
||||
|
||||
while (field_declaration != NULL_TREE)
|
||||
{
|
||||
tree declaration_name = DECL_NAME(field_declaration);
|
||||
const char *identifier_pointer = IDENTIFIER_POINTER(declaration_name);
|
||||
|
||||
if (expression->field() == identifier_pointer)
|
||||
{
|
||||
break;
|
||||
}
|
||||
field_declaration = TREE_CHAIN(field_declaration);
|
||||
}
|
||||
location_t expression_location = get_location(&expression->position());
|
||||
if (field_declaration == NULL_TREE)
|
||||
{
|
||||
error_at(expression_location,
|
||||
"record type does not have a field named '%s'",
|
||||
expression->field().c_str());
|
||||
this->current_expression = error_mark_node;
|
||||
}
|
||||
else
|
||||
{
|
||||
this->current_expression = build3_loc(expression_location, COMPONENT_REF,
|
||||
TREE_TYPE(field_declaration), this->current_expression,
|
||||
field_declaration, NULL_TREE);
|
||||
}
|
||||
}
|
||||
|
||||
void generic_visitor::visit(source::dereference_expression *expression)
|
||||
{
|
||||
expression->base().accept(this);
|
||||
|
||||
this->current_expression = build1_loc(get_location(&expression->position()), INDIRECT_REF,
|
||||
TREE_TYPE(TREE_TYPE(this->current_expression)), this->current_expression);
|
||||
}
|
||||
|
||||
void generic_visitor::visit(source::assign_statement *statement)
|
||||
{
|
||||
statement->lvalue().accept(this);
|
||||
|
||||
auto lvalue = this->current_expression;
|
||||
auto statement_location = get_location(&statement->position());
|
||||
|
||||
statement->rvalue().accept(this);
|
||||
|
||||
if (TREE_CODE(lvalue) == CONST_DECL)
|
||||
{
|
||||
error_at(statement_location, "cannot modify constant '%s'",
|
||||
statement->lvalue().is_variable()->name().c_str());
|
||||
this->current_expression = error_mark_node;
|
||||
return;
|
||||
}
|
||||
if (TREE_TYPE(this->current_expression) != TREE_TYPE(lvalue))
|
||||
{
|
||||
error_at(statement_location,
|
||||
"cannot assign value of type %s to variable of type %s",
|
||||
print_type(TREE_TYPE(this->current_expression)),
|
||||
print_type(TREE_TYPE(lvalue)));
|
||||
this->current_expression = error_mark_node;
|
||||
return;
|
||||
}
|
||||
auto assignment = build2_loc(statement_location, MODIFY_EXPR,
|
||||
void_type_node, lvalue, this->current_expression);
|
||||
|
||||
append_to_statement_list(assignment, &this->current_statements);
|
||||
this->current_expression = NULL_TREE;
|
||||
}
|
||||
|
||||
void generic_visitor::visit(source::if_statement *statement)
|
||||
{
|
||||
statement->prerequisite().accept(this);
|
||||
|
||||
if (TREE_TYPE(this->current_expression) != boolean_type_node)
|
||||
{
|
||||
error_at(get_location(&statement->prerequisite().position()),
|
||||
"expected expression of boolean type but its type is %s",
|
||||
print_type(TREE_TYPE(this->current_expression)));
|
||||
this->current_expression = error_mark_node;
|
||||
return;
|
||||
}
|
||||
auto then_location = get_location(&statement->body().position());
|
||||
auto prerequisite_location = get_location(&statement->prerequisite().position());
|
||||
|
||||
auto then_label_decl = build_label_decl("then", then_location);
|
||||
auto endif_label_decl = build_label_decl("end_if", then_location);
|
||||
|
||||
auto goto_then = build1_loc(prerequisite_location, GOTO_EXPR,
|
||||
void_type_node, then_label_decl);
|
||||
auto goto_endif = build1_loc(prerequisite_location, GOTO_EXPR,
|
||||
void_type_node, endif_label_decl);
|
||||
|
||||
tree else_label_decl = NULL_TREE;
|
||||
tree goto_else_or_endif = NULL_TREE;
|
||||
if (statement->alternative() != nullptr)
|
||||
{
|
||||
auto else_location = get_location(&statement->alternative()->position());
|
||||
else_label_decl = build_label_decl("else", else_location);
|
||||
goto_else_or_endif = build1_loc(else_location, GOTO_EXPR, void_type_node, else_label_decl);
|
||||
}
|
||||
else
|
||||
{
|
||||
goto_else_or_endif = goto_endif;
|
||||
}
|
||||
|
||||
auto cond_expr = build3_loc(prerequisite_location, COND_EXPR,
|
||||
void_type_node, this->current_expression, goto_then, goto_else_or_endif);
|
||||
append_to_statement_list(cond_expr, &this->current_statements);
|
||||
|
||||
auto then_label_expr = build1_loc(then_location, LABEL_EXPR,
|
||||
void_type_node, then_label_decl);
|
||||
append_to_statement_list(then_label_expr, &this->current_statements);
|
||||
|
||||
statement->body().accept(this);
|
||||
|
||||
if (statement->alternative() != nullptr)
|
||||
{
|
||||
append_to_statement_list(goto_endif, &this->current_statements);
|
||||
|
||||
auto else_label_expr = build1(LABEL_EXPR, void_type_node, else_label_decl);
|
||||
append_to_statement_list(else_label_expr, &this->current_statements);
|
||||
|
||||
statement->alternative()->accept(this);
|
||||
}
|
||||
auto endif_label_expr = build1(LABEL_EXPR, void_type_node, endif_label_decl);
|
||||
append_to_statement_list(endif_label_expr, &this->current_statements);
|
||||
|
||||
this->current_expression = NULL_TREE;
|
||||
}
|
||||
|
||||
tree generic_visitor::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) = this->main_fndecl;
|
||||
|
||||
return label_decl;
|
||||
}
|
||||
|
||||
void generic_visitor::visit(source::while_statement *statement)
|
||||
{
|
||||
statement->prerequisite().accept(this);
|
||||
|
||||
if (TREE_TYPE(this->current_expression) != boolean_type_node)
|
||||
{
|
||||
error_at(get_location(&statement->prerequisite().position()),
|
||||
"expected expression of boolean type but its type is %s",
|
||||
print_type(TREE_TYPE(this->current_expression)));
|
||||
this->current_expression = error_mark_node;
|
||||
return;
|
||||
}
|
||||
auto prerequisite_location = get_location(&statement->prerequisite().position());
|
||||
auto body_location = get_location(&statement->body().position());
|
||||
|
||||
auto prerequisite_label_decl = build_label_decl("while_check", prerequisite_location);
|
||||
auto prerequisite_label_expr = build1_loc(prerequisite_location, LABEL_EXPR,
|
||||
void_type_node, prerequisite_label_decl);
|
||||
append_to_statement_list(prerequisite_label_expr, &this->current_statements);
|
||||
|
||||
auto body_label_decl = build_label_decl("while_body", body_location);
|
||||
auto end_label_decl = build_label_decl("end_while", UNKNOWN_LOCATION);
|
||||
|
||||
auto goto_body = build1_loc(prerequisite_location, GOTO_EXPR,
|
||||
void_type_node, body_label_decl);
|
||||
auto goto_end = build1_loc(prerequisite_location, GOTO_EXPR,
|
||||
void_type_node, end_label_decl);
|
||||
|
||||
auto cond_expr = build3_loc(prerequisite_location, COND_EXPR,
|
||||
void_type_node, this->current_expression, goto_body, goto_end);
|
||||
append_to_statement_list(cond_expr, &this->current_statements);
|
||||
|
||||
auto body_label_expr = build1_loc(body_location, LABEL_EXPR,
|
||||
void_type_node, body_label_decl);
|
||||
append_to_statement_list(body_label_expr, &this->current_statements);
|
||||
|
||||
statement->body().accept(this);
|
||||
|
||||
auto goto_check = build1(GOTO_EXPR, void_type_node, prerequisite_label_decl);
|
||||
append_to_statement_list(goto_check, &this->current_statements);
|
||||
|
||||
auto endif_label_expr = build1(LABEL_EXPR, void_type_node, end_label_decl);
|
||||
append_to_statement_list(endif_label_expr, &this->current_statements);
|
||||
|
||||
this->current_expression = NULL_TREE;
|
||||
}
|
||||
|
||||
void generic_visitor::visit(source::expression_statement *statement)
|
||||
{
|
||||
statement->body().accept(this);
|
||||
}
|
||||
|
||||
void generic_visitor::visit(source::return_statement *statement)
|
||||
{
|
||||
source::expression *return_expression = statement->return_expression();
|
||||
|
||||
if (return_expression == nullptr)
|
||||
{
|
||||
return;
|
||||
}
|
||||
return_expression->accept(this);
|
||||
|
||||
tree set_result = build2(INIT_EXPR, void_type_node, DECL_RESULT(main_fndecl),
|
||||
this->current_expression);
|
||||
tree return_stmt = build1(RETURN_EXPR, void_type_node, set_result);
|
||||
append_to_statement_list(return_stmt, &this->current_statements);
|
||||
}
|
||||
}
|
||||
}
|
16
gcc/elna-spec.cc
Normal file
16
gcc/elna-spec.cc
Normal file
@ -0,0 +1,16 @@
|
||||
void
|
||||
lang_specific_driver (struct cl_decoded_option ** /* in_decoded_options */,
|
||||
unsigned int * /* in_decoded_options_count */,
|
||||
int * /*in_added_libraries */)
|
||||
{
|
||||
}
|
||||
|
||||
/* Called before linking. Returns 0 on success and -1 on failure. */
|
||||
int
|
||||
lang_specific_pre_link (void)
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* Number of extra output files that lang_specific_pre_link may generate. */
|
||||
int lang_specific_extra_outfiles = 0;
|
85
gcc/elna-tree.cc
Normal file
85
gcc/elna-tree.cc
Normal file
@ -0,0 +1,85 @@
|
||||
#include "elna/gcc/elna-tree.h"
|
||||
|
||||
#include "stor-layout.h"
|
||||
|
||||
tree elna_global_trees[ELNA_TI_MAX];
|
||||
|
||||
namespace elna
|
||||
{
|
||||
namespace gcc
|
||||
{
|
||||
void init_ttree()
|
||||
{
|
||||
elna_char_type_node = make_unsigned_type(8);
|
||||
elna_string_type_node = build_pointer_type(
|
||||
build_qualified_type(char_type_node, TYPE_QUAL_CONST)); /* const char* */
|
||||
TYPE_STRING_FLAG(elna_char_type_node) = 1;
|
||||
}
|
||||
|
||||
bool is_pointer_type(tree type)
|
||||
{
|
||||
gcc_assert(TYPE_P(type));
|
||||
return TREE_CODE(type) == POINTER_TYPE;
|
||||
}
|
||||
|
||||
bool is_string_type(tree type)
|
||||
{
|
||||
return is_pointer_type(type)
|
||||
&& TYPE_MAIN_VARIANT(TREE_TYPE(type)) == char_type_node;
|
||||
}
|
||||
|
||||
tree tree_chain_base::head()
|
||||
{
|
||||
return first;
|
||||
}
|
||||
|
||||
void tree_chain_base::append(tree t)
|
||||
{
|
||||
gcc_assert(t != NULL_TREE);
|
||||
|
||||
if (this->first == NULL_TREE)
|
||||
{
|
||||
this->first = this->last = t;
|
||||
}
|
||||
else
|
||||
{
|
||||
chain(t);
|
||||
this->last = t;
|
||||
}
|
||||
}
|
||||
|
||||
void tree_chain::chain(tree t)
|
||||
{
|
||||
TREE_CHAIN(this->last) = t;
|
||||
}
|
||||
|
||||
tree_symbol_mapping::tree_symbol_mapping(tree bind_expression, tree block)
|
||||
: m_bind_expression(bind_expression), m_block(block)
|
||||
{
|
||||
}
|
||||
|
||||
tree tree_symbol_mapping::bind_expression()
|
||||
{
|
||||
return m_bind_expression;
|
||||
}
|
||||
|
||||
tree tree_symbol_mapping::block()
|
||||
{
|
||||
return m_block;
|
||||
}
|
||||
|
||||
std::shared_ptr<elna::source::symbol_table<tree>> builtin_symbol_table()
|
||||
{
|
||||
std::shared_ptr<elna::source::symbol_table<tree>> initial_table =
|
||||
std::make_shared<elna::source::symbol_table<tree>>();
|
||||
|
||||
initial_table->enter("Int", source::make_info(integer_type_node));
|
||||
initial_table->enter("Bool", source::make_info(boolean_type_node));
|
||||
initial_table->enter("Float", source::make_info(double_type_node));
|
||||
initial_table->enter("Char", source::make_info(elna_char_type_node));
|
||||
initial_table->enter("String", source::make_info(elna_string_type_node));
|
||||
|
||||
return initial_table;
|
||||
}
|
||||
}
|
||||
}
|
243
gcc/elna1.cc
Normal file
243
gcc/elna1.cc
Normal file
@ -0,0 +1,243 @@
|
||||
#include "config.h"
|
||||
#include "system.h"
|
||||
#include "coretypes.h"
|
||||
#include "target.h"
|
||||
#include "tree.h"
|
||||
#include "tree-iterator.h"
|
||||
#include "gimple-expr.h"
|
||||
#include "diagnostic.h"
|
||||
#include "opts.h"
|
||||
#include "fold-const.h"
|
||||
#include "stor-layout.h"
|
||||
#include "debug.h"
|
||||
#include "langhooks.h"
|
||||
#include "langhooks-def.h"
|
||||
#include "common/common-target.h"
|
||||
|
||||
#include <fstream>
|
||||
#include <elna/source/driver.h>
|
||||
#include "elna/gcc/elna-tree.h"
|
||||
#include "elna/gcc/elna-generic.h"
|
||||
#include "elna/gcc/elna-diagnostic.h"
|
||||
#include "parser.hh"
|
||||
|
||||
/* Language-dependent contents of a type. */
|
||||
|
||||
struct GTY (()) lang_type
|
||||
{
|
||||
char dummy;
|
||||
};
|
||||
|
||||
/* Language-dependent contents of a decl. */
|
||||
|
||||
struct GTY (()) lang_decl
|
||||
{
|
||||
char dummy;
|
||||
};
|
||||
|
||||
/* Language-dependent contents of an identifier. This must include a
|
||||
tree_identifier. */
|
||||
|
||||
struct GTY (()) lang_identifier
|
||||
{
|
||||
struct tree_identifier common;
|
||||
};
|
||||
|
||||
/* The resulting tree type. */
|
||||
|
||||
union GTY ((desc ("TREE_CODE (&%h.generic) == IDENTIFIER_NODE"),
|
||||
chain_next ("CODE_CONTAINS_STRUCT (TREE_CODE (&%h.generic), "
|
||||
"TS_COMMON) ? ((union lang_tree_node *) TREE_CHAIN "
|
||||
"(&%h.generic)) : NULL"))) lang_tree_node
|
||||
{
|
||||
union tree_node GTY ((tag ("0"), desc ("tree_node_structure (&%h)"))) generic;
|
||||
struct lang_identifier GTY ((tag ("1"))) identifier;
|
||||
};
|
||||
|
||||
/* We don't use language_function. */
|
||||
|
||||
struct GTY (()) language_function
|
||||
{
|
||||
int dummy;
|
||||
};
|
||||
|
||||
/* Language hooks. */
|
||||
|
||||
static bool elna_langhook_init(void)
|
||||
{
|
||||
build_common_tree_nodes(false);
|
||||
elna::gcc::init_ttree();
|
||||
|
||||
void_list_node = build_tree_list(NULL_TREE, void_type_node);
|
||||
|
||||
build_common_builtin_nodes();
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
static void elna_parse_file(const char *filename)
|
||||
{
|
||||
std::ifstream file{ filename, std::ios::in };
|
||||
|
||||
if (!file)
|
||||
{
|
||||
fatal_error(UNKNOWN_LOCATION, "cannot open filename %s: %m", filename);
|
||||
}
|
||||
|
||||
elna::source::driver driver{ filename };
|
||||
elna::source::lexer lexer(file);
|
||||
yy::parser parser(lexer, driver);
|
||||
|
||||
linemap_add(line_table, LC_ENTER, 0, filename, 1);
|
||||
if (parser())
|
||||
{
|
||||
for (const auto& error : driver.errors())
|
||||
{
|
||||
auto gcc_location = elna::gcc::get_location(&error->position);
|
||||
|
||||
error_at(gcc_location, error->what().c_str());
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
elna::gcc::generic_visitor generic_visitor{ elna::gcc::builtin_symbol_table() };
|
||||
|
||||
generic_visitor.visit(driver.tree.get());
|
||||
}
|
||||
linemap_add(line_table, LC_LEAVE, 0, NULL, 0);
|
||||
}
|
||||
|
||||
static void elna_langhook_parse_file(void)
|
||||
{
|
||||
for (unsigned int i = 0; i < num_in_fnames; i++)
|
||||
{
|
||||
elna_parse_file(in_fnames[i]);
|
||||
}
|
||||
}
|
||||
|
||||
static tree elna_langhook_type_for_mode(enum machine_mode mode, int unsignedp)
|
||||
{
|
||||
if (mode == TYPE_MODE(float_type_node))
|
||||
{
|
||||
return float_type_node;
|
||||
}
|
||||
else if (mode == TYPE_MODE(double_type_node))
|
||||
{
|
||||
return double_type_node;
|
||||
}
|
||||
if (mode == TYPE_MODE(intQI_type_node))
|
||||
{
|
||||
return unsignedp ? unsigned_intQI_type_node : intQI_type_node;
|
||||
}
|
||||
else if (mode == TYPE_MODE(intHI_type_node))
|
||||
{
|
||||
return unsignedp ? unsigned_intHI_type_node : intHI_type_node;
|
||||
}
|
||||
else if (mode == TYPE_MODE(intSI_type_node))
|
||||
{
|
||||
return unsignedp ? unsigned_intSI_type_node : intSI_type_node;
|
||||
}
|
||||
else if (mode == TYPE_MODE(intDI_type_node))
|
||||
{
|
||||
return unsignedp ? unsigned_intDI_type_node : intDI_type_node;
|
||||
}
|
||||
else if (mode == TYPE_MODE(intTI_type_node))
|
||||
{
|
||||
return unsignedp ? unsigned_intTI_type_node : intTI_type_node;
|
||||
}
|
||||
else if (mode == TYPE_MODE(integer_type_node))
|
||||
{
|
||||
return unsignedp ? unsigned_type_node : integer_type_node;
|
||||
}
|
||||
else if (mode == TYPE_MODE(long_integer_type_node))
|
||||
{
|
||||
return unsignedp ? long_unsigned_type_node : long_integer_type_node;
|
||||
}
|
||||
else if (mode == TYPE_MODE(long_long_integer_type_node))
|
||||
{
|
||||
return unsignedp
|
||||
? long_long_unsigned_type_node
|
||||
: long_long_integer_type_node;
|
||||
}
|
||||
if (COMPLEX_MODE_P(mode))
|
||||
{
|
||||
if (mode == TYPE_MODE(complex_float_type_node))
|
||||
{
|
||||
return complex_float_type_node;
|
||||
}
|
||||
if (mode == TYPE_MODE(complex_double_type_node))
|
||||
{
|
||||
return complex_double_type_node;
|
||||
}
|
||||
if (mode == TYPE_MODE(complex_long_double_type_node))
|
||||
{
|
||||
return complex_long_double_type_node;
|
||||
}
|
||||
if (mode == TYPE_MODE(complex_integer_type_node) && !unsignedp)
|
||||
{
|
||||
return complex_integer_type_node;
|
||||
}
|
||||
}
|
||||
/* gcc_unreachable */
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
static tree elna_langhook_type_for_size(unsigned int bits ATTRIBUTE_UNUSED,
|
||||
int unsignedp ATTRIBUTE_UNUSED)
|
||||
{
|
||||
gcc_unreachable();
|
||||
}
|
||||
|
||||
/* Record a builtin function. We just ignore builtin functions. */
|
||||
|
||||
static tree elna_langhook_builtin_function(tree decl)
|
||||
{
|
||||
return decl;
|
||||
}
|
||||
|
||||
static bool elna_langhook_global_bindings_p(void)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
static tree elna_langhook_pushdecl(tree decl ATTRIBUTE_UNUSED)
|
||||
{
|
||||
gcc_unreachable();
|
||||
}
|
||||
|
||||
static tree elna_langhook_getdecls(void)
|
||||
{
|
||||
return NULL;
|
||||
}
|
||||
|
||||
#undef LANG_HOOKS_NAME
|
||||
#define LANG_HOOKS_NAME "Elna"
|
||||
|
||||
#undef LANG_HOOKS_INIT
|
||||
#define LANG_HOOKS_INIT elna_langhook_init
|
||||
|
||||
#undef LANG_HOOKS_PARSE_FILE
|
||||
#define LANG_HOOKS_PARSE_FILE elna_langhook_parse_file
|
||||
|
||||
#undef LANG_HOOKS_TYPE_FOR_MODE
|
||||
#define LANG_HOOKS_TYPE_FOR_MODE elna_langhook_type_for_mode
|
||||
|
||||
#undef LANG_HOOKS_TYPE_FOR_SIZE
|
||||
#define LANG_HOOKS_TYPE_FOR_SIZE elna_langhook_type_for_size
|
||||
|
||||
#undef LANG_HOOKS_BUILTIN_FUNCTION
|
||||
#define LANG_HOOKS_BUILTIN_FUNCTION elna_langhook_builtin_function
|
||||
|
||||
#undef LANG_HOOKS_GLOBAL_BINDINGS_P
|
||||
#define LANG_HOOKS_GLOBAL_BINDINGS_P elna_langhook_global_bindings_p
|
||||
|
||||
#undef LANG_HOOKS_PUSHDECL
|
||||
#define LANG_HOOKS_PUSHDECL elna_langhook_pushdecl
|
||||
|
||||
#undef LANG_HOOKS_GETDECLS
|
||||
#define LANG_HOOKS_GETDECLS elna_langhook_getdecls
|
||||
|
||||
struct lang_hooks lang_hooks = LANG_HOOKS_INITIALIZER;
|
||||
|
||||
#include "gt-elna-elna1.h"
|
||||
#include "gtype-elna.h"
|
6
gcc/lang-specs.h
Normal file
6
gcc/lang-specs.h
Normal file
@ -0,0 +1,6 @@
|
||||
/* gcc/gcc.cc */
|
||||
{".elna", "@elna", nullptr, 0, 0},
|
||||
{"@elna",
|
||||
"elna1 %{!Q:-quiet} \
|
||||
%i %{!fsyntax-only:%(invoke_as)}",
|
||||
nullptr, 0, 0},
|
19
include/elna/gcc/elna-diagnostic.h
Normal file
19
include/elna/gcc/elna-diagnostic.h
Normal file
@ -0,0 +1,19 @@
|
||||
#pragma once
|
||||
|
||||
#include "config.h"
|
||||
#include "system.h"
|
||||
#include "coretypes.h"
|
||||
#include "input.h"
|
||||
#include "tree.h"
|
||||
|
||||
#include "elna/source/result.h"
|
||||
|
||||
namespace elna
|
||||
{
|
||||
namespace gcc
|
||||
{
|
||||
location_t get_location(const elna::source::position *position);
|
||||
|
||||
const char *print_type(tree type);
|
||||
}
|
||||
}
|
64
include/elna/gcc/elna-generic.h
Normal file
64
include/elna/gcc/elna-generic.h
Normal file
@ -0,0 +1,64 @@
|
||||
#pragma once
|
||||
|
||||
#include "elna/source/ast.h"
|
||||
#include "elna/source/symbol.h"
|
||||
#include "elna/gcc/elna-tree.h"
|
||||
|
||||
#include "config.h"
|
||||
#include "system.h"
|
||||
#include "coretypes.h"
|
||||
#include "tree.h"
|
||||
#include "tree-iterator.h"
|
||||
|
||||
#include <unordered_map>
|
||||
#include <string>
|
||||
|
||||
namespace elna
|
||||
{
|
||||
namespace gcc
|
||||
{
|
||||
class generic_visitor final : public source::empty_visitor
|
||||
{
|
||||
tree current_statements{ NULL_TREE };
|
||||
tree current_expression{ NULL_TREE };
|
||||
std::shared_ptr<source::symbol_table<tree>> symbol_map;
|
||||
tree main_fndecl{ NULL_TREE };
|
||||
tree_chain variable_chain;
|
||||
|
||||
tree build_label_decl(const char *name, location_t loc);
|
||||
tree build_type(source::type_expression& type);
|
||||
|
||||
void enter_scope();
|
||||
tree_symbol_mapping leave_scope();
|
||||
|
||||
void build_binary_operation(bool condition, source::binary_expression *expression,
|
||||
tree_code operator_code, tree left, tree right, tree target_type);
|
||||
|
||||
public:
|
||||
generic_visitor(std::shared_ptr<source::symbol_table<tree>> symbol_table);
|
||||
|
||||
void visit(source::program *program) override;
|
||||
void visit(source::procedure_definition *definition) override;
|
||||
void visit(source::call_expression *statement) override;
|
||||
void visit(source::number_literal<std::int32_t> *literal) override;
|
||||
void visit(source::number_literal<double> *literal) override;
|
||||
void visit(source::number_literal<bool> *boolean) override;
|
||||
void visit(source::number_literal<unsigned char> *character) override;
|
||||
void visit(source::string_literal *string) override;
|
||||
void visit(source::binary_expression *expression) override;
|
||||
void visit(source::unary_expression *expression) override;
|
||||
void visit(source::constant_definition *definition) override;
|
||||
void visit(source::type_definition *definition) override;
|
||||
void visit(source::variable_declaration *declaration) override;
|
||||
void visit(source::variable_expression *expression) override;
|
||||
void visit(source::array_access_expression *expression) override;
|
||||
void visit(source::field_access_expression *expression) override;
|
||||
void visit(source::dereference_expression *expression) override;
|
||||
void visit(source::assign_statement *statement) override;
|
||||
void visit(source::if_statement *statement) override;
|
||||
void visit(source::while_statement *statement) override;
|
||||
void visit(source::expression_statement *statement) override;
|
||||
void visit(source::return_statement *statement) override;
|
||||
};
|
||||
}
|
||||
}
|
64
include/elna/gcc/elna-tree.h
Normal file
64
include/elna/gcc/elna-tree.h
Normal file
@ -0,0 +1,64 @@
|
||||
#pragma once
|
||||
|
||||
#include "config.h"
|
||||
#include "system.h"
|
||||
#include "coretypes.h"
|
||||
#include "tree.h"
|
||||
#include "tree.h"
|
||||
|
||||
#include "elna/source/symbol.h"
|
||||
|
||||
enum elna_tree_index
|
||||
{
|
||||
ELNA_TI_CHAR_TYPE,
|
||||
ELNA_TI_STRING_TYPE,
|
||||
ELNA_TI_MAX
|
||||
};
|
||||
|
||||
extern GTY(()) tree elna_global_trees[ELNA_TI_MAX];
|
||||
|
||||
#define elna_char_type_node elna_global_trees[ELNA_TI_CHAR_TYPE]
|
||||
#define elna_string_type_node elna_global_trees[ELNA_TI_STRING_TYPE]
|
||||
|
||||
namespace elna
|
||||
{
|
||||
namespace gcc
|
||||
{
|
||||
void init_ttree();
|
||||
bool is_pointer_type(tree type);
|
||||
bool is_string_type(tree type);
|
||||
|
||||
class tree_chain_base
|
||||
{
|
||||
protected:
|
||||
tree first{};
|
||||
tree last{};
|
||||
|
||||
public:
|
||||
tree head();
|
||||
void append(tree t);
|
||||
|
||||
protected:
|
||||
virtual void chain(tree t) = 0;
|
||||
};
|
||||
|
||||
class tree_chain final : public tree_chain_base
|
||||
{
|
||||
void chain(tree t) override;
|
||||
};
|
||||
|
||||
class tree_symbol_mapping final
|
||||
{
|
||||
tree m_bind_expression;
|
||||
tree m_block;
|
||||
|
||||
public:
|
||||
tree_symbol_mapping(tree bind_expression, tree block);
|
||||
|
||||
tree bind_expression();
|
||||
tree block();
|
||||
};
|
||||
|
||||
std::shared_ptr<source::symbol_table<tree>> builtin_symbol_table();
|
||||
}
|
||||
}
|
750
include/elna/source/ast.h
Normal file
750
include/elna/source/ast.h
Normal file
@ -0,0 +1,750 @@
|
||||
// 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 http://mozilla.org/MPL/2.0/.
|
||||
#pragma once
|
||||
|
||||
#include <cstdint>
|
||||
#include <memory>
|
||||
#include <string>
|
||||
#include <vector>
|
||||
#include "elna/source/result.h"
|
||||
|
||||
namespace elna
|
||||
{
|
||||
namespace source
|
||||
{
|
||||
enum class binary_operator
|
||||
{
|
||||
sum,
|
||||
subtraction,
|
||||
multiplication,
|
||||
division,
|
||||
equals,
|
||||
not_equals,
|
||||
less,
|
||||
greater,
|
||||
less_equal,
|
||||
greater_equal,
|
||||
disjunction,
|
||||
conjunction
|
||||
};
|
||||
|
||||
enum class unary_operator
|
||||
{
|
||||
reference,
|
||||
negation
|
||||
};
|
||||
|
||||
class variable_declaration;
|
||||
class constant_definition;
|
||||
class procedure_definition;
|
||||
class type_definition;
|
||||
class call_expression;
|
||||
class compound_statement;
|
||||
class assign_statement;
|
||||
class if_statement;
|
||||
class while_statement;
|
||||
class return_statement;
|
||||
class expression_statement;
|
||||
class block;
|
||||
class program;
|
||||
class binary_expression;
|
||||
class unary_expression;
|
||||
class basic_type_expression;
|
||||
class array_type_expression;
|
||||
class pointer_type_expression;
|
||||
class record_type_expression;
|
||||
class union_type_expression;
|
||||
class variable_expression;
|
||||
class array_access_expression;
|
||||
class field_access_expression;
|
||||
class dereference_expression;
|
||||
template<typename T>
|
||||
class number_literal;
|
||||
class char_literal;
|
||||
class string_literal;
|
||||
|
||||
/**
|
||||
* Interface for AST visitors.
|
||||
*/
|
||||
struct parser_visitor
|
||||
{
|
||||
virtual void visit(variable_declaration *) = 0;
|
||||
virtual void visit(constant_definition *) = 0;
|
||||
virtual void visit(procedure_definition *) = 0;
|
||||
virtual void visit(type_definition *) = 0;
|
||||
virtual void visit(call_expression *) = 0;
|
||||
virtual void visit(expression_statement *) = 0;
|
||||
virtual void visit(compound_statement *) = 0;
|
||||
virtual void visit(assign_statement *) = 0;
|
||||
virtual void visit(if_statement *) = 0;
|
||||
virtual void visit(while_statement *) = 0;
|
||||
virtual void visit(return_statement *) = 0;
|
||||
virtual void visit(block *) = 0;
|
||||
virtual void visit(program *) = 0;
|
||||
virtual void visit(binary_expression *) = 0;
|
||||
virtual void visit(unary_expression *) = 0;
|
||||
virtual void visit(basic_type_expression *) = 0;
|
||||
virtual void visit(array_type_expression *) = 0;
|
||||
virtual void visit(pointer_type_expression *) = 0;
|
||||
virtual void visit(record_type_expression *) = 0;
|
||||
virtual void visit(union_type_expression *) = 0;
|
||||
virtual void visit(variable_expression *) = 0;
|
||||
virtual void visit(array_access_expression *) = 0;
|
||||
virtual void visit(field_access_expression *is_field_access) = 0;
|
||||
virtual void visit(dereference_expression *is_dereference) = 0;
|
||||
virtual void visit(number_literal<std::int32_t> *) = 0;
|
||||
virtual void visit(number_literal<double> *) = 0;
|
||||
virtual void visit(number_literal<bool> *) = 0;
|
||||
virtual void visit(number_literal<unsigned char> *) = 0;
|
||||
virtual void visit(string_literal *) = 0;
|
||||
};
|
||||
|
||||
/**
|
||||
* A visitor which visits all nodes but does nothing.
|
||||
*/
|
||||
struct empty_visitor : parser_visitor
|
||||
{
|
||||
virtual void visit(variable_declaration *) override;
|
||||
virtual void visit(constant_definition *definition) override;
|
||||
virtual void visit(procedure_definition *definition) override;
|
||||
virtual void visit(type_definition *definition) override;
|
||||
virtual void visit(call_expression *statement) override;
|
||||
virtual void visit(expression_statement *statement) override;
|
||||
virtual void visit(compound_statement *statement) override;
|
||||
virtual void visit(assign_statement *statement) override;
|
||||
virtual void visit(if_statement *) override;
|
||||
virtual void visit(while_statement *) override;
|
||||
virtual void visit(return_statement *) override;
|
||||
virtual void visit(block *block) override;
|
||||
virtual void visit(program *program) override;
|
||||
virtual void visit(binary_expression *expression) override;
|
||||
virtual void visit(unary_expression *expression) override;
|
||||
virtual void visit(basic_type_expression *) override;
|
||||
virtual void visit(array_type_expression *expression) override;
|
||||
virtual void visit(pointer_type_expression *) override;
|
||||
virtual void visit(record_type_expression *expression) override;
|
||||
virtual void visit(union_type_expression *expression) override;
|
||||
virtual void visit(variable_expression *) override;
|
||||
virtual void visit(array_access_expression *expression) override;
|
||||
virtual void visit(field_access_expression *expression) override;
|
||||
virtual void visit(dereference_expression *expression) override;
|
||||
virtual void visit(number_literal<std::int32_t> *) override;
|
||||
virtual void visit(number_literal<double> *) override;
|
||||
virtual void visit(number_literal<bool> *) override;
|
||||
virtual void visit(number_literal<unsigned char> *) override;
|
||||
virtual void visit(string_literal *) override;
|
||||
};
|
||||
|
||||
/**
|
||||
* Operand representing a subexpression in the 3 address code.
|
||||
*/
|
||||
struct operand
|
||||
{
|
||||
public:
|
||||
virtual ~operand() = 0;
|
||||
};
|
||||
|
||||
struct integer_operand final : public operand
|
||||
{
|
||||
std::int32_t m_value;
|
||||
|
||||
public:
|
||||
explicit integer_operand(const std::int32_t value);
|
||||
|
||||
std::int32_t value() const;
|
||||
};
|
||||
|
||||
class variable_operand final : public operand
|
||||
{
|
||||
std::string m_name;
|
||||
|
||||
public:
|
||||
explicit variable_operand(const std::string& name);
|
||||
|
||||
const std::string& name() const;
|
||||
};
|
||||
|
||||
struct temporary_variable final : public operand
|
||||
{
|
||||
std::size_t m_counter;
|
||||
|
||||
public:
|
||||
explicit temporary_variable(const std::size_t counter);
|
||||
|
||||
std::size_t counter() const;
|
||||
};
|
||||
|
||||
struct label_operand final : public operand
|
||||
{
|
||||
std::size_t m_counter;
|
||||
|
||||
public:
|
||||
explicit label_operand(const std::size_t counter);
|
||||
|
||||
std::size_t counter() const;
|
||||
};
|
||||
|
||||
/**
|
||||
* AST node.
|
||||
*/
|
||||
class node
|
||||
{
|
||||
const struct position source_position;
|
||||
|
||||
protected:
|
||||
/**
|
||||
* \param position Source code position.
|
||||
*/
|
||||
explicit node(const position position);
|
||||
|
||||
public:
|
||||
virtual ~node() = default;
|
||||
virtual void accept(parser_visitor *) = 0;
|
||||
|
||||
/**
|
||||
* \return Node position in the source code.
|
||||
*/
|
||||
const struct position& position() const;
|
||||
};
|
||||
|
||||
class statement : public node
|
||||
{
|
||||
protected:
|
||||
/**
|
||||
* \param position Source code position.
|
||||
*/
|
||||
explicit statement(const struct position position);
|
||||
};
|
||||
|
||||
class expression : public node
|
||||
{
|
||||
public:
|
||||
std::shared_ptr<operand> place;
|
||||
|
||||
protected:
|
||||
/**
|
||||
* \param position Source code position.
|
||||
*/
|
||||
explicit expression(const struct position position);
|
||||
};
|
||||
|
||||
/**
|
||||
* Symbol definition.
|
||||
*/
|
||||
class definition : public node
|
||||
{
|
||||
std::string m_identifier;
|
||||
|
||||
protected:
|
||||
/**
|
||||
* Constructs a definition identified by some name.
|
||||
*
|
||||
* \param position Source code position.
|
||||
* \param identifier Definition name.
|
||||
*/
|
||||
definition(const struct position position, const std::string& identifier);
|
||||
|
||||
public:
|
||||
/**
|
||||
* \return Definition name.
|
||||
*/
|
||||
std::string& identifier();
|
||||
};
|
||||
|
||||
/**
|
||||
* Some type expression.
|
||||
*/
|
||||
class type_expression : public node
|
||||
{
|
||||
public:
|
||||
virtual basic_type_expression *is_basic();
|
||||
virtual array_type_expression *is_array();
|
||||
virtual pointer_type_expression *is_pointer();
|
||||
virtual record_type_expression *is_record();
|
||||
virtual union_type_expression *is_union();
|
||||
|
||||
protected:
|
||||
type_expression(const struct position position);
|
||||
};
|
||||
|
||||
/**
|
||||
* Expression defining a basic type.
|
||||
*/
|
||||
class basic_type_expression final : public type_expression
|
||||
{
|
||||
const std::string m_name;
|
||||
|
||||
public:
|
||||
/**
|
||||
* \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 final : public type_expression
|
||||
{
|
||||
type_expression *m_base;
|
||||
|
||||
public:
|
||||
const std::uint32_t size;
|
||||
|
||||
array_type_expression(const struct position position, type_expression *base, const std::uint32_t size);
|
||||
virtual void accept(parser_visitor *visitor) override;
|
||||
|
||||
type_expression& base();
|
||||
|
||||
array_type_expression *is_array() override;
|
||||
|
||||
virtual ~array_type_expression() override;
|
||||
};
|
||||
|
||||
class pointer_type_expression final : public type_expression
|
||||
{
|
||||
type_expression *m_base;
|
||||
|
||||
public:
|
||||
pointer_type_expression(const struct position position, type_expression *base);
|
||||
virtual void accept(parser_visitor *visitor) override;
|
||||
|
||||
type_expression& base();
|
||||
|
||||
pointer_type_expression *is_pointer() override;
|
||||
|
||||
virtual ~pointer_type_expression() override;
|
||||
};
|
||||
|
||||
using field_t = std::pair<std::string, type_expression *>;
|
||||
using fields_t = std::vector<field_t>;
|
||||
|
||||
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:
|
||||
record_type_expression(const struct position position, fields_t&& fields);
|
||||
|
||||
virtual void accept(parser_visitor *visitor) override;
|
||||
record_type_expression *is_record() override;
|
||||
};
|
||||
|
||||
class union_type_expression final : public composite_type_expression
|
||||
{
|
||||
public:
|
||||
union_type_expression(const struct position position, fields_t&& fields);
|
||||
|
||||
virtual void accept(parser_visitor *visitor) override;
|
||||
union_type_expression *is_union() override;
|
||||
};
|
||||
|
||||
/**
|
||||
* Variable declaration.
|
||||
*/
|
||||
class variable_declaration : public definition
|
||||
{
|
||||
type_expression *m_type;
|
||||
|
||||
public:
|
||||
/**
|
||||
* Constructs a declaration with a name and a type.
|
||||
*
|
||||
* \param position Source code position.
|
||||
* \param identifier Definition name.
|
||||
* \param type Declared type.
|
||||
*/
|
||||
variable_declaration(const struct position position, const std::string& identifier,
|
||||
type_expression *type);
|
||||
virtual void accept(parser_visitor *visitor) override;
|
||||
|
||||
type_expression& type();
|
||||
|
||||
virtual ~variable_declaration() override;
|
||||
};
|
||||
|
||||
class literal : public expression
|
||||
{
|
||||
protected:
|
||||
explicit literal(const struct position position);
|
||||
};
|
||||
|
||||
/**
|
||||
* Constant definition.
|
||||
*/
|
||||
class constant_definition : public definition
|
||||
{
|
||||
literal *m_body;
|
||||
|
||||
public:
|
||||
/**
|
||||
* \param position Source code position.
|
||||
* \param identifier Constant name.
|
||||
* \param body Constant value.
|
||||
*/
|
||||
constant_definition(const struct position position, const std::string& identifier,
|
||||
literal *body);
|
||||
virtual void accept(parser_visitor *visitor) override;
|
||||
|
||||
literal& body();
|
||||
|
||||
virtual ~constant_definition() override;
|
||||
};
|
||||
|
||||
/**
|
||||
* Procedure definition.
|
||||
*/
|
||||
class procedure_definition : public definition
|
||||
{
|
||||
type_expression *m_return_type{ nullptr };
|
||||
block *m_body{ nullptr };
|
||||
|
||||
public:
|
||||
std::vector<variable_declaration *> parameters;
|
||||
|
||||
/**
|
||||
* \param position Source code position.
|
||||
* \param identifier Procedure name.
|
||||
* \param parameters Procedure formal parameters.
|
||||
* \param return_type Return type if any.
|
||||
* \param body Procedure body.
|
||||
*/
|
||||
procedure_definition(const struct position position, const std::string& identifier,
|
||||
std::vector<variable_declaration *>&& parameters,
|
||||
type_expression *return_type = nullptr, block *body = nullptr);
|
||||
virtual void accept(parser_visitor *visitor) override;
|
||||
|
||||
type_expression *return_type();
|
||||
block *body();
|
||||
|
||||
virtual ~procedure_definition() override;
|
||||
};
|
||||
|
||||
class type_definition : public definition
|
||||
{
|
||||
type_expression *m_body;
|
||||
|
||||
public:
|
||||
type_definition(const struct position position, const std::string& identifier,
|
||||
type_expression *expression);
|
||||
virtual void accept(parser_visitor *visitor) override;
|
||||
|
||||
type_expression& body();
|
||||
|
||||
virtual ~type_definition() override;
|
||||
};
|
||||
|
||||
/**
|
||||
* Call statement.
|
||||
*/
|
||||
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;
|
||||
};
|
||||
|
||||
class expression_statement : public statement
|
||||
{
|
||||
expression *m_body;
|
||||
|
||||
public:
|
||||
expression_statement(const struct position position, expression *body);
|
||||
virtual void accept(parser_visitor *visitor) override;
|
||||
|
||||
expression& body();
|
||||
|
||||
virtual ~expression_statement() override;
|
||||
};
|
||||
|
||||
class compound_statement : public node
|
||||
{
|
||||
public:
|
||||
std::vector<statement *> statements;
|
||||
|
||||
compound_statement(const struct position position, std::vector<statement *>&& statements);
|
||||
virtual void accept(parser_visitor *visitor) override;
|
||||
|
||||
virtual ~compound_statement() override;
|
||||
};
|
||||
|
||||
class return_statement : public statement
|
||||
{
|
||||
expression *m_return_expression{ nullptr };
|
||||
|
||||
public:
|
||||
return_statement(const struct position position, expression *return_expression);
|
||||
virtual void accept(parser_visitor *visitor) override;
|
||||
|
||||
expression *return_expression();
|
||||
|
||||
virtual ~return_statement() override;
|
||||
};
|
||||
|
||||
class designator_expression : public expression
|
||||
{
|
||||
public:
|
||||
virtual variable_expression *is_variable();
|
||||
virtual array_access_expression *is_array_access();
|
||||
virtual field_access_expression *is_field_access();
|
||||
virtual dereference_expression *is_dereference();
|
||||
|
||||
protected:
|
||||
designator_expression(const struct position position);
|
||||
};
|
||||
|
||||
class variable_expression : public designator_expression
|
||||
{
|
||||
std::string m_name;
|
||||
|
||||
public:
|
||||
variable_expression(const struct position position, const std::string& name);
|
||||
virtual void accept(parser_visitor *visitor) override;
|
||||
|
||||
const std::string& name() const;
|
||||
|
||||
variable_expression *is_variable() override;
|
||||
};
|
||||
|
||||
class array_access_expression : public designator_expression
|
||||
{
|
||||
designator_expression *m_base;
|
||||
expression *m_index;
|
||||
|
||||
public:
|
||||
array_access_expression(const struct position position, designator_expression *base, expression *index);
|
||||
virtual void accept(parser_visitor *visitor) override;
|
||||
|
||||
designator_expression& base();
|
||||
expression& index();
|
||||
|
||||
array_access_expression *is_array_access() override;
|
||||
|
||||
~array_access_expression() override;
|
||||
};
|
||||
|
||||
class field_access_expression : public designator_expression
|
||||
{
|
||||
designator_expression *m_base;
|
||||
std::string m_field;
|
||||
|
||||
public:
|
||||
field_access_expression(const struct position position, designator_expression *base,
|
||||
const std::string& field);
|
||||
virtual void accept(parser_visitor *visitor) override;
|
||||
|
||||
designator_expression& base();
|
||||
std::string& field();
|
||||
|
||||
field_access_expression *is_field_access() override;
|
||||
|
||||
~field_access_expression() override;
|
||||
};
|
||||
|
||||
class dereference_expression : public designator_expression
|
||||
{
|
||||
designator_expression *m_base;
|
||||
|
||||
public:
|
||||
dereference_expression(const struct position position, designator_expression *base);
|
||||
virtual void accept(parser_visitor *visitor) override;
|
||||
|
||||
designator_expression& base();
|
||||
|
||||
dereference_expression *is_dereference() override;
|
||||
|
||||
~dereference_expression() override;
|
||||
};
|
||||
|
||||
class assign_statement : public statement
|
||||
{
|
||||
designator_expression *m_lvalue;
|
||||
expression *m_rvalue;
|
||||
|
||||
public:
|
||||
/**
|
||||
* \param position Source code position.
|
||||
* \param lvalue Left-hand side.
|
||||
* \param rvalue Assigned expression.
|
||||
*/
|
||||
assign_statement(const struct position position, designator_expression *lvalue,
|
||||
expression *rvalue);
|
||||
virtual void accept(parser_visitor *visitor) override;
|
||||
|
||||
designator_expression& lvalue();
|
||||
expression& rvalue();
|
||||
|
||||
virtual ~assign_statement() override;
|
||||
};
|
||||
|
||||
/**
|
||||
* If-statement.
|
||||
*/
|
||||
class if_statement : public statement
|
||||
{
|
||||
expression *m_prerequisite;
|
||||
compound_statement *m_body;
|
||||
compound_statement *m_alternative;
|
||||
|
||||
public:
|
||||
/**
|
||||
* \param position Source code position.
|
||||
* \param prerequisite Condition.
|
||||
* \param body Statement executed if the condition is met.
|
||||
* \param alternative Statement executed if the condition is not met.
|
||||
*/
|
||||
if_statement(const struct position position, expression *prerequisite,
|
||||
compound_statement *body, compound_statement *alternative = nullptr);
|
||||
virtual void accept(parser_visitor *visitor) override;
|
||||
|
||||
expression& prerequisite();
|
||||
compound_statement& body();
|
||||
compound_statement *alternative();
|
||||
|
||||
virtual ~if_statement() override;
|
||||
};
|
||||
|
||||
/**
|
||||
* While-statement.
|
||||
*/
|
||||
class while_statement : public statement
|
||||
{
|
||||
expression *m_prerequisite;
|
||||
compound_statement *m_body;
|
||||
|
||||
public:
|
||||
/**
|
||||
* \param position Source code position.
|
||||
* \param prerequisite Condition.
|
||||
* \param body Statement executed while the condition is met.
|
||||
*/
|
||||
while_statement(const struct position position, expression *prerequisite,
|
||||
compound_statement *body);
|
||||
virtual void accept(parser_visitor *visitor) override;
|
||||
|
||||
expression& prerequisite();
|
||||
compound_statement& body();
|
||||
|
||||
virtual ~while_statement() override;
|
||||
};
|
||||
|
||||
class block : public node
|
||||
{
|
||||
public:
|
||||
std::vector<definition *> value_definitions;
|
||||
std::vector<statement *> body;
|
||||
|
||||
block(const struct position position, std::vector<definition *>&& value_definitions,
|
||||
std::vector<statement *>&& body);
|
||||
virtual void accept(parser_visitor *visitor) override;
|
||||
|
||||
virtual ~block() override;
|
||||
};
|
||||
|
||||
class program : public block
|
||||
{
|
||||
public:
|
||||
std::vector<definition *> type_definitions;
|
||||
|
||||
program(const struct position position, std::vector<definition *>&& type_definitions,
|
||||
std::vector<definition *>&& value_definitions, std::vector<statement *>&& body);
|
||||
virtual void accept(parser_visitor *visitor) override;
|
||||
|
||||
virtual ~program() override;
|
||||
};
|
||||
|
||||
template<typename T>
|
||||
class number_literal : public literal
|
||||
{
|
||||
T m_number;
|
||||
|
||||
public:
|
||||
number_literal(const struct position position, const T value)
|
||||
: literal(position), m_number(value)
|
||||
{
|
||||
}
|
||||
|
||||
virtual void accept(parser_visitor *visitor) override
|
||||
{
|
||||
visitor->visit(this);
|
||||
}
|
||||
|
||||
T number() const
|
||||
{
|
||||
return m_number;
|
||||
}
|
||||
};
|
||||
|
||||
class string_literal : public literal
|
||||
{
|
||||
std::string m_string;
|
||||
|
||||
public:
|
||||
string_literal(const struct position position, const std::string& value);
|
||||
virtual void accept(parser_visitor *visitor) override;
|
||||
|
||||
const std::string& string() const;
|
||||
};
|
||||
|
||||
class binary_expression : public expression
|
||||
{
|
||||
expression *m_lhs;
|
||||
expression *m_rhs;
|
||||
binary_operator m_operator;
|
||||
|
||||
public:
|
||||
binary_expression(const struct position position, expression *lhs,
|
||||
expression *rhs, const unsigned char operation);
|
||||
|
||||
virtual void accept(parser_visitor *visitor) override;
|
||||
expression& lhs();
|
||||
expression& rhs();
|
||||
binary_operator operation() const;
|
||||
|
||||
virtual ~binary_expression() override;
|
||||
};
|
||||
|
||||
class unary_expression : public expression
|
||||
{
|
||||
expression *m_operand;
|
||||
unary_operator m_operator;
|
||||
|
||||
public:
|
||||
unary_expression(const struct position position, expression *operand,
|
||||
const unsigned char operation);
|
||||
|
||||
virtual void accept(parser_visitor *visitor) override;
|
||||
expression& operand();
|
||||
unary_operator operation() const;
|
||||
|
||||
virtual ~unary_expression() override;
|
||||
};
|
||||
|
||||
const char *print_binary_operator(const binary_operator operation);
|
||||
}
|
||||
}
|
41
include/elna/source/driver.h
Normal file
41
include/elna/source/driver.h
Normal file
@ -0,0 +1,41 @@
|
||||
// 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 http://mozilla.org/MPL/2.0/.
|
||||
#pragma once
|
||||
|
||||
#include <list>
|
||||
#include "elna/source/ast.h"
|
||||
#include "location.hh"
|
||||
|
||||
namespace elna
|
||||
{
|
||||
namespace source
|
||||
{
|
||||
position make_position(const yy::location& location);
|
||||
|
||||
class syntax_error final : public error
|
||||
{
|
||||
std::string message;
|
||||
|
||||
public:
|
||||
syntax_error(const std::string& message,
|
||||
const char *input_file, const yy::location& location);
|
||||
|
||||
virtual std::string what() const override;
|
||||
};
|
||||
|
||||
class driver
|
||||
{
|
||||
std::list<std::unique_ptr<struct error>> m_errors;
|
||||
const char *input_file;
|
||||
|
||||
public:
|
||||
std::unique_ptr<program> tree;
|
||||
|
||||
driver(const char *input_file);
|
||||
|
||||
void error(const yy::location& loc, const std::string& message);
|
||||
const std::list<std::unique_ptr<struct error>>& errors() const noexcept;
|
||||
};
|
||||
}
|
||||
}
|
55
include/elna/source/result.h
Normal file
55
include/elna/source/result.h
Normal file
@ -0,0 +1,55 @@
|
||||
// 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 http://mozilla.org/MPL/2.0/.
|
||||
#pragma once
|
||||
|
||||
#include <cstddef>
|
||||
#include <string>
|
||||
|
||||
namespace elna
|
||||
{
|
||||
namespace source
|
||||
{
|
||||
/**
|
||||
* Position in the source text.
|
||||
*/
|
||||
struct position
|
||||
{
|
||||
/// Line.
|
||||
std::size_t line = 1;
|
||||
|
||||
/// Column.
|
||||
std::size_t column = 1;
|
||||
};
|
||||
|
||||
/**
|
||||
* A compilation error consists of an error message and position.
|
||||
*/
|
||||
class error
|
||||
{
|
||||
protected:
|
||||
/**
|
||||
* Constructs an error.
|
||||
*
|
||||
* \param path Source file name.
|
||||
* \param position Error position in the source text.
|
||||
*/
|
||||
error(const char *path, const struct position position);
|
||||
|
||||
public:
|
||||
const struct position position;
|
||||
const char *path;
|
||||
|
||||
virtual ~error() noexcept = default;
|
||||
|
||||
/// Error text.
|
||||
virtual std::string what() const = 0;
|
||||
|
||||
/// Error line in the source text.
|
||||
std::size_t line() const noexcept;
|
||||
|
||||
/// Error column in the source text.
|
||||
std::size_t column() const noexcept;
|
||||
};
|
||||
}
|
||||
}
|
106
include/elna/source/symbol.h
Normal file
106
include/elna/source/symbol.h
Normal file
@ -0,0 +1,106 @@
|
||||
// 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 http://mozilla.org/MPL/2.0/.
|
||||
#pragma once
|
||||
|
||||
#include <cstdint>
|
||||
#include <unordered_map>
|
||||
#include <string>
|
||||
#include <memory>
|
||||
|
||||
namespace elna
|
||||
{
|
||||
namespace source
|
||||
{
|
||||
/**
|
||||
* Generic language entity information.
|
||||
*/
|
||||
template<typename T>
|
||||
class info
|
||||
{
|
||||
public:
|
||||
T payload;
|
||||
|
||||
info(T payload)
|
||||
: payload(payload)
|
||||
{
|
||||
}
|
||||
};
|
||||
|
||||
template<typename T>
|
||||
std::shared_ptr<info<T>> make_info(T payload)
|
||||
{
|
||||
return std::make_shared<info<T>>(info(payload));
|
||||
}
|
||||
|
||||
/**
|
||||
* Symbol table.
|
||||
*/
|
||||
template<typename T>
|
||||
class symbol_table
|
||||
{
|
||||
public:
|
||||
using symbol_ptr = std::shared_ptr<info<T>>;
|
||||
|
||||
private:
|
||||
std::unordered_map<std::string, symbol_ptr> entries;
|
||||
std::shared_ptr<symbol_table> outer_scope;
|
||||
|
||||
public:
|
||||
/**
|
||||
* Constructs a new symbol with an optional outer scope.
|
||||
*
|
||||
* \param scope Outer scope.
|
||||
*/
|
||||
explicit symbol_table(std::shared_ptr<symbol_table> scope = nullptr)
|
||||
: outer_scope(scope)
|
||||
{
|
||||
}
|
||||
|
||||
/**
|
||||
* Looks for symbol in the table by name. Returns nullptr if the symbol
|
||||
* can not be found.
|
||||
*
|
||||
* \param name Symbol name.
|
||||
* \return Symbol from the table if found.
|
||||
*/
|
||||
symbol_ptr lookup(const std::string& name)
|
||||
{
|
||||
auto entry = entries.find(name);
|
||||
|
||||
if (entry != entries.cend())
|
||||
{
|
||||
return entry->second;
|
||||
}
|
||||
if (this->outer_scope != nullptr)
|
||||
{
|
||||
return this->outer_scope->lookup(name);
|
||||
}
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
/**
|
||||
* Registers new symbol.
|
||||
*
|
||||
* \param name Symbol name.
|
||||
* \param entry Symbol information.
|
||||
*
|
||||
* \return Whether the insertion took place.
|
||||
*/
|
||||
bool enter(const std::string& name, symbol_ptr entry)
|
||||
{
|
||||
return entries.insert({ name, entry }).second;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the outer scope or nullptr if the this is the global scope.
|
||||
*
|
||||
* \return Outer scope.
|
||||
*/
|
||||
std::shared_ptr<symbol_table> scope()
|
||||
{
|
||||
return this->outer_scope;
|
||||
}
|
||||
};
|
||||
}
|
||||
}
|
116
include/elna/source/types.h
Normal file
116
include/elna/source/types.h
Normal file
@ -0,0 +1,116 @@
|
||||
// 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 http://mozilla.org/MPL/2.0/.
|
||||
#pragma once
|
||||
|
||||
#include <memory>
|
||||
#include <string>
|
||||
#include <vector>
|
||||
|
||||
namespace elna
|
||||
{
|
||||
namespace source
|
||||
{
|
||||
class primitive_type;
|
||||
class pointer_type;
|
||||
class procedure_type;
|
||||
|
||||
/**
|
||||
* Type representation.
|
||||
*/
|
||||
class type
|
||||
{
|
||||
const std::size_t byte_size;
|
||||
|
||||
protected:
|
||||
/**
|
||||
* Constructor.
|
||||
*
|
||||
* \param byte_size The type size in bytes.
|
||||
*/
|
||||
explicit type(const std::size_t byte_size);
|
||||
|
||||
public:
|
||||
/**
|
||||
* \return The type size in bytes.
|
||||
*/
|
||||
virtual std::size_t size() const noexcept;
|
||||
|
||||
/**
|
||||
* \return Unique type representation.
|
||||
*/
|
||||
virtual std::string type_name() const = 0;
|
||||
|
||||
virtual const pointer_type *is_pointer_type() const;
|
||||
|
||||
friend bool operator==(const type& lhs, const type& rhs) noexcept;
|
||||
friend bool operator!=(const type& lhs, const type& rhs) noexcept;
|
||||
};
|
||||
|
||||
/**
|
||||
* Built-in type representation.
|
||||
*/
|
||||
class primitive_type final : public type
|
||||
{
|
||||
/// Type name.
|
||||
const std::string m_type_name;
|
||||
|
||||
public:
|
||||
/**
|
||||
* Constructor.
|
||||
*
|
||||
* \param type_name Type name.
|
||||
* \param byte_size The type size in bytes.
|
||||
*/
|
||||
primitive_type(const std::string& type_name, const std::size_t byte_size);
|
||||
|
||||
virtual std::string type_name() const override;
|
||||
};
|
||||
|
||||
/**
|
||||
* Typed pointer.
|
||||
*/
|
||||
struct pointer_type final : public type
|
||||
{
|
||||
/// Pointer target type.
|
||||
std::shared_ptr<const type> base_type;
|
||||
|
||||
/**
|
||||
* Constructor.
|
||||
*
|
||||
* \param base_type Pointer target type.
|
||||
* \param byte_size The type size in bytes.
|
||||
*/
|
||||
pointer_type(std::shared_ptr<const type> base_type, const std::size_t byte_size);
|
||||
|
||||
virtual std::string type_name() const override;
|
||||
|
||||
virtual const pointer_type *is_pointer_type() const override;
|
||||
};
|
||||
|
||||
/**
|
||||
* Type of a procedure.
|
||||
*/
|
||||
struct procedure_type final : public type
|
||||
{
|
||||
/// Argument types.
|
||||
std::vector<std::shared_ptr<const type>> arguments;
|
||||
|
||||
/**
|
||||
* Constructor.
|
||||
*
|
||||
* \param arguments Argument types.
|
||||
* \param byte_size Function pointer size.
|
||||
*/
|
||||
procedure_type(std::vector<std::shared_ptr<const type>> arguments, const std::size_t byte_size);
|
||||
|
||||
virtual std::string type_name() const override;
|
||||
};
|
||||
|
||||
bool operator==(const type& lhs, const type& rhs) noexcept;
|
||||
bool operator!=(const type& lhs, const type& rhs) noexcept;
|
||||
|
||||
extern const primitive_type boolean_type;
|
||||
extern const primitive_type int_type;
|
||||
}
|
||||
}
|
341
rakelib/cross.rake
Normal file
341
rakelib/cross.rake
Normal file
@ -0,0 +1,341 @@
|
||||
# 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 'pathname'
|
||||
require 'uri'
|
||||
require 'net/http'
|
||||
require 'rake/clean'
|
||||
require 'open3'
|
||||
require 'etc'
|
||||
require_relative 'shared'
|
||||
|
||||
GCC_VERSION = "14.2.0"
|
||||
BINUTILS_VERSION = '2.43.1'
|
||||
GLIBC_VERSION = '2.40'
|
||||
KERNEL_VERSION = '5.15.166'
|
||||
|
||||
CLOBBER.include TMP
|
||||
|
||||
class BuildTarget
|
||||
attr_accessor(:build, :gcc, :target, :tmp)
|
||||
|
||||
def gxx
|
||||
@gcc.gsub 'c', '+'
|
||||
end
|
||||
|
||||
def sysroot
|
||||
tmp + 'sysroot'
|
||||
end
|
||||
|
||||
def rootfs
|
||||
tmp + 'rootfs'
|
||||
end
|
||||
|
||||
def tools
|
||||
tmp + 'tools'
|
||||
end
|
||||
|
||||
def configuration
|
||||
case target
|
||||
when /^riscv[[:digit:]]+-/
|
||||
[
|
||||
'--with-arch=rv32imafdc',
|
||||
'--with-abi=ilp32d',
|
||||
'--with-tune=rocket',
|
||||
'--with-isa-spec=20191213'
|
||||
]
|
||||
else
|
||||
[]
|
||||
end
|
||||
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, task)
|
||||
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
|
||||
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
|
||||
task.with_defaults target: 'riscv32-unknown-linux-gnu'
|
||||
result.target = task[:target]
|
||||
result
|
||||
end
|
||||
|
||||
def download_and_unarchive(url, target)
|
||||
case File.extname url.path
|
||||
when '.bz2'
|
||||
archive_type = '-j'
|
||||
root_directory = File.basename url.path, '.tar.bz2'
|
||||
when '.xz'
|
||||
archive_type = '-J'
|
||||
root_directory = File.basename url.path, '.tar.xz'
|
||||
else
|
||||
raise "Unsupported archive type #{url.path}."
|
||||
end
|
||||
|
||||
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_unarchive URI.parse(response['location'])
|
||||
when Net::HTTPSuccess
|
||||
Open3.popen2 'tar', '-C', target.to_path, archive_type, '-xv' 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
|
||||
else
|
||||
response.error!
|
||||
end
|
||||
end
|
||||
end
|
||||
target + root_directory
|
||||
end
|
||||
|
||||
namespace :cross do
|
||||
desc 'Build cross binutils'
|
||||
task :binutils, [:target] do |_, args|
|
||||
options = find_build_target GCC_VERSION, args
|
||||
options.tools.mkpath
|
||||
source_directory = download_and_unarchive(
|
||||
URI.parse("https://ftp.gnu.org/gnu/binutils/binutils-#{BINUTILS_VERSION}.tar.xz"),
|
||||
options.tools)
|
||||
|
||||
cwd = source_directory.dirname + 'build-binutils'
|
||||
cwd.mkpath
|
||||
options.rootfs.mkpath
|
||||
|
||||
env = {
|
||||
'CC' => options.gcc,
|
||||
'CXX' => options.gxx
|
||||
}
|
||||
configure_options = [
|
||||
"--prefix=#{options.rootfs.realpath}",
|
||||
"--target=#{options.target}",
|
||||
'--disable-nls',
|
||||
'--enable-gprofng=no',
|
||||
'--disable-werror',
|
||||
'--enable-default-hash-style=gnu',
|
||||
'--disable-libquadmath'
|
||||
]
|
||||
configure = source_directory.relative_path_from(cwd) + 'configure'
|
||||
sh env, configure.to_path, *configure_options, chdir: cwd.to_path
|
||||
sh env, 'make', '-j', Etc.nprocessors.to_s, chdir: cwd.to_path
|
||||
sh env, 'make', 'install', chdir: cwd.to_path
|
||||
end
|
||||
|
||||
desc 'Build stage 1 GCC'
|
||||
task :gcc1, [:target] do |_, args|
|
||||
options = find_build_target GCC_VERSION, args
|
||||
options.tools.mkpath
|
||||
source_directory = download_and_unarchive(
|
||||
URI.parse("https://gcc.gnu.org/pub/gcc/releases/gcc-#{GCC_VERSION}/gcc-#{GCC_VERSION}.tar.xz"),
|
||||
options.tools)
|
||||
|
||||
cwd = source_directory.dirname + 'build-gcc'
|
||||
cwd.mkpath
|
||||
options.rootfs.mkpath
|
||||
options.sysroot.mkpath
|
||||
|
||||
sh 'contrib/download_prerequisites', chdir: source_directory.to_path
|
||||
configure_options = options.configuration + [
|
||||
"--prefix=#{options.rootfs.realpath}",
|
||||
"--with-sysroot=#{options.sysroot.realpath}",
|
||||
'--enable-languages=c,c++',
|
||||
'--disable-shared',
|
||||
'--disable-bootstrap',
|
||||
'--disable-multilib',
|
||||
'--disable-libmudflap',
|
||||
'--disable-libssp',
|
||||
'--disable-libquadmath',
|
||||
'--disable-libsanitizer',
|
||||
'--disable-threads',
|
||||
'--disable-libatomic',
|
||||
'--disable-libgomp',
|
||||
'--disable-libvtv',
|
||||
'--disable-libstdcxx',
|
||||
'--disable-nls',
|
||||
'--with-newlib',
|
||||
'--without-headers',
|
||||
"--target=#{options.target}",
|
||||
"--build=#{options.build}",
|
||||
"--host=#{options.build}"
|
||||
]
|
||||
flags = '-O2 -fPIC'
|
||||
env = {
|
||||
'CC' => options.gcc,
|
||||
'CXX' => options.gxx,
|
||||
'CFLAGS' => flags,
|
||||
'CXXFLAGS' => flags,
|
||||
'PATH' => "#{options.rootfs.realpath + 'bin'}:#{ENV['PATH']}"
|
||||
}
|
||||
configure = source_directory.relative_path_from(cwd) + 'configure'
|
||||
sh env, configure.to_path, *configure_options, chdir: cwd.to_path
|
||||
sh env, 'make', '-j', Etc.nprocessors.to_s, chdir: cwd.to_path
|
||||
sh env, 'make', 'install', chdir: cwd.to_path
|
||||
end
|
||||
|
||||
desc 'Copy glibc headers'
|
||||
task :headers, [:target] do |_, args|
|
||||
options = find_build_target GCC_VERSION, args
|
||||
options.tools.mkpath
|
||||
|
||||
source_directory = download_and_unarchive(
|
||||
URI.parse("https://ftp.gnu.org/gnu/glibc/glibc-#{GLIBC_VERSION}.tar.xz"),
|
||||
options.tools)
|
||||
include_directory = options.tools + 'include'
|
||||
|
||||
include_directory.mkpath
|
||||
cp (source_directory + 'elf/elf.h'), (include_directory + 'elf.h')
|
||||
end
|
||||
|
||||
desc 'Build linux kernel'
|
||||
task :kernel, [:target] do |_, args|
|
||||
options = find_build_target GCC_VERSION, args
|
||||
options.tools.mkpath
|
||||
|
||||
cwd = download_and_unarchive(
|
||||
URI.parse("https://cdn.kernel.org/pub/linux/kernel/v5.x/linux-#{KERNEL_VERSION}.tar.xz"),
|
||||
options.tools)
|
||||
|
||||
env = {
|
||||
'CROSS_COMPILE' => "#{options.target}-",
|
||||
'ARCH' => 'riscv',
|
||||
'PATH' => "#{options.rootfs.realpath + 'bin'}:#{ENV['PATH']}",
|
||||
'HOSTCFLAGS' => "-D_UUID_T -D__GETHOSTUUID_H -I#{options.tools.realpath + 'include'}"
|
||||
}
|
||||
sh env, 'make', 'rv32_defconfig', chdir: cwd.to_path
|
||||
sh env, 'make', '-j', Etc.nprocessors.to_s, chdir: cwd.to_path
|
||||
sh env, 'make', 'headers', chdir: cwd.to_path
|
||||
|
||||
user_directory = options.sysroot + 'usr'
|
||||
|
||||
user_directory.mkpath
|
||||
cp_r (cwd + 'usr/include'), (user_directory + 'include')
|
||||
end
|
||||
|
||||
desc 'Build glibc'
|
||||
task :glibc, [:target] do |_, args|
|
||||
options = find_build_target GCC_VERSION, args
|
||||
source_directory = options.tools + "glibc-#{GLIBC_VERSION}"
|
||||
configure_options = [
|
||||
'--prefix=/usr',
|
||||
"--host=#{options.target}",
|
||||
"--target=#{options.target}",
|
||||
"--build=#{options.build}",
|
||||
"--enable-kernel=#{KERNEL_VERSION}",
|
||||
"--with-headers=#{options.sysroot.realpath + 'usr/include'}",
|
||||
'--disable-nscd',
|
||||
'--disable-libquadmath',
|
||||
'--disable-libitm',
|
||||
'--disable-werror',
|
||||
'libc_cv_forced_unwind=yes'
|
||||
]
|
||||
bin = options.rootfs.realpath + 'bin'
|
||||
env = {
|
||||
'PATH' => "#{bin}:#{ENV['PATH']}",
|
||||
'MAKE' => 'make' # Otherwise it uses gnumake which can be different and too old.
|
||||
}
|
||||
cwd = source_directory.dirname + 'build-glibc'
|
||||
cwd.mkpath
|
||||
|
||||
configure = source_directory.relative_path_from(cwd) +'./configure'
|
||||
sh env, configure.to_path, *configure_options, chdir: cwd.to_path
|
||||
sh env, 'make', '-j', Etc.nprocessors.to_s, chdir: cwd.to_path
|
||||
sh env, 'make', "install_root=#{options.sysroot.realpath}", 'install', chdir: cwd.to_path
|
||||
end
|
||||
|
||||
desc 'Build stage 2 GCC'
|
||||
task :gcc2, [:target] do |_, args|
|
||||
options = find_build_target GCC_VERSION, args
|
||||
source_directory = options.tools + "gcc-#{GCC_VERSION}"
|
||||
cwd = options.tools + 'build-gcc'
|
||||
|
||||
rm_rf cwd
|
||||
cwd.mkpath
|
||||
|
||||
configure_options = options.configuration + [
|
||||
"--prefix=#{options.rootfs.realpath}",
|
||||
"--with-sysroot=#{options.sysroot.realpath}",
|
||||
'--enable-languages=c,c++,lto',
|
||||
'--enable-lto',
|
||||
'--enable-shared',
|
||||
'--disable-bootstrap',
|
||||
'--disable-multilib',
|
||||
'--enable-checking=release',
|
||||
'--disable-libssp',
|
||||
'--disable-libquadmath',
|
||||
'--enable-threads=posix',
|
||||
'--with-default-libstdcxx-abi=new',
|
||||
'--disable-nls',
|
||||
"--target=#{options.target}",
|
||||
"--build=#{options.build}",
|
||||
"--host=#{options.build}"
|
||||
|
||||
]
|
||||
flags = '-O2 -fPIC'
|
||||
env = {
|
||||
'CFLAGS' => flags,
|
||||
'CXXFLAGS' => flags,
|
||||
'PATH' => "#{options.rootfs.realpath + 'bin'}:#{ENV['PATH']}"
|
||||
}
|
||||
configure = source_directory.relative_path_from(cwd) + 'configure'
|
||||
sh env, configure.to_path, *configure_options, chdir: cwd.to_path
|
||||
sh env, 'make', '-j', Etc.nprocessors.to_s, chdir: cwd.to_path
|
||||
sh env, 'make', 'install', chdir: cwd.to_path
|
||||
end
|
||||
|
||||
task :init, [:target] do |_, args|
|
||||
options = find_build_target GCC_VERSION, args
|
||||
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
|
||||
|
||||
desc 'Build cross toolchain'
|
||||
task :cross, [:target] => [
|
||||
'cross:binutils',
|
||||
'cross:gcc1',
|
||||
'cross:headers',
|
||||
'cross:kernel',
|
||||
'cross:glibc',
|
||||
'cross:gcc2',
|
||||
'cross:init'
|
||||
] do
|
||||
end
|
5
rakelib/shared.rb
Normal file
5
rakelib/shared.rb
Normal file
@ -0,0 +1,5 @@
|
||||
# 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/. -}
|
||||
|
||||
TMP = Pathname.new('./build')
|
100
rakelib/tester.rake
Normal file
100
rakelib/tester.rake
Normal file
@ -0,0 +1,100 @@
|
||||
# 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 'open3'
|
||||
require 'rake/clean'
|
||||
require_relative 'shared'
|
||||
|
||||
CLEAN.include(TMP + 'riscv')
|
||||
|
||||
LINKER = 'build/rootfs/riscv32-unknown-linux-gnu/bin/ld'
|
||||
AS = 'build/rootfs/riscv32-unknown-linux-gnu/bin/as'
|
||||
|
||||
namespace :test do
|
||||
test_sources = FileList['tests/vm/*.elna', 'tests/vm/*.s']
|
||||
compiler = `cabal list-bin elna`.strip
|
||||
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
|
1023
source/ast.cc
Normal file
1023
source/ast.cc
Normal file
File diff suppressed because it is too large
Load Diff
44
source/driver.cc
Normal file
44
source/driver.cc
Normal file
@ -0,0 +1,44 @@
|
||||
// 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 http://mozilla.org/MPL/2.0/.
|
||||
#include "elna/source/driver.h"
|
||||
|
||||
namespace elna
|
||||
{
|
||||
namespace source
|
||||
{
|
||||
position make_position(const yy::location& location)
|
||||
{
|
||||
return position{
|
||||
static_cast<std::size_t>(location.begin.line),
|
||||
static_cast<std::size_t>(location.begin.column)
|
||||
};
|
||||
}
|
||||
|
||||
syntax_error::syntax_error(const std::string& message,
|
||||
const char *input_file, const yy::location& location)
|
||||
: error(input_file, make_position(location)), message(message)
|
||||
{
|
||||
}
|
||||
|
||||
std::string syntax_error::what() const
|
||||
{
|
||||
return message;
|
||||
}
|
||||
|
||||
driver::driver(const char *input_file)
|
||||
: input_file(input_file)
|
||||
{
|
||||
}
|
||||
|
||||
void driver::error(const yy::location& loc, const std::string& message)
|
||||
{
|
||||
m_errors.emplace_back(std::make_unique<elna::source::syntax_error>(message, input_file, loc));
|
||||
}
|
||||
|
||||
const std::list<std::unique_ptr<struct error>>& driver::errors() const noexcept
|
||||
{
|
||||
return m_errors;
|
||||
}
|
||||
}
|
||||
}
|
@ -1,154 +0,0 @@
|
||||
/**
|
||||
* Argument parsing.
|
||||
*/
|
||||
module elna.arguments;
|
||||
|
||||
import std.algorithm;
|
||||
import std.range;
|
||||
import std.sumtype;
|
||||
|
||||
struct ArgumentError
|
||||
{
|
||||
enum Type
|
||||
{
|
||||
expectedOutputFile,
|
||||
noInput,
|
||||
superfluousArguments,
|
||||
}
|
||||
|
||||
private Type type_;
|
||||
private string argument_;
|
||||
|
||||
@property Type type() const @nogc nothrow pure @safe
|
||||
{
|
||||
return this.type_;
|
||||
}
|
||||
|
||||
@property string argument() const @nogc nothrow pure @safe
|
||||
{
|
||||
return this.argument_;
|
||||
}
|
||||
|
||||
void toString(OR)(OR range)
|
||||
if (isOutputRage!OR)
|
||||
{
|
||||
final switch (Type)
|
||||
{
|
||||
case Type.expectedOutputFile:
|
||||
put(range, "Expected an output filename after -o");
|
||||
break;
|
||||
case Type.noInput:
|
||||
put(range, "No input files specified");
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Supported compiler arguments.
|
||||
*/
|
||||
struct Arguments
|
||||
{
|
||||
private bool assembler_;
|
||||
private string output_;
|
||||
private string inFile_;
|
||||
|
||||
@property string inFile() @nogc nothrow pure @safe
|
||||
{
|
||||
return this.inFile_;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns: Whether to generate assembly instead of an object file.
|
||||
*/
|
||||
@property bool assembler() const @nogc nothrow pure @safe
|
||||
{
|
||||
return this.assembler_;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns: Output file.
|
||||
*/
|
||||
@property string output() const @nogc nothrow pure @safe
|
||||
{
|
||||
return this.output_;
|
||||
}
|
||||
|
||||
/**
|
||||
* Parse command line arguments.
|
||||
*
|
||||
* The first argument is expected to be the program name (and it is
|
||||
* ignored).
|
||||
*
|
||||
* Params:
|
||||
* arguments = Command line arguments.
|
||||
*
|
||||
* Returns: Parsed arguments or an error.
|
||||
*/
|
||||
static SumType!(ArgumentError, Arguments) parse(string[] arguments)
|
||||
@nogc nothrow pure @safe
|
||||
{
|
||||
if (!arguments.empty)
|
||||
{
|
||||
arguments.popFront;
|
||||
}
|
||||
alias ReturnType = typeof(return);
|
||||
|
||||
return parseArguments(arguments).match!(
|
||||
(Arguments parsed) {
|
||||
if (parsed.inFile is null)
|
||||
{
|
||||
return ReturnType(ArgumentError(ArgumentError.Type.noInput));
|
||||
}
|
||||
else if (!arguments.empty)
|
||||
{
|
||||
return ReturnType(ArgumentError(
|
||||
ArgumentError.Type.superfluousArguments,
|
||||
arguments.front
|
||||
));
|
||||
}
|
||||
return ReturnType(parsed);
|
||||
},
|
||||
(ArgumentError argumentError) => ReturnType(argumentError)
|
||||
);
|
||||
}
|
||||
|
||||
private static SumType!(ArgumentError, Arguments) parseArguments(ref string[] arguments)
|
||||
@nogc nothrow pure @safe
|
||||
{
|
||||
Arguments parsed;
|
||||
|
||||
while (!arguments.empty)
|
||||
{
|
||||
if (arguments.front == "-s")
|
||||
{
|
||||
parsed.assembler_ = true;
|
||||
}
|
||||
else if (arguments.front == "-o")
|
||||
{
|
||||
if (arguments.empty)
|
||||
{
|
||||
return typeof(return)(ArgumentError(
|
||||
ArgumentError.Type.expectedOutputFile,
|
||||
arguments.front
|
||||
));
|
||||
}
|
||||
arguments.popFront;
|
||||
parsed.output_ = arguments.front;
|
||||
}
|
||||
else if (arguments.front == "--")
|
||||
{
|
||||
arguments.popFront;
|
||||
parsed.inFile_ = arguments.front;
|
||||
arguments.popFront;
|
||||
break;
|
||||
}
|
||||
else if (!arguments.front.startsWith("-"))
|
||||
{
|
||||
parsed.inFile_ = arguments.front;
|
||||
}
|
||||
arguments.popFront;
|
||||
}
|
||||
return typeof(return)(parsed);
|
||||
}
|
||||
}
|
@ -1,100 +0,0 @@
|
||||
module elna.backend;
|
||||
|
||||
import core.stdc.stdio;
|
||||
import elna.elf;
|
||||
import elna.ir;
|
||||
import elna.extended;
|
||||
import elna.riscv;
|
||||
import elna.lexer;
|
||||
import elna.parser;
|
||||
import elna.result;
|
||||
import std.algorithm;
|
||||
import std.sumtype;
|
||||
import std.typecons;
|
||||
import tanya.os.error;
|
||||
import tanya.container.array;
|
||||
import tanya.container.string;
|
||||
import tanya.memory.allocator;
|
||||
|
||||
private Nullable!String readSource(string source) @nogc
|
||||
{
|
||||
enum size_t bufferSize = 255;
|
||||
auto sourceFilename = String(source);
|
||||
|
||||
return readFile(sourceFilename).match!(
|
||||
(ErrorCode errorCode) {
|
||||
perror(sourceFilename.toStringz);
|
||||
return Nullable!String();
|
||||
},
|
||||
(Array!ubyte contents) => nullable(String(cast(char[]) contents.get))
|
||||
);
|
||||
}
|
||||
|
||||
int generate(string inFile, ref String outputFilename) @nogc
|
||||
{
|
||||
auto sourceText = readSource(inFile);
|
||||
if (sourceText.isNull)
|
||||
{
|
||||
return 3;
|
||||
}
|
||||
auto tokens = lex(sourceText.get.get);
|
||||
if (!tokens.valid)
|
||||
{
|
||||
auto compileError = tokens.error.get;
|
||||
printf("%lu:%lu: %s\n", compileError.line, compileError.column, compileError.message.ptr);
|
||||
return 1;
|
||||
}
|
||||
auto ast = parse(tokens.result);
|
||||
if (!ast.valid)
|
||||
{
|
||||
auto compileError = ast.error.get;
|
||||
printf("%lu:%lu: %s\n", compileError.line, compileError.column, compileError.message.ptr);
|
||||
return 2;
|
||||
}
|
||||
auto transformVisitor = defaultAllocator.make!TransformVisitor();
|
||||
auto ir = transformVisitor.visit(ast.result);
|
||||
defaultAllocator.dispose(transformVisitor);
|
||||
|
||||
auto handle = File.open(outputFilename.toStringz, BitFlags!(File.Mode)(File.Mode.truncate));
|
||||
if (!handle.valid)
|
||||
{
|
||||
return 1;
|
||||
}
|
||||
auto program = writeNext(ir);
|
||||
auto elf = Elf!ELFCLASS32(move(handle));
|
||||
auto readOnlyData = Array!ubyte(cast(const(ubyte)[]) "%d\n".ptr[0 .. 4]); // With \0.
|
||||
|
||||
elf.addReadOnlyData(String(".CL0"), readOnlyData);
|
||||
elf.addCode(program.name, program.text);
|
||||
|
||||
elf.addExternSymbol(String("printf"));
|
||||
foreach (ref reference; program.symbols)
|
||||
{
|
||||
elf.Rela relocationEntry = {
|
||||
r_offset: cast(elf.Addr) reference.offset
|
||||
};
|
||||
elf.Rela relocationSub = {
|
||||
r_offset: cast(elf.Addr) reference.offset,
|
||||
r_info: R_RISCV_RELAX
|
||||
};
|
||||
|
||||
final switch (reference.target)
|
||||
{
|
||||
case Reference.Target.text:
|
||||
relocationEntry.r_info = R_RISCV_CALL;
|
||||
break;
|
||||
case Reference.Target.high20:
|
||||
relocationEntry.r_info = R_RISCV_HI20;
|
||||
break;
|
||||
case Reference.Target.lower12i:
|
||||
relocationEntry.r_info = R_RISCV_LO12_I;
|
||||
break;
|
||||
}
|
||||
|
||||
elf.relocate(reference.name, relocationEntry, relocationSub);
|
||||
}
|
||||
|
||||
elf.finish();
|
||||
|
||||
return 0;
|
||||
}
|
1060
source/elna/elf.d
1060
source/elna/elf.d
File diff suppressed because it is too large
Load Diff
@ -1,335 +0,0 @@
|
||||
/**
|
||||
* File I/O that can be moved into more generic library when and if finished.
|
||||
*/
|
||||
module elna.extended;
|
||||
|
||||
import core.stdc.errno;
|
||||
import core.stdc.stdio;
|
||||
import std.sumtype;
|
||||
import std.typecons;
|
||||
import tanya.os.error;
|
||||
import tanya.container.array;
|
||||
import tanya.container.string;
|
||||
|
||||
/**
|
||||
* File handle abstraction.
|
||||
*/
|
||||
struct File
|
||||
{
|
||||
/// Plattform dependent file type.
|
||||
alias Handle = FILE*;
|
||||
|
||||
/// Uninitialized file handle value.
|
||||
enum Handle invalid = null;
|
||||
|
||||
/**
|
||||
* Relative position.
|
||||
*/
|
||||
enum Whence
|
||||
{
|
||||
/// Relative to the start of the file.
|
||||
set = SEEK_SET,
|
||||
/// Relative to the current cursor position.
|
||||
currentt = SEEK_CUR,
|
||||
/// Relative from the end of the file.
|
||||
end = SEEK_END,
|
||||
}
|
||||
|
||||
/**
|
||||
* File open modes.
|
||||
*/
|
||||
enum Mode
|
||||
{
|
||||
/// Open the file for reading.
|
||||
read = 1 << 0,
|
||||
/// Open the file for writing. The stream is positioned at the beginning
|
||||
/// of the file.
|
||||
write = 1 << 1,
|
||||
/// Open the file for writing and remove its contents.
|
||||
truncate = 1 << 2,
|
||||
/// Open the file for writing. The stream is positioned at the end of
|
||||
/// the file.
|
||||
append = 1 << 3,
|
||||
}
|
||||
|
||||
private enum Status
|
||||
{
|
||||
invalid,
|
||||
owned,
|
||||
borrowed,
|
||||
}
|
||||
|
||||
private union Storage
|
||||
{
|
||||
Handle handle;
|
||||
ErrorCode errorCode;
|
||||
}
|
||||
private Storage storage;
|
||||
private Status status = Status.invalid;
|
||||
|
||||
@disable this(scope return ref File f);
|
||||
@disable this();
|
||||
|
||||
/**
|
||||
* Closes the file.
|
||||
*/
|
||||
~this() @nogc nothrow
|
||||
{
|
||||
if (this.status == Status.owned)
|
||||
{
|
||||
fclose(this.storage.handle);
|
||||
}
|
||||
this.storage.handle = invalid;
|
||||
this.status = Status.invalid;
|
||||
}
|
||||
|
||||
/**
|
||||
* Construct the object with the given system handle. The won't be claused
|
||||
* in the descructor if this constructor is used.
|
||||
*
|
||||
* Params:
|
||||
* handle = File handle to be wrapped by this structure.
|
||||
*/
|
||||
this(Handle handle) @nogc nothrow pure @safe
|
||||
{
|
||||
this.storage.handle = handle;
|
||||
this.status = Status.borrowed;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns: Plattform dependent file handle.
|
||||
*/
|
||||
@property Handle handle() @nogc nothrow pure @trusted
|
||||
{
|
||||
return valid ? this.storage.handle : invalid;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns: An error code if an error has occurred.
|
||||
*/
|
||||
@property ErrorCode errorCode() @nogc nothrow pure @safe
|
||||
{
|
||||
return valid ? ErrorCode() : this.storage.errorCode;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns: Whether a valid, opened file is represented.
|
||||
*/
|
||||
@property bool valid() @nogc nothrow pure @safe
|
||||
{
|
||||
return this.status != Status.invalid;
|
||||
}
|
||||
|
||||
/**
|
||||
* Transfers the file into invalid state.
|
||||
*
|
||||
* Returns: The old file handle.
|
||||
*/
|
||||
Handle reset() @nogc nothrow pure @safe
|
||||
{
|
||||
if (!valid)
|
||||
{
|
||||
return invalid;
|
||||
}
|
||||
auto oldHandle = handle;
|
||||
|
||||
this.status = Status.invalid;
|
||||
this.storage.errorCode = ErrorCode();
|
||||
|
||||
return oldHandle;
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets stream position in the file.
|
||||
*
|
||||
* Params:
|
||||
* offset = File offset.
|
||||
* whence = File position to add the offset to.
|
||||
*
|
||||
* Returns: Error code if any.
|
||||
*/
|
||||
ErrorCode seek(size_t offset, Whence whence) @nogc nothrow
|
||||
{
|
||||
if (!valid)
|
||||
{
|
||||
return ErrorCode(ErrorCode.ErrorNo.badDescriptor);
|
||||
}
|
||||
if (fseek(this.storage.handle, offset, whence))
|
||||
{
|
||||
return ErrorCode(cast(ErrorCode.ErrorNo) errno);
|
||||
}
|
||||
return ErrorCode();
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns: Current offset or an error.
|
||||
*/
|
||||
SumType!(ErrorCode, size_t) tell() @nogc nothrow
|
||||
{
|
||||
if (!valid)
|
||||
{
|
||||
return typeof(return)(ErrorCode(ErrorCode.ErrorNo.badDescriptor));
|
||||
}
|
||||
auto result = ftell(this.storage.handle);
|
||||
|
||||
if (result < 0)
|
||||
{
|
||||
return typeof(return)(ErrorCode(cast(ErrorCode.ErrorNo) errno));
|
||||
}
|
||||
return typeof(return)(cast(size_t) result);
|
||||
}
|
||||
|
||||
/**
|
||||
* Params:
|
||||
* buffer = Destination buffer.
|
||||
*
|
||||
* Returns: Bytes read. $(D_PSYMBOL ErrorCode.ErrorNo.success) means that
|
||||
* while reading the file an unknown error has occurred.
|
||||
*/
|
||||
SumType!(ErrorCode, size_t) read(ubyte[] buffer) @nogc nothrow
|
||||
{
|
||||
if (!valid)
|
||||
{
|
||||
return typeof(return)(ErrorCode(ErrorCode.ErrorNo.badDescriptor));
|
||||
}
|
||||
const bytesRead = fread(buffer.ptr, 1, buffer.length, this.storage.handle);
|
||||
if (bytesRead == buffer.length || eof())
|
||||
{
|
||||
return typeof(return)(bytesRead);
|
||||
}
|
||||
return typeof(return)(ErrorCode());
|
||||
}
|
||||
|
||||
/**
|
||||
* Params:
|
||||
* buffer = Source buffer.
|
||||
*
|
||||
* Returns: Bytes written. $(D_PSYMBOL ErrorCode.ErrorNo.success) means that
|
||||
* while reading the file an unknown error has occurred.
|
||||
*/
|
||||
SumType!(ErrorCode, size_t) write(const(ubyte)[] buffer) @nogc nothrow
|
||||
{
|
||||
if (!valid)
|
||||
{
|
||||
return typeof(return)(ErrorCode(ErrorCode.ErrorNo.badDescriptor));
|
||||
}
|
||||
const bytesWritten = fwrite(buffer.ptr, 1, buffer.length, this.storage.handle);
|
||||
if (bytesWritten == buffer.length)
|
||||
{
|
||||
return typeof(return)(buffer.length);
|
||||
}
|
||||
return typeof(return)(ErrorCode());
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns: EOF status of the file.
|
||||
*/
|
||||
bool eof() @nogc nothrow
|
||||
{
|
||||
return valid && feof(this.storage.handle) != 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* Constructs a file object that will be closed in the destructor.
|
||||
*
|
||||
* Params:
|
||||
* filename = The file to open.
|
||||
*
|
||||
* Returns: Opened file or an error.
|
||||
*/
|
||||
static File open(const(char)* filename, BitFlags!Mode mode) @nogc nothrow
|
||||
{
|
||||
char[3] modeBuffer = "\0\0\0";
|
||||
|
||||
if (mode.truncate)
|
||||
{
|
||||
modeBuffer[0] = 'w';
|
||||
if (mode.read)
|
||||
{
|
||||
modeBuffer[1] = '+';
|
||||
}
|
||||
}
|
||||
else if (mode.append)
|
||||
{
|
||||
modeBuffer[0] = 'a';
|
||||
if (mode.read)
|
||||
{
|
||||
modeBuffer[1] = '+';
|
||||
}
|
||||
}
|
||||
else if (mode.read)
|
||||
{
|
||||
modeBuffer[0] = 'r';
|
||||
if (mode.write)
|
||||
{
|
||||
modeBuffer[1] = '+';
|
||||
}
|
||||
}
|
||||
|
||||
auto newHandle = fopen(filename, modeBuffer.ptr);
|
||||
auto newFile = File(newHandle);
|
||||
|
||||
if (newHandle is null)
|
||||
{
|
||||
newFile.status = Status.invalid;
|
||||
newFile.storage.errorCode = ErrorCode(cast(ErrorCode.ErrorNo) errno);
|
||||
}
|
||||
else
|
||||
{
|
||||
if (mode == BitFlags!Mode(Mode.write))
|
||||
{
|
||||
rewind(newHandle);
|
||||
}
|
||||
newFile.status = Status.owned;
|
||||
}
|
||||
|
||||
return newFile;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Reads the whole file and returns its contents.
|
||||
*
|
||||
* Params:
|
||||
* sourceFilename = Source filename.
|
||||
*
|
||||
* Returns: File contents or an error.
|
||||
*
|
||||
* See_Also: $(D_PSYMBOL File.read)
|
||||
*/
|
||||
SumType!(ErrorCode, Array!ubyte) readFile(String sourceFilename) @nogc
|
||||
{
|
||||
enum size_t bufferSize = 255;
|
||||
auto sourceFile = File.open(sourceFilename.toStringz, BitFlags!(File.Mode)(File.Mode.read));
|
||||
|
||||
if (!sourceFile.valid)
|
||||
{
|
||||
return typeof(return)(sourceFile.errorCode);
|
||||
}
|
||||
Array!ubyte sourceText;
|
||||
size_t totalRead;
|
||||
size_t bytesRead;
|
||||
do
|
||||
{
|
||||
sourceText.length = sourceText.length + bufferSize;
|
||||
const readStatus = sourceFile
|
||||
.read(sourceText[totalRead .. $].get)
|
||||
.match!(
|
||||
(ErrorCode errorCode) => nullable(errorCode),
|
||||
(size_t bytesRead_) {
|
||||
bytesRead = bytesRead_;
|
||||
return Nullable!ErrorCode();
|
||||
}
|
||||
);
|
||||
if (!readStatus.isNull)
|
||||
{
|
||||
return typeof(return)(readStatus.get);
|
||||
}
|
||||
totalRead += bytesRead;
|
||||
}
|
||||
while (bytesRead == bufferSize);
|
||||
|
||||
sourceText.length = totalRead;
|
||||
|
||||
return typeof(return)(sourceText);
|
||||
}
|
272
source/elna/ir.d
272
source/elna/ir.d
@ -1,272 +0,0 @@
|
||||
module elna.ir;
|
||||
|
||||
import parser = elna.parser;
|
||||
import tanya.container.array;
|
||||
import tanya.container.hashtable;
|
||||
import tanya.container.string;
|
||||
import tanya.memory.allocator;
|
||||
public import elna.parser : BinaryOperator;
|
||||
|
||||
/**
|
||||
* Mapping between the parser and IR AST.
|
||||
*/
|
||||
struct ASTMapping
|
||||
{
|
||||
alias Node = .Node;
|
||||
alias Definition = .Definition;
|
||||
alias VariableDeclaration = .VariableDeclaration;
|
||||
alias Statement = Array!(.Statement);
|
||||
alias BangStatement = .Expression;
|
||||
alias Block = .Definition;
|
||||
alias Expression = .Expression;
|
||||
alias Number = .Number;
|
||||
alias Variable = .Variable;
|
||||
alias BinaryExpression = .BinaryExpression;
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* IR visitor.
|
||||
*/
|
||||
interface IRVisitor
|
||||
{
|
||||
abstract void visit(Node) @nogc;
|
||||
abstract void visit(Definition) @nogc;
|
||||
abstract void visit(Expression) @nogc;
|
||||
abstract void visit(Statement) @nogc;
|
||||
abstract void visit(Variable) @nogc;
|
||||
abstract void visit(VariableDeclaration) @nogc;
|
||||
abstract void visit(Number) @nogc;
|
||||
abstract void visit(BinaryExpression) @nogc;
|
||||
}
|
||||
|
||||
/**
|
||||
* AST node.
|
||||
*/
|
||||
abstract class Node
|
||||
{
|
||||
abstract void accept(IRVisitor) @nogc;
|
||||
}
|
||||
|
||||
/**
|
||||
* Definition.
|
||||
*/
|
||||
class Definition : Node
|
||||
{
|
||||
char[] identifier;
|
||||
Array!Statement statements;
|
||||
Array!VariableDeclaration variableDeclarations;
|
||||
|
||||
override void accept(IRVisitor visitor) @nogc
|
||||
{
|
||||
visitor.visit(this);
|
||||
}
|
||||
}
|
||||
|
||||
class Statement : Node
|
||||
{
|
||||
BinaryExpression expression;
|
||||
|
||||
override void accept(IRVisitor visitor) @nogc
|
||||
{
|
||||
visitor.visit(this);
|
||||
}
|
||||
}
|
||||
|
||||
abstract class Expression : Node
|
||||
{
|
||||
override void accept(IRVisitor visitor) @nogc
|
||||
{
|
||||
visitor.visit(this);
|
||||
}
|
||||
}
|
||||
|
||||
class Number : Expression
|
||||
{
|
||||
int value;
|
||||
|
||||
override void accept(IRVisitor visitor) @nogc
|
||||
{
|
||||
visitor.visit(this);
|
||||
}
|
||||
}
|
||||
|
||||
class Variable : Expression
|
||||
{
|
||||
size_t counter;
|
||||
|
||||
override void accept(IRVisitor visitor) @nogc
|
||||
{
|
||||
visitor.visit(this);
|
||||
}
|
||||
}
|
||||
|
||||
class VariableDeclaration : Node
|
||||
{
|
||||
String identifier;
|
||||
|
||||
override void accept(IRVisitor visitor) @nogc
|
||||
{
|
||||
visitor.visit(this);
|
||||
}
|
||||
}
|
||||
|
||||
class BinaryExpression : Node
|
||||
{
|
||||
Expression lhs, rhs;
|
||||
BinaryOperator operator;
|
||||
|
||||
this(Expression lhs, Expression rhs, BinaryOperator operator)
|
||||
@nogc
|
||||
{
|
||||
this.lhs = lhs;
|
||||
this.rhs = rhs;
|
||||
this.operator = operator;
|
||||
}
|
||||
|
||||
override void accept(IRVisitor visitor) @nogc
|
||||
{
|
||||
visitor.visit(this);
|
||||
}
|
||||
}
|
||||
|
||||
final class TransformVisitor : parser.ParserVisitor!ASTMapping
|
||||
{
|
||||
private HashTable!(String, int) constants;
|
||||
|
||||
ASTMapping.Node visit(parser.Node node) @nogc
|
||||
{
|
||||
assert(false, "Not implemented");
|
||||
}
|
||||
|
||||
ASTMapping.Definition visit(parser.Definition definition) @nogc
|
||||
{
|
||||
assert(false, "Not implemented");
|
||||
}
|
||||
|
||||
ASTMapping.VariableDeclaration visit(parser.VariableDeclaration declaration) @nogc
|
||||
{
|
||||
assert(false, "Not implemented");
|
||||
}
|
||||
|
||||
ASTMapping.BangStatement visit(parser.BangStatement statement) @nogc
|
||||
{
|
||||
assert(false, "Not implemented");
|
||||
}
|
||||
|
||||
ASTMapping.Block visit(parser.Block block) @nogc
|
||||
{
|
||||
auto target = defaultAllocator.make!Definition;
|
||||
this.constants = transformConstants(block.definitions);
|
||||
|
||||
target.statements = block.statement.accept(this);
|
||||
target.variableDeclarations = transformVariableDeclarations(block.variableDeclarations);
|
||||
|
||||
return target;
|
||||
}
|
||||
|
||||
ASTMapping.Expression visit(parser.Expression expression) @nogc
|
||||
{
|
||||
assert(false, "Not implemented");
|
||||
}
|
||||
|
||||
ASTMapping.Number visit(parser.Number number) @nogc
|
||||
{
|
||||
assert(false, "Not implemented");
|
||||
}
|
||||
|
||||
ASTMapping.Variable visit(parser.Variable variable) @nogc
|
||||
{
|
||||
assert(false, "Not implemented");
|
||||
}
|
||||
|
||||
ASTMapping.BinaryExpression visit(parser.BinaryExpression) @nogc
|
||||
{
|
||||
assert(false, "Not implemented");
|
||||
}
|
||||
|
||||
private Number transformNumber(parser.Number number) @nogc
|
||||
{
|
||||
return defaultAllocator.make!Number(number.value);
|
||||
}
|
||||
|
||||
private Variable binaryExpression(parser.BinaryExpression binaryExpression,
|
||||
ref Array!Statement statements) @nogc
|
||||
{
|
||||
auto target = defaultAllocator.make!BinaryExpression(
|
||||
expression(binaryExpression.lhs, statements),
|
||||
expression(binaryExpression.rhs, statements),
|
||||
binaryExpression.operator
|
||||
);
|
||||
|
||||
auto newStatement = defaultAllocator.make!Statement;
|
||||
newStatement.expression = target;
|
||||
statements.insertBack(newStatement);
|
||||
|
||||
auto newVariable = defaultAllocator.make!Variable;
|
||||
newVariable.counter = statements.length;
|
||||
|
||||
return newVariable;
|
||||
}
|
||||
|
||||
private Expression expression(parser.Expression expression,
|
||||
ref Array!Statement statements) @nogc
|
||||
{
|
||||
if ((cast(parser.Number) expression) !is null)
|
||||
{
|
||||
auto numberExpression = defaultAllocator.make!Number;
|
||||
numberExpression.value = (cast(parser.Number) expression).value;
|
||||
|
||||
return numberExpression;
|
||||
}
|
||||
if ((cast(parser.Variable) expression) !is null)
|
||||
{
|
||||
auto numberExpression = defaultAllocator.make!Number;
|
||||
numberExpression.value = this.constants[(cast(parser.Variable) expression).identifier];
|
||||
|
||||
return numberExpression;
|
||||
}
|
||||
else if ((cast(parser.BinaryExpression) expression) !is null)
|
||||
{
|
||||
return binaryExpression(cast(parser.BinaryExpression) expression, statements);
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
override Array!Statement visit(parser.Statement statement) @nogc
|
||||
{
|
||||
typeof(return) statements;
|
||||
if ((cast(parser.BangStatement) statement) !is null)
|
||||
{
|
||||
expression((cast(parser.BangStatement) statement).expression, statements);
|
||||
}
|
||||
return statements;
|
||||
}
|
||||
|
||||
private HashTable!(String, int) transformConstants(ref Array!(parser.Definition) definitions) @nogc
|
||||
{
|
||||
typeof(return) constants;
|
||||
|
||||
foreach (definition; definitions[])
|
||||
{
|
||||
constants[definition.identifier] = definition.number.value;
|
||||
}
|
||||
|
||||
return constants;
|
||||
}
|
||||
|
||||
Array!VariableDeclaration transformVariableDeclarations(ref Array!(parser.VariableDeclaration) variableDeclarations)
|
||||
@nogc
|
||||
{
|
||||
typeof(return) variables;
|
||||
|
||||
foreach (ref variableDeclaration; variableDeclarations)
|
||||
{
|
||||
auto newDeclaration = defaultAllocator.make!VariableDeclaration;
|
||||
newDeclaration.identifier = variableDeclaration.identifier;
|
||||
variables.insertBack(newDeclaration);
|
||||
}
|
||||
|
||||
return variables;
|
||||
}
|
||||
}
|
@ -1,254 +0,0 @@
|
||||
module elna.lexer;
|
||||
|
||||
import core.stdc.stdlib;
|
||||
import core.stdc.ctype;
|
||||
import core.stdc.string;
|
||||
import elna.result;
|
||||
import std.range;
|
||||
import tanya.container.array;
|
||||
import tanya.container.string;
|
||||
|
||||
struct Token
|
||||
{
|
||||
enum Type
|
||||
{
|
||||
number,
|
||||
operator,
|
||||
let,
|
||||
identifier,
|
||||
equals,
|
||||
var,
|
||||
semicolon,
|
||||
leftParen,
|
||||
rightParen,
|
||||
bang,
|
||||
dot,
|
||||
comma,
|
||||
}
|
||||
|
||||
union Value
|
||||
{
|
||||
int number;
|
||||
String identifier;
|
||||
}
|
||||
|
||||
private Type type;
|
||||
private Value value_;
|
||||
private Position position_;
|
||||
|
||||
@disable this();
|
||||
|
||||
this(Type type, Position position) @nogc nothrow pure @safe
|
||||
{
|
||||
this.type = type;
|
||||
this.position_ = position;
|
||||
}
|
||||
|
||||
this(Type type, int value, Position position) @nogc nothrow pure @trusted
|
||||
in (type == Type.number)
|
||||
{
|
||||
this(type, position);
|
||||
this.value_.number = value;
|
||||
}
|
||||
|
||||
this()(Type type, auto ref String value, Position position)
|
||||
@nogc nothrow pure @trusted
|
||||
in (type == Type.identifier || type == Type.operator)
|
||||
{
|
||||
this(type, position);
|
||||
this.value_.identifier = value;
|
||||
}
|
||||
|
||||
/**
|
||||
* Params:
|
||||
* type = Expected type.
|
||||
*
|
||||
* Returns: Whether this token is of the expected type.
|
||||
*/
|
||||
bool ofType(Type type) const @nogc nothrow pure @safe
|
||||
{
|
||||
return this.type == type;
|
||||
}
|
||||
|
||||
@property auto value(Type type)() @nogc nothrow pure @trusted
|
||||
in (ofType(type))
|
||||
{
|
||||
static if (type == Type.number)
|
||||
{
|
||||
return this.value_.number;
|
||||
}
|
||||
else static if (type == Type.identifier || type == Type.operator)
|
||||
{
|
||||
return this.value_.identifier;
|
||||
}
|
||||
else
|
||||
{
|
||||
static assert(false, "This type doesn't have a value");
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns: The token position in the source text.
|
||||
*/
|
||||
@property const(Position) position() const @nogc nothrow pure @safe
|
||||
{
|
||||
return this.position_;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Range over the source text that keeps track of the current position.
|
||||
*/
|
||||
struct Source
|
||||
{
|
||||
char[] buffer;
|
||||
Position position;
|
||||
|
||||
this(char[] buffer) @nogc nothrow pure @safe
|
||||
{
|
||||
this.buffer = buffer;
|
||||
}
|
||||
|
||||
@disable this();
|
||||
|
||||
bool empty() @nogc nothrow pure @safe
|
||||
{
|
||||
return this.length == 0;
|
||||
}
|
||||
|
||||
char front() @nogc nothrow pure @safe
|
||||
in (!empty)
|
||||
{
|
||||
return this.buffer[0];
|
||||
}
|
||||
|
||||
void popFront() @nogc nothrow pure @safe
|
||||
in (!empty)
|
||||
{
|
||||
this.buffer = buffer[1 .. $];
|
||||
++this.position.column;
|
||||
}
|
||||
|
||||
void breakLine() @nogc nothrow pure @safe
|
||||
in (!empty)
|
||||
{
|
||||
this.buffer = buffer[1 .. $];
|
||||
++this.position.line;
|
||||
this.position.column = 1;
|
||||
}
|
||||
|
||||
@property size_t length() const @nogc nothrow pure @safe
|
||||
{
|
||||
return this.buffer.length;
|
||||
}
|
||||
|
||||
char opIndex(size_t index) @nogc nothrow pure @safe
|
||||
in (index < length)
|
||||
{
|
||||
return this.buffer[index];
|
||||
}
|
||||
|
||||
char[] opSlice(size_t i, size_t j) @nogc nothrow pure @safe
|
||||
in
|
||||
{
|
||||
assert(i <= j);
|
||||
assert(j <= length);
|
||||
}
|
||||
do
|
||||
{
|
||||
return this.buffer[i .. j];
|
||||
}
|
||||
}
|
||||
|
||||
Result!(Array!Token) lex(char[] buffer) @nogc
|
||||
{
|
||||
Array!Token tokens;
|
||||
auto source = Source(buffer);
|
||||
|
||||
while (!source.empty)
|
||||
{
|
||||
if (source.front == ' ')
|
||||
{
|
||||
source.popFront;
|
||||
}
|
||||
else if (source.front >= '0' && source.front <= '9') // Multi-digit.
|
||||
{
|
||||
tokens.insertBack(Token(Token.Type.number, source.front - '0', source.position));
|
||||
source.popFront;
|
||||
}
|
||||
else if (source.front == '=')
|
||||
{
|
||||
tokens.insertBack(Token(Token.Type.equals, source.position));
|
||||
source.popFront;
|
||||
}
|
||||
else if (source.front == '(')
|
||||
{
|
||||
tokens.insertBack(Token(Token.Type.leftParen, source.position));
|
||||
source.popFront;
|
||||
}
|
||||
else if (source.front == ')')
|
||||
{
|
||||
tokens.insertBack(Token(Token.Type.rightParen, source.position));
|
||||
source.popFront;
|
||||
}
|
||||
else if (source.front == ';')
|
||||
{
|
||||
tokens.insertBack(Token(Token.Type.semicolon, source.position));
|
||||
source.popFront;
|
||||
}
|
||||
else if (source.front == ',')
|
||||
{
|
||||
tokens.insertBack(Token(Token.Type.comma, source.position));
|
||||
source.popFront;
|
||||
}
|
||||
else if (source.front == '!')
|
||||
{
|
||||
tokens.insertBack(Token(Token.Type.bang, source.position));
|
||||
source.popFront;
|
||||
}
|
||||
else if (source.front == '.')
|
||||
{
|
||||
tokens.insertBack(Token(Token.Type.dot, source.position));
|
||||
source.popFront;
|
||||
}
|
||||
else if (isalpha(source.front))
|
||||
{
|
||||
size_t i = 1;
|
||||
while (i < source.length && isalpha(source[i]))
|
||||
{
|
||||
++i;
|
||||
}
|
||||
if (source[0 .. i] == "const")
|
||||
{
|
||||
tokens.insertBack(Token(Token.Type.let, source.position));
|
||||
}
|
||||
else if (source[0 .. i] == "var")
|
||||
{
|
||||
tokens.insertBack(Token(Token.Type.var, source.position));
|
||||
}
|
||||
else
|
||||
{
|
||||
auto identifier = String(source[0 .. i]);
|
||||
tokens.insertBack(Token(Token.Type.identifier, identifier, source.position));
|
||||
}
|
||||
source.popFrontN(i);
|
||||
}
|
||||
else if (source.front == '+' || source.front == '-')
|
||||
{
|
||||
String operator;
|
||||
|
||||
operator.insertBack(source.front);
|
||||
tokens.insertBack(Token(Token.Type.operator, operator, source.position));
|
||||
source.popFront;
|
||||
}
|
||||
else if (source.front == '\n')
|
||||
{
|
||||
source.breakLine;
|
||||
}
|
||||
else
|
||||
{
|
||||
return typeof(return)("Unexptected next character", source.position);
|
||||
}
|
||||
}
|
||||
return typeof(return)(tokens);
|
||||
}
|
@ -1,372 +0,0 @@
|
||||
module elna.parser;
|
||||
|
||||
import elna.lexer;
|
||||
import elna.result;
|
||||
import tanya.container.array;
|
||||
import tanya.container.string;
|
||||
import tanya.memory.allocator;
|
||||
|
||||
|
||||
/**
|
||||
* Parser visitor.
|
||||
*/
|
||||
interface ParserVisitor(Mapping)
|
||||
{
|
||||
Mapping.Node visit(Node) @nogc;
|
||||
Mapping.Definition visit(Definition) @nogc;
|
||||
Mapping.VariableDeclaration visit(VariableDeclaration) @nogc;
|
||||
Mapping.Statement visit(Statement) @nogc;
|
||||
Mapping.BangStatement visit(BangStatement) @nogc;
|
||||
Mapping.Block visit(Block) @nogc;
|
||||
Mapping.Expression visit(Expression) @nogc;
|
||||
Mapping.Number visit(Number) @nogc;
|
||||
Mapping.Variable visit(Variable) @nogc;
|
||||
Mapping.BinaryExpression visit(BinaryExpression) @nogc;
|
||||
}
|
||||
|
||||
/**
|
||||
* AST node.
|
||||
*/
|
||||
abstract class Node
|
||||
{
|
||||
Mapping.Node accept(Mapping)(ParserVisitor!Mapping visitor) @nogc
|
||||
{
|
||||
return visitor.visit(this);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Constant definition.
|
||||
*/
|
||||
class Definition : Node
|
||||
{
|
||||
Number number;
|
||||
String identifier;
|
||||
|
||||
Mapping.Definition accept(Mapping)(ParserVisitor!Mapping visitor) @nogc
|
||||
{
|
||||
return visitor.visit(this);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Variable declaration.
|
||||
*/
|
||||
class VariableDeclaration : Node
|
||||
{
|
||||
String identifier;
|
||||
|
||||
Mapping.VariableDeclaration accept(Mapping)(ParserVisitor!Mapping visitor) @nogc
|
||||
{
|
||||
return visitor.visit(this);
|
||||
}
|
||||
}
|
||||
|
||||
abstract class Statement : Node
|
||||
{
|
||||
Mapping.Statement accept(Mapping)(ParserVisitor!Mapping visitor) @nogc
|
||||
{
|
||||
return visitor.visit(this);
|
||||
}
|
||||
}
|
||||
|
||||
class BangStatement : Statement
|
||||
{
|
||||
Expression expression;
|
||||
|
||||
Mapping.BangStatement accept(Mapping)(ParserVisitor!Mapping visitor) @nogc
|
||||
{
|
||||
return visitor.visit(this);
|
||||
}
|
||||
}
|
||||
|
||||
class Block : Node
|
||||
{
|
||||
Array!Definition definitions;
|
||||
Array!VariableDeclaration variableDeclarations;
|
||||
Statement statement;
|
||||
|
||||
Mapping.Block accept(Mapping)(ParserVisitor!Mapping visitor) @nogc
|
||||
{
|
||||
return visitor.visit(this);
|
||||
}
|
||||
}
|
||||
|
||||
abstract class Expression : Node
|
||||
{
|
||||
Mapping.Expression accept(Mapping)(ParserVisitor!Mapping visitor) @nogc
|
||||
{
|
||||
return visitor.visit(this);
|
||||
}
|
||||
}
|
||||
|
||||
class Number : Expression
|
||||
{
|
||||
int value;
|
||||
|
||||
Mapping.Number accept(Mapping)(ParserVisitor!Mapping visitor) @nogc
|
||||
{
|
||||
return visitor.visit(this);
|
||||
}
|
||||
}
|
||||
|
||||
class Variable : Expression
|
||||
{
|
||||
String identifier;
|
||||
|
||||
Mapping.Variable accept(Mapping)(ParserVisitor!Mapping visitor) @nogc
|
||||
{
|
||||
return visitor.visit(this);
|
||||
}
|
||||
}
|
||||
|
||||
enum BinaryOperator
|
||||
{
|
||||
sum,
|
||||
subtraction
|
||||
}
|
||||
|
||||
class BinaryExpression : Expression
|
||||
{
|
||||
Expression lhs, rhs;
|
||||
BinaryOperator operator;
|
||||
|
||||
this(Expression lhs, Expression rhs, String operator) @nogc
|
||||
{
|
||||
this.lhs = lhs;
|
||||
this.rhs = rhs;
|
||||
if (operator == "+")
|
||||
{
|
||||
this.operator = BinaryOperator.sum;
|
||||
}
|
||||
else if (operator == "-")
|
||||
{
|
||||
this.operator = BinaryOperator.subtraction;
|
||||
}
|
||||
else
|
||||
{
|
||||
assert(false, "Invalid binary operator");
|
||||
}
|
||||
}
|
||||
|
||||
Mapping.BinaryExpression accept(Mapping)(ParserVisitor!Mapping visitor) @nogc
|
||||
{
|
||||
return visitor.visit(this);
|
||||
}
|
||||
}
|
||||
|
||||
private Result!Expression parseFactor(ref Array!Token.Range tokens) @nogc
|
||||
in (!tokens.empty, "Expected factor, got end of stream")
|
||||
{
|
||||
if (tokens.front.ofType(Token.Type.identifier))
|
||||
{
|
||||
auto variable = defaultAllocator.make!Variable;
|
||||
variable.identifier = tokens.front.value!(Token.Type.identifier);
|
||||
tokens.popFront;
|
||||
return Result!Expression(variable);
|
||||
}
|
||||
else if (tokens.front.ofType(Token.Type.number))
|
||||
{
|
||||
auto number = defaultAllocator.make!Number;
|
||||
number.value = tokens.front.value!(Token.Type.number);
|
||||
tokens.popFront;
|
||||
return Result!Expression(number);
|
||||
}
|
||||
else if (tokens.front.ofType(Token.Type.leftParen))
|
||||
{
|
||||
tokens.popFront;
|
||||
|
||||
auto expression = parseExpression(tokens);
|
||||
|
||||
tokens.popFront;
|
||||
return expression;
|
||||
}
|
||||
return Result!Expression("Expected a factor", tokens.front.position);
|
||||
}
|
||||
|
||||
private Result!Expression parseTerm(ref Array!(Token).Range tokens) @nogc
|
||||
{
|
||||
return parseFactor(tokens);
|
||||
}
|
||||
|
||||
private Result!Expression parseExpression(ref Array!(Token).Range tokens) @nogc
|
||||
in (!tokens.empty, "Expected expression, got end of stream")
|
||||
{
|
||||
auto term = parseTerm(tokens);
|
||||
if (!term.valid || tokens.empty || !tokens.front.ofType(Token.Type.operator))
|
||||
{
|
||||
return term;
|
||||
}
|
||||
auto operator = tokens.front.value!(Token.Type.operator);
|
||||
tokens.popFront;
|
||||
|
||||
auto expression = parseExpression(tokens);
|
||||
|
||||
if (expression.valid)
|
||||
{
|
||||
auto binaryExpression = defaultAllocator
|
||||
.make!BinaryExpression(term.result, expression.result, operator);
|
||||
|
||||
return Result!Expression(binaryExpression);
|
||||
}
|
||||
else
|
||||
{
|
||||
return Result!Expression("Expected right-hand side to be an expression", tokens.front.position);
|
||||
}
|
||||
}
|
||||
|
||||
private Result!Definition parseDefinition(ref Array!Token.Range tokens) @nogc
|
||||
in (!tokens.empty, "Expected definition, got end of stream")
|
||||
{
|
||||
auto definition = defaultAllocator.make!Definition;
|
||||
definition.identifier = tokens.front.value!(Token.Type.identifier); // Copy.
|
||||
|
||||
tokens.popFront();
|
||||
tokens.popFront(); // Skip the equals sign.
|
||||
|
||||
if (tokens.front.ofType(Token.Type.number))
|
||||
{
|
||||
auto number = defaultAllocator.make!Number;
|
||||
number.value = tokens.front.value!(Token.Type.number);
|
||||
definition.number = number;
|
||||
tokens.popFront;
|
||||
return Result!Definition(definition);
|
||||
}
|
||||
return Result!Definition("Expected a number", tokens.front.position);
|
||||
}
|
||||
|
||||
private Result!Statement parseStatement(ref Array!Token.Range tokens) @nogc
|
||||
in (!tokens.empty, "Expected block, got end of stream")
|
||||
{
|
||||
if (tokens.front.ofType(Token.Type.bang))
|
||||
{
|
||||
tokens.popFront;
|
||||
auto statement = defaultAllocator.make!BangStatement;
|
||||
auto expression = parseExpression(tokens);
|
||||
if (expression.valid)
|
||||
{
|
||||
statement.expression = expression.result;
|
||||
}
|
||||
else
|
||||
{
|
||||
return Result!Statement(expression.error.get);
|
||||
}
|
||||
return Result!Statement(statement);
|
||||
}
|
||||
return Result!Statement("Expected ! statement", tokens.front.position);
|
||||
}
|
||||
|
||||
private Result!(Array!Definition) parseDefinitions(ref Array!Token.Range tokens) @nogc
|
||||
in (!tokens.empty, "Expected definition, got end of stream")
|
||||
{
|
||||
tokens.popFront; // Skip const.
|
||||
|
||||
Array!Definition definitions;
|
||||
|
||||
while (!tokens.empty)
|
||||
{
|
||||
auto definition = parseDefinition(tokens);
|
||||
if (!definition.valid)
|
||||
{
|
||||
return typeof(return)(definition.error.get);
|
||||
}
|
||||
definitions.insertBack(definition.result);
|
||||
if (tokens.front.ofType(Token.Type.semicolon))
|
||||
{
|
||||
break;
|
||||
}
|
||||
if (tokens.front.ofType(Token.Type.comma))
|
||||
{
|
||||
tokens.popFront;
|
||||
}
|
||||
}
|
||||
|
||||
return typeof(return)(definitions);
|
||||
}
|
||||
|
||||
private Result!(Array!VariableDeclaration) parseVariableDeclarations(ref Array!Token.Range tokens) @nogc
|
||||
in (!tokens.empty, "Expected variable declarations, got end of stream")
|
||||
{
|
||||
tokens.popFront; // Skip var.
|
||||
|
||||
Array!VariableDeclaration variableDeclarations;
|
||||
|
||||
while (!tokens.empty)
|
||||
{
|
||||
auto currentToken = tokens.front;
|
||||
if (currentToken.ofType(Token.Type.identifier))
|
||||
{
|
||||
auto variableDeclaration = defaultAllocator.make!VariableDeclaration;
|
||||
variableDeclaration.identifier = currentToken.value!(Token.Type.identifier);
|
||||
variableDeclarations.insertBack(variableDeclaration);
|
||||
tokens.popFront;
|
||||
}
|
||||
else
|
||||
{
|
||||
return typeof(return)("Expected variable name", tokens.front.position);
|
||||
}
|
||||
if (tokens.empty)
|
||||
{
|
||||
return typeof(return)("Expected \";\" or \",\" name", currentToken.position);
|
||||
}
|
||||
if (tokens.front.ofType(Token.Type.semicolon))
|
||||
{
|
||||
break;
|
||||
}
|
||||
if (tokens.front.ofType(Token.Type.comma))
|
||||
{
|
||||
tokens.popFront;
|
||||
}
|
||||
}
|
||||
|
||||
return typeof(return)(variableDeclarations);
|
||||
}
|
||||
|
||||
private Result!Block parseBlock(ref Array!Token.Range tokens) @nogc
|
||||
in (!tokens.empty, "Expected block, got end of stream")
|
||||
{
|
||||
auto block = defaultAllocator.make!Block;
|
||||
if (tokens.front.ofType(Token.Type.let))
|
||||
{
|
||||
auto constDefinitions = parseDefinitions(tokens);
|
||||
if (constDefinitions.valid)
|
||||
{
|
||||
block.definitions = constDefinitions.result;
|
||||
}
|
||||
else
|
||||
{
|
||||
return Result!Block(constDefinitions.error.get);
|
||||
}
|
||||
tokens.popFront;
|
||||
}
|
||||
if (tokens.front.ofType(Token.Type.var))
|
||||
{
|
||||
auto variableDeclarations = parseVariableDeclarations(tokens);
|
||||
if (variableDeclarations.valid)
|
||||
{
|
||||
block.variableDeclarations = variableDeclarations.result;
|
||||
}
|
||||
else
|
||||
{
|
||||
return Result!Block(variableDeclarations.error.get);
|
||||
}
|
||||
tokens.popFront;
|
||||
}
|
||||
auto statement = parseStatement(tokens);
|
||||
if (statement.valid)
|
||||
{
|
||||
block.statement = statement.result;
|
||||
}
|
||||
else
|
||||
{
|
||||
return Result!Block(statement.error.get);
|
||||
}
|
||||
|
||||
return Result!Block(block);
|
||||
}
|
||||
|
||||
Result!Block parse(ref Array!Token tokenStream) @nogc
|
||||
{
|
||||
auto tokens = tokenStream[];
|
||||
return parseBlock(tokens);
|
||||
}
|
@ -1,107 +0,0 @@
|
||||
module elna.result;
|
||||
|
||||
import std.typecons;
|
||||
import tanya.container.array;
|
||||
import tanya.container.string;
|
||||
|
||||
/**
|
||||
* Position in the source text.
|
||||
*/
|
||||
struct Position
|
||||
{
|
||||
/// Line.
|
||||
size_t line = 1;
|
||||
|
||||
/// Column.
|
||||
size_t column = 1;
|
||||
}
|
||||
|
||||
struct CompileError
|
||||
{
|
||||
private string message_;
|
||||
|
||||
private Position position_;
|
||||
|
||||
@disable this();
|
||||
|
||||
/**
|
||||
* Params:
|
||||
* message = Error text.
|
||||
* position = Error position in the source text.
|
||||
*/
|
||||
this(string message, Position position) @nogc nothrow pure @safe
|
||||
{
|
||||
this.message_ = message;
|
||||
this.position_ = position;
|
||||
}
|
||||
|
||||
/// Error text.
|
||||
@property string message() const @nogc nothrow pure @safe
|
||||
{
|
||||
return this.message_;
|
||||
}
|
||||
|
||||
/// Error line in the source text.
|
||||
@property size_t line() const @nogc nothrow pure @safe
|
||||
{
|
||||
return this.position_.line;
|
||||
}
|
||||
|
||||
/// Error column in the source text.
|
||||
@property size_t column() const @nogc nothrow pure @safe
|
||||
{
|
||||
return this.position_.column;
|
||||
}
|
||||
}
|
||||
|
||||
struct Result(T)
|
||||
{
|
||||
Nullable!CompileError error;
|
||||
T result;
|
||||
|
||||
this(T result)
|
||||
{
|
||||
this.result = result;
|
||||
this.error = typeof(this.error).init;
|
||||
}
|
||||
|
||||
this(string message, Position position)
|
||||
{
|
||||
this.result = T.init;
|
||||
this.error = CompileError(message, position);
|
||||
}
|
||||
|
||||
this(CompileError compileError)
|
||||
{
|
||||
this.result = null;
|
||||
this.error = compileError;
|
||||
}
|
||||
|
||||
@disable this();
|
||||
|
||||
@property bool valid() const
|
||||
{
|
||||
return error.isNull;
|
||||
}
|
||||
}
|
||||
|
||||
struct Reference
|
||||
{
|
||||
enum Target
|
||||
{
|
||||
text,
|
||||
high20,
|
||||
lower12i
|
||||
}
|
||||
|
||||
String name;
|
||||
size_t offset;
|
||||
Target target;
|
||||
}
|
||||
|
||||
struct Symbol
|
||||
{
|
||||
String name;
|
||||
Array!ubyte text;
|
||||
Array!Reference symbols;
|
||||
}
|
@ -1,352 +0,0 @@
|
||||
module elna.riscv;
|
||||
|
||||
import elna.extended;
|
||||
import elna.ir;
|
||||
import elna.result;
|
||||
import std.algorithm;
|
||||
import std.typecons;
|
||||
import tanya.container.array;
|
||||
import tanya.container.string;
|
||||
import tanya.memory.allocator;
|
||||
|
||||
enum XRegister : ubyte
|
||||
{
|
||||
zero = 0,
|
||||
ra = 1,
|
||||
sp = 2,
|
||||
gp = 3,
|
||||
tp = 4,
|
||||
t0 = 5,
|
||||
t1 = 6,
|
||||
t2 = 7,
|
||||
s0 = 8,
|
||||
s1 = 9,
|
||||
a0 = 10,
|
||||
a1 = 11,
|
||||
a2 = 12,
|
||||
a3 = 13,
|
||||
a4 = 14,
|
||||
a5 = 15,
|
||||
a6 = 16,
|
||||
a7 = 17,
|
||||
s2 = 18,
|
||||
s3 = 19,
|
||||
s4 = 20,
|
||||
s5 = 21,
|
||||
s6 = 22,
|
||||
s7 = 23,
|
||||
s8 = 24,
|
||||
s9 = 25,
|
||||
s10 = 26,
|
||||
s11 = 27,
|
||||
t3 = 28,
|
||||
t4 = 29,
|
||||
t5 = 30,
|
||||
t6 = 31,
|
||||
}
|
||||
|
||||
enum Funct3 : ubyte
|
||||
{
|
||||
addi = 0b000,
|
||||
slti = 0b001,
|
||||
sltiu = 0b011,
|
||||
andi = 0b111,
|
||||
ori = 0b110,
|
||||
xori = 0b100,
|
||||
slli = 0b000,
|
||||
srli = 0b101,
|
||||
srai = 0b101,
|
||||
add = 0b000,
|
||||
slt = 0b010,
|
||||
sltu = 0b011,
|
||||
and = 0b111,
|
||||
or = 0b110,
|
||||
xor = 0b100,
|
||||
sll = 0b001,
|
||||
srl = 0b101,
|
||||
sub = 0b000,
|
||||
sra = 0b101,
|
||||
beq = 0b000,
|
||||
bne = 0b001,
|
||||
blt = 0b100,
|
||||
bltu = 0b110,
|
||||
bge = 0b101,
|
||||
bgeu = 0b111,
|
||||
fence = 0b000,
|
||||
fenceI = 0b001,
|
||||
csrrw = 0b001,
|
||||
csrrs = 0b010,
|
||||
csrrc = 0b011,
|
||||
csrrwi = 0b101,
|
||||
csrrsi = 0b110,
|
||||
csrrci = 0b111,
|
||||
priv = 0b000,
|
||||
sb = 0b000,
|
||||
sh = 0b001,
|
||||
sw = 0b010,
|
||||
lb = 0b000,
|
||||
lh = 0b001,
|
||||
lw = 0b010,
|
||||
lbu = 0b100,
|
||||
lhu = 0b101,
|
||||
jalr = 0b000,
|
||||
}
|
||||
|
||||
enum Funct12 : ubyte
|
||||
{
|
||||
ecall = 0b000000000000,
|
||||
ebreak = 0b000000000001,
|
||||
}
|
||||
|
||||
enum Funct7 : ubyte
|
||||
{
|
||||
none = 0,
|
||||
sub = 0b0100000
|
||||
}
|
||||
|
||||
enum BaseOpcode : ubyte
|
||||
{
|
||||
opImm = 0b0010011,
|
||||
lui = 0b0110111,
|
||||
auipc = 0b0010111,
|
||||
op = 0b0110011,
|
||||
jal = 0b1101111,
|
||||
jalr = 0b1100111,
|
||||
branch = 0b1100011,
|
||||
load = 0b0000011,
|
||||
store = 0b0100011,
|
||||
miscMem = 0b0001111,
|
||||
system = 0b1110011,
|
||||
}
|
||||
|
||||
struct Instruction
|
||||
{
|
||||
private uint instruction;
|
||||
|
||||
this(BaseOpcode opcode) @nogc
|
||||
{
|
||||
this.instruction = opcode;
|
||||
}
|
||||
@disable this();
|
||||
|
||||
ref Instruction i(XRegister rd, Funct3 funct3, XRegister rs1, uint immediate)
|
||||
return scope @nogc
|
||||
{
|
||||
this.instruction |= (rd << 7)
|
||||
| (funct3 << 12)
|
||||
| (rs1 << 15)
|
||||
| (immediate << 20);
|
||||
|
||||
return this;
|
||||
}
|
||||
|
||||
ref Instruction s(uint imm1, Funct3 funct3, XRegister rs1, XRegister rs2, uint imm2 = 0)
|
||||
return scope @nogc
|
||||
{
|
||||
this.instruction |= (imm1 << 7)
|
||||
| (funct3 << 12)
|
||||
| (rs1 << 15)
|
||||
| (rs2 << 20)
|
||||
| (imm2 << 25);
|
||||
|
||||
return this;
|
||||
}
|
||||
|
||||
ref Instruction r(XRegister rd, Funct3 funct3, XRegister rs1, XRegister rs2, Funct7 funct7 = Funct7.none)
|
||||
return scope @nogc
|
||||
{
|
||||
this.instruction |= (rd << 7)
|
||||
| (funct3 << 12)
|
||||
| (rs1 << 15)
|
||||
| (rs2 << 20)
|
||||
| (funct7 << 25);
|
||||
|
||||
return this;
|
||||
}
|
||||
|
||||
ref Instruction u(XRegister rd, uint imm)
|
||||
return scope @nogc
|
||||
{
|
||||
this.instruction |= (rd << 7) | (imm << 12);
|
||||
|
||||
return this;
|
||||
}
|
||||
|
||||
ubyte[] encode() return scope @nogc
|
||||
{
|
||||
return (cast(ubyte*) (&this.instruction))[0 .. uint.sizeof];
|
||||
}
|
||||
}
|
||||
|
||||
class RiscVVisitor : IRVisitor
|
||||
{
|
||||
Array!Instruction instructions;
|
||||
bool registerInUse;
|
||||
uint variableCounter = 1;
|
||||
Array!Reference references;
|
||||
|
||||
override void visit(Node) @nogc
|
||||
{
|
||||
}
|
||||
|
||||
override void visit(Definition definition) @nogc
|
||||
{
|
||||
// Prologue.
|
||||
this.instructions.insertBack(
|
||||
Instruction(BaseOpcode.opImm)
|
||||
.i(XRegister.sp, Funct3.addi, XRegister.sp, cast(uint) -32)
|
||||
);
|
||||
this.instructions.insertBack(
|
||||
Instruction(BaseOpcode.store)
|
||||
.s(28, Funct3.sw, XRegister.sp, XRegister.s0)
|
||||
);
|
||||
this.instructions.insertBack(
|
||||
Instruction(BaseOpcode.store)
|
||||
.s(24, Funct3.sw, XRegister.sp, XRegister.ra)
|
||||
);
|
||||
this.instructions.insertBack(
|
||||
Instruction(BaseOpcode.opImm)
|
||||
.i(XRegister.s0, Funct3.addi, XRegister.sp, 32)
|
||||
);
|
||||
|
||||
foreach (statement; definition.statements[])
|
||||
{
|
||||
statement.accept(this);
|
||||
}
|
||||
foreach (variableDeclaration; definition.variableDeclarations[])
|
||||
{
|
||||
variableDeclaration.accept(this);
|
||||
}
|
||||
// Print the result.
|
||||
this.instructions.insertBack(
|
||||
Instruction(BaseOpcode.opImm)
|
||||
.i(XRegister.a1, Funct3.addi, XRegister.a0, 0)
|
||||
);
|
||||
this.references.insertBack(Reference(String(".CL0"), instructions.length * 4, Reference.Target.high20));
|
||||
this.instructions.insertBack(
|
||||
Instruction(BaseOpcode.lui).u(XRegister.a5, 0)
|
||||
);
|
||||
this.references.insertBack(Reference(String(".CL0"), instructions.length * 4, Reference.Target.lower12i));
|
||||
this.instructions.insertBack(
|
||||
Instruction(BaseOpcode.opImm).i(XRegister.a0, Funct3.addi, XRegister.a5, 0)
|
||||
);
|
||||
this.references.insertBack(Reference(String("printf"), instructions.length * 4, Reference.Target.text));
|
||||
this.instructions.insertBack(
|
||||
Instruction(BaseOpcode.auipc).u(XRegister.ra, 0)
|
||||
);
|
||||
this.instructions.insertBack(
|
||||
Instruction(BaseOpcode.jalr)
|
||||
.i(XRegister.ra, Funct3.jalr, XRegister.ra, 0)
|
||||
);
|
||||
// Set the return value (0).
|
||||
this.instructions.insertBack(
|
||||
Instruction(BaseOpcode.op)
|
||||
.r(XRegister.a0, Funct3.and, XRegister.zero, XRegister.zero)
|
||||
);
|
||||
|
||||
// Epilogue.
|
||||
this.instructions.insertBack(
|
||||
Instruction(BaseOpcode.load)
|
||||
.i(XRegister.s0, Funct3.lw, XRegister.sp, 28)
|
||||
);
|
||||
this.instructions.insertBack(
|
||||
Instruction(BaseOpcode.load)
|
||||
.i(XRegister.ra, Funct3.lw, XRegister.sp, 24)
|
||||
);
|
||||
this.instructions.insertBack(
|
||||
Instruction(BaseOpcode.opImm)
|
||||
.i(XRegister.sp, Funct3.addi, XRegister.sp, 32)
|
||||
);
|
||||
this.instructions.insertBack(
|
||||
Instruction(BaseOpcode.jalr)
|
||||
.i(XRegister.zero, Funct3.jalr, XRegister.ra, 0)
|
||||
);
|
||||
}
|
||||
|
||||
override void visit(Expression) @nogc
|
||||
{
|
||||
}
|
||||
|
||||
override void visit(Statement statement) @nogc
|
||||
{
|
||||
statement.expression.accept(this);
|
||||
}
|
||||
|
||||
override void visit(Variable variable) @nogc
|
||||
{
|
||||
const freeRegister = this.registerInUse ? XRegister.a0 : XRegister.t0;
|
||||
|
||||
// movl -x(%rbp), %eax; where x is a number.
|
||||
this.instructions.insertBack(
|
||||
Instruction(BaseOpcode.load)
|
||||
.i(freeRegister, Funct3.lw, XRegister.sp,
|
||||
cast(byte) (variable.counter * 4))
|
||||
);
|
||||
}
|
||||
|
||||
override void visit(VariableDeclaration) @nogc
|
||||
{
|
||||
}
|
||||
|
||||
override void visit(Number number) @nogc
|
||||
{
|
||||
const freeRegister = this.registerInUse ? XRegister.a0 : XRegister.t0;
|
||||
|
||||
this.instructions.insertBack(
|
||||
Instruction(BaseOpcode.opImm) // movl $x, %eax; where $x is a number.
|
||||
.i(freeRegister, Funct3.addi, XRegister.zero, number.value)
|
||||
);
|
||||
}
|
||||
|
||||
override void visit(BinaryExpression expression) @nogc
|
||||
{
|
||||
this.registerInUse = true;
|
||||
expression.lhs.accept(this);
|
||||
this.registerInUse = false;
|
||||
expression.rhs.accept(this);
|
||||
|
||||
// Calculate the result and assign it to a variable on the stack.
|
||||
final switch (expression.operator)
|
||||
{
|
||||
case BinaryOperator.sum:
|
||||
this.instructions.insertBack(
|
||||
Instruction(BaseOpcode.op)
|
||||
.r(XRegister.a0, Funct3.add, XRegister.a0, XRegister.t0)
|
||||
);
|
||||
break;
|
||||
case BinaryOperator.subtraction:
|
||||
this.instructions.insertBack(
|
||||
Instruction(BaseOpcode.op)
|
||||
.r(XRegister.a0, Funct3.sub, XRegister.a0, XRegister.t0, Funct7.sub)
|
||||
);
|
||||
break;
|
||||
}
|
||||
this.instructions.insertBack( // movl %eax, -x(%rbp); where x is a number.
|
||||
Instruction(BaseOpcode.store)
|
||||
.s(cast(uint) (this.variableCounter * 4), Funct3.sw, XRegister.sp, XRegister.a0)
|
||||
);
|
||||
|
||||
++this.variableCounter;
|
||||
}
|
||||
}
|
||||
|
||||
Symbol writeNext(Definition ast) @nogc
|
||||
{
|
||||
Array!Instruction instructions;
|
||||
Array!Reference references;
|
||||
auto visitor = defaultAllocator.make!RiscVVisitor;
|
||||
scope (exit)
|
||||
{
|
||||
defaultAllocator.dispose(visitor);
|
||||
}
|
||||
visitor.visit(ast);
|
||||
|
||||
auto program = Symbol(String("main"));
|
||||
|
||||
program.symbols = move(visitor.references);
|
||||
foreach (ref instruction; visitor.instructions)
|
||||
{
|
||||
program.text.insertBack(instruction.encode);
|
||||
}
|
||||
return program;
|
||||
}
|
192
source/lexer.ll
Normal file
192
source/lexer.ll
Normal file
@ -0,0 +1,192 @@
|
||||
/*
|
||||
* 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 http://mozilla.org/MPL/2.0/.
|
||||
*/
|
||||
%{
|
||||
#define YY_NO_UNISTD_H
|
||||
#define YY_USER_ACTION this->location.columns(yyleng);
|
||||
|
||||
#include <sstream>
|
||||
#include "parser.hh"
|
||||
|
||||
#undef YY_DECL
|
||||
#define YY_DECL yy::parser::symbol_type elna::source::lexer::lex(elna::source::driver& driver)
|
||||
#define yyterminate() return yy::parser::make_YYEOF(this->location)
|
||||
%}
|
||||
|
||||
%option c++ noyywrap never-interactive
|
||||
%option yyclass="elna::source::lexer"
|
||||
|
||||
%%
|
||||
%{
|
||||
this->location.step();
|
||||
%}
|
||||
|
||||
\-\-.* {
|
||||
/* Skip the comment */
|
||||
}
|
||||
[\ \t\r] ; /* Skip the whitespaces */
|
||||
\n+ {
|
||||
this->location.lines(yyleng);
|
||||
this->location.step();
|
||||
}
|
||||
if {
|
||||
return yy::parser::make_IF(this->location);
|
||||
}
|
||||
then {
|
||||
return yy::parser::make_THEN(this->location);
|
||||
}
|
||||
else {
|
||||
return yy::parser::make_ELSE(this->location);
|
||||
}
|
||||
while {
|
||||
return yy::parser::make_WHILE(this->location);
|
||||
}
|
||||
do {
|
||||
return yy::parser::make_DO(this->location);
|
||||
}
|
||||
proc {
|
||||
return yy::parser::make_PROCEDURE(this->location);
|
||||
}
|
||||
begin {
|
||||
return yy::parser::make_BEGIN_BLOCK(this->location);
|
||||
}
|
||||
end {
|
||||
return yy::parser::make_END_BLOCK(this->location);
|
||||
}
|
||||
extern {
|
||||
return yy::parser::make_EXTERN(this->location);
|
||||
}
|
||||
const {
|
||||
return yy::parser::make_CONST(this->location);
|
||||
}
|
||||
var {
|
||||
return yy::parser::make_VAR(this->location);
|
||||
}
|
||||
array {
|
||||
return yy::parser::make_ARRAY(this->location);
|
||||
}
|
||||
of {
|
||||
return yy::parser::make_OF(this->location);
|
||||
}
|
||||
type {
|
||||
return yy::parser::make_TYPE(this->location);
|
||||
}
|
||||
record {
|
||||
return yy::parser::make_RECORD(this->location);
|
||||
}
|
||||
union {
|
||||
return yy::parser::make_UNION(this->location);
|
||||
}
|
||||
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);
|
||||
}
|
||||
and {
|
||||
return yy::parser::make_AND(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);
|
||||
}
|
||||
[A-Za-z_][A-Za-z0-9_]* {
|
||||
return yy::parser::make_IDENTIFIER(yytext, this->location);
|
||||
}
|
||||
[0-9]+ {
|
||||
return yy::parser::make_INTEGER(strtol(yytext, NULL, 10), this->location);
|
||||
}
|
||||
[0-9]+\.[0-9] {
|
||||
return yy::parser::make_FLOAT(strtof(yytext, NULL), this->location);
|
||||
}
|
||||
'[^']' {
|
||||
return yy::parser::make_CHARACTER(
|
||||
std::string(yytext, 1, strlen(yytext) - 2), this->location);
|
||||
}
|
||||
\"[^\"]*\" {
|
||||
return yy::parser::make_STRING(
|
||||
std::string(yytext, 1, strlen(yytext) - 2), this->location);
|
||||
}
|
||||
\( {
|
||||
return yy::parser::make_LEFT_PAREN(this->location);
|
||||
}
|
||||
\) {
|
||||
return yy::parser::make_RIGHT_PAREN(this->location);
|
||||
}
|
||||
\[ {
|
||||
return yy::parser::make_LEFT_SQUARE(this->location);
|
||||
}
|
||||
\] {
|
||||
return yy::parser::make_RIGHT_SQUARE(this->location);
|
||||
}
|
||||
\>= {
|
||||
return yy::parser::make_GREATER_EQUAL(this->location);
|
||||
}
|
||||
\<= {
|
||||
return yy::parser::make_LESS_EQUAL(this->location);
|
||||
}
|
||||
\> {
|
||||
return yy::parser::make_GREATER_THAN(this->location);
|
||||
}
|
||||
\< {
|
||||
return yy::parser::make_LESS_THAN(this->location);
|
||||
}
|
||||
\/= {
|
||||
return yy::parser::make_NOT_EQUAL(this->location);
|
||||
}
|
||||
= {
|
||||
return yy::parser::make_EQUALS(this->location);
|
||||
}
|
||||
; {
|
||||
return yy::parser::make_SEMICOLON(this->location);
|
||||
}
|
||||
\. {
|
||||
return yy::parser::make_DOT(this->location);
|
||||
}
|
||||
, {
|
||||
return yy::parser::make_COMMA(this->location);
|
||||
}
|
||||
\+ {
|
||||
return yy::parser::make_PLUS(this->location);
|
||||
}
|
||||
\- {
|
||||
return yy::parser::make_MINUS(this->location);
|
||||
}
|
||||
\* {
|
||||
return yy::parser::make_MULTIPLICATION(this->location);
|
||||
}
|
||||
\/ {
|
||||
return yy::parser::make_DIVISION(this->location);
|
||||
}
|
||||
:= {
|
||||
return yy::parser::make_ASSIGNMENT(this->location);
|
||||
}
|
||||
: {
|
||||
return yy::parser::make_COLON(this->location);
|
||||
}
|
||||
\^ {
|
||||
return yy::parser::make_HAT(this->location);
|
||||
}
|
||||
@ {
|
||||
return yy::parser::make_AT(this->location);
|
||||
}
|
||||
. {
|
||||
std::stringstream ss;
|
||||
|
||||
ss << "Illegal character 0x" << std::hex << static_cast<unsigned int>(yytext[0]);
|
||||
driver.error(this->location, ss.str());
|
||||
}
|
||||
%%
|
@ -1,33 +0,0 @@
|
||||
import elna.backend;
|
||||
import elna.ir;
|
||||
import elna.arguments;
|
||||
import std.path;
|
||||
import std.sumtype;
|
||||
import tanya.container.string;
|
||||
import tanya.memory.allocator;
|
||||
import tanya.memory.mmappool;
|
||||
|
||||
int main(string[] args)
|
||||
{
|
||||
defaultAllocator = MmapPool.instance;
|
||||
|
||||
return Arguments.parse(args).match!(
|
||||
(ArgumentError argumentError) => 4,
|
||||
(Arguments arguments) {
|
||||
String outputFilename;
|
||||
if (arguments.output is null)
|
||||
{
|
||||
outputFilename = arguments
|
||||
.inFile
|
||||
.baseName
|
||||
.withExtension("o");
|
||||
}
|
||||
else
|
||||
{
|
||||
outputFilename = String(arguments.output);
|
||||
}
|
||||
|
||||
return generate(arguments.inFile, outputFilename);
|
||||
}
|
||||
);
|
||||
}
|
423
source/parser.yy
Normal file
423
source/parser.yy
Normal file
@ -0,0 +1,423 @@
|
||||
/*
|
||||
* 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 http://mozilla.org/MPL/2.0/.
|
||||
*/
|
||||
%require "3.2"
|
||||
%language "c++"
|
||||
|
||||
%code requires {
|
||||
#include <cstdint>
|
||||
#include <iostream>
|
||||
#include "elna/source/driver.h"
|
||||
|
||||
#if !defined(yyFlexLexerOnce)
|
||||
#include <FlexLexer.h>
|
||||
#endif
|
||||
|
||||
namespace elna::source
|
||||
{
|
||||
class lexer;
|
||||
}
|
||||
}
|
||||
|
||||
%code provides {
|
||||
namespace elna::source
|
||||
{
|
||||
|
||||
class lexer: public yyFlexLexer
|
||||
{
|
||||
public:
|
||||
yy::location location;
|
||||
|
||||
lexer(std::istream& arg_yyin)
|
||||
: yyFlexLexer(&arg_yyin)
|
||||
{
|
||||
}
|
||||
|
||||
yy::parser::symbol_type lex(elna::source::driver& driver);
|
||||
};
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
%define api.token.raw
|
||||
%define api.token.constructor
|
||||
%define api.value.type variant
|
||||
|
||||
%parse-param {elna::source::lexer& lexer}
|
||||
%param {elna::source::driver& driver}
|
||||
%locations
|
||||
|
||||
%header
|
||||
|
||||
%code {
|
||||
#define yylex lexer.lex
|
||||
}
|
||||
%start program;
|
||||
|
||||
%token <std::string> IDENTIFIER "identifier"
|
||||
%token <std::int32_t> INTEGER "integer"
|
||||
%token <float> FLOAT "float"
|
||||
%token <std::string> CHARACTER "character"
|
||||
%token <std::string> STRING "string"
|
||||
%token <bool> BOOLEAN
|
||||
%token IF WHILE DO THEN ELSE RETURN
|
||||
%token CONST VAR PROCEDURE ARRAY OF TYPE RECORD POINTER TO UNION
|
||||
%token BEGIN_BLOCK END_BLOCK EXTERN
|
||||
%token LEFT_PAREN RIGHT_PAREN LEFT_SQUARE RIGHT_SQUARE SEMICOLON DOT COMMA
|
||||
%token AND OR NOT
|
||||
%token GREATER_EQUAL LESS_EQUAL LESS_THAN GREATER_THAN NOT_EQUAL EQUALS
|
||||
%token PLUS MINUS MULTIPLICATION DIVISION
|
||||
%token ASSIGNMENT COLON HAT AT
|
||||
|
||||
%type <elna::source::literal *> literal;
|
||||
%type <elna::source::constant_definition *> constant_definition;
|
||||
%type <std::vector<elna::source::constant_definition *>> constant_part constant_definitions;
|
||||
%type <elna::source::variable_declaration *> variable_declaration;
|
||||
%type <std::vector<elna::source::variable_declaration *>> variable_declarations variable_part
|
||||
formal_parameter_list;
|
||||
%type <elna::source::type_expression *> type_expression;
|
||||
%type <elna::source::expression *> expression pointer summand factor comparand logical_operand;
|
||||
%type <std::vector<elna::source::expression *>> expressions actual_parameter_list;
|
||||
%type <elna::source::designator_expression *> designator_expression;
|
||||
%type <elna::source::assign_statement *> assign_statement;
|
||||
%type <elna::source::call_expression *> call_expression;
|
||||
%type <elna::source::while_statement *> while_statement;
|
||||
%type <elna::source::if_statement *> if_statement;
|
||||
%type <elna::source::return_statement *> return_statement;
|
||||
%type <elna::source::statement *> statement;
|
||||
%type <std::vector<elna::source::statement *>> statements optional_statements;
|
||||
%type <elna::source::procedure_definition *> procedure_definition;
|
||||
%type <std::vector<elna::source::procedure_definition *>> procedure_definitions procedure_part;
|
||||
%type <elna::source::type_definition *> type_definition;
|
||||
%type <std::vector<elna::source::type_definition *>> type_definitions type_part;
|
||||
%type <elna::source::block *> block;
|
||||
%type <std::pair<std::string, elna::source::type_expression *>> field_declaration;
|
||||
%type <std::vector<std::pair<std::string, elna::source::type_expression *>>> field_list;
|
||||
%%
|
||||
program:
|
||||
type_part constant_part procedure_part variable_part BEGIN_BLOCK optional_statements END_BLOCK DOT
|
||||
{
|
||||
std::vector<elna::source::definition *> definitions($1.size() + $3.size());
|
||||
std::vector<elna::source::definition *>::iterator definition = definitions.begin();
|
||||
std::vector<elna::source::definition *> value_definitions($2.size() + $4.size());
|
||||
std::vector<elna::source::definition *>::iterator value_definition = value_definitions.begin();
|
||||
|
||||
for (auto type : $1)
|
||||
{
|
||||
*definition++ = type;
|
||||
}
|
||||
for (auto constant : $2)
|
||||
{
|
||||
*value_definition++ = constant;
|
||||
}
|
||||
for (auto procedure : $3)
|
||||
{
|
||||
*definition++ = procedure;
|
||||
}
|
||||
for (auto variable : $4)
|
||||
{
|
||||
*value_definition++ = variable;
|
||||
}
|
||||
auto tree = new elna::source::program(elna::source::position{},
|
||||
std::move(definitions), std::move(value_definitions), std::move($6));
|
||||
|
||||
driver.tree.reset(tree);
|
||||
}
|
||||
block: constant_part variable_part BEGIN_BLOCK optional_statements END_BLOCK
|
||||
{
|
||||
std::vector<elna::source::definition *> definitions($1.size() + $2.size());
|
||||
std::vector<elna::source::definition *>::iterator definition = definitions.begin();
|
||||
|
||||
for (auto constant : $1)
|
||||
{
|
||||
*definition++ = constant;
|
||||
}
|
||||
for (auto variable : $2)
|
||||
{
|
||||
*definition++ = variable;
|
||||
}
|
||||
$$ = new elna::source::block(elna::source::position{},
|
||||
std::move(definitions), std::move($4));
|
||||
}
|
||||
procedure_definition:
|
||||
PROCEDURE IDENTIFIER formal_parameter_list SEMICOLON block SEMICOLON
|
||||
{
|
||||
$$ = new elna::source::procedure_definition(elna::source::position{},
|
||||
$2, std::move($3), nullptr, $5);
|
||||
}
|
||||
| PROCEDURE IDENTIFIER formal_parameter_list SEMICOLON EXTERN SEMICOLON
|
||||
{
|
||||
$$ = new elna::source::procedure_definition(elna::source::position{},
|
||||
$2, std::move($3), nullptr, nullptr);
|
||||
}
|
||||
| PROCEDURE IDENTIFIER formal_parameter_list COLON type_expression SEMICOLON block SEMICOLON
|
||||
{
|
||||
$$ = new elna::source::procedure_definition(elna::source::position{},
|
||||
$2, std::move($3), $5, $7);
|
||||
}
|
||||
| PROCEDURE IDENTIFIER formal_parameter_list COLON type_expression SEMICOLON EXTERN SEMICOLON
|
||||
{
|
||||
$$ = new elna::source::procedure_definition(elna::source::position{},
|
||||
$2, std::move($3), $5, nullptr);
|
||||
}
|
||||
procedure_definitions:
|
||||
procedure_definition procedure_definitions
|
||||
{
|
||||
std::swap($$, $2);
|
||||
$$.emplace($$.cbegin(), std::move($1));
|
||||
}
|
||||
| procedure_definition { $$.emplace_back(std::move($1)); }
|
||||
procedure_part:
|
||||
/* no procedure definitions */ {}
|
||||
| procedure_definitions { std::swap($$, $1); }
|
||||
assign_statement: designator_expression ASSIGNMENT expression
|
||||
{
|
||||
$$ = new elna::source::assign_statement(elna::source::make_position(@1), $1, $3);
|
||||
}
|
||||
call_expression: IDENTIFIER actual_parameter_list
|
||||
{
|
||||
$$ = new elna::source::call_expression(elna::source::make_position(@1), $1);
|
||||
std::swap($$->arguments(), $2);
|
||||
}
|
||||
while_statement: WHILE expression DO optional_statements END_BLOCK
|
||||
{
|
||||
auto body = new elna::source::compound_statement(elna::source::make_position(@3), std::move($4));
|
||||
$$ = new elna::source::while_statement(elna::source::make_position(@1), $2, body);
|
||||
}
|
||||
if_statement:
|
||||
IF expression THEN optional_statements END_BLOCK
|
||||
{
|
||||
auto then = new elna::source::compound_statement(elna::source::make_position(@3), std::move($4));
|
||||
$$ = new elna::source::if_statement(elna::source::make_position(@1), $2, then);
|
||||
}
|
||||
| IF expression THEN optional_statements ELSE optional_statements END_BLOCK
|
||||
{
|
||||
auto then = new elna::source::compound_statement(elna::source::make_position(@3), std::move($4));
|
||||
auto _else = new elna::source::compound_statement(elna::source::make_position(@5), std::move($6));
|
||||
$$ = new elna::source::if_statement(elna::source::make_position(@1), $2, then, _else);
|
||||
}
|
||||
return_statement:
|
||||
RETURN expression
|
||||
{
|
||||
$$ = new elna::source::return_statement(elna::source::make_position(@1), $2);
|
||||
}
|
||||
literal:
|
||||
INTEGER
|
||||
{
|
||||
$$ = new elna::source::number_literal<std::int32_t>(elna::source::make_position(@1), $1);
|
||||
}
|
||||
| FLOAT
|
||||
{
|
||||
$$ = new elna::source::number_literal<double>(elna::source::make_position(@1), $1);
|
||||
}
|
||||
| BOOLEAN
|
||||
{
|
||||
$$ = new elna::source::number_literal<bool>(elna::source::make_position(@1), $1);
|
||||
}
|
||||
| CHARACTER
|
||||
{
|
||||
$$ = new elna::source::number_literal<unsigned char>(elna::source::make_position(@1), $1.at(0));
|
||||
}
|
||||
| STRING
|
||||
{
|
||||
$$ = new elna::source::string_literal(elna::source::make_position(@1), $1);
|
||||
}
|
||||
pointer:
|
||||
literal { $$ = $1; }
|
||||
| designator_expression { $$ = $1; }
|
||||
| LEFT_PAREN expression RIGHT_PAREN { $$ = std::move($2); }
|
||||
summand:
|
||||
factor { $$ = std::move($1); }
|
||||
| factor MULTIPLICATION factor
|
||||
{
|
||||
$$ = new elna::source::binary_expression(elna::source::make_position(@1),
|
||||
$1, $3, '*');
|
||||
}
|
||||
| factor DIVISION factor
|
||||
{
|
||||
$$ = new elna::source::binary_expression(elna::source::make_position(@1),
|
||||
$1, $3, '/');
|
||||
}
|
||||
factor:
|
||||
AT pointer
|
||||
{
|
||||
$$ = new elna::source::unary_expression(elna::source::make_position(@1), $2, '@');
|
||||
}
|
||||
| NOT pointer
|
||||
{
|
||||
$$ = new elna::source::unary_expression(elna::source::make_position(@1), $2, '!');
|
||||
}
|
||||
| pointer { $$ = $1; }
|
||||
comparand:
|
||||
summand PLUS summand
|
||||
{
|
||||
$$ = new elna::source::binary_expression(elna::source::make_position(@1), $1, $3, '+');
|
||||
}
|
||||
| summand MINUS summand
|
||||
{
|
||||
$$ = new elna::source::binary_expression(elna::source::make_position(@1), $1, $3, '-');
|
||||
}
|
||||
| summand { $$ = std::move($1); }
|
||||
logical_operand:
|
||||
comparand EQUALS comparand
|
||||
{
|
||||
$$ = new elna::source::binary_expression(elna::source::make_position(@1), $1, $3, '=');
|
||||
}
|
||||
| comparand NOT_EQUAL comparand
|
||||
{
|
||||
$$ = new elna::source::binary_expression(elna::source::make_position(@1), $1, $3, 'n');
|
||||
}
|
||||
| comparand LESS_THAN comparand
|
||||
{
|
||||
$$ = new elna::source::binary_expression(elna::source::make_position(@1), $1, $3, '<');
|
||||
}
|
||||
| comparand GREATER_THAN comparand
|
||||
{
|
||||
$$ = new elna::source::binary_expression(elna::source::make_position(@1), $1, $3, '>');
|
||||
}
|
||||
| comparand LESS_EQUAL comparand
|
||||
{
|
||||
$$ = new elna::source::binary_expression(elna::source::make_position(@1), $1, $3, '<');
|
||||
}
|
||||
| comparand GREATER_EQUAL comparand
|
||||
{
|
||||
$$ = new elna::source::binary_expression(elna::source::make_position(@1), $1, $3, '>');
|
||||
}
|
||||
| comparand { $$ = $1; }
|
||||
expression:
|
||||
logical_operand AND logical_operand
|
||||
{
|
||||
$$ = new elna::source::binary_expression(elna::source::make_position(@1), $1, $3, 'a');
|
||||
}
|
||||
| logical_operand OR logical_operand
|
||||
{
|
||||
$$ = new elna::source::binary_expression(elna::source::make_position(@1), $1, $3, 'o');
|
||||
}
|
||||
| logical_operand { $$ = $1; }
|
||||
| call_expression { $$ = $1; }
|
||||
expressions:
|
||||
expression COMMA expressions
|
||||
{
|
||||
std::swap($$, $3);
|
||||
$$.emplace($$.cbegin(), $1);
|
||||
}
|
||||
| expression { $$.emplace_back(std::move($1)); }
|
||||
designator_expression:
|
||||
designator_expression LEFT_SQUARE expression RIGHT_SQUARE
|
||||
{
|
||||
$$ = new elna::source::array_access_expression(elna::source::make_position(@1), $1, $3);
|
||||
}
|
||||
| designator_expression DOT IDENTIFIER
|
||||
{
|
||||
$$ = new elna::source::field_access_expression(elna::source::make_position(@1), $1, $3);
|
||||
}
|
||||
| designator_expression HAT
|
||||
{
|
||||
$$ = new elna::source::dereference_expression(elna::source::make_position(@1), $1);
|
||||
}
|
||||
| IDENTIFIER
|
||||
{
|
||||
$$ = new elna::source::variable_expression(elna::source::make_position(@1), $1);
|
||||
}
|
||||
statement:
|
||||
assign_statement { $$ = $1; }
|
||||
| while_statement { $$ = $1; }
|
||||
| if_statement { $$ = $1; }
|
||||
| return_statement { $$ = $1; }
|
||||
| expression { $$ = new elna::source::expression_statement(elna::source::make_position(@1), $1); }
|
||||
statements:
|
||||
statement SEMICOLON statements
|
||||
{
|
||||
std::swap($$, $3);
|
||||
$$.emplace($$.cbegin(), $1);
|
||||
}
|
||||
| statement { $$.push_back($1); }
|
||||
optional_statements:
|
||||
statements { std::swap($$, $1); }
|
||||
| /* no statements */ {}
|
||||
field_declaration:
|
||||
IDENTIFIER COLON type_expression { $$ = std::make_pair($1, $3); }
|
||||
field_list:
|
||||
field_declaration SEMICOLON field_list
|
||||
{
|
||||
std::swap($$, $3);
|
||||
$$.emplace($$.cbegin(), $1);
|
||||
}
|
||||
| field_declaration { $$.emplace_back($1); }
|
||||
type_expression:
|
||||
ARRAY INTEGER OF type_expression
|
||||
{
|
||||
$$ = new elna::source::array_type_expression(elna::source::make_position(@1), $4, $2);
|
||||
}
|
||||
| POINTER TO type_expression
|
||||
{
|
||||
$$ = new elna::source::pointer_type_expression(elna::source::make_position(@1), $3);
|
||||
}
|
||||
| RECORD field_list END_BLOCK
|
||||
{
|
||||
$$ = new elna::source::record_type_expression(elna::source::make_position(@1), std::move($2));
|
||||
}
|
||||
| UNION field_list END_BLOCK
|
||||
{
|
||||
$$ = new elna::source::union_type_expression(elna::source::make_position(@1), std::move($2));
|
||||
}
|
||||
| IDENTIFIER
|
||||
{
|
||||
$$ = new elna::source::basic_type_expression(elna::source::make_position(@1), $1);
|
||||
}
|
||||
variable_declaration: IDENTIFIER COLON type_expression
|
||||
{
|
||||
$$ = new elna::source::variable_declaration(elna::source::make_position(@1), $1, $3);
|
||||
}
|
||||
variable_declarations:
|
||||
variable_declaration COMMA variable_declarations
|
||||
{
|
||||
std::swap($$, $3);
|
||||
$$.emplace($$.cbegin(), $1);
|
||||
}
|
||||
| variable_declaration { $$.emplace_back(std::move($1)); }
|
||||
variable_part:
|
||||
/* no variable declarations */ {}
|
||||
| VAR variable_declarations SEMICOLON { std::swap($$, $2); }
|
||||
constant_definition: IDENTIFIER EQUALS literal
|
||||
{
|
||||
$$ = new elna::source::constant_definition(elna::source::make_position(@1), $1, $3);
|
||||
}
|
||||
constant_definitions:
|
||||
constant_definition COMMA constant_definitions
|
||||
{
|
||||
std::swap($$, $3);
|
||||
$$.emplace($$.cbegin(), std::move($1));
|
||||
}
|
||||
| constant_definition { $$.emplace_back(std::move($1)); }
|
||||
constant_part:
|
||||
/* no constant definitions */ {}
|
||||
| CONST constant_definitions SEMICOLON { std::swap($$, $2); }
|
||||
type_definition: IDENTIFIER EQUALS type_expression
|
||||
{
|
||||
$$ = new elna::source::type_definition(elna::source::make_position(@1), $1, $3);
|
||||
}
|
||||
type_definitions:
|
||||
type_definition COMMA type_definitions
|
||||
{
|
||||
std::swap($$, $3);
|
||||
$$.emplace($$.cbegin(), std::move($1));
|
||||
}
|
||||
| type_definition { $$.emplace_back(std::move($1)); }
|
||||
type_part:
|
||||
/* no type definitions */ {}
|
||||
| TYPE type_definitions SEMICOLON { std::swap($$, $2); }
|
||||
formal_parameter_list:
|
||||
LEFT_PAREN RIGHT_PAREN {}
|
||||
| LEFT_PAREN variable_declarations RIGHT_PAREN { std::swap($$, $2); }
|
||||
actual_parameter_list:
|
||||
LEFT_PAREN RIGHT_PAREN {}
|
||||
| LEFT_PAREN expressions RIGHT_PAREN { std::swap($$, $2); }
|
||||
%%
|
||||
|
||||
void yy::parser::error(const location_type& loc, const std::string& message)
|
||||
{
|
||||
driver.error(loc, message);
|
||||
}
|
25
source/result.cc
Normal file
25
source/result.cc
Normal file
@ -0,0 +1,25 @@
|
||||
// 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 http://mozilla.org/MPL/2.0/.
|
||||
#include "elna/source/result.h"
|
||||
|
||||
namespace elna
|
||||
{
|
||||
namespace source
|
||||
{
|
||||
error::error(const char *path, const struct position position)
|
||||
: position(position), path(path)
|
||||
{
|
||||
}
|
||||
|
||||
std::size_t error::line() const noexcept
|
||||
{
|
||||
return this->position.line;
|
||||
}
|
||||
|
||||
std::size_t error::column() const noexcept
|
||||
{
|
||||
return this->position.column;
|
||||
}
|
||||
}
|
||||
}
|
83
source/types.cc
Normal file
83
source/types.cc
Normal file
@ -0,0 +1,83 @@
|
||||
// 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 http://mozilla.org/MPL/2.0/.
|
||||
#include <elna/source/types.h>
|
||||
|
||||
namespace elna
|
||||
{
|
||||
namespace source
|
||||
{
|
||||
type::type(const std::size_t byte_size)
|
||||
: byte_size(byte_size)
|
||||
{
|
||||
}
|
||||
|
||||
std::size_t type::size() const noexcept
|
||||
{
|
||||
return this->byte_size;
|
||||
}
|
||||
|
||||
const pointer_type *type::is_pointer_type() const
|
||||
{
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
primitive_type::primitive_type(const std::string& type_name, const std::size_t byte_size)
|
||||
: type(byte_size), m_type_name(type_name)
|
||||
{
|
||||
}
|
||||
|
||||
std::string primitive_type::type_name() const
|
||||
{
|
||||
return m_type_name;
|
||||
}
|
||||
|
||||
pointer_type::pointer_type(std::shared_ptr<const type> base_type, const std::size_t byte_size)
|
||||
: type(byte_size), base_type(base_type)
|
||||
{
|
||||
}
|
||||
|
||||
const pointer_type *pointer_type::is_pointer_type() const
|
||||
{
|
||||
return this;
|
||||
}
|
||||
|
||||
std::string pointer_type::type_name() const
|
||||
{
|
||||
return '^' + base_type->type_name();
|
||||
}
|
||||
|
||||
procedure_type::procedure_type(std::vector<std::shared_ptr<const type>> arguments, const std::size_t byte_size)
|
||||
:type(byte_size), arguments(std::move(arguments))
|
||||
{
|
||||
}
|
||||
|
||||
std::string procedure_type::type_name() const
|
||||
{
|
||||
std::string result{ "proc(" };
|
||||
for (const auto& argument : arguments)
|
||||
{
|
||||
result += argument->type_name() + ',';
|
||||
}
|
||||
result.at(result.size() - 1) = ')';
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
bool operator==(const type& lhs, const type& rhs) noexcept
|
||||
{
|
||||
auto lhs_type = lhs.type_name();
|
||||
auto rhs_type = rhs.type_name();
|
||||
|
||||
return lhs_type == rhs_type;
|
||||
}
|
||||
|
||||
bool operator!=(const type& lhs, const type& rhs) noexcept
|
||||
{
|
||||
return !(lhs == rhs);
|
||||
}
|
||||
|
||||
const primitive_type boolean_type{ "Boolean", 1 };
|
||||
const primitive_type int_type{ "Int", 4 };
|
||||
}
|
||||
}
|
@ -1,3 +0,0 @@
|
||||
const a = 1, b = 2;
|
||||
! a + b
|
||||
.
|
@ -1 +0,0 @@
|
||||
3
|
@ -1 +0,0 @@
|
||||
8
|
@ -1 +0,0 @@
|
||||
1
|
@ -1 +0,0 @@
|
||||
8
|
@ -1 +0,0 @@
|
||||
8
|
@ -1,2 +0,0 @@
|
||||
! (3 + 4) + 1
|
||||
.
|
@ -1,2 +0,0 @@
|
||||
! 5 - 4
|
||||
.
|
@ -1,2 +0,0 @@
|
||||
! 1 + 7
|
||||
.
|
@ -1,2 +0,0 @@
|
||||
! 1 + (3 + 4)
|
||||
.
|
Loading…
x
Reference in New Issue
Block a user