Compile to bytecode
This commit is contained in:
23
README.md
23
README.md
@@ -22,14 +22,14 @@ afterwards can be a bit cryptic as to where it originated.
|
|||||||
## Notation
|
## Notation
|
||||||
|
|
||||||
- `[operation][number type]`, e.g. `divi` for divide (div) integer
|
- `[operation][number type]`, e.g. `divi` for divide (div) integer
|
||||||
- `%[register]` for addressing registers
|
- `%[register]` for addressing registers, e.g. `%A`
|
||||||
- `$[value]` for using immediate (literal) integer values
|
- `$[value]` for using immediate (literal) integer values, e.g. `$38`
|
||||||
- `'a'` for using immediate character values
|
- `'[character]'` for using immediate character values, e.g. `'r'`
|
||||||
- `;` for end of statement (mandatory)
|
- `;` for end of statement (mandatory)
|
||||||
- `[label]:` for labels
|
- `[label]:` for labels, e.g. `loop:`
|
||||||
- `#[text]` for comments: any text is ignored till a newline (`\n`) is found
|
- `#[text]` for comments: any text is ignored till a newline (`\n`) is found
|
||||||
- `[[%register|$value]]` for accessing memory
|
- `[[%register|$value]]` for accessing memory, e.g. `[$104]`
|
||||||
- Elements must be separated by whitespace character
|
- Elements must be separated by whitespace character(s)
|
||||||
- Good: `add $2 $5 %A;`
|
- Good: `add $2 $5 %A;`
|
||||||
- Bad: `add $2$5%A;`
|
- Bad: `add $2$5%A;`
|
||||||
|
|
||||||
@@ -103,7 +103,8 @@ directive is not supported therefore.
|
|||||||
|
|
||||||
### Registers
|
### Registers
|
||||||
|
|
||||||
All registers are 32 bits wide. The following 4 registers currently exist:
|
All registers are 32 bits wide. The following 4 general purpose registers
|
||||||
|
currently exist:
|
||||||
|
|
||||||
- A
|
- A
|
||||||
- B
|
- B
|
||||||
@@ -159,14 +160,14 @@ the first argument
|
|||||||
|
|
||||||
## Interupts
|
## Interupts
|
||||||
|
|
||||||
- [0..3] Output to STDOUT
|
- [0..9] Output to STDOUT
|
||||||
- `0` put value of register A as ASCII character on stdout
|
- `0` put value of register A as ASCII character on stdout
|
||||||
- `1` put value of register A as decimal integer on stdout
|
- `1` put value of register A as decimal integer on stdout
|
||||||
- `2` put value of register A as hexadecimal integer on stdout
|
- `2` put value of register A as hexadecimal integer on stdout
|
||||||
- `3` put the string pointed at by register A for the amount of characters
|
- `3` put the string pointed at by register A for the amount of characters
|
||||||
defined by register B on stdout
|
defined by register B on stdout
|
||||||
- [4..5] Input from STDIN
|
- [10..19] Input from STDIN
|
||||||
- `4` get a single ASCII character from STDIN and store it in register A
|
- `10` get a single ASCII character from STDIN and store it in register A
|
||||||
- `5` get a string of a maximum length determined by register B and store it
|
- `11` get a string of a maximum length determined by register B and store it
|
||||||
in the address specified by register A. After execution register B will
|
in the address specified by register A. After execution register B will
|
||||||
contain the number of characters actually read.
|
contain the number of characters actually read.
|
||||||
@@ -93,7 +93,7 @@ int $3;
|
|||||||
call noop_function;
|
call noop_function;
|
||||||
|
|
||||||
# Echo a character
|
# Echo a character
|
||||||
int $4;
|
int $10;
|
||||||
int $0;
|
int $0;
|
||||||
|
|
||||||
seti %A $10; # newline
|
seti %A $10; # newline
|
||||||
@@ -102,7 +102,7 @@ int $0;
|
|||||||
# Echo a string
|
# Echo a string
|
||||||
seti %A $0;
|
seti %A $0;
|
||||||
seti %B $10;
|
seti %B $10;
|
||||||
int $5;
|
int $11;
|
||||||
int $3;
|
int $3;
|
||||||
|
|
||||||
exit;
|
exit;
|
||||||
|
|||||||
25
include/compile/compiler.hpp
Normal file
25
include/compile/compiler.hpp
Normal 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);
|
||||||
|
};
|
||||||
|
}
|
||||||
28
include/compile/errors.hpp
Normal file
28
include/compile/errors.hpp
Normal 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);
|
||||||
|
};
|
||||||
|
}
|
||||||
@@ -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();
|
|
||||||
};
|
|
||||||
37
include/execute/argumentvalue.hpp
Normal file
37
include/execute/argumentvalue.hpp
Normal 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);
|
||||||
|
};
|
||||||
|
}
|
||||||
43
include/execute/bytecode.hpp
Normal file
43
include/execute/bytecode.hpp
Normal 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
|
||||||
|
};
|
||||||
|
}
|
||||||
@@ -7,50 +7,44 @@ namespace Execute
|
|||||||
{
|
{
|
||||||
protected:
|
protected:
|
||||||
std::string message;
|
std::string message;
|
||||||
|
std::size_t byteLocation;
|
||||||
|
|
||||||
public:
|
public:
|
||||||
std::string const & GetMessage() const;
|
std::string const & GetMessage() const;
|
||||||
|
|
||||||
RuntimeError();
|
RuntimeError();
|
||||||
RuntimeError(std::string const & what);
|
RuntimeError(std::string const & what, std::size_t const byteLocation);
|
||||||
};
|
|
||||||
|
|
||||||
class StackUnderflow : public RuntimeError
|
|
||||||
{
|
|
||||||
public:
|
|
||||||
StackUnderflow();
|
|
||||||
};
|
|
||||||
|
|
||||||
class StackOverflow : public RuntimeError
|
|
||||||
{
|
|
||||||
public:
|
|
||||||
StackOverflow();
|
|
||||||
};
|
|
||||||
|
|
||||||
class MissingLabel : public RuntimeError
|
|
||||||
{
|
|
||||||
public:
|
|
||||||
MissingLabel(std::string const & label);
|
|
||||||
};
|
};
|
||||||
|
|
||||||
class InterruptIndexOutOfRange : public RuntimeError
|
class InterruptIndexOutOfRange : public RuntimeError
|
||||||
{
|
{
|
||||||
public:
|
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
|
class OutOfMemory : public RuntimeError
|
||||||
{
|
{
|
||||||
public:
|
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();
|
|
||||||
};
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
@@ -1,9 +0,0 @@
|
|||||||
#pragma once
|
|
||||||
|
|
||||||
namespace Execute
|
|
||||||
{
|
|
||||||
struct Flags
|
|
||||||
{
|
|
||||||
|
|
||||||
};
|
|
||||||
}
|
|
||||||
@@ -3,9 +3,10 @@
|
|||||||
#include <execute/registers.hpp>
|
#include <execute/registers.hpp>
|
||||||
#include <vector>
|
#include <vector>
|
||||||
|
|
||||||
using InterruptFn = void (*)(Execute::Registers & registers, std::vector<std::uint8_t> & memory);
|
|
||||||
|
|
||||||
namespace Execute
|
namespace Execute
|
||||||
{
|
{
|
||||||
std::vector<InterruptFn> GetInterrupts();
|
void ExecuteInterrupt(
|
||||||
|
int const id,
|
||||||
|
Execute::Registers & registers,
|
||||||
|
std::vector<std::uint8_t> & memory);
|
||||||
}
|
}
|
||||||
@@ -1,19 +1,14 @@
|
|||||||
#pragma once
|
#pragma once
|
||||||
|
#include <cstddef>
|
||||||
|
|
||||||
namespace Execute
|
namespace Execute
|
||||||
{
|
{
|
||||||
struct Registers
|
struct Registers
|
||||||
{
|
{
|
||||||
union
|
int A, B, C, D;
|
||||||
{
|
std::size_t programCounter;
|
||||||
struct
|
std::size_t stackPointer;
|
||||||
{
|
|
||||||
int A;
|
// TODO status registers?
|
||||||
int B;
|
|
||||||
int C;
|
|
||||||
int D;
|
|
||||||
};
|
|
||||||
int registers[4];
|
|
||||||
};
|
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,18 +1,17 @@
|
|||||||
#pragma once
|
#pragma once
|
||||||
#include <execute/interrupts.hpp>
|
#include <execute/registers.hpp>
|
||||||
#include <string>
|
#include <string>
|
||||||
#include <unordered_map>
|
#include <vector>
|
||||||
|
|
||||||
namespace Execute
|
namespace Execute
|
||||||
{
|
{
|
||||||
struct State
|
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;
|
bool terminated;
|
||||||
|
Registers registers;
|
||||||
|
std::vector<std::uint8_t> memory;
|
||||||
|
|
||||||
|
void PushToStack(int const value);
|
||||||
|
int PopFromStack();
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
@@ -1,8 +1,7 @@
|
|||||||
#include <configuration.hpp>
|
#include <array>
|
||||||
#include <execute/flags.hpp>
|
#include <execute/argumentvalue.hpp>
|
||||||
#include <execute/registers.hpp>
|
#include <execute/bytecode.hpp>
|
||||||
#include <execute/state.hpp>
|
#include <execute/state.hpp>
|
||||||
#include <interpret/code.hpp>
|
|
||||||
#include <memory>
|
#include <memory>
|
||||||
|
|
||||||
namespace Execute
|
namespace Execute
|
||||||
@@ -10,27 +9,39 @@ namespace Execute
|
|||||||
class VirtualMachine
|
class VirtualMachine
|
||||||
{
|
{
|
||||||
private:
|
private:
|
||||||
Flags flags;
|
|
||||||
Registers registers;
|
|
||||||
State state;
|
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();
|
void Step();
|
||||||
|
|
||||||
public:
|
public:
|
||||||
void Run();
|
void Run();
|
||||||
void SingleStep();
|
void SingleStep();
|
||||||
void LoadConfiguration(Configuration const & c);
|
|
||||||
void LoadCode(std::unique_ptr<Interpret::Code> code);
|
|
||||||
|
|
||||||
Flags const & GetFlags() const;
|
void SetMemorySize(std::size_t const size);
|
||||||
Registers const & GetRegisters() const;
|
void LoadCode(
|
||||||
|
std::vector<std::uint8_t> const & byteCode,
|
||||||
|
bool const printDecodedBytes);
|
||||||
|
|
||||||
State const & GetState() const;
|
State const & GetState() const;
|
||||||
Interpret::Statement const * const GetCurrentStatement() const;
|
Execute::InstructionByte GetCurrentInstruction() const;
|
||||||
|
|
||||||
bool IsTerminated() const;
|
bool IsTerminated() const;
|
||||||
|
|
||||||
VirtualMachine();
|
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
@@ -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;
|
|
||||||
};
|
|
||||||
}
|
|
||||||
@@ -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);
|
|
||||||
};
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -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);
|
|
||||||
};
|
|
||||||
}
|
|
||||||
@@ -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);
|
|
||||||
}
|
|
||||||
@@ -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;
|
|
||||||
};
|
|
||||||
}
|
|
||||||
@@ -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);
|
|
||||||
};
|
|
||||||
}
|
|
||||||
@@ -10,9 +10,4 @@ namespace Token
|
|||||||
std::string errorMsg;
|
std::string errorMsg;
|
||||||
TokenizationError(Token const & token, std::string const & msg);
|
TokenizationError(Token const & token, std::string const & msg);
|
||||||
};
|
};
|
||||||
|
|
||||||
struct MissingEndOfString : public TokenizationError
|
|
||||||
{
|
|
||||||
MissingEndOfString(Token const & token);
|
|
||||||
};
|
|
||||||
}
|
}
|
||||||
@@ -39,13 +39,15 @@ namespace Token
|
|||||||
static Token CreateEmptyToken(int const lineNumber, int const lineColumn);
|
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 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 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 CreateImmediateValueToken(int const value, int const lineNumber, int const lineColumn);
|
||||||
static Token CreateRegisterToken(RegisterType const registerType, 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 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(RegisterType const registerType, int const lineNumber, int const lineColumn);
|
||||||
static Token CreateMemoryToken(int const value, 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;
|
void Print() const;
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -8,7 +8,7 @@ namespace Token
|
|||||||
class Tokenizer
|
class Tokenizer
|
||||||
{
|
{
|
||||||
private:
|
private:
|
||||||
// argument for string should never be of length zero
|
// Argument for string should never be of length zero
|
||||||
Token ExtractToken(
|
Token ExtractToken(
|
||||||
std::string const & string,
|
std::string const & string,
|
||||||
std::size_t const lineNumber,
|
std::size_t const lineNumber,
|
||||||
|
|||||||
@@ -9,7 +9,8 @@ namespace Token
|
|||||||
ImmediateInteger,
|
ImmediateInteger,
|
||||||
Register,
|
Register,
|
||||||
StatementEnd,
|
StatementEnd,
|
||||||
Label,
|
LabelDefinition,
|
||||||
|
LabelArgument,
|
||||||
Memory
|
Memory
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
@@ -1,6 +1,8 @@
|
|||||||
#pragma once
|
#pragma once
|
||||||
|
#include <cstdint>
|
||||||
#include <optional>
|
#include <optional>
|
||||||
#include <string>
|
#include <string>
|
||||||
|
#include <vector>
|
||||||
|
|
||||||
namespace Utils
|
namespace Utils
|
||||||
{
|
{
|
||||||
@@ -15,4 +17,14 @@ namespace Utils
|
|||||||
std::string getValueSurroundedByWhitespace(
|
std::string getValueSurroundedByWhitespace(
|
||||||
std::string const & src,
|
std::string const & src,
|
||||||
std::size_t const pos);
|
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);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
@@ -1,29 +1,44 @@
|
|||||||
#pragma once
|
#pragma once
|
||||||
#include <configuration.hpp>
|
#include <cstdint>
|
||||||
#include <execute/virtualmachine.hpp>
|
#include <execute/virtualmachine.hpp>
|
||||||
#include <interpret/interpreter.hpp>
|
|
||||||
#include <token/tokenizer.hpp>
|
#include <token/tokenizer.hpp>
|
||||||
|
|
||||||
class Wassembler
|
class Wassembler
|
||||||
{
|
{
|
||||||
private:
|
private:
|
||||||
Configuration config;
|
|
||||||
Execute::VirtualMachine vm;
|
Execute::VirtualMachine vm;
|
||||||
bool printSubstitutions;
|
bool printSubstitutions;
|
||||||
bool printTokens;
|
bool printTokens;
|
||||||
|
bool printTranslatedBytes;
|
||||||
|
|
||||||
bool LoadLinesFromFile(std::string const & filePath, std::vector<std::string> & lines) const;
|
bool LoadTextFile(
|
||||||
bool LoadTokens(std::vector<std::string> const & lines, std::vector<Token::Token> & tokens) const;
|
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:
|
public:
|
||||||
void SetMemorySize(unsigned const size);
|
void SetMemorySize(unsigned const size);
|
||||||
|
|
||||||
void EnableSubstitutionsLogging();
|
void EnableSubstitutionsLogging();
|
||||||
void EnableTokensLogging();
|
void EnableTokensLogging();
|
||||||
|
void EnableByteTranslationLogging();
|
||||||
|
|
||||||
bool LoadFromFile(std::string const & filePath);
|
bool CompileAndRun(std::string const & filePath);
|
||||||
|
bool CompileToFile(
|
||||||
void Run();
|
std::string const & inputFilePath,
|
||||||
|
std::string const & outputFilePath);
|
||||||
|
|
||||||
Wassembler() = default;
|
Wassembler() = default;
|
||||||
};
|
};
|
||||||
2
makefile
2
makefile
@@ -13,7 +13,7 @@ BINARY = bin/wassembler
|
|||||||
all: ${BINARY}
|
all: ${BINARY}
|
||||||
|
|
||||||
check: ${BINARY}
|
check: ${BINARY}
|
||||||
./$< ./bin/test.wasm
|
./$< ./bin/test.wasm -e
|
||||||
|
|
||||||
clean:
|
clean:
|
||||||
-rm -rf build ./${BINARY}
|
-rm -rf build ./${BINARY}
|
||||||
|
|||||||
433
src/compile/compiler.cpp
Normal file
433
src/compile/compiler.cpp
Normal file
@@ -0,0 +1,433 @@
|
|||||||
|
#include <compile/compiler.hpp>
|
||||||
|
#include <compile/errors.hpp>
|
||||||
|
#include <execute/bytecode.hpp>
|
||||||
|
#include <stdexcept>
|
||||||
|
#include <utils.hpp>
|
||||||
|
|
||||||
|
namespace Compile
|
||||||
|
{
|
||||||
|
int GetRequiredNumberOfArguments(Token::OperandType const type)
|
||||||
|
{
|
||||||
|
switch (type)
|
||||||
|
{
|
||||||
|
case Token::OperandType::AddInteger:
|
||||||
|
case Token::OperandType::SubtractInteger:
|
||||||
|
case Token::OperandType::DivideInteger:
|
||||||
|
case Token::OperandType::MultiplyInteger:
|
||||||
|
case Token::OperandType::ShiftIntegerLeft:
|
||||||
|
case Token::OperandType::ShiftIntegerRight:
|
||||||
|
return 3;
|
||||||
|
|
||||||
|
case Token::OperandType::LessThanInteger:
|
||||||
|
case Token::OperandType::GreaterThanInteger:
|
||||||
|
case Token::OperandType::EqualInteger:
|
||||||
|
case Token::OperandType::SetInteger:
|
||||||
|
return 2;
|
||||||
|
|
||||||
|
case Token::OperandType::Jump:
|
||||||
|
case Token::OperandType::CallFunction:
|
||||||
|
case Token::OperandType::Interrupt:
|
||||||
|
case Token::OperandType::PushInteger:
|
||||||
|
case Token::OperandType::PopInteger:
|
||||||
|
return 1;
|
||||||
|
|
||||||
|
default:
|
||||||
|
std::printf("WARNING: returning default argument length of 0 for operand type %i\n", static_cast<int>(type));
|
||||||
|
case Token::OperandType::ReturnFromFunction:
|
||||||
|
case Token::OperandType::ExitProgram:
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
bool IsArgumentToken(Token::Token const & t)
|
||||||
|
{
|
||||||
|
return
|
||||||
|
t.type == Token::TokenType::ImmediateInteger ||
|
||||||
|
t.type == Token::TokenType::Register ||
|
||||||
|
t.type == Token::TokenType::LabelArgument ||
|
||||||
|
t.type == Token::TokenType::Memory;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool IsReadableToken(Token::Token const & t)
|
||||||
|
{
|
||||||
|
return
|
||||||
|
t.type == Token::TokenType::ImmediateInteger ||
|
||||||
|
t.type == Token::TokenType::Register ||
|
||||||
|
t.type == Token::TokenType::Memory;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool IsWriteableToken(Token::Token const & t)
|
||||||
|
{
|
||||||
|
return
|
||||||
|
t.type == Token::TokenType::Register ||
|
||||||
|
t.type == Token::TokenType::Memory;
|
||||||
|
}
|
||||||
|
|
||||||
|
void ValidateArguments(
|
||||||
|
std::vector<Token::Token> const & tokens,
|
||||||
|
std::size_t const operandIndex)
|
||||||
|
{
|
||||||
|
auto const operandType = std::get<Token::OperandType>(tokens[operandIndex].data);
|
||||||
|
switch(operandType)
|
||||||
|
{
|
||||||
|
// 2 Read values + 1 write value
|
||||||
|
case Token::OperandType::AddInteger:
|
||||||
|
case Token::OperandType::SubtractInteger:
|
||||||
|
case Token::OperandType::DivideInteger:
|
||||||
|
case Token::OperandType::MultiplyInteger:
|
||||||
|
case Token::OperandType::ShiftIntegerLeft:
|
||||||
|
case Token::OperandType::ShiftIntegerRight:
|
||||||
|
if (!IsReadableToken(tokens[operandIndex + 1]))
|
||||||
|
{
|
||||||
|
throw CompilationError::CreateExpectedImmediateOrRegisterOrMemory(tokens[operandIndex + 1]);
|
||||||
|
}
|
||||||
|
if (!IsReadableToken(tokens[operandIndex + 2]))
|
||||||
|
{
|
||||||
|
throw CompilationError::CreateExpectedImmediateOrRegisterOrMemory(tokens[operandIndex + 2]);
|
||||||
|
}
|
||||||
|
if (!IsWriteableToken(tokens[operandIndex + 3]))
|
||||||
|
{
|
||||||
|
throw CompilationError::CreateExpectedRegisterOrMemoryError(tokens[operandIndex + 3]);
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
|
||||||
|
// 2 Read values
|
||||||
|
case Token::OperandType::LessThanInteger:
|
||||||
|
case Token::OperandType::GreaterThanInteger:
|
||||||
|
case Token::OperandType::EqualInteger:
|
||||||
|
case Token::OperandType::SetInteger:
|
||||||
|
if (!IsReadableToken(tokens[operandIndex + 1]))
|
||||||
|
{
|
||||||
|
throw CompilationError::CreateExpectedImmediateOrRegisterOrMemory(tokens[operandIndex + 1]);
|
||||||
|
}
|
||||||
|
if (!IsReadableToken(tokens[operandIndex + 2]))
|
||||||
|
{
|
||||||
|
throw CompilationError::CreateExpectedImmediateOrRegisterOrMemory(tokens[operandIndex + 2]);
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
|
||||||
|
// 1 Label value
|
||||||
|
case Token::OperandType::Jump:
|
||||||
|
case Token::OperandType::CallFunction:
|
||||||
|
if (tokens[operandIndex + 1].type != Token::TokenType::LabelArgument)
|
||||||
|
{
|
||||||
|
throw CompilationError::CreateExpectedLabelError(tokens[operandIndex + 1]);
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
|
||||||
|
// 1 Read value
|
||||||
|
case Token::OperandType::Interrupt:
|
||||||
|
case Token::OperandType::PushInteger:
|
||||||
|
if (!IsReadableToken(tokens[operandIndex + 1]))
|
||||||
|
{
|
||||||
|
throw CompilationError::CreateExpectedImmediateOrRegisterOrMemory(tokens[operandIndex + 1]);
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
|
||||||
|
// 1 Write value
|
||||||
|
case Token::OperandType::PopInteger:
|
||||||
|
if (!IsWriteableToken(tokens[operandIndex + 1]))
|
||||||
|
{
|
||||||
|
throw CompilationError::CreateExpectedRegisterOrMemoryError(tokens[operandIndex + 1]);
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
|
||||||
|
default:
|
||||||
|
throw std::runtime_error("Unimplemented operandType case in ValidateArguments");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
Execute::RegisterByte GetByteCodeRegister(Token::RegisterType const v)
|
||||||
|
{
|
||||||
|
switch(v)
|
||||||
|
{
|
||||||
|
case Token::RegisterType::A:
|
||||||
|
return Execute::RegisterByte::A;
|
||||||
|
|
||||||
|
case Token::RegisterType::B:
|
||||||
|
return Execute::RegisterByte::B;
|
||||||
|
|
||||||
|
case Token::RegisterType::C:
|
||||||
|
return Execute::RegisterByte::C;
|
||||||
|
|
||||||
|
case Token::RegisterType::D:
|
||||||
|
return Execute::RegisterByte::D;
|
||||||
|
|
||||||
|
default:
|
||||||
|
throw std::runtime_error("Unhandled register type in GetByteCodeRegister");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void Compiler::InsertAsBytes(
|
||||||
|
Token::Token const & token,
|
||||||
|
std::vector<std::uint8_t> & bytes)
|
||||||
|
{
|
||||||
|
switch(token.type)
|
||||||
|
{
|
||||||
|
case Token::TokenType::ImmediateInteger:
|
||||||
|
bytes.push_back(static_cast<std::uint8_t>(Execute::InstructionByte::IMMEDIATE_INTEGER));
|
||||||
|
{
|
||||||
|
int value = std::get<int>(token.data);
|
||||||
|
auto const insertionIndex = bytes.size();
|
||||||
|
bytes.resize(bytes.size() + 4);
|
||||||
|
Utils::Bytes::Write(value, bytes, insertionIndex);
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
|
||||||
|
case Token::TokenType::Operand:
|
||||||
|
{
|
||||||
|
switch(std::get<Token::OperandType>(token.data))
|
||||||
|
{
|
||||||
|
case Token::OperandType::AddInteger:
|
||||||
|
bytes.push_back(static_cast<std::uint8_t>(Execute::InstructionByte::ADD_INTEGER));
|
||||||
|
break;
|
||||||
|
|
||||||
|
case Token::OperandType::SubtractInteger:
|
||||||
|
bytes.push_back(static_cast<std::uint8_t>(Execute::InstructionByte::SUBTRACT_INTEGER));
|
||||||
|
break;
|
||||||
|
|
||||||
|
case Token::OperandType::DivideInteger:
|
||||||
|
bytes.push_back(static_cast<std::uint8_t>(Execute::InstructionByte::DIVIDE_INTEGER));
|
||||||
|
break;
|
||||||
|
|
||||||
|
case Token::OperandType::MultiplyInteger:
|
||||||
|
bytes.push_back(static_cast<std::uint8_t>(Execute::InstructionByte::MULTIPLY_INTEGER));
|
||||||
|
break;
|
||||||
|
|
||||||
|
case Token::OperandType::ShiftIntegerLeft:
|
||||||
|
bytes.push_back(static_cast<std::uint8_t>(Execute::InstructionByte::SHIFT_LEFT_INTEGER));
|
||||||
|
break;
|
||||||
|
|
||||||
|
case Token::OperandType::ShiftIntegerRight:
|
||||||
|
bytes.push_back(static_cast<std::uint8_t>(Execute::InstructionByte::SHIFT_RIGHT_INTEGER));
|
||||||
|
break;
|
||||||
|
|
||||||
|
case Token::OperandType::LessThanInteger:
|
||||||
|
bytes.push_back(static_cast<std::uint8_t>(Execute::InstructionByte::LESS_THAN_INTEGER));
|
||||||
|
break;
|
||||||
|
|
||||||
|
case Token::OperandType::GreaterThanInteger:
|
||||||
|
bytes.push_back(static_cast<std::uint8_t>(Execute::InstructionByte::GREATER_THAN_INTEGER));
|
||||||
|
break;
|
||||||
|
|
||||||
|
case Token::OperandType::EqualInteger:
|
||||||
|
bytes.push_back(static_cast<std::uint8_t>(Execute::InstructionByte::EQUALS_INTEGER));
|
||||||
|
break;
|
||||||
|
|
||||||
|
case Token::OperandType::SetInteger:
|
||||||
|
bytes.push_back(static_cast<std::uint8_t>(Execute::InstructionByte::SET_INTEGER));
|
||||||
|
break;
|
||||||
|
|
||||||
|
case Token::OperandType::Jump:
|
||||||
|
bytes.push_back(static_cast<std::uint8_t>(Execute::InstructionByte::JUMP));
|
||||||
|
break;
|
||||||
|
|
||||||
|
case Token::OperandType::CallFunction:
|
||||||
|
bytes.push_back(static_cast<std::uint8_t>(Execute::InstructionByte::CALL));
|
||||||
|
break;
|
||||||
|
|
||||||
|
case Token::OperandType::Interrupt:
|
||||||
|
bytes.push_back(static_cast<std::uint8_t>(Execute::InstructionByte::INTERRUPT));
|
||||||
|
break;
|
||||||
|
|
||||||
|
case Token::OperandType::PushInteger:
|
||||||
|
bytes.push_back(static_cast<std::uint8_t>(Execute::InstructionByte::PUSH_INTEGER));
|
||||||
|
break;
|
||||||
|
|
||||||
|
case Token::OperandType::PopInteger:
|
||||||
|
bytes.push_back(static_cast<std::uint8_t>(Execute::InstructionByte::POP_INTEGER));
|
||||||
|
break;
|
||||||
|
|
||||||
|
case Token::OperandType::ReturnFromFunction:
|
||||||
|
bytes.push_back(static_cast<std::uint8_t>(Execute::InstructionByte::RETURN));
|
||||||
|
break;
|
||||||
|
|
||||||
|
case Token::OperandType::ExitProgram:
|
||||||
|
bytes.push_back(static_cast<std::uint8_t>(Execute::InstructionByte::EXIT));
|
||||||
|
break;
|
||||||
|
|
||||||
|
break;
|
||||||
|
|
||||||
|
default:
|
||||||
|
throw std::runtime_error("Unhandled operand type in InsertAsBytes");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
|
||||||
|
case Token::TokenType::Register:
|
||||||
|
bytes.push_back(static_cast<std::uint8_t>(Execute::InstructionByte::REGISTER));
|
||||||
|
bytes.push_back(static_cast<std::uint8_t>(GetByteCodeRegister(std::get<Token::RegisterType>(token.data))));
|
||||||
|
break;
|
||||||
|
|
||||||
|
case Token::TokenType::StatementEnd:
|
||||||
|
case Token::TokenType::LabelDefinition:
|
||||||
|
// NO OP
|
||||||
|
break;
|
||||||
|
|
||||||
|
case Token::TokenType::LabelArgument:
|
||||||
|
{
|
||||||
|
bytes.push_back(static_cast<std::uint8_t>(Execute::InstructionByte::LABEL));
|
||||||
|
auto const & label = std::get<std::string>(token.data);
|
||||||
|
auto const findResult = jumpLabelLocations.find(label);
|
||||||
|
int jumpLocation = 0;
|
||||||
|
if (findResult == jumpLabelLocations.end())
|
||||||
|
{
|
||||||
|
unresolvedJumpLabels.push_back(std::make_pair(token, bytes.size()));
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
jumpLocation = findResult->second;
|
||||||
|
}
|
||||||
|
|
||||||
|
auto const insertionIndex = bytes.size();
|
||||||
|
bytes.resize(bytes.size() + 4);
|
||||||
|
Utils::Bytes::Write(jumpLocation, bytes, insertionIndex);
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
|
||||||
|
case Token::TokenType::Memory:
|
||||||
|
{
|
||||||
|
bytes.push_back(static_cast<std::uint8_t>(Execute::InstructionByte::MEMORY_OP));
|
||||||
|
switch(token.valueType)
|
||||||
|
{
|
||||||
|
case Token::TokenValueType::Register:
|
||||||
|
bytes.push_back(static_cast<std::uint8_t>(Execute::InstructionByte::REGISTER));
|
||||||
|
bytes.push_back(static_cast<std::uint8_t>(GetByteCodeRegister(std::get<Token::RegisterType>(token.data))));
|
||||||
|
break;
|
||||||
|
|
||||||
|
case Token::TokenValueType::Integer:
|
||||||
|
bytes.push_back(static_cast<std::uint8_t>(Execute::InstructionByte::IMMEDIATE_INTEGER));
|
||||||
|
{
|
||||||
|
auto const insertionIndex = bytes.size();
|
||||||
|
bytes.resize(bytes.size() + 4);
|
||||||
|
Utils::Bytes::Write(std::get<int>(token.data), bytes, insertionIndex);
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
|
||||||
|
default:
|
||||||
|
throw std::runtime_error("Unhandled value type for memory operand in InsertAsBytes");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
|
||||||
|
default:
|
||||||
|
throw std::runtime_error("Unhandled token type in InsertAsBytes");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
bool Compiler::Compile(
|
||||||
|
std::vector<Token::Token> const & tokens,
|
||||||
|
std::vector<std::uint8_t> & bytes)
|
||||||
|
{
|
||||||
|
jumpLabelLocations.clear();
|
||||||
|
unresolvedJumpLabels.clear();
|
||||||
|
|
||||||
|
enum class State
|
||||||
|
{
|
||||||
|
FindOperand,
|
||||||
|
FindArguments,
|
||||||
|
FindStatementEnd
|
||||||
|
};
|
||||||
|
|
||||||
|
State state = State::FindOperand;
|
||||||
|
Token::OperandType operandType;
|
||||||
|
unsigned operatorTokenIndex = 0u;
|
||||||
|
int expectedNumberOfArguments = 0;
|
||||||
|
for(std::size_t i = 0u; i < tokens.size(); ++i)
|
||||||
|
{
|
||||||
|
auto const & token = tokens[i];
|
||||||
|
InsertAsBytes(token, bytes);
|
||||||
|
|
||||||
|
switch(state)
|
||||||
|
{
|
||||||
|
case State::FindOperand:
|
||||||
|
switch(token.type)
|
||||||
|
{
|
||||||
|
case Token::TokenType::Operand:
|
||||||
|
operatorTokenIndex = i;
|
||||||
|
operandType = std::get<Token::OperandType>(token.data);
|
||||||
|
expectedNumberOfArguments = GetRequiredNumberOfArguments(operandType);
|
||||||
|
|
||||||
|
if (expectedNumberOfArguments < 1)
|
||||||
|
{
|
||||||
|
state = State::FindStatementEnd;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
state = State::FindArguments;
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
|
||||||
|
case Token::TokenType::LabelDefinition:
|
||||||
|
{
|
||||||
|
auto findResult = jumpLabelLocations.find(std::get<std::string>(token.data));
|
||||||
|
if (findResult == jumpLabelLocations.end())
|
||||||
|
{
|
||||||
|
jumpLabelLocations[std::get<std::string>(token.data)] = bytes.size();
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
throw CompilationError::CreateDuplicateLabelError(token);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
|
||||||
|
case Token::TokenType::StatementEnd:
|
||||||
|
// NO OP
|
||||||
|
break;
|
||||||
|
|
||||||
|
default:
|
||||||
|
throw CompilationError::CreateExpectedOperandError(token);
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
|
||||||
|
case State::FindArguments:
|
||||||
|
if (IsArgumentToken(token))
|
||||||
|
{
|
||||||
|
expectedNumberOfArguments -= 1;
|
||||||
|
if (expectedNumberOfArguments < 1)
|
||||||
|
{
|
||||||
|
ValidateArguments(tokens, operatorTokenIndex);
|
||||||
|
state = State::FindStatementEnd;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
// TODO Further specify this error?
|
||||||
|
throw CompilationError::CreateExpectedArgumentError(token);
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
|
||||||
|
case State::FindStatementEnd:
|
||||||
|
if (token.type != Token::TokenType::StatementEnd)
|
||||||
|
{
|
||||||
|
// TODO Further specify this error?
|
||||||
|
throw CompilationError::CreateExpectedEndOfStatementError(token);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
InsertAsBytes(
|
||||||
|
token,
|
||||||
|
bytes);
|
||||||
|
state = State::FindOperand;
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
for(auto const & unresolved : unresolvedJumpLabels)
|
||||||
|
{
|
||||||
|
auto const & findResult = jumpLabelLocations.find(std::get<std::string>(unresolved.first.data));
|
||||||
|
if (findResult == jumpLabelLocations.end())
|
||||||
|
{
|
||||||
|
throw CompilationError::CreateNonExistingLabelError(unresolved.first);
|
||||||
|
}
|
||||||
|
|
||||||
|
int const jumpLocation = findResult->second;
|
||||||
|
auto const index = unresolved.second;
|
||||||
|
Utils::Bytes::Write(jumpLocation, bytes, index);
|
||||||
|
}
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
}
|
||||||
75
src/compile/errors.cpp
Normal file
75
src/compile/errors.cpp
Normal file
@@ -0,0 +1,75 @@
|
|||||||
|
#include <compile/errors.hpp>
|
||||||
|
#include <sstream>
|
||||||
|
|
||||||
|
namespace Compile
|
||||||
|
{
|
||||||
|
CompilationError::CompilationError(
|
||||||
|
std::string const & message,
|
||||||
|
Token::Token const & token)
|
||||||
|
: errorToken(token)
|
||||||
|
{
|
||||||
|
errorToken.errorMessage = message;
|
||||||
|
}
|
||||||
|
|
||||||
|
CompilationError CompilationError::CreateExpectedArgumentError(Token::Token const & token)
|
||||||
|
{
|
||||||
|
return CompilationError("Expected an argument", token);
|
||||||
|
}
|
||||||
|
|
||||||
|
CompilationError CompilationError::CreateExpectedLabelError(Token::Token const & token)
|
||||||
|
{
|
||||||
|
return CompilationError("Expected a label", token);
|
||||||
|
}
|
||||||
|
|
||||||
|
CompilationError CompilationError::CreateExpectedImmediateError(Token::Token const & token)
|
||||||
|
{
|
||||||
|
return CompilationError("Expected an immediate value", token);
|
||||||
|
}
|
||||||
|
|
||||||
|
CompilationError CompilationError::CreateExpectedImmediateOrRegisterOrMemory(Token::Token const & token)
|
||||||
|
{
|
||||||
|
return CompilationError("Expected an immediate value, a register or a memory location", token);
|
||||||
|
}
|
||||||
|
|
||||||
|
CompilationError CompilationError::CreateExpectedRegisterError(Token::Token const & token)
|
||||||
|
{
|
||||||
|
return CompilationError("Expected a register", token);
|
||||||
|
}
|
||||||
|
|
||||||
|
CompilationError CompilationError::CreateExpectedRegisterOrMemoryError(Token::Token const & token)
|
||||||
|
{
|
||||||
|
return CompilationError("Expected a register or a memory location", token);
|
||||||
|
}
|
||||||
|
|
||||||
|
CompilationError CompilationError::CreateExpectedOperandError(Token::Token const & token)
|
||||||
|
{
|
||||||
|
return CompilationError("Expected an operand", token);
|
||||||
|
}
|
||||||
|
|
||||||
|
CompilationError CompilationError::CreateTooManyArgumentsError(Token::Token const & token)
|
||||||
|
{
|
||||||
|
return CompilationError("Too many arguments for operand", token);
|
||||||
|
}
|
||||||
|
|
||||||
|
CompilationError CompilationError::CreateTooFewArgumentsError(Token::Token const & token)
|
||||||
|
{
|
||||||
|
return CompilationError("Too few arguments for operand", token);
|
||||||
|
}
|
||||||
|
|
||||||
|
CompilationError CompilationError::CreateExpectedEndOfStatementError(Token::Token const & token)
|
||||||
|
{
|
||||||
|
std::stringstream ss;
|
||||||
|
ss << "Expected end of statement (;), but got " << token.GetName() << " instead";
|
||||||
|
return CompilationError(ss.str(), token);
|
||||||
|
}
|
||||||
|
|
||||||
|
CompilationError CompilationError::CreateDuplicateLabelError(Token::Token const & token)
|
||||||
|
{
|
||||||
|
return CompilationError("Duplicate label definition", token);
|
||||||
|
}
|
||||||
|
|
||||||
|
CompilationError CompilationError::CreateNonExistingLabelError(Token::Token const & token)
|
||||||
|
{
|
||||||
|
return CompilationError("Jumping to non existing label", token);
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -1,25 +0,0 @@
|
|||||||
#include <configuration.hpp>
|
|
||||||
|
|
||||||
void Configuration::PrepareMemory(std::vector<std::uint8_t> & memory) const
|
|
||||||
{
|
|
||||||
memory.resize(memorySize);
|
|
||||||
|
|
||||||
for(auto const & pair : strings)
|
|
||||||
{
|
|
||||||
for(std::size_t i = 0; i < pair.second.size(); ++i)
|
|
||||||
{
|
|
||||||
auto const index = i + pair.first;
|
|
||||||
if (index >= memory.size())
|
|
||||||
{
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
|
|
||||||
memory[index] = pair.second[i];
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
Configuration::Configuration()
|
|
||||||
: memorySize(1024)
|
|
||||||
{
|
|
||||||
}
|
|
||||||
144
src/execute/argumentvalue.cpp
Normal file
144
src/execute/argumentvalue.cpp
Normal file
@@ -0,0 +1,144 @@
|
|||||||
|
#include <execute/argumentvalue.hpp>
|
||||||
|
#include <utils.hpp>
|
||||||
|
|
||||||
|
namespace Execute
|
||||||
|
{
|
||||||
|
int & ArgumentValue::GetRegister(State & state) const
|
||||||
|
{
|
||||||
|
switch(std::get<RegisterByte>(data))
|
||||||
|
{
|
||||||
|
case RegisterByte::A:
|
||||||
|
return state.registers.A;
|
||||||
|
|
||||||
|
case RegisterByte::B:
|
||||||
|
return state.registers.B;
|
||||||
|
|
||||||
|
case RegisterByte::C:
|
||||||
|
return state.registers.C;
|
||||||
|
|
||||||
|
case RegisterByte::D:
|
||||||
|
return state.registers.D;
|
||||||
|
|
||||||
|
default:
|
||||||
|
throw std::runtime_error("Unhandled register byte in GetRegister");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
std::uint8_t * ArgumentValue::GetMemory(State & state) const
|
||||||
|
{
|
||||||
|
switch (memoryValueType)
|
||||||
|
{
|
||||||
|
case ArgumentType::Immediate:
|
||||||
|
return &(state.memory[std::get<int>(data)]);
|
||||||
|
|
||||||
|
case ArgumentType::Register:
|
||||||
|
return &(state.memory[GetRegister(state)]);
|
||||||
|
|
||||||
|
default:
|
||||||
|
throw std::runtime_error("Unhandled argument type in GetMemory");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void ArgumentValue::Write(int const value, State & state) const
|
||||||
|
{
|
||||||
|
if (type == ArgumentType::Immediate)
|
||||||
|
{
|
||||||
|
throw AttemptedWriteToImmediate(state.registers.programCounter);
|
||||||
|
}
|
||||||
|
|
||||||
|
switch(type)
|
||||||
|
{
|
||||||
|
case ArgumentType::Memory:
|
||||||
|
{
|
||||||
|
auto * ptr = GetMemory(state);
|
||||||
|
*ptr = value & 0xFF;
|
||||||
|
*(++ptr) = (value >> 8) & 0xFF;
|
||||||
|
*(++ptr) = (value >> 16) & 0xFF;
|
||||||
|
*(++ptr) = (value >> 24) & 0xFF;
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
|
||||||
|
case ArgumentType::Register:
|
||||||
|
GetRegister(state) = value;
|
||||||
|
break;
|
||||||
|
|
||||||
|
default:
|
||||||
|
throw std::runtime_error("Unhandled argument type in Write");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
int ArgumentValue::Read(State & state) const
|
||||||
|
{
|
||||||
|
switch(type)
|
||||||
|
{
|
||||||
|
case ArgumentType::Immediate:
|
||||||
|
return std::get<int>(data);
|
||||||
|
|
||||||
|
case ArgumentType::Memory:
|
||||||
|
{
|
||||||
|
int result = 0;
|
||||||
|
auto * ptr = GetMemory(state);
|
||||||
|
result |= static_cast<int>(*ptr);
|
||||||
|
result |= static_cast<int>(*(++ptr)) << 8;
|
||||||
|
result |= static_cast<int>(*(++ptr)) << 16;
|
||||||
|
result |= static_cast<int>(*(++ptr)) << 24;
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
|
||||||
|
case ArgumentType::Register:
|
||||||
|
return GetRegister(state);
|
||||||
|
|
||||||
|
default:
|
||||||
|
throw std::runtime_error("Unhandled argument type in Read");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Returns the size of the argument in bytes
|
||||||
|
std::size_t ArgumentValue::Parse(
|
||||||
|
std::vector<std::uint8_t> const & memory,
|
||||||
|
std::size_t const pos)
|
||||||
|
{
|
||||||
|
InstructionByte const valueByte = static_cast<InstructionByte>(memory[pos]);
|
||||||
|
switch(valueByte)
|
||||||
|
{
|
||||||
|
case InstructionByte::IMMEDIATE_INTEGER:
|
||||||
|
case InstructionByte::LABEL:
|
||||||
|
type = ArgumentType::Immediate;
|
||||||
|
data = Utils::Bytes::Read(memory, pos + 1);
|
||||||
|
return 5;
|
||||||
|
|
||||||
|
case InstructionByte::REGISTER:
|
||||||
|
type = ArgumentType::Register;
|
||||||
|
data = static_cast<RegisterByte>(memory[pos + 1]);
|
||||||
|
return 2;
|
||||||
|
|
||||||
|
case InstructionByte::MEMORY_OP:
|
||||||
|
{
|
||||||
|
type = ArgumentType::Memory;
|
||||||
|
InstructionByte const memoryArgTypeByte = static_cast<InstructionByte>(memory[pos + 1]);
|
||||||
|
switch(memoryArgTypeByte)
|
||||||
|
{
|
||||||
|
case InstructionByte::IMMEDIATE_INTEGER:
|
||||||
|
memoryValueType = ArgumentType::Immediate;
|
||||||
|
data = Utils::Bytes::Read(memory, pos + 2);
|
||||||
|
return 6;
|
||||||
|
|
||||||
|
case InstructionByte::REGISTER:
|
||||||
|
memoryValueType = ArgumentType::Register;
|
||||||
|
data = static_cast<RegisterByte>(memory[pos + 2]);
|
||||||
|
return 3;
|
||||||
|
|
||||||
|
default:
|
||||||
|
// TODO throw more specific error?
|
||||||
|
throw NonArgumentByte(pos);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
|
||||||
|
default:
|
||||||
|
throw NonArgumentByte(pos);
|
||||||
|
}
|
||||||
|
|
||||||
|
throw std::runtime_error("Reached end of function in Parse");
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -12,49 +12,54 @@ namespace Execute
|
|||||||
{
|
{
|
||||||
}
|
}
|
||||||
|
|
||||||
RuntimeError::RuntimeError(std::string const & what)
|
RuntimeError::RuntimeError(
|
||||||
: message(what)
|
std::string const & what,
|
||||||
|
std::size_t const _byteLocation)
|
||||||
|
: message(what),
|
||||||
|
byteLocation(_byteLocation)
|
||||||
{
|
{
|
||||||
}
|
}
|
||||||
|
|
||||||
StackUnderflow::StackUnderflow()
|
InterruptIndexOutOfRange::InterruptIndexOutOfRange(std::size_t const location, int const index)
|
||||||
: RuntimeError("Stack underflow error")
|
: RuntimeError("", location)
|
||||||
{
|
{
|
||||||
}
|
message = "Interrupt at byte ";
|
||||||
|
message += std::to_string(location);
|
||||||
StackOverflow::StackOverflow()
|
message += " with index ";
|
||||||
: RuntimeError("Stack overflow error")
|
|
||||||
{
|
|
||||||
}
|
|
||||||
|
|
||||||
MissingLabel::MissingLabel(std::string const & label)
|
|
||||||
{
|
|
||||||
message = "Missing jump/function label \"";
|
|
||||||
message += label;
|
|
||||||
message += '"';
|
|
||||||
}
|
|
||||||
|
|
||||||
InterruptIndexOutOfRange::InterruptIndexOutOfRange(int const index)
|
|
||||||
{
|
|
||||||
message = "Interrupt index ";
|
|
||||||
message += std::to_string(index);
|
message += std::to_string(index);
|
||||||
message += " is out of range";
|
message += " is out of range";
|
||||||
}
|
}
|
||||||
|
|
||||||
OutOfMemory::OutOfMemory(int const memoryLocation, int const memorySize)
|
AttemptedWriteToImmediate::AttemptedWriteToImmediate(std::size_t const location)
|
||||||
|
: RuntimeError("", location)
|
||||||
{
|
{
|
||||||
message = "Attempted interaction at memory location ";
|
message = "Instruction at ";
|
||||||
message += std::to_string(memoryLocation);
|
message += std::to_string(location);
|
||||||
message += " with size ";
|
message += " attempted to write to an immediate value";
|
||||||
message += std::to_string(memorySize);
|
|
||||||
message += " failed";
|
|
||||||
}
|
}
|
||||||
|
|
||||||
namespace Internal
|
NonExecutableInstruction::NonExecutableInstruction(std::size_t const location)
|
||||||
|
: RuntimeError("", location)
|
||||||
{
|
{
|
||||||
BadValueType::BadValueType()
|
message = "Attempted to execute byte at ";
|
||||||
: RuntimeError("Internal error: bad value type")
|
message += std::to_string(location);
|
||||||
{
|
message += " which is not an instruction byte";
|
||||||
}
|
}
|
||||||
|
|
||||||
|
NonArgumentByte::NonArgumentByte(std::size_t const location)
|
||||||
|
: RuntimeError("", location)
|
||||||
|
{
|
||||||
|
message = "Expected an argument byte (immediate, register or memory location) at ";
|
||||||
|
message += std::to_string(location);
|
||||||
|
}
|
||||||
|
|
||||||
|
OutOfMemory::OutOfMemory(
|
||||||
|
std::size_t const requiredMemorySize,
|
||||||
|
std::size_t const actualMemorySize)
|
||||||
|
{
|
||||||
|
message = "Not enough memory to fit code. Actual size is ";
|
||||||
|
message += std::to_string(actualMemorySize);
|
||||||
|
message += ". Minimal required size is ";
|
||||||
|
message += std::to_string(requiredMemorySize);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -1,38 +1,43 @@
|
|||||||
#include <cstdio>
|
#include <cstdio>
|
||||||
#include <execute/error.hpp>
|
#include <execute/error.hpp>
|
||||||
#include <execute/interrupts.hpp>
|
#include <execute/interrupts.hpp>
|
||||||
|
#include <vector>
|
||||||
|
|
||||||
namespace Execute
|
namespace Execute
|
||||||
{
|
{
|
||||||
std::vector<InterruptFn> GetInterrupts()
|
void ExecuteInterrupt(
|
||||||
|
int const id,
|
||||||
|
Execute::Registers & registers,
|
||||||
|
std::vector<std::uint8_t> & memory)
|
||||||
{
|
{
|
||||||
return std::vector<InterruptFn>
|
switch(id)
|
||||||
{
|
{
|
||||||
/* STDOUT INTERRUPTS */
|
/* STDOUT interrupts */
|
||||||
// 0 print char
|
case 0:
|
||||||
[](Execute::Registers & registers, std::vector<std::uint8_t> & memory) { std::putc(registers.A, stdout); },
|
std::putc(registers.A, stdout);
|
||||||
// 1 print decimal integer
|
break;
|
||||||
[](Execute::Registers & registers, std::vector<std::uint8_t> & memory) { std::printf("%i", registers.A); },
|
|
||||||
// 2 print hexadecimal integer
|
case 1:
|
||||||
[](Execute::Registers & registers, std::vector<std::uint8_t> & memory) { std::printf("0x%x", registers.A); },
|
std::printf("%i", registers.A);
|
||||||
// 3 print string from memory
|
break;
|
||||||
[](Execute::Registers & registers, std::vector<std::uint8_t> & memory)
|
|
||||||
|
case 2:
|
||||||
|
std::printf("0x%x", registers.A);
|
||||||
|
break;
|
||||||
|
|
||||||
|
case 3:
|
||||||
{
|
{
|
||||||
unsigned const end = registers.A + registers.B;
|
unsigned const end = registers.A + registers.B;
|
||||||
if (end >= memory.size())
|
// TODO handle out of bounds
|
||||||
{
|
|
||||||
throw OutOfMemory(registers.A, registers.B);
|
|
||||||
}
|
|
||||||
|
|
||||||
for(unsigned i = registers.A; i < end; ++i)
|
for(unsigned i = registers.A; i < end; ++i)
|
||||||
{
|
{
|
||||||
std::putc(memory[i], stdout);
|
std::putc(memory[i], stdout);
|
||||||
}
|
}
|
||||||
},
|
}
|
||||||
|
break;
|
||||||
|
|
||||||
/* STDIN INTERRUPTS */
|
/* STDIN interrupts */
|
||||||
// 4 get char from STDIN
|
case 10:
|
||||||
[](Execute::Registers & registers, std::vector<std::uint8_t> & memory)
|
|
||||||
{
|
{
|
||||||
registers.A = std::getchar();
|
registers.A = std::getchar();
|
||||||
if (registers.A == '\n')
|
if (registers.A == '\n')
|
||||||
@@ -41,9 +46,10 @@ namespace Execute
|
|||||||
}
|
}
|
||||||
|
|
||||||
while(std::getchar() != '\n');
|
while(std::getchar() != '\n');
|
||||||
},
|
}
|
||||||
// 5 get string from STDIN
|
break;
|
||||||
[](Execute::Registers & registers, std::vector<std::uint8_t> & memory)
|
|
||||||
|
case 11:
|
||||||
{
|
{
|
||||||
if (registers.B <= 0)
|
if (registers.B <= 0)
|
||||||
{
|
{
|
||||||
@@ -77,7 +83,11 @@ namespace Execute
|
|||||||
{
|
{
|
||||||
while(std::getchar() != '\n');
|
while(std::getchar() != '\n');
|
||||||
}
|
}
|
||||||
},
|
}
|
||||||
};
|
break;
|
||||||
|
|
||||||
|
default:
|
||||||
|
throw InterruptIndexOutOfRange(registers.programCounter, id);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
28
src/execute/state.cpp
Normal file
28
src/execute/state.cpp
Normal file
@@ -0,0 +1,28 @@
|
|||||||
|
#include <execute/state.hpp>
|
||||||
|
|
||||||
|
namespace Execute
|
||||||
|
{
|
||||||
|
void State::PushToStack(int const value)
|
||||||
|
{
|
||||||
|
memory[registers.stackPointer] = value & 0xFF;
|
||||||
|
memory[registers.stackPointer + 1] = (value >> 8) & 0xFF;
|
||||||
|
memory[registers.stackPointer + 2] = (value >> 16) & 0xFF;
|
||||||
|
memory[registers.stackPointer + 3] = (value >> 24) & 0xFF;
|
||||||
|
|
||||||
|
registers.stackPointer += 4;
|
||||||
|
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
int State::PopFromStack()
|
||||||
|
{
|
||||||
|
int value = static_cast<int>(memory[registers.stackPointer - 1]) << 24;
|
||||||
|
value |= static_cast<int>(memory[registers.stackPointer - 2]) << 16;
|
||||||
|
value |= static_cast<int>(memory[registers.stackPointer - 3]) << 8;
|
||||||
|
value |= static_cast<int>(memory[registers.stackPointer - 4]);
|
||||||
|
|
||||||
|
registers.stackPointer -= 4;
|
||||||
|
|
||||||
|
return value;
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -1,32 +1,399 @@
|
|||||||
#include <execute/virtualmachine.hpp>
|
|
||||||
#include <execute/error.hpp>
|
#include <execute/error.hpp>
|
||||||
|
#include <execute/interrupts.hpp>
|
||||||
|
#include <execute/virtualmachine.hpp>
|
||||||
|
#include <stdexcept>
|
||||||
|
#include <utils.hpp>
|
||||||
|
#include <variant>
|
||||||
|
|
||||||
namespace Execute
|
namespace Execute
|
||||||
{
|
{
|
||||||
void VirtualMachine::Step()
|
std::size_t GetArguments(
|
||||||
|
InstructionByte const instruction,
|
||||||
|
std::array<ArgumentValue, 3> & arguments,
|
||||||
|
std::vector<std::uint8_t> const & memory,
|
||||||
|
std::size_t const memoryPos)
|
||||||
{
|
{
|
||||||
state.nextStatement = state.currentStatement + 1u;
|
std::size_t expectedNumberOfArguments = 0;
|
||||||
|
switch(instruction)
|
||||||
try
|
|
||||||
{
|
{
|
||||||
codePtr->statements[state.currentStatement]->Execute(flags, state, registers);
|
case InstructionByte::JUMP:
|
||||||
|
case InstructionByte::INTERRUPT:
|
||||||
|
case InstructionByte::CALL:
|
||||||
|
case InstructionByte::POP_INTEGER:
|
||||||
|
case InstructionByte::PUSH_INTEGER:
|
||||||
|
expectedNumberOfArguments = 1;
|
||||||
|
break;
|
||||||
|
|
||||||
|
case InstructionByte::SET_INTEGER:
|
||||||
|
case InstructionByte::LESS_THAN_INTEGER:
|
||||||
|
case InstructionByte::GREATER_THAN_INTEGER:
|
||||||
|
case InstructionByte::EQUALS_INTEGER:
|
||||||
|
expectedNumberOfArguments = 2;
|
||||||
|
break;
|
||||||
|
|
||||||
|
case InstructionByte::ADD_INTEGER:
|
||||||
|
case InstructionByte::SUBTRACT_INTEGER:
|
||||||
|
case InstructionByte::DIVIDE_INTEGER:
|
||||||
|
case InstructionByte::MULTIPLY_INTEGER:
|
||||||
|
case InstructionByte::SHIFT_LEFT_INTEGER:
|
||||||
|
case InstructionByte::SHIFT_RIGHT_INTEGER:
|
||||||
|
expectedNumberOfArguments = 3;
|
||||||
|
break;
|
||||||
|
|
||||||
|
default:
|
||||||
|
throw std::runtime_error("No instruction length set for instruction byte");
|
||||||
}
|
}
|
||||||
catch(RuntimeError & ex)
|
|
||||||
{
|
|
||||||
state.terminated = true;
|
|
||||||
std::puts("\nA fatal error occurred and execution has been halted:");
|
|
||||||
std::printf("%s\n", ex.GetMessage().c_str());
|
|
||||||
|
|
||||||
|
std::size_t memoryOffset = memoryPos;
|
||||||
|
for(std::size_t i = 0; i < expectedNumberOfArguments; ++i)
|
||||||
|
{
|
||||||
|
memoryOffset += arguments[i].Parse(memory, memoryOffset);
|
||||||
|
}
|
||||||
|
|
||||||
|
return memoryOffset - memoryPos;
|
||||||
|
}
|
||||||
|
|
||||||
|
void VirtualMachine::DoArithmatic(
|
||||||
|
InstructionByte const instruction,
|
||||||
|
std::array<ArgumentValue, 3> & arguments)
|
||||||
|
{
|
||||||
|
switch (instruction)
|
||||||
|
{
|
||||||
|
case InstructionByte::ADD_INTEGER:
|
||||||
|
arguments[2].Write(arguments[0].Read(state) + arguments[1].Read(state), state);
|
||||||
|
break;
|
||||||
|
|
||||||
|
case InstructionByte::SUBTRACT_INTEGER:
|
||||||
|
arguments[2].Write(arguments[0].Read(state) - arguments[1].Read(state), state);
|
||||||
|
break;
|
||||||
|
|
||||||
|
case InstructionByte::DIVIDE_INTEGER:
|
||||||
|
arguments[2].Write(arguments[0].Read(state) / arguments[1].Read(state), state);
|
||||||
|
break;
|
||||||
|
|
||||||
|
case InstructionByte::MULTIPLY_INTEGER:
|
||||||
|
arguments[2].Write(arguments[0].Read(state) * arguments[1].Read(state), state);
|
||||||
|
break;
|
||||||
|
|
||||||
|
case InstructionByte::SHIFT_LEFT_INTEGER:
|
||||||
|
arguments[2].Write(arguments[0].Read(state) >> arguments[1].Read(state), state);
|
||||||
|
break;
|
||||||
|
|
||||||
|
case InstructionByte::SHIFT_RIGHT_INTEGER:
|
||||||
|
arguments[2].Write(arguments[0].Read(state) << arguments[1].Read(state), state);
|
||||||
|
break;
|
||||||
|
|
||||||
|
default:
|
||||||
|
throw std::runtime_error("Unhandled instruction bytr in DoArithmatic");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void VirtualMachine::SetInteger(std::array<ArgumentValue, 3> & arguments)
|
||||||
|
{
|
||||||
|
arguments[0].Write(arguments[1].Read(state), state);
|
||||||
|
}
|
||||||
|
|
||||||
|
void VirtualMachine::ExecuteJump(std::array<ArgumentValue, 3> & arguments)
|
||||||
|
{
|
||||||
|
state.registers.programCounter = arguments[0].Read(state);
|
||||||
|
}
|
||||||
|
|
||||||
|
void VirtualMachine::ExecuteInterrupt(std::array<ArgumentValue, 3> & arguments)
|
||||||
|
{
|
||||||
|
int const interruptNo = arguments[0].Read(state);
|
||||||
|
Execute::ExecuteInterrupt(interruptNo, state.registers, state.memory);
|
||||||
|
}
|
||||||
|
|
||||||
|
void VirtualMachine::ExecuteCall(
|
||||||
|
std::array<ArgumentValue, 3> & arguments,
|
||||||
|
std::size_t const returnByte)
|
||||||
|
{
|
||||||
|
state.PushToStack(returnByte);
|
||||||
|
state.registers.programCounter = arguments[0].Read(state);
|
||||||
|
}
|
||||||
|
|
||||||
|
void VirtualMachine::ExecuteReturn()
|
||||||
|
{
|
||||||
|
int const returnByte = state.PopFromStack();
|
||||||
|
state.registers.programCounter = returnByte;
|
||||||
|
}
|
||||||
|
|
||||||
|
void VirtualMachine::DoBooleanLogic(
|
||||||
|
InstructionByte const instruction,
|
||||||
|
std::array<ArgumentValue, 3> & arguments,
|
||||||
|
std::size_t const nextInstruction)
|
||||||
|
{
|
||||||
|
bool executeNextInstruction = false;
|
||||||
|
switch(instruction)
|
||||||
|
{
|
||||||
|
case InstructionByte::LESS_THAN_INTEGER:
|
||||||
|
executeNextInstruction = arguments[0].Read(state) < arguments[1].Read(state);
|
||||||
|
break;
|
||||||
|
|
||||||
|
case InstructionByte::GREATER_THAN_INTEGER:
|
||||||
|
executeNextInstruction = arguments[0].Read(state) > arguments[1].Read(state);
|
||||||
|
break;
|
||||||
|
|
||||||
|
case InstructionByte::EQUALS_INTEGER:
|
||||||
|
executeNextInstruction = arguments[0].Read(state) == arguments[1].Read(state);
|
||||||
|
break;
|
||||||
|
|
||||||
|
default:
|
||||||
|
throw std::runtime_error("Unhandled instruction byte for boolean logic");
|
||||||
|
}
|
||||||
|
|
||||||
|
if (executeNextInstruction)
|
||||||
|
{
|
||||||
|
state.registers.programCounter = nextInstruction;
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
state.currentStatement = state.nextStatement;
|
auto const argumentOffset = GetArguments(
|
||||||
if (state.currentStatement >= codePtr->statements.size())
|
static_cast<InstructionByte>(state.memory[nextInstruction]),
|
||||||
|
arguments,
|
||||||
|
state.memory,
|
||||||
|
nextInstruction + 1);
|
||||||
|
state.registers.programCounter = nextInstruction + 1 + argumentOffset;
|
||||||
|
}
|
||||||
|
|
||||||
|
void VirtualMachine::ExecutePop(std::array<ArgumentValue, 3> & arguments)
|
||||||
|
{
|
||||||
|
arguments[0].Write(state.PopFromStack(), state);
|
||||||
|
}
|
||||||
|
|
||||||
|
void VirtualMachine::ExecutePush(std::array<ArgumentValue, 3> & arguments)
|
||||||
|
{
|
||||||
|
state.PushToStack(arguments[0].Read(state));
|
||||||
|
}
|
||||||
|
|
||||||
|
void VirtualMachine::Step()
|
||||||
|
{
|
||||||
|
// Default to 1 byte (= 1 instruction)
|
||||||
|
std::size_t programCounterIncrement = 1;
|
||||||
|
std::array<ArgumentValue, 3> arguments;
|
||||||
|
InstructionByte const instruction =
|
||||||
|
static_cast<InstructionByte>(state.memory[state.registers.programCounter]);
|
||||||
|
switch(instruction)
|
||||||
{
|
{
|
||||||
|
case InstructionByte::ADD_INTEGER:
|
||||||
|
case InstructionByte::SUBTRACT_INTEGER:
|
||||||
|
case InstructionByte::DIVIDE_INTEGER:
|
||||||
|
case InstructionByte::MULTIPLY_INTEGER:
|
||||||
|
case InstructionByte::SHIFT_LEFT_INTEGER:
|
||||||
|
case InstructionByte::SHIFT_RIGHT_INTEGER:
|
||||||
|
programCounterIncrement += GetArguments(
|
||||||
|
instruction,
|
||||||
|
arguments,
|
||||||
|
state.memory,
|
||||||
|
state.registers.programCounter + 1);
|
||||||
|
DoArithmatic(instruction, arguments);
|
||||||
|
state.registers.programCounter += programCounterIncrement;
|
||||||
|
break;
|
||||||
|
|
||||||
|
case InstructionByte::SET_INTEGER:
|
||||||
|
programCounterIncrement += GetArguments(
|
||||||
|
instruction,
|
||||||
|
arguments,
|
||||||
|
state.memory,
|
||||||
|
state.registers.programCounter + 1);
|
||||||
|
SetInteger(arguments);
|
||||||
|
state.registers.programCounter += programCounterIncrement;
|
||||||
|
break;
|
||||||
|
|
||||||
|
case InstructionByte::JUMP:
|
||||||
|
GetArguments(
|
||||||
|
instruction,
|
||||||
|
arguments,
|
||||||
|
state.memory,
|
||||||
|
state.registers.programCounter + 1);
|
||||||
|
ExecuteJump(arguments);
|
||||||
|
break;
|
||||||
|
|
||||||
|
case InstructionByte::INTERRUPT:
|
||||||
|
programCounterIncrement += GetArguments(
|
||||||
|
instruction,
|
||||||
|
arguments,
|
||||||
|
state.memory,
|
||||||
|
state.registers.programCounter + 1);
|
||||||
|
ExecuteInterrupt(arguments);
|
||||||
|
state.registers.programCounter += programCounterIncrement;
|
||||||
|
break;
|
||||||
|
|
||||||
|
case InstructionByte::CALL:
|
||||||
|
programCounterIncrement += GetArguments(
|
||||||
|
instruction,
|
||||||
|
arguments,
|
||||||
|
state.memory,
|
||||||
|
state.registers.programCounter + 1);
|
||||||
|
ExecuteCall(arguments, state.registers.programCounter + programCounterIncrement);
|
||||||
|
break;
|
||||||
|
|
||||||
|
case InstructionByte::RETURN:
|
||||||
|
ExecuteReturn();
|
||||||
|
break;
|
||||||
|
|
||||||
|
case InstructionByte::EXIT:
|
||||||
state.terminated = true;
|
state.terminated = true;
|
||||||
|
return;
|
||||||
|
|
||||||
|
case InstructionByte::LESS_THAN_INTEGER:
|
||||||
|
case InstructionByte::GREATER_THAN_INTEGER:
|
||||||
|
case InstructionByte::EQUALS_INTEGER:
|
||||||
|
programCounterIncrement += GetArguments(
|
||||||
|
instruction,
|
||||||
|
arguments,
|
||||||
|
state.memory,
|
||||||
|
state.registers.programCounter + 1);
|
||||||
|
DoBooleanLogic(
|
||||||
|
instruction,
|
||||||
|
arguments,
|
||||||
|
state.registers.programCounter + programCounterIncrement);
|
||||||
|
break;
|
||||||
|
|
||||||
|
case InstructionByte::POP_INTEGER:
|
||||||
|
programCounterIncrement += GetArguments(
|
||||||
|
instruction,
|
||||||
|
arguments,
|
||||||
|
state.memory,
|
||||||
|
state.registers.programCounter + 1);
|
||||||
|
ExecutePop(arguments);
|
||||||
|
state.registers.programCounter += programCounterIncrement;
|
||||||
|
break;
|
||||||
|
|
||||||
|
case InstructionByte::PUSH_INTEGER:
|
||||||
|
programCounterIncrement += GetArguments(
|
||||||
|
instruction,
|
||||||
|
arguments,
|
||||||
|
state.memory,
|
||||||
|
state.registers.programCounter + 1);
|
||||||
|
ExecutePush(arguments);
|
||||||
|
state.registers.programCounter += programCounterIncrement;
|
||||||
|
break;
|
||||||
|
|
||||||
|
case InstructionByte::IMMEDIATE_INTEGER:
|
||||||
|
case InstructionByte::REGISTER:
|
||||||
|
case InstructionByte::MEMORY_OP:
|
||||||
|
case InstructionByte::LABEL:
|
||||||
|
default:
|
||||||
|
throw NonExecutableInstruction(state.registers.programCounter);
|
||||||
|
break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void PrintOperand(std::size_t const index, std::string const name)
|
||||||
|
{
|
||||||
|
std::printf("\n%04lu %s", index, name.c_str());
|
||||||
|
}
|
||||||
|
|
||||||
|
void PrintBytes(std::vector<std::uint8_t> const & byteCode)
|
||||||
|
{
|
||||||
|
for(std::size_t i = 0; i < byteCode.size(); ++i)
|
||||||
|
{
|
||||||
|
InstructionByte const id = static_cast<InstructionByte>(byteCode[i]);
|
||||||
|
switch(id)
|
||||||
|
{
|
||||||
|
case InstructionByte::ADD_INTEGER:
|
||||||
|
PrintOperand(i, "addi");
|
||||||
|
break;
|
||||||
|
|
||||||
|
case InstructionByte::SUBTRACT_INTEGER:
|
||||||
|
PrintOperand(i, "subi");
|
||||||
|
break;
|
||||||
|
|
||||||
|
case InstructionByte::DIVIDE_INTEGER:
|
||||||
|
PrintOperand(i, "divi");
|
||||||
|
break;
|
||||||
|
|
||||||
|
case InstructionByte::MULTIPLY_INTEGER:
|
||||||
|
PrintOperand(i, "muli");
|
||||||
|
break;
|
||||||
|
|
||||||
|
case InstructionByte::SHIFT_LEFT_INTEGER:
|
||||||
|
PrintOperand(i, "shli");
|
||||||
|
break;
|
||||||
|
|
||||||
|
case InstructionByte::SHIFT_RIGHT_INTEGER:
|
||||||
|
PrintOperand(i, "shri");
|
||||||
|
break;
|
||||||
|
|
||||||
|
case InstructionByte::SET_INTEGER:
|
||||||
|
PrintOperand(i, "sti");
|
||||||
|
break;
|
||||||
|
|
||||||
|
case InstructionByte::JUMP:
|
||||||
|
PrintOperand(i, "jmp");
|
||||||
|
break;
|
||||||
|
|
||||||
|
case InstructionByte::INTERRUPT:
|
||||||
|
PrintOperand(i, "int");
|
||||||
|
break;
|
||||||
|
|
||||||
|
case InstructionByte::CALL:
|
||||||
|
PrintOperand(i, "call");
|
||||||
|
break;
|
||||||
|
|
||||||
|
case InstructionByte::RETURN:
|
||||||
|
PrintOperand(i, "ret");
|
||||||
|
break;
|
||||||
|
|
||||||
|
case InstructionByte::EXIT:
|
||||||
|
PrintOperand(i, "exit");
|
||||||
|
break;
|
||||||
|
|
||||||
|
case InstructionByte::LESS_THAN_INTEGER:
|
||||||
|
PrintOperand(i, "lti");
|
||||||
|
break;
|
||||||
|
|
||||||
|
case InstructionByte::GREATER_THAN_INTEGER:
|
||||||
|
PrintOperand(i, "gti");
|
||||||
|
break;
|
||||||
|
|
||||||
|
case InstructionByte::EQUALS_INTEGER:
|
||||||
|
PrintOperand(i, "eqi");
|
||||||
|
break;
|
||||||
|
|
||||||
|
case InstructionByte::POP_INTEGER:
|
||||||
|
PrintOperand(i, "popi");
|
||||||
|
break;
|
||||||
|
|
||||||
|
case InstructionByte::PUSH_INTEGER:
|
||||||
|
PrintOperand(i, "pushi");
|
||||||
|
break;
|
||||||
|
|
||||||
|
case InstructionByte::IMMEDIATE_INTEGER:
|
||||||
|
std::printf("$%i", Utils::Bytes::Read(byteCode, i + 1));
|
||||||
|
i += 4u;
|
||||||
|
break;
|
||||||
|
|
||||||
|
case InstructionByte::REGISTER:
|
||||||
|
{
|
||||||
|
std::string registerName {"A"};
|
||||||
|
registerName[0] += byteCode[i + 1] - 1;
|
||||||
|
std::printf("%%%s", registerName.c_str());
|
||||||
|
++i;
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
|
||||||
|
case InstructionByte::MEMORY_OP:
|
||||||
|
std::printf("[]");
|
||||||
|
break;
|
||||||
|
|
||||||
|
case InstructionByte::LABEL:
|
||||||
|
std::printf("%i:", Utils::Bytes::Read(byteCode, i + 1));
|
||||||
|
i += 4u;
|
||||||
|
break;
|
||||||
|
|
||||||
|
default:
|
||||||
|
std::printf("UNKNOWN");
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
std::putc(' ', stdout);
|
||||||
|
}
|
||||||
|
|
||||||
|
std::puts("");
|
||||||
|
}
|
||||||
|
|
||||||
void VirtualMachine::Run()
|
void VirtualMachine::Run()
|
||||||
{
|
{
|
||||||
while(!IsTerminated())
|
while(!IsTerminated())
|
||||||
@@ -43,29 +410,38 @@ namespace Execute
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void VirtualMachine::LoadConfiguration(Configuration const & c)
|
void VirtualMachine::SetMemorySize(std::size_t const size)
|
||||||
{
|
{
|
||||||
c.PrepareMemory(state.memory);
|
state.memory.resize(size);
|
||||||
}
|
}
|
||||||
|
|
||||||
void VirtualMachine::LoadCode(std::unique_ptr<Interpret::Code> code)
|
void VirtualMachine::LoadCode(
|
||||||
|
std::vector<std::uint8_t> const & byteCode,
|
||||||
|
bool const printDecodedBytes)
|
||||||
{
|
{
|
||||||
codePtr = std::move(code);
|
if (printDecodedBytes)
|
||||||
state.labelStatementIndice = &(codePtr->labelStatementIndice);
|
{
|
||||||
|
PrintBytes(byteCode);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (state.memory.size() < byteCode.size())
|
||||||
|
{
|
||||||
|
throw OutOfMemory(byteCode.size(), state.memory.size());
|
||||||
|
}
|
||||||
|
|
||||||
|
for (std::size_t i = 0; i < byteCode.size(); ++i)
|
||||||
|
{
|
||||||
|
state.memory[i] = byteCode[i];
|
||||||
|
}
|
||||||
|
|
||||||
|
state.registers.stackPointer = byteCode.size();
|
||||||
}
|
}
|
||||||
|
|
||||||
Flags const & VirtualMachine::GetFlags() const { return flags; }
|
|
||||||
Registers const & VirtualMachine::GetRegisters() const { return registers; }
|
|
||||||
State const & VirtualMachine::GetState() const { return state; }
|
State const & VirtualMachine::GetState() const { return state; }
|
||||||
Interpret::Statement const * const VirtualMachine::GetCurrentStatement() const
|
Execute::InstructionByte VirtualMachine::GetCurrentInstruction() const
|
||||||
{
|
{
|
||||||
return codePtr->statements[state.currentStatement].get();
|
return static_cast<InstructionByte>(state.memory[state.registers.programCounter]);
|
||||||
}
|
}
|
||||||
|
|
||||||
bool VirtualMachine::IsTerminated() const { return state.terminated; }
|
bool VirtualMachine::IsTerminated() const { return state.terminated; }
|
||||||
|
|
||||||
VirtualMachine::VirtualMachine()
|
|
||||||
{
|
|
||||||
state.interrupts = GetInterrupts();
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
@@ -1,73 +0,0 @@
|
|||||||
#include <interpret/errors.hpp>
|
|
||||||
|
|
||||||
namespace Interpret
|
|
||||||
{
|
|
||||||
InterpretationError::InterpretationError(Token::Token const & token, std::string const & msg)
|
|
||||||
: errorToken(token)
|
|
||||||
{
|
|
||||||
errorToken.errorMessage = msg;
|
|
||||||
}
|
|
||||||
|
|
||||||
ExpectedArgument::ExpectedArgument(Token::Token const & token)
|
|
||||||
: InterpretationError(token, "Expected an argument")
|
|
||||||
{
|
|
||||||
}
|
|
||||||
|
|
||||||
ExpectedLabel::ExpectedLabel(Token::Token const & token)
|
|
||||||
: InterpretationError(token, "Expected a label")
|
|
||||||
{
|
|
||||||
}
|
|
||||||
|
|
||||||
ExpectedValue::ExpectedValue(Token::Token const & token)
|
|
||||||
: InterpretationError(token, "Expected an immediate value, a register or a memory location")
|
|
||||||
{
|
|
||||||
}
|
|
||||||
|
|
||||||
ExpectedImmediate::ExpectedImmediate(Token::Token const & token)
|
|
||||||
: InterpretationError(token, "Expected an immediate value")
|
|
||||||
{
|
|
||||||
}
|
|
||||||
|
|
||||||
ExpectedImmediateOrMemory::ExpectedImmediateOrMemory(Token::Token const & token)
|
|
||||||
: InterpretationError(token, "Expected an immediate value or a memory location")
|
|
||||||
{
|
|
||||||
}
|
|
||||||
|
|
||||||
ExpectedRegister::ExpectedRegister(Token::Token const & token)
|
|
||||||
: InterpretationError(token, "Expected a register")
|
|
||||||
{
|
|
||||||
}
|
|
||||||
|
|
||||||
ExpectedRegisterOrMemory::ExpectedRegisterOrMemory(Token::Token const & token)
|
|
||||||
: InterpretationError(token, "Expected a register or a memory location")
|
|
||||||
{
|
|
||||||
}
|
|
||||||
|
|
||||||
ExpectedOperand::ExpectedOperand(Token::Token const & token)
|
|
||||||
: InterpretationError(token, "Expected an operand")
|
|
||||||
{
|
|
||||||
}
|
|
||||||
|
|
||||||
TooManyArguments::TooManyArguments(Token::Token const & token)
|
|
||||||
: InterpretationError(token, "Too many arguments for operand")
|
|
||||||
{
|
|
||||||
}
|
|
||||||
|
|
||||||
TooFewArguments::TooFewArguments(Token::Token const & token)
|
|
||||||
: InterpretationError(token, "Too few arguments for operand")
|
|
||||||
{
|
|
||||||
}
|
|
||||||
|
|
||||||
MissingEndOfStatment::MissingEndOfStatment(Token::Token const & token)
|
|
||||||
: InterpretationError(token, "Missing end of line terminator (;)")
|
|
||||||
{
|
|
||||||
}
|
|
||||||
|
|
||||||
namespace Internal
|
|
||||||
{
|
|
||||||
BadTokenForValue::BadTokenForValue(Token::Token const & token)
|
|
||||||
: InterpretationError(token, "Internal error when converting token to value")
|
|
||||||
{
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,86 +0,0 @@
|
|||||||
#include <interpret/errors.hpp>
|
|
||||||
#include <interpret/interpreter.hpp>
|
|
||||||
#include <interpret/operanddefinitions.hpp>
|
|
||||||
|
|
||||||
namespace Interpret
|
|
||||||
{
|
|
||||||
bool IsArgumentToken(Token::Token const & t)
|
|
||||||
{
|
|
||||||
return
|
|
||||||
t.type == Token::TokenType::ImmediateInteger ||
|
|
||||||
t.type == Token::TokenType::Register ||
|
|
||||||
t.type == Token::TokenType::Label ||
|
|
||||||
t.type == Token::TokenType::Memory;
|
|
||||||
}
|
|
||||||
|
|
||||||
void Interpreter::Interpret(std::vector<Token::Token> const & tokens, Code & code)
|
|
||||||
{
|
|
||||||
enum class InterpreterState
|
|
||||||
{
|
|
||||||
FindOperand,
|
|
||||||
FindArguments,
|
|
||||||
FindStatementEnd
|
|
||||||
};
|
|
||||||
|
|
||||||
InterpreterState state = InterpreterState::FindOperand;
|
|
||||||
unsigned operatorTokenIndex = 0u;
|
|
||||||
int expectedNumberOfArguments = 0;
|
|
||||||
for(unsigned i = 0u; i < tokens.size(); ++i)
|
|
||||||
{
|
|
||||||
auto const & token = tokens[i];
|
|
||||||
switch(state)
|
|
||||||
{
|
|
||||||
case InterpreterState::FindOperand:
|
|
||||||
if (token.type == Token::TokenType::Operand)
|
|
||||||
{
|
|
||||||
operatorTokenIndex = i;
|
|
||||||
expectedNumberOfArguments = GetRequiredNumberOfArguments(std::get<Token::OperandType>(token.data));
|
|
||||||
if (expectedNumberOfArguments < 1)
|
|
||||||
{
|
|
||||||
state = InterpreterState::FindStatementEnd;
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
state = InterpreterState::FindArguments;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
else if (token.type == Token::TokenType::Label)
|
|
||||||
{
|
|
||||||
code.labelStatementIndice[std::get<std::string>(token.data)] = code.statements.size();
|
|
||||||
}
|
|
||||||
else if (token.type != Token::TokenType::StatementEnd)
|
|
||||||
{
|
|
||||||
throw ExpectedOperand(token);
|
|
||||||
}
|
|
||||||
break;
|
|
||||||
|
|
||||||
case InterpreterState::FindArguments:
|
|
||||||
if (IsArgumentToken(token))
|
|
||||||
{
|
|
||||||
expectedNumberOfArguments -= 1;
|
|
||||||
if (expectedNumberOfArguments < 1)
|
|
||||||
{
|
|
||||||
state = InterpreterState::FindStatementEnd;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
throw ExpectedArgument(token);
|
|
||||||
}
|
|
||||||
break;
|
|
||||||
|
|
||||||
case InterpreterState::FindStatementEnd:
|
|
||||||
if (token.type != Token::TokenType::StatementEnd)
|
|
||||||
{
|
|
||||||
throw MissingEndOfStatment(token);
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
code.statements.emplace_back(ExtractStatement(operatorTokenIndex, tokens));
|
|
||||||
state = InterpreterState::FindOperand;
|
|
||||||
}
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,327 +0,0 @@
|
|||||||
#include <interpret/operanddefinitions.hpp>
|
|
||||||
#include <interpret/errors.hpp>
|
|
||||||
|
|
||||||
namespace Interpret
|
|
||||||
{
|
|
||||||
Value GetImmediateArgument(unsigned const index, std::vector<Token::Token> const & tokens)
|
|
||||||
{
|
|
||||||
auto const & token = tokens[index];
|
|
||||||
if (token.type == Token::TokenType::ImmediateInteger)
|
|
||||||
{
|
|
||||||
Value v;
|
|
||||||
v.CreateFromToken(token);
|
|
||||||
|
|
||||||
return v;
|
|
||||||
}
|
|
||||||
|
|
||||||
throw ExpectedImmediate(token);
|
|
||||||
}
|
|
||||||
|
|
||||||
Value GetRegisterArgument(unsigned const index, std::vector<Token::Token> const & tokens)
|
|
||||||
{
|
|
||||||
auto const & token = tokens[index];
|
|
||||||
if (token.type == Token::TokenType::Register)
|
|
||||||
{
|
|
||||||
Value v;
|
|
||||||
v.CreateFromToken(token);
|
|
||||||
|
|
||||||
return v;
|
|
||||||
}
|
|
||||||
|
|
||||||
throw ExpectedRegister(token);
|
|
||||||
}
|
|
||||||
|
|
||||||
Value GetRegisterOrMemoryArgument(unsigned const index, std::vector<Token::Token> const & tokens)
|
|
||||||
{
|
|
||||||
auto const & token = tokens[index];
|
|
||||||
if (token.type == Token::TokenType::Register || token.type == Token::TokenType::Memory)
|
|
||||||
{
|
|
||||||
Value v;
|
|
||||||
v.CreateFromToken(token);
|
|
||||||
|
|
||||||
return v;
|
|
||||||
}
|
|
||||||
|
|
||||||
throw ExpectedRegisterOrMemory(token);
|
|
||||||
}
|
|
||||||
|
|
||||||
Value GetValueArgument(unsigned const index, std::vector<Token::Token> const & tokens)
|
|
||||||
{
|
|
||||||
auto const & token = tokens[index];
|
|
||||||
if (token.type == Token::TokenType::ImmediateInteger || token.type == Token::TokenType::Register || token.type == Token::TokenType::Memory)
|
|
||||||
{
|
|
||||||
Value v;
|
|
||||||
v.CreateFromToken(token);
|
|
||||||
|
|
||||||
return v;
|
|
||||||
}
|
|
||||||
|
|
||||||
throw ExpectedValue(token);
|
|
||||||
}
|
|
||||||
|
|
||||||
void AddArithmeticArguments(ArithmeticStatement & statement, unsigned const operandIndex, std::vector<Token::Token> const & tokens)
|
|
||||||
{
|
|
||||||
statement.firstArgument = GetValueArgument(operandIndex + 1u, tokens);
|
|
||||||
statement.secondArgument = GetValueArgument(operandIndex + 2u, tokens);
|
|
||||||
statement.thirdArgument = GetRegisterOrMemoryArgument(operandIndex + 3u, tokens);
|
|
||||||
}
|
|
||||||
|
|
||||||
void AddLogicArguments(ControlFlowStatement & statement, unsigned const operandIndex, std::vector<Token::Token> const & tokens)
|
|
||||||
{
|
|
||||||
statement.firstArgument = GetValueArgument(operandIndex + 1u, tokens);
|
|
||||||
statement.secondArgument = GetValueArgument(operandIndex + 2u, tokens);
|
|
||||||
}
|
|
||||||
|
|
||||||
std::unique_ptr<Statement> ExtractStatement(unsigned const operandIndex, std::vector<Token::Token> const & tokens)
|
|
||||||
{
|
|
||||||
auto const & token = tokens[operandIndex];
|
|
||||||
switch(std::get<Token::OperandType>(token.data))
|
|
||||||
{
|
|
||||||
case Token::OperandType::AddInteger:
|
|
||||||
{
|
|
||||||
auto statement = std::make_unique<ArithmeticStatement>();
|
|
||||||
statement->function = [](int argument1, int argument2, int & argument3)
|
|
||||||
{
|
|
||||||
argument3 = argument1 + argument2;
|
|
||||||
};
|
|
||||||
AddArithmeticArguments(*statement, operandIndex, tokens);
|
|
||||||
return statement;
|
|
||||||
}
|
|
||||||
|
|
||||||
case Token::OperandType::SubtractInteger:
|
|
||||||
{
|
|
||||||
auto statement = std::make_unique<ArithmeticStatement>();
|
|
||||||
statement->function = [](int argument1, int argument2, int & argument3)
|
|
||||||
{
|
|
||||||
argument3 = argument1 - argument2;
|
|
||||||
};
|
|
||||||
AddArithmeticArguments(*statement, operandIndex, tokens);
|
|
||||||
return statement;
|
|
||||||
}
|
|
||||||
|
|
||||||
case Token::OperandType::DivideInteger:
|
|
||||||
{
|
|
||||||
auto statement = std::make_unique<ArithmeticStatement>();
|
|
||||||
statement->function = [](int argument1, int argument2, int & argument3)
|
|
||||||
{
|
|
||||||
argument3 = argument1 / argument2;
|
|
||||||
};
|
|
||||||
AddArithmeticArguments(*statement, operandIndex, tokens);
|
|
||||||
return statement;
|
|
||||||
}
|
|
||||||
|
|
||||||
case Token::OperandType::MultiplyInteger:
|
|
||||||
{
|
|
||||||
auto statement = std::make_unique<ArithmeticStatement>();
|
|
||||||
statement->function = [](int argument1, int argument2, int & argument3)
|
|
||||||
{
|
|
||||||
argument3 = argument1 * argument2;
|
|
||||||
};
|
|
||||||
AddArithmeticArguments(*statement, operandIndex, tokens);
|
|
||||||
return statement;
|
|
||||||
}
|
|
||||||
|
|
||||||
case Token::OperandType::ShiftIntegerLeft:
|
|
||||||
{
|
|
||||||
auto statement = std::make_unique<ArithmeticStatement>();
|
|
||||||
statement->function = [](int argument1, int argument2, int & argument3)
|
|
||||||
{
|
|
||||||
argument3 = argument1 << argument2;
|
|
||||||
};
|
|
||||||
AddArithmeticArguments(*statement, operandIndex, tokens);
|
|
||||||
return statement;
|
|
||||||
}
|
|
||||||
|
|
||||||
case Token::OperandType::ShiftIntegerRight:
|
|
||||||
{
|
|
||||||
auto statement = std::make_unique<ArithmeticStatement>();
|
|
||||||
statement->function = [](int argument1, int argument2, int & argument3)
|
|
||||||
{
|
|
||||||
argument3 = argument1 >> argument2;
|
|
||||||
};
|
|
||||||
AddArithmeticArguments(*statement, operandIndex, tokens);
|
|
||||||
return statement;
|
|
||||||
}
|
|
||||||
|
|
||||||
case Token::OperandType::Jump:
|
|
||||||
{
|
|
||||||
auto labelToken = tokens[operandIndex + 1u];
|
|
||||||
if (labelToken.type != Token::TokenType::Label)
|
|
||||||
{
|
|
||||||
throw ExpectedLabel(labelToken);
|
|
||||||
}
|
|
||||||
|
|
||||||
return std::make_unique<JumpStatement>(std::get<std::string>(labelToken.data));
|
|
||||||
}
|
|
||||||
|
|
||||||
case Token::OperandType::CallFunction:
|
|
||||||
{
|
|
||||||
auto labelToken = tokens[operandIndex + 1u];
|
|
||||||
if (labelToken.type != Token::TokenType::Label)
|
|
||||||
{
|
|
||||||
throw ExpectedLabel(labelToken);
|
|
||||||
}
|
|
||||||
|
|
||||||
return std::make_unique<FunctionCallStatement>(std::get<std::string>(labelToken.data));
|
|
||||||
}
|
|
||||||
|
|
||||||
case Token::OperandType::ReturnFromFunction:
|
|
||||||
{
|
|
||||||
return std::make_unique<ReturnFromFunctionStatement>();
|
|
||||||
}
|
|
||||||
|
|
||||||
case Token::OperandType::ExitProgram:
|
|
||||||
{
|
|
||||||
return std::make_unique<ExitProgramStatement>();
|
|
||||||
}
|
|
||||||
|
|
||||||
case Token::OperandType::LessThanInteger:
|
|
||||||
{
|
|
||||||
auto statement = std::make_unique<ControlFlowStatement>();
|
|
||||||
statement->function = [](Execute::State & state, int argument1, int argument2)
|
|
||||||
{
|
|
||||||
if (argument1 < argument2)
|
|
||||||
{
|
|
||||||
state.nextStatement = state.currentStatement + 1u;
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
state.nextStatement = state.currentStatement + 2u;
|
|
||||||
}
|
|
||||||
};
|
|
||||||
AddLogicArguments(*statement, operandIndex, tokens);
|
|
||||||
return statement;
|
|
||||||
}
|
|
||||||
|
|
||||||
case Token::OperandType::GreaterThanInteger:
|
|
||||||
{
|
|
||||||
auto statement = std::make_unique<ControlFlowStatement>();
|
|
||||||
statement->function = [](Execute::State & state, int argument1, int argument2)
|
|
||||||
{
|
|
||||||
if (argument1 > argument2)
|
|
||||||
{
|
|
||||||
state.nextStatement = state.currentStatement + 1u;
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
state.nextStatement = state.currentStatement + 2u;
|
|
||||||
}
|
|
||||||
};
|
|
||||||
AddLogicArguments(*statement, operandIndex, tokens);
|
|
||||||
return statement;
|
|
||||||
}
|
|
||||||
|
|
||||||
case Token::OperandType::EqualInteger:
|
|
||||||
{
|
|
||||||
auto statement = std::make_unique<ControlFlowStatement>();
|
|
||||||
statement->function = [](Execute::State & state, int argument1, int argument2)
|
|
||||||
{
|
|
||||||
if (argument1 == argument2)
|
|
||||||
{
|
|
||||||
state.nextStatement = state.currentStatement + 1u;
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
state.nextStatement = state.currentStatement + 2u;
|
|
||||||
}
|
|
||||||
};
|
|
||||||
AddLogicArguments(*statement, operandIndex, tokens);
|
|
||||||
return statement;
|
|
||||||
}
|
|
||||||
|
|
||||||
case Token::OperandType::SetInteger:
|
|
||||||
{
|
|
||||||
auto statement = std::make_unique<SetStatement>();
|
|
||||||
statement->firstArgument = GetRegisterOrMemoryArgument(operandIndex + 1u, tokens);
|
|
||||||
statement->secondArgument = GetValueArgument(operandIndex + 2u, tokens);
|
|
||||||
|
|
||||||
return statement;
|
|
||||||
}
|
|
||||||
|
|
||||||
case Token::OperandType::Interrupt:
|
|
||||||
{
|
|
||||||
auto statement = std::make_unique<InterruptStatement>();
|
|
||||||
statement->firstArgument = GetValueArgument(operandIndex + 1u, tokens);
|
|
||||||
|
|
||||||
return statement;
|
|
||||||
}
|
|
||||||
|
|
||||||
case Token::OperandType::PopInteger:
|
|
||||||
{
|
|
||||||
auto statement = std::make_unique<PopStatement>();
|
|
||||||
statement->firstArgument = GetRegisterArgument(operandIndex + 1u, tokens);
|
|
||||||
|
|
||||||
return statement;
|
|
||||||
}
|
|
||||||
|
|
||||||
case Token::OperandType::PushInteger:
|
|
||||||
{
|
|
||||||
auto statement = std::make_unique<PushStatement>();
|
|
||||||
statement->firstArgument = GetValueArgument(operandIndex + 1u, tokens);
|
|
||||||
|
|
||||||
return statement;
|
|
||||||
}
|
|
||||||
|
|
||||||
default:
|
|
||||||
{
|
|
||||||
auto statement = std::make_unique<NoArgumentStatement>();
|
|
||||||
|
|
||||||
// TODO throw error?
|
|
||||||
statement->function = [](Execute::Flags & flags, Execute::Registers & registers)
|
|
||||||
{
|
|
||||||
std::puts("ExtractStatement: Extracted unhandled operator type");
|
|
||||||
};
|
|
||||||
return statement;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
std::tuple<std::string, int> ExtractDeclaration(unsigned const operatorIndex, std::vector<Token::Token> const & tokens)
|
|
||||||
{
|
|
||||||
if (tokens[operatorIndex + 1u].type != Token::TokenType::Label)
|
|
||||||
{
|
|
||||||
throw ExpectedLabel(tokens[operatorIndex + 1u]);
|
|
||||||
}
|
|
||||||
if (tokens[operatorIndex + 2u].type != Token::TokenType::ImmediateInteger)
|
|
||||||
{
|
|
||||||
throw ExpectedImmediate(tokens[operatorIndex + 2u]);
|
|
||||||
}
|
|
||||||
|
|
||||||
auto const label = std::get<std::string>(tokens[operatorIndex + 1u].data);
|
|
||||||
auto const value = std::get<int>(tokens[operatorIndex + 2u].data);
|
|
||||||
return std::make_tuple(label, value);
|
|
||||||
}
|
|
||||||
|
|
||||||
int GetRequiredNumberOfArguments(Token::OperandType const type)
|
|
||||||
{
|
|
||||||
switch (type)
|
|
||||||
{
|
|
||||||
case Token::OperandType::AddInteger:
|
|
||||||
case Token::OperandType::SubtractInteger:
|
|
||||||
case Token::OperandType::DivideInteger:
|
|
||||||
case Token::OperandType::MultiplyInteger:
|
|
||||||
case Token::OperandType::ShiftIntegerLeft:
|
|
||||||
case Token::OperandType::ShiftIntegerRight:
|
|
||||||
return 3;
|
|
||||||
|
|
||||||
case Token::OperandType::LessThanInteger:
|
|
||||||
case Token::OperandType::GreaterThanInteger:
|
|
||||||
case Token::OperandType::EqualInteger:
|
|
||||||
case Token::OperandType::SetInteger:
|
|
||||||
return 2;
|
|
||||||
|
|
||||||
case Token::OperandType::Jump:
|
|
||||||
case Token::OperandType::CallFunction:
|
|
||||||
case Token::OperandType::Interrupt:
|
|
||||||
case Token::OperandType::PushInteger:
|
|
||||||
case Token::OperandType::PopInteger:
|
|
||||||
return 1;
|
|
||||||
|
|
||||||
default:
|
|
||||||
std::printf("WARNING: returning default argument length of 0 for operand type %i\n", static_cast<int>(type));
|
|
||||||
case Token::OperandType::ReturnFromFunction:
|
|
||||||
case Token::OperandType::ExitProgram:
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,125 +0,0 @@
|
|||||||
#include <execute/error.hpp>
|
|
||||||
#include <interpret/statement.hpp>
|
|
||||||
#include <stdexcept>
|
|
||||||
|
|
||||||
namespace Interpret
|
|
||||||
{
|
|
||||||
void NoArgumentStatement::Execute(Execute::Flags & flags, Execute::State & state, Execute::Registers & registers)
|
|
||||||
{
|
|
||||||
function(flags, registers);
|
|
||||||
}
|
|
||||||
|
|
||||||
void OneArgumentStatement::Execute(Execute::Flags & flags, Execute::State & state, Execute::Registers & registers)
|
|
||||||
{
|
|
||||||
function(flags, firstArgument.GetValue(state, registers));
|
|
||||||
}
|
|
||||||
|
|
||||||
void ControlFlowStatement::Execute(Execute::Flags & flags, Execute::State & state, Execute::Registers & registers)
|
|
||||||
{
|
|
||||||
function(state, firstArgument.GetValue(state, registers), secondArgument.GetValue(state, registers));
|
|
||||||
}
|
|
||||||
|
|
||||||
void ArithmeticStatement::Execute(Execute::Flags & flags, Execute::State & state, Execute::Registers & registers)
|
|
||||||
{
|
|
||||||
function(firstArgument.GetValue(state, registers), secondArgument.GetValue(state, registers), thirdArgument.GetValue(state, registers));
|
|
||||||
}
|
|
||||||
|
|
||||||
void JumpStatement::Execute(Execute::Flags & flags, Execute::State & state, Execute::Registers & registers)
|
|
||||||
{
|
|
||||||
auto const & elem = state.labelStatementIndice->find(label);
|
|
||||||
if (elem != state.labelStatementIndice->end())
|
|
||||||
{
|
|
||||||
state.nextStatement = elem->second;
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
throw Execute::MissingLabel(label);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
JumpStatement::JumpStatement(std::string const & _label)
|
|
||||||
: label(_label)
|
|
||||||
{
|
|
||||||
}
|
|
||||||
|
|
||||||
void FunctionCallStatement::Execute(Execute::Flags & flags, Execute::State & state, Execute::Registers & registers)
|
|
||||||
{
|
|
||||||
auto const & elem = state.labelStatementIndice->find(label);
|
|
||||||
if (elem == state.labelStatementIndice->end())
|
|
||||||
{
|
|
||||||
throw Execute::MissingLabel(label);
|
|
||||||
}
|
|
||||||
|
|
||||||
if ((state.memory.size() - state.stackPointer) < sizeof(unsigned))
|
|
||||||
{
|
|
||||||
throw Execute::StackOverflow();
|
|
||||||
}
|
|
||||||
|
|
||||||
*(reinterpret_cast<unsigned *>(state.memory.data() + state.stackPointer + (sizeof(unsigned) - 1))) = state.nextStatement;
|
|
||||||
|
|
||||||
state.stackPointer += sizeof(int);
|
|
||||||
state.nextStatement = elem->second;
|
|
||||||
}
|
|
||||||
|
|
||||||
FunctionCallStatement::FunctionCallStatement(std::string const & _label)
|
|
||||||
: label(_label)
|
|
||||||
{
|
|
||||||
}
|
|
||||||
|
|
||||||
void ReturnFromFunctionStatement::Execute(Execute::Flags & flags, Execute::State & state, Execute::Registers & registers)
|
|
||||||
{
|
|
||||||
if (state.stackPointer < sizeof(unsigned))
|
|
||||||
{
|
|
||||||
throw Execute::StackUnderflow();
|
|
||||||
}
|
|
||||||
|
|
||||||
state.nextStatement = *(reinterpret_cast<unsigned const *>(state.memory.data() + (state.stackPointer - 1u)));
|
|
||||||
state.stackPointer -= sizeof(unsigned);
|
|
||||||
}
|
|
||||||
|
|
||||||
void ExitProgramStatement::Execute(Execute::Flags & flags, Execute::State & state, Execute::Registers & registers)
|
|
||||||
{
|
|
||||||
state.terminated = true;
|
|
||||||
}
|
|
||||||
|
|
||||||
void SetStatement::Execute(Execute::Flags & flags, Execute::State & state, Execute::Registers & registers)
|
|
||||||
{
|
|
||||||
firstArgument.GetValue(state, registers) = secondArgument.GetValue(state, registers);
|
|
||||||
}
|
|
||||||
|
|
||||||
void InterruptStatement::Execute(Execute::Flags & flags, Execute::State & state, Execute::Registers & registers)
|
|
||||||
{
|
|
||||||
auto const interruptIndex = firstArgument.GetValue(state, registers);
|
|
||||||
if (interruptIndex < 0 || static_cast<unsigned>(interruptIndex) > state.interrupts.size())
|
|
||||||
{
|
|
||||||
throw Execute::InterruptIndexOutOfRange(interruptIndex);
|
|
||||||
}
|
|
||||||
|
|
||||||
state.interrupts[interruptIndex](registers, state.memory);
|
|
||||||
}
|
|
||||||
|
|
||||||
void PopStatement::Execute(Execute::Flags & flags, Execute::State & state, Execute::Registers & registers)
|
|
||||||
{
|
|
||||||
if (state.stackPointer < sizeof(int))
|
|
||||||
{
|
|
||||||
throw Execute::StackUnderflow();
|
|
||||||
}
|
|
||||||
|
|
||||||
auto const value = *(reinterpret_cast<int const *>(state.memory.data() + (state.stackPointer - 1u)));
|
|
||||||
firstArgument.GetValue(state, registers) = value;
|
|
||||||
|
|
||||||
state.stackPointer -= sizeof(int);
|
|
||||||
}
|
|
||||||
|
|
||||||
void PushStatement::Execute(Execute::Flags & flags, Execute::State & state, Execute::Registers & registers)
|
|
||||||
{
|
|
||||||
if ((state.memory.size() - state.stackPointer) < sizeof(int))
|
|
||||||
{
|
|
||||||
throw Execute::StackOverflow();
|
|
||||||
}
|
|
||||||
|
|
||||||
*(reinterpret_cast<int *>(state.memory.data() + state.stackPointer + (sizeof(int) - 1))) = firstArgument.GetValue(state, registers);
|
|
||||||
|
|
||||||
state.stackPointer += sizeof(int);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,75 +0,0 @@
|
|||||||
#include <execute/error.hpp>
|
|
||||||
#include <interpret/errors.hpp>
|
|
||||||
#include <interpret/value.hpp>
|
|
||||||
|
|
||||||
namespace Interpret
|
|
||||||
{
|
|
||||||
int & Value::GetValue(Execute::State & state, Execute::Registers & registers)
|
|
||||||
{
|
|
||||||
switch(type)
|
|
||||||
{
|
|
||||||
case ValueType::ImmediateInteger:
|
|
||||||
return data;
|
|
||||||
|
|
||||||
case ValueType::Register:
|
|
||||||
return registers.registers[data];
|
|
||||||
|
|
||||||
case ValueType::MemoryLocation:
|
|
||||||
if (dataType == ValueDataType::Register)
|
|
||||||
{
|
|
||||||
return *reinterpret_cast<int *>(state.memory.data() + registers.registers[data]);
|
|
||||||
}
|
|
||||||
else if (dataType == ValueDataType::Immediate)
|
|
||||||
{
|
|
||||||
return *reinterpret_cast<int *>(state.memory.data() + data);
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
throw Execute::Internal::BadValueType();
|
|
||||||
}
|
|
||||||
break;
|
|
||||||
|
|
||||||
default:
|
|
||||||
throw Execute::Internal::BadValueType();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
void Value::CreateFromToken(Token::Token const & token)
|
|
||||||
{
|
|
||||||
switch(token.type)
|
|
||||||
{
|
|
||||||
case Token::TokenType::ImmediateInteger:
|
|
||||||
type = ValueType::ImmediateInteger;
|
|
||||||
dataType = ValueDataType::Immediate;
|
|
||||||
data = std::get<int>(token.data);
|
|
||||||
break;
|
|
||||||
|
|
||||||
case Token::TokenType::Register:
|
|
||||||
type = ValueType::Register;
|
|
||||||
dataType = ValueDataType::Register;
|
|
||||||
data = static_cast<int>(std::get<Token::RegisterType>(token.data));
|
|
||||||
break;
|
|
||||||
|
|
||||||
case Token::TokenType::Memory:
|
|
||||||
type = ValueType::MemoryLocation;
|
|
||||||
if (token.valueType == Token::TokenValueType::Integer)
|
|
||||||
{
|
|
||||||
dataType = ValueDataType::Immediate;
|
|
||||||
data = std::get<int>(token.data);
|
|
||||||
}
|
|
||||||
else if(token.valueType == Token::TokenValueType::Register)
|
|
||||||
{
|
|
||||||
dataType = ValueDataType::Register;
|
|
||||||
data = static_cast<int>(std::get<Token::RegisterType>(token.data));
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
throw Internal::BadTokenForValue(token);
|
|
||||||
}
|
|
||||||
break;
|
|
||||||
|
|
||||||
default:
|
|
||||||
throw Internal::BadTokenForValue(token);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
39
src/main.cpp
39
src/main.cpp
@@ -6,13 +6,22 @@
|
|||||||
int main(int argc, char ** argv)
|
int main(int argc, char ** argv)
|
||||||
{
|
{
|
||||||
std::string inputFile;
|
std::string inputFile;
|
||||||
unsigned memorySize = 1024;
|
unsigned memorySize = 4096;
|
||||||
bool printSubstitutions = false;
|
bool printSubstitutions = false, printTokens = false, printBytes = false;
|
||||||
bool printTokens = false;
|
bool execute = false, compile = false;
|
||||||
|
std::string outputFile("program.bin");
|
||||||
|
|
||||||
auto cli = (
|
auto cli = (
|
||||||
clipp::value("input wasm file").set(inputFile),
|
clipp::value("input file").set(inputFile),
|
||||||
clipp::option("-m", "--memory-size") & clipp::value("memory size", memorySize),
|
(
|
||||||
|
clipp::required("-e", "--execute").set(execute),
|
||||||
|
clipp::option("-m", "--memory-size") & clipp::value("memory size in bytes (defaults to 4096)", memorySize),
|
||||||
|
clipp::option("-pb", "--print-bytes").set(printBytes)
|
||||||
|
) |
|
||||||
|
(
|
||||||
|
clipp::required("-c", "--compile").set(compile),
|
||||||
|
clipp::option("-o", "--output-file") & clipp::value("output file", outputFile)
|
||||||
|
),
|
||||||
clipp::option("-ps", "--print-substitutions").set(printSubstitutions),
|
clipp::option("-ps", "--print-substitutions").set(printSubstitutions),
|
||||||
clipp::option("-pt", "--print-tokens").set(printTokens)
|
clipp::option("-pt", "--print-tokens").set(printTokens)
|
||||||
);
|
);
|
||||||
@@ -35,12 +44,26 @@ int main(int argc, char ** argv)
|
|||||||
wassembler.EnableTokensLogging();
|
wassembler.EnableTokensLogging();
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!wassembler.LoadFromFile(inputFile))
|
if (execute)
|
||||||
{
|
{
|
||||||
exit(1);
|
if (printBytes)
|
||||||
|
{
|
||||||
|
wassembler.EnableByteTranslationLogging();
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!wassembler.CompileAndRun(inputFile))
|
||||||
|
{
|
||||||
|
exit(1);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
wassembler.Run();
|
if (compile)
|
||||||
|
{
|
||||||
|
if (!wassembler.CompileToFile(inputFile, outputFile))
|
||||||
|
{
|
||||||
|
exit(1);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
@@ -7,9 +7,4 @@ namespace Token
|
|||||||
errorMsg(msg)
|
errorMsg(msg)
|
||||||
{
|
{
|
||||||
}
|
}
|
||||||
|
|
||||||
MissingEndOfString::MissingEndOfString(Token const & token)
|
|
||||||
: TokenizationError(token, "Missing string terminator (\")")
|
|
||||||
{
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
@@ -86,9 +86,14 @@ namespace Token
|
|||||||
return Token(TokenType::StatementEnd, true, lineNumber, lineColumn);
|
return Token(TokenType::StatementEnd, true, lineNumber, lineColumn);
|
||||||
}
|
}
|
||||||
|
|
||||||
Token Token::CreateLabelToken(std::string const & string, int const lineNumber, int const lineColumn)
|
Token Token::CreateLabelDefinitionToken(std::string const & string, int const lineNumber, int const lineColumn)
|
||||||
{
|
{
|
||||||
return Token(TokenType::Label, string, true, lineNumber, lineColumn);
|
return Token(TokenType::LabelDefinition, string, true, lineNumber, lineColumn);
|
||||||
|
}
|
||||||
|
|
||||||
|
Token Token::CreateLabelArgumentToken(std::string const & string, int const lineNumber, int const lineColumn)
|
||||||
|
{
|
||||||
|
return Token(TokenType::LabelArgument, string, true, lineNumber, lineColumn);
|
||||||
}
|
}
|
||||||
|
|
||||||
Token Token::CreateImmediateValueToken(int const value, int const lineNumber, int const lineColumn)
|
Token Token::CreateImmediateValueToken(int const value, int const lineNumber, int const lineColumn)
|
||||||
@@ -121,6 +126,35 @@ namespace Token
|
|||||||
return Token(TokenType::Memory, value, true, lineNumber, lineColumn);
|
return Token(TokenType::Memory, value, true, lineNumber, lineColumn);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
std::string Token::GetName() const
|
||||||
|
{
|
||||||
|
switch(type)
|
||||||
|
{
|
||||||
|
case TokenType::ImmediateInteger:
|
||||||
|
return "immediate value";
|
||||||
|
|
||||||
|
case TokenType::Operand:
|
||||||
|
return "operand";
|
||||||
|
|
||||||
|
case TokenType::Register:
|
||||||
|
return "register";
|
||||||
|
|
||||||
|
case TokenType::StatementEnd:
|
||||||
|
return "end of statement";
|
||||||
|
|
||||||
|
case TokenType::LabelDefinition:
|
||||||
|
case TokenType::LabelArgument:
|
||||||
|
return "label";
|
||||||
|
|
||||||
|
case TokenType::Memory:
|
||||||
|
return "memory location";
|
||||||
|
|
||||||
|
case TokenType::Unknown:
|
||||||
|
default:
|
||||||
|
return "UNKNOWN";
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
void Token::Print() const
|
void Token::Print() const
|
||||||
{
|
{
|
||||||
std::putc(' ', stdout);
|
std::putc(' ', stdout);
|
||||||
@@ -183,8 +217,12 @@ namespace Token
|
|||||||
std::printf("EOS");
|
std::printf("EOS");
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case TokenType::Label:
|
case TokenType::LabelDefinition:
|
||||||
std::printf("LABEL=%s", std::get<std::string>(data).c_str());
|
std::printf("LABEL_DEF=%s", std::get<std::string>(data).c_str());
|
||||||
|
break;
|
||||||
|
|
||||||
|
case TokenType::LabelArgument:
|
||||||
|
std::printf("LABEL_ARG=%s", std::get<std::string>(data).c_str());
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case TokenType::Memory:
|
case TokenType::Memory:
|
||||||
|
|||||||
@@ -178,7 +178,7 @@ namespace Token
|
|||||||
|
|
||||||
case ':':
|
case ':':
|
||||||
// TODO check if label is an Operand?
|
// TODO check if label is an Operand?
|
||||||
return Token::CreateLabelToken(
|
return Token::CreateLabelDefinitionToken(
|
||||||
string.substr(0, string.size() - 1),
|
string.substr(0, string.size() - 1),
|
||||||
lineNumber,
|
lineNumber,
|
||||||
lineColumn);
|
lineColumn);
|
||||||
@@ -196,7 +196,7 @@ namespace Token
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Last resort: it must be a jump target
|
// Last resort: it must be a jump target
|
||||||
return Token::CreateLabelToken(string, lineNumber, lineColumn);
|
return Token::CreateLabelArgumentToken(string, lineNumber, lineColumn);
|
||||||
}
|
}
|
||||||
|
|
||||||
void Tokenizer::Tokenize(
|
void Tokenizer::Tokenize(
|
||||||
|
|||||||
@@ -37,4 +37,28 @@ namespace Utils
|
|||||||
|
|
||||||
return src.substr(pos);
|
return src.substr(pos);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
namespace Bytes
|
||||||
|
{
|
||||||
|
void Write(
|
||||||
|
int const value,
|
||||||
|
std::vector<std::uint8_t> & vec,
|
||||||
|
std::size_t const pos)
|
||||||
|
{
|
||||||
|
vec[pos] = value & 0xFF;
|
||||||
|
vec[pos + 1] = (value >> 8) & 0xFF;
|
||||||
|
vec[pos + 2] = (value >> 16) & 0xFF;
|
||||||
|
vec[pos + 3] = (value >> 24) & 0xFF;
|
||||||
|
}
|
||||||
|
|
||||||
|
int Read(std::vector<std::uint8_t> const & vec, std::size_t const pos)
|
||||||
|
{
|
||||||
|
int value = vec[pos];
|
||||||
|
value |= static_cast<int>(vec[pos + 1]) << 8;
|
||||||
|
value |= static_cast<int>(vec[pos + 2]) << 16;
|
||||||
|
value |= static_cast<int>(vec[pos + 3]) << 24;
|
||||||
|
|
||||||
|
return value;
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
@@ -1,5 +1,7 @@
|
|||||||
|
#include <compile/compiler.hpp>
|
||||||
|
#include <compile/errors.hpp>
|
||||||
|
#include <execute/error.hpp>
|
||||||
#include <fstream>
|
#include <fstream>
|
||||||
#include <interpret/errors.hpp>
|
|
||||||
#include <preprocessor/preprocessor.hpp>
|
#include <preprocessor/preprocessor.hpp>
|
||||||
#include <token/errors.hpp>
|
#include <token/errors.hpp>
|
||||||
#include <wassembler.hpp>
|
#include <wassembler.hpp>
|
||||||
@@ -19,18 +21,13 @@ void PrintBadToken(Token::Token const & token, std::vector<std::string> const &
|
|||||||
std::puts("^");
|
std::puts("^");
|
||||||
}
|
}
|
||||||
|
|
||||||
void PrintTokenError(Interpret::InterpretationError const & err, std::vector<std::string> const & lines)
|
|
||||||
{
|
|
||||||
PrintBadToken(err.errorToken, lines);
|
|
||||||
}
|
|
||||||
|
|
||||||
void PrintTokenError(Token::TokenizationError const & err, std::vector<std::string> const & lines)
|
void PrintTokenError(Token::TokenizationError const & err, std::vector<std::string> const & lines)
|
||||||
{
|
{
|
||||||
std::printf("%s ", err.errorMsg.c_str());
|
std::printf("%s ", err.errorMsg.c_str());
|
||||||
PrintBadToken(err.errorToken, lines);
|
PrintBadToken(err.errorToken, lines);
|
||||||
}
|
}
|
||||||
|
|
||||||
bool Wassembler::LoadLinesFromFile(std::string const & filePath, std::vector<std::string> & lines) const
|
bool Wassembler::LoadTextFile(std::string const & filePath, std::vector<std::string> & lines) const
|
||||||
{
|
{
|
||||||
std::ifstream input(filePath);
|
std::ifstream input(filePath);
|
||||||
if (!input.is_open())
|
if (!input.is_open())
|
||||||
@@ -48,7 +45,19 @@ bool Wassembler::LoadLinesFromFile(std::string const & filePath, std::vector<std
|
|||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
bool Wassembler::LoadTokens(std::vector<std::string> const & lines, std::vector<Token::Token> & tokens) const
|
bool Wassembler::Preprocess(std::vector<std::string> & lines) const
|
||||||
|
{
|
||||||
|
Preprocessor preprocessor;
|
||||||
|
preprocessor.process(lines);
|
||||||
|
if (printSubstitutions)
|
||||||
|
{
|
||||||
|
preprocessor.printSubstitutions();
|
||||||
|
}
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool Wassembler::Tokenize(std::vector<std::string> const & lines, std::vector<Token::Token> & tokens) const
|
||||||
{
|
{
|
||||||
Token::Tokenizer tokenizer;
|
Token::Tokenizer tokenizer;
|
||||||
bool tokenizationError = false;
|
bool tokenizationError = false;
|
||||||
@@ -98,9 +107,64 @@ bool Wassembler::LoadTokens(std::vector<std::string> const & lines, std::vector<
|
|||||||
return !(syntaxError || tokenizationError);
|
return !(syntaxError || tokenizationError);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
bool Wassembler::CompileToBytes(
|
||||||
|
std::vector<Token::Token> const & tokens,
|
||||||
|
std::vector<std::string> const & lines,
|
||||||
|
std::vector<std::uint8_t> & bytes) const
|
||||||
|
{
|
||||||
|
Compile::Compiler compiler;
|
||||||
|
try
|
||||||
|
{
|
||||||
|
compiler.Compile(tokens, bytes);
|
||||||
|
}
|
||||||
|
catch(Compile::CompilationError & e)
|
||||||
|
{
|
||||||
|
std::printf("Semantic error ");
|
||||||
|
PrintBadToken(e.errorToken, lines);
|
||||||
|
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
void Wassembler::ExecuteCode(std::vector<uint8_t> const & bytes)
|
||||||
|
{
|
||||||
|
vm.LoadCode(bytes, printTranslatedBytes);
|
||||||
|
// TODO clear memory?
|
||||||
|
vm.Run();
|
||||||
|
}
|
||||||
|
|
||||||
|
bool Wassembler::CompileFile(
|
||||||
|
std::string const & filePath,
|
||||||
|
std::vector<std::uint8_t> & bytes) const
|
||||||
|
{
|
||||||
|
std::vector<std::string> lines;
|
||||||
|
if (!LoadTextFile(filePath, lines))
|
||||||
|
{
|
||||||
|
std::printf("Error: Cannot open file %s for reading", filePath.c_str());
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
if(!Preprocess(lines))
|
||||||
|
{
|
||||||
|
std::puts("Aborting due to preprocessor error(s)");
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
std::vector<Token::Token> tokens;
|
||||||
|
if (!Tokenize(lines, tokens) || !CompileToBytes(tokens, lines, bytes))
|
||||||
|
{
|
||||||
|
std::puts("Aborting due to syntax error(s)");
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
void Wassembler::SetMemorySize(unsigned const size)
|
void Wassembler::SetMemorySize(unsigned const size)
|
||||||
{
|
{
|
||||||
config.memorySize = size;
|
vm.SetMemorySize(size);
|
||||||
}
|
}
|
||||||
|
|
||||||
void Wassembler::EnableSubstitutionsLogging()
|
void Wassembler::EnableSubstitutionsLogging()
|
||||||
@@ -113,50 +177,54 @@ void Wassembler::EnableTokensLogging()
|
|||||||
printTokens = true;
|
printTokens = true;
|
||||||
}
|
}
|
||||||
|
|
||||||
bool Wassembler::LoadFromFile(std::string const & filePath)
|
void Wassembler::EnableByteTranslationLogging()
|
||||||
|
{
|
||||||
|
printTranslatedBytes = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool Wassembler::CompileAndRun(std::string const & filePath)
|
||||||
{
|
{
|
||||||
std::vector<std::string> lines;
|
std::vector<std::string> lines;
|
||||||
if (!LoadLinesFromFile(filePath, lines))
|
std::vector<std::uint8_t> bytes;
|
||||||
|
if (!CompileFile(filePath, bytes))
|
||||||
{
|
{
|
||||||
std::printf("Error: Cannot open file %s for reading", filePath.c_str());
|
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
Preprocessor preprocessor;
|
|
||||||
preprocessor.process(lines);
|
|
||||||
if (printSubstitutions)
|
|
||||||
{
|
|
||||||
preprocessor.printSubstitutions();
|
|
||||||
}
|
|
||||||
|
|
||||||
std::vector<Token::Token> tokens;
|
|
||||||
if (!LoadTokens(lines, tokens))
|
|
||||||
{
|
|
||||||
std::puts("Aborting due to syntax error(s)");
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
Interpret::Interpreter interpreter;
|
|
||||||
auto codePtr = std::make_unique<Interpret::Code>();
|
|
||||||
try
|
try
|
||||||
{
|
{
|
||||||
interpreter.Interpret(tokens, *codePtr);
|
ExecuteCode(bytes);
|
||||||
}
|
}
|
||||||
catch(Interpret::InterpretationError & e)
|
catch (Execute::RuntimeError const & e)
|
||||||
{
|
{
|
||||||
std::printf("Semantic error ");
|
std::puts(e.GetMessage().c_str());
|
||||||
PrintBadToken(e.errorToken, lines);
|
std::puts("Aborting due to runtime error(s)");
|
||||||
std::puts("Aborting due to semantic error(s)");
|
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
vm.LoadCode(std::move(codePtr));
|
|
||||||
vm.LoadConfiguration(config);
|
|
||||||
|
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
void Wassembler::Run()
|
bool Wassembler::CompileToFile(
|
||||||
|
std::string const & inputFilePath,
|
||||||
|
std::string const & outputFilePath)
|
||||||
{
|
{
|
||||||
vm.Run();
|
std::vector<std::uint8_t> bytes;
|
||||||
|
if (!CompileFile(inputFilePath, bytes))
|
||||||
|
{
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
std::ofstream output(outputFilePath, std::ios::binary | std::ios::trunc);
|
||||||
|
if (!output.is_open())
|
||||||
|
{
|
||||||
|
std::printf("Error: Cannot open file %s for writing", outputFilePath.c_str());
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
for(std::size_t i = 0; i < bytes.size(); ++i)
|
||||||
|
{
|
||||||
|
output << bytes[i];
|
||||||
|
}
|
||||||
|
|
||||||
|
return true;
|
||||||
}
|
}
|
||||||
Reference in New Issue
Block a user