Start a Modula-2 experiment

This commit is contained in:
2022-06-11 22:52:13 +02:00
parent f5c4a27a6d
commit c6ea37173d
26 changed files with 819 additions and 2574 deletions

2
.gitignore vendored
View File

@@ -1,3 +1,3 @@
/.dub/ a.out
/dub.selections.json /dub.selections.json
/build/ /build/

37
README
View File

@@ -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.

63
README.md Normal file
View File

@@ -0,0 +1,63 @@
# 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
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 ")";
## Build
The frontend requires GCC 14.2.0 (not tested with other versions).
Download the GCC source. Copy the contents of this repository into `gcc/elna`
inside GCC. Finally build GCC enabling the frontend with
`--enable-languages=c,c++,elna`. After the installation the compiler can be
invoked with `$prefix/bin/gelna`.
There is also a `Rakefile` that downloads, builds and installs GCC into the
`./build/` subdirectory. The `Rakefile` assumes that ruby and rake, as well as
all GCC dependencies are already available in the system. It works under Linux
and Mac OS. In the latter case GCC is patched with the patches used by Homebrew
(official GCC doesn't support Apple silicon targets). Invoke with
```sh
rake boot
```
See `rake -T` for more tasks. The GCC source is under `build/tools`. The
installation path is `build/host/install`.

125
Rakefile
View File

@@ -2,87 +2,82 @@ require 'pathname'
require 'rake/clean' require 'rake/clean'
require 'open3' require 'open3'
DFLAGS = ['--warn-no-deprecated', '-L/usr/lib64/gcc-12'] M2C = 'gm2' # Modula-2 compiler.
BINARY = 'build/bin/elna' BOOT_OBJECTS = FileList['boot/*.mod']
TESTS = FileList['tests/*.eln'].flat_map do |test| .map do |source|
build = Pathname.new 'build' Pathname.new(source).basename.sub_ext('.o')
test_basename = Pathname.new(test).basename('') end
[build + 'riscv' + test_basename].map { |path| path.sub_ext('').to_path } def source_for_object(out_file)
path = Pathname.new(out_file).relative_path_from('build')
result = ['build/boot']
definition = File.join('boot', path.basename.sub_ext('.def'))
result << definition if File.exist? definition
implementation = path.sub_ext('.mod').to_path
implementation = File.join 'build', implementation unless File.exist? implementation
result << implementation
end end
SOURCES = FileList['source/**/*.d'] directory 'build/boot'
directory 'build/self'
directory 'build'
CLEAN.include 'build' CLEAN.include 'build'
CLEAN.include '.dub'
rule(/build\/riscv\/[^\/\.]+$/ => ->(file) { test_for_out(file, '.o') }) do |t| rule(/build\/.+\.o$/ => ->(file) { source_for_object(file) }) do |t|
sh '/opt/riscv/bin/riscv32-unknown-elf-ld', sources = t.prerequisites.filter { |f| f.end_with? '.mod' }
'-o', t.name,
'-L/opt/riscv/lib/gcc/riscv32-unknown-elf/11.1.0', sh M2C, '-c', '-I', 'boot', '-o', t.name, *sources
'-L/opt/riscv/riscv32-unknown-elf/lib',
'/opt/riscv/riscv32-unknown-elf/lib/crt0.o',
'/opt/riscv/lib/gcc/riscv32-unknown-elf/11.1.0/crtbegin.o',
t.source,
'--start-group', '-lc', '-lgloss', '--end-group',
'/opt/riscv/lib/gcc/riscv32-unknown-elf/11.1.0/crtend.o'
end end
rule(/build\/riscv\/.+\.o$/ => ->(file) { test_for_object(file, '.eln') }) do |t| rule(/build\/self\/.+\.mod$/ => [
Pathname.new(t.name).dirname.mkpath 'build/self', 'build/boot/Compiler',
sh BINARY, '-o', t.name, t.source ->(file) { File.join('boot', Pathname.new(file).basename) }
end ]) do |t|
sources, compiler = t.prerequisites
.reject { |f| File.directory? f }
.partition { |f| f.end_with? '.mod' }
file BINARY => SOURCES do |t| File.open t.name, 'w' do |output|
sh({ 'DFLAGS' => (DFLAGS * ' ') }, 'dub', 'build', '--compiler=gdc-12') puts
end puts(compiler * ' ')
task default: BINARY Open3.popen2(*compiler) do |cl_in, cl_out|
cl_in.write File.read(*sources)
cl_in.close
desc 'Run all tests and check the results' IO.copy_stream cl_out, output
task test: TESTS cl_out.close
task test: BINARY do
TESTS.each do |test|
expected = Pathname
.new(test)
.sub_ext('.txt')
.sub(/^build\/[[:alpha:]]+\//, 'tests/expectations/')
.read
.to_i
puts "Running #{test}"
if test.include? '/riscv/'
system('/opt/riscv/bin/spike',
'/opt/riscv/riscv32-unknown-elf/bin/pk', test,
{ out: '/dev/null' })
else
raise 'Unsupported test platform'
end end
actual = $?.exitstatus
fail "#{test}: Expected #{expected}, got #{actual}" unless expected == actual
end end
end end
desc 'Run unittest blocks' ['boot', 'self'].each do |sub|
task unittest: SOURCES do |t| compiler_binary = Pathname.new('build') + sub + 'Compiler'
sh('dub', 'test', '--compiler=gdc-12')
file compiler_binary.to_path => BOOT_OBJECTS.map { |file| File.join('build', sub, file) } do |t|
sh M2C, '-o', t.name, *t.prerequisites
end
compiler_object = compiler_binary.sub_ext('.o')
file compiler_object.to_path => source_for_object(compiler_object) do |t|
sources = t.prerequisites.filter { |f| f.end_with? '.mod' }
sh M2C, '-fscaffold-main', '-c', '-I', 'boot', '-o', t.name, *sources
end
end end
def test_for_object(out_file, extension) task default: 'build/self/Compiler'
test_source = Pathname task default: 'build/self/Compiler.mod'
.new(out_file) task default: 'boot/Compiler.mod'
.sub_ext(extension) task :default do |t|
.sub(/^build\/[[:alpha:]]+\//, 'tests/') exe, previous_output, source = t.prerequisites
.to_path
[test_source, BINARY]
end
def test_for_out(out_file, extension) cat_arguments = ['cat', source]
Pathname diff_arguments = ['diff', '-Nur', '--text', previous_output, '-']
.new(out_file)
.sub_ext(extension) puts [cat_arguments * ' ', exe, diff_arguments * ' '].join(' | ')
.to_path Open3.pipeline(cat_arguments, exe, diff_arguments)
end end

28
boot/Compiler.mod Normal file
View File

@@ -0,0 +1,28 @@
MODULE Compiler;
FROM Terminal IMPORT WriteLn;
FROM FIO IMPORT WriteNBytes, StdIn, StdOut;
FROM SYSTEM IMPORT ADR, ADDRESS;
FROM Lexer IMPORT Lexer, LexerToken, LexerDestroy, LexerInitialize, LexerLex;
VAR
ALexer: Lexer;
Index, WrittenBytes: CARDINAL;
Token: LexerToken;
BEGIN
LexerInitialize(ADR(ALexer), StdIn);
Token := LexerLex(ADR(ALexer));
WHILE ALexer.Current^ <> CHAR(0) DO
WrittenBytes := WriteNBytes(StdOut, ADDRESS(ALexer.Current - ALexer.Start), ALexer.Start);
IF ALexer.Current^ <> '.' THEN
WriteLn()
END;
Token := LexerLex(ADR(ALexer));
END;
LexerDestroy(ADR(ALexer));
END Compiler.

91
boot/Lexer.def Normal file
View File

@@ -0,0 +1,91 @@
DEFINITION MODULE Lexer;
FROM FIO IMPORT File;
TYPE
PLexerBuffer = POINTER TO CHAR;
Lexer = RECORD
Input: File;
Buffer: PLexerBuffer;
Size: CARDINAL;
Length: CARDINAL;
Start: PLexerBuffer;
Current: PLexerBuffer
END;
PLexer = POINTER TO Lexer;
LexerKind = (
lexerKindUnknown,
lexerKindIdentifier,
lexerKindIf,
lexerKindThen,
lexerKindElse,
lexerKindElsif,
lexerKindWhile,
lexerKindDo,
lexerKindProc,
lexerKindBegin,
lexerKindEnd,
lexerKindExtern,
lexerKindConst,
lexerKindVar,
lexerKindCase,
lexerKindOf,
lexerKindType,
lexerKindRecord,
lexerKindUnion,
lexerKindPipe,
lexerKindTo,
lexerKindBoolean,
lexerKindNull,
lexerKindAnd,
lexerKindOr,
lexerKindNot,
lexerKindReturn,
lexerKindCast,
lexerKindShiftLeft,
lexerKindShiftRight,
lexerKindLeftParen,
lexerKindRightParen,
lexerKindLeftSquare,
lexerKindRightSquare,
lexerKindGreaterEqual,
lexerKindLessEqual,
lexerKindGreaterThan,
lexerKindLessThan,
lexerKindNotEqual,
lexerKindEqual,
lexerKindSemicolon,
lexerKindDot,
lexerKindComma,
lexerKindPlus,
lexerKindMinus,
lexerKindMultiplication,
lexerKindDivision,
lexerKindRemainder,
lexerKindAssignment,
lexerKindColon,
lexerKindHat,
lexerKindAt,
lexerKindComment,
lexerKindInteger,
lexerKindWord,
lexerKindCharacter,
lexerKindString,
lexerKindDefer,
lexerKindExclamation,
lexerKindArrow,
lexerKindTrait,
lexerKindProgram,
lexerKindModule,
lexerKindImport
);
LexerToken = RECORD
kind: LexerKind
END;
PLexerToken = POINTER TO LexerToken;
PROCEDURE LexerInitialize(ALexer: PLexer; Input: File);
PROCEDURE LexerDestroy(ALexer: PLexer);
PROCEDURE LexerLex(ALexer: PLexer): LexerToken;
END Lexer.

576
boot/Lexer.mod Normal file
View File

@@ -0,0 +1,576 @@
IMPLEMENTATION MODULE Lexer;
FROM FIO IMPORT ReadNBytes;
FROM SYSTEM IMPORT ADR;
FROM Storage IMPORT DEALLOCATE, ALLOCATE;
FROM MemUtils IMPORT MemZero;
CONST
ChunkSize = 32768;
TYPE
(*
* Classification table assigns each possible character to a group (class). All
* characters of the same group a handled equivalently.
*
* Classification:
*)
TransitionClass = (
transitionClassInvalid,
transitionClassDigit,
transitionClassAlpha,
transitionClassSpace,
transitionClassColon,
transitionClassEquals,
transitionClassLeftParen,
transitionClassRightParen,
transitionClassAsterisk,
transitionClassUnderscore,
transitionClassSingle,
transitionClassHex,
transitionClassZero,
transitionClassX,
transitionClassEof,
transitionClassDot,
transitionClassMinus,
transitionClassQuote,
transitionClassGreater,
transitionClassLess,
transitionClassOther
);
TransitionState = (
transitionStateStart,
transitionStateColon,
transitionStateIdentifier,
transitionStateDecimal,
transitionStateGreater,
transitionStateMinus,
transitionStateLeftParen,
transitionStateLess,
transitionStateHexadecimal,
transitionStateComment,
transitionStateClosingComment,
transitionStateString,
transitionStateLeadingZero,
transitionStateHexadecimalPrefix,
transitionStateEnd
);
TransitionAction = PROCEDURE(PLexer, PLexerToken);
Transition = RECORD
Action: TransitionAction;
NextState: TransitionState
END;
VAR
Classification: ARRAY[1..128] OF TransitionClass;
Transitions: ARRAY[0..MAX(TransitionState)] OF ARRAY[0..MAX(TransitionClass)] OF Transition;
PROCEDURE InitializeClassification();
BEGIN
Classification[1] := transitionClassEof; (* NUL *)
Classification[2] := transitionClassInvalid; (* SOH *)
Classification[3] := transitionClassInvalid; (* STX *)
Classification[4] := transitionClassInvalid; (* ETX *)
Classification[5] := transitionClassInvalid; (* EOT *)
Classification[6] := transitionClassInvalid; (* EMQ *)
Classification[7] := transitionClassInvalid; (* ACK *)
Classification[8] := transitionClassInvalid; (* BEL *)
Classification[9] := transitionClassInvalid; (* BS *)
Classification[10] := transitionClassSpace; (* HT *)
Classification[11] := transitionClassSpace; (* LF *)
Classification[12] := transitionClassInvalid; (* VT *)
Classification[13] := transitionClassInvalid; (* FF *)
Classification[14] := transitionClassSpace; (* CR *)
Classification[15] := transitionClassInvalid; (* SO *)
Classification[16] := transitionClassInvalid; (* SI *)
Classification[17] := transitionClassInvalid; (* DLE *)
Classification[18] := transitionClassInvalid; (* DC1 *)
Classification[19] := transitionClassInvalid; (* DC2 *)
Classification[20] := transitionClassInvalid; (* DC3 *)
Classification[21] := transitionClassInvalid; (* DC4 *)
Classification[22] := transitionClassInvalid; (* NAK *)
Classification[23] := transitionClassInvalid; (* SYN *)
Classification[24] := transitionClassInvalid; (* ETB *)
Classification[25] := transitionClassInvalid; (* CAN *)
Classification[26] := transitionClassInvalid; (* EM *)
Classification[27] := transitionClassInvalid; (* SUB *)
Classification[28] := transitionClassInvalid; (* ESC *)
Classification[29] := transitionClassInvalid; (* FS *)
Classification[30] := transitionClassInvalid; (* GS *)
Classification[31] := transitionClassInvalid; (* RS *)
Classification[32] := transitionClassInvalid; (* US *)
Classification[33] := transitionClassSpace; (* Space *)
Classification[34] := transitionClassSingle; (* ! *)
Classification[35] := transitionClassQuote; (* " *)
Classification[36] := transitionClassOther; (* # *)
Classification[37] := transitionClassOther; (* $ *)
Classification[38] := transitionClassSingle; (* % *)
Classification[39] := transitionClassSingle; (* & *)
Classification[40] := transitionClassQuote; (* ' *)
Classification[41] := transitionClassLeftParen; (* ( *)
Classification[42] := transitionClassRightParen; (* ) *)
Classification[43] := transitionClassAsterisk; (* * *)
Classification[44] := transitionClassSingle; (* + *)
Classification[45] := transitionClassSingle; (* , *)
Classification[46] := transitionClassMinus; (* - *)
Classification[47] := transitionClassDot; (* . *)
Classification[48] := transitionClassSingle; (* / *)
Classification[49] := transitionClassZero; (* 0 *)
Classification[50] := transitionClassDigit; (* 1 *)
Classification[51] := transitionClassDigit; (* 2 *)
Classification[52] := transitionClassDigit; (* 3 *)
Classification[53] := transitionClassDigit; (* 4 *)
Classification[54] := transitionClassDigit; (* 5 *)
Classification[55] := transitionClassDigit; (* 6 *)
Classification[56] := transitionClassDigit; (* 7 *)
Classification[57] := transitionClassDigit; (* 8 *)
Classification[58] := transitionClassDigit; (* 9 *)
Classification[59] := transitionClassColon; (* : *)
Classification[60] := transitionClassSingle; (* ; *)
Classification[61] := transitionClassLess; (* < *)
Classification[62] := transitionClassEquals; (* = *)
Classification[63] := transitionClassGreater; (* > *)
Classification[64] := transitionClassOther; (* ? *)
Classification[65] := transitionClassSingle; (* @ *)
Classification[66] := transitionClassAlpha; (* A *)
Classification[67] := transitionClassAlpha; (* B *)
Classification[68] := transitionClassAlpha; (* C *)
Classification[69] := transitionClassAlpha; (* D *)
Classification[70] := transitionClassAlpha; (* E *)
Classification[71] := transitionClassAlpha; (* F *)
Classification[72] := transitionClassAlpha; (* G *)
Classification[73] := transitionClassAlpha; (* H *)
Classification[74] := transitionClassAlpha; (* I *)
Classification[75] := transitionClassAlpha; (* J *)
Classification[76] := transitionClassAlpha; (* K *)
Classification[77] := transitionClassAlpha; (* L *)
Classification[78] := transitionClassAlpha; (* M *)
Classification[79] := transitionClassAlpha; (* N *)
Classification[80] := transitionClassAlpha; (* O *)
Classification[81] := transitionClassAlpha; (* P *)
Classification[82] := transitionClassAlpha; (* Q *)
Classification[83] := transitionClassAlpha; (* R *)
Classification[84] := transitionClassAlpha; (* S *)
Classification[85] := transitionClassAlpha; (* T *)
Classification[86] := transitionClassAlpha; (* U *)
Classification[87] := transitionClassAlpha; (* V *)
Classification[88] := transitionClassAlpha; (* W *)
Classification[89] := transitionClassAlpha; (* X *)
Classification[90] := transitionClassAlpha; (* Y *)
Classification[91] := transitionClassAlpha; (* Z *)
Classification[92] := transitionClassSingle; (* [ *)
Classification[93] := transitionClassOther; (* \ *)
Classification[94] := transitionClassSingle; (* ] *)
Classification[95] := transitionClassSingle; (* ^ *)
Classification[96] := transitionClassUnderscore; (* _ *)
Classification[97] := transitionClassOther; (* ` *)
Classification[98] := transitionClassHex; (* a *)
Classification[99] := transitionClassHex; (* b *)
Classification[100] := transitionClassHex; (* c *)
Classification[101] := transitionClassHex; (* d *)
Classification[102] := transitionClassHex; (* e *)
Classification[103] := transitionClassHex; (* f *)
Classification[104] := transitionClassAlpha; (* g *)
Classification[105] := transitionClassAlpha; (* h *)
Classification[106] := transitionClassAlpha; (* i *)
Classification[107] := transitionClassAlpha; (* j *)
Classification[108] := transitionClassAlpha; (* k *)
Classification[109] := transitionClassAlpha; (* l *)
Classification[110] := transitionClassAlpha; (* m *)
Classification[111] := transitionClassAlpha; (* n *)
Classification[112] := transitionClassAlpha; (* o *)
Classification[113] := transitionClassAlpha; (* p *)
Classification[114] := transitionClassAlpha; (* q *)
Classification[115] := transitionClassAlpha; (* r *)
Classification[116] := transitionClassAlpha; (* s *)
Classification[117] := transitionClassAlpha; (* t *)
Classification[118] := transitionClassAlpha; (* u *)
Classification[119] := transitionClassAlpha; (* v *)
Classification[120] := transitionClassAlpha; (* w *)
Classification[121] := transitionClassX; (* x *)
Classification[122] := transitionClassAlpha; (* y *)
Classification[123] := transitionClassAlpha; (* z *)
Classification[124] := transitionClassOther; (* { *)
Classification[125] := transitionClassSingle; (* | *)
Classification[126] := transitionClassOther; (* } *)
Classification[127] := transitionClassSingle; (* ~ *)
Classification[128] := transitionClassInvalid (* DEL *)
END InitializeClassification;
(* Reached the end of file. *)
PROCEDURE TransitionActionEof(ALexer: PLexer; AToken: PLexerToken);
END TransitionActionEof;
(* Add the character to the token currently read and advance to the next character. *)
PROCEDURE TransitionActionAccumulate(ALexer: PLexer; AToken: PLexerToken);
BEGIN
INC(ALexer^.Current)
END TransitionActionAccumulate;
(* The current character is not a part of the token. Finish the token already
* read. Don't advance to the next character. *)
PROCEDURE TransitionActionFinalize(ALexer: PLexer; AToken: PLexerToken);
END TransitionActionFinalize;
(* Skip a space. *)
PROCEDURE TransitionActionSkip(ALexer: PLexer; AToken: PLexerToken);
BEGIN
INC(ALexer^.Current);
INC(ALexer^.Start)
END TransitionActionSkip;
(* 0x04. Delimited string action. *)
PROCEDURE TransitionActionDelimited(ALexer: PLexer; AToken: PLexerToken);
BEGIN
INC(ALexer^.Current)
END TransitionActionDelimited;
(* Finalize keyword or identifier. *)
PROCEDURE TransitionActionKeyId(ALexer: PLexer; AToken: PLexerToken);
END TransitionActionKeyId;
(* Action for tokens containing only one character. The character cannot be
* followed by other characters forming a composite token. *)
PROCEDURE TransitionActionSingle(ALexer: PLexer; AToken: PLexerToken);
BEGIN
INC(ALexer^.Current)
END TransitionActionSingle;
(* An action for tokens containing multiple characters. *)
PROCEDURE TransitionActionComposite(ALexer: PLexer; AToken: PLexerToken);
BEGIN
INC(ALexer^.Current)
END TransitionActionComposite;
(* Handle an integer literal. *)
PROCEDURE TransitionActionInteger(ALexer: PLexer; AToken: PLexerToken);
END TransitionActionInteger;
PROCEDURE SetDefaultTransition(currentState: TransitionState; DefaultAction: TransitionAction; NextState: TransitionState);
VAR DefaultTransition: Transition;
BEGIN
DefaultTransition.Action := DefaultAction;
DefaultTransition.NextState := NextState;
Transitions[ORD(currentState)][ORD(transitionClassInvalid)] := DefaultTransition;
Transitions[ORD(currentState)][ORD(transitionClassDigit)] := DefaultTransition;
Transitions[ORD(currentState)][ORD(transitionClassAlpha)] := DefaultTransition;
Transitions[ORD(currentState)][ORD(transitionClassSpace)] := DefaultTransition;
Transitions[ORD(currentState)][ORD(transitionClassColon)] := DefaultTransition;
Transitions[ORD(currentState)][ORD(transitionClassEquals)] := DefaultTransition;
Transitions[ORD(currentState)][ORD(transitionClassLeftParen)] := DefaultTransition;
Transitions[ORD(currentState)][ORD(transitionClassRightParen)] := DefaultTransition;
Transitions[ORD(currentState)][ORD(transitionClassAsterisk)] := DefaultTransition;
Transitions[ORD(currentState)][ORD(transitionClassUnderscore)] := DefaultTransition;
Transitions[ORD(currentState)][ORD(transitionClassSingle)] := DefaultTransition;
Transitions[ORD(currentState)][ORD(transitionClassHex)] := DefaultTransition;
Transitions[ORD(currentState)][ORD(transitionClassZero)] := DefaultTransition;
Transitions[ORD(currentState)][ORD(transitionClassX)] := DefaultTransition;
Transitions[ORD(currentState)][ORD(transitionClassEof)] := DefaultTransition;
Transitions[ORD(currentState)][ORD(transitionClassDot)] := DefaultTransition;
Transitions[ORD(currentState)][ORD(transitionClassMinus)] := DefaultTransition;
Transitions[ORD(currentState)][ORD(transitionClassQuote)] := DefaultTransition;
Transitions[ORD(currentState)][ORD(transitionClassGreater)] := DefaultTransition;
Transitions[ORD(currentState)][ORD(transitionClassLess)] := DefaultTransition;
Transitions[ORD(currentState)][ORD(transitionClassOther)] := DefaultTransition;
END SetDefaultTransition;
(*
* The transition table describes transitions from one state to another, given
* a symbol (character class).
*
* The table has m rows and n columns, where m is the amount of states and n is
* the amount of classes. So given the current state and a classified character
* the table can be used to look up the next state.
*
* Each cell is a word long.
* - The least significant byte of the word is a row number (beginning with 0).
* It specifies the target state. "ff" means that this is an end state and no
* transition is possible.
* - The next byte is the action that should be performed when transitioning.
* For the meaning of actions see labels in the lex_next function, which
* handles each action.
*)
PROCEDURE InitializeTransitions();
BEGIN
(* Start state. *)
Transitions[ORD(transitionStateStart)][ORD(transitionClassInvalid)].Action := NIL;
Transitions[ORD(transitionStateStart)][ORD(transitionClassInvalid)].NextState := transitionStateEnd;
Transitions[ORD(transitionStateStart)][ORD(transitionClassDigit)].Action := TransitionActionAccumulate;
Transitions[ORD(transitionStateStart)][ORD(transitionClassDigit)].NextState := transitionStateDecimal;
Transitions[ORD(transitionStateStart)][ORD(transitionClassAlpha)].Action := TransitionActionAccumulate;
Transitions[ORD(transitionStateStart)][ORD(transitionClassAlpha)].NextState := transitionStateIdentifier;
Transitions[ORD(transitionStateStart)][ORD(transitionClassSpace)].Action := TransitionActionSkip;
Transitions[ORD(transitionStateStart)][ORD(transitionClassSpace)].NextState := transitionStateStart;
Transitions[ORD(transitionStateStart)][ORD(transitionClassColon)].Action := TransitionActionAccumulate;
Transitions[ORD(transitionStateStart)][ORD(transitionClassColon)].NextState := transitionStateColon;
Transitions[ORD(transitionStateStart)][ORD(transitionClassEquals)].Action := TransitionActionSingle;
Transitions[ORD(transitionStateStart)][ORD(transitionClassEquals)].NextState := transitionStateEnd;
Transitions[ORD(transitionStateStart)][ORD(transitionClassLeftParen)].Action := TransitionActionAccumulate;
Transitions[ORD(transitionStateStart)][ORD(transitionClassLeftParen)].NextState := transitionStateLeftParen;
Transitions[ORD(transitionStateStart)][ORD(transitionClassRightParen)].Action := TransitionActionSingle;
Transitions[ORD(transitionStateStart)][ORD(transitionClassRightParen)].NextState := transitionStateEnd;
Transitions[ORD(transitionStateStart)][ORD(transitionClassAsterisk)].Action := TransitionActionSingle;
Transitions[ORD(transitionStateStart)][ORD(transitionClassAsterisk)].NextState := transitionStateEnd;
Transitions[ORD(transitionStateStart)][ORD(transitionClassUnderscore)].Action := TransitionActionAccumulate;
Transitions[ORD(transitionStateStart)][ORD(transitionClassUnderscore)].NextState := transitionStateIdentifier;
Transitions[ORD(transitionStateStart)][ORD(transitionClassSingle)].Action := TransitionActionSingle;
Transitions[ORD(transitionStateStart)][ORD(transitionClassSingle)].NextState := transitionStateEnd;
Transitions[ORD(transitionStateStart)][ORD(transitionClassHex)].Action := TransitionActionAccumulate;
Transitions[ORD(transitionStateStart)][ORD(transitionClassHex)].NextState := transitionStateIdentifier;
Transitions[ORD(transitionStateStart)][ORD(transitionClassZero)].Action := TransitionActionAccumulate;
Transitions[ORD(transitionStateStart)][ORD(transitionClassZero)].NextState := transitionStateLeadingZero;
Transitions[ORD(transitionStateStart)][ORD(transitionClassX)].Action := TransitionActionAccumulate;
Transitions[ORD(transitionStateStart)][ORD(transitionClassX)].NextState := transitionStateIdentifier;
Transitions[ORD(transitionStateStart)][ORD(transitionClassEof)].Action := TransitionActionEof;
Transitions[ORD(transitionStateStart)][ORD(transitionClassEof)].NextState := transitionStateEnd;
Transitions[ORD(transitionStateStart)][ORD(transitionClassDot)].Action := TransitionActionSingle;
Transitions[ORD(transitionStateStart)][ORD(transitionClassDot)].NextState := transitionStateEnd;
Transitions[ORD(transitionStateStart)][ORD(transitionClassMinus)].Action := TransitionActionAccumulate;
Transitions[ORD(transitionStateStart)][ORD(transitionClassMinus)].NextState := transitionStateMinus;
Transitions[ORD(transitionStateStart)][ORD(transitionClassQuote)].Action := TransitionActionAccumulate;
Transitions[ORD(transitionStateStart)][ORD(transitionClassQuote)].NextState := transitionStateString;
Transitions[ORD(transitionStateStart)][ORD(transitionClassGreater)].Action := TransitionActionAccumulate;
Transitions[ORD(transitionStateStart)][ORD(transitionClassGreater)].NextState := transitionStateGreater;
Transitions[ORD(transitionStateStart)][ORD(transitionClassLess)].Action := TransitionActionAccumulate;
Transitions[ORD(transitionStateStart)][ORD(transitionClassLess)].NextState := transitionStateLess;
Transitions[ORD(transitionStateStart)][ORD(transitionClassOther)].Action := NIL;
Transitions[ORD(transitionStateStart)][ORD(transitionClassOther)].NextState := transitionStateEnd;
(* Colon state. *)
SetDefaultTransition(transitionStateColon, TransitionActionFinalize, transitionStateEnd);
Transitions[ORD(transitionStateColon)][ORD(transitionClassEquals)].Action := TransitionActionComposite;
Transitions[ORD(transitionStateColon)][ORD(transitionClassEquals)].NextState := transitionStateEnd;
(* Identifier state. *)
SetDefaultTransition(transitionStateIdentifier, TransitionActionKeyId, transitionStateEnd);
Transitions[ORD(transitionStateIdentifier)][ORD(transitionClassDigit)].Action := TransitionActionAccumulate;
Transitions[ORD(transitionStateIdentifier)][ORD(transitionClassDigit)].NextState := transitionStateIdentifier;
Transitions[ORD(transitionStateIdentifier)][ORD(transitionClassAlpha)].Action := TransitionActionAccumulate;
Transitions[ORD(transitionStateIdentifier)][ORD(transitionClassAlpha)].NextState := transitionStateIdentifier;
Transitions[ORD(transitionStateIdentifier)][ORD(transitionClassUnderscore)].Action := TransitionActionAccumulate;
Transitions[ORD(transitionStateIdentifier)][ORD(transitionClassUnderscore)].NextState := transitionStateIdentifier;
Transitions[ORD(transitionStateIdentifier)][ORD(transitionClassHex)].Action := TransitionActionAccumulate;
Transitions[ORD(transitionStateIdentifier)][ORD(transitionClassHex)].NextState := transitionStateIdentifier;
Transitions[ORD(transitionStateIdentifier)][ORD(transitionClassZero)].Action := TransitionActionAccumulate;
Transitions[ORD(transitionStateIdentifier)][ORD(transitionClassZero)].NextState := transitionStateIdentifier;
Transitions[ORD(transitionStateIdentifier)][ORD(transitionClassX)].Action := TransitionActionAccumulate;
Transitions[ORD(transitionStateIdentifier)][ORD(transitionClassX)].NextState := transitionStateIdentifier;
(* Decimal state. *)
SetDefaultTransition(transitionStateDecimal, TransitionActionInteger, transitionStateEnd);
Transitions[ORD(transitionStateDecimal)][ORD(transitionClassDigit)].Action := TransitionActionAccumulate;
Transitions[ORD(transitionStateDecimal)][ORD(transitionClassDigit)].NextState := transitionStateDecimal;
Transitions[ORD(transitionStateDecimal)][ORD(transitionClassAlpha)].Action := NIL;
Transitions[ORD(transitionStateDecimal)][ORD(transitionClassAlpha)].NextState := transitionStateEnd;
Transitions[ORD(transitionStateDecimal)][ORD(transitionClassUnderscore)].Action := NIL;
Transitions[ORD(transitionStateDecimal)][ORD(transitionClassUnderscore)].NextState := transitionStateEnd;
Transitions[ORD(transitionStateDecimal)][ORD(transitionClassHex)].Action := NIL;
Transitions[ORD(transitionStateDecimal)][ORD(transitionClassHex)].NextState := transitionStateEnd;
Transitions[ORD(transitionStateDecimal)][ORD(transitionClassZero)].Action := TransitionActionAccumulate;
Transitions[ORD(transitionStateDecimal)][ORD(transitionClassZero)].NextState := transitionStateDecimal;
Transitions[ORD(transitionStateDecimal)][ORD(transitionClassX)].Action := NIL;
Transitions[ORD(transitionStateDecimal)][ORD(transitionClassX)].NextState := transitionStateEnd;
(* Greater state. *)
SetDefaultTransition(transitionStateGreater, TransitionActionFinalize, transitionStateEnd);
Transitions[ORD(transitionStateGreater)][ORD(transitionClassEquals)].Action := TransitionActionDelimited;
Transitions[ORD(transitionStateGreater)][ORD(transitionClassEquals)].NextState := transitionStateEnd;
Transitions[ORD(transitionStateGreater)][ORD(transitionClassGreater)].Action := TransitionActionDelimited;
Transitions[ORD(transitionStateGreater)][ORD(transitionClassGreater)].NextState := transitionStateEnd;
(* Minus state. *)
SetDefaultTransition(transitionStateMinus, TransitionActionFinalize, transitionStateEnd);
Transitions[ORD(transitionStateMinus)][ORD(transitionClassGreater)].Action := TransitionActionDelimited;
Transitions[ORD(transitionStateMinus)][ORD(transitionClassGreater)].NextState := transitionStateEnd;
(* Left paren state. *)
SetDefaultTransition(transitionStateLeftParen, TransitionActionFinalize, transitionStateEnd);
Transitions[ORD(transitionStateLeftParen)][ORD(transitionClassAsterisk)].Action := TransitionActionAccumulate;
Transitions[ORD(transitionStateLeftParen)][ORD(transitionClassAsterisk)].NextState := transitionStateComment;
(* Less state. *)
SetDefaultTransition(transitionStateLess, TransitionActionSingle, transitionStateEnd);
Transitions[ORD(transitionStateLess)][ORD(transitionClassGreater)].Action := TransitionActionComposite;
Transitions[ORD(transitionStateLess)][ORD(transitionClassGreater)].NextState := transitionStateEnd;
Transitions[ORD(transitionStateLess)][ORD(transitionClassLess)].Action := TransitionActionComposite;
Transitions[ORD(transitionStateLess)][ORD(transitionClassLess)].NextState := transitionStateEnd;
(* Hexadecimal after 0x. *)
SetDefaultTransition(transitionStateHexadecimal, TransitionActionInteger, transitionStateEnd);
Transitions[ORD(transitionStateHexadecimal)][ORD(transitionClassDigit)].Action := TransitionActionAccumulate;
Transitions[ORD(transitionStateHexadecimal)][ORD(transitionClassDigit)].NextState := transitionStateHexadecimal;
Transitions[ORD(transitionStateHexadecimal)][ORD(transitionClassAlpha)].Action := NIL;
Transitions[ORD(transitionStateHexadecimal)][ORD(transitionClassAlpha)].NextState := transitionStateEnd;
Transitions[ORD(transitionStateHexadecimal)][ORD(transitionClassUnderscore)].Action := NIL;
Transitions[ORD(transitionStateHexadecimal)][ORD(transitionClassUnderscore)].NextState := transitionStateEnd;
Transitions[ORD(transitionStateHexadecimal)][ORD(transitionClassHex)].Action := TransitionActionAccumulate;
Transitions[ORD(transitionStateHexadecimal)][ORD(transitionClassHex)].NextState := transitionStateHexadecimal;
Transitions[ORD(transitionStateHexadecimal)][ORD(transitionClassZero)].Action := TransitionActionAccumulate;
Transitions[ORD(transitionStateHexadecimal)][ORD(transitionClassZero)].NextState := transitionStateHexadecimal;
Transitions[ORD(transitionStateHexadecimal)][ORD(transitionClassX)].Action := NIL;
Transitions[ORD(transitionStateHexadecimal)][ORD(transitionClassX)].NextState := transitionStateEnd;
(* Comment. *)
SetDefaultTransition(transitionStateComment, TransitionActionAccumulate, transitionStateComment);
Transitions[ORD(transitionStateComment)][ORD(transitionClassAsterisk)].Action := TransitionActionAccumulate;
Transitions[ORD(transitionStateComment)][ORD(transitionClassAsterisk)].NextState := transitionStateClosingComment;
Transitions[ORD(transitionStateComment)][ORD(transitionClassEof)].Action := NIL;
Transitions[ORD(transitionStateComment)][ORD(transitionClassEof)].NextState := transitionStateEnd;
(* Closing comment. *)
SetDefaultTransition(transitionStateClosingComment, TransitionActionAccumulate, transitionStateComment);
Transitions[ORD(transitionStateClosingComment)][ORD(transitionClassInvalid)].Action := NIL;
Transitions[ORD(transitionStateClosingComment)][ORD(transitionClassInvalid)].NextState := transitionStateEnd;
Transitions[ORD(transitionStateClosingComment)][ORD(transitionClassRightParen)].Action := TransitionActionDelimited;
Transitions[ORD(transitionStateClosingComment)][ORD(transitionClassRightParen)].NextState := transitionStateEnd;
Transitions[ORD(transitionStateClosingComment)][ORD(transitionClassAsterisk)].Action := TransitionActionAccumulate;
Transitions[ORD(transitionStateClosingComment)][ORD(transitionClassAsterisk)].NextState := transitionStateClosingComment;
Transitions[ORD(transitionStateClosingComment)][ORD(transitionClassEof)].Action := NIL;
Transitions[ORD(transitionStateClosingComment)][ORD(transitionClassEof)].NextState := transitionStateEnd;
(* String. *)
SetDefaultTransition(transitionStateString, TransitionActionAccumulate, transitionStateString);
Transitions[ORD(transitionStateString)][ORD(transitionClassInvalid)].Action := NIL;
Transitions[ORD(transitionStateString)][ORD(transitionClassInvalid)].NextState := transitionStateEnd;
Transitions[ORD(transitionStateString)][ORD(transitionClassEof)].Action := NIL;
Transitions[ORD(transitionStateString)][ORD(transitionClassEof)].NextState := transitionStateEnd;
Transitions[ORD(transitionStateString)][ORD(transitionClassQuote)].Action := TransitionActionDelimited;
Transitions[ORD(transitionStateString)][ORD(transitionClassQuote)].NextState := transitionStateEnd;
(* Leading zero. *)
SetDefaultTransition(transitionStateLeadingZero, TransitionActionInteger, transitionStateEnd);
Transitions[ORD(transitionStateLeadingZero)][ORD(transitionClassDigit)].Action := NIL;
Transitions[ORD(transitionStateLeadingZero)][ORD(transitionClassDigit)].NextState := transitionStateEnd;
Transitions[ORD(transitionStateLeadingZero)][ORD(transitionClassAlpha)].Action := NIL;
Transitions[ORD(transitionStateLeadingZero)][ORD(transitionClassAlpha)].NextState := transitionStateEnd;
Transitions[ORD(transitionStateLeadingZero)][ORD(transitionClassUnderscore)].Action := NIL;
Transitions[ORD(transitionStateLeadingZero)][ORD(transitionClassUnderscore)].NextState := transitionStateEnd;
Transitions[ORD(transitionStateLeadingZero)][ORD(transitionClassHex)].Action := NIL;
Transitions[ORD(transitionStateLeadingZero)][ORD(transitionClassHex)].NextState := transitionStateEnd;
Transitions[ORD(transitionStateLeadingZero)][ORD(transitionClassZero)].Action := NIL;
Transitions[ORD(transitionStateLeadingZero)][ORD(transitionClassZero)].NextState := transitionStateEnd;
Transitions[ORD(transitionStateLeadingZero)][ORD(transitionClassX)].Action := TransitionActionAccumulate;
Transitions[ORD(transitionStateLeadingZero)][ORD(transitionClassX)].NextState := transitionStateHexadecimalPrefix;
(* Leading zero. *)
SetDefaultTransition(transitionStateHexadecimalPrefix, NIL, transitionStateEnd);
Transitions[ORD(transitionStateHexadecimalPrefix)][ORD(transitionClassDigit)].Action := TransitionActionAccumulate;
Transitions[ORD(transitionStateHexadecimalPrefix)][ORD(transitionClassDigit)].NextState := transitionStateHexadecimal;
Transitions[ORD(transitionStateHexadecimalPrefix)][ORD(transitionClassHex)].Action := TransitionActionAccumulate;
Transitions[ORD(transitionStateHexadecimalPrefix)][ORD(transitionClassHex)].NextState := transitionStateHexadecimal;
Transitions[ORD(transitionStateHexadecimalPrefix)][ORD(transitionClassZero)].Action := TransitionActionAccumulate;
Transitions[ORD(transitionStateHexadecimalPrefix)][ORD(transitionClassZero)].NextState := transitionStateHexadecimal
END InitializeTransitions;
PROCEDURE LexerInitialize(ALexer: PLexer; Input: File);
BEGIN
ALexer^.Input := Input;
ALexer^.Length := 0;
ALLOCATE(ALexer^.Buffer, ChunkSize);
MemZero(ALexer^.Buffer, ChunkSize);
ALexer^.Size := ChunkSize
END LexerInitialize;
PROCEDURE LexerLex(ALexer: PLexer): LexerToken;
VAR
WrittenBytes: CARDINAL;
CurrentClass: TransitionClass;
CurrentState: TransitionState;
CurrentTransition: Transition;
Result: LexerToken;
BEGIN
IF ALexer^.Length = 0 THEN
ALexer^.Length := ReadNBytes(ALexer^.Input, ChunkSize, ALexer^.Buffer);
ALexer^.Current := ALexer^.Buffer
END;
ALexer^.Start := ALexer^.Current;
CurrentState := transitionStateStart;
WHILE CurrentState <> transitionStateEnd DO
CurrentClass := Classification[ORD(ALexer^.Current^) + 1];
CurrentTransition := Transitions[ORD(CurrentState)][ORD(CurrentClass)];
IF CurrentTransition.Action <> NIL THEN
CurrentTransition.Action(ALexer, ADR(Result))
END;
CurrentState := CurrentTransition.NextState
END;
RETURN(Result)
END LexerLex;
PROCEDURE LexerDestroy(ALexer: PLexer);
BEGIN
DEALLOCATE(ALexer^.Buffer, ALexer^.Size)
END LexerDestroy;
BEGIN
InitializeClassification();
InitializeTransitions()
END Lexer.

View File

@@ -1,9 +0,0 @@
{
"dependencies": {
"tanya": "~>0.18.0"
},
"name": "elna",
"targetType": "executable",
"targetPath": "build/bin",
"mainSourceFile": "source/main.d"
}

View File

@@ -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);
}
}

View File

@@ -1,65 +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 std.algorithm;
import std.sumtype;
import std.typecons;
import tanya.os.error;
import tanya.container.array;
import tanya.container.string;
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.length == 0)
{
printf("Lexical analysis failed.\n");
return 1;
}
auto ast = parse(tokens);
if (!ast.valid)
{
auto compileError = ast.error.get;
printf("%lu:%lu: %s\n", compileError.line, compileError.column, compileError.message.ptr);
return 2;
}
auto ir = transform(ast.result);
auto handle = File.open(outputFilename.toStringz, BitFlags!(File.Mode)(File.Mode.truncate));
if (!handle.valid)
{
return 1;
}
auto programText = writeNext(ir);
auto elf = Elf(move(handle));
elf.addCode("main", programText);
elf.finish();
return 0;
}

View File

@@ -1,859 +0,0 @@
module elna.elf;
import elna.extended;
import std.algorithm;
import tanya.container.array;
import tanya.container.string;
/// Unsigned program address.
alias Elf64_Addr = ulong*;
/// Unsigned file offset.
alias Elf64_Off = ulong;
/// Unsigned medium integer.
alias Elf64_Half = ushort;
/// Unsigned integer.
alias Elf64_Word = uint;
/// Signed integer.
alias Elf64_Sword = int;
/// Unsigned long integer.
alias Elf64_Xword = ulong;
/// Signed long integer.
alias Elf64_Sxword = long;
/// Unsigned program address.
alias Elf32_Addr = uint;
/// Unsigned file offset.
alias Elf32_Off = uint;
/// Unsigned medium integer.
alias Elf32_Half = ushort;
/// Unsigned integer.
alias Elf32_Word = uint;
/// Signed integer.
alias Elf32_Sword = int;
enum : size_t
{
/// File identification.
EI_MAG0 = 0,
/// File identification.
EI_MAG1 = 1,
/// File identification.
EI_MAG2 = 2,
/// File identification.
EI_MAG3 = 3,
/// File class.
EI_CLASS = 4,
/// Data encoding.
EI_DATA = 5,
/// File version.
EI_VERSION = 6,
/// Start of padding bytes.
EI_PAD = 7,
/// Size of e_ident[]
EI_NIDENT = 16
}
enum : ubyte
{
/// e_ident[EI_MAG0].
ELFMAG0 = 0x7f,
/// e_ident[EI_MAG1].
ELFMAG1 = 'E',
/// e_ident[EI_MAG2].
ELFMAG2 = 'L',
/// e_ident[EI_MAG3].
ELFMAG3 = 'F'
}
/**
* File header.
*/
struct Elf64_Ehdr
{
/// ELF identification.
ubyte[EI_NIDENT] e_ident;
/// Object file type.
Elf64_Half e_type;
/// Machine type.
Elf64_Half e_machine;
/// Object file version
Elf64_Word e_version;
/// Entry point address.
Elf64_Addr e_entry;
/// Program header offset.
Elf64_Off e_phoff;
/// Section header offset.
Elf64_Off e_shoff;
/// Processor-specific flags.
Elf64_Word e_flags;
/// ELF header size.
Elf64_Half e_ehsize;
/// Size of program header entry.
Elf64_Half e_phentsize;
/// Number of program header entries.
Elf64_Half e_phnum;
/// Size of section header entry.
Elf64_Half e_shentsize;
/// Number of section header entries.
Elf64_Half e_shnum;
/// Section name string table index.
Elf64_Half e_shstrndx;
}
/**
* File header.
*/
struct Elf32_Ehdr {
/// ELF identification.
ubyte[EI_NIDENT] e_ident;
/// Object file type.
Elf32_Half e_type;
/// Machine type.
Elf32_Half e_machine;
/// Object file version
Elf32_Word e_version;
/// Entry point address.
Elf32_Addr e_entry;
/// Program header offset.
Elf32_Off e_phoff;
/// Section header offset.
Elf32_Off e_shoff;
/// Processor-specific flags.
Elf32_Word e_flags;
/// ELF header size.
Elf32_Half e_ehsize;
/// Size of program header entry.
Elf32_Half e_phentsize;
/// Number of program header entries.
Elf32_Half e_phnum;
/// Size of section header entry.
Elf32_Half e_shentsize;
/// Number of section header entries.
Elf32_Half e_shnum;
/// Section name string table index.
Elf32_Half e_shstrndx;
}
/**
* Section header.
*/
struct Elf64_Shdr
{
/// Section name.
Elf64_Word sh_name;
/// Section type.
Elf64_Word sh_type;
/// Section attributes.
Elf64_Xword sh_flags;
/// Virtual address in memory.
Elf64_Addr sh_addr;
/// Offset in file.
Elf64_Off sh_offset;
/// Size of section.
Elf64_Xword sh_size;
/// Link to other section.
Elf64_Word sh_link;
/// Miscellaneous information.
Elf64_Word sh_info;
/// Address alignment boundary.
Elf64_Xword sh_addralign;
/// Size of entries, if section has table.
Elf64_Xword sh_entsize;
}
/**
* Section header.
*/
struct Elf32_Shdr
{
/// Section name.
Elf32_Word sh_name;
/// Section type.
Elf32_Word sh_type;
/// Section attributes.
Elf32_Word sh_flags;
/// Virtual address in memory.
Elf32_Addr sh_addr;
/// Offset in file.
Elf32_Off sh_offset;
/// Size of section.
Elf32_Word sh_size;
/// Link to other section.
Elf32_Word sh_link;
/// Miscellaneous information.
Elf32_Word sh_info;
/// Address alignment boundary.
Elf32_Word sh_addralign;
/// Size of entries, if section has table.
Elf32_Word sh_entsize;
}
/**
* Symbol table entry.
*/
struct Elf64_Sym
{
/// Symbol name.
Elf64_Word st_name;
/// Type and Binding attributes.
ubyte st_info;
/// Reserved.
ubyte st_other;
/// Section table index.
Elf64_Half st_shndx;
/// Symbol value.
Elf64_Addr st_value;
/// Size of object (e.g., common).
Elf64_Xword st_size;
}
/**
* Relocation entry.
*/
struct Elf64_Rel
{
/// Address of reference.
Elf64_Addr r_offset;
/// Symbol index and type of relocation.
Elf64_Xword r_info;
}
/**
* Relocation entry with explicit addend.
*/
struct Elf64_Rela
{
/// Address of reference.
Elf64_Addr r_offset;
/// Symbol index and type of relocation.
Elf64_Xword r_info;
/// Constant part of expression.
Elf64_Sxword r_addend;
}
/**
* Symbol table entry.
*/
struct Elf32_Sym
{
/// Symbol name.
Elf32_Word st_name;
/// Symbol value.
Elf32_Addr st_value;
/// Size of object (e.g., common).
Elf32_Word st_size;
/// Type and Binding attributes.
ubyte st_info;
/// Reserved.
ubyte st_other;
/// Section table index.
Elf32_Half st_shndx;
}
/**
* Relocation entry.
*/
struct Elf32_Rel
{
/// Address of reference.
Elf32_Addr r_offset;
/// Symbol index and type of relocation.
Elf32_Word r_info;
}
/**
* Relocation entry with explicit addend.
*/
struct Elf32_Rela
{
/// Address of reference.
Elf32_Addr r_offset;
/// Symbol index and type of relocation.
Elf32_Word r_info;
/// Constant part of expression.
Elf32_Sword r_addend;
}
/// Section Types, sh_type.
enum : Elf64_Word
{
/// Marks an unused section header.
SHT_NULL = 0,
/// Contains information defined by the program.
SHT_PROGBITS = 1,
/// Contains a linker symbol table.
SHT_SYMTAB = 2,
/// Contains a string table.
SHT_STRTAB = 3,
/// Contains “Rela” type relocation entries.
SHT_RELA = 4,
/// Contains a symbol hash table
SHT_HASH = 5,
/// Contains dynamic linking tables
SHT_DYNAMIC = 6,
/// Contains note information
SHT_NOTE = 7,
/// Contains uninitialized space; does not occupy any space in the file.
SHT_NOBITS = 8,
/// Contains "Rel" type relocation entries.
SHT_REL = 9,
/// Reserved.
SHT_SHLIB = 10,
/// Contains a dynamic loader symbol table.
SHT_DYNSYM = 11,
/// Environment-specific use.
SHT_LOOS = 0x60000000,
SHT_HIOS = 0x6FFFFFFF,
/// Processor-specific use.
SHT_LOPROC = 0x70000000,
SHT_HIPROC = 0x7FFFFFFF,
}
/**
* Section Attributes, sh_flags.
*/
enum : Elf64_Xword
{
/// Section contains writable data.
SHF_WRITE = 0x1,
/// Section is allocated in memory image of program.
SHF_ALLOC = 0x2,
/// Section contains executable instructions.
SHF_EXECINSTR = 0x4,
/// Environment-specific use.
SHF_MASKOS = 0x0F000000,
/// Processor-specific use.
SHF_MASKPROC = 0xF0000000,
}
auto ELF64_R_SYM(I)(I i)
{
return i >> 32;
}
auto ELF64_R_TYPE(I)(I i)
{
return i & 0xffffffffL;
}
auto ELF64_R_INFO(S, T)(S s, t)
{
return (s << 32) + (t & 0xffffffffL);
}
ubyte ELF32_ST_BIND(ubyte i) @nogc nothrow pure @safe
{
return i >> 4;
}
ubyte ELF32_ST_TYPE(ubyte i) @nogc nothrow pure @safe
{
return i & 0xf;
}
ubyte ELF32_ST_INFO(ubyte b, ubyte t) @nogc nothrow pure @safe
{
return cast(ubyte) ((b << 4) + (t & 0xf));
}
T ELF32_R_SYMT(I)(I i)
{
return i >> 8;
}
ubyte ELF32_R_TYPE(I)(I i)
{
return cast(ubyte) i;
}
auto ELF32_R_INFO(S, T)(S s, T t)
{
return (s << 8) + cast(ubyte) t;
}
enum : uint
{
/// Not visible outside the object file.
STB_LOCAL = 0,
/// Global symbol, visible to all object files.
STB_GLOBAL = 1,
/// Global scope, but with lower precedence than global symbols.
STB_WEAK = 2,
/// Environment-specific use.
STB_LOOS = 10,
STB_HIOS = 12,
/// Processor-specific use.
STB_LOPROC = 13,
STB_HIPROC = 15,
}
enum : uint
{
/// No type specified (e.g., an absolute symbol).
STT_NOTYPE = 0,
/// Data object.
STT_OBJECT = 1,
/// Function entry point.
STT_FUNC = 2,
/// Symbol is associated with a section.
STT_SECTION = 3,
/// Source file associated with the object file.
STT_FILE = 4,
/// Environment-specific use.
STT_LOOS = 10,
STT_HIOS = 12,
/// Processor-specific use.
STT_LOPROC = 13,
STT_HIPROC = 15,
}
Elf64_Ehdr makeFileHeader(Elf64_Off sectionHeaderOffset,
Elf64_Half sectionHeaderCount,
Elf64_Half stringIndex) @nogc
{
Elf64_Ehdr header;
// Magic number.
header.e_ident[0] = '\x7f';
header.e_ident[1] = 'E';
header.e_ident[2] = 'L';
header.e_ident[3] = 'F';
// File class.
header.e_ident[4] = ELFCLASS64;
// Data encoding.
header.e_ident[5] = ELFDATA2LSB;
// Version.
header.e_ident[6] = EV_CURRENT;
// OS/ABI identification.
header.e_ident[7] = EI_OSABI.ELFOSABI_SYSV;
// ABI version.
header.e_ident[8] = 0;
// Size of e_ident[].
header.e_ident[15] = 0;
header.e_type = ET_REL;
header.e_machine = 0x3e; // EM_X86_64: AMD x86-64 architecture
header.e_version = EV_CURRENT;
header.e_entry = null;
header.e_phoff = 0;
header.e_shoff = sectionHeaderOffset;
header.e_flags = 0;
header.e_ehsize = Elf64_Ehdr.sizeof;
header.e_phentsize = 0;
header.e_phnum = 0;
header.e_shentsize = Elf64_Shdr.sizeof;
header.e_shnum = sectionHeaderCount;
header.e_shstrndx = stringIndex;
return header;
}
Elf64_Shdr makeTextHeader(Elf64_Off offset, Elf64_Xword size) @nogc
{
Elf64_Shdr table;
table.sh_name = 0x1b;
table.sh_type = SHT_PROGBITS;
table.sh_flags = SHF_EXECINSTR | SHF_ALLOC;
table.sh_addr = null;
table.sh_offset = offset;
table.sh_size = size;
table.sh_link = SHN_UNDEF;
table.sh_info = 0;
table.sh_addralign = 1;
table.sh_entsize = 0;
return table;
}
Elf64_Shdr makeSymtableHeader(Elf64_Off offset, Elf64_Xword size, Elf64_Word entriesCount) @nogc
{
Elf64_Shdr table;
table.sh_name = 0x01;
table.sh_type = SHT_SYMTAB;
table.sh_flags = 0;
table.sh_addr = null;
table.sh_offset = offset;
table.sh_size = size;
table.sh_link = 0x03; // String table used by entries in this section.
table.sh_info = entriesCount;
table.sh_addralign = 8;
table.sh_entsize = Elf64_Sym.sizeof;
return table;
}
Elf64_Shdr makeStringHeader(Elf64_Word stringIndex, Elf64_Off offset, Elf64_Xword size) @nogc
{
Elf64_Shdr table;
table.sh_name = stringIndex;
table.sh_type = SHT_STRTAB;
table.sh_flags = 0;
table.sh_addr = null;
table.sh_offset = offset;
table.sh_size = size;
table.sh_link = SHN_UNDEF;
table.sh_info = 0;
table.sh_addralign = 1;
table.sh_entsize = 0;
return table;
}
Elf64_Shdr makeInitialHeader() @nogc
{
Elf64_Shdr table;
table.sh_name = 0;
table.sh_type = SHT_NULL;
table.sh_flags = 0;
table.sh_addr = null;
table.sh_offset = 0;
table.sh_size = 0;
table.sh_link = SHN_UNDEF;
table.sh_info = 0;
table.sh_addralign = 0;
table.sh_entsize = 0;
return table;
}
Elf64_Sym makeInitialSymTable() @nogc
{
Elf64_Sym table;
table.st_name = 0;
table.st_info = 0;
table.st_other = 0;
table.st_shndx = 0;
table.st_value = null;
table.st_size = 0;
return table;
}
Elf64_Sym makeMainSymTable(Elf64_Half textIndex) @nogc
{
Elf64_Sym table;
table.st_name = 0x01;
table.st_info = ELF32_ST_INFO(STB_GLOBAL, STT_FUNC);
table.st_other = 0;
table.st_shndx = textIndex;
table.st_value = null;
table.st_size = 0;
return table;
}
/// Special Section Indices.
enum : ushort
{
/// Used to mark an undefined or meaningless section reference.
SHN_UNDEF = 0,
/// This value specifies the lower bound of the range of reserved indexes.
SHN_LORESERVE = 0xff00,
/// Processor-specific use.
SHN_LOPROC = 0xFF00,
SHN_HIPROC = 0xFF1F,
/// Environment-specific use.
SHN_LOOS = 0xFF20,
SHN_HIOS = 0xFF3F,
/// Indicates that the corresponding reference is an absolute value.
SHN_ABS = 0xFFF1,
/**
* Indicates a symbol that has been declared as a common block (Fortran
* COMMON or C tentative declaration).
*/
SHN_COMMON = 0xFFF2,
}
/**
* Object File Classes, e_ident[EI_CLASS].
*/
enum : ubyte
{
/// Invalid class.
ELFCLASSNONE = 0,
/// 32-bit objects.
ELFCLASS32 = 1,
/// 64-bit objects.
ELFCLASS64 = 2
}
enum : ubyte {
/// Invalid version.
EV_NONE = 0,
/// Current version.
EV_CURRENT = 1
}
/**
* Data Encodings, e_ident[EI_DATA].
*/
enum : ubyte
{
/// Object file data structures are little-endian.
ELFDATA2LSB = 1,
/// Object file data structures are big-endian.
ELFDATA2MSB = 2,
}
/**
* Operating System and ABI Identifiers, e_ident[EI_OSABI].
*/
enum EI_OSABI : ubyte
{
/// System V ABI.
ELFOSABI_SYSV = 0,
/// HP-UX operating system.
ELFOSABI_HPUX = 1,
/// Standalone (embedded) application.
ELFOSABI_STANDALONE = 255,
}
enum : Elf64_Half
{
ET_NONE = 0, /// No file type.
ET_REL = 1, /// Relocatable object file.
ET_EXEC = 2, /// Executable file.
ET_DYN = 3, /// Shared object file.
ET_CORE = 4, /// Core file.
ET_LOOS = 0xFE00, /// Environment-specific use.
ET_HIOS = 0xFEFF,
ET_LOPROC = 0xFF00, /// Processor-specific use.
ET_HIPROC = 0xFFFF,
}
auto pad(ubyte elfClass)(size_t value) @nogc
{
static if (elfClass == ELFCLASS32)
{
return cast(Elf32_Word) (value / 4 + 1) * 4;
}
else static if (elfClass == ELFCLASS64)
{
return cast(Elf64_Xword) (value / 8 + 1) * 8;
}
else
{
static assert(false, "Invalid ELF class");
}
}
struct Symbol
{
String name;
const(ubyte)[] text;
}
struct Elf
{
private Elf32_Ehdr fileHeader;
private Array!Elf32_Shdr sectionHeaders;
private Elf32_Off currentOffset = Elf32_Ehdr.sizeof;
private Array!Elf32_Sym symbols;
static immutable char[41] sections =
"\0.symtab\0.strtab\0.shstrtab\0.text\0.rodata\0";
private String strings;
private Elf32_Word lastLocalSymbol;
private Elf32_Word textSize;
private File output;
static Elf opCall(File output) @nogc
{
Elf elf = Elf.init;
elf.initializeFileHeader();
elf.initializeSectionHeaders();
elf.insertSymbols();
elf.output = move(output);
elf.output.seek(Elf32_Ehdr.sizeof, File.Whence.set);
return elf;
}
@disable this(this);
void finish() @nogc
{
makeTextHeader();
initializeSymbolTable(cast(Elf32_Word) (this.sectionHeaders.length + 1));
foreach (symbol; this.symbols)
{
output.write((cast(ubyte*) &symbol)[0 .. Elf32_Sym.sizeof]);
this.currentOffset += Elf32_Sym.sizeof;
}
this.sectionHeaders.insertBack(makeStringHeader(0x09, this.currentOffset, cast(Elf32_Word) strings.length));
output.write(cast(ubyte[]) this.strings.toStringz[0 .. this.strings.length + 1]);
this.currentOffset += this.strings.length + 1;
this.sectionHeaders.insertBack(makeStringHeader(0x11, this.currentOffset, sections.length));
output.write(cast(const(ubyte)[]) this.sections);
this.currentOffset += this.sections.length;
auto alignment = pad!ELFCLASS32(this.strings.length + 1 + this.sections.length);
const(ubyte)[4] padding = 0;
output.write(padding[0 .. alignment - this.strings.length - 1 - this.sections.length]);
this.currentOffset += alignment - this.strings.length - 1 - this.sections.length;
// End writing data, start writing headers.
output.write((cast(ubyte*) this.sectionHeaders.get)[0 .. Elf32_Shdr.sizeof * this.sectionHeaders.length]);
output.seek(0, File.Whence.set);
this.fileHeader.e_shoff = this.currentOffset;
this.fileHeader.e_shnum = cast(Elf32_Half) this.sectionHeaders.length;
// String table is the last one
this.fileHeader.e_shstrndx = cast(Elf32_Half) (this.sectionHeaders.length - 1);
output.write((cast(ubyte*) &this.fileHeader)[0 .. fileHeader.sizeof]);
}
private void insertSymbols() @nogc
{
// Zero symbol
Elf32_Sym symbol;
symbol.st_name = 0; // Word
symbol.st_value = 0; // Addr
symbol.st_size = 0; // Word
symbol.st_info = 0; // char
symbol.st_other = 0; // char
symbol.st_shndx = 0; // Half word
this.symbols.insertBack(symbol);
// All symbols are global.
this.lastLocalSymbol = cast(Elf32_Word) this.symbols.length;
}
private Elf32_Shdr makeStringHeader(Elf32_Word stringIndex, Elf32_Off offset, Elf32_Word size) @nogc
{
Elf32_Shdr table;
table.sh_name = stringIndex;
table.sh_type = SHT_STRTAB;
table.sh_flags = 0;
table.sh_addr = 0;
table.sh_offset = offset;
table.sh_size = size;
table.sh_link = SHN_UNDEF;
table.sh_info = 0;
table.sh_addralign = 1;
table.sh_entsize = 0;
return table;
}
private void initializeSymbolTable(Elf32_Word stringTableIndex) @nogc
{
Elf32_Shdr symbolTableHeader;
symbolTableHeader.sh_name = 0x01;
symbolTableHeader.sh_type = SHT_SYMTAB;
symbolTableHeader.sh_flags = 0;
symbolTableHeader.sh_addr = 0;
symbolTableHeader.sh_offset = this.currentOffset;
symbolTableHeader.sh_size = cast(Elf32_Word) (this.symbols.length * Elf32_Sym.sizeof);
// String table used by entries in this section.
symbolTableHeader.sh_link = stringTableIndex;
symbolTableHeader.sh_info = this.lastLocalSymbol;
symbolTableHeader.sh_addralign = 4;
symbolTableHeader.sh_entsize = Elf32_Sym.sizeof;
this.sectionHeaders.insertBack(symbolTableHeader);
}
void addCode(string name, ref Array!ubyte text) @nogc
{
this.output.write(text.get);
this.strings.insertBack("\0");
this.strings.insertBack(name[]);
Elf32_Sym symbol;
// Main function
symbol.st_name = 0x1; // Word
symbol.st_value = 0; // Addr
symbol.st_size = cast(Elf32_Word) text.length; // Word
symbol.st_info = ELF32_ST_INFO(STB_GLOBAL, STT_FUNC); // char
symbol.st_other = 0; // char
// .text header index, half word
symbol.st_shndx = cast(Elf32_Half) this.sectionHeaders.length;
this.symbols.insertBack(symbol);
this.textSize += text.length;
}
private void makeTextHeader() @nogc
{
Elf32_Shdr textHeader;
textHeader.sh_name = 0x1b;
textHeader.sh_type = SHT_PROGBITS;
textHeader.sh_flags = SHF_EXECINSTR | SHF_ALLOC;
textHeader.sh_addr = 0;
textHeader.sh_offset = this.currentOffset;
textHeader.sh_size = cast(Elf32_Word) this.textSize;
textHeader.sh_link = SHN_UNDEF;
textHeader.sh_info = 0;
textHeader.sh_addralign = 1;
textHeader.sh_entsize = 0;
this.sectionHeaders.insertBack(textHeader);
this.currentOffset += this.textSize;
}
private void initializeSectionHeaders() @nogc
{
Elf32_Shdr table;
table.sh_name = 0;
table.sh_type = SHT_NULL;
table.sh_flags = 0;
table.sh_addr = 0;
table.sh_offset = 0;
table.sh_size = 0;
table.sh_link = SHN_UNDEF;
table.sh_info = 0;
table.sh_addralign = 0;
table.sh_entsize = 0;
this.sectionHeaders.insertBack(table);
}
private void initializeFileHeader() @nogc
{
// Magic number.
this.fileHeader.e_ident[0] = '\x7f';
this.fileHeader.e_ident[1] = 'E';
this.fileHeader.e_ident[2] = 'L';
this.fileHeader.e_ident[3] = 'F';
this.fileHeader.e_ident[4] = ELFCLASS32;
this.fileHeader.e_ident[5] = ELFDATA2LSB;
this.fileHeader.e_ident[6] = EV_CURRENT;
this.fileHeader.e_ident[7] = EI_OSABI.ELFOSABI_SYSV;
this.fileHeader.e_ident[8] = 0;
this.fileHeader.e_type = ET_REL;
this.fileHeader.e_machine = 0xf3; // EM_RISCV
this.fileHeader.e_version = EV_CURRENT;
this.fileHeader.e_entry = 0;
this.fileHeader.e_phoff = 0;
// this.fileHeader.e_shoff = ?; (section header offset)
this.fileHeader.e_flags = 0;
this.fileHeader.e_ehsize = Elf32_Ehdr.sizeof;
this.fileHeader.e_phentsize = 0;
this.fileHeader.e_phnum = 0;
this.fileHeader.e_shentsize = Elf32_Shdr.sizeof;
// this.fileHeader.e_shnum = ?; (section header count)
// this.fileHeader.e_shstrndx = ?; (string index)
}
}

View File

@@ -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);
}

View File

@@ -1,144 +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;
import tanya.memory.mmappool;
/**
* Definition.
*/
class Definition
{
char[] identifier;
Array!Statement statements;
Array!VariableDeclaration variableDeclarations;
}
class Statement
{
Subroutine subroutine;
}
abstract class Expression
{
}
class Number : Expression
{
int value;
}
class Variable : Expression
{
size_t counter;
}
class VariableDeclaration
{
String identifier;
}
class Subroutine
{
Expression lhs, rhs;
}
private Number transformNumber(parser.Number number) @nogc
{
return MmapPool.instance.make!Number(number.value);
}
private Variable transformSubroutine(parser.Subroutine subroutine,
ref Array!Statement statements,
ref HashTable!(String, int) constants) @nogc
{
auto target = MmapPool.instance.make!Subroutine;
target.lhs = transformExpression(subroutine.lhs, statements, constants);
target.rhs = transformExpression(subroutine.rhs, statements, constants);
auto newStatement = MmapPool.instance.make!Statement;
newStatement.subroutine = target;
statements.insertBack(newStatement);
auto newVariable = MmapPool.instance.make!Variable;
newVariable.counter = statements.length;
return newVariable;
}
private Expression transformExpression(parser.Expression expression,
ref Array!Statement statements,
ref HashTable!(String, int) constants) @nogc
{
if ((cast(parser.Number) expression) !is null)
{
auto numberExpression = MmapPool.instance.make!Number;
numberExpression.value = (cast(parser.Number) expression).value;
return numberExpression;
}
if ((cast(parser.Variable) expression) !is null)
{
auto numberExpression = MmapPool.instance.make!Number;
numberExpression.value = constants[(cast(parser.Variable) expression).identifier];
return numberExpression;
}
else if ((cast(parser.Subroutine) expression) !is null)
{
return transformSubroutine(cast(parser.Subroutine) expression, statements, constants);
}
return null;
}
Expression transformStatement(parser.Statement statement,
ref Array!Statement statements,
ref HashTable!(String, int) constants) @nogc
{
if ((cast(parser.BangStatement) statement) !is null)
{
return transformExpression((cast(parser.BangStatement) statement).expression, statements, constants);
}
return null;
}
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 = MmapPool.instance.make!VariableDeclaration;
newDeclaration.identifier = variableDeclaration.identifier;
variables.insertBack(newDeclaration);
}
return variables;
}
Definition transform(parser.Block block) @nogc
{
auto target = MmapPool.instance.make!Definition;
auto constants = transformConstants(block.definitions);
transformStatement(block.statement, target.statements, constants);
target.variableDeclarations = transformVariableDeclarations(block.variableDeclarations);
return target;
}

View File

@@ -1,252 +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;
import tanya.memory.mmappool;
struct Token
{
enum Type
{
number,
subroutine, // 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)
{
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)
{
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];
}
}
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 == '+') // Multi-character, random special characters.
{
tokens.insertBack(Token(Token.Type.subroutine, source.position));
source.popFront;
}
else if (source.front == '\n')
{
source.breakLine;
}
else
{
return typeof(tokens)(); // Error.
}
}
return tokens;
}

View File

@@ -1,269 +0,0 @@
module elna.parser;
import elna.lexer;
import elna.result;
import tanya.container.array;
import tanya.container.string;
import tanya.memory.allocator;
import tanya.memory.mmappool;
/**
* Constant definition.
*/
class Definition
{
Number number;
String identifier;
}
/**
* Variable declaration.
*/
class VariableDeclaration
{
String identifier;
}
abstract class Statement
{
}
class BangStatement : Statement
{
Expression expression;
}
class Block
{
Array!Definition definitions;
Array!VariableDeclaration variableDeclarations;
Statement statement;
}
abstract class Expression
{
}
class Number : Expression
{
int value;
}
class Variable : Expression
{
String identifier;
}
class Subroutine : Expression
{
Expression lhs, rhs;
}
private Result!Expression parseExpression(ref Array!(Token).Range tokens) @nogc
in (!tokens.empty, "Expected expression, got end of stream")
{
if (tokens.front.ofType(Token.Type.number))
{
auto number = MmapPool.instance.make!Number;
number.value = tokens.front.value!(Token.Type.number);
tokens.popFront;
return Result!Expression(number);
}
else if (tokens.front.ofType(Token.Type.identifier))
{
auto variable = MmapPool.instance.make!Variable;
variable.identifier = tokens.front.value!(Token.Type.identifier);
tokens.popFront;
return Result!Expression(variable);
}
else if (tokens.front.ofType(Token.Type.subroutine))
{
auto subroutine = MmapPool.instance.make!Subroutine;
tokens.popFront;
auto expression = parseExpression(tokens);
if (expression.valid)
{
subroutine.lhs = expression.result;
}
else
{
return Result!Expression("Expected left-hand side to be an expression", tokens.front.position);
}
expression = parseExpression(tokens);
if (expression.valid)
{
subroutine.rhs = expression.result;
}
else
{
return Result!Expression("Expected left-hand side to be an expression", tokens.front.position);
}
return Result!Expression(subroutine);
}
else if (tokens.front.ofType(Token.Type.leftParen))
{
tokens.popFront;
auto expression = parseExpression(tokens);
tokens.popFront;
return expression;
}
return Result!Expression("Expected 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 = MmapPool.instance.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 = MmapPool.instance.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 = MmapPool.instance.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 = MmapPool.instance.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 = MmapPool.instance.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);
}

View File

@@ -1,84 +0,0 @@
module elna.result;
import std.typecons;
/**
* 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;
}
}

View File

@@ -1,254 +0,0 @@
module elna.riscv;
import elna.extended;
import elna.ir;
import std.algorithm;
import std.typecons;
import tanya.container.array;
import tanya.container.string;
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 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;
ref Instruction i(BaseOpcode opcode, XRegister rd, Funct3 funct3, XRegister rs1, uint immediate)
return scope @nogc
{
this.instruction = opcode
| (rd << 7)
| (funct3 << 12)
| (rs1 << 15)
| (immediate << 20);
return this;
}
ref Instruction s(BaseOpcode opcode, uint imm1, Funct3 funct3, XRegister rs1, XRegister rs2, uint imm2 = 0)
return scope @nogc
{
this.instruction = opcode
| (imm1 << 7)
| (funct3 << 12)
| (rs1 << 15)
| (rs2 << 20)
| (imm2 << 25);
return this;
}
ref Instruction r(BaseOpcode opcode, XRegister rd, Funct3 funct3, XRegister rs1, XRegister rs2, ubyte funct7 = 0)
return scope @nogc
{
this.instruction = opcode
| (rd << 7)
| (funct3 << 12)
| (rs1 << 15)
| (rs2 << 20)
| (funct7 << 25);
return this;
}
ubyte[] encode() return scope @nogc
{
return (cast(ubyte*) (&this.instruction))[0 .. uint.sizeof];
}
}
Array!ubyte writeNext(Definition ast) @nogc
{
Array!Instruction instructions;
// Prologue.
instructions.insertBack(
Instruction()
.i(BaseOpcode.opImm, XRegister.sp, Funct3.addi, XRegister.sp, cast(uint) -16)
);
instructions.insertBack(
Instruction()
.s(BaseOpcode.store, 12, Funct3.sw, XRegister.sp, XRegister.s0)
);
instructions.insertBack(
Instruction()
.i(BaseOpcode.opImm, XRegister.s0, Funct3.addi, XRegister.sp, 16)
);
int i = 1;
foreach (statement; ast.statements[])
{
if ((cast(Number) statement.subroutine.lhs) !is null)
{
// Opcode of mov is “0xb8 + r”, where “r” is the register opcode.
// Register opcode of %eax is 0.
instructions.insertBack(
Instruction() // movl $x, %eax; where $x is a number.
.i(BaseOpcode.opImm, XRegister.a0, Funct3.addi, XRegister.zero,
(cast(Number) statement.subroutine.lhs).value)
);
}
else if ((cast(Variable) statement.subroutine.lhs) !is null)
{
// movl -x(%rbp), %eax; where x is a number.
instructions.insertBack(
Instruction()
.i(BaseOpcode.load, XRegister.a0, Funct3.lw, XRegister.sp,
cast(byte) (cast(Variable) statement.subroutine.lhs).counter * 4)
);
}
if ((cast(Number) statement.subroutine.rhs) !is null)
{
// Opcode of mov is “0xb8 + r”, where “r” is the register opcode.
// Register opcode of %ebx is 3.
instructions.insertBack(
Instruction() // movl $x, %ebx; where $x is a number.
.i(BaseOpcode.opImm, XRegister.t0, Funct3.addi, XRegister.zero,
(cast(Number) statement.subroutine.rhs).value)
);
}
else if ((cast(Variable) statement.subroutine.rhs) !is null)
{
// movl -x(%rbp), %ebx; where x is a number.
instructions.insertBack(
Instruction()
.i(BaseOpcode.load, XRegister.t0, Funct3.lw, XRegister.sp,
cast(byte) (cast(Variable) statement.subroutine.rhs).counter * 4)
);
}
// Calculate the result and assign it to a variable on the stack.
instructions.insertBack(
Instruction()
.r(BaseOpcode.op, XRegister.a0, Funct3.add, XRegister.a0, XRegister.t0)
);
instructions.insertBack( // movl %eax, -x(%rbp); where x is a number.
Instruction()
.s(BaseOpcode.store, cast(uint) (i * 4), Funct3.sw, XRegister.sp, XRegister.a0)
);
++i;
}
// Prologue.
instructions.insertBack(
Instruction()
.i(BaseOpcode.load, XRegister.s0, Funct3.lw, XRegister.sp, 12)
);
instructions.insertBack(
Instruction()
.i(BaseOpcode.opImm, XRegister.sp, Funct3.addi, XRegister.sp, 16)
);
instructions.insertBack(
Instruction()
.i(BaseOpcode.jalr, XRegister.zero, Funct3.jalr, XRegister.ra, 0)
);
Array!ubyte programText;
foreach (ref instruction; instructions)
{
programText.insertBack(instruction.encode);
}
return programText;
}

View File

@@ -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);
}
);
}

View File

@@ -1,3 +0,0 @@
const a = 1, b = 2;
! + a b
.

View File

@@ -1 +0,0 @@
3

View File

@@ -1 +0,0 @@
8

View File

@@ -1 +0,0 @@
8

View File

@@ -1 +0,0 @@
8

View File

@@ -1,2 +0,0 @@
! + (+ 3 4) 1
.

View File

@@ -1,2 +0,0 @@
! + 1 7
.

View File

@@ -1,2 +0,0 @@
! + 1 (+ 3 4)
.