diff --git a/.gitignore b/.gitignore index d0d201a..cde7e11 100644 --- a/.gitignore +++ b/.gitignore @@ -1,3 +1,3 @@ -/.dub/ +a.out /dub.selections.json /build/ diff --git a/README b/README deleted file mode 100644 index b6e03eb..0000000 --- a/README +++ /dev/null @@ -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. diff --git a/README.md b/README.md new file mode 100644 index 0000000..211ca55 --- /dev/null +++ b/README.md @@ -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`. diff --git a/Rakefile b/Rakefile index 4dfb642..870de81 100644 --- a/Rakefile +++ b/Rakefile @@ -2,87 +2,82 @@ require 'pathname' require 'rake/clean' require 'open3' -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('') +M2C = 'gm2' # Modula-2 compiler. +BOOT_OBJECTS = FileList['boot/*.mod'] + .map do |source| + Pathname.new(source).basename.sub_ext('.o') + 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 -SOURCES = FileList['source/**/*.d'] - -directory 'build' +directory 'build/boot' +directory 'build/self' 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/11.1.0', - '-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' +rule(/build\/.+\.o$/ => ->(file) { source_for_object(file) }) do |t| + sources = t.prerequisites.filter { |f| f.end_with? '.mod' } + + sh M2C, '-c', '-I', 'boot', '-o', t.name, *sources 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 +rule(/build\/self\/.+\.mod$/ => [ + 'build/self', 'build/boot/Compiler', + ->(file) { File.join('boot', Pathname.new(file).basename) } +]) do |t| + sources, compiler = t.prerequisites + .reject { |f| File.directory? f } + .partition { |f| f.end_with? '.mod' } -file BINARY => SOURCES do |t| - sh({ 'DFLAGS' => (DFLAGS * ' ') }, 'dub', 'build', '--compiler=gdc-12') -end + File.open t.name, 'w' do |output| + puts + 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' -task test: TESTS -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' + IO.copy_stream cl_out, output + cl_out.close end - actual = $?.exitstatus - - fail "#{test}: Expected #{expected}, got #{actual}" unless expected == actual end end -desc 'Run unittest blocks' -task unittest: SOURCES do |t| - sh('dub', 'test', '--compiler=gdc-12') +['boot', 'self'].each do |sub| + compiler_binary = Pathname.new('build') + sub + 'Compiler' + + 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 -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 +task default: 'build/self/Compiler' +task default: 'build/self/Compiler.mod' +task default: 'boot/Compiler.mod' +task :default do |t| + exe, previous_output, source = t.prerequisites -def test_for_out(out_file, extension) - Pathname - .new(out_file) - .sub_ext(extension) - .to_path + cat_arguments = ['cat', source] + diff_arguments = ['diff', '-Nur', '--text', previous_output, '-'] + + puts [cat_arguments * ' ', exe, diff_arguments * ' '].join(' | ') + Open3.pipeline(cat_arguments, exe, diff_arguments) end diff --git a/boot/Compiler.mod b/boot/Compiler.mod new file mode 100644 index 0000000..e74a872 --- /dev/null +++ b/boot/Compiler.mod @@ -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. diff --git a/boot/Lexer.def b/boot/Lexer.def new file mode 100644 index 0000000..6ab8e54 --- /dev/null +++ b/boot/Lexer.def @@ -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. diff --git a/boot/Lexer.mod b/boot/Lexer.mod new file mode 100644 index 0000000..9bc08cb --- /dev/null +++ b/boot/Lexer.mod @@ -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. diff --git a/dub.json b/dub.json deleted file mode 100644 index 8567d3a..0000000 --- a/dub.json +++ /dev/null @@ -1,9 +0,0 @@ -{ - "dependencies": { - "tanya": "~>0.18.0" - }, - "name": "elna", - "targetType": "executable", - "targetPath": "build/bin", - "mainSourceFile": "source/main.d" -} diff --git a/source/elna/arguments.d b/source/elna/arguments.d deleted file mode 100644 index 1ad928a..0000000 --- a/source/elna/arguments.d +++ /dev/null @@ -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); - } -} diff --git a/source/elna/backend.d b/source/elna/backend.d deleted file mode 100644 index c7cb2ca..0000000 --- a/source/elna/backend.d +++ /dev/null @@ -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; -} diff --git a/source/elna/elf.d b/source/elna/elf.d deleted file mode 100644 index b0f6d2c..0000000 --- a/source/elna/elf.d +++ /dev/null @@ -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) - } -} diff --git a/source/elna/extended.d b/source/elna/extended.d deleted file mode 100644 index ff06a0c..0000000 --- a/source/elna/extended.d +++ /dev/null @@ -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); -} diff --git a/source/elna/ir.d b/source/elna/ir.d deleted file mode 100644 index e2a8df4..0000000 --- a/source/elna/ir.d +++ /dev/null @@ -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; -} diff --git a/source/elna/lexer.d b/source/elna/lexer.d deleted file mode 100644 index c47aae0..0000000 --- a/source/elna/lexer.d +++ /dev/null @@ -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; -} diff --git a/source/elna/parser.d b/source/elna/parser.d deleted file mode 100644 index 28a090b..0000000 --- a/source/elna/parser.d +++ /dev/null @@ -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); -} diff --git a/source/elna/result.d b/source/elna/result.d deleted file mode 100644 index 049c453..0000000 --- a/source/elna/result.d +++ /dev/null @@ -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; - } -} diff --git a/source/elna/riscv.d b/source/elna/riscv.d deleted file mode 100644 index fe6989f..0000000 --- a/source/elna/riscv.d +++ /dev/null @@ -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; -} diff --git a/source/main.d b/source/main.d deleted file mode 100644 index dcb9fa0..0000000 --- a/source/main.d +++ /dev/null @@ -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); - } - ); -} diff --git a/tests/const_list.eln b/tests/const_list.eln deleted file mode 100644 index 18a6711..0000000 --- a/tests/const_list.eln +++ /dev/null @@ -1,3 +0,0 @@ -const a = 1, b = 2; -! + a b -. diff --git a/tests/expectations/const_list.txt b/tests/expectations/const_list.txt deleted file mode 100644 index 00750ed..0000000 --- a/tests/expectations/const_list.txt +++ /dev/null @@ -1 +0,0 @@ -3 diff --git a/tests/expectations/left_nested_sum.txt b/tests/expectations/left_nested_sum.txt deleted file mode 100644 index 45a4fb7..0000000 --- a/tests/expectations/left_nested_sum.txt +++ /dev/null @@ -1 +0,0 @@ -8 diff --git a/tests/expectations/sum.txt b/tests/expectations/sum.txt deleted file mode 100644 index 45a4fb7..0000000 --- a/tests/expectations/sum.txt +++ /dev/null @@ -1 +0,0 @@ -8 diff --git a/tests/expectations/sums.txt b/tests/expectations/sums.txt deleted file mode 100644 index 45a4fb7..0000000 --- a/tests/expectations/sums.txt +++ /dev/null @@ -1 +0,0 @@ -8 diff --git a/tests/left_nested_sum.eln b/tests/left_nested_sum.eln deleted file mode 100644 index 93fb29b..0000000 --- a/tests/left_nested_sum.eln +++ /dev/null @@ -1,2 +0,0 @@ -! + (+ 3 4) 1 -. diff --git a/tests/sum.eln b/tests/sum.eln deleted file mode 100644 index 12343f0..0000000 --- a/tests/sum.eln +++ /dev/null @@ -1,2 +0,0 @@ -! + 1 7 -. diff --git a/tests/sums.eln b/tests/sums.eln deleted file mode 100644 index bf80ecc..0000000 --- a/tests/sums.eln +++ /dev/null @@ -1,2 +0,0 @@ -! + 1 (+ 3 4) -.