Print test summary
This commit is contained in:
		@@ -12,11 +12,14 @@ add_executable(elnsh shell/main.cpp
 | 
				
			|||||||
	shell/interactive.cpp include/elna/interactive.hpp
 | 
						shell/interactive.cpp include/elna/interactive.hpp
 | 
				
			||||||
	shell/history.cpp include/elna/history.hpp
 | 
						shell/history.cpp include/elna/history.hpp
 | 
				
			||||||
	shell/state.cpp include/elna/state.hpp
 | 
						shell/state.cpp include/elna/state.hpp
 | 
				
			||||||
	shell/result.cpp include/elna/result.hpp
 | 
					 | 
				
			||||||
)
 | 
					)
 | 
				
			||||||
target_include_directories(elnsh PRIVATE include)
 | 
					target_include_directories(elnsh PRIVATE include)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
add_library(elna
 | 
					add_library(elna
 | 
				
			||||||
	source/lexer.cpp include/elna/lexer.hpp
 | 
						source/lexer.cpp include/elna/lexer.hpp
 | 
				
			||||||
 | 
						source/result.cpp include/elna/result.hpp
 | 
				
			||||||
 | 
						source/riscv.cpp include/elna/riscv.hpp
 | 
				
			||||||
 | 
						source/ir.cpp include/elna/ir.hpp
 | 
				
			||||||
 | 
						include/elna/parser.hpp
 | 
				
			||||||
)
 | 
					)
 | 
				
			||||||
target_include_directories(elna PRIVATE include)
 | 
					target_include_directories(elna PRIVATE include)
 | 
				
			||||||
 
 | 
				
			|||||||
							
								
								
									
										75
									
								
								Rakefile
									
									
									
									
									
								
							
							
						
						
									
										75
									
								
								Rakefile
									
									
									
									
									
								
							@@ -4,87 +4,16 @@ require 'open3'
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
DFLAGS = ['--warn-no-deprecated', '-L/usr/lib64/gcc-12']
 | 
					DFLAGS = ['--warn-no-deprecated', '-L/usr/lib64/gcc-12']
 | 
				
			||||||
BINARY = 'build/bin/elna'
 | 
					BINARY = 'build/bin/elna'
 | 
				
			||||||
TESTS = FileList['tests/*.eln'].flat_map do |test|
 | 
					 | 
				
			||||||
  build = Pathname.new 'build'
 | 
					 | 
				
			||||||
  test_basename = Pathname.new(test).basename('')
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
  [build + 'riscv' + test_basename].map { |path| path.sub_ext('').to_path }
 | 
					 | 
				
			||||||
end
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
SOURCES = FileList['source/**/*.d']
 | 
					SOURCES = FileList['source/**/*.d']
 | 
				
			||||||
 | 
					
 | 
				
			||||||
directory 'build'
 | 
					directory 'build/riscv'
 | 
				
			||||||
 | 
					
 | 
				
			||||||
CLEAN.include 'build'
 | 
					CLEAN.include 'build'
 | 
				
			||||||
CLEAN.include '.dub'
 | 
					CLEAN.include '.dub'
 | 
				
			||||||
 | 
					
 | 
				
			||||||
rule(/build\/riscv\/[^\/\.]+$/ => ->(file) { test_for_out(file, '.o') }) do |t|
 | 
					 | 
				
			||||||
  sh '/opt/riscv/bin/riscv32-unknown-elf-ld',
 | 
					 | 
				
			||||||
    '-o', t.name,
 | 
					 | 
				
			||||||
    '-L/opt/riscv/lib/gcc/riscv32-unknown-elf/13.2.0/',
 | 
					 | 
				
			||||||
    '-L/opt/riscv/riscv32-unknown-elf/lib',
 | 
					 | 
				
			||||||
    '/opt/riscv/riscv32-unknown-elf/lib/crt0.o',
 | 
					 | 
				
			||||||
    '/opt/riscv/lib/gcc/riscv32-unknown-elf/13.2.0/crtbegin.o',
 | 
					 | 
				
			||||||
    t.source,
 | 
					 | 
				
			||||||
    '--start-group', '-lgcc', '-lc', '-lgloss', '--end-group',
 | 
					 | 
				
			||||||
    '/opt/riscv/lib/gcc/riscv32-unknown-elf/13.2.0/crtend.o'
 | 
					 | 
				
			||||||
end
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
rule(/build\/riscv\/.+\.o$/ => ->(file) { test_for_object(file, '.eln') }) do |t|
 | 
					 | 
				
			||||||
  Pathname.new(t.name).dirname.mkpath
 | 
					 | 
				
			||||||
  sh BINARY, '-o', t.name, t.source
 | 
					 | 
				
			||||||
end
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
file BINARY => SOURCES do |t|
 | 
					file BINARY => SOURCES do |t|
 | 
				
			||||||
  sh({ 'DFLAGS' => (DFLAGS * ' ') }, 'dub', 'build', '--compiler=gdc')
 | 
					  sh({ 'DFLAGS' => (DFLAGS * ' ') }, 'dub', 'build', '--compiler=gdc')
 | 
				
			||||||
end
 | 
					end
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					task default: 'build/riscv'
 | 
				
			||||||
task default: BINARY
 | 
					task default: BINARY
 | 
				
			||||||
 | 
					 | 
				
			||||||
desc 'Run all tests and check the results'
 | 
					 | 
				
			||||||
task test: TESTS
 | 
					 | 
				
			||||||
task test: BINARY do
 | 
					 | 
				
			||||||
  TESTS.each do |test|
 | 
					 | 
				
			||||||
    expected = Pathname
 | 
					 | 
				
			||||||
      .new(test)
 | 
					 | 
				
			||||||
      .sub_ext('.txt')
 | 
					 | 
				
			||||||
      .sub(/^build\/[[:alpha:]]+\//, 'tests/expectations/')
 | 
					 | 
				
			||||||
      .to_path
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    puts "Running #{test}"
 | 
					 | 
				
			||||||
    if test.include? '/riscv/'
 | 
					 | 
				
			||||||
      spike = [
 | 
					 | 
				
			||||||
        '/opt/riscv/bin/spike',
 | 
					 | 
				
			||||||
        '--isa=RV32IMAC',
 | 
					 | 
				
			||||||
        '/opt/riscv/riscv32-unknown-elf/bin/pk',
 | 
					 | 
				
			||||||
        test
 | 
					 | 
				
			||||||
      ]
 | 
					 | 
				
			||||||
      diff = ['diff', '-Nur', '--color', expected, '-']
 | 
					 | 
				
			||||||
      tail = ['tail', '-n', '1']
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
      last_stdout, wait_threads = Open3.pipeline_r spike, tail, diff
 | 
					 | 
				
			||||||
    else
 | 
					 | 
				
			||||||
      raise 'Unsupported test platform'
 | 
					 | 
				
			||||||
    end
 | 
					 | 
				
			||||||
    print last_stdout.read
 | 
					 | 
				
			||||||
    last_stdout.close
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    fail unless wait_threads.last.value.exitstatus.zero?
 | 
					 | 
				
			||||||
  end
 | 
					 | 
				
			||||||
end
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
def test_for_object(out_file, extension)
 | 
					 | 
				
			||||||
  test_source = Pathname
 | 
					 | 
				
			||||||
    .new(out_file)
 | 
					 | 
				
			||||||
    .sub_ext(extension)
 | 
					 | 
				
			||||||
    .sub(/^build\/[[:alpha:]]+\//, 'tests/')
 | 
					 | 
				
			||||||
    .to_path
 | 
					 | 
				
			||||||
  [test_source, BINARY]
 | 
					 | 
				
			||||||
end
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
def test_for_out(out_file, extension)
 | 
					 | 
				
			||||||
  Pathname
 | 
					 | 
				
			||||||
    .new(out_file)
 | 
					 | 
				
			||||||
    .sub_ext(extension)
 | 
					 | 
				
			||||||
    .to_path
 | 
					 | 
				
			||||||
end
 | 
					 | 
				
			||||||
 
 | 
				
			|||||||
							
								
								
									
										4
									
								
								dub.json
									
									
									
									
									
								
							
							
						
						
									
										4
									
								
								dub.json
									
									
									
									
									
								
							@@ -5,5 +5,7 @@
 | 
				
			|||||||
	"name": "elna",
 | 
						"name": "elna",
 | 
				
			||||||
	"targetType": "executable",
 | 
						"targetType": "executable",
 | 
				
			||||||
	"targetPath": "build/bin",
 | 
						"targetPath": "build/bin",
 | 
				
			||||||
	"mainSourceFile": "source/main.d"
 | 
						"mainSourceFile": "source/main.d",
 | 
				
			||||||
 | 
						"libs": ["elna", "stdc++"],
 | 
				
			||||||
 | 
						"lflags": ["-Lbuild"]
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 
 | 
				
			|||||||
							
								
								
									
										95
									
								
								include/elna/ir.hpp
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										95
									
								
								include/elna/ir.hpp
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,95 @@
 | 
				
			|||||||
 | 
					#pragma once
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					#include "elna/parser.hpp"
 | 
				
			||||||
 | 
					#include <cstddef>
 | 
				
			||||||
 | 
					#include <cstdint>
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					namespace elna::ir
 | 
				
			||||||
 | 
					{
 | 
				
			||||||
 | 
					    class Node;
 | 
				
			||||||
 | 
					    class Definition;
 | 
				
			||||||
 | 
					    class Operand;
 | 
				
			||||||
 | 
					    class BinaryExpression;
 | 
				
			||||||
 | 
					    class Variable;
 | 
				
			||||||
 | 
					    class VariableDeclaration;
 | 
				
			||||||
 | 
					    class Number;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    struct IRVisitor
 | 
				
			||||||
 | 
					    {
 | 
				
			||||||
 | 
					        virtual void visit(Node *) = 0;
 | 
				
			||||||
 | 
					        virtual void visit(Definition *) = 0;
 | 
				
			||||||
 | 
					        virtual void visit(Operand *) = 0;
 | 
				
			||||||
 | 
					        virtual void visit(BinaryExpression *) = 0;
 | 
				
			||||||
 | 
					        virtual void visit(Variable *) = 0;
 | 
				
			||||||
 | 
					        virtual void visit(Number *) = 0;
 | 
				
			||||||
 | 
					    };
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    /**
 | 
				
			||||||
 | 
					     * AST node.
 | 
				
			||||||
 | 
					     */
 | 
				
			||||||
 | 
					    class Node
 | 
				
			||||||
 | 
					    {
 | 
				
			||||||
 | 
					    public:
 | 
				
			||||||
 | 
					        virtual void accept(IRVisitor *) = 0;
 | 
				
			||||||
 | 
					    };
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    /**
 | 
				
			||||||
 | 
					     * Definition.
 | 
				
			||||||
 | 
					     */
 | 
				
			||||||
 | 
					    class Definition : public Node
 | 
				
			||||||
 | 
					    {
 | 
				
			||||||
 | 
					    public:
 | 
				
			||||||
 | 
					        BinaryExpression **statements;
 | 
				
			||||||
 | 
					        std::size_t statementsLength;
 | 
				
			||||||
 | 
					        Operand *result;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        virtual void accept(IRVisitor *visitor) override;
 | 
				
			||||||
 | 
					    };
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    class Statement : public Node
 | 
				
			||||||
 | 
					    {
 | 
				
			||||||
 | 
					    };
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    class Operand : public Node
 | 
				
			||||||
 | 
					    {
 | 
				
			||||||
 | 
					    public:
 | 
				
			||||||
 | 
					        virtual void accept(IRVisitor *visitor) override;
 | 
				
			||||||
 | 
					    };
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    class Number : public Operand
 | 
				
			||||||
 | 
					    {
 | 
				
			||||||
 | 
					    public:
 | 
				
			||||||
 | 
					        std::int32_t value;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        virtual void accept(IRVisitor *visitor) override;
 | 
				
			||||||
 | 
					    };
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    class Variable : public Operand
 | 
				
			||||||
 | 
					    {
 | 
				
			||||||
 | 
					    public:
 | 
				
			||||||
 | 
					        std::size_t counter;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        virtual void accept(IRVisitor *visitor) override;
 | 
				
			||||||
 | 
					    };
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    class BinaryExpression : public Statement
 | 
				
			||||||
 | 
					    {
 | 
				
			||||||
 | 
					    public:
 | 
				
			||||||
 | 
					        Operand *lhs, *rhs;
 | 
				
			||||||
 | 
					        BinaryOperator _operator;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        BinaryExpression(Operand *lhs, Operand *rhs, BinaryOperator _operator);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        virtual void accept(IRVisitor *visitor) override;
 | 
				
			||||||
 | 
					    };
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    class BangExpression : public Statement
 | 
				
			||||||
 | 
					    {
 | 
				
			||||||
 | 
					        Operand *operand;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    public:
 | 
				
			||||||
 | 
					        BangExpression(Operand *operand);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        virtual void accept(IRVisitor *visitor) override;
 | 
				
			||||||
 | 
					    };
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
@@ -14,10 +14,10 @@ namespace elna
 | 
				
			|||||||
        class const_iterator
 | 
					        class const_iterator
 | 
				
			||||||
        {
 | 
					        {
 | 
				
			||||||
            std::string::const_iterator m_buffer;
 | 
					            std::string::const_iterator m_buffer;
 | 
				
			||||||
            source_position m_position;
 | 
					            Position m_position;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
            const_iterator(std::string::const_iterator buffer,
 | 
					            const_iterator(std::string::const_iterator buffer,
 | 
				
			||||||
                    const source_position position = source_position());
 | 
					                    const Position position = Position());
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        public:
 | 
					        public:
 | 
				
			||||||
            using iterator_category = std::forward_iterator_tag;
 | 
					            using iterator_category = std::forward_iterator_tag;
 | 
				
			||||||
@@ -26,7 +26,7 @@ namespace elna
 | 
				
			|||||||
            using pointer = const value_type *;
 | 
					            using pointer = const value_type *;
 | 
				
			||||||
            using reference = const value_type&;
 | 
					            using reference = const value_type&;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
            const source_position& position() const noexcept;
 | 
					            const Position& position() const noexcept;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
            reference operator*() const noexcept;
 | 
					            reference operator*() const noexcept;
 | 
				
			||||||
            pointer operator->() const noexcept;
 | 
					            pointer operator->() const noexcept;
 | 
				
			||||||
@@ -43,38 +43,61 @@ namespace elna
 | 
				
			|||||||
        const_iterator end() const;
 | 
					        const_iterator end() const;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    private:
 | 
					    private:
 | 
				
			||||||
        const std::string& m_buffer;
 | 
					        const std::string m_buffer;
 | 
				
			||||||
    };
 | 
					    };
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    /**
 | 
					    /**
 | 
				
			||||||
     * Union type representing a single token.
 | 
					     * Union type representing a single token.
 | 
				
			||||||
     */
 | 
					     */
 | 
				
			||||||
    struct token
 | 
					    struct Token
 | 
				
			||||||
    {
 | 
					    {
 | 
				
			||||||
        /**
 | 
					        /**
 | 
				
			||||||
         * Token type.
 | 
					         * Token type.
 | 
				
			||||||
         */
 | 
					         */
 | 
				
			||||||
        enum class type
 | 
					        enum Type : std::uint16_t
 | 
				
			||||||
        {
 | 
					        {
 | 
				
			||||||
            word,
 | 
					            TOKEN_NUMBER = 0,
 | 
				
			||||||
 | 
					            TOKEN_OPERATOR = 1,
 | 
				
			||||||
 | 
					            TOKEN_LET = 2,
 | 
				
			||||||
 | 
					            TOKEN_IDENTIFIER = 3,
 | 
				
			||||||
 | 
					            TOKEN_EQUALS = 4,
 | 
				
			||||||
 | 
					            TOKEN_VAR = 5,
 | 
				
			||||||
 | 
					            TOKEN_SEMICOLON = 6,
 | 
				
			||||||
 | 
					            TOKEN_LEFT_PAREN = 7,
 | 
				
			||||||
 | 
					            TOKEN_RIGHT_PAREN = 8,
 | 
				
			||||||
 | 
					            TOKEN_BANG = 9,
 | 
				
			||||||
 | 
					            TOKEN_DOT = 10,
 | 
				
			||||||
 | 
					            TOKEN_COMMA = 11,
 | 
				
			||||||
        };
 | 
					        };
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					 | 
				
			||||||
        /**
 | 
					        /**
 | 
				
			||||||
         * Type of the token value.
 | 
					         * Type of the token value.
 | 
				
			||||||
         */
 | 
					         */
 | 
				
			||||||
        using value = std::string;
 | 
					        union Value
 | 
				
			||||||
 | 
					        {
 | 
				
			||||||
 | 
					            std::int32_t number;
 | 
				
			||||||
 | 
					            const char *identifier;
 | 
				
			||||||
 | 
					        };
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        token(const type of, source::const_iterator begin, source::const_iterator end);
 | 
					        Token(Type of, Position position);
 | 
				
			||||||
 | 
					        Token(Type of, std::int32_t value, Position position);
 | 
				
			||||||
 | 
					        Token(Type of, const char *value, Position position);
 | 
				
			||||||
 | 
					        Token(const Token& that);
 | 
				
			||||||
 | 
					        Token(Token&& that);
 | 
				
			||||||
 | 
					        ~Token();
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        type of() const noexcept;
 | 
					        Token& operator=(const Token& that);
 | 
				
			||||||
        const value& identifier() const noexcept;
 | 
					        Token& operator=(Token&& that);
 | 
				
			||||||
        const source_position& position() const noexcept;
 | 
					
 | 
				
			||||||
 | 
					        Type of() const noexcept;
 | 
				
			||||||
 | 
					        const char *identifier() const noexcept;
 | 
				
			||||||
 | 
					        std::int32_t number() const noexcept;
 | 
				
			||||||
 | 
					        const Position& position() const noexcept;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    private:
 | 
					    private:
 | 
				
			||||||
        std::string m_value;
 | 
					        Type m_type;
 | 
				
			||||||
        source_position m_position;
 | 
					        Value m_value;
 | 
				
			||||||
        type m_type;
 | 
					        Position m_position;
 | 
				
			||||||
    };
 | 
					    };
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    /**
 | 
					    /**
 | 
				
			||||||
@@ -82,5 +105,5 @@ namespace elna
 | 
				
			|||||||
     *
 | 
					     *
 | 
				
			||||||
     * \return Tokens or error.
 | 
					     * \return Tokens or error.
 | 
				
			||||||
     */
 | 
					     */
 | 
				
			||||||
    result<std::vector<token>> lex(const std::string& buffer);
 | 
					    Token *lex(const char *buffer, CompileError *compile_error, std::size_t *length);
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 
 | 
				
			|||||||
							
								
								
									
										10
									
								
								include/elna/parser.hpp
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										10
									
								
								include/elna/parser.hpp
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,10 @@
 | 
				
			|||||||
 | 
					#pragma once
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					namespace elna
 | 
				
			||||||
 | 
					{
 | 
				
			||||||
 | 
					    enum class BinaryOperator
 | 
				
			||||||
 | 
					    {
 | 
				
			||||||
 | 
					        sum,
 | 
				
			||||||
 | 
					        subtraction
 | 
				
			||||||
 | 
					    };
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
@@ -8,7 +8,7 @@ namespace elna
 | 
				
			|||||||
    /**
 | 
					    /**
 | 
				
			||||||
     * Position in the source text.
 | 
					     * Position in the source text.
 | 
				
			||||||
     */
 | 
					     */
 | 
				
			||||||
    struct source_position
 | 
					    struct Position
 | 
				
			||||||
    {
 | 
					    {
 | 
				
			||||||
        /// Line.
 | 
					        /// Line.
 | 
				
			||||||
        std::size_t line = 1;
 | 
					        std::size_t line = 1;
 | 
				
			||||||
@@ -20,18 +20,18 @@ namespace elna
 | 
				
			|||||||
    /**
 | 
					    /**
 | 
				
			||||||
     * A compilation error consists of an error message and position.
 | 
					     * A compilation error consists of an error message and position.
 | 
				
			||||||
     */
 | 
					     */
 | 
				
			||||||
    struct compile_error
 | 
					    struct CompileError
 | 
				
			||||||
    {
 | 
					    {
 | 
				
			||||||
    private:
 | 
					    private:
 | 
				
			||||||
        char const *message;
 | 
					        char const *message;
 | 
				
			||||||
        source_position position;
 | 
					        Position position;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    public:
 | 
					    public:
 | 
				
			||||||
        /**
 | 
					        /**
 | 
				
			||||||
         * @param message Error text.
 | 
					         * @param message Error text.
 | 
				
			||||||
         * @param position Error position in the source text.
 | 
					         * @param position Error position in the source text.
 | 
				
			||||||
         */
 | 
					         */
 | 
				
			||||||
        compile_error(char const *message, const source_position position) noexcept;
 | 
					        CompileError(char const *message, const Position position) noexcept;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        /// Error text.
 | 
					        /// Error text.
 | 
				
			||||||
        const char *what() const noexcept;
 | 
					        const char *what() const noexcept;
 | 
				
			||||||
@@ -44,5 +44,19 @@ namespace elna
 | 
				
			|||||||
    };
 | 
					    };
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    template<typename T>
 | 
					    template<typename T>
 | 
				
			||||||
    using result = boost::outcome_v2::result<T, compile_error>;
 | 
					    using result = boost::outcome_v2::result<T, CompileError>;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    enum class Target
 | 
				
			||||||
 | 
					    {
 | 
				
			||||||
 | 
					        text,
 | 
				
			||||||
 | 
					        high20,
 | 
				
			||||||
 | 
					        lower12i
 | 
				
			||||||
 | 
					    };
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    struct Reference
 | 
				
			||||||
 | 
					    {
 | 
				
			||||||
 | 
					        const char* name;
 | 
				
			||||||
 | 
					        size_t offset;
 | 
				
			||||||
 | 
					        Target target;
 | 
				
			||||||
 | 
					    };
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 
 | 
				
			|||||||
							
								
								
									
										148
									
								
								include/elna/riscv.hpp
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										148
									
								
								include/elna/riscv.hpp
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,148 @@
 | 
				
			|||||||
 | 
					#pragma once
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					#include <cstdint>
 | 
				
			||||||
 | 
					#include "elna/ir.hpp"
 | 
				
			||||||
 | 
					#include "elna/result.hpp"
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					namespace elna
 | 
				
			||||||
 | 
					{
 | 
				
			||||||
 | 
					    enum class XRegister : std::uint8_t
 | 
				
			||||||
 | 
					    {
 | 
				
			||||||
 | 
					        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 class Funct3 : std::uint8_t
 | 
				
			||||||
 | 
					    {
 | 
				
			||||||
 | 
					        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 class Funct12 : std::uint8_t
 | 
				
			||||||
 | 
					    {
 | 
				
			||||||
 | 
					        ecall = 0b000000000000,
 | 
				
			||||||
 | 
					        ebreak = 0b000000000001,
 | 
				
			||||||
 | 
					    };
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    enum class Funct7 : std::uint8_t
 | 
				
			||||||
 | 
					    {
 | 
				
			||||||
 | 
					        none = 0,
 | 
				
			||||||
 | 
					        sub = 0b0100000
 | 
				
			||||||
 | 
					    };
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    enum class BaseOpcode : std::uint8_t
 | 
				
			||||||
 | 
					    {
 | 
				
			||||||
 | 
					        opImm = 0b0010011,
 | 
				
			||||||
 | 
					        lui = 0b0110111,
 | 
				
			||||||
 | 
					        auipc = 0b0010111,
 | 
				
			||||||
 | 
					        op = 0b0110011,
 | 
				
			||||||
 | 
					        jal = 0b1101111,
 | 
				
			||||||
 | 
					        jalr = 0b1100111,
 | 
				
			||||||
 | 
					        branch = 0b1100011,
 | 
				
			||||||
 | 
					        load = 0b0000011,
 | 
				
			||||||
 | 
					        store = 0b0100011,
 | 
				
			||||||
 | 
					        miscMem = 0b0001111,
 | 
				
			||||||
 | 
					        system = 0b1110011,
 | 
				
			||||||
 | 
					    };
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    struct Instruction
 | 
				
			||||||
 | 
					    {
 | 
				
			||||||
 | 
					        Instruction(BaseOpcode opcode);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        Instruction& i(XRegister rd, Funct3 funct3, XRegister rs1, std::uint32_t immediate);
 | 
				
			||||||
 | 
					        Instruction& s(std::uint32_t imm1, Funct3 funct3, XRegister rs1, XRegister rs2);
 | 
				
			||||||
 | 
					        Instruction& r(XRegister rd, Funct3 funct3, XRegister rs1, XRegister rs2, Funct7 funct7 = Funct7::none);
 | 
				
			||||||
 | 
					        Instruction& u(XRegister rd, std::uint32_t imm);
 | 
				
			||||||
 | 
					        std::uint8_t *encode();
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    private:
 | 
				
			||||||
 | 
					        std::uint32_t instruction{ 0 };
 | 
				
			||||||
 | 
					    };
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    class RiscVVisitor : public ir::IRVisitor
 | 
				
			||||||
 | 
					    {
 | 
				
			||||||
 | 
					        Instruction *instructions;
 | 
				
			||||||
 | 
					        std::size_t instructionsLength;
 | 
				
			||||||
 | 
					        bool registerInUse;
 | 
				
			||||||
 | 
					        std::uint32_t variableCounter = 1;
 | 
				
			||||||
 | 
					        Reference references[3];
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        virtual void visit(ir::Node *) override;
 | 
				
			||||||
 | 
					        virtual void visit(ir::Definition *definition) override;
 | 
				
			||||||
 | 
					        virtual void visit(ir::Operand *operand) override;
 | 
				
			||||||
 | 
					        virtual void visit(ir::Variable *variable) override;
 | 
				
			||||||
 | 
					        virtual void visit(ir::Number *number) override;
 | 
				
			||||||
 | 
					        virtual void visit(ir::BinaryExpression *expression) override;
 | 
				
			||||||
 | 
					    };
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
@@ -2,6 +2,7 @@ module elna.backend;
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
import core.stdc.stdio;
 | 
					import core.stdc.stdio;
 | 
				
			||||||
import core.stdc.stdlib;
 | 
					import core.stdc.stdlib;
 | 
				
			||||||
 | 
					import core.stdc.string;
 | 
				
			||||||
import elna.elf;
 | 
					import elna.elf;
 | 
				
			||||||
import elna.ir;
 | 
					import elna.ir;
 | 
				
			||||||
import elna.extended;
 | 
					import elna.extended;
 | 
				
			||||||
@@ -17,7 +18,7 @@ import tanya.container.array;
 | 
				
			|||||||
import tanya.container.string;
 | 
					import tanya.container.string;
 | 
				
			||||||
import tanya.memory.allocator;
 | 
					import tanya.memory.allocator;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
private Nullable!String readSource(string source) @nogc
 | 
					private char* readSource(string source) @nogc
 | 
				
			||||||
{
 | 
					{
 | 
				
			||||||
    enum size_t bufferSize = 255;
 | 
					    enum size_t bufferSize = 255;
 | 
				
			||||||
    auto sourceFilename = String(source);
 | 
					    auto sourceFilename = String(source);
 | 
				
			||||||
@@ -25,31 +26,39 @@ private Nullable!String readSource(string source) @nogc
 | 
				
			|||||||
    return readFile(sourceFilename).match!(
 | 
					    return readFile(sourceFilename).match!(
 | 
				
			||||||
        (ErrorCode errorCode) {
 | 
					        (ErrorCode errorCode) {
 | 
				
			||||||
            perror(sourceFilename.toStringz);
 | 
					            perror(sourceFilename.toStringz);
 | 
				
			||||||
            return Nullable!String();
 | 
					            return null;
 | 
				
			||||||
        },
 | 
					        },
 | 
				
			||||||
        (Array!ubyte contents) => nullable(String(cast(char[]) contents.get))
 | 
					        (Array!ubyte contents) {
 | 
				
			||||||
 | 
					            char* cString = cast(char*) malloc(contents.length + 1);
 | 
				
			||||||
 | 
					            memcpy(cString, contents.get.ptr, contents.length);
 | 
				
			||||||
 | 
					            cString[contents.length] = '\0';
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					            return cString;
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
    );
 | 
					    );
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
int generate(string inFile, ref String outputFilename) @nogc
 | 
					int generate(string inFile, ref String outputFilename) @nogc
 | 
				
			||||||
{
 | 
					{
 | 
				
			||||||
    auto sourceText = readSource(inFile);
 | 
					    auto sourceText = readSource(inFile);
 | 
				
			||||||
    if (sourceText.isNull)
 | 
					    if (sourceText is null)
 | 
				
			||||||
    {
 | 
					    {
 | 
				
			||||||
        return 3;
 | 
					        return 3;
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
    auto tokens = lex(sourceText.get.get);
 | 
					    CompileError compileError = void;
 | 
				
			||||||
    if (!tokens.valid)
 | 
					    size_t tokensCount;
 | 
				
			||||||
 | 
					    auto tokens = lex(sourceText, &compileError, &tokensCount);
 | 
				
			||||||
 | 
					    free(sourceText);
 | 
				
			||||||
 | 
					    if (tokens is null)
 | 
				
			||||||
    {
 | 
					    {
 | 
				
			||||||
        auto compileError = tokens.error.get;
 | 
					        printf("%lu:%lu: %s\n", compileError.line, compileError.column, compileError.what);
 | 
				
			||||||
        printf("%lu:%lu: %s\n", compileError.line, compileError.column, compileError.message.ptr);
 | 
					 | 
				
			||||||
        return 1;
 | 
					        return 1;
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
    auto ast = parse(tokens.result);
 | 
					    auto ast = parse(tokens[0 .. tokensCount]);
 | 
				
			||||||
    if (!ast.valid)
 | 
					    if (!ast.valid)
 | 
				
			||||||
    {
 | 
					    {
 | 
				
			||||||
        auto compileError = ast.error.get;
 | 
					        compileError = ast.error.get;
 | 
				
			||||||
        printf("%lu:%lu: %s\n", compileError.line, compileError.column, compileError.message.ptr);
 | 
					        printf("%lu:%lu: %s\n", compileError.line, compileError.column, compileError.what);
 | 
				
			||||||
        return 2;
 | 
					        return 2;
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
    auto transformVisitor = cast(TransformVisitor) malloc(__traits(classInstanceSize, TransformVisitor));
 | 
					    auto transformVisitor = cast(TransformVisitor) malloc(__traits(classInstanceSize, TransformVisitor));
 | 
				
			||||||
@@ -85,18 +94,18 @@ int generate(string inFile, ref String outputFilename) @nogc
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
        final switch (reference.target)
 | 
					        final switch (reference.target)
 | 
				
			||||||
        {
 | 
					        {
 | 
				
			||||||
            case Reference.Target.text:
 | 
					            case Target.text:
 | 
				
			||||||
                relocationEntry.r_info = R_RISCV_CALL;
 | 
					                relocationEntry.r_info = R_RISCV_CALL;
 | 
				
			||||||
                break;
 | 
					                break;
 | 
				
			||||||
            case Reference.Target.high20:
 | 
					            case Target.high20:
 | 
				
			||||||
                relocationEntry.r_info = R_RISCV_HI20;
 | 
					                relocationEntry.r_info = R_RISCV_HI20;
 | 
				
			||||||
                break;
 | 
					                break;
 | 
				
			||||||
            case Reference.Target.lower12i:
 | 
					            case Target.lower12i:
 | 
				
			||||||
                relocationEntry.r_info = R_RISCV_LO12_I;
 | 
					                relocationEntry.r_info = R_RISCV_LO12_I;
 | 
				
			||||||
                break;
 | 
					                break;
 | 
				
			||||||
        }
 | 
					        }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        elf.relocate(reference.name, relocationEntry, relocationSub);
 | 
					        elf.relocate(String(reference.name[0 .. strlen(reference.name)]), relocationEntry, relocationSub);
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    elf.finish();
 | 
					    elf.finish();
 | 
				
			||||||
 
 | 
				
			|||||||
							
								
								
									
										104
									
								
								source/elna/ir.d
									
									
									
									
									
								
							
							
						
						
									
										104
									
								
								source/elna/ir.d
									
									
									
									
									
								
							@@ -1,5 +1,6 @@
 | 
				
			|||||||
module elna.ir;
 | 
					module elna.ir;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					import core.stdc.stdlib;
 | 
				
			||||||
import parser = elna.parser;
 | 
					import parser = elna.parser;
 | 
				
			||||||
import tanya.container.array;
 | 
					import tanya.container.array;
 | 
				
			||||||
import tanya.container.hashtable;
 | 
					import tanya.container.hashtable;
 | 
				
			||||||
@@ -14,7 +15,6 @@ struct ASTMapping
 | 
				
			|||||||
{
 | 
					{
 | 
				
			||||||
    alias Node = .Node;
 | 
					    alias Node = .Node;
 | 
				
			||||||
    alias Definition = .Definition;
 | 
					    alias Definition = .Definition;
 | 
				
			||||||
    alias VariableDeclaration = .VariableDeclaration;
 | 
					 | 
				
			||||||
    alias Statement = .Operand;
 | 
					    alias Statement = .Operand;
 | 
				
			||||||
    alias BangStatement = .Operand;
 | 
					    alias BangStatement = .Operand;
 | 
				
			||||||
    alias Block = .Definition;
 | 
					    alias Block = .Definition;
 | 
				
			||||||
@@ -28,20 +28,21 @@ struct ASTMapping
 | 
				
			|||||||
/**
 | 
					/**
 | 
				
			||||||
 * IR visitor.
 | 
					 * IR visitor.
 | 
				
			||||||
*/
 | 
					*/
 | 
				
			||||||
interface IRVisitor
 | 
					extern(C++, "elna", "ir")
 | 
				
			||||||
 | 
					abstract class IRVisitor
 | 
				
			||||||
{
 | 
					{
 | 
				
			||||||
    abstract void visit(Node) @nogc;
 | 
					    abstract void visit(Node) @nogc;
 | 
				
			||||||
    abstract void visit(Definition) @nogc;
 | 
					    abstract void visit(Definition) @nogc;
 | 
				
			||||||
    abstract void visit(Operand) @nogc;
 | 
					    abstract void visit(Operand) @nogc;
 | 
				
			||||||
    abstract void visit(BinaryExpression) @nogc;
 | 
					    abstract void visit(BinaryExpression) @nogc;
 | 
				
			||||||
    abstract void visit(Variable) @nogc;
 | 
					    abstract void visit(Variable) @nogc;
 | 
				
			||||||
    abstract void visit(VariableDeclaration) @nogc;
 | 
					 | 
				
			||||||
    abstract void visit(Number) @nogc;
 | 
					    abstract void visit(Number) @nogc;
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
/**
 | 
					/**
 | 
				
			||||||
 * AST node.
 | 
					 * AST node.
 | 
				
			||||||
 */
 | 
					 */
 | 
				
			||||||
 | 
					extern(C++, "elna", "ir")
 | 
				
			||||||
abstract class Node
 | 
					abstract class Node
 | 
				
			||||||
{
 | 
					{
 | 
				
			||||||
    abstract void accept(IRVisitor) @nogc;
 | 
					    abstract void accept(IRVisitor) @nogc;
 | 
				
			||||||
@@ -50,99 +51,69 @@ abstract class Node
 | 
				
			|||||||
/**
 | 
					/**
 | 
				
			||||||
 * Definition.
 | 
					 * Definition.
 | 
				
			||||||
 */
 | 
					 */
 | 
				
			||||||
 | 
					extern(C++, "elna", "ir")
 | 
				
			||||||
class Definition : Node
 | 
					class Definition : Node
 | 
				
			||||||
{
 | 
					{
 | 
				
			||||||
    char[] identifier;
 | 
					    BinaryExpression* statements;
 | 
				
			||||||
    Array!BinaryExpression statements;
 | 
					    size_t statementsLength;
 | 
				
			||||||
    Array!VariableDeclaration variableDeclarations;
 | 
					 | 
				
			||||||
    Operand result;
 | 
					    Operand result;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    override void accept(IRVisitor visitor) @nogc
 | 
					    override void accept(IRVisitor visitor) @nogc;
 | 
				
			||||||
    {
 | 
					 | 
				
			||||||
        visitor.visit(this);
 | 
					 | 
				
			||||||
    }
 | 
					 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					extern(C++, "elna", "ir")
 | 
				
			||||||
abstract class Statement : Node
 | 
					abstract class Statement : Node
 | 
				
			||||||
{
 | 
					{
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					extern(C++, "elna", "ir")
 | 
				
			||||||
abstract class Operand : Node
 | 
					abstract class Operand : Node
 | 
				
			||||||
{
 | 
					{
 | 
				
			||||||
    override void accept(IRVisitor visitor) @nogc
 | 
					    override void accept(IRVisitor visitor) @nogc;
 | 
				
			||||||
    {
 | 
					 | 
				
			||||||
        visitor.visit(this);
 | 
					 | 
				
			||||||
    }
 | 
					 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					extern(C++, "elna", "ir")
 | 
				
			||||||
class Number : Operand
 | 
					class Number : Operand
 | 
				
			||||||
{
 | 
					{
 | 
				
			||||||
    int value;
 | 
					    int value;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    override void accept(IRVisitor visitor) @nogc
 | 
					    override void accept(IRVisitor visitor) @nogc;
 | 
				
			||||||
    {
 | 
					 | 
				
			||||||
        visitor.visit(this);
 | 
					 | 
				
			||||||
    }
 | 
					 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					extern(C++, "elna", "ir")
 | 
				
			||||||
class Variable : Operand
 | 
					class Variable : Operand
 | 
				
			||||||
{
 | 
					{
 | 
				
			||||||
    size_t counter;
 | 
					    size_t counter;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    override void accept(IRVisitor visitor) @nogc
 | 
					    override void accept(IRVisitor visitor) @nogc;
 | 
				
			||||||
    {
 | 
					 | 
				
			||||||
        visitor.visit(this);
 | 
					 | 
				
			||||||
    }
 | 
					 | 
				
			||||||
}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
class VariableDeclaration : Node
 | 
					 | 
				
			||||||
{
 | 
					 | 
				
			||||||
    String identifier;
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    override void accept(IRVisitor visitor) @nogc
 | 
					 | 
				
			||||||
    {
 | 
					 | 
				
			||||||
        visitor.visit(this);
 | 
					 | 
				
			||||||
    }
 | 
					 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					extern(C++, "elna", "ir")
 | 
				
			||||||
class BinaryExpression : Statement
 | 
					class BinaryExpression : Statement
 | 
				
			||||||
{
 | 
					{
 | 
				
			||||||
    Operand lhs, rhs;
 | 
					    Operand lhs, rhs;
 | 
				
			||||||
    BinaryOperator operator;
 | 
					    BinaryOperator operator;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    this(Operand lhs, Operand rhs, BinaryOperator operator)
 | 
					    this(Operand lhs, Operand rhs, BinaryOperator operator) @nogc;
 | 
				
			||||||
    @nogc
 | 
					 | 
				
			||||||
    {
 | 
					 | 
				
			||||||
        this.lhs = lhs;
 | 
					 | 
				
			||||||
        this.rhs = rhs;
 | 
					 | 
				
			||||||
        this.operator = operator;
 | 
					 | 
				
			||||||
    }
 | 
					 | 
				
			||||||
 | 
					
 | 
				
			||||||
    override void accept(IRVisitor visitor) @nogc
 | 
					    override void accept(IRVisitor visitor) @nogc;
 | 
				
			||||||
    {
 | 
					 | 
				
			||||||
        visitor.visit(this);
 | 
					 | 
				
			||||||
    }
 | 
					 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					extern(C++, "elna", "ir")
 | 
				
			||||||
class BangExpression : Statement
 | 
					class BangExpression : Statement
 | 
				
			||||||
{
 | 
					{
 | 
				
			||||||
    Operand operand;
 | 
					    Operand operand;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    this(Operand operand)
 | 
					    this(Operand operand);
 | 
				
			||||||
    {
 | 
					 | 
				
			||||||
        this.operand = operand;
 | 
					 | 
				
			||||||
    }
 | 
					 | 
				
			||||||
 | 
					
 | 
				
			||||||
    override void accept(IRVisitor visitor) @nogc
 | 
					    override void accept(IRVisitor visitor) @nogc;
 | 
				
			||||||
    {
 | 
					 | 
				
			||||||
        visitor.visit(this);
 | 
					 | 
				
			||||||
    }
 | 
					 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
final class TransformVisitor : parser.ParserVisitor!ASTMapping
 | 
					final class TransformVisitor : parser.ParserVisitor!ASTMapping
 | 
				
			||||||
{
 | 
					{
 | 
				
			||||||
    private HashTable!(String, int) constants;
 | 
					    private HashTable!(String, int) constants;
 | 
				
			||||||
    private Array!BinaryExpression statements;
 | 
					    private BinaryExpression* statements;
 | 
				
			||||||
 | 
					    private size_t statementsLength;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    ASTMapping.Node visit(parser.Node node) @nogc
 | 
					    ASTMapping.Node visit(parser.Node node) @nogc
 | 
				
			||||||
    {
 | 
					    {
 | 
				
			||||||
@@ -154,11 +125,6 @@ final class TransformVisitor : parser.ParserVisitor!ASTMapping
 | 
				
			|||||||
        assert(false, "Not implemented");
 | 
					        assert(false, "Not implemented");
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    ASTMapping.VariableDeclaration visit(parser.VariableDeclaration declaration) @nogc
 | 
					 | 
				
			||||||
    {
 | 
					 | 
				
			||||||
        assert(false, "Not implemented");
 | 
					 | 
				
			||||||
    }
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    ASTMapping.BangStatement visit(parser.BangStatement statement) @nogc
 | 
					    ASTMapping.BangStatement visit(parser.BangStatement statement) @nogc
 | 
				
			||||||
    {
 | 
					    {
 | 
				
			||||||
        return statement.expression.accept(this);
 | 
					        return statement.expression.accept(this);
 | 
				
			||||||
@@ -171,8 +137,7 @@ final class TransformVisitor : parser.ParserVisitor!ASTMapping
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
        target.result = block.statement.accept(this);
 | 
					        target.result = block.statement.accept(this);
 | 
				
			||||||
        target.statements = this.statements;
 | 
					        target.statements = this.statements;
 | 
				
			||||||
 | 
					        target.statementsLength = this.statementsLength;
 | 
				
			||||||
        target.variableDeclarations = transformVariableDeclarations(block.variableDeclarations);
 | 
					 | 
				
			||||||
 | 
					
 | 
				
			||||||
        return target;
 | 
					        return target;
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
@@ -217,10 +182,12 @@ final class TransformVisitor : parser.ParserVisitor!ASTMapping
 | 
				
			|||||||
            binaryExpression.rhs.accept(this),
 | 
					            binaryExpression.rhs.accept(this),
 | 
				
			||||||
            binaryExpression.operator
 | 
					            binaryExpression.operator
 | 
				
			||||||
        );
 | 
					        );
 | 
				
			||||||
        statements.insertBack(target);
 | 
					        this.statements = cast(BinaryExpression*)
 | 
				
			||||||
 | 
					            realloc(this.statements, (this.statementsLength + 1) * BinaryExpression.sizeof);
 | 
				
			||||||
 | 
					        this.statements[this.statementsLength++] = target;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        auto newVariable = defaultAllocator.make!Variable;
 | 
					        auto newVariable = defaultAllocator.make!Variable;
 | 
				
			||||||
        newVariable.counter = statements.length;
 | 
					        newVariable.counter = this.statementsLength;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        return newVariable;
 | 
					        return newVariable;
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
@@ -250,19 +217,4 @@ final class TransformVisitor : parser.ParserVisitor!ASTMapping
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
        return constants;
 | 
					        return constants;
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
 | 
					 | 
				
			||||||
    Array!VariableDeclaration transformVariableDeclarations(ref Array!(parser.VariableDeclaration) variableDeclarations)
 | 
					 | 
				
			||||||
    @nogc
 | 
					 | 
				
			||||||
    {
 | 
					 | 
				
			||||||
        typeof(return) variables;
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
        foreach (ref variableDeclaration; variableDeclarations)
 | 
					 | 
				
			||||||
        {
 | 
					 | 
				
			||||||
            auto newDeclaration = defaultAllocator.make!VariableDeclaration;
 | 
					 | 
				
			||||||
            newDeclaration.identifier = variableDeclaration.identifier;
 | 
					 | 
				
			||||||
            variables.insertBack(newDeclaration);
 | 
					 | 
				
			||||||
        }
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
        return variables;
 | 
					 | 
				
			||||||
    }
 | 
					 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -8,28 +8,29 @@ import std.range;
 | 
				
			|||||||
import tanya.container.array;
 | 
					import tanya.container.array;
 | 
				
			||||||
import tanya.container.string;
 | 
					import tanya.container.string;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					extern(C++, "elna")
 | 
				
			||||||
struct Token
 | 
					struct Token
 | 
				
			||||||
{
 | 
					{
 | 
				
			||||||
    enum Type
 | 
					    enum Type : ushort
 | 
				
			||||||
    {
 | 
					    {
 | 
				
			||||||
        number,
 | 
					        number = 0,
 | 
				
			||||||
        operator,
 | 
					        operator = 1,
 | 
				
			||||||
        let,
 | 
					        let = 2,
 | 
				
			||||||
        identifier,
 | 
					        identifier = 3,
 | 
				
			||||||
        equals,
 | 
					        equals = 4,
 | 
				
			||||||
        var,
 | 
					        var = 5,
 | 
				
			||||||
        semicolon,
 | 
					        semicolon = 6,
 | 
				
			||||||
        leftParen,
 | 
					        leftParen = 7,
 | 
				
			||||||
        rightParen,
 | 
					        rightParen = 8,
 | 
				
			||||||
        bang,
 | 
					        bang = 9,
 | 
				
			||||||
        dot,
 | 
					        dot = 10,
 | 
				
			||||||
        comma,
 | 
					        comma = 11,
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    union Value
 | 
					    union Value
 | 
				
			||||||
    {
 | 
					    {
 | 
				
			||||||
        int number;
 | 
					        int number;
 | 
				
			||||||
        String identifier;
 | 
					        const(char)* identifier;
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    private Type type;
 | 
					    private Type type;
 | 
				
			||||||
@@ -38,62 +39,21 @@ struct Token
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
    @disable this();
 | 
					    @disable this();
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    this(Type type, Position position) @nogc nothrow pure @safe
 | 
					    this(Type type, Position position) @nogc nothrow pure @safe;
 | 
				
			||||||
    {
 | 
					    this(Type type, int value, Position position) @nogc nothrow pure @trusted;
 | 
				
			||||||
        this.type = type;
 | 
					    this(Type type, const(char)* value, Position position) @nogc nothrow;
 | 
				
			||||||
        this.position_ = position;
 | 
					 | 
				
			||||||
    }
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    this(Type type, int value, Position position) @nogc nothrow pure @trusted
 | 
					 | 
				
			||||||
    in (type == Type.number)
 | 
					 | 
				
			||||||
    {
 | 
					 | 
				
			||||||
        this(type, position);
 | 
					 | 
				
			||||||
        this.value_.number = value;
 | 
					 | 
				
			||||||
    }
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    this()(Type type, auto ref String value, Position position)
 | 
					 | 
				
			||||||
    @nogc nothrow pure @trusted
 | 
					 | 
				
			||||||
    in (type == Type.identifier || type == Type.operator)
 | 
					 | 
				
			||||||
    {
 | 
					 | 
				
			||||||
        this(type, position);
 | 
					 | 
				
			||||||
        this.value_.identifier = value;
 | 
					 | 
				
			||||||
    }
 | 
					 | 
				
			||||||
 | 
					
 | 
				
			||||||
    /**
 | 
					    /**
 | 
				
			||||||
     * Params:
 | 
					     * Returns: Expected token type.
 | 
				
			||||||
     *   type = Expected type.
 | 
					 | 
				
			||||||
     *
 | 
					 | 
				
			||||||
     * Returns: Whether this token is of the expected type.
 | 
					 | 
				
			||||||
     */
 | 
					     */
 | 
				
			||||||
    bool ofType(Type type) const @nogc nothrow pure @safe
 | 
					    Type of() const @nogc nothrow pure @safe;
 | 
				
			||||||
    {
 | 
					    const(char)* identifier() const @nogc nothrow pure;
 | 
				
			||||||
        return this.type == type;
 | 
					    int number() const @nogc nothrow pure;
 | 
				
			||||||
    }
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    @property auto value(Type type)() @nogc nothrow pure @trusted
 | 
					 | 
				
			||||||
    in (ofType(type))
 | 
					 | 
				
			||||||
    {
 | 
					 | 
				
			||||||
        static if (type == Type.number)
 | 
					 | 
				
			||||||
        {
 | 
					 | 
				
			||||||
            return this.value_.number;
 | 
					 | 
				
			||||||
        }
 | 
					 | 
				
			||||||
        else static if (type == Type.identifier || type == Type.operator)
 | 
					 | 
				
			||||||
        {
 | 
					 | 
				
			||||||
            return this.value_.identifier;
 | 
					 | 
				
			||||||
        }
 | 
					 | 
				
			||||||
        else
 | 
					 | 
				
			||||||
        {
 | 
					 | 
				
			||||||
            static assert(false, "This type doesn't have a value");
 | 
					 | 
				
			||||||
        }
 | 
					 | 
				
			||||||
    }
 | 
					 | 
				
			||||||
 | 
					
 | 
				
			||||||
    /**
 | 
					    /**
 | 
				
			||||||
     * Returns: The token position in the source text.
 | 
					     * Returns: The token position in the source text.
 | 
				
			||||||
     */
 | 
					     */
 | 
				
			||||||
    @property const(Position) position() const @nogc nothrow pure @safe
 | 
					    @property const(Position) position() const @nogc nothrow pure @safe;
 | 
				
			||||||
    {
 | 
					 | 
				
			||||||
        return this.position_;
 | 
					 | 
				
			||||||
    }
 | 
					 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
/**
 | 
					/**
 | 
				
			||||||
@@ -101,10 +61,10 @@ struct Token
 | 
				
			|||||||
 */
 | 
					 */
 | 
				
			||||||
struct Source
 | 
					struct Source
 | 
				
			||||||
{
 | 
					{
 | 
				
			||||||
    char[] buffer;
 | 
					    const(char)* buffer;
 | 
				
			||||||
    Position position;
 | 
					    Position position;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    this(char[] buffer) @nogc nothrow pure @safe
 | 
					    this(const(char)* buffer) @nogc nothrow pure @safe
 | 
				
			||||||
    {
 | 
					    {
 | 
				
			||||||
        this.buffer = buffer;
 | 
					        this.buffer = buffer;
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
@@ -113,7 +73,7 @@ struct Source
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
    bool empty() @nogc nothrow pure @safe
 | 
					    bool empty() @nogc nothrow pure @safe
 | 
				
			||||||
    {
 | 
					    {
 | 
				
			||||||
        return this.length == 0;
 | 
					        return this.buffer is null || this.buffer[0] == '\0';
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    char front() @nogc nothrow pure @safe
 | 
					    char front() @nogc nothrow pure @safe
 | 
				
			||||||
@@ -122,133 +82,36 @@ struct Source
 | 
				
			|||||||
        return this.buffer[0];
 | 
					        return this.buffer[0];
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    void popFront() @nogc nothrow pure @safe
 | 
					    void popFront() @nogc nothrow pure
 | 
				
			||||||
    in (!empty)
 | 
					    in (!empty)
 | 
				
			||||||
    {
 | 
					    {
 | 
				
			||||||
        this.buffer = buffer[1 .. $];
 | 
					        ++this.buffer;
 | 
				
			||||||
        ++this.position.column;
 | 
					        ++this.position.column;
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    void breakLine() @nogc nothrow pure @safe
 | 
					    void breakLine() @nogc nothrow pure
 | 
				
			||||||
    in (!empty)
 | 
					    in (!empty)
 | 
				
			||||||
    {
 | 
					    {
 | 
				
			||||||
        this.buffer = buffer[1 .. $];
 | 
					        ++this.buffer;
 | 
				
			||||||
        ++this.position.line;
 | 
					        ++this.position.line;
 | 
				
			||||||
        this.position.column = 1;
 | 
					        this.position.column = 1;
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    @property size_t length() const @nogc nothrow pure @safe
 | 
					    @property size_t length() const @nogc nothrow pure
 | 
				
			||||||
    {
 | 
					    {
 | 
				
			||||||
        return this.buffer.length;
 | 
					        return strlen(this.buffer);
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    char opIndex(size_t index) @nogc nothrow pure @safe
 | 
					    char opIndex(size_t index) @nogc nothrow pure
 | 
				
			||||||
    in (index < length)
 | 
					 | 
				
			||||||
    {
 | 
					    {
 | 
				
			||||||
        return this.buffer[index];
 | 
					        return this.buffer[index];
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    char[] opSlice(size_t i, size_t j) @nogc nothrow pure @safe
 | 
					    const(char)[] opSlice(size_t i, size_t j) @nogc nothrow pure
 | 
				
			||||||
    in
 | 
					 | 
				
			||||||
    {
 | 
					 | 
				
			||||||
        assert(i <= j);
 | 
					 | 
				
			||||||
        assert(j <= length);
 | 
					 | 
				
			||||||
    }
 | 
					 | 
				
			||||||
    do
 | 
					 | 
				
			||||||
    {
 | 
					    {
 | 
				
			||||||
        return this.buffer[i .. j];
 | 
					        return this.buffer[i .. j];
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
Result!(Array!Token) lex(char[] buffer) @nogc
 | 
					extern(C++, "elna")
 | 
				
			||||||
{
 | 
					Token* lex(const(char)* buffer, CompileError* compileError, size_t* length) @nogc;
 | 
				
			||||||
    Array!Token tokens;
 | 
					 | 
				
			||||||
    auto source = Source(buffer);
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    while (!source.empty)
 | 
					 | 
				
			||||||
    {
 | 
					 | 
				
			||||||
        if (source.front == ' ')
 | 
					 | 
				
			||||||
        {
 | 
					 | 
				
			||||||
            source.popFront;
 | 
					 | 
				
			||||||
        }
 | 
					 | 
				
			||||||
        else if (source.front >= '0' && source.front <= '9') // Multi-digit.
 | 
					 | 
				
			||||||
        {
 | 
					 | 
				
			||||||
            tokens.insertBack(Token(Token.Type.number, source.front - '0', source.position));
 | 
					 | 
				
			||||||
            source.popFront;
 | 
					 | 
				
			||||||
        }
 | 
					 | 
				
			||||||
        else if (source.front == '=')
 | 
					 | 
				
			||||||
        {
 | 
					 | 
				
			||||||
            tokens.insertBack(Token(Token.Type.equals, source.position));
 | 
					 | 
				
			||||||
            source.popFront;
 | 
					 | 
				
			||||||
        }
 | 
					 | 
				
			||||||
        else if (source.front == '(')
 | 
					 | 
				
			||||||
        {
 | 
					 | 
				
			||||||
            tokens.insertBack(Token(Token.Type.leftParen, source.position));
 | 
					 | 
				
			||||||
            source.popFront;
 | 
					 | 
				
			||||||
        }
 | 
					 | 
				
			||||||
        else if (source.front == ')')
 | 
					 | 
				
			||||||
        {
 | 
					 | 
				
			||||||
            tokens.insertBack(Token(Token.Type.rightParen, source.position));
 | 
					 | 
				
			||||||
            source.popFront;
 | 
					 | 
				
			||||||
        }
 | 
					 | 
				
			||||||
        else if (source.front == ';')
 | 
					 | 
				
			||||||
        {
 | 
					 | 
				
			||||||
            tokens.insertBack(Token(Token.Type.semicolon, source.position));
 | 
					 | 
				
			||||||
            source.popFront;
 | 
					 | 
				
			||||||
        }
 | 
					 | 
				
			||||||
        else if (source.front == ',')
 | 
					 | 
				
			||||||
        {
 | 
					 | 
				
			||||||
            tokens.insertBack(Token(Token.Type.comma, source.position));
 | 
					 | 
				
			||||||
            source.popFront;
 | 
					 | 
				
			||||||
        }
 | 
					 | 
				
			||||||
        else if (source.front == '!')
 | 
					 | 
				
			||||||
        {
 | 
					 | 
				
			||||||
            tokens.insertBack(Token(Token.Type.bang, source.position));
 | 
					 | 
				
			||||||
            source.popFront;
 | 
					 | 
				
			||||||
        }
 | 
					 | 
				
			||||||
        else if (source.front == '.')
 | 
					 | 
				
			||||||
        {
 | 
					 | 
				
			||||||
            tokens.insertBack(Token(Token.Type.dot, source.position));
 | 
					 | 
				
			||||||
            source.popFront;
 | 
					 | 
				
			||||||
        }
 | 
					 | 
				
			||||||
        else if (isalpha(source.front))
 | 
					 | 
				
			||||||
        {
 | 
					 | 
				
			||||||
            size_t i = 1;
 | 
					 | 
				
			||||||
            while (i < source.length && isalpha(source[i]))
 | 
					 | 
				
			||||||
            {
 | 
					 | 
				
			||||||
                ++i;
 | 
					 | 
				
			||||||
            }
 | 
					 | 
				
			||||||
            if (source[0 .. i] == "const")
 | 
					 | 
				
			||||||
            {
 | 
					 | 
				
			||||||
                tokens.insertBack(Token(Token.Type.let, source.position));
 | 
					 | 
				
			||||||
            }
 | 
					 | 
				
			||||||
            else if (source[0 .. i] == "var")
 | 
					 | 
				
			||||||
            {
 | 
					 | 
				
			||||||
                tokens.insertBack(Token(Token.Type.var, source.position));
 | 
					 | 
				
			||||||
            }
 | 
					 | 
				
			||||||
            else
 | 
					 | 
				
			||||||
            {
 | 
					 | 
				
			||||||
                auto identifier = String(source[0 .. i]);
 | 
					 | 
				
			||||||
                tokens.insertBack(Token(Token.Type.identifier, identifier, source.position));
 | 
					 | 
				
			||||||
            }
 | 
					 | 
				
			||||||
            source.popFrontN(i);
 | 
					 | 
				
			||||||
        }
 | 
					 | 
				
			||||||
        else if (source.front == '+' || source.front == '-')
 | 
					 | 
				
			||||||
        {
 | 
					 | 
				
			||||||
            String operator;
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
            operator.insertBack(source.front);
 | 
					 | 
				
			||||||
            tokens.insertBack(Token(Token.Type.operator, operator, source.position));
 | 
					 | 
				
			||||||
            source.popFront;
 | 
					 | 
				
			||||||
        }
 | 
					 | 
				
			||||||
        else if (source.front == '\n')
 | 
					 | 
				
			||||||
        {
 | 
					 | 
				
			||||||
            source.breakLine;
 | 
					 | 
				
			||||||
        }
 | 
					 | 
				
			||||||
        else
 | 
					 | 
				
			||||||
        {
 | 
					 | 
				
			||||||
            return typeof(return)("Unexptected next character", source.position);
 | 
					 | 
				
			||||||
        }
 | 
					 | 
				
			||||||
    }
 | 
					 | 
				
			||||||
    return typeof(return)(tokens);
 | 
					 | 
				
			||||||
}
 | 
					 | 
				
			||||||
 
 | 
				
			|||||||
@@ -1,11 +1,12 @@
 | 
				
			|||||||
module elna.parser;
 | 
					module elna.parser;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					import core.stdc.string;
 | 
				
			||||||
import elna.lexer;
 | 
					import elna.lexer;
 | 
				
			||||||
import elna.result;
 | 
					import elna.result;
 | 
				
			||||||
import tanya.container.array;
 | 
					import tanya.container.array;
 | 
				
			||||||
import tanya.container.string;
 | 
					import tanya.container.string;
 | 
				
			||||||
import tanya.memory.allocator;
 | 
					import tanya.memory.allocator;
 | 
				
			||||||
 | 
					import std.array;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
/**
 | 
					/**
 | 
				
			||||||
 * Parser visitor.
 | 
					 * Parser visitor.
 | 
				
			||||||
@@ -14,7 +15,6 @@ interface ParserVisitor(Mapping)
 | 
				
			|||||||
{
 | 
					{
 | 
				
			||||||
    Mapping.Node visit(Node) @nogc;
 | 
					    Mapping.Node visit(Node) @nogc;
 | 
				
			||||||
    Mapping.Definition visit(Definition) @nogc;
 | 
					    Mapping.Definition visit(Definition) @nogc;
 | 
				
			||||||
    Mapping.VariableDeclaration visit(VariableDeclaration) @nogc;
 | 
					 | 
				
			||||||
    Mapping.Statement visit(Statement) @nogc;
 | 
					    Mapping.Statement visit(Statement) @nogc;
 | 
				
			||||||
    Mapping.BangStatement visit(BangStatement) @nogc;
 | 
					    Mapping.BangStatement visit(BangStatement) @nogc;
 | 
				
			||||||
    Mapping.Block visit(Block) @nogc;
 | 
					    Mapping.Block visit(Block) @nogc;
 | 
				
			||||||
@@ -49,19 +49,6 @@ class Definition : Node
 | 
				
			|||||||
    }
 | 
					    }
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
/**
 | 
					 | 
				
			||||||
 * Variable declaration.
 | 
					 | 
				
			||||||
 */
 | 
					 | 
				
			||||||
class VariableDeclaration : Node
 | 
					 | 
				
			||||||
{
 | 
					 | 
				
			||||||
    String identifier;
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    Mapping.VariableDeclaration accept(Mapping)(ParserVisitor!Mapping visitor) @nogc
 | 
					 | 
				
			||||||
    {
 | 
					 | 
				
			||||||
        return visitor.visit(this);
 | 
					 | 
				
			||||||
    }
 | 
					 | 
				
			||||||
}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
abstract class Statement : Node
 | 
					abstract class Statement : Node
 | 
				
			||||||
{
 | 
					{
 | 
				
			||||||
    Mapping.Statement accept(Mapping)(ParserVisitor!Mapping visitor) @nogc
 | 
					    Mapping.Statement accept(Mapping)(ParserVisitor!Mapping visitor) @nogc
 | 
				
			||||||
@@ -83,7 +70,6 @@ class BangStatement : Statement
 | 
				
			|||||||
class Block : Node
 | 
					class Block : Node
 | 
				
			||||||
{
 | 
					{
 | 
				
			||||||
    Array!Definition definitions;
 | 
					    Array!Definition definitions;
 | 
				
			||||||
    Array!VariableDeclaration variableDeclarations;
 | 
					 | 
				
			||||||
    Statement statement;
 | 
					    Statement statement;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    Mapping.Block accept(Mapping)(ParserVisitor!Mapping visitor) @nogc
 | 
					    Mapping.Block accept(Mapping)(ParserVisitor!Mapping visitor) @nogc
 | 
				
			||||||
@@ -120,6 +106,7 @@ class Variable : Expression
 | 
				
			|||||||
    }
 | 
					    }
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					extern(C++, "elna")
 | 
				
			||||||
enum BinaryOperator
 | 
					enum BinaryOperator
 | 
				
			||||||
{
 | 
					{
 | 
				
			||||||
    sum,
 | 
					    sum,
 | 
				
			||||||
@@ -155,24 +142,24 @@ class BinaryExpression : Expression
 | 
				
			|||||||
    }
 | 
					    }
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
private Result!Expression parseFactor(ref Array!Token.Range tokens) @nogc
 | 
					private Result!Expression parseFactor(ref Token[] tokens) @nogc
 | 
				
			||||||
in (!tokens.empty, "Expected factor, got end of stream")
 | 
					in (!tokens.empty, "Expected factor, got end of stream")
 | 
				
			||||||
{
 | 
					{
 | 
				
			||||||
    if (tokens.front.ofType(Token.Type.identifier))
 | 
					    if (tokens.front.of() == Token.Type.identifier)
 | 
				
			||||||
    {
 | 
					    {
 | 
				
			||||||
        auto variable = defaultAllocator.make!Variable;
 | 
					        auto variable = defaultAllocator.make!Variable;
 | 
				
			||||||
        variable.identifier = tokens.front.value!(Token.Type.identifier);
 | 
					        variable.identifier = tokens.front.identifier()[0 .. strlen(tokens.front.identifier())];
 | 
				
			||||||
        tokens.popFront;
 | 
					        tokens.popFront;
 | 
				
			||||||
        return Result!Expression(variable);
 | 
					        return Result!Expression(variable);
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
    else if (tokens.front.ofType(Token.Type.number))
 | 
					    else if (tokens.front.of() == Token.Type.number)
 | 
				
			||||||
    {
 | 
					    {
 | 
				
			||||||
        auto number = defaultAllocator.make!Number;
 | 
					        auto number = defaultAllocator.make!Number;
 | 
				
			||||||
        number.value = tokens.front.value!(Token.Type.number);
 | 
					        number.value = tokens.front.number();
 | 
				
			||||||
        tokens.popFront;
 | 
					        tokens.popFront;
 | 
				
			||||||
        return Result!Expression(number);
 | 
					        return Result!Expression(number);
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
    else if (tokens.front.ofType(Token.Type.leftParen))
 | 
					    else if (tokens.front.of() == Token.Type.leftParen)
 | 
				
			||||||
    {
 | 
					    {
 | 
				
			||||||
        tokens.popFront;
 | 
					        tokens.popFront;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
@@ -184,20 +171,20 @@ in (!tokens.empty, "Expected factor, got end of stream")
 | 
				
			|||||||
    return Result!Expression("Expected a factor", tokens.front.position);
 | 
					    return Result!Expression("Expected a factor", tokens.front.position);
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
private Result!Expression parseTerm(ref Array!(Token).Range tokens) @nogc
 | 
					private Result!Expression parseTerm(ref Token[] tokens) @nogc
 | 
				
			||||||
{
 | 
					{
 | 
				
			||||||
    return parseFactor(tokens);
 | 
					    return parseFactor(tokens);
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
private Result!Expression parseExpression(ref Array!(Token).Range tokens) @nogc
 | 
					private Result!Expression parseExpression(ref Token[] tokens) @nogc
 | 
				
			||||||
in (!tokens.empty, "Expected expression, got end of stream")
 | 
					in (!tokens.empty, "Expected expression, got end of stream")
 | 
				
			||||||
{
 | 
					{
 | 
				
			||||||
    auto term = parseTerm(tokens);
 | 
					    auto term = parseTerm(tokens);
 | 
				
			||||||
    if (!term.valid || tokens.empty || !tokens.front.ofType(Token.Type.operator))
 | 
					    if (!term.valid || tokens.empty || tokens.front.of() != Token.Type.operator)
 | 
				
			||||||
    {
 | 
					    {
 | 
				
			||||||
        return term;
 | 
					        return term;
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
    auto operator = tokens.front.value!(Token.Type.operator);
 | 
					    auto operator = String(tokens.front.identifier()[0 .. strlen(tokens.front.identifier())]);
 | 
				
			||||||
    tokens.popFront;
 | 
					    tokens.popFront;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    auto expression = parseExpression(tokens);
 | 
					    auto expression = parseExpression(tokens);
 | 
				
			||||||
@@ -215,19 +202,19 @@ in (!tokens.empty, "Expected expression, got end of stream")
 | 
				
			|||||||
    }
 | 
					    }
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
private Result!Definition parseDefinition(ref Array!Token.Range tokens) @nogc
 | 
					private Result!Definition parseDefinition(ref Token[] tokens) @nogc
 | 
				
			||||||
in (!tokens.empty, "Expected definition, got end of stream")
 | 
					in (!tokens.empty, "Expected definition, got end of stream")
 | 
				
			||||||
{
 | 
					{
 | 
				
			||||||
    auto definition = defaultAllocator.make!Definition;
 | 
					    auto definition = defaultAllocator.make!Definition;
 | 
				
			||||||
    definition.identifier = tokens.front.value!(Token.Type.identifier); // Copy.
 | 
					    definition.identifier = tokens.front.identifier()[0 .. strlen(tokens.front.identifier())]; // Copy.
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    tokens.popFront();
 | 
					    tokens.popFront();
 | 
				
			||||||
    tokens.popFront(); // Skip the equals sign.
 | 
					    tokens.popFront(); // Skip the equals sign.
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    if (tokens.front.ofType(Token.Type.number))
 | 
					    if (tokens.front.of() == Token.Type.number)
 | 
				
			||||||
    {
 | 
					    {
 | 
				
			||||||
        auto number = defaultAllocator.make!Number;
 | 
					        auto number = defaultAllocator.make!Number;
 | 
				
			||||||
        number.value = tokens.front.value!(Token.Type.number);
 | 
					        number.value = tokens.front.number();
 | 
				
			||||||
        definition.number = number;
 | 
					        definition.number = number;
 | 
				
			||||||
        tokens.popFront;
 | 
					        tokens.popFront;
 | 
				
			||||||
        return Result!Definition(definition);
 | 
					        return Result!Definition(definition);
 | 
				
			||||||
@@ -235,10 +222,10 @@ in (!tokens.empty, "Expected definition, got end of stream")
 | 
				
			|||||||
    return Result!Definition("Expected a number", tokens.front.position);
 | 
					    return Result!Definition("Expected a number", tokens.front.position);
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
private Result!Statement parseStatement(ref Array!Token.Range tokens) @nogc
 | 
					private Result!Statement parseStatement(ref Token[] tokens) @nogc
 | 
				
			||||||
in (!tokens.empty, "Expected block, got end of stream")
 | 
					in (!tokens.empty, "Expected block, got end of stream")
 | 
				
			||||||
{
 | 
					{
 | 
				
			||||||
    if (tokens.front.ofType(Token.Type.bang))
 | 
					    if (tokens.front.of() == Token.Type.bang)
 | 
				
			||||||
    {
 | 
					    {
 | 
				
			||||||
        tokens.popFront;
 | 
					        tokens.popFront;
 | 
				
			||||||
        auto statement = defaultAllocator.make!BangStatement;
 | 
					        auto statement = defaultAllocator.make!BangStatement;
 | 
				
			||||||
@@ -256,7 +243,7 @@ in (!tokens.empty, "Expected block, got end of stream")
 | 
				
			|||||||
    return Result!Statement("Expected ! statement", tokens.front.position);
 | 
					    return Result!Statement("Expected ! statement", tokens.front.position);
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
private Result!(Array!Definition) parseDefinitions(ref Array!Token.Range tokens) @nogc
 | 
					private Result!(Array!Definition) parseDefinitions(ref Token[] tokens) @nogc
 | 
				
			||||||
in (!tokens.empty, "Expected definition, got end of stream")
 | 
					in (!tokens.empty, "Expected definition, got end of stream")
 | 
				
			||||||
{
 | 
					{
 | 
				
			||||||
    tokens.popFront; // Skip const.
 | 
					    tokens.popFront; // Skip const.
 | 
				
			||||||
@@ -271,11 +258,11 @@ in (!tokens.empty, "Expected definition, got end of stream")
 | 
				
			|||||||
            return typeof(return)(definition.error.get);
 | 
					            return typeof(return)(definition.error.get);
 | 
				
			||||||
        }
 | 
					        }
 | 
				
			||||||
        definitions.insertBack(definition.result);
 | 
					        definitions.insertBack(definition.result);
 | 
				
			||||||
        if (tokens.front.ofType(Token.Type.semicolon))
 | 
					        if (tokens.front.of() == Token.Type.semicolon)
 | 
				
			||||||
        {
 | 
					        {
 | 
				
			||||||
            break;
 | 
					            break;
 | 
				
			||||||
        }
 | 
					        }
 | 
				
			||||||
        if (tokens.front.ofType(Token.Type.comma))
 | 
					        if (tokens.front.of() == Token.Type.comma)
 | 
				
			||||||
        {
 | 
					        {
 | 
				
			||||||
            tokens.popFront;
 | 
					            tokens.popFront;
 | 
				
			||||||
        }
 | 
					        }
 | 
				
			||||||
@@ -284,49 +271,11 @@ in (!tokens.empty, "Expected definition, got end of stream")
 | 
				
			|||||||
    return typeof(return)(definitions);
 | 
					    return typeof(return)(definitions);
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
private Result!(Array!VariableDeclaration) parseVariableDeclarations(ref Array!Token.Range tokens) @nogc
 | 
					private Result!Block parseBlock(ref Token[] tokens) @nogc
 | 
				
			||||||
in (!tokens.empty, "Expected variable declarations, got end of stream")
 | 
					 | 
				
			||||||
{
 | 
					 | 
				
			||||||
    tokens.popFront; // Skip var.
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    Array!VariableDeclaration variableDeclarations;
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    while (!tokens.empty)
 | 
					 | 
				
			||||||
    {
 | 
					 | 
				
			||||||
        auto currentToken = tokens.front;
 | 
					 | 
				
			||||||
        if (currentToken.ofType(Token.Type.identifier))
 | 
					 | 
				
			||||||
        {
 | 
					 | 
				
			||||||
            auto variableDeclaration = defaultAllocator.make!VariableDeclaration;
 | 
					 | 
				
			||||||
            variableDeclaration.identifier = currentToken.value!(Token.Type.identifier);
 | 
					 | 
				
			||||||
            variableDeclarations.insertBack(variableDeclaration);
 | 
					 | 
				
			||||||
            tokens.popFront;
 | 
					 | 
				
			||||||
        }
 | 
					 | 
				
			||||||
        else
 | 
					 | 
				
			||||||
        {
 | 
					 | 
				
			||||||
            return typeof(return)("Expected variable name", tokens.front.position);
 | 
					 | 
				
			||||||
        }
 | 
					 | 
				
			||||||
        if (tokens.empty)
 | 
					 | 
				
			||||||
        {
 | 
					 | 
				
			||||||
            return typeof(return)("Expected \";\" or \",\" name", currentToken.position);
 | 
					 | 
				
			||||||
        }
 | 
					 | 
				
			||||||
        if (tokens.front.ofType(Token.Type.semicolon))
 | 
					 | 
				
			||||||
        {
 | 
					 | 
				
			||||||
            break;
 | 
					 | 
				
			||||||
        }
 | 
					 | 
				
			||||||
        if (tokens.front.ofType(Token.Type.comma))
 | 
					 | 
				
			||||||
        {
 | 
					 | 
				
			||||||
            tokens.popFront;
 | 
					 | 
				
			||||||
        }
 | 
					 | 
				
			||||||
    }
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    return typeof(return)(variableDeclarations);
 | 
					 | 
				
			||||||
}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
private Result!Block parseBlock(ref Array!Token.Range tokens) @nogc
 | 
					 | 
				
			||||||
in (!tokens.empty, "Expected block, got end of stream")
 | 
					in (!tokens.empty, "Expected block, got end of stream")
 | 
				
			||||||
{
 | 
					{
 | 
				
			||||||
    auto block = defaultAllocator.make!Block;
 | 
					    auto block = defaultAllocator.make!Block;
 | 
				
			||||||
    if (tokens.front.ofType(Token.Type.let))
 | 
					    if (tokens.front.of() == Token.Type.let)
 | 
				
			||||||
    {
 | 
					    {
 | 
				
			||||||
        auto constDefinitions = parseDefinitions(tokens);
 | 
					        auto constDefinitions = parseDefinitions(tokens);
 | 
				
			||||||
        if (constDefinitions.valid)
 | 
					        if (constDefinitions.valid)
 | 
				
			||||||
@@ -339,19 +288,6 @@ in (!tokens.empty, "Expected block, got end of stream")
 | 
				
			|||||||
        }
 | 
					        }
 | 
				
			||||||
        tokens.popFront;
 | 
					        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);
 | 
					    auto statement = parseStatement(tokens);
 | 
				
			||||||
    if (statement.valid)
 | 
					    if (statement.valid)
 | 
				
			||||||
    {
 | 
					    {
 | 
				
			||||||
@@ -365,7 +301,7 @@ in (!tokens.empty, "Expected block, got end of stream")
 | 
				
			|||||||
    return Result!Block(block);
 | 
					    return Result!Block(block);
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
Result!Block parse(ref Array!Token tokenStream) @nogc
 | 
					Result!Block parse(Token[] tokenStream) @nogc
 | 
				
			||||||
{
 | 
					{
 | 
				
			||||||
    auto tokens = tokenStream[];
 | 
					    auto tokens = tokenStream[];
 | 
				
			||||||
    return parseBlock(tokens);
 | 
					    return parseBlock(tokens);
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -7,6 +7,7 @@ import tanya.container.string;
 | 
				
			|||||||
/**
 | 
					/**
 | 
				
			||||||
 * Position in the source text.
 | 
					 * Position in the source text.
 | 
				
			||||||
 */
 | 
					 */
 | 
				
			||||||
 | 
					extern(C++, "elna")
 | 
				
			||||||
struct Position
 | 
					struct Position
 | 
				
			||||||
{
 | 
					{
 | 
				
			||||||
    /// Line.
 | 
					    /// Line.
 | 
				
			||||||
@@ -16,9 +17,10 @@ struct Position
 | 
				
			|||||||
    size_t column = 1;
 | 
					    size_t column = 1;
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					extern(C++, "elna")
 | 
				
			||||||
struct CompileError
 | 
					struct CompileError
 | 
				
			||||||
{
 | 
					{
 | 
				
			||||||
    private string message_;
 | 
					    private const(char)* message_;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    private Position position_;
 | 
					    private Position position_;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
@@ -29,29 +31,16 @@ struct CompileError
 | 
				
			|||||||
     *   message = Error text.
 | 
					     *   message = Error text.
 | 
				
			||||||
     *   position = Error position in the source text.
 | 
					     *   position = Error position in the source text.
 | 
				
			||||||
     */
 | 
					     */
 | 
				
			||||||
    this(string message, Position position) @nogc nothrow pure @safe
 | 
					    this(const(char)* message, const Position position) @nogc nothrow pure @safe;
 | 
				
			||||||
    {
 | 
					 | 
				
			||||||
        this.message_ = message;
 | 
					 | 
				
			||||||
        this.position_ = position;
 | 
					 | 
				
			||||||
    }
 | 
					 | 
				
			||||||
 | 
					
 | 
				
			||||||
    /// Error text.
 | 
					    /// Error text.
 | 
				
			||||||
    @property string message() const @nogc nothrow pure @safe
 | 
					    @property const(char)* what() const @nogc nothrow pure @safe;
 | 
				
			||||||
    {
 | 
					 | 
				
			||||||
        return this.message_;
 | 
					 | 
				
			||||||
    }
 | 
					 | 
				
			||||||
 | 
					
 | 
				
			||||||
    /// Error line in the source text.
 | 
					    /// Error line in the source text.
 | 
				
			||||||
    @property size_t line() const @nogc nothrow pure @safe
 | 
					    @property size_t line() const @nogc nothrow pure @safe;
 | 
				
			||||||
    {
 | 
					 | 
				
			||||||
        return this.position_.line;
 | 
					 | 
				
			||||||
    }
 | 
					 | 
				
			||||||
 | 
					
 | 
				
			||||||
    /// Error column in the source text.
 | 
					    /// Error column in the source text.
 | 
				
			||||||
    @property size_t column() const @nogc nothrow pure @safe
 | 
					    @property size_t column() const @nogc nothrow pure @safe;
 | 
				
			||||||
    {
 | 
					 | 
				
			||||||
        return this.position_.column;
 | 
					 | 
				
			||||||
    }
 | 
					 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
struct Result(T)
 | 
					struct Result(T)
 | 
				
			||||||
@@ -65,7 +54,7 @@ struct Result(T)
 | 
				
			|||||||
        this.error = typeof(this.error).init;
 | 
					        this.error = typeof(this.error).init;
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    this(string message, Position position)
 | 
					    this(const(char)* message, Position position)
 | 
				
			||||||
    {
 | 
					    {
 | 
				
			||||||
        this.result = T.init;
 | 
					        this.result = T.init;
 | 
				
			||||||
        this.error = CompileError(message, position);
 | 
					        this.error = CompileError(message, position);
 | 
				
			||||||
@@ -85,16 +74,18 @@ struct Result(T)
 | 
				
			|||||||
    }
 | 
					    }
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					extern(C++, "elna")
 | 
				
			||||||
 | 
					enum Target
 | 
				
			||||||
 | 
					{
 | 
				
			||||||
 | 
					    text,
 | 
				
			||||||
 | 
					    high20,
 | 
				
			||||||
 | 
					    lower12i
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					extern(C++, "elna")
 | 
				
			||||||
struct Reference
 | 
					struct Reference
 | 
				
			||||||
{
 | 
					{
 | 
				
			||||||
    enum Target
 | 
					    const(char)* name;
 | 
				
			||||||
    {
 | 
					 | 
				
			||||||
        text,
 | 
					 | 
				
			||||||
        high20,
 | 
					 | 
				
			||||||
        lower12i
 | 
					 | 
				
			||||||
    }
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    String name;
 | 
					 | 
				
			||||||
    size_t offset;
 | 
					    size_t offset;
 | 
				
			||||||
    Target target;
 | 
					    Target target;
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -1,14 +1,12 @@
 | 
				
			|||||||
module elna.riscv;
 | 
					module elna.riscv;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
import core.stdc.stdlib;
 | 
					import core.stdc.stdlib;
 | 
				
			||||||
import elna.extended;
 | 
					 | 
				
			||||||
import elna.ir;
 | 
					import elna.ir;
 | 
				
			||||||
import elna.result;
 | 
					import elna.result;
 | 
				
			||||||
import std.algorithm;
 | 
					 | 
				
			||||||
import std.typecons;
 | 
					 | 
				
			||||||
import tanya.container.array;
 | 
					import tanya.container.array;
 | 
				
			||||||
import tanya.container.string;
 | 
					import tanya.container.string;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					extern(C++, "elna")
 | 
				
			||||||
enum XRegister : ubyte
 | 
					enum XRegister : ubyte
 | 
				
			||||||
{
 | 
					{
 | 
				
			||||||
    zero = 0,
 | 
					    zero = 0,
 | 
				
			||||||
@@ -45,6 +43,7 @@ enum XRegister : ubyte
 | 
				
			|||||||
    t6 = 31,
 | 
					    t6 = 31,
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					extern(C++, "elna")
 | 
				
			||||||
enum Funct3 : ubyte
 | 
					enum Funct3 : ubyte
 | 
				
			||||||
{
 | 
					{
 | 
				
			||||||
    addi = 0b000,
 | 
					    addi = 0b000,
 | 
				
			||||||
@@ -92,18 +91,21 @@ enum Funct3 : ubyte
 | 
				
			|||||||
    jalr = 0b000,
 | 
					    jalr = 0b000,
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					extern(C++, "elna")
 | 
				
			||||||
enum Funct12 : ubyte
 | 
					enum Funct12 : ubyte
 | 
				
			||||||
{
 | 
					{
 | 
				
			||||||
    ecall = 0b000000000000,
 | 
					    ecall = 0b000000000000,
 | 
				
			||||||
    ebreak = 0b000000000001,
 | 
					    ebreak = 0b000000000001,
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					extern(C++, "elna")
 | 
				
			||||||
enum Funct7 : ubyte
 | 
					enum Funct7 : ubyte
 | 
				
			||||||
{
 | 
					{
 | 
				
			||||||
    none = 0,
 | 
					    none = 0,
 | 
				
			||||||
    sub = 0b0100000
 | 
					    sub = 0b0100000
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					extern(C++, "elna")
 | 
				
			||||||
enum BaseOpcode : ubyte
 | 
					enum BaseOpcode : ubyte
 | 
				
			||||||
{
 | 
					{
 | 
				
			||||||
    opImm = 0b0010011,
 | 
					    opImm = 0b0010011,
 | 
				
			||||||
@@ -119,243 +121,61 @@ enum BaseOpcode : ubyte
 | 
				
			|||||||
    system = 0b1110011,
 | 
					    system = 0b1110011,
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					extern(C++, "elna")
 | 
				
			||||||
struct Instruction
 | 
					struct Instruction
 | 
				
			||||||
{
 | 
					{
 | 
				
			||||||
    private uint instruction;
 | 
					    private uint instruction;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    this(BaseOpcode opcode) @nogc
 | 
					    this(BaseOpcode opcode) @nogc;
 | 
				
			||||||
    {
 | 
					 | 
				
			||||||
        this.instruction = opcode;
 | 
					 | 
				
			||||||
    }
 | 
					 | 
				
			||||||
    @disable this();
 | 
					    @disable this();
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    ref Instruction i(XRegister rd, Funct3 funct3, XRegister rs1, uint immediate)
 | 
					    ref Instruction i(XRegister rd, Funct3 funct3, XRegister rs1, uint immediate)
 | 
				
			||||||
    return scope @nogc
 | 
					    return scope @nogc;
 | 
				
			||||||
    {
 | 
					 | 
				
			||||||
        this.instruction |= (rd << 7)
 | 
					 | 
				
			||||||
            | (funct3 << 12)
 | 
					 | 
				
			||||||
            | (rs1 << 15)
 | 
					 | 
				
			||||||
            | (immediate << 20);
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
        return this;
 | 
					 | 
				
			||||||
    }
 | 
					 | 
				
			||||||
 | 
					
 | 
				
			||||||
    ref Instruction s(uint imm1, Funct3 funct3, XRegister rs1, XRegister rs2)
 | 
					    ref Instruction s(uint imm1, Funct3 funct3, XRegister rs1, XRegister rs2)
 | 
				
			||||||
    return scope @nogc
 | 
					    return scope @nogc;
 | 
				
			||||||
    {
 | 
					 | 
				
			||||||
        this.instruction |= ((imm1 & 0b11111) << 7)
 | 
					 | 
				
			||||||
            | (funct3 << 12)
 | 
					 | 
				
			||||||
            | (rs1 << 15)
 | 
					 | 
				
			||||||
            | (rs2 << 20)
 | 
					 | 
				
			||||||
            | ((imm1 & 0b111111100000) << 20);
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
        return this;
 | 
					 | 
				
			||||||
    }
 | 
					 | 
				
			||||||
 | 
					
 | 
				
			||||||
    ref Instruction r(XRegister rd, Funct3 funct3, XRegister rs1, XRegister rs2, Funct7 funct7 = Funct7.none)
 | 
					    ref Instruction r(XRegister rd, Funct3 funct3, XRegister rs1, XRegister rs2, Funct7 funct7 = Funct7.none)
 | 
				
			||||||
    return scope @nogc
 | 
					    return scope @nogc;
 | 
				
			||||||
    {
 | 
					 | 
				
			||||||
        this.instruction |= (rd << 7)
 | 
					 | 
				
			||||||
            | (funct3 << 12)
 | 
					 | 
				
			||||||
            | (rs1 << 15)
 | 
					 | 
				
			||||||
            | (rs2 << 20)
 | 
					 | 
				
			||||||
            | (funct7 << 25);
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
        return this;
 | 
					 | 
				
			||||||
    }
 | 
					 | 
				
			||||||
 | 
					
 | 
				
			||||||
    ref Instruction u(XRegister rd, uint imm)
 | 
					    ref Instruction u(XRegister rd, uint imm)
 | 
				
			||||||
    return scope @nogc
 | 
					    return scope @nogc;
 | 
				
			||||||
    {
 | 
					 | 
				
			||||||
        this.instruction |= (rd << 7) | (imm << 12);
 | 
					 | 
				
			||||||
 | 
					
 | 
				
			||||||
        return this;
 | 
					    ubyte* encode() return scope @nogc;
 | 
				
			||||||
    }
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    ubyte* encode() return scope @nogc
 | 
					 | 
				
			||||||
    {
 | 
					 | 
				
			||||||
        return cast(ubyte*) (&this.instruction);
 | 
					 | 
				
			||||||
    }
 | 
					 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					extern(C++, "elna")
 | 
				
			||||||
class RiscVVisitor : IRVisitor
 | 
					class RiscVVisitor : IRVisitor
 | 
				
			||||||
{
 | 
					{
 | 
				
			||||||
    Array!Instruction instructions;
 | 
					    Instruction *instructions;
 | 
				
			||||||
 | 
					    size_t instructionsLength;
 | 
				
			||||||
    bool registerInUse;
 | 
					    bool registerInUse;
 | 
				
			||||||
    uint variableCounter = 1;
 | 
					    uint variableCounter = 1;
 | 
				
			||||||
    Array!Reference references;
 | 
					    Reference[3] references;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    override void visit(Node) @nogc
 | 
					    override void visit(Node) @nogc;
 | 
				
			||||||
    {
 | 
					    override void visit(Definition definition) @nogc;
 | 
				
			||||||
    }
 | 
					    override void visit(Operand operand) @nogc;
 | 
				
			||||||
 | 
					    override void visit(Variable variable) @nogc;
 | 
				
			||||||
    override void visit(Definition definition) @nogc
 | 
					    override void visit(Number number) @nogc;
 | 
				
			||||||
    {
 | 
					    override void visit(BinaryExpression expression) @nogc;
 | 
				
			||||||
        const uint stackSize = cast(uint) (definition.statements.length * 4 + 12);
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
        // Prologue.
 | 
					 | 
				
			||||||
        this.instructions.insertBack(
 | 
					 | 
				
			||||||
            Instruction(BaseOpcode.opImm)
 | 
					 | 
				
			||||||
                .i(XRegister.sp, Funct3.addi, XRegister.sp, -stackSize)
 | 
					 | 
				
			||||||
        );
 | 
					 | 
				
			||||||
        this.instructions.insertBack(
 | 
					 | 
				
			||||||
            Instruction(BaseOpcode.store)
 | 
					 | 
				
			||||||
                .s(stackSize - 4, Funct3.sw, XRegister.sp, XRegister.s0)
 | 
					 | 
				
			||||||
        );
 | 
					 | 
				
			||||||
        this.instructions.insertBack(
 | 
					 | 
				
			||||||
            Instruction(BaseOpcode.store)
 | 
					 | 
				
			||||||
                .s(stackSize - 8, Funct3.sw, XRegister.sp, XRegister.ra)
 | 
					 | 
				
			||||||
        );
 | 
					 | 
				
			||||||
        this.instructions.insertBack(
 | 
					 | 
				
			||||||
            Instruction(BaseOpcode.opImm)
 | 
					 | 
				
			||||||
                .i(XRegister.s0, Funct3.addi, XRegister.sp, stackSize)
 | 
					 | 
				
			||||||
        );
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
        foreach (statement; definition.statements[])
 | 
					 | 
				
			||||||
        {
 | 
					 | 
				
			||||||
            statement.accept(this);
 | 
					 | 
				
			||||||
        }
 | 
					 | 
				
			||||||
        foreach (variableDeclaration; definition.variableDeclarations[])
 | 
					 | 
				
			||||||
        {
 | 
					 | 
				
			||||||
            variableDeclaration.accept(this);
 | 
					 | 
				
			||||||
        }
 | 
					 | 
				
			||||||
        this.registerInUse = true;
 | 
					 | 
				
			||||||
        definition.result.accept(this);
 | 
					 | 
				
			||||||
        this.registerInUse = false;
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
        // Print the result.
 | 
					 | 
				
			||||||
        this.instructions.insertBack(
 | 
					 | 
				
			||||||
            Instruction(BaseOpcode.opImm)
 | 
					 | 
				
			||||||
                .i(XRegister.a1, Funct3.addi, XRegister.a0, 0)
 | 
					 | 
				
			||||||
        );
 | 
					 | 
				
			||||||
        this.references.insertBack(Reference(String(".CL0"), instructions.length * 4, Reference.Target.high20));
 | 
					 | 
				
			||||||
        this.instructions.insertBack(
 | 
					 | 
				
			||||||
            Instruction(BaseOpcode.lui).u(XRegister.a5, 0)
 | 
					 | 
				
			||||||
        );
 | 
					 | 
				
			||||||
        this.references.insertBack(Reference(String(".CL0"), instructions.length * 4, Reference.Target.lower12i));
 | 
					 | 
				
			||||||
        this.instructions.insertBack(
 | 
					 | 
				
			||||||
            Instruction(BaseOpcode.opImm).i(XRegister.a0, Funct3.addi, XRegister.a5, 0)
 | 
					 | 
				
			||||||
        );
 | 
					 | 
				
			||||||
        this.references.insertBack(Reference(String("printf"), instructions.length * 4, Reference.Target.text));
 | 
					 | 
				
			||||||
        this.instructions.insertBack(
 | 
					 | 
				
			||||||
            Instruction(BaseOpcode.auipc).u(XRegister.ra, 0)
 | 
					 | 
				
			||||||
        );
 | 
					 | 
				
			||||||
        this.instructions.insertBack(
 | 
					 | 
				
			||||||
            Instruction(BaseOpcode.jalr)
 | 
					 | 
				
			||||||
                .i(XRegister.ra, Funct3.jalr, XRegister.ra, 0)
 | 
					 | 
				
			||||||
        );
 | 
					 | 
				
			||||||
        // Set the return value (0).
 | 
					 | 
				
			||||||
        this.instructions.insertBack(
 | 
					 | 
				
			||||||
            Instruction(BaseOpcode.op)
 | 
					 | 
				
			||||||
                .r(XRegister.a0, Funct3.and, XRegister.zero, XRegister.zero)
 | 
					 | 
				
			||||||
        );
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
        // Epilogue.
 | 
					 | 
				
			||||||
        this.instructions.insertBack(
 | 
					 | 
				
			||||||
            Instruction(BaseOpcode.load)
 | 
					 | 
				
			||||||
                .i(XRegister.s0, Funct3.lw, XRegister.sp, stackSize - 4)
 | 
					 | 
				
			||||||
        );
 | 
					 | 
				
			||||||
        this.instructions.insertBack(
 | 
					 | 
				
			||||||
            Instruction(BaseOpcode.load)
 | 
					 | 
				
			||||||
                .i(XRegister.ra, Funct3.lw, XRegister.sp, stackSize - 8)
 | 
					 | 
				
			||||||
        );
 | 
					 | 
				
			||||||
        this.instructions.insertBack(
 | 
					 | 
				
			||||||
            Instruction(BaseOpcode.opImm)
 | 
					 | 
				
			||||||
                .i(XRegister.sp, Funct3.addi, XRegister.sp, stackSize)
 | 
					 | 
				
			||||||
        );
 | 
					 | 
				
			||||||
        this.instructions.insertBack(
 | 
					 | 
				
			||||||
            Instruction(BaseOpcode.jalr)
 | 
					 | 
				
			||||||
                .i(XRegister.zero, Funct3.jalr, XRegister.ra, 0)
 | 
					 | 
				
			||||||
        );
 | 
					 | 
				
			||||||
    }
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    override void visit(Operand operand) @nogc
 | 
					 | 
				
			||||||
    {
 | 
					 | 
				
			||||||
        if ((cast(Variable) operand) !is null)
 | 
					 | 
				
			||||||
        {
 | 
					 | 
				
			||||||
            return (cast(Variable) operand).accept(this);
 | 
					 | 
				
			||||||
        }
 | 
					 | 
				
			||||||
        if ((cast(Number) operand) !is null)
 | 
					 | 
				
			||||||
        {
 | 
					 | 
				
			||||||
            return (cast(Number) operand).accept(this);
 | 
					 | 
				
			||||||
        }
 | 
					 | 
				
			||||||
    }
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    override void visit(Variable variable) @nogc
 | 
					 | 
				
			||||||
    {
 | 
					 | 
				
			||||||
        const freeRegister = this.registerInUse ? XRegister.a0 : XRegister.t0;
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
        // movl -x(%rbp), %eax; where x is a number.
 | 
					 | 
				
			||||||
        this.instructions.insertBack(
 | 
					 | 
				
			||||||
            Instruction(BaseOpcode.load)
 | 
					 | 
				
			||||||
                .i(freeRegister, Funct3.lw, XRegister.sp,
 | 
					 | 
				
			||||||
                    cast(byte) (variable.counter * 4))
 | 
					 | 
				
			||||||
        );
 | 
					 | 
				
			||||||
    }
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    override void visit(VariableDeclaration) @nogc
 | 
					 | 
				
			||||||
    {
 | 
					 | 
				
			||||||
    }
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    override void visit(Number number) @nogc
 | 
					 | 
				
			||||||
    {
 | 
					 | 
				
			||||||
        const freeRegister = this.registerInUse ? XRegister.a0 : XRegister.t0;
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
        this.instructions.insertBack(
 | 
					 | 
				
			||||||
            Instruction(BaseOpcode.opImm) // movl $x, %eax; where $x is a number.
 | 
					 | 
				
			||||||
                .i(freeRegister, Funct3.addi, XRegister.zero, number.value)
 | 
					 | 
				
			||||||
        );
 | 
					 | 
				
			||||||
    }
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    override void visit(BinaryExpression expression) @nogc
 | 
					 | 
				
			||||||
    {
 | 
					 | 
				
			||||||
        this.registerInUse = true;
 | 
					 | 
				
			||||||
        expression.lhs.accept(this);
 | 
					 | 
				
			||||||
        this.registerInUse = false;
 | 
					 | 
				
			||||||
        expression.rhs.accept(this);
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
        // Calculate the result and assign it to a variable on the stack.
 | 
					 | 
				
			||||||
        final switch (expression.operator)
 | 
					 | 
				
			||||||
        {
 | 
					 | 
				
			||||||
            case BinaryOperator.sum:
 | 
					 | 
				
			||||||
                this.instructions.insertBack(
 | 
					 | 
				
			||||||
                    Instruction(BaseOpcode.op)
 | 
					 | 
				
			||||||
                        .r(XRegister.a0, Funct3.add, XRegister.a0, XRegister.t0)
 | 
					 | 
				
			||||||
                );
 | 
					 | 
				
			||||||
                break;
 | 
					 | 
				
			||||||
            case BinaryOperator.subtraction:
 | 
					 | 
				
			||||||
                this.instructions.insertBack(
 | 
					 | 
				
			||||||
                    Instruction(BaseOpcode.op)
 | 
					 | 
				
			||||||
                        .r(XRegister.a0, Funct3.sub, XRegister.a0, XRegister.t0, Funct7.sub)
 | 
					 | 
				
			||||||
                );
 | 
					 | 
				
			||||||
                break;
 | 
					 | 
				
			||||||
        }
 | 
					 | 
				
			||||||
        this.instructions.insertBack( // movl %eax, -x(%rbp); where x is a number.
 | 
					 | 
				
			||||||
            Instruction(BaseOpcode.store)
 | 
					 | 
				
			||||||
                .s(cast(uint) (this.variableCounter * 4), Funct3.sw, XRegister.sp, XRegister.a0)
 | 
					 | 
				
			||||||
        );
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
        ++this.variableCounter;
 | 
					 | 
				
			||||||
    }
 | 
					 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
Symbol writeNext(Definition ast) @nogc
 | 
					Symbol writeNext(Definition ast) @nogc
 | 
				
			||||||
{
 | 
					{
 | 
				
			||||||
    Array!Instruction instructions;
 | 
					    Array!Instruction instructions;
 | 
				
			||||||
    Array!Reference references;
 | 
					 | 
				
			||||||
    auto visitor = cast(RiscVVisitor) malloc(__traits(classInstanceSize, RiscVVisitor));
 | 
					    auto visitor = cast(RiscVVisitor) malloc(__traits(classInstanceSize, RiscVVisitor));
 | 
				
			||||||
    (cast(void*) visitor)[0 .. __traits(classInstanceSize, RiscVVisitor)] = __traits(initSymbol, RiscVVisitor)[];
 | 
					    (cast(void*) visitor)[0 .. __traits(classInstanceSize, RiscVVisitor)] = __traits(initSymbol, RiscVVisitor)[];
 | 
				
			||||||
    scope (exit)
 | 
					    scope (exit)
 | 
				
			||||||
    {
 | 
					    {
 | 
				
			||||||
        visitor.__xdtor();
 | 
					 | 
				
			||||||
        free(cast(void*) visitor);
 | 
					        free(cast(void*) visitor);
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
    visitor.visit(ast);
 | 
					    visitor.visit(ast);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    auto program = Symbol(String("main"));
 | 
					    auto program = Symbol(String("main"));
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    program.symbols = move(visitor.references);
 | 
					    program.symbols = Array!Reference(visitor.references[]);
 | 
				
			||||||
    foreach (ref instruction; visitor.instructions)
 | 
					    foreach (ref instruction; visitor.instructions[0 .. visitor.instructionsLength])
 | 
				
			||||||
    {
 | 
					    {
 | 
				
			||||||
        program.text.insertBack(instruction.encode[0 .. uint.sizeof]);
 | 
					        program.text.insertBack(instruction.encode[0 .. uint.sizeof]);
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
 
 | 
				
			|||||||
							
								
								
									
										53
									
								
								source/ir.cpp
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										53
									
								
								source/ir.cpp
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,53 @@
 | 
				
			|||||||
 | 
					#include "elna/ir.hpp"
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					namespace elna::ir
 | 
				
			||||||
 | 
					{
 | 
				
			||||||
 | 
					    /**
 | 
				
			||||||
 | 
					     * AST node.
 | 
				
			||||||
 | 
					     */
 | 
				
			||||||
 | 
					    void Node::accept(IRVisitor *)
 | 
				
			||||||
 | 
					    {
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    void Definition::accept(IRVisitor *visitor)
 | 
				
			||||||
 | 
					    {
 | 
				
			||||||
 | 
					        visitor->visit(this);
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    void Operand::accept(IRVisitor *visitor)
 | 
				
			||||||
 | 
					    {
 | 
				
			||||||
 | 
					        visitor->visit(this);
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    void Number::accept(IRVisitor *visitor)
 | 
				
			||||||
 | 
					    {
 | 
				
			||||||
 | 
					        visitor->visit(this);
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    void Variable::accept(IRVisitor *visitor)
 | 
				
			||||||
 | 
					    {
 | 
				
			||||||
 | 
					        visitor->visit(this);
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    BinaryExpression::BinaryExpression(Operand *lhs, Operand *rhs, BinaryOperator _operator)
 | 
				
			||||||
 | 
					    {
 | 
				
			||||||
 | 
					        this->lhs = lhs;
 | 
				
			||||||
 | 
					        this->rhs = rhs;
 | 
				
			||||||
 | 
					        this->_operator = _operator;
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    void BinaryExpression::accept(IRVisitor *visitor)
 | 
				
			||||||
 | 
					    {
 | 
				
			||||||
 | 
					        visitor->visit(this);
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    BangExpression::BangExpression(Operand *operand)
 | 
				
			||||||
 | 
					    {
 | 
				
			||||||
 | 
					        this->operand = operand;
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    void BangExpression::accept(IRVisitor *visitor)
 | 
				
			||||||
 | 
					    {
 | 
				
			||||||
 | 
					        visitor->visit(this);
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
							
								
								
									
										187
									
								
								source/lexer.cpp
									
									
									
									
									
								
							
							
						
						
									
										187
									
								
								source/lexer.cpp
									
									
									
									
									
								
							@@ -14,18 +14,18 @@ namespace elna
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
    source::const_iterator source::end() const
 | 
					    source::const_iterator source::end() const
 | 
				
			||||||
    {
 | 
					    {
 | 
				
			||||||
        source_position end_position{ 0, 0 };
 | 
					        Position end_position{ 0, 0 };
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        return source::const_iterator(std::cend(m_buffer), end_position);
 | 
					        return source::const_iterator(std::cend(m_buffer), end_position);
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    source::const_iterator::const_iterator(std::string::const_iterator buffer,
 | 
					    source::const_iterator::const_iterator(std::string::const_iterator buffer,
 | 
				
			||||||
            const source_position start_position)
 | 
					            const Position start_position)
 | 
				
			||||||
        : m_buffer(buffer), m_position(start_position)
 | 
					        : m_buffer(buffer), m_position(start_position)
 | 
				
			||||||
    {
 | 
					    {
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    const source_position& source::const_iterator::position() const noexcept
 | 
					    const Position& source::const_iterator::position() const noexcept
 | 
				
			||||||
    {
 | 
					    {
 | 
				
			||||||
        return this->m_position;
 | 
					        return this->m_position;
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
@@ -73,29 +73,109 @@ namespace elna
 | 
				
			|||||||
        return !(*this == that);
 | 
					        return !(*this == that);
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    token::token(const type of, source::const_iterator begin, source::const_iterator end)
 | 
					    Token::Token(const Type of, const char *value, Position position)
 | 
				
			||||||
        : m_type(of), m_value(begin, end)
 | 
					        : m_type(of), m_position(position)
 | 
				
			||||||
 | 
					    {
 | 
				
			||||||
 | 
					        std::size_t value_length = strlen(value);
 | 
				
			||||||
 | 
					        char *buffer = reinterpret_cast<char *>(malloc(value_length + 1));
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        std::memcpy(buffer, value, value_length);
 | 
				
			||||||
 | 
					        buffer[value_length] = 0;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        m_value.identifier = buffer;
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    Token::Token(const Type of, std::int32_t number, Position position)
 | 
				
			||||||
 | 
					        : m_type(of), m_position(position)
 | 
				
			||||||
 | 
					    {
 | 
				
			||||||
 | 
					        m_value.number = number;
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    Token::Token(const Type of, Position position)
 | 
				
			||||||
 | 
					        : m_type(of), m_position(position)
 | 
				
			||||||
    {
 | 
					    {
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    token::type token::of() const noexcept
 | 
					    Token::Token(const Token& that)
 | 
				
			||||||
 | 
					        : m_type(that.of()), m_position(that.position())
 | 
				
			||||||
 | 
					    {
 | 
				
			||||||
 | 
					        *this = that;
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    Token::Token(Token&& that)
 | 
				
			||||||
 | 
					        : m_type(that.of()), m_position(that.position())
 | 
				
			||||||
 | 
					    {
 | 
				
			||||||
 | 
					        *this = std::move(that);
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    Token::~Token()
 | 
				
			||||||
 | 
					    {
 | 
				
			||||||
 | 
					        if (m_type == TOKEN_IDENTIFIER || m_type == TOKEN_OPERATOR)
 | 
				
			||||||
 | 
					        {
 | 
				
			||||||
 | 
					            std::free(const_cast<char*>(m_value.identifier));
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    Token& Token::operator=(const Token& that)
 | 
				
			||||||
 | 
					    {
 | 
				
			||||||
 | 
					        m_type = that.of();
 | 
				
			||||||
 | 
					        m_position = that.position();
 | 
				
			||||||
 | 
					        if (that.of() == TOKEN_IDENTIFIER || that.of() == TOKEN_OPERATOR)
 | 
				
			||||||
 | 
					        {
 | 
				
			||||||
 | 
					            std::size_t value_length = strlen(that.identifier());
 | 
				
			||||||
 | 
					            char *buffer = reinterpret_cast<char *>(malloc(value_length + 1));
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					            std::memcpy(buffer, that.identifier(), value_length);
 | 
				
			||||||
 | 
					            buffer[value_length] = 0;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					            m_value.identifier = buffer;
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					        else if (that.of() == TOKEN_NUMBER)
 | 
				
			||||||
 | 
					        {
 | 
				
			||||||
 | 
					            m_value.number = that.number();
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					        return *this;
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    Token& Token::operator=(Token&& that)
 | 
				
			||||||
 | 
					    {
 | 
				
			||||||
 | 
					        m_type = that.of();
 | 
				
			||||||
 | 
					        m_position = that.position();
 | 
				
			||||||
 | 
					        if (that.of() == TOKEN_IDENTIFIER || that.of() == TOKEN_OPERATOR)
 | 
				
			||||||
 | 
					        {
 | 
				
			||||||
 | 
					            m_value.identifier = that.identifier();
 | 
				
			||||||
 | 
					            that.m_value.identifier = nullptr;
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					        else if (that.of() == TOKEN_NUMBER)
 | 
				
			||||||
 | 
					        {
 | 
				
			||||||
 | 
					            m_value.number = that.number();
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					        return *this;
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    Token::Type Token::of() const noexcept
 | 
				
			||||||
    {
 | 
					    {
 | 
				
			||||||
        return m_type;
 | 
					        return m_type;
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    const token::value& token::identifier() const noexcept
 | 
					    const char *Token::identifier() const noexcept
 | 
				
			||||||
    {
 | 
					    {
 | 
				
			||||||
        return m_value;
 | 
					        return m_value.identifier;
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    const source_position& token::position() const noexcept
 | 
					    std::int32_t Token::number() const noexcept
 | 
				
			||||||
 | 
					    {
 | 
				
			||||||
 | 
					        return m_value.number;
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    const Position& Token::position() const noexcept
 | 
				
			||||||
    {
 | 
					    {
 | 
				
			||||||
        return m_position;
 | 
					        return m_position;
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    result<std::vector<token>> lex(const std::string& buffer)
 | 
					    Token *lex(const char *buffer, CompileError *compile_error, std::size_t *length)
 | 
				
			||||||
    {
 | 
					    {
 | 
				
			||||||
        std::vector<token> tokens;
 | 
					        std::vector<Token> tokens;
 | 
				
			||||||
        source input{ buffer };
 | 
					        source input{ buffer };
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        for (auto iterator = input.begin(); iterator != input.end();)
 | 
					        for (auto iterator = input.begin(); iterator != input.end();)
 | 
				
			||||||
@@ -103,23 +183,88 @@ namespace elna
 | 
				
			|||||||
            if (*iterator == ' ' || *iterator == '\n')
 | 
					            if (*iterator == ' ' || *iterator == '\n')
 | 
				
			||||||
            {
 | 
					            {
 | 
				
			||||||
            }
 | 
					            }
 | 
				
			||||||
            else if (std::isgraph(*iterator))
 | 
					            else if (std::isdigit(*iterator))
 | 
				
			||||||
            {
 | 
					            {
 | 
				
			||||||
                auto current_position = iterator;
 | 
					                tokens.emplace_back(
 | 
				
			||||||
                do
 | 
					                        Token::TOKEN_NUMBER,
 | 
				
			||||||
 | 
					                        static_cast<std::int32_t>(*iterator - '0'),
 | 
				
			||||||
 | 
					                        iterator.position()
 | 
				
			||||||
 | 
					                );
 | 
				
			||||||
 | 
					            }
 | 
				
			||||||
 | 
					            else if (*iterator == '=')
 | 
				
			||||||
 | 
					            {
 | 
				
			||||||
 | 
					                tokens.emplace_back(Token::TOKEN_EQUALS, iterator.position());
 | 
				
			||||||
 | 
					            }
 | 
				
			||||||
 | 
					            else if (*iterator == '(')
 | 
				
			||||||
 | 
					            {
 | 
				
			||||||
 | 
					                tokens.emplace_back(Token::TOKEN_LEFT_PAREN, iterator.position());
 | 
				
			||||||
 | 
					            }
 | 
				
			||||||
 | 
					            else if (*iterator == ')')
 | 
				
			||||||
 | 
					            {
 | 
				
			||||||
 | 
					                tokens.emplace_back(Token::TOKEN_RIGHT_PAREN, iterator.position());
 | 
				
			||||||
 | 
					            }
 | 
				
			||||||
 | 
					            else if (*iterator == ';')
 | 
				
			||||||
 | 
					            {
 | 
				
			||||||
 | 
					                tokens.emplace_back(Token::TOKEN_SEMICOLON, iterator.position());
 | 
				
			||||||
 | 
					            }
 | 
				
			||||||
 | 
					            else if (*iterator == ',')
 | 
				
			||||||
 | 
					            {
 | 
				
			||||||
 | 
					                tokens.emplace_back(Token::TOKEN_COMMA, iterator.position());
 | 
				
			||||||
 | 
					            }
 | 
				
			||||||
 | 
					            else if (*iterator == '!')
 | 
				
			||||||
 | 
					            {
 | 
				
			||||||
 | 
					                tokens.emplace_back(Token::TOKEN_BANG, iterator.position());
 | 
				
			||||||
 | 
					            }
 | 
				
			||||||
 | 
					            else if (*iterator == '.')
 | 
				
			||||||
 | 
					            {
 | 
				
			||||||
 | 
					                tokens.emplace_back(Token::TOKEN_DOT, iterator.position());
 | 
				
			||||||
 | 
					            }
 | 
				
			||||||
 | 
					            else if (std::isalpha(*iterator))
 | 
				
			||||||
 | 
					            {
 | 
				
			||||||
 | 
					                std::string word;
 | 
				
			||||||
 | 
					                auto i = iterator;
 | 
				
			||||||
 | 
					                while (i != input.end() && std::isalpha(*i))
 | 
				
			||||||
                {
 | 
					                {
 | 
				
			||||||
                    ++current_position;
 | 
					                    word.push_back(*i);
 | 
				
			||||||
 | 
					                    ++i;
 | 
				
			||||||
                }
 | 
					                }
 | 
				
			||||||
                while (current_position != input.end() && std::isgraph(*current_position));
 | 
					                if (word == "const")
 | 
				
			||||||
                token new_token{ token::type::word, iterator, current_position };
 | 
					                {
 | 
				
			||||||
 | 
					                    tokens.emplace_back(Token::TOKEN_LET, iterator.position());
 | 
				
			||||||
                tokens.push_back(new_token);
 | 
					                }
 | 
				
			||||||
                iterator = current_position;
 | 
					                else if (word == "var")
 | 
				
			||||||
 | 
					                {
 | 
				
			||||||
 | 
					                    tokens.emplace_back(Token::TOKEN_VAR, iterator.position());
 | 
				
			||||||
 | 
					                }
 | 
				
			||||||
 | 
					                else
 | 
				
			||||||
 | 
					                {
 | 
				
			||||||
 | 
					                    tokens.emplace_back(Token::TOKEN_IDENTIFIER, word.c_str(), iterator.position());
 | 
				
			||||||
 | 
					                }
 | 
				
			||||||
 | 
					                iterator = i;
 | 
				
			||||||
                continue;
 | 
					                continue;
 | 
				
			||||||
            }
 | 
					            }
 | 
				
			||||||
 | 
					            else if (*iterator == '+' || *iterator == '-')
 | 
				
			||||||
 | 
					            {
 | 
				
			||||||
 | 
					                std::string _operator{ *iterator };
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					                tokens.emplace_back(Token::TOKEN_OPERATOR, _operator.c_str(), iterator.position());
 | 
				
			||||||
 | 
					            }
 | 
				
			||||||
 | 
					            else
 | 
				
			||||||
 | 
					            {
 | 
				
			||||||
 | 
					                *compile_error = CompileError("Unexpected next character", iterator.position());
 | 
				
			||||||
 | 
					                return nullptr;
 | 
				
			||||||
 | 
					            }
 | 
				
			||||||
            ++iterator;
 | 
					            ++iterator;
 | 
				
			||||||
        }
 | 
					        }
 | 
				
			||||||
 | 
					        Token *target = reinterpret_cast<Token *>(malloc(tokens.size() * sizeof(Token) + sizeof(Token)));
 | 
				
			||||||
 | 
					        int i = 0;
 | 
				
			||||||
 | 
					        for (auto& token : tokens)
 | 
				
			||||||
 | 
					        {
 | 
				
			||||||
 | 
					            target[i] = std::move(token);
 | 
				
			||||||
 | 
					            ++i;
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					        *length = i;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        return tokens;
 | 
					        return target;
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -2,23 +2,23 @@
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
namespace elna
 | 
					namespace elna
 | 
				
			||||||
{
 | 
					{
 | 
				
			||||||
    compile_error::compile_error(const char *message, const source_position position) noexcept
 | 
					    CompileError::CompileError(const char *message, const Position position) noexcept
 | 
				
			||||||
    {
 | 
					    {
 | 
				
			||||||
        this->message = message;
 | 
					        this->message = message;
 | 
				
			||||||
        this->position = position;
 | 
					        this->position = position;
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    char const *compile_error::what() const noexcept
 | 
					    char const *CompileError::what() const noexcept
 | 
				
			||||||
    {
 | 
					    {
 | 
				
			||||||
        return this->message;
 | 
					        return this->message;
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    std::size_t compile_error::line() const noexcept
 | 
					    std::size_t CompileError::line() const noexcept
 | 
				
			||||||
    {
 | 
					    {
 | 
				
			||||||
        return this->position.line;
 | 
					        return this->position.line;
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    std::size_t compile_error::column() const noexcept
 | 
					    std::size_t CompileError::column() const noexcept
 | 
				
			||||||
    {
 | 
					    {
 | 
				
			||||||
        return this->position.column;
 | 
					        return this->position.column;
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
							
								
								
									
										191
									
								
								source/riscv.cpp
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										191
									
								
								source/riscv.cpp
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,191 @@
 | 
				
			|||||||
 | 
					#include "elna/parser.hpp"
 | 
				
			||||||
 | 
					#include "elna/riscv.hpp"
 | 
				
			||||||
 | 
					#include <type_traits>
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					namespace elna
 | 
				
			||||||
 | 
					{
 | 
				
			||||||
 | 
					    Instruction::Instruction(BaseOpcode opcode)
 | 
				
			||||||
 | 
					    {
 | 
				
			||||||
 | 
					        this->instruction = static_cast<std::underlying_type<BaseOpcode>::type>(opcode);
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    Instruction& Instruction::i(XRegister rd, Funct3 funct3, XRegister rs1, std::uint32_t immediate)
 | 
				
			||||||
 | 
					    {
 | 
				
			||||||
 | 
					        this->instruction |= (static_cast<std::underlying_type<XRegister>::type>(rd) << 7)
 | 
				
			||||||
 | 
					            | (static_cast<std::underlying_type<Funct3>::type>(funct3) << 12)
 | 
				
			||||||
 | 
					            | (static_cast<std::underlying_type<XRegister>::type>(rs1) << 15)
 | 
				
			||||||
 | 
					            | (immediate << 20);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        return *this;
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    Instruction& Instruction::s(std::uint32_t imm1, Funct3 funct3, XRegister rs1, XRegister rs2)
 | 
				
			||||||
 | 
					    {
 | 
				
			||||||
 | 
					        this->instruction |= ((imm1 & 0b11111) << 7)
 | 
				
			||||||
 | 
					            | (static_cast<std::underlying_type<Funct3>::type>(funct3) << 12)
 | 
				
			||||||
 | 
					            | (static_cast<std::underlying_type<XRegister>::type>(rs1) << 15)
 | 
				
			||||||
 | 
					            | (static_cast<std::underlying_type<XRegister>::type>(rs2) << 20)
 | 
				
			||||||
 | 
					            | ((imm1 & 0b111111100000) << 20);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        return *this;
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    Instruction& Instruction::r(XRegister rd, Funct3 funct3, XRegister rs1, XRegister rs2, Funct7 funct7)
 | 
				
			||||||
 | 
					    {
 | 
				
			||||||
 | 
					        this->instruction |= (static_cast<std::underlying_type<XRegister>::type>(rd) << 7)
 | 
				
			||||||
 | 
					            | (static_cast<std::underlying_type<Funct3>::type>(funct3) << 12)
 | 
				
			||||||
 | 
					            | (static_cast<std::underlying_type<XRegister>::type>(rs1) << 15)
 | 
				
			||||||
 | 
					            | (static_cast<std::underlying_type<XRegister>::type>(rs2) << 20)
 | 
				
			||||||
 | 
					            | (static_cast<std::underlying_type<Funct7>::type>(funct7) << 25);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        return *this;
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    Instruction& Instruction::u(XRegister rd, std::uint32_t imm)
 | 
				
			||||||
 | 
					    {
 | 
				
			||||||
 | 
					        this->instruction |= (static_cast<std::underlying_type<XRegister>::type>(rd) << 7) | (imm << 12);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        return *this;
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    std::uint8_t *Instruction::encode()
 | 
				
			||||||
 | 
					    {
 | 
				
			||||||
 | 
					        return reinterpret_cast<std::uint8_t *>(&this->instruction);
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    void RiscVVisitor::visit(ir::Node *)
 | 
				
			||||||
 | 
					    {
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    void RiscVVisitor::visit(ir::Definition *definition)
 | 
				
			||||||
 | 
					    {
 | 
				
			||||||
 | 
					        const uint stackSize = static_cast<std::uint32_t>(definition->statementsLength * 4 + 12);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        this->instructionsLength += 4;
 | 
				
			||||||
 | 
					        this->instructions = reinterpret_cast<Instruction *>(
 | 
				
			||||||
 | 
					                realloc(this->instructions, this->instructionsLength * sizeof(Instruction)));
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        // Prologue.
 | 
				
			||||||
 | 
					        this->instructions[instructionsLength - 4] = Instruction(BaseOpcode::opImm)
 | 
				
			||||||
 | 
					            .i(XRegister::sp, Funct3::addi, XRegister::sp, -stackSize);
 | 
				
			||||||
 | 
					        this->instructions[instructionsLength - 3] = Instruction(BaseOpcode::store)
 | 
				
			||||||
 | 
					            .s(stackSize - 4, Funct3::sw, XRegister::sp, XRegister::s0);
 | 
				
			||||||
 | 
					        this->instructions[instructionsLength - 2] = Instruction(BaseOpcode::store)
 | 
				
			||||||
 | 
					            .s(stackSize - 8, Funct3::sw, XRegister::sp, XRegister::ra);
 | 
				
			||||||
 | 
					        this->instructions[instructionsLength - 1] = Instruction(BaseOpcode::opImm)
 | 
				
			||||||
 | 
					            .i(XRegister::s0, Funct3::addi, XRegister::sp, stackSize);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        for (std::size_t i = 0; i < definition->statementsLength; ++i)
 | 
				
			||||||
 | 
					        {
 | 
				
			||||||
 | 
					            definition->statements[i]->accept(this);
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					        this->registerInUse = true;
 | 
				
			||||||
 | 
					        definition->result->accept(this);
 | 
				
			||||||
 | 
					        this->registerInUse = false;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        this->instructions = reinterpret_cast<Instruction*>(
 | 
				
			||||||
 | 
					            realloc(this->instructions, (this->instructionsLength + 10) * sizeof(Instruction)));
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        // Print the result.
 | 
				
			||||||
 | 
					        this->instructions[instructionsLength++] = Instruction(BaseOpcode::opImm)
 | 
				
			||||||
 | 
					            .i(XRegister::a1, Funct3::addi, XRegister::a0, 0);
 | 
				
			||||||
 | 
					        this->references[0] = Reference();
 | 
				
			||||||
 | 
					        this->references[0].name = ".CL0";
 | 
				
			||||||
 | 
					        this->references[0].offset = instructionsLength * 4;
 | 
				
			||||||
 | 
					        this->references[0].target = Target::high20;
 | 
				
			||||||
 | 
					        this->instructions[instructionsLength++] = Instruction(BaseOpcode::lui).u(XRegister::a5, 0);
 | 
				
			||||||
 | 
					        this->references[1] = Reference();
 | 
				
			||||||
 | 
					        this->references[1].name = ".CL0";
 | 
				
			||||||
 | 
					        this->references[1].offset = instructionsLength * 4;
 | 
				
			||||||
 | 
					        this->references[1].target = Target::lower12i;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        this->instructions[instructionsLength++] = Instruction(BaseOpcode::opImm)
 | 
				
			||||||
 | 
					            .i(XRegister::a0, Funct3::addi, XRegister::a5, 0);
 | 
				
			||||||
 | 
					        this->references[2] = Reference();
 | 
				
			||||||
 | 
					        this->references[2].name = "printf";
 | 
				
			||||||
 | 
					        this->references[2].offset = instructionsLength * 4;
 | 
				
			||||||
 | 
					        this->references[2].target = Target::text;
 | 
				
			||||||
 | 
					        this->instructions[instructionsLength++] = Instruction(BaseOpcode::auipc).u(XRegister::ra, 0);
 | 
				
			||||||
 | 
					        this->instructions[instructionsLength++] = Instruction(BaseOpcode::jalr)
 | 
				
			||||||
 | 
					            .i(XRegister::ra, Funct3::jalr, XRegister::ra, 0);
 | 
				
			||||||
 | 
					        // Set the return value (0).
 | 
				
			||||||
 | 
					        this->instructions[instructionsLength++] = Instruction(BaseOpcode::op)
 | 
				
			||||||
 | 
					            .r(XRegister::a0, Funct3::_and, XRegister::zero, XRegister::zero);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        // Epilogue.
 | 
				
			||||||
 | 
					        this->instructions[instructionsLength++] = Instruction(BaseOpcode::load)
 | 
				
			||||||
 | 
					            .i(XRegister::s0, Funct3::lw, XRegister::sp, stackSize - 4);
 | 
				
			||||||
 | 
					        this->instructions[instructionsLength++] = Instruction(BaseOpcode::load)
 | 
				
			||||||
 | 
					            .i(XRegister::ra, Funct3::lw, XRegister::sp, stackSize - 8);
 | 
				
			||||||
 | 
					        this->instructions[instructionsLength++] = Instruction(BaseOpcode::opImm)
 | 
				
			||||||
 | 
					                .i(XRegister::sp, Funct3::addi, XRegister::sp, stackSize);
 | 
				
			||||||
 | 
					        this->instructions[instructionsLength++] = Instruction(BaseOpcode::jalr)
 | 
				
			||||||
 | 
					            .i(XRegister::zero, Funct3::jalr, XRegister::ra, 0);
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    void RiscVVisitor::visit(ir::Operand *operand)
 | 
				
			||||||
 | 
					    {
 | 
				
			||||||
 | 
					        if (dynamic_cast<ir::Variable *>(operand) != nullptr)
 | 
				
			||||||
 | 
					        {
 | 
				
			||||||
 | 
					            return dynamic_cast<ir::Variable *>(operand)->accept(this);
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					        if (dynamic_cast<ir::Number *>(operand) != nullptr)
 | 
				
			||||||
 | 
					        {
 | 
				
			||||||
 | 
					            return dynamic_cast<ir::Number *>(operand)->accept(this);
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    void RiscVVisitor::visit(ir::Variable *variable)
 | 
				
			||||||
 | 
					    {
 | 
				
			||||||
 | 
					        const auto freeRegister = this->registerInUse ? XRegister::a0 : XRegister::t0;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        ++this->instructionsLength;
 | 
				
			||||||
 | 
					        this->instructions = reinterpret_cast<Instruction *>(
 | 
				
			||||||
 | 
					                realloc(this->instructions, this->instructionsLength * sizeof(Instruction)));
 | 
				
			||||||
 | 
					        // movl -x(%rbp), %eax; where x is a number.
 | 
				
			||||||
 | 
					        this->instructions[instructionsLength - 1] = Instruction(BaseOpcode::load)
 | 
				
			||||||
 | 
					            .i(freeRegister, Funct3::lw, XRegister::sp,
 | 
				
			||||||
 | 
					                static_cast<std::int8_t>(variable->counter * 4));
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    void RiscVVisitor::visit(ir::Number *number)
 | 
				
			||||||
 | 
					    {
 | 
				
			||||||
 | 
					        const auto freeRegister = this->registerInUse ? XRegister::a0 : XRegister::t0;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        ++this->instructionsLength;
 | 
				
			||||||
 | 
					        this->instructions = reinterpret_cast<Instruction *>(
 | 
				
			||||||
 | 
					                realloc(this->instructions, this->instructionsLength * sizeof(Instruction)));
 | 
				
			||||||
 | 
					        this->instructions[this->instructionsLength - 1] =
 | 
				
			||||||
 | 
					            Instruction(BaseOpcode::opImm) // movl $x, %eax; where $x is a number.
 | 
				
			||||||
 | 
					                .i(freeRegister, Funct3::addi, XRegister::zero, number->value);
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    void RiscVVisitor::visit(ir::BinaryExpression *expression)
 | 
				
			||||||
 | 
					    {
 | 
				
			||||||
 | 
					        this->registerInUse = true;
 | 
				
			||||||
 | 
					        expression->lhs->accept(this);
 | 
				
			||||||
 | 
					        this->registerInUse = false;
 | 
				
			||||||
 | 
					        expression->rhs->accept(this);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        this->instructionsLength += 2;
 | 
				
			||||||
 | 
					        this->instructions = reinterpret_cast<Instruction *>(
 | 
				
			||||||
 | 
					                realloc(this->instructions, this->instructionsLength * sizeof(Instruction)));
 | 
				
			||||||
 | 
					        // Calculate the result and assign it to a variable on the stack.
 | 
				
			||||||
 | 
					        switch (expression->_operator)
 | 
				
			||||||
 | 
					        {
 | 
				
			||||||
 | 
					            case BinaryOperator::sum:
 | 
				
			||||||
 | 
					                this->instructions[instructionsLength - 2] = Instruction(BaseOpcode::op)
 | 
				
			||||||
 | 
					                    .r(XRegister::a0, Funct3::add, XRegister::a0, XRegister::t0);
 | 
				
			||||||
 | 
					                break;
 | 
				
			||||||
 | 
					            case BinaryOperator::subtraction:
 | 
				
			||||||
 | 
					                this->instructions[instructionsLength - 2] = Instruction(BaseOpcode::op)
 | 
				
			||||||
 | 
					                    .r(XRegister::a0, Funct3::sub, XRegister::a0, XRegister::t0, Funct7::sub);
 | 
				
			||||||
 | 
					                break;
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					        this->instructions[instructionsLength - 1] = // movl %eax, -x(%rbp); where x is a number.
 | 
				
			||||||
 | 
					            Instruction(BaseOpcode::store)
 | 
				
			||||||
 | 
					                .s(static_cast<std::uint32_t>(this->variableCounter * 4), Funct3::sw, XRegister::sp, XRegister::a0);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        ++this->variableCounter;
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
		Reference in New Issue
	
	Block a user