Compile to bytecode

This commit is contained in:
2020-09-03 19:03:32 +02:00
parent 473334c3db
commit 96345ad6ba
45 changed files with 1615 additions and 1231 deletions

View File

@@ -0,0 +1,25 @@
#pragma once
#include <string>
#include <token/token.hpp>
#include <unordered_map>
#include <vector>
#include <utility>
namespace Compile
{
class Compiler
{
private:
std::unordered_map<std::string, std::size_t> jumpLabelLocations;
std::vector<std::pair<Token::Token, std::size_t>> unresolvedJumpLabels;
void InsertAsBytes(
Token::Token const & token,
std::vector<std::uint8_t> & bytes);
public:
bool Compile(
std::vector<Token::Token> const & tokens,
std::vector<std::uint8_t> & bytes);
};
}

View File

@@ -0,0 +1,28 @@
#pragma once
#include <string>
#include <token/token.hpp>
namespace Compile
{
class CompilationError
{
public:
Token::Token errorToken;
CompilationError(std::string const & message, Token::Token const & token);
static CompilationError CreateExpectedArgumentError(Token::Token const & token);
static CompilationError CreateExpectedLabelError(Token::Token const & token);
static CompilationError CreateExpectedImmediateError(Token::Token const & token);
static CompilationError CreateExpectedImmediateOrRegisterOrMemory(Token::Token const & token);
static CompilationError CreateExpectedRegisterError(Token::Token const & token);
static CompilationError CreateExpectedRegisterOrMemoryError(Token::Token const & token);
static CompilationError CreateExpectedOperandError(Token::Token const & token);
static CompilationError CreateTooManyArgumentsError(Token::Token const & token);
static CompilationError CreateTooFewArgumentsError(Token::Token const & token);
static CompilationError CreateExpectedEndOfStatementError(Token::Token const & token);
static CompilationError CreateDuplicateLabelError(Token::Token const & token);
static CompilationError CreateNonExistingLabelError(Token::Token const & token);
};
}

View File

@@ -1,13 +0,0 @@
#pragma once
#include <string>
#include <utility>
#include <vector>
struct Configuration
{
unsigned memorySize;
std::vector<std::pair<unsigned, std::string>> strings;
void PrepareMemory(std::vector<std::uint8_t> & memory) const;
Configuration();
};

View File

@@ -0,0 +1,37 @@
#pragma once
#include <variant>
#include <execute/bytecode.hpp>
#include <execute/error.hpp>
#include <execute/state.hpp>
#include <stdexcept>
#include <vector>
namespace Execute
{
enum class ArgumentType
{
Immediate,
Register,
Memory
};
class ArgumentValue
{
private:
ArgumentType type;
ArgumentType memoryValueType;
std::variant<RegisterByte, int> data;
int & GetRegister(State & state) const;
std::uint8_t * GetMemory(State & state) const;
public:
void Write(int const value, State & state) const;
int Read(State & state) const;
// Returns the size of the argument in bytes
std::size_t Parse(
std::vector<std::uint8_t> const & memory,
std::size_t const pos);
};
}

View File

@@ -0,0 +1,43 @@
#pragma once
#include <cstdint>
namespace Execute
{
enum class InstructionByte : std::uint8_t
{
NOOP = 0,
/* Integer functions */
ADD_INTEGER,
SUBTRACT_INTEGER,
DIVIDE_INTEGER,
MULTIPLY_INTEGER,
SHIFT_LEFT_INTEGER,
SHIFT_RIGHT_INTEGER,
SET_INTEGER,
/* Control flow */
JUMP,
INTERRUPT,
CALL,
RETURN,
EXIT,
LESS_THAN_INTEGER,
GREATER_THAN_INTEGER,
EQUALS_INTEGER,
/* Memory */
POP_INTEGER,
PUSH_INTEGER,
/* Values */
IMMEDIATE_INTEGER,
REGISTER,
MEMORY_OP,
LABEL,
};
enum class RegisterByte : std::uint8_t
{
A = 1,
B,
C,
D
};
}

View File

@@ -7,50 +7,44 @@ namespace Execute
{
protected:
std::string message;
std::size_t byteLocation;
public:
std::string const & GetMessage() const;
RuntimeError();
RuntimeError(std::string const & what);
};
class StackUnderflow : public RuntimeError
{
public:
StackUnderflow();
};
class StackOverflow : public RuntimeError
{
public:
StackOverflow();
};
class MissingLabel : public RuntimeError
{
public:
MissingLabel(std::string const & label);
RuntimeError(std::string const & what, std::size_t const byteLocation);
};
class InterruptIndexOutOfRange : public RuntimeError
{
public:
InterruptIndexOutOfRange(int const index);
InterruptIndexOutOfRange(std::size_t const location, int const index);
};
class AttemptedWriteToImmediate : public RuntimeError
{
public:
AttemptedWriteToImmediate(std::size_t const location);
};
class NonExecutableInstruction : public RuntimeError
{
public:
NonExecutableInstruction(std::size_t const location);
};
class NonArgumentByte : public RuntimeError
{
public:
NonArgumentByte(std::size_t const location);
};
class OutOfMemory : public RuntimeError
{
public:
OutOfMemory(int const memoryLocation, int const memorySize);
OutOfMemory(
std::size_t const requiredMemorySize,
std::size_t const actualMemorySize);
};
namespace Internal
{
class BadValueType : public RuntimeError
{
public:
BadValueType();
};
}
}

View File

@@ -1,9 +0,0 @@
#pragma once
namespace Execute
{
struct Flags
{
};
}

View File

@@ -3,9 +3,10 @@
#include <execute/registers.hpp>
#include <vector>
using InterruptFn = void (*)(Execute::Registers & registers, std::vector<std::uint8_t> & memory);
namespace Execute
{
std::vector<InterruptFn> GetInterrupts();
void ExecuteInterrupt(
int const id,
Execute::Registers & registers,
std::vector<std::uint8_t> & memory);
}

View File

@@ -1,19 +1,14 @@
#pragma once
#include <cstddef>
namespace Execute
{
struct Registers
{
union
{
struct
{
int A;
int B;
int C;
int D;
};
int registers[4];
};
int A, B, C, D;
std::size_t programCounter;
std::size_t stackPointer;
// TODO status registers?
};
}

View File

@@ -1,18 +1,17 @@
#pragma once
#include <execute/interrupts.hpp>
#include <execute/registers.hpp>
#include <string>
#include <unordered_map>
#include <vector>
namespace Execute
{
struct State
{
unsigned currentStatement;
unsigned nextStatement;
std::unordered_map<std::string, unsigned> const * labelStatementIndice;
std::vector<InterruptFn> interrupts;
std::vector<std::uint8_t> memory;
unsigned stackPointer;
bool terminated;
Registers registers;
std::vector<std::uint8_t> memory;
void PushToStack(int const value);
int PopFromStack();
};
}

View File

@@ -1,8 +1,7 @@
#include <configuration.hpp>
#include <execute/flags.hpp>
#include <execute/registers.hpp>
#include <array>
#include <execute/argumentvalue.hpp>
#include <execute/bytecode.hpp>
#include <execute/state.hpp>
#include <interpret/code.hpp>
#include <memory>
namespace Execute
@@ -10,27 +9,39 @@ namespace Execute
class VirtualMachine
{
private:
Flags flags;
Registers registers;
State state;
std::unique_ptr<Interpret::Code const> codePtr;
void DoArithmatic(
InstructionByte const instruction,
std::array<ArgumentValue, 3> & arguments);
void SetInteger(std::array<ArgumentValue, 3> & arguments);
void ExecuteJump(std::array<ArgumentValue, 3> & arguments);
void ExecuteInterrupt(std::array<ArgumentValue, 3> & arguments);
void ExecuteCall(
std::array<ArgumentValue, 3> & arguments,
std::size_t const returnByte);
void ExecuteReturn();
void DoBooleanLogic(
InstructionByte const instruction,
std::array<ArgumentValue, 3> & arguments,
std::size_t const nextInstruction);
void ExecutePop(std::array<ArgumentValue, 3> & arguments);
void ExecutePush(std::array<ArgumentValue, 3> & arguments);
void Step();
public:
void Run();
void SingleStep();
void LoadConfiguration(Configuration const & c);
void LoadCode(std::unique_ptr<Interpret::Code> code);
Flags const & GetFlags() const;
Registers const & GetRegisters() const;
void SetMemorySize(std::size_t const size);
void LoadCode(
std::vector<std::uint8_t> const & byteCode,
bool const printDecodedBytes);
State const & GetState() const;
Interpret::Statement const * const GetCurrentStatement() const;
Execute::InstructionByte GetCurrentInstruction() const;
bool IsTerminated() const;
VirtualMachine();
};
}

View File

@@ -1,21 +0,0 @@
#pragma once
#include <interpret/statement.hpp>
#include <memory>
#include <string>
#include <unordered_map>
#include <vector>
namespace Interpret
{
struct Code
{
std::vector<std::unique_ptr<Statement>> statements;
std::unordered_map<std::string, unsigned> labelStatementIndice;
std::unordered_map<std::string, int> declarations;
Code() = default;
~Code() = default;
Code(const Code&) = delete;
Code& operator=(const Code&) = delete;
};
}

View File

@@ -1,76 +0,0 @@
#pragma once
#include <exception>
#include <string>
#include <token/token.hpp>
namespace Interpret
{
struct InterpretationError : public std::exception
{
Token::Token errorToken;
InterpretationError(Token::Token const & token, std::string const & msg);
};
struct ExpectedArgument : public InterpretationError
{
ExpectedArgument(Token::Token const & token);
};
struct ExpectedLabel : public InterpretationError
{
ExpectedLabel(Token::Token const & token);
};
struct ExpectedValue : public InterpretationError
{
ExpectedValue(Token::Token const & token);
};
struct ExpectedImmediate : public InterpretationError
{
ExpectedImmediate(Token::Token const & token);
};
struct ExpectedImmediateOrMemory : public InterpretationError
{
ExpectedImmediateOrMemory(Token::Token const & token);
};
struct ExpectedRegister : public InterpretationError
{
ExpectedRegister(Token::Token const & token);
};
struct ExpectedRegisterOrMemory : public InterpretationError
{
ExpectedRegisterOrMemory(Token::Token const & token);
};
struct ExpectedOperand : public InterpretationError
{
ExpectedOperand(Token::Token const & token);
};
struct TooManyArguments : public InterpretationError
{
TooManyArguments(Token::Token const & token);
};
struct TooFewArguments : public InterpretationError
{
TooFewArguments(Token::Token const & token);
};
struct MissingEndOfStatment : public InterpretationError
{
MissingEndOfStatment(Token::Token const & token);
};
namespace Internal
{
struct BadTokenForValue : public InterpretationError
{
BadTokenForValue(Token::Token const & token);
};
}
}

View File

@@ -1,14 +0,0 @@
#pragma once
#include <interpret/code.hpp>
#include <memory>
#include <token/token.hpp>
#include <vector>
namespace Interpret
{
class Interpreter
{
public:
void Interpret(std::vector<Token::Token> const & tokens, Code & code);
};
}

View File

@@ -1,13 +0,0 @@
#pragma once
#include <interpret/statement.hpp>
#include <memory>
#include <vector>
namespace Interpret
{
std::unique_ptr<Statement> ExtractStatement(unsigned const operatorIndex, std::vector<Token::Token> const & tokens);
std::tuple<std::string, int> ExtractDeclaration(unsigned const operatorIndex, std::vector<Token::Token> const & tokens);
int GetRequiredNumberOfArguments(Token::OperandType const type);
}

View File

@@ -1,107 +0,0 @@
#pragma once
#include <execute/flags.hpp>
#include <execute/registers.hpp>
#include <execute/state.hpp>
#include <interpret/value.hpp>
namespace Interpret
{
struct Statement
{
virtual void Execute(Execute::Flags & flags, Execute::State & state, Execute::Registers & registers) = 0;
};
struct NoArgumentStatement : Statement
{
void (* function)(Execute::Flags & flags, Execute::Registers & registers);
void Execute(Execute::Flags & flags, Execute::State & state, Execute::Registers & registers) override;
};
struct OneArgumentStatement : Statement
{
void (* function)(Execute::Flags & flags, int argument1);
Value firstArgument;
void Execute(Execute::Flags & flags, Execute::State & state, Execute::Registers & registers) override;
};
struct ControlFlowStatement : Statement
{
void (* function)(Execute::State & state, int argument1, int argument2);
Value firstArgument;
Value secondArgument;
void Execute(Execute::Flags & flags, Execute::State & state, Execute::Registers & registers) override;
};
struct ArithmeticStatement : Statement
{
void (* function)(int argument1, int argument2, int & argument3);
Value firstArgument;
Value secondArgument;
Value thirdArgument;
void Execute(Execute::Flags & flags, Execute::State & state, Execute::Registers & registers) override;
};
struct JumpStatement : Statement
{
private:
std::string const label;
public:
void Execute(Execute::Flags & flags, Execute::State & state, Execute::Registers & registers) override;
JumpStatement(std::string const & label);
};
struct FunctionCallStatement : Statement
{
private:
std::string const label;
public:
void Execute(Execute::Flags & flags, Execute::State & state, Execute::Registers & registers) override;
FunctionCallStatement(std::string const & label);
};
struct ReturnFromFunctionStatement : Statement
{
void Execute(Execute::Flags & flags, Execute::State & state, Execute::Registers & registers) override;
};
struct ExitProgramStatement : Statement
{
void Execute(Execute::Flags & flags, Execute::State & state, Execute::Registers & registers) override;
};
struct SetStatement : Statement
{
Value firstArgument;
Value secondArgument;
void Execute(Execute::Flags & flags, Execute::State & state, Execute::Registers & registers) override;
};
struct InterruptStatement : Statement
{
Value firstArgument;
void Execute(Execute::Flags & flags, Execute::State & state, Execute::Registers & registers) override;
};
struct PushStatement : Statement
{
Value firstArgument;
void Execute(Execute::Flags & flags, Execute::State & state, Execute::Registers & registers) override;
};
struct PopStatement : Statement
{
Value firstArgument;
void Execute(Execute::Flags & flags, Execute::State & state, Execute::Registers & registers) override;
};
}

View File

@@ -1,30 +0,0 @@
#pragma once
#include <execute/state.hpp>
#include <token/token.hpp>
namespace Interpret
{
enum class ValueType
{
Register,
ImmediateInteger,
MemoryLocation
};
enum class ValueDataType
{
Register,
Immediate
};
struct Value
{
ValueType type;
ValueDataType dataType;
int data;
int & GetValue(Execute::State & state, Execute::Registers & registers);
void CreateFromToken(Token::Token const & token);
};
}

View File

@@ -10,9 +10,4 @@ namespace Token
std::string errorMsg;
TokenizationError(Token const & token, std::string const & msg);
};
struct MissingEndOfString : public TokenizationError
{
MissingEndOfString(Token const & token);
};
}

View File

@@ -39,13 +39,15 @@ namespace Token
static Token CreateEmptyToken(int const lineNumber, int const lineColumn);
static Token CreateErrorToken(std::string const & message, TokenType const type, int const lineNumber, int const lineColumn);
static Token CreateStatementEndToken(int const lineNumber, int const lineColumn);
static Token CreateLabelToken(std::string const & string, int const lineNumber, int const lineColumn);
static Token CreateLabelDefinitionToken(std::string const & string, int const lineNumber, int const lineColumn);
static Token CreateLabelArgumentToken(std::string const & string, int const lineNumber, int const lineColumn);
static Token CreateImmediateValueToken(int const value, int const lineNumber, int const lineColumn);
static Token CreateRegisterToken(RegisterType const registerType, int const lineNumber, int const lineColumn);
static Token CreateOperandToken(OperandType const operandType, int const lineNumber, int const lineColumn);
static Token CreateMemoryToken(RegisterType const registerType, int const lineNumber, int const lineColumn);
static Token CreateMemoryToken(int const value, int const lineNumber, int const lineColumn);
std::string GetName() const;
void Print() const;
};
}

View File

@@ -8,7 +8,7 @@ namespace Token
class Tokenizer
{
private:
// argument for string should never be of length zero
// Argument for string should never be of length zero
Token ExtractToken(
std::string const & string,
std::size_t const lineNumber,

View File

@@ -9,7 +9,8 @@ namespace Token
ImmediateInteger,
Register,
StatementEnd,
Label,
LabelDefinition,
LabelArgument,
Memory
};
}

View File

@@ -1,6 +1,8 @@
#pragma once
#include <cstdint>
#include <optional>
#include <string>
#include <vector>
namespace Utils
{
@@ -15,4 +17,14 @@ namespace Utils
std::string getValueSurroundedByWhitespace(
std::string const & src,
std::size_t const pos);
namespace Bytes
{
void Write(
int const value,
std::vector<std::uint8_t> & vec,
std::size_t const pos);
int Read(std::vector<std::uint8_t> const & vec, std::size_t const pos);
}
}

View File

@@ -1,29 +1,44 @@
#pragma once
#include <configuration.hpp>
#include <cstdint>
#include <execute/virtualmachine.hpp>
#include <interpret/interpreter.hpp>
#include <token/tokenizer.hpp>
class Wassembler
{
private:
Configuration config;
Execute::VirtualMachine vm;
bool printSubstitutions;
bool printTokens;
bool printTranslatedBytes;
bool LoadLinesFromFile(std::string const & filePath, std::vector<std::string> & lines) const;
bool LoadTokens(std::vector<std::string> const & lines, std::vector<Token::Token> & tokens) const;
bool LoadTextFile(
std::string const & filePath,
std::vector<std::string> & lines) const;
bool Preprocess(std::vector<std::string> & lines) const;
bool Tokenize(
std::vector<std::string> const & lines,
std::vector<Token::Token> & tokens) const;
bool CompileToBytes(
std::vector<Token::Token> const & tokens,
std::vector<std::string> const & lines,
std::vector<std::uint8_t> & bytes) const;
void ExecuteCode(std::vector<std::uint8_t> const & bytes);
bool CompileFile(
std::string const & filePath,
std::vector<std::uint8_t> & bytes) const;
public:
void SetMemorySize(unsigned const size);
void EnableSubstitutionsLogging();
void EnableTokensLogging();
void EnableByteTranslationLogging();
bool LoadFromFile(std::string const & filePath);
void Run();
bool CompileAndRun(std::string const & filePath);
bool CompileToFile(
std::string const & inputFilePath,
std::string const & outputFilePath);
Wassembler() = default;
};