Format with clang
This commit is contained in:
107
.clang-format
Normal file
107
.clang-format
Normal file
@@ -0,0 +1,107 @@
|
|||||||
|
---
|
||||||
|
Language: Cpp
|
||||||
|
|
||||||
|
DisableFormat: false
|
||||||
|
Standard: Latest
|
||||||
|
BasedOnStyle: WebKit
|
||||||
|
TabWidth: 4
|
||||||
|
IndentWidth: 4
|
||||||
|
ContinuationIndentWidth: 4
|
||||||
|
ConstructorInitializerIndentWidth: 1
|
||||||
|
UseTab: Never
|
||||||
|
ColumnLimit: 120
|
||||||
|
|
||||||
|
AccessModifierOffset: -4
|
||||||
|
AlignAfterOpenBracket: AlwaysBreak
|
||||||
|
AlignConsecutiveAssignments: false
|
||||||
|
AlignConsecutiveDeclarations: false
|
||||||
|
AlignConsecutiveMacros: false
|
||||||
|
AlignEscapedNewlines: DontAlign
|
||||||
|
AlignOperands: false
|
||||||
|
AlignTrailingComments: false
|
||||||
|
AllowAllArgumentsOnNextLine: false
|
||||||
|
AllowAllConstructorInitializersOnNextLine: false
|
||||||
|
AllowAllParametersOfDeclarationOnNextLine: false
|
||||||
|
AllowShortBlocksOnASingleLine: Never
|
||||||
|
AllowShortCaseLabelsOnASingleLine: false
|
||||||
|
AllowShortLambdasOnASingleLine: Inline
|
||||||
|
AllowShortLoopsOnASingleLine: false
|
||||||
|
AlwaysBreakAfterReturnType: None
|
||||||
|
AlwaysBreakBeforeMultilineStrings: false
|
||||||
|
AlwaysBreakTemplateDeclarations: No
|
||||||
|
BinPackArguments: false
|
||||||
|
BinPackParameters: false
|
||||||
|
|
||||||
|
# Configure each individual brace in BraceWrapping
|
||||||
|
BreakBeforeBraces: Custom
|
||||||
|
# Control of individual brace wrapping cases
|
||||||
|
BraceWrapping:
|
||||||
|
AfterCaseLabel: true
|
||||||
|
AfterClass: false
|
||||||
|
AfterControlStatement: Always
|
||||||
|
AfterEnum: true
|
||||||
|
AfterFunction: true
|
||||||
|
AfterNamespace: true
|
||||||
|
AfterStruct: true
|
||||||
|
AfterUnion: true
|
||||||
|
AfterExternBlock: true
|
||||||
|
BeforeCatch: true
|
||||||
|
BeforeElse: true
|
||||||
|
BeforeLambdaBody: true
|
||||||
|
IndentBraces: false
|
||||||
|
SplitEmptyFunction: false
|
||||||
|
SplitEmptyRecord: false
|
||||||
|
SplitEmptyNamespace: true
|
||||||
|
|
||||||
|
BreakAfterJavaFieldAnnotations: true
|
||||||
|
BreakBeforeTernaryOperators: false
|
||||||
|
BreakConstructorInitializers: BeforeColon
|
||||||
|
BreakInheritanceList: BeforeColon
|
||||||
|
BreakStringLiterals: false
|
||||||
|
CommentPragmas: "^ IWYU pragma:"
|
||||||
|
CompactNamespaces: false
|
||||||
|
ConstructorInitializerAllOnOneLineOrOnePerLine: false
|
||||||
|
Cpp11BracedListStyle: true
|
||||||
|
FixNamespaceComments: false
|
||||||
|
IndentCaseLabels: true
|
||||||
|
IndentPPDirectives: BeforeHash
|
||||||
|
IndentWrappedFunctionNames: false
|
||||||
|
KeepEmptyLinesAtTheStartOfBlocks: false
|
||||||
|
MacroBlockBegin: ""
|
||||||
|
MacroBlockEnd: ""
|
||||||
|
MaxEmptyLinesToKeep: 1
|
||||||
|
NamespaceIndentation: All
|
||||||
|
PenaltyBreakBeforeFirstCallParameter: 19
|
||||||
|
PenaltyBreakComment: 300
|
||||||
|
PenaltyBreakFirstLessLess: 120
|
||||||
|
PenaltyBreakString: 1000
|
||||||
|
|
||||||
|
PenaltyExcessCharacter: 1000000
|
||||||
|
PenaltyReturnTypeOnItsOwnLine: 60
|
||||||
|
PointerAlignment: Middle
|
||||||
|
# QualifierAlignment: Right # Only supported in clang-format 14+
|
||||||
|
# ReferenceAlignmentStyle: Middle # Only supported in clang-format 14+
|
||||||
|
SortIncludes: true
|
||||||
|
SortUsingDeclarations: true
|
||||||
|
SpaceAfterCStyleCast: false
|
||||||
|
SpaceAfterLogicalNot: false
|
||||||
|
SpaceAfterTemplateKeyword: false
|
||||||
|
SpaceBeforeAssignmentOperators: true
|
||||||
|
SpaceBeforeCpp11BracedList: true
|
||||||
|
SpaceBeforeCtorInitializerColon: true
|
||||||
|
SpaceBeforeInheritanceColon: true
|
||||||
|
SpaceBeforeParens: Never
|
||||||
|
SpaceBeforeRangeBasedForLoopColon: false
|
||||||
|
SpaceInEmptyParentheses: false
|
||||||
|
SpacesBeforeTrailingComments: 1
|
||||||
|
SpacesInAngles: false
|
||||||
|
SpacesInCStyleCastParentheses: false
|
||||||
|
SpacesInContainerLiterals: false
|
||||||
|
SpacesInParentheses: false
|
||||||
|
SpacesInSquareBrackets: false
|
||||||
|
|
||||||
|
# Comments are for developers, they should arrange them
|
||||||
|
ReflowComments: false
|
||||||
|
|
||||||
|
IncludeBlocks: Preserve
|
||||||
|
---
|
||||||
9
bin/fibonacci.wasm
Normal file
9
bin/fibonacci.wasm
Normal file
@@ -0,0 +1,9 @@
|
|||||||
|
# Get a number from STDIN
|
||||||
|
|
||||||
|
# Clobbers registers
|
||||||
|
get_user_integer:
|
||||||
|
seti [STRING_STORAGE]
|
||||||
|
|
||||||
|
seti %A $0;
|
||||||
|
seti %B $100;
|
||||||
|
int $5;
|
||||||
5
compile_flags.txt
Normal file
5
compile_flags.txt
Normal file
@@ -0,0 +1,5 @@
|
|||||||
|
-xc++
|
||||||
|
-Wall
|
||||||
|
-Wextra
|
||||||
|
-Iinclude/
|
||||||
|
-std=c++17
|
||||||
@@ -2,24 +2,19 @@
|
|||||||
#include <string>
|
#include <string>
|
||||||
#include <token/token.hpp>
|
#include <token/token.hpp>
|
||||||
#include <unordered_map>
|
#include <unordered_map>
|
||||||
#include <vector>
|
|
||||||
#include <utility>
|
#include <utility>
|
||||||
|
#include <vector>
|
||||||
|
|
||||||
namespace Compile
|
namespace Compile
|
||||||
{
|
{
|
||||||
class Compiler
|
class Compiler {
|
||||||
{
|
private:
|
||||||
private:
|
std::unordered_map<std::string, std::size_t> jumpLabelLocations;
|
||||||
std::unordered_map<std::string, std::size_t> jumpLabelLocations;
|
std::vector<std::pair<Token::Token, std::size_t>> unresolvedJumpLabels;
|
||||||
std::vector<std::pair<Token::Token, std::size_t>> unresolvedJumpLabels;
|
|
||||||
|
|
||||||
void InsertAsBytes(
|
void InsertAsBytes(Token::Token const & token, std::vector<std::uint8_t> & bytes);
|
||||||
Token::Token const & token,
|
|
||||||
std::vector<std::uint8_t> & bytes);
|
|
||||||
|
|
||||||
public:
|
public:
|
||||||
bool Compile(
|
bool Compile(std::vector<Token::Token> const & tokens, std::vector<std::uint8_t> & bytes);
|
||||||
std::vector<Token::Token> const & tokens,
|
};
|
||||||
std::vector<std::uint8_t> & bytes);
|
|
||||||
};
|
|
||||||
}
|
}
|
||||||
@@ -4,25 +4,24 @@
|
|||||||
|
|
||||||
namespace Compile
|
namespace Compile
|
||||||
{
|
{
|
||||||
class CompilationError
|
class CompilationError {
|
||||||
{
|
public:
|
||||||
public:
|
Token::Token errorToken;
|
||||||
Token::Token errorToken;
|
|
||||||
|
|
||||||
CompilationError(std::string const & message, Token::Token const & token);
|
CompilationError(std::string const & message, Token::Token const & token);
|
||||||
|
|
||||||
static CompilationError CreateExpectedArgumentError(Token::Token const & token);
|
static CompilationError CreateExpectedArgumentError(Token::Token const & token);
|
||||||
static CompilationError CreateExpectedLabelError(Token::Token const & token);
|
static CompilationError CreateExpectedLabelError(Token::Token const & token);
|
||||||
static CompilationError CreateExpectedImmediateError(Token::Token const & token);
|
static CompilationError CreateExpectedImmediateError(Token::Token const & token);
|
||||||
static CompilationError CreateExpectedImmediateOrRegisterOrMemory(Token::Token const & token);
|
static CompilationError CreateExpectedImmediateOrRegisterOrMemory(Token::Token const & token);
|
||||||
static CompilationError CreateExpectedRegisterError(Token::Token const & token);
|
static CompilationError CreateExpectedRegisterError(Token::Token const & token);
|
||||||
static CompilationError CreateExpectedRegisterOrMemoryError(Token::Token const & token);
|
static CompilationError CreateExpectedRegisterOrMemoryError(Token::Token const & token);
|
||||||
|
|
||||||
static CompilationError CreateExpectedOperandError(Token::Token const & token);
|
static CompilationError CreateExpectedOperandError(Token::Token const & token);
|
||||||
static CompilationError CreateTooManyArgumentsError(Token::Token const & token);
|
static CompilationError CreateTooManyArgumentsError(Token::Token const & token);
|
||||||
static CompilationError CreateTooFewArgumentsError(Token::Token const & token);
|
static CompilationError CreateTooFewArgumentsError(Token::Token const & token);
|
||||||
static CompilationError CreateExpectedEndOfStatementError(Token::Token const & token);
|
static CompilationError CreateExpectedEndOfStatementError(Token::Token const & token);
|
||||||
static CompilationError CreateDuplicateLabelError(Token::Token const & token);
|
static CompilationError CreateDuplicateLabelError(Token::Token const & token);
|
||||||
static CompilationError CreateNonExistingLabelError(Token::Token const & token);
|
static CompilationError CreateNonExistingLabelError(Token::Token const & token);
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
@@ -1,37 +1,34 @@
|
|||||||
#pragma once
|
#pragma once
|
||||||
#include <variant>
|
|
||||||
#include <execute/bytecode.hpp>
|
#include <execute/bytecode.hpp>
|
||||||
#include <execute/error.hpp>
|
#include <execute/error.hpp>
|
||||||
#include <execute/state.hpp>
|
#include <execute/state.hpp>
|
||||||
#include <stdexcept>
|
#include <stdexcept>
|
||||||
|
#include <variant>
|
||||||
#include <vector>
|
#include <vector>
|
||||||
|
|
||||||
namespace Execute
|
namespace Execute
|
||||||
{
|
{
|
||||||
enum class ArgumentType
|
enum class ArgumentType
|
||||||
{
|
{
|
||||||
Immediate,
|
Immediate,
|
||||||
Register,
|
Register,
|
||||||
Memory
|
Memory
|
||||||
};
|
};
|
||||||
|
|
||||||
class ArgumentValue
|
class ArgumentValue {
|
||||||
{
|
private:
|
||||||
private:
|
ArgumentType type;
|
||||||
ArgumentType type;
|
ArgumentType memoryValueType;
|
||||||
ArgumentType memoryValueType;
|
std::variant<RegisterByte, int> data;
|
||||||
std::variant<RegisterByte, int> data;
|
|
||||||
|
|
||||||
int & GetRegister(State & state) const;
|
int & GetRegister(State & state) const;
|
||||||
std::uint8_t * GetMemory(State & state) const;
|
std::uint8_t * GetMemory(State & state) const;
|
||||||
|
|
||||||
public:
|
public:
|
||||||
void Write(int const value, State & state) const;
|
void Write(int const value, State & state) const;
|
||||||
int Read(State & state) const;
|
int Read(State & state) const;
|
||||||
|
|
||||||
// Returns the size of the argument in bytes
|
// Returns the size of the argument in bytes
|
||||||
std::size_t Parse(
|
std::size_t Parse(std::vector<std::uint8_t> const & memory, std::size_t const pos);
|
||||||
std::vector<std::uint8_t> const & memory,
|
};
|
||||||
std::size_t const pos);
|
|
||||||
};
|
|
||||||
}
|
}
|
||||||
@@ -3,41 +3,41 @@
|
|||||||
|
|
||||||
namespace Execute
|
namespace Execute
|
||||||
{
|
{
|
||||||
enum class InstructionByte : std::uint8_t
|
enum class InstructionByte : std::uint8_t
|
||||||
{
|
{
|
||||||
NOOP = 0,
|
NOOP = 0,
|
||||||
/* Integer functions */
|
/* Integer functions */
|
||||||
ADD_INTEGER,
|
ADD_INTEGER,
|
||||||
SUBTRACT_INTEGER,
|
SUBTRACT_INTEGER,
|
||||||
DIVIDE_INTEGER,
|
DIVIDE_INTEGER,
|
||||||
MULTIPLY_INTEGER,
|
MULTIPLY_INTEGER,
|
||||||
SHIFT_LEFT_INTEGER,
|
SHIFT_LEFT_INTEGER,
|
||||||
SHIFT_RIGHT_INTEGER,
|
SHIFT_RIGHT_INTEGER,
|
||||||
SET_INTEGER,
|
SET_INTEGER,
|
||||||
/* Control flow */
|
/* Control flow */
|
||||||
JUMP,
|
JUMP,
|
||||||
INTERRUPT,
|
INTERRUPT,
|
||||||
CALL,
|
CALL,
|
||||||
RETURN,
|
RETURN,
|
||||||
EXIT,
|
EXIT,
|
||||||
LESS_THAN_INTEGER,
|
LESS_THAN_INTEGER,
|
||||||
GREATER_THAN_INTEGER,
|
GREATER_THAN_INTEGER,
|
||||||
EQUALS_INTEGER,
|
EQUALS_INTEGER,
|
||||||
/* Memory */
|
/* Memory */
|
||||||
POP_INTEGER,
|
POP_INTEGER,
|
||||||
PUSH_INTEGER,
|
PUSH_INTEGER,
|
||||||
/* Values */
|
/* Values */
|
||||||
IMMEDIATE_INTEGER,
|
IMMEDIATE_INTEGER,
|
||||||
REGISTER,
|
REGISTER,
|
||||||
MEMORY_OP,
|
MEMORY_OP,
|
||||||
LABEL,
|
LABEL,
|
||||||
};
|
};
|
||||||
|
|
||||||
enum class RegisterByte : std::uint8_t
|
enum class RegisterByte : std::uint8_t
|
||||||
{
|
{
|
||||||
A = 1,
|
A = 1,
|
||||||
B,
|
B,
|
||||||
C,
|
C,
|
||||||
D
|
D
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
@@ -3,48 +3,40 @@
|
|||||||
|
|
||||||
namespace Execute
|
namespace Execute
|
||||||
{
|
{
|
||||||
class RuntimeError
|
class RuntimeError {
|
||||||
{
|
protected:
|
||||||
protected:
|
std::string message;
|
||||||
std::string message;
|
std::size_t byteLocation;
|
||||||
std::size_t byteLocation;
|
|
||||||
|
|
||||||
public:
|
public:
|
||||||
std::string const & GetMessage() const;
|
std::string const & GetMessage() const;
|
||||||
|
|
||||||
RuntimeError();
|
RuntimeError();
|
||||||
RuntimeError(std::string const & what, std::size_t const byteLocation);
|
RuntimeError(std::string const & what, std::size_t const byteLocation);
|
||||||
};
|
};
|
||||||
|
|
||||||
class InterruptIndexOutOfRange : public RuntimeError
|
class InterruptIndexOutOfRange : public RuntimeError {
|
||||||
{
|
public:
|
||||||
public:
|
InterruptIndexOutOfRange(std::size_t const location, int const index);
|
||||||
InterruptIndexOutOfRange(std::size_t const location, int const index);
|
};
|
||||||
};
|
|
||||||
|
|
||||||
class AttemptedWriteToImmediate : public RuntimeError
|
class AttemptedWriteToImmediate : public RuntimeError {
|
||||||
{
|
public:
|
||||||
public:
|
AttemptedWriteToImmediate(std::size_t const location);
|
||||||
AttemptedWriteToImmediate(std::size_t const location);
|
};
|
||||||
};
|
|
||||||
|
|
||||||
class NonExecutableInstruction : public RuntimeError
|
class NonExecutableInstruction : public RuntimeError {
|
||||||
{
|
public:
|
||||||
public:
|
NonExecutableInstruction(std::size_t const location);
|
||||||
NonExecutableInstruction(std::size_t const location);
|
};
|
||||||
};
|
|
||||||
|
|
||||||
class NonArgumentByte : public RuntimeError
|
class NonArgumentByte : public RuntimeError {
|
||||||
{
|
public:
|
||||||
public:
|
NonArgumentByte(std::size_t const location);
|
||||||
NonArgumentByte(std::size_t const location);
|
};
|
||||||
};
|
|
||||||
|
|
||||||
class OutOfMemory : public RuntimeError
|
class OutOfMemory : public RuntimeError {
|
||||||
{
|
public:
|
||||||
public:
|
OutOfMemory(std::size_t const requiredMemorySize, std::size_t const actualMemorySize);
|
||||||
OutOfMemory(
|
};
|
||||||
std::size_t const requiredMemorySize,
|
|
||||||
std::size_t const actualMemorySize);
|
|
||||||
};
|
|
||||||
}
|
}
|
||||||
@@ -5,8 +5,5 @@
|
|||||||
|
|
||||||
namespace Execute
|
namespace Execute
|
||||||
{
|
{
|
||||||
void ExecuteInterrupt(
|
void ExecuteInterrupt(int const id, Execute::Registers & registers, std::vector<std::uint8_t> & memory);
|
||||||
int const id,
|
|
||||||
Execute::Registers & registers,
|
|
||||||
std::vector<std::uint8_t> & memory);
|
|
||||||
}
|
}
|
||||||
@@ -3,12 +3,12 @@
|
|||||||
|
|
||||||
namespace Execute
|
namespace Execute
|
||||||
{
|
{
|
||||||
struct Registers
|
struct Registers
|
||||||
{
|
{
|
||||||
int A, B, C, D;
|
int A, B, C, D;
|
||||||
std::size_t programCounter;
|
std::size_t programCounter;
|
||||||
std::size_t stackPointer;
|
std::size_t stackPointer;
|
||||||
|
|
||||||
// TODO status registers?
|
// TODO status registers?
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -5,13 +5,13 @@
|
|||||||
|
|
||||||
namespace Execute
|
namespace Execute
|
||||||
{
|
{
|
||||||
struct State
|
struct State
|
||||||
{
|
{
|
||||||
bool terminated;
|
bool terminated;
|
||||||
Registers registers;
|
Registers registers;
|
||||||
std::vector<std::uint8_t> memory;
|
std::vector<std::uint8_t> memory;
|
||||||
|
|
||||||
void PushToStack(int const value);
|
void PushToStack(int const value);
|
||||||
int PopFromStack();
|
int PopFromStack();
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
@@ -6,42 +6,35 @@
|
|||||||
|
|
||||||
namespace Execute
|
namespace Execute
|
||||||
{
|
{
|
||||||
class VirtualMachine
|
class VirtualMachine {
|
||||||
{
|
private:
|
||||||
private:
|
State state;
|
||||||
State state;
|
|
||||||
|
|
||||||
void DoArithmatic(
|
void DoArithmatic(InstructionByte const instruction, std::array<ArgumentValue, 3> & arguments);
|
||||||
InstructionByte const instruction,
|
void SetInteger(std::array<ArgumentValue, 3> & arguments);
|
||||||
std::array<ArgumentValue, 3> & arguments);
|
void ExecuteJump(std::array<ArgumentValue, 3> & arguments);
|
||||||
void SetInteger(std::array<ArgumentValue, 3> & arguments);
|
void ExecuteInterrupt(std::array<ArgumentValue, 3> & arguments);
|
||||||
void ExecuteJump(std::array<ArgumentValue, 3> & arguments);
|
void ExecuteCall(std::array<ArgumentValue, 3> & arguments, std::size_t const returnByte);
|
||||||
void ExecuteInterrupt(std::array<ArgumentValue, 3> & arguments);
|
void ExecuteReturn();
|
||||||
void ExecuteCall(
|
void DoBooleanLogic(
|
||||||
std::array<ArgumentValue, 3> & arguments,
|
InstructionByte const instruction,
|
||||||
std::size_t const returnByte);
|
std::array<ArgumentValue, 3> & arguments,
|
||||||
void ExecuteReturn();
|
std::size_t const nextInstruction);
|
||||||
void DoBooleanLogic(
|
void ExecutePop(std::array<ArgumentValue, 3> & arguments);
|
||||||
InstructionByte const instruction,
|
void ExecutePush(std::array<ArgumentValue, 3> & arguments);
|
||||||
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 SetMemorySize(std::size_t const size);
|
void SetMemorySize(std::size_t const size);
|
||||||
void LoadCode(
|
void LoadCode(std::vector<std::uint8_t> const & byteCode, bool const printDecodedBytes);
|
||||||
std::vector<std::uint8_t> const & byteCode,
|
|
||||||
bool const printDecodedBytes);
|
|
||||||
|
|
||||||
State const & GetState() const;
|
State const & GetState() const;
|
||||||
Execute::InstructionByte GetCurrentInstruction() const;
|
Execute::InstructionByte GetCurrentInstruction() const;
|
||||||
|
|
||||||
bool IsTerminated() const;
|
bool IsTerminated() const;
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
@@ -2,20 +2,17 @@
|
|||||||
#include <string>
|
#include <string>
|
||||||
#include <vector>
|
#include <vector>
|
||||||
|
|
||||||
class Preprocessor
|
class Preprocessor {
|
||||||
{
|
|
||||||
private:
|
private:
|
||||||
std::vector<std::string> substitutionIdentifiers;
|
std::vector<std::string> substitutionIdentifiers;
|
||||||
std::vector<std::string> substitutionValues;
|
std::vector<std::string> substitutionValues;
|
||||||
|
|
||||||
void extractComment(std::string & line,
|
void extractComment(std::string & line, std::size_t const lineNumber, std::size_t const lineColumn);
|
||||||
std::size_t const lineNumber,
|
|
||||||
std::size_t const lineColumn);
|
|
||||||
|
|
||||||
void processLine(std::string & line, std::size_t const lineNumber);
|
void processLine(std::string & line, std::size_t const lineNumber);
|
||||||
|
|
||||||
public:
|
public:
|
||||||
void process(std::vector<std::string> & lines);
|
void process(std::vector<std::string> & lines);
|
||||||
|
|
||||||
void printSubstitutions() const;
|
void printSubstitutions() const;
|
||||||
};
|
};
|
||||||
@@ -4,10 +4,10 @@
|
|||||||
|
|
||||||
namespace Token
|
namespace Token
|
||||||
{
|
{
|
||||||
struct TokenizationError : public std::exception
|
struct TokenizationError : public std::exception
|
||||||
{
|
{
|
||||||
Token errorToken;
|
Token errorToken;
|
||||||
std::string errorMsg;
|
std::string errorMsg;
|
||||||
TokenizationError(Token const & token, std::string const & msg);
|
TokenizationError(Token const & token, std::string const & msg);
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
@@ -3,27 +3,27 @@
|
|||||||
|
|
||||||
namespace Token
|
namespace Token
|
||||||
{
|
{
|
||||||
enum class OperandType : int
|
enum class OperandType : int
|
||||||
{
|
{
|
||||||
Unknown = -1,
|
Unknown = -1,
|
||||||
AddInteger = 0,
|
AddInteger = 0,
|
||||||
SubtractInteger,
|
SubtractInteger,
|
||||||
DivideInteger,
|
DivideInteger,
|
||||||
MultiplyInteger,
|
MultiplyInteger,
|
||||||
ShiftIntegerLeft,
|
ShiftIntegerLeft,
|
||||||
ShiftIntegerRight,
|
ShiftIntegerRight,
|
||||||
Jump,
|
Jump,
|
||||||
CallFunction,
|
CallFunction,
|
||||||
ReturnFromFunction,
|
ReturnFromFunction,
|
||||||
ExitProgram,
|
ExitProgram,
|
||||||
LessThanInteger,
|
LessThanInteger,
|
||||||
GreaterThanInteger,
|
GreaterThanInteger,
|
||||||
EqualInteger,
|
EqualInteger,
|
||||||
SetInteger,
|
SetInteger,
|
||||||
Interrupt,
|
Interrupt,
|
||||||
PushInteger,
|
PushInteger,
|
||||||
PopInteger
|
PopInteger
|
||||||
};
|
};
|
||||||
|
|
||||||
OperandType GetOperandType(std::string const & op);
|
OperandType GetOperandType(std::string const & op);
|
||||||
}
|
}
|
||||||
@@ -3,14 +3,14 @@
|
|||||||
|
|
||||||
namespace Token
|
namespace Token
|
||||||
{
|
{
|
||||||
enum class RegisterType
|
enum class RegisterType
|
||||||
{
|
{
|
||||||
Unknown = -1,
|
Unknown = -1,
|
||||||
A = 0,
|
A = 0,
|
||||||
B,
|
B,
|
||||||
C,
|
C,
|
||||||
D
|
D
|
||||||
};
|
};
|
||||||
|
|
||||||
RegisterType GetRegisterType(std::string const & reg);
|
RegisterType GetRegisterType(std::string const & reg);
|
||||||
}
|
}
|
||||||
@@ -7,47 +7,58 @@
|
|||||||
|
|
||||||
namespace Token
|
namespace Token
|
||||||
{
|
{
|
||||||
enum class TokenValueType
|
enum class TokenValueType
|
||||||
{
|
{
|
||||||
None = 0,
|
None = 0,
|
||||||
Integer,
|
Integer,
|
||||||
Operand,
|
Operand,
|
||||||
Register,
|
Register,
|
||||||
String
|
String
|
||||||
};
|
};
|
||||||
|
|
||||||
struct Token
|
struct Token
|
||||||
{
|
{
|
||||||
private:
|
private:
|
||||||
Token(TokenType type, bool validness, int const lineNumber, int const lineColumn);
|
Token(TokenType type, bool validness, int const lineNumber, int const lineColumn);
|
||||||
Token(TokenType type, std::string const & string, bool validness, int const lineNumber, int const lineColumn);
|
Token(TokenType type, std::string const & string, bool validness, int const lineNumber, int const lineColumn);
|
||||||
Token(TokenType type, int value, bool validness, int const lineNumber, int const lineColumn);
|
Token(TokenType type, int value, bool validness, int const lineNumber, int const lineColumn);
|
||||||
Token(TokenType type, RegisterType const registerType, bool validness, int const lineNumber, int const lineColumn);
|
Token(
|
||||||
Token(TokenType type, OperandType const OperandType, bool validness, int const lineNumber, int const lineColumn);
|
TokenType type,
|
||||||
|
RegisterType const registerType,
|
||||||
|
bool validness,
|
||||||
|
int const lineNumber,
|
||||||
|
int const lineColumn);
|
||||||
|
Token(
|
||||||
|
TokenType type,
|
||||||
|
OperandType const OperandType,
|
||||||
|
bool validness,
|
||||||
|
int const lineNumber,
|
||||||
|
int const lineColumn);
|
||||||
|
|
||||||
public:
|
public:
|
||||||
int const lineNumber;
|
int const lineNumber;
|
||||||
int const lineColumn;
|
int const lineColumn;
|
||||||
TokenType type;
|
TokenType type;
|
||||||
TokenValueType const valueType;
|
TokenValueType const valueType;
|
||||||
bool isValid;
|
bool isValid;
|
||||||
std::variant<OperandType, RegisterType, int, std::string> data;
|
std::variant<OperandType, RegisterType, int, std::string> data;
|
||||||
std::string errorMessage;
|
std::string errorMessage;
|
||||||
|
|
||||||
Token(Token const & other);
|
Token(Token const & other);
|
||||||
|
|
||||||
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
|
||||||
static Token CreateStatementEndToken(int const lineNumber, int const lineColumn);
|
CreateErrorToken(std::string const & message, TokenType const type, int const lineNumber, int const lineColumn);
|
||||||
static Token CreateLabelDefinitionToken(std::string const & string, int const lineNumber, int const lineColumn);
|
static Token CreateStatementEndToken(int const lineNumber, int const lineColumn);
|
||||||
static Token CreateLabelArgumentToken(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 CreateImmediateValueToken(int const value, int const lineNumber, int const lineColumn);
|
static Token CreateLabelArgumentToken(std::string const & string, int const lineNumber, int const lineColumn);
|
||||||
static Token CreateRegisterToken(RegisterType const registerType, int const lineNumber, int const lineColumn);
|
static Token CreateImmediateValueToken(int const value, int const lineNumber, int const lineColumn);
|
||||||
static Token CreateOperandToken(OperandType const operandType, int const lineNumber, int const lineColumn);
|
static Token CreateRegisterToken(RegisterType const registerType, int const lineNumber, int const lineColumn);
|
||||||
static Token CreateMemoryToken(RegisterType const registerType, int const lineNumber, int const lineColumn);
|
static Token CreateOperandToken(OperandType const operandType, int const lineNumber, int const lineColumn);
|
||||||
static Token CreateMemoryToken(int const value, int const lineNumber, int const lineColumn);
|
static Token CreateMemoryToken(RegisterType const registerType, int const lineNumber, int const lineColumn);
|
||||||
|
static Token CreateMemoryToken(int const value, int const lineNumber, int const lineColumn);
|
||||||
|
|
||||||
std::string GetName() const;
|
std::string GetName() const;
|
||||||
void Print() const;
|
void Print() const;
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -5,19 +5,13 @@
|
|||||||
|
|
||||||
namespace Token
|
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
|
||||||
Token ExtractToken(
|
ExtractToken(std::string const & string, std::size_t const lineNumber, std::size_t const lineColumn) const;
|
||||||
std::string const & string,
|
|
||||||
std::size_t const lineNumber,
|
|
||||||
std::size_t const lineColumn) const;
|
|
||||||
|
|
||||||
public:
|
public:
|
||||||
void Tokenize(
|
void Tokenize(std::string const & line, std::size_t const lineNumber, std::vector<Token> & tokens);
|
||||||
std::string const & line,
|
};
|
||||||
std::size_t const lineNumber,
|
|
||||||
std::vector<Token> & tokens);
|
|
||||||
};
|
|
||||||
}
|
}
|
||||||
@@ -2,15 +2,15 @@
|
|||||||
|
|
||||||
namespace Token
|
namespace Token
|
||||||
{
|
{
|
||||||
enum class TokenType
|
enum class TokenType
|
||||||
{
|
{
|
||||||
Unknown = -1,
|
Unknown = -1,
|
||||||
Operand = 0,
|
Operand = 0,
|
||||||
ImmediateInteger,
|
ImmediateInteger,
|
||||||
Register,
|
Register,
|
||||||
StatementEnd,
|
StatementEnd,
|
||||||
LabelDefinition,
|
LabelDefinition,
|
||||||
LabelArgument,
|
LabelArgument,
|
||||||
Memory
|
Memory
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
@@ -6,25 +6,18 @@
|
|||||||
|
|
||||||
namespace Utils
|
namespace Utils
|
||||||
{
|
{
|
||||||
bool isWhitespaceCharacter(char const c);
|
bool isWhitespaceCharacter(char const c);
|
||||||
|
|
||||||
// Returns nullopt in case the value is missing its terminator character
|
// Returns nullopt in case the value is missing its terminator character
|
||||||
std::optional<std::string> getValueSurroundedBy(
|
std::optional<std::string>
|
||||||
std::string const & src,
|
getValueSurroundedBy(std::string const & src, std::size_t const pos, char const surroundingCharacter);
|
||||||
std::size_t const pos,
|
|
||||||
char const surroundingCharacter);
|
|
||||||
|
|
||||||
std::string getValueSurroundedByWhitespace(
|
std::string getValueSurroundedByWhitespace(std::string const & src, std::size_t const pos);
|
||||||
std::string const & src,
|
|
||||||
std::size_t const pos);
|
|
||||||
|
|
||||||
namespace Bytes
|
namespace Bytes
|
||||||
{
|
{
|
||||||
void Write(
|
void Write(int const value, std::vector<std::uint8_t> & vec, std::size_t const pos);
|
||||||
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);
|
int Read(std::vector<std::uint8_t> const & vec, std::size_t const pos);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -3,42 +3,33 @@
|
|||||||
#include <execute/virtualmachine.hpp>
|
#include <execute/virtualmachine.hpp>
|
||||||
#include <token/tokenizer.hpp>
|
#include <token/tokenizer.hpp>
|
||||||
|
|
||||||
class Wassembler
|
class Wassembler {
|
||||||
{
|
|
||||||
private:
|
private:
|
||||||
Execute::VirtualMachine vm;
|
Execute::VirtualMachine vm;
|
||||||
bool printSubstitutions;
|
bool printSubstitutions;
|
||||||
bool printTokens;
|
bool printTokens;
|
||||||
bool printTranslatedBytes;
|
bool printTranslatedBytes;
|
||||||
|
|
||||||
bool LoadTextFile(
|
bool LoadTextFile(std::string const & filePath, std::vector<std::string> & lines) const;
|
||||||
std::string const & filePath,
|
bool Preprocess(std::vector<std::string> & lines) const;
|
||||||
std::vector<std::string> & lines) const;
|
bool Tokenize(std::vector<std::string> const & lines, std::vector<Token::Token> & tokens) const;
|
||||||
bool Preprocess(std::vector<std::string> & lines) const;
|
bool CompileToBytes(
|
||||||
bool Tokenize(
|
std::vector<Token::Token> const & tokens,
|
||||||
std::vector<std::string> const & lines,
|
std::vector<std::string> const & lines,
|
||||||
std::vector<Token::Token> & tokens) const;
|
std::vector<std::uint8_t> & bytes) const;
|
||||||
bool CompileToBytes(
|
void ExecuteCode(std::vector<std::uint8_t> const & bytes);
|
||||||
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(
|
bool CompileFile(std::string const & filePath, std::vector<std::uint8_t> & bytes) const;
|
||||||
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();
|
void EnableByteTranslationLogging();
|
||||||
|
|
||||||
bool CompileAndRun(std::string const & filePath);
|
bool CompileAndRun(std::string const & filePath);
|
||||||
bool CompileToFile(
|
bool CompileToFile(std::string const & inputFilePath, std::string const & outputFilePath);
|
||||||
std::string const & inputFilePath,
|
|
||||||
std::string const & outputFilePath);
|
|
||||||
|
|
||||||
Wassembler() = default;
|
Wassembler() = default;
|
||||||
};
|
};
|
||||||
4
makefile
4
makefile
@@ -1,6 +1,6 @@
|
|||||||
CC = g++
|
CC = g++
|
||||||
CFLAGS = -g -std=c++17 -Wall -Iinclude #-Werror
|
CFLAGS = -g -std=c++17 -Wall -Wextra -Iinclude
|
||||||
LFLAGS = #-lsfml-graphics -lsfml-window -lsfml-system
|
LFLAGS =
|
||||||
|
|
||||||
CPPS = $(shell find src/ -name '*.cpp')
|
CPPS = $(shell find src/ -name '*.cpp')
|
||||||
OBJS = $(patsubst src/%.cpp, build/%.o, ${CPPS})
|
OBJS = $(patsubst src/%.cpp, build/%.o, ${CPPS})
|
||||||
|
|||||||
@@ -6,428 +6,417 @@
|
|||||||
|
|
||||||
namespace Compile
|
namespace Compile
|
||||||
{
|
{
|
||||||
int GetRequiredNumberOfArguments(Token::OperandType const type)
|
int GetRequiredNumberOfArguments(Token::OperandType const type)
|
||||||
{
|
{
|
||||||
switch (type)
|
switch(type)
|
||||||
{
|
{
|
||||||
case Token::OperandType::AddInteger:
|
case Token::OperandType::AddInteger:
|
||||||
case Token::OperandType::SubtractInteger:
|
case Token::OperandType::SubtractInteger:
|
||||||
case Token::OperandType::DivideInteger:
|
case Token::OperandType::DivideInteger:
|
||||||
case Token::OperandType::MultiplyInteger:
|
case Token::OperandType::MultiplyInteger:
|
||||||
case Token::OperandType::ShiftIntegerLeft:
|
case Token::OperandType::ShiftIntegerLeft:
|
||||||
case Token::OperandType::ShiftIntegerRight:
|
case Token::OperandType::ShiftIntegerRight:
|
||||||
return 3;
|
return 3;
|
||||||
|
|
||||||
case Token::OperandType::LessThanInteger:
|
case Token::OperandType::LessThanInteger:
|
||||||
case Token::OperandType::GreaterThanInteger:
|
case Token::OperandType::GreaterThanInteger:
|
||||||
case Token::OperandType::EqualInteger:
|
case Token::OperandType::EqualInteger:
|
||||||
case Token::OperandType::SetInteger:
|
case Token::OperandType::SetInteger:
|
||||||
return 2;
|
return 2;
|
||||||
|
|
||||||
case Token::OperandType::Jump:
|
case Token::OperandType::Jump:
|
||||||
case Token::OperandType::CallFunction:
|
case Token::OperandType::CallFunction:
|
||||||
case Token::OperandType::Interrupt:
|
case Token::OperandType::Interrupt:
|
||||||
case Token::OperandType::PushInteger:
|
case Token::OperandType::PushInteger:
|
||||||
case Token::OperandType::PopInteger:
|
case Token::OperandType::PopInteger:
|
||||||
return 1;
|
return 1;
|
||||||
|
|
||||||
default:
|
default:
|
||||||
std::printf("WARNING: returning default argument length of 0 for operand type %i\n", static_cast<int>(type));
|
std::printf(
|
||||||
case Token::OperandType::ReturnFromFunction:
|
"WARNING: returning default argument length of 0 for operand type %i\n",
|
||||||
case Token::OperandType::ExitProgram:
|
static_cast<int>(type));
|
||||||
return 0;
|
case Token::OperandType::ReturnFromFunction:
|
||||||
}
|
case Token::OperandType::ExitProgram:
|
||||||
}
|
return 0;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
bool IsArgumentToken(Token::Token const & t)
|
bool IsArgumentToken(Token::Token const & t)
|
||||||
{
|
{
|
||||||
return
|
return t.type == Token::TokenType::ImmediateInteger || t.type == Token::TokenType::Register
|
||||||
t.type == Token::TokenType::ImmediateInteger ||
|
|| t.type == Token::TokenType::LabelArgument || t.type == Token::TokenType::Memory;
|
||||||
t.type == Token::TokenType::Register ||
|
}
|
||||||
t.type == Token::TokenType::LabelArgument ||
|
|
||||||
t.type == Token::TokenType::Memory;
|
|
||||||
}
|
|
||||||
|
|
||||||
bool IsReadableToken(Token::Token const & t)
|
bool IsReadableToken(Token::Token const & t)
|
||||||
{
|
{
|
||||||
return
|
return t.type == Token::TokenType::ImmediateInteger || t.type == Token::TokenType::Register
|
||||||
t.type == Token::TokenType::ImmediateInteger ||
|
|| t.type == Token::TokenType::Memory;
|
||||||
t.type == Token::TokenType::Register ||
|
}
|
||||||
t.type == Token::TokenType::Memory;
|
|
||||||
}
|
|
||||||
|
|
||||||
bool IsWriteableToken(Token::Token const & t)
|
bool IsWriteableToken(Token::Token const & t)
|
||||||
{
|
{
|
||||||
return
|
return t.type == Token::TokenType::Register || t.type == Token::TokenType::Memory;
|
||||||
t.type == Token::TokenType::Register ||
|
}
|
||||||
t.type == Token::TokenType::Memory;
|
|
||||||
}
|
|
||||||
|
|
||||||
void ValidateArguments(
|
void ValidateArguments(std::vector<Token::Token> const & tokens, std::size_t const operandIndex)
|
||||||
std::vector<Token::Token> const & tokens,
|
{
|
||||||
std::size_t const operandIndex)
|
auto const operandType = std::get<Token::OperandType>(tokens[operandIndex].data);
|
||||||
{
|
switch(operandType)
|
||||||
auto const operandType = std::get<Token::OperandType>(tokens[operandIndex].data);
|
{
|
||||||
switch(operandType)
|
// 2 Read values + 1 write value
|
||||||
{
|
case Token::OperandType::AddInteger:
|
||||||
// 2 Read values + 1 write value
|
case Token::OperandType::SubtractInteger:
|
||||||
case Token::OperandType::AddInteger:
|
case Token::OperandType::DivideInteger:
|
||||||
case Token::OperandType::SubtractInteger:
|
case Token::OperandType::MultiplyInteger:
|
||||||
case Token::OperandType::DivideInteger:
|
case Token::OperandType::ShiftIntegerLeft:
|
||||||
case Token::OperandType::MultiplyInteger:
|
case Token::OperandType::ShiftIntegerRight:
|
||||||
case Token::OperandType::ShiftIntegerLeft:
|
if(!IsReadableToken(tokens[operandIndex + 1]))
|
||||||
case Token::OperandType::ShiftIntegerRight:
|
{
|
||||||
if (!IsReadableToken(tokens[operandIndex + 1]))
|
throw CompilationError::CreateExpectedImmediateOrRegisterOrMemory(tokens[operandIndex + 1]);
|
||||||
{
|
}
|
||||||
throw CompilationError::CreateExpectedImmediateOrRegisterOrMemory(tokens[operandIndex + 1]);
|
if(!IsReadableToken(tokens[operandIndex + 2]))
|
||||||
}
|
{
|
||||||
if (!IsReadableToken(tokens[operandIndex + 2]))
|
throw CompilationError::CreateExpectedImmediateOrRegisterOrMemory(tokens[operandIndex + 2]);
|
||||||
{
|
}
|
||||||
throw CompilationError::CreateExpectedImmediateOrRegisterOrMemory(tokens[operandIndex + 2]);
|
if(!IsWriteableToken(tokens[operandIndex + 3]))
|
||||||
}
|
{
|
||||||
if (!IsWriteableToken(tokens[operandIndex + 3]))
|
throw CompilationError::CreateExpectedRegisterOrMemoryError(tokens[operandIndex + 3]);
|
||||||
{
|
}
|
||||||
throw CompilationError::CreateExpectedRegisterOrMemoryError(tokens[operandIndex + 3]);
|
break;
|
||||||
}
|
|
||||||
break;
|
|
||||||
|
|
||||||
// 2 Read values
|
// 2 Read values
|
||||||
case Token::OperandType::LessThanInteger:
|
case Token::OperandType::LessThanInteger:
|
||||||
case Token::OperandType::GreaterThanInteger:
|
case Token::OperandType::GreaterThanInteger:
|
||||||
case Token::OperandType::EqualInteger:
|
case Token::OperandType::EqualInteger:
|
||||||
case Token::OperandType::SetInteger:
|
case Token::OperandType::SetInteger:
|
||||||
if (!IsReadableToken(tokens[operandIndex + 1]))
|
if(!IsReadableToken(tokens[operandIndex + 1]))
|
||||||
{
|
{
|
||||||
throw CompilationError::CreateExpectedImmediateOrRegisterOrMemory(tokens[operandIndex + 1]);
|
throw CompilationError::CreateExpectedImmediateOrRegisterOrMemory(tokens[operandIndex + 1]);
|
||||||
}
|
}
|
||||||
if (!IsReadableToken(tokens[operandIndex + 2]))
|
if(!IsReadableToken(tokens[operandIndex + 2]))
|
||||||
{
|
{
|
||||||
throw CompilationError::CreateExpectedImmediateOrRegisterOrMemory(tokens[operandIndex + 2]);
|
throw CompilationError::CreateExpectedImmediateOrRegisterOrMemory(tokens[operandIndex + 2]);
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
|
|
||||||
// 1 Label value
|
// 1 Label value
|
||||||
case Token::OperandType::Jump:
|
case Token::OperandType::Jump:
|
||||||
case Token::OperandType::CallFunction:
|
case Token::OperandType::CallFunction:
|
||||||
if (tokens[operandIndex + 1].type != Token::TokenType::LabelArgument)
|
if(tokens[operandIndex + 1].type != Token::TokenType::LabelArgument)
|
||||||
{
|
{
|
||||||
throw CompilationError::CreateExpectedLabelError(tokens[operandIndex + 1]);
|
throw CompilationError::CreateExpectedLabelError(tokens[operandIndex + 1]);
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
|
|
||||||
// 1 Read value
|
// 1 Read value
|
||||||
case Token::OperandType::Interrupt:
|
case Token::OperandType::Interrupt:
|
||||||
case Token::OperandType::PushInteger:
|
case Token::OperandType::PushInteger:
|
||||||
if (!IsReadableToken(tokens[operandIndex + 1]))
|
if(!IsReadableToken(tokens[operandIndex + 1]))
|
||||||
{
|
{
|
||||||
throw CompilationError::CreateExpectedImmediateOrRegisterOrMemory(tokens[operandIndex + 1]);
|
throw CompilationError::CreateExpectedImmediateOrRegisterOrMemory(tokens[operandIndex + 1]);
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
|
|
||||||
// 1 Write value
|
// 1 Write value
|
||||||
case Token::OperandType::PopInteger:
|
case Token::OperandType::PopInteger:
|
||||||
if (!IsWriteableToken(tokens[operandIndex + 1]))
|
if(!IsWriteableToken(tokens[operandIndex + 1]))
|
||||||
{
|
{
|
||||||
throw CompilationError::CreateExpectedRegisterOrMemoryError(tokens[operandIndex + 1]);
|
throw CompilationError::CreateExpectedRegisterOrMemoryError(tokens[operandIndex + 1]);
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
|
|
||||||
default:
|
default:
|
||||||
throw std::runtime_error("Unimplemented operandType case in ValidateArguments");
|
throw std::runtime_error("Unimplemented operandType case in ValidateArguments");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
Execute::RegisterByte GetByteCodeRegister(Token::RegisterType const v)
|
Execute::RegisterByte GetByteCodeRegister(Token::RegisterType const v)
|
||||||
{
|
{
|
||||||
switch(v)
|
switch(v)
|
||||||
{
|
{
|
||||||
case Token::RegisterType::A:
|
case Token::RegisterType::A:
|
||||||
return Execute::RegisterByte::A;
|
return Execute::RegisterByte::A;
|
||||||
|
|
||||||
case Token::RegisterType::B:
|
case Token::RegisterType::B:
|
||||||
return Execute::RegisterByte::B;
|
return Execute::RegisterByte::B;
|
||||||
|
|
||||||
case Token::RegisterType::C:
|
case Token::RegisterType::C:
|
||||||
return Execute::RegisterByte::C;
|
return Execute::RegisterByte::C;
|
||||||
|
|
||||||
case Token::RegisterType::D:
|
case Token::RegisterType::D:
|
||||||
return Execute::RegisterByte::D;
|
return Execute::RegisterByte::D;
|
||||||
|
|
||||||
default:
|
default:
|
||||||
throw std::runtime_error("Unhandled register type in GetByteCodeRegister");
|
throw std::runtime_error("Unhandled register type in GetByteCodeRegister");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void Compiler::InsertAsBytes(
|
void Compiler::InsertAsBytes(Token::Token const & token, std::vector<std::uint8_t> & bytes)
|
||||||
Token::Token const & token,
|
{
|
||||||
std::vector<std::uint8_t> & bytes)
|
switch(token.type)
|
||||||
{
|
{
|
||||||
switch(token.type)
|
case Token::TokenType::ImmediateInteger:
|
||||||
{
|
bytes.push_back(static_cast<std::uint8_t>(Execute::InstructionByte::IMMEDIATE_INTEGER));
|
||||||
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();
|
||||||
int value = std::get<int>(token.data);
|
bytes.resize(bytes.size() + 4);
|
||||||
auto const insertionIndex = bytes.size();
|
Utils::Bytes::Write(value, bytes, insertionIndex);
|
||||||
bytes.resize(bytes.size() + 4);
|
}
|
||||||
Utils::Bytes::Write(value, bytes, insertionIndex);
|
break;
|
||||||
}
|
|
||||||
break;
|
|
||||||
|
|
||||||
case Token::TokenType::Operand:
|
case Token::TokenType::Operand:
|
||||||
{
|
{
|
||||||
switch(std::get<Token::OperandType>(token.data))
|
switch(std::get<Token::OperandType>(token.data))
|
||||||
{
|
{
|
||||||
case Token::OperandType::AddInteger:
|
case Token::OperandType::AddInteger:
|
||||||
bytes.push_back(static_cast<std::uint8_t>(Execute::InstructionByte::ADD_INTEGER));
|
bytes.push_back(static_cast<std::uint8_t>(Execute::InstructionByte::ADD_INTEGER));
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case Token::OperandType::SubtractInteger:
|
case Token::OperandType::SubtractInteger:
|
||||||
bytes.push_back(static_cast<std::uint8_t>(Execute::InstructionByte::SUBTRACT_INTEGER));
|
bytes.push_back(static_cast<std::uint8_t>(Execute::InstructionByte::SUBTRACT_INTEGER));
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case Token::OperandType::DivideInteger:
|
case Token::OperandType::DivideInteger:
|
||||||
bytes.push_back(static_cast<std::uint8_t>(Execute::InstructionByte::DIVIDE_INTEGER));
|
bytes.push_back(static_cast<std::uint8_t>(Execute::InstructionByte::DIVIDE_INTEGER));
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case Token::OperandType::MultiplyInteger:
|
case Token::OperandType::MultiplyInteger:
|
||||||
bytes.push_back(static_cast<std::uint8_t>(Execute::InstructionByte::MULTIPLY_INTEGER));
|
bytes.push_back(static_cast<std::uint8_t>(Execute::InstructionByte::MULTIPLY_INTEGER));
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case Token::OperandType::ShiftIntegerLeft:
|
case Token::OperandType::ShiftIntegerLeft:
|
||||||
bytes.push_back(static_cast<std::uint8_t>(Execute::InstructionByte::SHIFT_LEFT_INTEGER));
|
bytes.push_back(static_cast<std::uint8_t>(Execute::InstructionByte::SHIFT_LEFT_INTEGER));
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case Token::OperandType::ShiftIntegerRight:
|
case Token::OperandType::ShiftIntegerRight:
|
||||||
bytes.push_back(static_cast<std::uint8_t>(Execute::InstructionByte::SHIFT_RIGHT_INTEGER));
|
bytes.push_back(static_cast<std::uint8_t>(Execute::InstructionByte::SHIFT_RIGHT_INTEGER));
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case Token::OperandType::LessThanInteger:
|
case Token::OperandType::LessThanInteger:
|
||||||
bytes.push_back(static_cast<std::uint8_t>(Execute::InstructionByte::LESS_THAN_INTEGER));
|
bytes.push_back(static_cast<std::uint8_t>(Execute::InstructionByte::LESS_THAN_INTEGER));
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case Token::OperandType::GreaterThanInteger:
|
case Token::OperandType::GreaterThanInteger:
|
||||||
bytes.push_back(static_cast<std::uint8_t>(Execute::InstructionByte::GREATER_THAN_INTEGER));
|
bytes.push_back(static_cast<std::uint8_t>(Execute::InstructionByte::GREATER_THAN_INTEGER));
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case Token::OperandType::EqualInteger:
|
case Token::OperandType::EqualInteger:
|
||||||
bytes.push_back(static_cast<std::uint8_t>(Execute::InstructionByte::EQUALS_INTEGER));
|
bytes.push_back(static_cast<std::uint8_t>(Execute::InstructionByte::EQUALS_INTEGER));
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case Token::OperandType::SetInteger:
|
case Token::OperandType::SetInteger:
|
||||||
bytes.push_back(static_cast<std::uint8_t>(Execute::InstructionByte::SET_INTEGER));
|
bytes.push_back(static_cast<std::uint8_t>(Execute::InstructionByte::SET_INTEGER));
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case Token::OperandType::Jump:
|
case Token::OperandType::Jump:
|
||||||
bytes.push_back(static_cast<std::uint8_t>(Execute::InstructionByte::JUMP));
|
bytes.push_back(static_cast<std::uint8_t>(Execute::InstructionByte::JUMP));
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case Token::OperandType::CallFunction:
|
case Token::OperandType::CallFunction:
|
||||||
bytes.push_back(static_cast<std::uint8_t>(Execute::InstructionByte::CALL));
|
bytes.push_back(static_cast<std::uint8_t>(Execute::InstructionByte::CALL));
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case Token::OperandType::Interrupt:
|
case Token::OperandType::Interrupt:
|
||||||
bytes.push_back(static_cast<std::uint8_t>(Execute::InstructionByte::INTERRUPT));
|
bytes.push_back(static_cast<std::uint8_t>(Execute::InstructionByte::INTERRUPT));
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case Token::OperandType::PushInteger:
|
case Token::OperandType::PushInteger:
|
||||||
bytes.push_back(static_cast<std::uint8_t>(Execute::InstructionByte::PUSH_INTEGER));
|
bytes.push_back(static_cast<std::uint8_t>(Execute::InstructionByte::PUSH_INTEGER));
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case Token::OperandType::PopInteger:
|
case Token::OperandType::PopInteger:
|
||||||
bytes.push_back(static_cast<std::uint8_t>(Execute::InstructionByte::POP_INTEGER));
|
bytes.push_back(static_cast<std::uint8_t>(Execute::InstructionByte::POP_INTEGER));
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case Token::OperandType::ReturnFromFunction:
|
case Token::OperandType::ReturnFromFunction:
|
||||||
bytes.push_back(static_cast<std::uint8_t>(Execute::InstructionByte::RETURN));
|
bytes.push_back(static_cast<std::uint8_t>(Execute::InstructionByte::RETURN));
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case Token::OperandType::ExitProgram:
|
case Token::OperandType::ExitProgram:
|
||||||
bytes.push_back(static_cast<std::uint8_t>(Execute::InstructionByte::EXIT));
|
bytes.push_back(static_cast<std::uint8_t>(Execute::InstructionByte::EXIT));
|
||||||
break;
|
break;
|
||||||
|
|
||||||
break;
|
break;
|
||||||
|
|
||||||
default:
|
default:
|
||||||
throw std::runtime_error("Unhandled operand type in InsertAsBytes");
|
throw std::runtime_error("Unhandled operand type in InsertAsBytes");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case Token::TokenType::Register:
|
case Token::TokenType::Register:
|
||||||
bytes.push_back(static_cast<std::uint8_t>(Execute::InstructionByte::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))));
|
bytes.push_back(
|
||||||
break;
|
static_cast<std::uint8_t>(GetByteCodeRegister(std::get<Token::RegisterType>(token.data))));
|
||||||
|
break;
|
||||||
|
|
||||||
case Token::TokenType::StatementEnd:
|
case Token::TokenType::StatementEnd:
|
||||||
case Token::TokenType::LabelDefinition:
|
case Token::TokenType::LabelDefinition:
|
||||||
// NO OP
|
// NO OP
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case Token::TokenType::LabelArgument:
|
case Token::TokenType::LabelArgument:
|
||||||
{
|
{
|
||||||
bytes.push_back(static_cast<std::uint8_t>(Execute::InstructionByte::LABEL));
|
bytes.push_back(static_cast<std::uint8_t>(Execute::InstructionByte::LABEL));
|
||||||
auto const & label = std::get<std::string>(token.data);
|
auto const & label = std::get<std::string>(token.data);
|
||||||
auto const findResult = jumpLabelLocations.find(label);
|
auto const findResult = jumpLabelLocations.find(label);
|
||||||
int jumpLocation = 0;
|
int jumpLocation = 0;
|
||||||
if (findResult == jumpLabelLocations.end())
|
if(findResult == jumpLabelLocations.end())
|
||||||
{
|
{
|
||||||
unresolvedJumpLabels.push_back(std::make_pair(token, bytes.size()));
|
unresolvedJumpLabels.push_back(std::make_pair(token, bytes.size()));
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
jumpLocation = findResult->second;
|
jumpLocation = findResult->second;
|
||||||
}
|
}
|
||||||
|
|
||||||
auto const insertionIndex = bytes.size();
|
auto const insertionIndex = bytes.size();
|
||||||
bytes.resize(bytes.size() + 4);
|
bytes.resize(bytes.size() + 4);
|
||||||
Utils::Bytes::Write(jumpLocation, bytes, insertionIndex);
|
Utils::Bytes::Write(jumpLocation, bytes, insertionIndex);
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case Token::TokenType::Memory:
|
case Token::TokenType::Memory:
|
||||||
{
|
{
|
||||||
bytes.push_back(static_cast<std::uint8_t>(Execute::InstructionByte::MEMORY_OP));
|
bytes.push_back(static_cast<std::uint8_t>(Execute::InstructionByte::MEMORY_OP));
|
||||||
switch(token.valueType)
|
switch(token.valueType)
|
||||||
{
|
{
|
||||||
case Token::TokenValueType::Register:
|
case Token::TokenValueType::Register:
|
||||||
bytes.push_back(static_cast<std::uint8_t>(Execute::InstructionByte::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))));
|
bytes.push_back(
|
||||||
break;
|
static_cast<std::uint8_t>(GetByteCodeRegister(std::get<Token::RegisterType>(token.data))));
|
||||||
|
break;
|
||||||
|
|
||||||
case Token::TokenValueType::Integer:
|
case Token::TokenValueType::Integer:
|
||||||
bytes.push_back(static_cast<std::uint8_t>(Execute::InstructionByte::IMMEDIATE_INTEGER));
|
bytes.push_back(static_cast<std::uint8_t>(Execute::InstructionByte::IMMEDIATE_INTEGER));
|
||||||
{
|
{
|
||||||
auto const insertionIndex = bytes.size();
|
auto const insertionIndex = bytes.size();
|
||||||
bytes.resize(bytes.size() + 4);
|
bytes.resize(bytes.size() + 4);
|
||||||
Utils::Bytes::Write(std::get<int>(token.data), bytes, insertionIndex);
|
Utils::Bytes::Write(std::get<int>(token.data), bytes, insertionIndex);
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
|
|
||||||
default:
|
default:
|
||||||
throw std::runtime_error("Unhandled value type for memory operand in InsertAsBytes");
|
throw std::runtime_error("Unhandled value type for memory operand in InsertAsBytes");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
|
|
||||||
default:
|
default:
|
||||||
throw std::runtime_error("Unhandled token type in InsertAsBytes");
|
throw std::runtime_error("Unhandled token type in InsertAsBytes");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
bool Compiler::Compile(
|
bool Compiler::Compile(std::vector<Token::Token> const & tokens, std::vector<std::uint8_t> & bytes)
|
||||||
std::vector<Token::Token> const & tokens,
|
{
|
||||||
std::vector<std::uint8_t> & bytes)
|
jumpLabelLocations.clear();
|
||||||
{
|
unresolvedJumpLabels.clear();
|
||||||
jumpLabelLocations.clear();
|
|
||||||
unresolvedJumpLabels.clear();
|
|
||||||
|
|
||||||
enum class State
|
enum class State
|
||||||
{
|
{
|
||||||
FindOperand,
|
FindOperand,
|
||||||
FindArguments,
|
FindArguments,
|
||||||
FindStatementEnd
|
FindStatementEnd
|
||||||
};
|
};
|
||||||
|
|
||||||
State state = State::FindOperand;
|
State state = State::FindOperand;
|
||||||
Token::OperandType operandType;
|
Token::OperandType operandType;
|
||||||
unsigned operatorTokenIndex = 0u;
|
unsigned operatorTokenIndex = 0u;
|
||||||
int expectedNumberOfArguments = 0;
|
int expectedNumberOfArguments = 0;
|
||||||
for(std::size_t i = 0u; i < tokens.size(); ++i)
|
for(std::size_t i = 0u; i < tokens.size(); ++i)
|
||||||
{
|
{
|
||||||
auto const & token = tokens[i];
|
auto const & token = tokens[i];
|
||||||
InsertAsBytes(token, bytes);
|
InsertAsBytes(token, bytes);
|
||||||
|
|
||||||
switch(state)
|
switch(state)
|
||||||
{
|
{
|
||||||
case State::FindOperand:
|
case State::FindOperand:
|
||||||
switch(token.type)
|
switch(token.type)
|
||||||
{
|
{
|
||||||
case Token::TokenType::Operand:
|
case Token::TokenType::Operand:
|
||||||
operatorTokenIndex = i;
|
operatorTokenIndex = i;
|
||||||
operandType = std::get<Token::OperandType>(token.data);
|
operandType = std::get<Token::OperandType>(token.data);
|
||||||
expectedNumberOfArguments = GetRequiredNumberOfArguments(operandType);
|
expectedNumberOfArguments = GetRequiredNumberOfArguments(operandType);
|
||||||
|
|
||||||
if (expectedNumberOfArguments < 1)
|
if(expectedNumberOfArguments < 1)
|
||||||
{
|
{
|
||||||
state = State::FindStatementEnd;
|
state = State::FindStatementEnd;
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
state = State::FindArguments;
|
state = State::FindArguments;
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case Token::TokenType::LabelDefinition:
|
case Token::TokenType::LabelDefinition:
|
||||||
{
|
{
|
||||||
auto findResult = jumpLabelLocations.find(std::get<std::string>(token.data));
|
auto findResult = jumpLabelLocations.find(std::get<std::string>(token.data));
|
||||||
if (findResult == jumpLabelLocations.end())
|
if(findResult == jumpLabelLocations.end())
|
||||||
{
|
{
|
||||||
jumpLabelLocations[std::get<std::string>(token.data)] = bytes.size();
|
jumpLabelLocations[std::get<std::string>(token.data)] = bytes.size();
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
throw CompilationError::CreateDuplicateLabelError(token);
|
throw CompilationError::CreateDuplicateLabelError(token);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case Token::TokenType::StatementEnd:
|
case Token::TokenType::StatementEnd:
|
||||||
// NO OP
|
// NO OP
|
||||||
break;
|
break;
|
||||||
|
|
||||||
default:
|
default:
|
||||||
throw CompilationError::CreateExpectedOperandError(token);
|
throw CompilationError::CreateExpectedOperandError(token);
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case State::FindArguments:
|
case State::FindArguments:
|
||||||
if (IsArgumentToken(token))
|
if(IsArgumentToken(token))
|
||||||
{
|
{
|
||||||
expectedNumberOfArguments -= 1;
|
expectedNumberOfArguments -= 1;
|
||||||
if (expectedNumberOfArguments < 1)
|
if(expectedNumberOfArguments < 1)
|
||||||
{
|
{
|
||||||
ValidateArguments(tokens, operatorTokenIndex);
|
ValidateArguments(tokens, operatorTokenIndex);
|
||||||
state = State::FindStatementEnd;
|
state = State::FindStatementEnd;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
// TODO Further specify this error?
|
// TODO Further specify this error?
|
||||||
throw CompilationError::CreateExpectedArgumentError(token);
|
throw CompilationError::CreateExpectedArgumentError(token);
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case State::FindStatementEnd:
|
case State::FindStatementEnd:
|
||||||
if (token.type != Token::TokenType::StatementEnd)
|
if(token.type != Token::TokenType::StatementEnd)
|
||||||
{
|
{
|
||||||
// TODO Further specify this error?
|
// TODO Further specify this error?
|
||||||
throw CompilationError::CreateExpectedEndOfStatementError(token);
|
throw CompilationError::CreateExpectedEndOfStatementError(token);
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
InsertAsBytes(
|
InsertAsBytes(token, bytes);
|
||||||
token,
|
state = State::FindOperand;
|
||||||
bytes);
|
}
|
||||||
state = State::FindOperand;
|
break;
|
||||||
}
|
}
|
||||||
break;
|
}
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
for(auto const & unresolved : unresolvedJumpLabels)
|
for(auto const & unresolved: unresolvedJumpLabels)
|
||||||
{
|
{
|
||||||
auto const & findResult = jumpLabelLocations.find(std::get<std::string>(unresolved.first.data));
|
auto const & findResult = jumpLabelLocations.find(std::get<std::string>(unresolved.first.data));
|
||||||
if (findResult == jumpLabelLocations.end())
|
if(findResult == jumpLabelLocations.end())
|
||||||
{
|
{
|
||||||
throw CompilationError::CreateNonExistingLabelError(unresolved.first);
|
throw CompilationError::CreateNonExistingLabelError(unresolved.first);
|
||||||
}
|
}
|
||||||
|
|
||||||
int const jumpLocation = findResult->second;
|
int const jumpLocation = findResult->second;
|
||||||
auto const index = unresolved.second;
|
auto const index = unresolved.second;
|
||||||
Utils::Bytes::Write(jumpLocation, bytes, index);
|
Utils::Bytes::Write(jumpLocation, bytes, index);
|
||||||
}
|
}
|
||||||
|
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -3,73 +3,70 @@
|
|||||||
|
|
||||||
namespace Compile
|
namespace Compile
|
||||||
{
|
{
|
||||||
CompilationError::CompilationError(
|
CompilationError::CompilationError(std::string const & message, Token::Token const & token) : errorToken(token)
|
||||||
std::string const & message,
|
{
|
||||||
Token::Token const & token)
|
errorToken.errorMessage = message;
|
||||||
: errorToken(token)
|
}
|
||||||
{
|
|
||||||
errorToken.errorMessage = message;
|
|
||||||
}
|
|
||||||
|
|
||||||
CompilationError CompilationError::CreateExpectedArgumentError(Token::Token const & token)
|
CompilationError CompilationError::CreateExpectedArgumentError(Token::Token const & token)
|
||||||
{
|
{
|
||||||
return CompilationError("Expected an argument", token);
|
return CompilationError("Expected an argument", token);
|
||||||
}
|
}
|
||||||
|
|
||||||
CompilationError CompilationError::CreateExpectedLabelError(Token::Token const & token)
|
CompilationError CompilationError::CreateExpectedLabelError(Token::Token const & token)
|
||||||
{
|
{
|
||||||
return CompilationError("Expected a label", token);
|
return CompilationError("Expected a label", token);
|
||||||
}
|
}
|
||||||
|
|
||||||
CompilationError CompilationError::CreateExpectedImmediateError(Token::Token const & token)
|
CompilationError CompilationError::CreateExpectedImmediateError(Token::Token const & token)
|
||||||
{
|
{
|
||||||
return CompilationError("Expected an immediate value", token);
|
return CompilationError("Expected an immediate value", token);
|
||||||
}
|
}
|
||||||
|
|
||||||
CompilationError CompilationError::CreateExpectedImmediateOrRegisterOrMemory(Token::Token const & token)
|
CompilationError CompilationError::CreateExpectedImmediateOrRegisterOrMemory(Token::Token const & token)
|
||||||
{
|
{
|
||||||
return CompilationError("Expected an immediate value, a register or a memory location", token);
|
return CompilationError("Expected an immediate value, a register or a memory location", token);
|
||||||
}
|
}
|
||||||
|
|
||||||
CompilationError CompilationError::CreateExpectedRegisterError(Token::Token const & token)
|
CompilationError CompilationError::CreateExpectedRegisterError(Token::Token const & token)
|
||||||
{
|
{
|
||||||
return CompilationError("Expected a register", token);
|
return CompilationError("Expected a register", token);
|
||||||
}
|
}
|
||||||
|
|
||||||
CompilationError CompilationError::CreateExpectedRegisterOrMemoryError(Token::Token const & token)
|
CompilationError CompilationError::CreateExpectedRegisterOrMemoryError(Token::Token const & token)
|
||||||
{
|
{
|
||||||
return CompilationError("Expected a register or a memory location", token);
|
return CompilationError("Expected a register or a memory location", token);
|
||||||
}
|
}
|
||||||
|
|
||||||
CompilationError CompilationError::CreateExpectedOperandError(Token::Token const & token)
|
CompilationError CompilationError::CreateExpectedOperandError(Token::Token const & token)
|
||||||
{
|
{
|
||||||
return CompilationError("Expected an operand", token);
|
return CompilationError("Expected an operand", token);
|
||||||
}
|
}
|
||||||
|
|
||||||
CompilationError CompilationError::CreateTooManyArgumentsError(Token::Token const & token)
|
CompilationError CompilationError::CreateTooManyArgumentsError(Token::Token const & token)
|
||||||
{
|
{
|
||||||
return CompilationError("Too many arguments for operand", token);
|
return CompilationError("Too many arguments for operand", token);
|
||||||
}
|
}
|
||||||
|
|
||||||
CompilationError CompilationError::CreateTooFewArgumentsError(Token::Token const & token)
|
CompilationError CompilationError::CreateTooFewArgumentsError(Token::Token const & token)
|
||||||
{
|
{
|
||||||
return CompilationError("Too few arguments for operand", token);
|
return CompilationError("Too few arguments for operand", token);
|
||||||
}
|
}
|
||||||
|
|
||||||
CompilationError CompilationError::CreateExpectedEndOfStatementError(Token::Token const & token)
|
CompilationError CompilationError::CreateExpectedEndOfStatementError(Token::Token const & token)
|
||||||
{
|
{
|
||||||
std::stringstream ss;
|
std::stringstream ss;
|
||||||
ss << "Expected end of statement (;), but got " << token.GetName() << " instead";
|
ss << "Expected end of statement (;), but got " << token.GetName() << " instead";
|
||||||
return CompilationError(ss.str(), token);
|
return CompilationError(ss.str(), token);
|
||||||
}
|
}
|
||||||
|
|
||||||
CompilationError CompilationError::CreateDuplicateLabelError(Token::Token const & token)
|
CompilationError CompilationError::CreateDuplicateLabelError(Token::Token const & token)
|
||||||
{
|
{
|
||||||
return CompilationError("Duplicate label definition", token);
|
return CompilationError("Duplicate label definition", token);
|
||||||
}
|
}
|
||||||
|
|
||||||
CompilationError CompilationError::CreateNonExistingLabelError(Token::Token const & token)
|
CompilationError CompilationError::CreateNonExistingLabelError(Token::Token const & token)
|
||||||
{
|
{
|
||||||
return CompilationError("Jumping to non existing label", token);
|
return CompilationError("Jumping to non existing label", token);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -3,142 +3,140 @@
|
|||||||
|
|
||||||
namespace Execute
|
namespace Execute
|
||||||
{
|
{
|
||||||
int & ArgumentValue::GetRegister(State & state) const
|
int & ArgumentValue::GetRegister(State & state) const
|
||||||
{
|
{
|
||||||
switch(std::get<RegisterByte>(data))
|
switch(std::get<RegisterByte>(data))
|
||||||
{
|
{
|
||||||
case RegisterByte::A:
|
case RegisterByte::A:
|
||||||
return state.registers.A;
|
return state.registers.A;
|
||||||
|
|
||||||
case RegisterByte::B:
|
case RegisterByte::B:
|
||||||
return state.registers.B;
|
return state.registers.B;
|
||||||
|
|
||||||
case RegisterByte::C:
|
case RegisterByte::C:
|
||||||
return state.registers.C;
|
return state.registers.C;
|
||||||
|
|
||||||
case RegisterByte::D:
|
case RegisterByte::D:
|
||||||
return state.registers.D;
|
return state.registers.D;
|
||||||
|
|
||||||
default:
|
default:
|
||||||
throw std::runtime_error("Unhandled register byte in GetRegister");
|
throw std::runtime_error("Unhandled register byte in GetRegister");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
std::uint8_t * ArgumentValue::GetMemory(State & state) const
|
std::uint8_t * ArgumentValue::GetMemory(State & state) const
|
||||||
{
|
{
|
||||||
switch (memoryValueType)
|
switch(memoryValueType)
|
||||||
{
|
{
|
||||||
case ArgumentType::Immediate:
|
case ArgumentType::Immediate:
|
||||||
return &(state.memory[std::get<int>(data)]);
|
return &(state.memory[std::get<int>(data)]);
|
||||||
|
|
||||||
case ArgumentType::Register:
|
case ArgumentType::Register:
|
||||||
return &(state.memory[GetRegister(state)]);
|
return &(state.memory[GetRegister(state)]);
|
||||||
|
|
||||||
default:
|
default:
|
||||||
throw std::runtime_error("Unhandled argument type in GetMemory");
|
throw std::runtime_error("Unhandled argument type in GetMemory");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void ArgumentValue::Write(int const value, State & state) const
|
void ArgumentValue::Write(int const value, State & state) const
|
||||||
{
|
{
|
||||||
if (type == ArgumentType::Immediate)
|
if(type == ArgumentType::Immediate)
|
||||||
{
|
{
|
||||||
throw AttemptedWriteToImmediate(state.registers.programCounter);
|
throw AttemptedWriteToImmediate(state.registers.programCounter);
|
||||||
}
|
}
|
||||||
|
|
||||||
switch(type)
|
switch(type)
|
||||||
{
|
{
|
||||||
case ArgumentType::Memory:
|
case ArgumentType::Memory:
|
||||||
{
|
{
|
||||||
auto * ptr = GetMemory(state);
|
auto * ptr = GetMemory(state);
|
||||||
*ptr = value & 0xFF;
|
*ptr = value & 0xFF;
|
||||||
*(++ptr) = (value >> 8) & 0xFF;
|
*(++ptr) = (value >> 8) & 0xFF;
|
||||||
*(++ptr) = (value >> 16) & 0xFF;
|
*(++ptr) = (value >> 16) & 0xFF;
|
||||||
*(++ptr) = (value >> 24) & 0xFF;
|
*(++ptr) = (value >> 24) & 0xFF;
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case ArgumentType::Register:
|
case ArgumentType::Register:
|
||||||
GetRegister(state) = value;
|
GetRegister(state) = value;
|
||||||
break;
|
break;
|
||||||
|
|
||||||
default:
|
default:
|
||||||
throw std::runtime_error("Unhandled argument type in Write");
|
throw std::runtime_error("Unhandled argument type in Write");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
int ArgumentValue::Read(State & state) const
|
int ArgumentValue::Read(State & state) const
|
||||||
{
|
{
|
||||||
switch(type)
|
switch(type)
|
||||||
{
|
{
|
||||||
case ArgumentType::Immediate:
|
case ArgumentType::Immediate:
|
||||||
return std::get<int>(data);
|
return std::get<int>(data);
|
||||||
|
|
||||||
case ArgumentType::Memory:
|
case ArgumentType::Memory:
|
||||||
{
|
{
|
||||||
int result = 0;
|
int result = 0;
|
||||||
auto * ptr = GetMemory(state);
|
auto * ptr = GetMemory(state);
|
||||||
result |= static_cast<int>(*ptr);
|
result |= static_cast<int>(*ptr);
|
||||||
result |= static_cast<int>(*(++ptr)) << 8;
|
result |= static_cast<int>(*(++ptr)) << 8;
|
||||||
result |= static_cast<int>(*(++ptr)) << 16;
|
result |= static_cast<int>(*(++ptr)) << 16;
|
||||||
result |= static_cast<int>(*(++ptr)) << 24;
|
result |= static_cast<int>(*(++ptr)) << 24;
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case ArgumentType::Register:
|
case ArgumentType::Register:
|
||||||
return GetRegister(state);
|
return GetRegister(state);
|
||||||
|
|
||||||
default:
|
default:
|
||||||
throw std::runtime_error("Unhandled argument type in Read");
|
throw std::runtime_error("Unhandled argument type in Read");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Returns the size of the argument in bytes
|
// Returns the size of the argument in bytes
|
||||||
std::size_t ArgumentValue::Parse(
|
std::size_t ArgumentValue::Parse(std::vector<std::uint8_t> const & memory, std::size_t const pos)
|
||||||
std::vector<std::uint8_t> const & memory,
|
{
|
||||||
std::size_t const pos)
|
InstructionByte const valueByte = static_cast<InstructionByte>(memory[pos]);
|
||||||
{
|
switch(valueByte)
|
||||||
InstructionByte const valueByte = static_cast<InstructionByte>(memory[pos]);
|
{
|
||||||
switch(valueByte)
|
case InstructionByte::IMMEDIATE_INTEGER:
|
||||||
{
|
case InstructionByte::LABEL:
|
||||||
case InstructionByte::IMMEDIATE_INTEGER:
|
type = ArgumentType::Immediate;
|
||||||
case InstructionByte::LABEL:
|
data = Utils::Bytes::Read(memory, pos + 1);
|
||||||
type = ArgumentType::Immediate;
|
return 5;
|
||||||
data = Utils::Bytes::Read(memory, pos + 1);
|
|
||||||
return 5;
|
|
||||||
|
|
||||||
case InstructionByte::REGISTER:
|
case InstructionByte::REGISTER:
|
||||||
type = ArgumentType::Register;
|
type = ArgumentType::Register;
|
||||||
data = static_cast<RegisterByte>(memory[pos + 1]);
|
data = static_cast<RegisterByte>(memory[pos + 1]);
|
||||||
return 2;
|
return 2;
|
||||||
|
|
||||||
case InstructionByte::MEMORY_OP:
|
case InstructionByte::MEMORY_OP:
|
||||||
{
|
{
|
||||||
type = ArgumentType::Memory;
|
type = ArgumentType::Memory;
|
||||||
InstructionByte const memoryArgTypeByte = static_cast<InstructionByte>(memory[pos + 1]);
|
InstructionByte const memoryArgTypeByte = static_cast<InstructionByte>(memory[pos + 1]);
|
||||||
switch(memoryArgTypeByte)
|
switch(memoryArgTypeByte)
|
||||||
{
|
{
|
||||||
case InstructionByte::IMMEDIATE_INTEGER:
|
case InstructionByte::IMMEDIATE_INTEGER:
|
||||||
memoryValueType = ArgumentType::Immediate;
|
memoryValueType = ArgumentType::Immediate;
|
||||||
data = Utils::Bytes::Read(memory, pos + 2);
|
data = Utils::Bytes::Read(memory, pos + 2);
|
||||||
return 6;
|
return 6;
|
||||||
|
|
||||||
case InstructionByte::REGISTER:
|
case InstructionByte::REGISTER:
|
||||||
memoryValueType = ArgumentType::Register;
|
memoryValueType = ArgumentType::Register;
|
||||||
data = static_cast<RegisterByte>(memory[pos + 2]);
|
data = static_cast<RegisterByte>(memory[pos + 2]);
|
||||||
return 3;
|
return 3;
|
||||||
|
|
||||||
default:
|
default:
|
||||||
// TODO throw more specific error?
|
// TODO throw more specific error?
|
||||||
throw NonArgumentByte(pos);
|
throw NonArgumentByte(pos);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
|
|
||||||
default:
|
default:
|
||||||
throw NonArgumentByte(pos);
|
throw NonArgumentByte(pos);
|
||||||
}
|
}
|
||||||
|
|
||||||
throw std::runtime_error("Reached end of function in Parse");
|
throw std::runtime_error("Reached end of function in Parse");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -2,64 +2,49 @@
|
|||||||
|
|
||||||
namespace Execute
|
namespace Execute
|
||||||
{
|
{
|
||||||
std::string const & RuntimeError::GetMessage() const
|
std::string const & RuntimeError::GetMessage() const { return message; }
|
||||||
{
|
|
||||||
return message;
|
|
||||||
}
|
|
||||||
|
|
||||||
RuntimeError::RuntimeError()
|
RuntimeError::RuntimeError() : message("Undocumented runtime error") { }
|
||||||
: message("Undocumented runtime error")
|
|
||||||
{
|
|
||||||
}
|
|
||||||
|
|
||||||
RuntimeError::RuntimeError(
|
RuntimeError::RuntimeError(std::string const & what, std::size_t const _byteLocation)
|
||||||
std::string const & what,
|
: message(what), byteLocation(_byteLocation)
|
||||||
std::size_t const _byteLocation)
|
{ }
|
||||||
: message(what),
|
|
||||||
byteLocation(_byteLocation)
|
|
||||||
{
|
|
||||||
}
|
|
||||||
|
|
||||||
InterruptIndexOutOfRange::InterruptIndexOutOfRange(std::size_t const location, int const index)
|
InterruptIndexOutOfRange::InterruptIndexOutOfRange(std::size_t const location, int const index)
|
||||||
: RuntimeError("", location)
|
: RuntimeError("", location)
|
||||||
{
|
{
|
||||||
message = "Interrupt at byte ";
|
message = "Interrupt at byte ";
|
||||||
message += std::to_string(location);
|
message += std::to_string(location);
|
||||||
message += " with index ";
|
message += " with index ";
|
||||||
message += std::to_string(index);
|
message += std::to_string(index);
|
||||||
message += " is out of range";
|
message += " is out of range";
|
||||||
}
|
}
|
||||||
|
|
||||||
AttemptedWriteToImmediate::AttemptedWriteToImmediate(std::size_t const location)
|
AttemptedWriteToImmediate::AttemptedWriteToImmediate(std::size_t const location) : RuntimeError("", location)
|
||||||
: RuntimeError("", location)
|
{
|
||||||
{
|
message = "Instruction at ";
|
||||||
message = "Instruction at ";
|
message += std::to_string(location);
|
||||||
message += std::to_string(location);
|
message += " attempted to write to an immediate value";
|
||||||
message += " attempted to write to an immediate value";
|
}
|
||||||
}
|
|
||||||
|
|
||||||
NonExecutableInstruction::NonExecutableInstruction(std::size_t const location)
|
NonExecutableInstruction::NonExecutableInstruction(std::size_t const location) : RuntimeError("", location)
|
||||||
: RuntimeError("", location)
|
{
|
||||||
{
|
message = "Attempted to execute byte at ";
|
||||||
message = "Attempted to execute byte at ";
|
message += std::to_string(location);
|
||||||
message += std::to_string(location);
|
message += " which is not an instruction byte";
|
||||||
message += " which is not an instruction byte";
|
}
|
||||||
}
|
|
||||||
|
|
||||||
NonArgumentByte::NonArgumentByte(std::size_t const location)
|
NonArgumentByte::NonArgumentByte(std::size_t const location) : RuntimeError("", location)
|
||||||
: RuntimeError("", location)
|
{
|
||||||
{
|
message = "Expected an argument byte (immediate, register or memory location) at ";
|
||||||
message = "Expected an argument byte (immediate, register or memory location) at ";
|
message += std::to_string(location);
|
||||||
message += std::to_string(location);
|
}
|
||||||
}
|
|
||||||
|
|
||||||
OutOfMemory::OutOfMemory(
|
OutOfMemory::OutOfMemory(std::size_t const requiredMemorySize, std::size_t const actualMemorySize)
|
||||||
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 = "Not enough memory to fit code. Actual size is ";
|
message += ". Minimal required size is ";
|
||||||
message += std::to_string(actualMemorySize);
|
message += std::to_string(requiredMemorySize);
|
||||||
message += ". Minimal required size is ";
|
}
|
||||||
message += std::to_string(requiredMemorySize);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
@@ -5,89 +5,88 @@
|
|||||||
|
|
||||||
namespace Execute
|
namespace Execute
|
||||||
{
|
{
|
||||||
void ExecuteInterrupt(
|
void ExecuteInterrupt(int const id, Execute::Registers & registers, std::vector<std::uint8_t> & memory)
|
||||||
int const id,
|
{
|
||||||
Execute::Registers & registers,
|
switch(id)
|
||||||
std::vector<std::uint8_t> & memory)
|
{
|
||||||
{
|
/* STDOUT interrupts */
|
||||||
switch(id)
|
case 0:
|
||||||
{
|
std::putc(registers.A, stdout);
|
||||||
/* STDOUT interrupts */
|
break;
|
||||||
case 0:
|
|
||||||
std::putc(registers.A, stdout);
|
|
||||||
break;
|
|
||||||
|
|
||||||
case 1:
|
case 1:
|
||||||
std::printf("%i", registers.A);
|
std::printf("%i", registers.A);
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case 2:
|
case 2:
|
||||||
std::printf("0x%x", registers.A);
|
std::printf("0x%x", registers.A);
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case 3:
|
case 3:
|
||||||
{
|
{
|
||||||
unsigned const end = registers.A + registers.B;
|
unsigned const end = registers.A + registers.B;
|
||||||
// TODO handle out of bounds
|
// TODO handle out of bounds
|
||||||
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;
|
break;
|
||||||
|
|
||||||
/* STDIN interrupts */
|
/* STDIN interrupts */
|
||||||
case 10:
|
case 10:
|
||||||
{
|
{
|
||||||
registers.A = std::getchar();
|
registers.A = std::getchar();
|
||||||
if (registers.A == '\n')
|
if(registers.A == '\n')
|
||||||
{
|
{
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
while(std::getchar() != '\n');
|
while(std::getchar() != '\n')
|
||||||
}
|
;
|
||||||
break;
|
}
|
||||||
|
break;
|
||||||
|
|
||||||
case 11:
|
case 11:
|
||||||
{
|
{
|
||||||
if (registers.B <= 0)
|
if(registers.B <= 0)
|
||||||
{
|
{
|
||||||
registers.B = 0;
|
registers.B = 0;
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (registers.A < 0 || static_cast<unsigned>(registers.A + registers.B) >= memory.size())
|
if(registers.A < 0 || static_cast<unsigned>(registers.A + registers.B) >= memory.size())
|
||||||
{
|
{
|
||||||
throw OutOfMemory(registers.A, registers.B);
|
throw OutOfMemory(registers.A, registers.B);
|
||||||
}
|
}
|
||||||
|
|
||||||
int charactersRead = 0;
|
int charactersRead = 0;
|
||||||
bool newlineRead = false;
|
bool newlineRead = false;
|
||||||
while(charactersRead < registers.B)
|
while(charactersRead < registers.B)
|
||||||
{
|
{
|
||||||
char result = std::getchar();
|
char result = std::getchar();
|
||||||
if (result == '\n')
|
if(result == '\n')
|
||||||
{
|
{
|
||||||
newlineRead = true;
|
newlineRead = true;
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
memory[registers.A + charactersRead] = result;
|
memory[registers.A + charactersRead] = result;
|
||||||
++charactersRead;
|
++charactersRead;
|
||||||
}
|
}
|
||||||
|
|
||||||
registers.B = charactersRead;
|
registers.B = charactersRead;
|
||||||
|
|
||||||
if (!newlineRead)
|
if(!newlineRead)
|
||||||
{
|
{
|
||||||
while(std::getchar() != '\n');
|
while(std::getchar() != '\n')
|
||||||
}
|
;
|
||||||
}
|
}
|
||||||
break;
|
}
|
||||||
|
break;
|
||||||
|
|
||||||
default:
|
default:
|
||||||
throw InterruptIndexOutOfRange(registers.programCounter, id);
|
throw InterruptIndexOutOfRange(registers.programCounter, id);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -2,27 +2,27 @@
|
|||||||
|
|
||||||
namespace Execute
|
namespace Execute
|
||||||
{
|
{
|
||||||
void State::PushToStack(int const value)
|
void State::PushToStack(int const value)
|
||||||
{
|
{
|
||||||
memory[registers.stackPointer] = value & 0xFF;
|
memory[registers.stackPointer] = value & 0xFF;
|
||||||
memory[registers.stackPointer + 1] = (value >> 8) & 0xFF;
|
memory[registers.stackPointer + 1] = (value >> 8) & 0xFF;
|
||||||
memory[registers.stackPointer + 2] = (value >> 16) & 0xFF;
|
memory[registers.stackPointer + 2] = (value >> 16) & 0xFF;
|
||||||
memory[registers.stackPointer + 3] = (value >> 24) & 0xFF;
|
memory[registers.stackPointer + 3] = (value >> 24) & 0xFF;
|
||||||
|
|
||||||
registers.stackPointer += 4;
|
registers.stackPointer += 4;
|
||||||
|
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
int State::PopFromStack()
|
int State::PopFromStack()
|
||||||
{
|
{
|
||||||
int value = static_cast<int>(memory[registers.stackPointer - 1]) << 24;
|
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 - 2]) << 16;
|
||||||
value |= static_cast<int>(memory[registers.stackPointer - 3]) << 8;
|
value |= static_cast<int>(memory[registers.stackPointer - 3]) << 8;
|
||||||
value |= static_cast<int>(memory[registers.stackPointer - 4]);
|
value |= static_cast<int>(memory[registers.stackPointer - 4]);
|
||||||
|
|
||||||
registers.stackPointer -= 4;
|
registers.stackPointer -= 4;
|
||||||
|
|
||||||
return value;
|
return value;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -7,441 +7,403 @@
|
|||||||
|
|
||||||
namespace Execute
|
namespace Execute
|
||||||
{
|
{
|
||||||
std::size_t GetArguments(
|
std::size_t GetArguments(
|
||||||
InstructionByte const instruction,
|
InstructionByte const instruction,
|
||||||
std::array<ArgumentValue, 3> & arguments,
|
std::array<ArgumentValue, 3> & arguments,
|
||||||
std::vector<std::uint8_t> const & memory,
|
std::vector<std::uint8_t> const & memory,
|
||||||
std::size_t const memoryPos)
|
std::size_t const memoryPos)
|
||||||
{
|
{
|
||||||
std::size_t expectedNumberOfArguments = 0;
|
std::size_t expectedNumberOfArguments = 0;
|
||||||
switch(instruction)
|
switch(instruction)
|
||||||
{
|
{
|
||||||
case InstructionByte::JUMP:
|
case InstructionByte::JUMP:
|
||||||
case InstructionByte::INTERRUPT:
|
case InstructionByte::INTERRUPT:
|
||||||
case InstructionByte::CALL:
|
case InstructionByte::CALL:
|
||||||
case InstructionByte::POP_INTEGER:
|
case InstructionByte::POP_INTEGER:
|
||||||
case InstructionByte::PUSH_INTEGER:
|
case InstructionByte::PUSH_INTEGER:
|
||||||
expectedNumberOfArguments = 1;
|
expectedNumberOfArguments = 1;
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case InstructionByte::SET_INTEGER:
|
case InstructionByte::SET_INTEGER:
|
||||||
case InstructionByte::LESS_THAN_INTEGER:
|
case InstructionByte::LESS_THAN_INTEGER:
|
||||||
case InstructionByte::GREATER_THAN_INTEGER:
|
case InstructionByte::GREATER_THAN_INTEGER:
|
||||||
case InstructionByte::EQUALS_INTEGER:
|
case InstructionByte::EQUALS_INTEGER:
|
||||||
expectedNumberOfArguments = 2;
|
expectedNumberOfArguments = 2;
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case InstructionByte::ADD_INTEGER:
|
case InstructionByte::ADD_INTEGER:
|
||||||
case InstructionByte::SUBTRACT_INTEGER:
|
case InstructionByte::SUBTRACT_INTEGER:
|
||||||
case InstructionByte::DIVIDE_INTEGER:
|
case InstructionByte::DIVIDE_INTEGER:
|
||||||
case InstructionByte::MULTIPLY_INTEGER:
|
case InstructionByte::MULTIPLY_INTEGER:
|
||||||
case InstructionByte::SHIFT_LEFT_INTEGER:
|
case InstructionByte::SHIFT_LEFT_INTEGER:
|
||||||
case InstructionByte::SHIFT_RIGHT_INTEGER:
|
case InstructionByte::SHIFT_RIGHT_INTEGER:
|
||||||
expectedNumberOfArguments = 3;
|
expectedNumberOfArguments = 3;
|
||||||
break;
|
break;
|
||||||
|
|
||||||
default:
|
default:
|
||||||
throw std::runtime_error("No instruction length set for instruction byte");
|
throw std::runtime_error("No instruction length set for instruction byte");
|
||||||
}
|
}
|
||||||
|
|
||||||
std::size_t memoryOffset = memoryPos;
|
std::size_t memoryOffset = memoryPos;
|
||||||
for(std::size_t i = 0; i < expectedNumberOfArguments; ++i)
|
for(std::size_t i = 0; i < expectedNumberOfArguments; ++i)
|
||||||
{
|
{
|
||||||
memoryOffset += arguments[i].Parse(memory, memoryOffset);
|
memoryOffset += arguments[i].Parse(memory, memoryOffset);
|
||||||
}
|
}
|
||||||
|
|
||||||
return memoryOffset - memoryPos;
|
return memoryOffset - memoryPos;
|
||||||
}
|
}
|
||||||
|
|
||||||
void VirtualMachine::DoArithmatic(
|
void VirtualMachine::DoArithmatic(InstructionByte const instruction, std::array<ArgumentValue, 3> & arguments)
|
||||||
InstructionByte const instruction,
|
{
|
||||||
std::array<ArgumentValue, 3> & arguments)
|
switch(instruction)
|
||||||
{
|
{
|
||||||
switch (instruction)
|
case InstructionByte::ADD_INTEGER:
|
||||||
{
|
arguments[2].Write(arguments[0].Read(state) + arguments[1].Read(state), state);
|
||||||
case InstructionByte::ADD_INTEGER:
|
break;
|
||||||
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);
|
||||||
case InstructionByte::SUBTRACT_INTEGER:
|
break;
|
||||||
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);
|
||||||
case InstructionByte::DIVIDE_INTEGER:
|
break;
|
||||||
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);
|
||||||
case InstructionByte::MULTIPLY_INTEGER:
|
break;
|
||||||
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);
|
||||||
case InstructionByte::SHIFT_LEFT_INTEGER:
|
break;
|
||||||
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);
|
||||||
case InstructionByte::SHIFT_RIGHT_INTEGER:
|
break;
|
||||||
arguments[2].Write(arguments[0].Read(state) << arguments[1].Read(state), state);
|
|
||||||
break;
|
default:
|
||||||
|
throw std::runtime_error("Unhandled instruction bytr in DoArithmatic");
|
||||||
default:
|
}
|
||||||
throw std::runtime_error("Unhandled instruction bytr in DoArithmatic");
|
}
|
||||||
}
|
|
||||||
}
|
void VirtualMachine::SetInteger(std::array<ArgumentValue, 3> & arguments)
|
||||||
|
{
|
||||||
void VirtualMachine::SetInteger(std::array<ArgumentValue, 3> & arguments)
|
arguments[0].Write(arguments[1].Read(state), state);
|
||||||
{
|
}
|
||||||
arguments[0].Write(arguments[1].Read(state), state);
|
|
||||||
}
|
void VirtualMachine::ExecuteJump(std::array<ArgumentValue, 3> & arguments)
|
||||||
|
{
|
||||||
void VirtualMachine::ExecuteJump(std::array<ArgumentValue, 3> & arguments)
|
state.registers.programCounter = arguments[0].Read(state);
|
||||||
{
|
}
|
||||||
state.registers.programCounter = arguments[0].Read(state);
|
|
||||||
}
|
void VirtualMachine::ExecuteInterrupt(std::array<ArgumentValue, 3> & arguments)
|
||||||
|
{
|
||||||
void VirtualMachine::ExecuteInterrupt(std::array<ArgumentValue, 3> & arguments)
|
int const interruptNo = arguments[0].Read(state);
|
||||||
{
|
Execute::ExecuteInterrupt(interruptNo, state.registers, state.memory);
|
||||||
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)
|
||||||
|
{
|
||||||
void VirtualMachine::ExecuteCall(
|
state.PushToStack(returnByte);
|
||||||
std::array<ArgumentValue, 3> & arguments,
|
state.registers.programCounter = arguments[0].Read(state);
|
||||||
std::size_t const returnByte)
|
}
|
||||||
{
|
|
||||||
state.PushToStack(returnByte);
|
void VirtualMachine::ExecuteReturn()
|
||||||
state.registers.programCounter = arguments[0].Read(state);
|
{
|
||||||
}
|
int const returnByte = state.PopFromStack();
|
||||||
|
state.registers.programCounter = returnByte;
|
||||||
void VirtualMachine::ExecuteReturn()
|
}
|
||||||
{
|
|
||||||
int const returnByte = state.PopFromStack();
|
void VirtualMachine::DoBooleanLogic(
|
||||||
state.registers.programCounter = returnByte;
|
InstructionByte const instruction,
|
||||||
}
|
std::array<ArgumentValue, 3> & arguments,
|
||||||
|
std::size_t const nextInstruction)
|
||||||
void VirtualMachine::DoBooleanLogic(
|
{
|
||||||
InstructionByte const instruction,
|
bool executeNextInstruction = false;
|
||||||
std::array<ArgumentValue, 3> & arguments,
|
switch(instruction)
|
||||||
std::size_t const nextInstruction)
|
{
|
||||||
{
|
case InstructionByte::LESS_THAN_INTEGER:
|
||||||
bool executeNextInstruction = false;
|
executeNextInstruction = arguments[0].Read(state) < arguments[1].Read(state);
|
||||||
switch(instruction)
|
break;
|
||||||
{
|
|
||||||
case InstructionByte::LESS_THAN_INTEGER:
|
case InstructionByte::GREATER_THAN_INTEGER:
|
||||||
executeNextInstruction = arguments[0].Read(state) < arguments[1].Read(state);
|
executeNextInstruction = arguments[0].Read(state) > arguments[1].Read(state);
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case InstructionByte::GREATER_THAN_INTEGER:
|
case InstructionByte::EQUALS_INTEGER:
|
||||||
executeNextInstruction = arguments[0].Read(state) > arguments[1].Read(state);
|
executeNextInstruction = arguments[0].Read(state) == arguments[1].Read(state);
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case InstructionByte::EQUALS_INTEGER:
|
default:
|
||||||
executeNextInstruction = arguments[0].Read(state) == arguments[1].Read(state);
|
throw std::runtime_error("Unhandled instruction byte for boolean logic");
|
||||||
break;
|
}
|
||||||
|
|
||||||
default:
|
if(executeNextInstruction)
|
||||||
throw std::runtime_error("Unhandled instruction byte for boolean logic");
|
{
|
||||||
}
|
state.registers.programCounter = nextInstruction;
|
||||||
|
return;
|
||||||
if (executeNextInstruction)
|
}
|
||||||
{
|
|
||||||
state.registers.programCounter = nextInstruction;
|
auto const argumentOffset = GetArguments(
|
||||||
return;
|
static_cast<InstructionByte>(state.memory[nextInstruction]),
|
||||||
}
|
arguments,
|
||||||
|
state.memory,
|
||||||
auto const argumentOffset = GetArguments(
|
nextInstruction + 1);
|
||||||
static_cast<InstructionByte>(state.memory[nextInstruction]),
|
state.registers.programCounter = nextInstruction + 1 + argumentOffset;
|
||||||
arguments,
|
}
|
||||||
state.memory,
|
|
||||||
nextInstruction + 1);
|
void VirtualMachine::ExecutePop(std::array<ArgumentValue, 3> & arguments)
|
||||||
state.registers.programCounter = nextInstruction + 1 + argumentOffset;
|
{
|
||||||
}
|
arguments[0].Write(state.PopFromStack(), state);
|
||||||
|
}
|
||||||
void VirtualMachine::ExecutePop(std::array<ArgumentValue, 3> & arguments)
|
|
||||||
{
|
void VirtualMachine::ExecutePush(std::array<ArgumentValue, 3> & arguments)
|
||||||
arguments[0].Write(state.PopFromStack(), state);
|
{
|
||||||
}
|
state.PushToStack(arguments[0].Read(state));
|
||||||
|
}
|
||||||
void VirtualMachine::ExecutePush(std::array<ArgumentValue, 3> & arguments)
|
|
||||||
{
|
void VirtualMachine::Step()
|
||||||
state.PushToStack(arguments[0].Read(state));
|
{
|
||||||
}
|
// Default to 1 byte (= 1 instruction)
|
||||||
|
std::size_t programCounterIncrement = 1;
|
||||||
void VirtualMachine::Step()
|
std::array<ArgumentValue, 3> arguments;
|
||||||
{
|
InstructionByte const instruction = static_cast<InstructionByte>(state.memory[state.registers.programCounter]);
|
||||||
// Default to 1 byte (= 1 instruction)
|
switch(instruction)
|
||||||
std::size_t programCounterIncrement = 1;
|
{
|
||||||
std::array<ArgumentValue, 3> arguments;
|
case InstructionByte::ADD_INTEGER:
|
||||||
InstructionByte const instruction =
|
case InstructionByte::SUBTRACT_INTEGER:
|
||||||
static_cast<InstructionByte>(state.memory[state.registers.programCounter]);
|
case InstructionByte::DIVIDE_INTEGER:
|
||||||
switch(instruction)
|
case InstructionByte::MULTIPLY_INTEGER:
|
||||||
{
|
case InstructionByte::SHIFT_LEFT_INTEGER:
|
||||||
case InstructionByte::ADD_INTEGER:
|
case InstructionByte::SHIFT_RIGHT_INTEGER:
|
||||||
case InstructionByte::SUBTRACT_INTEGER:
|
programCounterIncrement
|
||||||
case InstructionByte::DIVIDE_INTEGER:
|
+= GetArguments(instruction, arguments, state.memory, state.registers.programCounter + 1);
|
||||||
case InstructionByte::MULTIPLY_INTEGER:
|
DoArithmatic(instruction, arguments);
|
||||||
case InstructionByte::SHIFT_LEFT_INTEGER:
|
state.registers.programCounter += programCounterIncrement;
|
||||||
case InstructionByte::SHIFT_RIGHT_INTEGER:
|
break;
|
||||||
programCounterIncrement += GetArguments(
|
|
||||||
instruction,
|
case InstructionByte::SET_INTEGER:
|
||||||
arguments,
|
programCounterIncrement
|
||||||
state.memory,
|
+= GetArguments(instruction, arguments, state.memory, state.registers.programCounter + 1);
|
||||||
state.registers.programCounter + 1);
|
SetInteger(arguments);
|
||||||
DoArithmatic(instruction, arguments);
|
state.registers.programCounter += programCounterIncrement;
|
||||||
state.registers.programCounter += programCounterIncrement;
|
break;
|
||||||
break;
|
|
||||||
|
case InstructionByte::JUMP:
|
||||||
case InstructionByte::SET_INTEGER:
|
GetArguments(instruction, arguments, state.memory, state.registers.programCounter + 1);
|
||||||
programCounterIncrement += GetArguments(
|
ExecuteJump(arguments);
|
||||||
instruction,
|
break;
|
||||||
arguments,
|
|
||||||
state.memory,
|
case InstructionByte::INTERRUPT:
|
||||||
state.registers.programCounter + 1);
|
programCounterIncrement
|
||||||
SetInteger(arguments);
|
+= GetArguments(instruction, arguments, state.memory, state.registers.programCounter + 1);
|
||||||
state.registers.programCounter += programCounterIncrement;
|
ExecuteInterrupt(arguments);
|
||||||
break;
|
state.registers.programCounter += programCounterIncrement;
|
||||||
|
break;
|
||||||
case InstructionByte::JUMP:
|
|
||||||
GetArguments(
|
case InstructionByte::CALL:
|
||||||
instruction,
|
programCounterIncrement
|
||||||
arguments,
|
+= GetArguments(instruction, arguments, state.memory, state.registers.programCounter + 1);
|
||||||
state.memory,
|
ExecuteCall(arguments, state.registers.programCounter + programCounterIncrement);
|
||||||
state.registers.programCounter + 1);
|
break;
|
||||||
ExecuteJump(arguments);
|
|
||||||
break;
|
case InstructionByte::RETURN:
|
||||||
|
ExecuteReturn();
|
||||||
case InstructionByte::INTERRUPT:
|
break;
|
||||||
programCounterIncrement += GetArguments(
|
|
||||||
instruction,
|
case InstructionByte::EXIT:
|
||||||
arguments,
|
state.terminated = true;
|
||||||
state.memory,
|
return;
|
||||||
state.registers.programCounter + 1);
|
|
||||||
ExecuteInterrupt(arguments);
|
case InstructionByte::LESS_THAN_INTEGER:
|
||||||
state.registers.programCounter += programCounterIncrement;
|
case InstructionByte::GREATER_THAN_INTEGER:
|
||||||
break;
|
case InstructionByte::EQUALS_INTEGER:
|
||||||
|
programCounterIncrement
|
||||||
case InstructionByte::CALL:
|
+= GetArguments(instruction, arguments, state.memory, state.registers.programCounter + 1);
|
||||||
programCounterIncrement += GetArguments(
|
DoBooleanLogic(instruction, arguments, state.registers.programCounter + programCounterIncrement);
|
||||||
instruction,
|
break;
|
||||||
arguments,
|
|
||||||
state.memory,
|
case InstructionByte::POP_INTEGER:
|
||||||
state.registers.programCounter + 1);
|
programCounterIncrement
|
||||||
ExecuteCall(arguments, state.registers.programCounter + programCounterIncrement);
|
+= GetArguments(instruction, arguments, state.memory, state.registers.programCounter + 1);
|
||||||
break;
|
ExecutePop(arguments);
|
||||||
|
state.registers.programCounter += programCounterIncrement;
|
||||||
case InstructionByte::RETURN:
|
break;
|
||||||
ExecuteReturn();
|
|
||||||
break;
|
case InstructionByte::PUSH_INTEGER:
|
||||||
|
programCounterIncrement
|
||||||
case InstructionByte::EXIT:
|
+= GetArguments(instruction, arguments, state.memory, state.registers.programCounter + 1);
|
||||||
state.terminated = true;
|
ExecutePush(arguments);
|
||||||
return;
|
state.registers.programCounter += programCounterIncrement;
|
||||||
|
break;
|
||||||
case InstructionByte::LESS_THAN_INTEGER:
|
|
||||||
case InstructionByte::GREATER_THAN_INTEGER:
|
case InstructionByte::IMMEDIATE_INTEGER:
|
||||||
case InstructionByte::EQUALS_INTEGER:
|
case InstructionByte::REGISTER:
|
||||||
programCounterIncrement += GetArguments(
|
case InstructionByte::MEMORY_OP:
|
||||||
instruction,
|
case InstructionByte::LABEL:
|
||||||
arguments,
|
default:
|
||||||
state.memory,
|
throw NonExecutableInstruction(state.registers.programCounter);
|
||||||
state.registers.programCounter + 1);
|
break;
|
||||||
DoBooleanLogic(
|
}
|
||||||
instruction,
|
}
|
||||||
arguments,
|
|
||||||
state.registers.programCounter + programCounterIncrement);
|
void PrintOperand(std::size_t const index, std::string const name)
|
||||||
break;
|
{
|
||||||
|
std::printf("\n%04lu %s", index, name.c_str());
|
||||||
case InstructionByte::POP_INTEGER:
|
}
|
||||||
programCounterIncrement += GetArguments(
|
|
||||||
instruction,
|
void PrintBytes(std::vector<std::uint8_t> const & byteCode)
|
||||||
arguments,
|
{
|
||||||
state.memory,
|
for(std::size_t i = 0; i < byteCode.size(); ++i)
|
||||||
state.registers.programCounter + 1);
|
{
|
||||||
ExecutePop(arguments);
|
InstructionByte const id = static_cast<InstructionByte>(byteCode[i]);
|
||||||
state.registers.programCounter += programCounterIncrement;
|
switch(id)
|
||||||
break;
|
{
|
||||||
|
case InstructionByte::ADD_INTEGER:
|
||||||
case InstructionByte::PUSH_INTEGER:
|
PrintOperand(i, "addi");
|
||||||
programCounterIncrement += GetArguments(
|
break;
|
||||||
instruction,
|
|
||||||
arguments,
|
case InstructionByte::SUBTRACT_INTEGER:
|
||||||
state.memory,
|
PrintOperand(i, "subi");
|
||||||
state.registers.programCounter + 1);
|
break;
|
||||||
ExecutePush(arguments);
|
|
||||||
state.registers.programCounter += programCounterIncrement;
|
case InstructionByte::DIVIDE_INTEGER:
|
||||||
break;
|
PrintOperand(i, "divi");
|
||||||
|
break;
|
||||||
case InstructionByte::IMMEDIATE_INTEGER:
|
|
||||||
case InstructionByte::REGISTER:
|
case InstructionByte::MULTIPLY_INTEGER:
|
||||||
case InstructionByte::MEMORY_OP:
|
PrintOperand(i, "muli");
|
||||||
case InstructionByte::LABEL:
|
break;
|
||||||
default:
|
|
||||||
throw NonExecutableInstruction(state.registers.programCounter);
|
case InstructionByte::SHIFT_LEFT_INTEGER:
|
||||||
break;
|
PrintOperand(i, "shli");
|
||||||
}
|
break;
|
||||||
}
|
|
||||||
|
case InstructionByte::SHIFT_RIGHT_INTEGER:
|
||||||
void PrintOperand(std::size_t const index, std::string const name)
|
PrintOperand(i, "shri");
|
||||||
{
|
break;
|
||||||
std::printf("\n%04lu %s", index, name.c_str());
|
|
||||||
}
|
case InstructionByte::SET_INTEGER:
|
||||||
|
PrintOperand(i, "sti");
|
||||||
void PrintBytes(std::vector<std::uint8_t> const & byteCode)
|
break;
|
||||||
{
|
|
||||||
for(std::size_t i = 0; i < byteCode.size(); ++i)
|
case InstructionByte::JUMP:
|
||||||
{
|
PrintOperand(i, "jmp");
|
||||||
InstructionByte const id = static_cast<InstructionByte>(byteCode[i]);
|
break;
|
||||||
switch(id)
|
|
||||||
{
|
case InstructionByte::INTERRUPT:
|
||||||
case InstructionByte::ADD_INTEGER:
|
PrintOperand(i, "int");
|
||||||
PrintOperand(i, "addi");
|
break;
|
||||||
break;
|
|
||||||
|
case InstructionByte::CALL:
|
||||||
case InstructionByte::SUBTRACT_INTEGER:
|
PrintOperand(i, "call");
|
||||||
PrintOperand(i, "subi");
|
break;
|
||||||
break;
|
|
||||||
|
case InstructionByte::RETURN:
|
||||||
case InstructionByte::DIVIDE_INTEGER:
|
PrintOperand(i, "ret");
|
||||||
PrintOperand(i, "divi");
|
break;
|
||||||
break;
|
|
||||||
|
case InstructionByte::EXIT:
|
||||||
case InstructionByte::MULTIPLY_INTEGER:
|
PrintOperand(i, "exit");
|
||||||
PrintOperand(i, "muli");
|
break;
|
||||||
break;
|
|
||||||
|
case InstructionByte::LESS_THAN_INTEGER:
|
||||||
case InstructionByte::SHIFT_LEFT_INTEGER:
|
PrintOperand(i, "lti");
|
||||||
PrintOperand(i, "shli");
|
break;
|
||||||
break;
|
|
||||||
|
case InstructionByte::GREATER_THAN_INTEGER:
|
||||||
case InstructionByte::SHIFT_RIGHT_INTEGER:
|
PrintOperand(i, "gti");
|
||||||
PrintOperand(i, "shri");
|
break;
|
||||||
break;
|
|
||||||
|
case InstructionByte::EQUALS_INTEGER:
|
||||||
case InstructionByte::SET_INTEGER:
|
PrintOperand(i, "eqi");
|
||||||
PrintOperand(i, "sti");
|
break;
|
||||||
break;
|
|
||||||
|
case InstructionByte::POP_INTEGER:
|
||||||
case InstructionByte::JUMP:
|
PrintOperand(i, "popi");
|
||||||
PrintOperand(i, "jmp");
|
break;
|
||||||
break;
|
|
||||||
|
case InstructionByte::PUSH_INTEGER:
|
||||||
case InstructionByte::INTERRUPT:
|
PrintOperand(i, "pushi");
|
||||||
PrintOperand(i, "int");
|
break;
|
||||||
break;
|
|
||||||
|
case InstructionByte::IMMEDIATE_INTEGER:
|
||||||
case InstructionByte::CALL:
|
std::printf("$%i", Utils::Bytes::Read(byteCode, i + 1));
|
||||||
PrintOperand(i, "call");
|
i += 4u;
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case InstructionByte::RETURN:
|
case InstructionByte::REGISTER:
|
||||||
PrintOperand(i, "ret");
|
{
|
||||||
break;
|
std::string registerName {"A"};
|
||||||
|
registerName[0] += byteCode[i + 1] - 1;
|
||||||
case InstructionByte::EXIT:
|
std::printf("%%%s", registerName.c_str());
|
||||||
PrintOperand(i, "exit");
|
++i;
|
||||||
break;
|
}
|
||||||
|
break;
|
||||||
case InstructionByte::LESS_THAN_INTEGER:
|
|
||||||
PrintOperand(i, "lti");
|
case InstructionByte::MEMORY_OP:
|
||||||
break;
|
std::printf("[]");
|
||||||
|
break;
|
||||||
case InstructionByte::GREATER_THAN_INTEGER:
|
|
||||||
PrintOperand(i, "gti");
|
case InstructionByte::LABEL:
|
||||||
break;
|
std::printf("%i:", Utils::Bytes::Read(byteCode, i + 1));
|
||||||
|
i += 4u;
|
||||||
case InstructionByte::EQUALS_INTEGER:
|
break;
|
||||||
PrintOperand(i, "eqi");
|
|
||||||
break;
|
default:
|
||||||
|
std::printf("UNKNOWN");
|
||||||
case InstructionByte::POP_INTEGER:
|
break;
|
||||||
PrintOperand(i, "popi");
|
}
|
||||||
break;
|
|
||||||
|
std::putc(' ', stdout);
|
||||||
case InstructionByte::PUSH_INTEGER:
|
}
|
||||||
PrintOperand(i, "pushi");
|
|
||||||
break;
|
std::puts("");
|
||||||
|
}
|
||||||
case InstructionByte::IMMEDIATE_INTEGER:
|
|
||||||
std::printf("$%i", Utils::Bytes::Read(byteCode, i + 1));
|
void VirtualMachine::Run()
|
||||||
i += 4u;
|
{
|
||||||
break;
|
while(!IsTerminated())
|
||||||
|
{
|
||||||
case InstructionByte::REGISTER:
|
Step();
|
||||||
{
|
}
|
||||||
std::string registerName {"A"};
|
}
|
||||||
registerName[0] += byteCode[i + 1] - 1;
|
|
||||||
std::printf("%%%s", registerName.c_str());
|
void VirtualMachine::SingleStep()
|
||||||
++i;
|
{
|
||||||
}
|
if(!IsTerminated())
|
||||||
break;
|
{
|
||||||
|
Step();
|
||||||
case InstructionByte::MEMORY_OP:
|
}
|
||||||
std::printf("[]");
|
}
|
||||||
break;
|
|
||||||
|
void VirtualMachine::SetMemorySize(std::size_t const size) { state.memory.resize(size); }
|
||||||
case InstructionByte::LABEL:
|
|
||||||
std::printf("%i:", Utils::Bytes::Read(byteCode, i + 1));
|
void VirtualMachine::LoadCode(std::vector<std::uint8_t> const & byteCode, bool const printDecodedBytes)
|
||||||
i += 4u;
|
{
|
||||||
break;
|
if(printDecodedBytes)
|
||||||
|
{
|
||||||
default:
|
PrintBytes(byteCode);
|
||||||
std::printf("UNKNOWN");
|
}
|
||||||
break;
|
|
||||||
}
|
if(state.memory.size() < byteCode.size())
|
||||||
|
{
|
||||||
std::putc(' ', stdout);
|
throw OutOfMemory(byteCode.size(), state.memory.size());
|
||||||
}
|
}
|
||||||
|
|
||||||
std::puts("");
|
for(std::size_t i = 0; i < byteCode.size(); ++i)
|
||||||
}
|
{
|
||||||
|
state.memory[i] = byteCode[i];
|
||||||
void VirtualMachine::Run()
|
}
|
||||||
{
|
|
||||||
while(!IsTerminated())
|
state.registers.stackPointer = byteCode.size();
|
||||||
{
|
}
|
||||||
Step();
|
|
||||||
}
|
State const & VirtualMachine::GetState() const { return state; }
|
||||||
}
|
Execute::InstructionByte VirtualMachine::GetCurrentInstruction() const
|
||||||
|
{
|
||||||
void VirtualMachine::SingleStep()
|
return static_cast<InstructionByte>(state.memory[state.registers.programCounter]);
|
||||||
{
|
}
|
||||||
if(!IsTerminated())
|
|
||||||
{
|
bool VirtualMachine::IsTerminated() const { return state.terminated; }
|
||||||
Step();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
void VirtualMachine::SetMemorySize(std::size_t const size)
|
|
||||||
{
|
|
||||||
state.memory.resize(size);
|
|
||||||
}
|
|
||||||
|
|
||||||
void VirtualMachine::LoadCode(
|
|
||||||
std::vector<std::uint8_t> const & byteCode,
|
|
||||||
bool const printDecodedBytes)
|
|
||||||
{
|
|
||||||
if (printDecodedBytes)
|
|
||||||
{
|
|
||||||
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();
|
|
||||||
}
|
|
||||||
|
|
||||||
State const & VirtualMachine::GetState() const { return state; }
|
|
||||||
Execute::InstructionByte VirtualMachine::GetCurrentInstruction() const
|
|
||||||
{
|
|
||||||
return static_cast<InstructionByte>(state.memory[state.registers.programCounter]);
|
|
||||||
}
|
|
||||||
|
|
||||||
bool VirtualMachine::IsTerminated() const { return state.terminated; }
|
|
||||||
}
|
}
|
||||||
101
src/main.cpp
101
src/main.cpp
@@ -5,65 +5,60 @@
|
|||||||
|
|
||||||
int main(int argc, char ** argv)
|
int main(int argc, char ** argv)
|
||||||
{
|
{
|
||||||
std::string inputFile;
|
std::string inputFile;
|
||||||
unsigned memorySize = 4096;
|
unsigned memorySize = 4096;
|
||||||
bool printSubstitutions = false, printTokens = false, printBytes = false;
|
bool printSubstitutions = false, printTokens = false, printBytes = false;
|
||||||
bool execute = false, compile = false;
|
bool execute = false, compile = false;
|
||||||
std::string outputFile("program.bin");
|
std::string outputFile("program.bin");
|
||||||
|
|
||||||
auto cli = (
|
auto cli
|
||||||
clipp::value("input file (*.wasm or *.bin)").set(inputFile),
|
= (clipp::value("input file (*.wasm or *.bin)").set(inputFile),
|
||||||
(
|
(clipp::required("-e", "--execute").set(execute),
|
||||||
clipp::required("-e", "--execute").set(execute),
|
clipp::option("-m", "--memory-size") & clipp::value("memory size in bytes (defaults to 4096)", memorySize),
|
||||||
clipp::option("-m", "--memory-size") & clipp::value("memory size in bytes (defaults to 4096)", memorySize),
|
clipp::option("-pb", "--print-bytes").set(printBytes))
|
||||||
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::required("-c", "--compile").set(compile),
|
clipp::option("-pt", "--print-tokens").set(printTokens));
|
||||||
clipp::option("-o", "--output-file") & clipp::value("output file", outputFile)
|
|
||||||
),
|
|
||||||
clipp::option("-ps", "--print-substitutions").set(printSubstitutions),
|
|
||||||
clipp::option("-pt", "--print-tokens").set(printTokens)
|
|
||||||
);
|
|
||||||
|
|
||||||
if (!clipp::parse(argc, argv, cli))
|
if(!clipp::parse(argc, argv, cli))
|
||||||
{
|
{
|
||||||
std::cout << clipp::make_man_page(cli, argv[0]);
|
std::cout << clipp::make_man_page(cli, argv[0]);
|
||||||
return 1;
|
return 1;
|
||||||
}
|
}
|
||||||
|
|
||||||
Wassembler wassembler;
|
Wassembler wassembler;
|
||||||
wassembler.SetMemorySize(memorySize);
|
wassembler.SetMemorySize(memorySize);
|
||||||
if (printSubstitutions)
|
if(printSubstitutions)
|
||||||
{
|
{
|
||||||
wassembler.EnableSubstitutionsLogging();
|
wassembler.EnableSubstitutionsLogging();
|
||||||
}
|
}
|
||||||
|
|
||||||
if (printTokens)
|
if(printTokens)
|
||||||
{
|
{
|
||||||
wassembler.EnableTokensLogging();
|
wassembler.EnableTokensLogging();
|
||||||
}
|
}
|
||||||
|
|
||||||
if (execute)
|
if(execute)
|
||||||
{
|
{
|
||||||
if (printBytes)
|
if(printBytes)
|
||||||
{
|
{
|
||||||
wassembler.EnableByteTranslationLogging();
|
wassembler.EnableByteTranslationLogging();
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!wassembler.CompileAndRun(inputFile))
|
if(!wassembler.CompileAndRun(inputFile))
|
||||||
{
|
{
|
||||||
exit(1);
|
exit(1);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if (compile)
|
if(compile)
|
||||||
{
|
{
|
||||||
if (!wassembler.CompileToFile(inputFile, outputFile))
|
if(!wassembler.CompileToFile(inputFile, outputFile))
|
||||||
{
|
{
|
||||||
exit(1);
|
exit(1);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
@@ -2,158 +2,151 @@
|
|||||||
#include <utils.hpp>
|
#include <utils.hpp>
|
||||||
|
|
||||||
bool trySubstitute(
|
bool trySubstitute(
|
||||||
std::string & line,
|
std::string & line,
|
||||||
std::size_t const lineColumn,
|
std::size_t const lineColumn,
|
||||||
std::vector<std::string> const & substitutionIdentifiers,
|
std::vector<std::string> const & substitutionIdentifiers,
|
||||||
std::vector<std::string> const & substitutionValues)
|
std::vector<std::string> const & substitutionValues)
|
||||||
{
|
{
|
||||||
for(std::size_t i = 0; i < substitutionIdentifiers.size(); ++i)
|
for(std::size_t i = 0; i < substitutionIdentifiers.size(); ++i)
|
||||||
{
|
{
|
||||||
if (line.compare(lineColumn, substitutionIdentifiers[i].size(), substitutionIdentifiers[i]) != 0)
|
if(line.compare(lineColumn, substitutionIdentifiers[i].size(), substitutionIdentifiers[i]) != 0)
|
||||||
{
|
{
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
std::string const lineCopy = line;
|
std::string const lineCopy = line;
|
||||||
line = lineCopy.substr(0, lineColumn) + substitutionValues[i];
|
line = lineCopy.substr(0, lineColumn) + substitutionValues[i];
|
||||||
if (lineCopy.size() > lineColumn + substitutionIdentifiers[i].size())
|
if(lineCopy.size() > lineColumn + substitutionIdentifiers[i].size())
|
||||||
{
|
{
|
||||||
line += lineCopy.substr(lineColumn + substitutionIdentifiers[i].size(), lineCopy.size());
|
line += lineCopy.substr(lineColumn + substitutionIdentifiers[i].size(), lineCopy.size());
|
||||||
}
|
}
|
||||||
|
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
void Preprocessor::extractComment(
|
void Preprocessor::extractComment(std::string & line, std::size_t const lineNumber, std::size_t const lineColumn)
|
||||||
std::string & line,
|
|
||||||
std::size_t const lineNumber,
|
|
||||||
std::size_t const lineColumn)
|
|
||||||
{
|
{
|
||||||
if (line.size() <= lineColumn + 1 ||
|
if(line.size() <= lineColumn + 1 || line.compare(lineColumn + 1, std::string::npos, "DEFINE") <= 0)
|
||||||
line.compare(lineColumn + 1, std::string::npos, "DEFINE") <= 0)
|
{
|
||||||
{
|
// No match or empty DEFINE statement
|
||||||
// No match or empty DEFINE statement
|
line = line.substr(0, lineColumn);
|
||||||
line = line.substr(0, lineColumn);
|
}
|
||||||
}
|
|
||||||
|
|
||||||
enum CommentParseState
|
enum CommentParseState
|
||||||
{
|
{
|
||||||
LookForArgumentStart,
|
LookForArgumentStart,
|
||||||
LookForArgumentEnd
|
LookForArgumentEnd
|
||||||
};
|
};
|
||||||
|
|
||||||
std::string firstArgument, secondArgument;
|
std::string firstArgument, secondArgument;
|
||||||
std::size_t argumentCount = 0, argumentStart = 0;
|
std::size_t argumentCount = 0, argumentStart = 0;
|
||||||
CommentParseState state = LookForArgumentStart;
|
CommentParseState state = LookForArgumentStart;
|
||||||
for(std::size_t i = lineColumn + 7; i < line.size(); ++i)
|
for(std::size_t i = lineColumn + 7; i < line.size(); ++i)
|
||||||
{
|
{
|
||||||
switch(state)
|
switch(state)
|
||||||
{
|
{
|
||||||
case LookForArgumentStart:
|
case LookForArgumentStart:
|
||||||
if(!Utils::isWhitespaceCharacter(line[i]))
|
if(!Utils::isWhitespaceCharacter(line[i]))
|
||||||
{
|
{
|
||||||
argumentStart = i;
|
argumentStart = i;
|
||||||
state = CommentParseState::LookForArgumentEnd;
|
state = CommentParseState::LookForArgumentEnd;
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case LookForArgumentEnd:
|
case LookForArgumentEnd:
|
||||||
if (Utils::isWhitespaceCharacter(line[i]))
|
if(Utils::isWhitespaceCharacter(line[i]))
|
||||||
{
|
{
|
||||||
switch(argumentCount)
|
switch(argumentCount)
|
||||||
{
|
{
|
||||||
case 0:
|
case 0:
|
||||||
firstArgument = line.substr(argumentStart, i - argumentStart);
|
firstArgument = line.substr(argumentStart, i - argumentStart);
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case 1:
|
case 1:
|
||||||
secondArgument = line.substr(argumentStart, i - argumentStart);
|
secondArgument = line.substr(argumentStart, i - argumentStart);
|
||||||
break;
|
break;
|
||||||
|
|
||||||
default:
|
default:
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
++argumentCount;
|
++argumentCount;
|
||||||
state = CommentParseState::LookForArgumentStart;
|
state = CommentParseState::LookForArgumentStart;
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
switch(state)
|
switch(state)
|
||||||
{
|
{
|
||||||
case CommentParseState::LookForArgumentStart:
|
case CommentParseState::LookForArgumentStart:
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case CommentParseState::LookForArgumentEnd:
|
case CommentParseState::LookForArgumentEnd:
|
||||||
switch(argumentCount)
|
switch(argumentCount)
|
||||||
{
|
{
|
||||||
case 0:
|
case 0:
|
||||||
firstArgument = line.substr(argumentStart);
|
firstArgument = line.substr(argumentStart);
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case 1:
|
case 1:
|
||||||
secondArgument = line.substr(argumentStart);
|
secondArgument = line.substr(argumentStart);
|
||||||
break;
|
break;
|
||||||
|
|
||||||
default:
|
default:
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
++argumentCount;
|
++argumentCount;
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (argumentCount > 0)
|
if(argumentCount > 0)
|
||||||
{
|
{
|
||||||
substitutionIdentifiers.push_back(firstArgument);
|
substitutionIdentifiers.push_back(firstArgument);
|
||||||
substitutionValues.push_back(secondArgument);
|
substitutionValues.push_back(secondArgument);
|
||||||
}
|
}
|
||||||
|
|
||||||
line = line.substr(0, lineColumn);
|
line = line.substr(0, lineColumn);
|
||||||
}
|
}
|
||||||
|
|
||||||
void Preprocessor::processLine(std::string & line, std::size_t const lineNumber)
|
void Preprocessor::processLine(std::string & line, std::size_t const lineNumber)
|
||||||
{
|
{
|
||||||
for(std::size_t i = 0; i < line.size(); ++i)
|
for(std::size_t i = 0; i < line.size(); ++i)
|
||||||
{
|
{
|
||||||
if (!Utils::isWhitespaceCharacter(line[i]))
|
if(!Utils::isWhitespaceCharacter(line[i]))
|
||||||
{
|
{
|
||||||
if (trySubstitute(line, i, substitutionIdentifiers, substitutionValues))
|
if(trySubstitute(line, i, substitutionIdentifiers, substitutionValues))
|
||||||
{
|
{
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (line[i] == '#')
|
if(line[i] == '#')
|
||||||
{
|
{
|
||||||
extractComment(line, lineNumber, i);
|
extractComment(line, lineNumber, i);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void Preprocessor::process(std::vector<std::string> & lines)
|
void Preprocessor::process(std::vector<std::string> & lines)
|
||||||
{
|
{
|
||||||
substitutionIdentifiers.clear();
|
substitutionIdentifiers.clear();
|
||||||
substitutionValues.clear();
|
substitutionValues.clear();
|
||||||
|
|
||||||
for(std::size_t i = 0; i < lines.size(); ++i)
|
for(std::size_t i = 0; i < lines.size(); ++i)
|
||||||
{
|
{
|
||||||
processLine(lines[i], i);
|
processLine(lines[i], i);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void Preprocessor::printSubstitutions() const
|
void Preprocessor::printSubstitutions() const
|
||||||
{
|
{
|
||||||
for(std::size_t i = 0; i < substitutionIdentifiers.size(); ++i)
|
for(std::size_t i = 0; i < substitutionIdentifiers.size(); ++i)
|
||||||
{
|
{
|
||||||
std::printf(
|
std::printf("%s -> %s\n", substitutionIdentifiers[i].c_str(), substitutionValues[i].c_str());
|
||||||
"%s -> %s\n",
|
}
|
||||||
substitutionIdentifiers[i].c_str(),
|
|
||||||
substitutionValues[i].c_str());
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
@@ -2,9 +2,7 @@
|
|||||||
|
|
||||||
namespace Token
|
namespace Token
|
||||||
{
|
{
|
||||||
TokenizationError::TokenizationError(Token const & token, std::string const & msg)
|
TokenizationError::TokenizationError(Token const & token, std::string const & msg)
|
||||||
: errorToken(token),
|
: errorToken(token), errorMsg(msg)
|
||||||
errorMsg(msg)
|
{ }
|
||||||
{
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
@@ -3,35 +3,34 @@
|
|||||||
|
|
||||||
namespace Token
|
namespace Token
|
||||||
{
|
{
|
||||||
OperandType GetOperandType(std::string const & op)
|
OperandType GetOperandType(std::string const & op)
|
||||||
{
|
{
|
||||||
static std::map<std::string, OperandType, std::less<>> const operations =
|
static std::map<std::string, OperandType, std::less<>> const operations = {
|
||||||
{
|
{"addi", OperandType::AddInteger},
|
||||||
{ "addi", OperandType::AddInteger },
|
{"subi", OperandType::SubtractInteger},
|
||||||
{ "subi", OperandType::SubtractInteger },
|
{"divi", OperandType::DivideInteger},
|
||||||
{ "divi", OperandType::DivideInteger },
|
{"muli", OperandType::MultiplyInteger},
|
||||||
{ "muli", OperandType::MultiplyInteger },
|
{"shri", OperandType::ShiftIntegerRight},
|
||||||
{ "shri", OperandType::ShiftIntegerRight },
|
{"shli", OperandType::ShiftIntegerLeft},
|
||||||
{ "shli", OperandType::ShiftIntegerLeft },
|
{"jmp", OperandType::Jump},
|
||||||
{ "jmp", OperandType::Jump },
|
{"call", OperandType::CallFunction},
|
||||||
{ "call", OperandType::CallFunction },
|
{"ret", OperandType::ReturnFromFunction},
|
||||||
{ "ret", OperandType::ReturnFromFunction },
|
{"exit", OperandType::ExitProgram},
|
||||||
{ "exit", OperandType::ExitProgram },
|
{"lti", OperandType::LessThanInteger},
|
||||||
{ "lti", OperandType::LessThanInteger },
|
{"gti", OperandType::GreaterThanInteger},
|
||||||
{ "gti", OperandType::GreaterThanInteger },
|
{"eqi", OperandType::EqualInteger},
|
||||||
{ "eqi", OperandType::EqualInteger },
|
{"seti", OperandType::SetInteger},
|
||||||
{ "seti", OperandType::SetInteger },
|
{"int", OperandType::Interrupt},
|
||||||
{ "int", OperandType::Interrupt },
|
{"pushi", OperandType::PushInteger},
|
||||||
{ "pushi", OperandType::PushInteger},
|
{"popi", OperandType::PopInteger},
|
||||||
{ "popi", OperandType::PopInteger},
|
};
|
||||||
};
|
|
||||||
|
|
||||||
auto const & result = operations.find(op);
|
auto const & result = operations.find(op);
|
||||||
if (result != operations.end())
|
if(result != operations.end())
|
||||||
{
|
{
|
||||||
return result->second;
|
return result->second;
|
||||||
}
|
}
|
||||||
|
|
||||||
return OperandType::Unknown;
|
return OperandType::Unknown;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -1,24 +1,19 @@
|
|||||||
#include <token/registertype.hpp>
|
|
||||||
#include <map>
|
#include <map>
|
||||||
|
#include <token/registertype.hpp>
|
||||||
|
|
||||||
namespace Token
|
namespace Token
|
||||||
{
|
{
|
||||||
RegisterType GetRegisterType(std::string const & reg)
|
RegisterType GetRegisterType(std::string const & reg)
|
||||||
{
|
{
|
||||||
static std::map<std::string, RegisterType, std::less<>> const registers =
|
static std::map<std::string, RegisterType, std::less<>> const registers
|
||||||
{
|
= {{"A", RegisterType::A}, {"B", RegisterType::B}, {"C", RegisterType::C}, {"D", RegisterType::D}};
|
||||||
{ "A", RegisterType::A },
|
|
||||||
{ "B", RegisterType::B },
|
|
||||||
{ "C", RegisterType::C },
|
|
||||||
{ "D", RegisterType::D }
|
|
||||||
};
|
|
||||||
|
|
||||||
auto const & result = registers.find(reg);
|
auto const & result = registers.find(reg);
|
||||||
if (result != registers.end())
|
if(result != registers.end())
|
||||||
{
|
{
|
||||||
return result->second;
|
return result->second;
|
||||||
}
|
}
|
||||||
|
|
||||||
return RegisterType::Unknown;
|
return RegisterType::Unknown;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -3,251 +3,234 @@
|
|||||||
|
|
||||||
namespace Token
|
namespace Token
|
||||||
{
|
{
|
||||||
Token::Token(TokenType _type, bool validness, int const _lineNumber, int const _lineColumn)
|
Token::Token(TokenType _type, bool validness, int const _lineNumber, int const _lineColumn)
|
||||||
: lineNumber(_lineNumber),
|
: lineNumber(_lineNumber), lineColumn(_lineColumn), type(_type), valueType(TokenValueType::None),
|
||||||
lineColumn(_lineColumn),
|
isValid(validness), data(0), errorMessage()
|
||||||
type(_type),
|
{ }
|
||||||
valueType(TokenValueType::None),
|
|
||||||
isValid(validness),
|
|
||||||
data(0),
|
|
||||||
errorMessage()
|
|
||||||
{
|
|
||||||
}
|
|
||||||
|
|
||||||
Token::Token(TokenType _type, std::string const & string, bool validness, int const _lineNumber, int const _lineColumn)
|
Token::Token(
|
||||||
: lineNumber(_lineNumber),
|
TokenType _type,
|
||||||
lineColumn(_lineColumn),
|
std::string const & string,
|
||||||
type(_type),
|
bool validness,
|
||||||
valueType(TokenValueType::String),
|
int const _lineNumber,
|
||||||
isValid(validness),
|
int const _lineColumn)
|
||||||
data(string),
|
: lineNumber(_lineNumber), lineColumn(_lineColumn), type(_type), valueType(TokenValueType::String),
|
||||||
errorMessage()
|
isValid(validness), data(string), errorMessage()
|
||||||
{
|
{ }
|
||||||
}
|
|
||||||
|
|
||||||
Token::Token(TokenType _type, int value, bool validness, int const _lineNumber, int const _lineColumn)
|
Token::Token(TokenType _type, int value, bool validness, int const _lineNumber, int const _lineColumn)
|
||||||
: lineNumber(_lineNumber),
|
: lineNumber(_lineNumber), lineColumn(_lineColumn), type(_type), valueType(TokenValueType::Integer),
|
||||||
lineColumn(_lineColumn),
|
isValid(validness), data(value), errorMessage()
|
||||||
type(_type),
|
{ }
|
||||||
valueType(TokenValueType::Integer),
|
|
||||||
isValid(validness),
|
|
||||||
data(value),
|
|
||||||
errorMessage()
|
|
||||||
{
|
|
||||||
}
|
|
||||||
|
|
||||||
Token::Token(TokenType _type, RegisterType const registerType, bool validness, int const _lineNumber, int const _lineColumn)
|
Token::Token(
|
||||||
: lineNumber(_lineNumber),
|
TokenType _type,
|
||||||
lineColumn(_lineColumn),
|
RegisterType const registerType,
|
||||||
type(_type),
|
bool validness,
|
||||||
valueType(TokenValueType::Register),
|
int const _lineNumber,
|
||||||
isValid(validness),
|
int const _lineColumn)
|
||||||
data(registerType),
|
: lineNumber(_lineNumber), lineColumn(_lineColumn), type(_type), valueType(TokenValueType::Register),
|
||||||
errorMessage()
|
isValid(validness), data(registerType), errorMessage()
|
||||||
{
|
{ }
|
||||||
}
|
|
||||||
|
|
||||||
Token::Token(TokenType _type, OperandType const operandType, bool validness, int const _lineNumber, int const _lineColumn)
|
Token::Token(
|
||||||
: lineNumber(_lineNumber),
|
TokenType _type,
|
||||||
lineColumn(_lineColumn),
|
OperandType const operandType,
|
||||||
type(_type),
|
bool validness,
|
||||||
valueType(TokenValueType::Operand),
|
int const _lineNumber,
|
||||||
isValid(validness),
|
int const _lineColumn)
|
||||||
data(operandType),
|
: lineNumber(_lineNumber), lineColumn(_lineColumn), type(_type), valueType(TokenValueType::Operand),
|
||||||
errorMessage()
|
isValid(validness), data(operandType), errorMessage()
|
||||||
{
|
{ }
|
||||||
}
|
|
||||||
|
|
||||||
Token::Token(Token const & other)
|
Token::Token(Token const & other)
|
||||||
: lineNumber(other.lineNumber),
|
: lineNumber(other.lineNumber), lineColumn(other.lineColumn), type(other.type), valueType(other.valueType),
|
||||||
lineColumn(other.lineColumn),
|
isValid(other.isValid), data(other.data), errorMessage(other.errorMessage)
|
||||||
type(other.type),
|
{ }
|
||||||
valueType(other.valueType),
|
|
||||||
isValid(other.isValid),
|
|
||||||
data(other.data),
|
|
||||||
errorMessage(other.errorMessage)
|
|
||||||
{
|
|
||||||
}
|
|
||||||
|
|
||||||
Token Token::CreateEmptyToken(int const lineNumber, int const lineColumn)
|
Token Token::CreateEmptyToken(int const lineNumber, int const lineColumn)
|
||||||
{
|
{
|
||||||
return Token(TokenType::Unknown, false, lineNumber, lineColumn);
|
return Token(TokenType::Unknown, false, lineNumber, lineColumn);
|
||||||
}
|
}
|
||||||
|
|
||||||
Token Token::CreateErrorToken(std::string const & message, TokenType const type, int const lineNumber, int const lineColumn)
|
Token Token::CreateErrorToken(
|
||||||
{
|
std::string const & message,
|
||||||
Token token(type, false, lineNumber, lineColumn);
|
TokenType const type,
|
||||||
token.errorMessage = message;
|
int const lineNumber,
|
||||||
return token;
|
int const lineColumn)
|
||||||
}
|
{
|
||||||
|
Token token(type, false, lineNumber, lineColumn);
|
||||||
|
token.errorMessage = message;
|
||||||
|
return token;
|
||||||
|
}
|
||||||
|
|
||||||
Token Token::CreateStatementEndToken(int const lineNumber, int const lineColumn)
|
Token Token::CreateStatementEndToken(int const lineNumber, int const lineColumn)
|
||||||
{
|
{
|
||||||
return Token(TokenType::StatementEnd, true, lineNumber, lineColumn);
|
return Token(TokenType::StatementEnd, true, lineNumber, lineColumn);
|
||||||
}
|
}
|
||||||
|
|
||||||
Token Token::CreateLabelDefinitionToken(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::LabelDefinition, 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)
|
Token Token::CreateLabelArgumentToken(std::string const & string, int const lineNumber, int const lineColumn)
|
||||||
{
|
{
|
||||||
return Token(TokenType::LabelArgument, string, true, lineNumber, 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)
|
||||||
{
|
{
|
||||||
return Token(TokenType::ImmediateInteger, value, true, lineNumber, lineColumn);
|
return Token(TokenType::ImmediateInteger, value, true, lineNumber, lineColumn);
|
||||||
}
|
}
|
||||||
|
|
||||||
Token Token::CreateRegisterToken(RegisterType const registerType, int const lineNumber, int const lineColumn)
|
Token Token::CreateRegisterToken(RegisterType const registerType, int const lineNumber, int const lineColumn)
|
||||||
{
|
{
|
||||||
return Token(TokenType::Register, registerType, registerType != RegisterType::Unknown, lineNumber, lineColumn);
|
return Token(TokenType::Register, registerType, registerType != RegisterType::Unknown, lineNumber, lineColumn);
|
||||||
}
|
}
|
||||||
|
|
||||||
Token Token::CreateOperandToken(OperandType const operandType, int const lineNumber, int const lineColumn)
|
Token Token::CreateOperandToken(OperandType const operandType, int const lineNumber, int const lineColumn)
|
||||||
{
|
{
|
||||||
return Token(TokenType::Operand, operandType, operandType != OperandType::Unknown, lineNumber, lineColumn);
|
return Token(TokenType::Operand, operandType, operandType != OperandType::Unknown, lineNumber, lineColumn);
|
||||||
}
|
}
|
||||||
|
|
||||||
Token Token::CreateMemoryToken(RegisterType const registerType, int const lineNumber, int const lineColumn)
|
Token Token::CreateMemoryToken(RegisterType const registerType, int const lineNumber, int const lineColumn)
|
||||||
{
|
{
|
||||||
if (registerType == RegisterType::Unknown)
|
if(registerType == RegisterType::Unknown)
|
||||||
{
|
{
|
||||||
return CreateErrorToken("Unknown register used", TokenType::Register, lineNumber, lineColumn);
|
return CreateErrorToken("Unknown register used", TokenType::Register, lineNumber, lineColumn);
|
||||||
}
|
}
|
||||||
|
|
||||||
return Token(TokenType::Memory, registerType, true, lineNumber, lineColumn);
|
return Token(TokenType::Memory, registerType, true, lineNumber, lineColumn);
|
||||||
}
|
}
|
||||||
|
|
||||||
Token Token::CreateMemoryToken(int const value, int const lineNumber, int const lineColumn)
|
Token Token::CreateMemoryToken(int const value, int const lineNumber, int const lineColumn)
|
||||||
{
|
{
|
||||||
return Token(TokenType::Memory, value, true, lineNumber, lineColumn);
|
return Token(TokenType::Memory, value, true, lineNumber, lineColumn);
|
||||||
}
|
}
|
||||||
|
|
||||||
std::string Token::GetName() const
|
std::string Token::GetName() const
|
||||||
{
|
{
|
||||||
switch(type)
|
switch(type)
|
||||||
{
|
{
|
||||||
case TokenType::ImmediateInteger:
|
case TokenType::ImmediateInteger:
|
||||||
return "immediate value";
|
return "immediate value";
|
||||||
|
|
||||||
case TokenType::Operand:
|
case TokenType::Operand:
|
||||||
return "operand";
|
return "operand";
|
||||||
|
|
||||||
case TokenType::Register:
|
case TokenType::Register:
|
||||||
return "register";
|
return "register";
|
||||||
|
|
||||||
case TokenType::StatementEnd:
|
case TokenType::StatementEnd:
|
||||||
return "end of statement";
|
return "end of statement";
|
||||||
|
|
||||||
case TokenType::LabelDefinition:
|
case TokenType::LabelDefinition:
|
||||||
case TokenType::LabelArgument:
|
case TokenType::LabelArgument:
|
||||||
return "label";
|
return "label";
|
||||||
|
|
||||||
case TokenType::Memory:
|
case TokenType::Memory:
|
||||||
return "memory location";
|
return "memory location";
|
||||||
|
|
||||||
case TokenType::Unknown:
|
case TokenType::Unknown:
|
||||||
default:
|
default:
|
||||||
return "UNKNOWN";
|
return "UNKNOWN";
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void Token::Print() const
|
void Token::Print() const
|
||||||
{
|
{
|
||||||
std::putc(' ', stdout);
|
std::putc(' ', stdout);
|
||||||
switch(type)
|
switch(type)
|
||||||
{
|
{
|
||||||
case TokenType::ImmediateInteger:
|
case TokenType::ImmediateInteger:
|
||||||
if (isValid)
|
if(isValid)
|
||||||
{
|
{
|
||||||
std::printf("%i", std::get<int>(data));
|
std::printf("%i", std::get<int>(data));
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
std::printf("BAD_IMM");
|
std::printf("BAD_IMM");
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case TokenType::Operand:
|
case TokenType::Operand:
|
||||||
if (isValid)
|
if(isValid)
|
||||||
{
|
{
|
||||||
OperandType const opType = std::get<OperandType>(data);
|
OperandType const opType = std::get<OperandType>(data);
|
||||||
switch(opType)
|
switch(opType)
|
||||||
{
|
{
|
||||||
case OperandType::Unknown:
|
case OperandType::Unknown:
|
||||||
std::printf("unknown_op");
|
std::printf("unknown_op");
|
||||||
break;
|
break;
|
||||||
|
|
||||||
default:
|
default:
|
||||||
std::printf("op%i", static_cast<int>(opType));
|
std::printf("op%i", static_cast<int>(opType));
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
std::printf("BAD_OP");
|
std::printf("BAD_OP");
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case TokenType::Register:
|
case TokenType::Register:
|
||||||
if (isValid)
|
if(isValid)
|
||||||
{
|
{
|
||||||
RegisterType const regType = std::get<RegisterType>(data);
|
RegisterType const regType = std::get<RegisterType>(data);
|
||||||
switch(regType)
|
switch(regType)
|
||||||
{
|
{
|
||||||
default:
|
default:
|
||||||
std::printf("%%%i", static_cast<int>(regType));
|
std::printf("%%%i", static_cast<int>(regType));
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case RegisterType::Unknown:
|
case RegisterType::Unknown:
|
||||||
std::printf("%%unknown_reg");
|
std::printf("%%unknown_reg");
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
std::printf("BAD_REG");
|
std::printf("BAD_REG");
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case TokenType::StatementEnd:
|
case TokenType::StatementEnd:
|
||||||
std::printf("EOS");
|
std::printf("EOS");
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case TokenType::LabelDefinition:
|
case TokenType::LabelDefinition:
|
||||||
std::printf("LABEL_DEF=%s", std::get<std::string>(data).c_str());
|
std::printf("LABEL_DEF=%s", std::get<std::string>(data).c_str());
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case TokenType::LabelArgument:
|
case TokenType::LabelArgument:
|
||||||
std::printf("LABEL_ARG=%s", std::get<std::string>(data).c_str());
|
std::printf("LABEL_ARG=%s", std::get<std::string>(data).c_str());
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case TokenType::Memory:
|
case TokenType::Memory:
|
||||||
{
|
{
|
||||||
switch(valueType)
|
switch(valueType)
|
||||||
{
|
{
|
||||||
case TokenValueType::Integer:
|
case TokenValueType::Integer:
|
||||||
std::printf("[$%i]", std::get<int>(data));
|
std::printf("[$%i]", std::get<int>(data));
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case TokenValueType::Register:
|
case TokenValueType::Register:
|
||||||
std::printf("[%%%i]", static_cast<int>(std::get<RegisterType>(data)));
|
std::printf("[%%%i]", static_cast<int>(std::get<RegisterType>(data)));
|
||||||
break;
|
break;
|
||||||
|
|
||||||
default:
|
default:
|
||||||
std::printf("[UNKNOWN_TYPE]");
|
std::printf("[UNKNOWN_TYPE]");
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case TokenType::Unknown:
|
case TokenType::Unknown:
|
||||||
default:
|
default:
|
||||||
std::printf("UNKNOWN_TOKEN");
|
std::printf("UNKNOWN_TOKEN");
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -7,257 +7,228 @@
|
|||||||
|
|
||||||
namespace Token
|
namespace Token
|
||||||
{
|
{
|
||||||
std::optional<int> TryParseInt(std::string const & string)
|
std::optional<int> TryParseInt(std::string const & string)
|
||||||
{
|
{
|
||||||
try
|
try
|
||||||
{
|
{
|
||||||
int value = std::stoi(string);
|
int value = std::stoi(string);
|
||||||
return std::make_optional<int>(value);
|
return std::make_optional<int>(value);
|
||||||
}
|
}
|
||||||
catch(std::invalid_argument &)
|
catch(std::invalid_argument &)
|
||||||
{
|
{
|
||||||
return std::nullopt;
|
return std::nullopt;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
Token GetCharacterLiteralToken(
|
Token
|
||||||
std::string const & token,
|
GetCharacterLiteralToken(std::string const & token, std::size_t const lineNumber, std::size_t const lineColumn)
|
||||||
std::size_t const lineNumber,
|
{
|
||||||
std::size_t const lineColumn)
|
for(std::size_t i = 1; i < token.size(); ++i)
|
||||||
{
|
{
|
||||||
for(std::size_t i = 1; i < token.size(); ++i)
|
if(token[i] == '\'')
|
||||||
{
|
{
|
||||||
if (token[i] == '\'')
|
if(i != 2)
|
||||||
{
|
{
|
||||||
if (i != 2)
|
return Token::CreateErrorToken(
|
||||||
{
|
"Character literal must be exactly 1 character long between single quotes",
|
||||||
return Token::CreateErrorToken(
|
TokenType::ImmediateInteger,
|
||||||
"Character literal must be exactly 1 character long between single quotes",
|
lineNumber,
|
||||||
TokenType::ImmediateInteger,
|
lineColumn + 1u);
|
||||||
lineNumber,
|
}
|
||||||
lineColumn + 1u);
|
else
|
||||||
}
|
{
|
||||||
else
|
return Token::CreateImmediateValueToken(token[1], lineNumber, lineColumn + 1);
|
||||||
{
|
}
|
||||||
return Token::CreateImmediateValueToken(
|
}
|
||||||
token[1],
|
}
|
||||||
lineNumber,
|
|
||||||
lineColumn + 1);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return Token::CreateErrorToken(
|
return Token::CreateErrorToken(
|
||||||
"Non terminated character literal",
|
"Non terminated character literal",
|
||||||
TokenType::ImmediateInteger,
|
TokenType::ImmediateInteger,
|
||||||
lineNumber,
|
lineNumber,
|
||||||
lineColumn);
|
lineColumn);
|
||||||
}
|
}
|
||||||
|
|
||||||
Token GetMemoryToken(
|
Token GetMemoryToken(std::string const & token, std::size_t const lineNumber, std::size_t const lineColumn)
|
||||||
std::string const & token,
|
{
|
||||||
std::size_t const lineNumber,
|
// Minimal example: [$1] or [%A]
|
||||||
std::size_t const lineColumn)
|
if(token.size() < 4)
|
||||||
{
|
{
|
||||||
// Minimal example: [$1] or [%A]
|
return Token::CreateErrorToken(
|
||||||
if(token.size() < 4)
|
"Memory address statement is empty",
|
||||||
{
|
TokenType::Memory,
|
||||||
return Token::CreateErrorToken(
|
lineNumber,
|
||||||
"Memory address statement is empty",
|
lineColumn);
|
||||||
TokenType::Memory,
|
}
|
||||||
lineNumber,
|
|
||||||
lineColumn);
|
|
||||||
}
|
|
||||||
|
|
||||||
if (token[0] != '[' || token[token.size() - 1] != ']')
|
if(token[0] != '[' || token[token.size() - 1] != ']')
|
||||||
{
|
{
|
||||||
return Token::CreateErrorToken(
|
return Token::CreateErrorToken(
|
||||||
"Non terminated memory address brackets",
|
"Non terminated memory address brackets",
|
||||||
TokenType::Memory,
|
TokenType::Memory,
|
||||||
lineNumber,
|
lineNumber,
|
||||||
lineColumn);
|
lineColumn);
|
||||||
}
|
}
|
||||||
|
|
||||||
char const memoryPrefix = token[1];
|
char const memoryPrefix = token[1];
|
||||||
std::string const valueString = token.substr(2, token.size() - 3u);
|
std::string const valueString = token.substr(2, token.size() - 3u);
|
||||||
if (memoryPrefix == '$')
|
if(memoryPrefix == '$')
|
||||||
{
|
{
|
||||||
auto const result = TryParseInt(valueString);
|
auto const result = TryParseInt(valueString);
|
||||||
|
|
||||||
if (result.has_value())
|
if(result.has_value())
|
||||||
{
|
{
|
||||||
return Token::CreateMemoryToken(
|
return Token::CreateMemoryToken(result.value(), lineNumber, lineColumn);
|
||||||
result.value(),
|
}
|
||||||
lineNumber,
|
|
||||||
lineColumn);
|
|
||||||
}
|
|
||||||
|
|
||||||
return Token::CreateErrorToken(
|
return Token::CreateErrorToken(
|
||||||
"Memory immediate address cannot be parsed as an integer",
|
"Memory immediate address cannot be parsed as an integer",
|
||||||
TokenType::Memory,
|
TokenType::Memory,
|
||||||
lineNumber,
|
lineNumber,
|
||||||
lineColumn);
|
lineColumn);
|
||||||
}
|
}
|
||||||
else if (memoryPrefix == '%')
|
else if(memoryPrefix == '%')
|
||||||
{
|
{
|
||||||
return Token::CreateMemoryToken(
|
return Token::CreateMemoryToken(GetRegisterType(valueString), lineNumber, lineColumn);
|
||||||
GetRegisterType(valueString),
|
}
|
||||||
lineNumber,
|
|
||||||
lineColumn);
|
|
||||||
}
|
|
||||||
|
|
||||||
return Token::CreateErrorToken(
|
return Token::CreateErrorToken(
|
||||||
"Memory immediate address contains an unexpected value",
|
"Memory immediate address contains an unexpected value",
|
||||||
TokenType::Memory,
|
TokenType::Memory,
|
||||||
lineNumber,
|
lineNumber,
|
||||||
lineColumn + 1u);
|
lineColumn + 1u);
|
||||||
}
|
}
|
||||||
|
|
||||||
Token GetUnterminatedCharacterLiteralError(
|
Token GetUnterminatedCharacterLiteralError(std::size_t const lineNumber, std::size_t const lineColumn)
|
||||||
std::size_t const lineNumber,
|
{
|
||||||
std::size_t const lineColumn)
|
return Token::CreateErrorToken(
|
||||||
{
|
"Unterminated character or string literal",
|
||||||
return Token::CreateErrorToken(
|
TokenType::Unknown,
|
||||||
"Unterminated character or string literal",
|
lineNumber,
|
||||||
TokenType::Unknown,
|
lineColumn);
|
||||||
lineNumber,
|
}
|
||||||
lineColumn);
|
|
||||||
}
|
|
||||||
|
|
||||||
Token Tokenizer::ExtractToken(
|
Token Tokenizer::ExtractToken(
|
||||||
std::string const & string,
|
std::string const & string,
|
||||||
std::size_t const lineNumber,
|
std::size_t const lineNumber,
|
||||||
std::size_t const lineColumn) const
|
std::size_t const lineColumn) const
|
||||||
{
|
{
|
||||||
char const prefix = string[0];
|
char const prefix = string[0];
|
||||||
switch(prefix)
|
switch(prefix)
|
||||||
{
|
{
|
||||||
case '$':
|
case '$':
|
||||||
{
|
{
|
||||||
auto const result = TryParseInt(string.substr(1, string.size()));
|
auto const result = TryParseInt(string.substr(1, string.size()));
|
||||||
|
|
||||||
if (result.has_value())
|
if(result.has_value())
|
||||||
{
|
{
|
||||||
return Token::CreateImmediateValueToken(
|
return Token::CreateImmediateValueToken(result.value(), lineNumber, lineColumn);
|
||||||
result.value(),
|
}
|
||||||
lineNumber,
|
|
||||||
lineColumn);
|
|
||||||
}
|
|
||||||
|
|
||||||
return Token::CreateErrorToken(
|
return Token::CreateErrorToken(
|
||||||
"Immediate cannot be parsed as an integer",
|
"Immediate cannot be parsed as an integer",
|
||||||
TokenType::ImmediateInteger,
|
TokenType::ImmediateInteger,
|
||||||
lineNumber,
|
lineNumber,
|
||||||
lineColumn);
|
lineColumn);
|
||||||
}
|
}
|
||||||
|
|
||||||
case '%':
|
case '%':
|
||||||
return Token::CreateRegisterToken(GetRegisterType(
|
return Token::CreateRegisterToken(
|
||||||
string.substr(1, string.size())),
|
GetRegisterType(string.substr(1, string.size())),
|
||||||
lineNumber,
|
lineNumber,
|
||||||
lineColumn);
|
lineColumn);
|
||||||
|
|
||||||
case '\'':
|
case '\'':
|
||||||
return GetCharacterLiteralToken(string, lineNumber, lineColumn);
|
return GetCharacterLiteralToken(string, lineNumber, lineColumn);
|
||||||
|
|
||||||
case ';':
|
case ';':
|
||||||
return Token::CreateStatementEndToken(lineNumber, lineColumn);
|
return Token::CreateStatementEndToken(lineNumber, lineColumn);
|
||||||
|
|
||||||
case '[':
|
case '[':
|
||||||
return GetMemoryToken(string, lineNumber, lineColumn);
|
return GetMemoryToken(string, lineNumber, lineColumn);
|
||||||
|
|
||||||
default:
|
default:
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
char const postfix = string[string.size() - 1];
|
char const postfix = string[string.size() - 1];
|
||||||
switch(postfix)
|
switch(postfix)
|
||||||
{
|
{
|
||||||
case ']':
|
case ']':
|
||||||
return GetMemoryToken(string, lineNumber, lineColumn);
|
return GetMemoryToken(string, lineNumber, lineColumn);
|
||||||
|
|
||||||
case ':':
|
case ':':
|
||||||
// TODO check if label is an Operand?
|
// TODO check if label is an Operand?
|
||||||
return Token::CreateLabelDefinitionToken(
|
return Token::CreateLabelDefinitionToken(string.substr(0, string.size() - 1), lineNumber, lineColumn);
|
||||||
string.substr(0, string.size() - 1),
|
|
||||||
lineNumber,
|
|
||||||
lineColumn);
|
|
||||||
|
|
||||||
case '\'':
|
case '\'':
|
||||||
case '\"':
|
case '\"':
|
||||||
// This shouldn't happen
|
// This shouldn't happen
|
||||||
return GetUnterminatedCharacterLiteralError(lineNumber, lineColumn);
|
return GetUnterminatedCharacterLiteralError(lineNumber, lineColumn);
|
||||||
}
|
}
|
||||||
|
|
||||||
OperandType const opType = GetOperandType(string);
|
OperandType const opType = GetOperandType(string);
|
||||||
if (opType != OperandType::Unknown)
|
if(opType != OperandType::Unknown)
|
||||||
{
|
{
|
||||||
return Token::CreateOperandToken(opType, lineNumber, lineColumn);
|
return Token::CreateOperandToken(opType, lineNumber, lineColumn);
|
||||||
}
|
}
|
||||||
|
|
||||||
// Last resort: it must be a jump target
|
// Last resort: it must be a jump target
|
||||||
return Token::CreateLabelArgumentToken(string, lineNumber, lineColumn);
|
return Token::CreateLabelArgumentToken(string, lineNumber, lineColumn);
|
||||||
}
|
}
|
||||||
|
|
||||||
void Tokenizer::Tokenize(
|
void Tokenizer::Tokenize(std::string const & line, std::size_t const lineNumber, std::vector<Token> & tokens)
|
||||||
std::string const & line,
|
{
|
||||||
std::size_t const lineNumber,
|
for(std::size_t column = 0u; column < line.size(); ++column)
|
||||||
std::vector<Token> & tokens)
|
{
|
||||||
{
|
if(Utils::isWhitespaceCharacter(line[column]))
|
||||||
for(std::size_t column = 0u; column < line.size(); ++column)
|
{
|
||||||
{
|
continue;
|
||||||
if (Utils::isWhitespaceCharacter(line[column]))
|
}
|
||||||
{
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
|
|
||||||
switch(line[column])
|
switch(line[column])
|
||||||
{
|
{
|
||||||
case '\'':
|
case '\'':
|
||||||
case '\"':
|
case '\"':
|
||||||
{
|
{
|
||||||
auto const result = Utils::getValueSurroundedBy(
|
auto const result = Utils::getValueSurroundedBy(line, column, line[column]);
|
||||||
line,
|
if(result.has_value())
|
||||||
column,
|
{
|
||||||
line[column]);
|
tokens.push_back(ExtractToken(result.value(), lineNumber, column));
|
||||||
if (result.has_value())
|
column += result.value().size() - 1;
|
||||||
{
|
}
|
||||||
tokens.push_back(ExtractToken(result.value(), lineNumber, column));
|
else
|
||||||
column += result.value().size() - 1;
|
{
|
||||||
}
|
tokens.push_back(GetUnterminatedCharacterLiteralError(lineNumber, column));
|
||||||
else
|
|
||||||
{
|
|
||||||
tokens.push_back(
|
|
||||||
GetUnterminatedCharacterLiteralError(lineNumber, column));
|
|
||||||
|
|
||||||
// Parsing must stop here, the line is malformed
|
// Parsing must stop here, the line is malformed
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case ';':
|
case ';':
|
||||||
tokens.push_back(ExtractToken(";", lineNumber, column));
|
tokens.push_back(ExtractToken(";", lineNumber, column));
|
||||||
break;
|
break;
|
||||||
|
|
||||||
default:
|
default:
|
||||||
{
|
{
|
||||||
auto const result = Utils::getValueSurroundedByWhitespace(line, column);
|
auto const result = Utils::getValueSurroundedByWhitespace(line, column);
|
||||||
auto const lastCharacterIndex = result.size() - 1;
|
auto const lastCharacterIndex = result.size() - 1;
|
||||||
if (result[lastCharacterIndex] == ';')
|
if(result[lastCharacterIndex] == ';')
|
||||||
{
|
{
|
||||||
tokens.push_back(ExtractToken(result.substr(0, result.size() -1), lineNumber, column));
|
tokens.push_back(ExtractToken(result.substr(0, result.size() - 1), lineNumber, column));
|
||||||
tokens.push_back(ExtractToken(";", lineNumber, column + lastCharacterIndex));
|
tokens.push_back(ExtractToken(";", lineNumber, column + lastCharacterIndex));
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
tokens.push_back(ExtractToken(result, lineNumber, column));
|
tokens.push_back(ExtractToken(result, lineNumber, column));
|
||||||
}
|
}
|
||||||
|
|
||||||
column += result.size();
|
column += result.size();
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -2,63 +2,53 @@
|
|||||||
|
|
||||||
namespace Utils
|
namespace Utils
|
||||||
{
|
{
|
||||||
bool isWhitespaceCharacter(char const c)
|
bool isWhitespaceCharacter(char const c) { return c == '\n' || c == ' ' || c == '\t' || c == '\r'; }
|
||||||
{
|
|
||||||
return c == '\n' || c == ' ' || c == '\t' || c == '\r';
|
|
||||||
}
|
|
||||||
|
|
||||||
std::optional<std::string> getValueSurroundedBy(
|
std::optional<std::string>
|
||||||
std::string const & src,
|
getValueSurroundedBy(std::string const & src, std::size_t const pos, char const surroundingCharacter)
|
||||||
std::size_t const pos,
|
{
|
||||||
char const surroundingCharacter)
|
for(std::size_t i = pos + 1; i < src.size(); ++i)
|
||||||
{
|
{
|
||||||
for(std::size_t i = pos + 1; i < src.size(); ++i)
|
if(src[i] == surroundingCharacter)
|
||||||
{
|
{
|
||||||
if (src[i] == surroundingCharacter)
|
return std::make_optional(src.substr(pos, (i + 1) - pos));
|
||||||
{
|
}
|
||||||
return std::make_optional(src.substr(pos, (i + 1) - pos));
|
}
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return std::nullopt;
|
return std::nullopt;
|
||||||
}
|
}
|
||||||
|
|
||||||
std::string getValueSurroundedByWhitespace(
|
std::string getValueSurroundedByWhitespace(std::string const & src, std::size_t const pos)
|
||||||
std::string const & src,
|
{
|
||||||
std::size_t const pos)
|
for(std::size_t i = pos + 1; i < src.size(); ++i)
|
||||||
{
|
{
|
||||||
for(std::size_t i = pos + 1; i < src.size(); ++i)
|
if(isWhitespaceCharacter(src[i]))
|
||||||
{
|
{
|
||||||
if (isWhitespaceCharacter(src[i]))
|
return src.substr(pos, i - pos);
|
||||||
{
|
}
|
||||||
return src.substr(pos, i - pos);
|
}
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return src.substr(pos);
|
return src.substr(pos);
|
||||||
}
|
}
|
||||||
|
|
||||||
namespace Bytes
|
namespace Bytes
|
||||||
{
|
{
|
||||||
void Write(
|
void Write(int const value, std::vector<std::uint8_t> & vec, std::size_t const pos)
|
||||||
int const value,
|
{
|
||||||
std::vector<std::uint8_t> & vec,
|
vec[pos] = value & 0xFF;
|
||||||
std::size_t const pos)
|
vec[pos + 1] = (value >> 8) & 0xFF;
|
||||||
{
|
vec[pos + 2] = (value >> 16) & 0xFF;
|
||||||
vec[pos] = value & 0xFF;
|
vec[pos + 3] = (value >> 24) & 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 Read(std::vector<std::uint8_t> const & vec, std::size_t const pos)
|
||||||
{
|
{
|
||||||
int value = vec[pos];
|
int value = vec[pos];
|
||||||
value |= static_cast<int>(vec[pos + 1]) << 8;
|
value |= static_cast<int>(vec[pos + 1]) << 8;
|
||||||
value |= static_cast<int>(vec[pos + 2]) << 16;
|
value |= static_cast<int>(vec[pos + 2]) << 16;
|
||||||
value |= static_cast<int>(vec[pos + 3]) << 24;
|
value |= static_cast<int>(vec[pos + 3]) << 24;
|
||||||
|
|
||||||
return value;
|
return value;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -8,255 +8,234 @@
|
|||||||
|
|
||||||
void PrintBadToken(Token::Token const & token, std::vector<std::string> const & lines)
|
void PrintBadToken(Token::Token const & token, std::vector<std::string> const & lines)
|
||||||
{
|
{
|
||||||
std::printf("at line number %i, column %i: ",
|
std::printf("at line number %i, column %i: ", token.lineNumber + 1, token.lineColumn + 1);
|
||||||
token.lineNumber + 1,
|
std::puts(token.errorMessage.c_str());
|
||||||
token.lineColumn + 1);
|
|
||||||
std::puts(token.errorMessage.c_str());
|
|
||||||
|
|
||||||
std::printf("%s\n", lines[token.lineNumber].c_str());
|
std::printf("%s\n", lines[token.lineNumber].c_str());
|
||||||
for(int i = 0; i < token.lineColumn; ++i)
|
for(int i = 0; i < token.lineColumn; ++i)
|
||||||
{
|
{
|
||||||
std::putc(' ', stdout);
|
std::putc(' ', stdout);
|
||||||
}
|
}
|
||||||
std::puts("^");
|
std::puts("^");
|
||||||
}
|
}
|
||||||
|
|
||||||
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::LoadTextFile(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())
|
||||||
{
|
{
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
std::string line;
|
std::string line;
|
||||||
while(std::getline(input, line))
|
while(std::getline(input, line))
|
||||||
{
|
{
|
||||||
lines.push_back(line);
|
lines.push_back(line);
|
||||||
}
|
}
|
||||||
input.close();
|
input.close();
|
||||||
|
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
bool Wassembler::Preprocess(std::vector<std::string> & lines) const
|
bool Wassembler::Preprocess(std::vector<std::string> & lines) const
|
||||||
{
|
{
|
||||||
Preprocessor preprocessor;
|
Preprocessor preprocessor;
|
||||||
preprocessor.process(lines);
|
preprocessor.process(lines);
|
||||||
if (printSubstitutions)
|
if(printSubstitutions)
|
||||||
{
|
{
|
||||||
preprocessor.printSubstitutions();
|
preprocessor.printSubstitutions();
|
||||||
}
|
}
|
||||||
|
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
bool Wassembler::Tokenize(std::vector<std::string> const & lines, std::vector<Token::Token> & tokens) const
|
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;
|
||||||
for(std::size_t i = 0; i < lines.size(); ++i)
|
for(std::size_t i = 0; i < lines.size(); ++i)
|
||||||
{
|
{
|
||||||
try
|
try
|
||||||
{
|
{
|
||||||
tokenizer.Tokenize(lines[i], i, tokens);
|
tokenizer.Tokenize(lines[i], i, tokens);
|
||||||
}
|
}
|
||||||
catch(Token::TokenizationError & err)
|
catch(Token::TokenizationError & err)
|
||||||
{
|
{
|
||||||
tokenizationError = true;
|
tokenizationError = true;
|
||||||
PrintTokenError(err, lines);
|
PrintTokenError(err, lines);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if (printTokens && tokens.size() > 0)
|
if(printTokens && tokens.size() > 0)
|
||||||
{
|
{
|
||||||
int previousLine = tokens[0].lineNumber;
|
int previousLine = tokens[0].lineNumber;
|
||||||
std::printf("Line %04i: ", previousLine);
|
std::printf("Line %04i: ", previousLine);
|
||||||
for(auto const & token : tokens)
|
for(auto const & token: tokens)
|
||||||
{
|
{
|
||||||
if (token.lineNumber != previousLine)
|
if(token.lineNumber != previousLine)
|
||||||
{
|
{
|
||||||
std::putc('\n', stdout);
|
std::putc('\n', stdout);
|
||||||
previousLine = token.lineNumber;
|
previousLine = token.lineNumber;
|
||||||
std::printf("Line %04i: ", previousLine);
|
std::printf("Line %04i: ", previousLine);
|
||||||
}
|
}
|
||||||
|
|
||||||
token.Print();
|
token.Print();
|
||||||
}
|
}
|
||||||
std::putc('\n', stdout);
|
std::putc('\n', stdout);
|
||||||
}
|
}
|
||||||
|
|
||||||
// Validate the syntax
|
// Validate the syntax
|
||||||
bool syntaxError = false;
|
bool syntaxError = false;
|
||||||
for(auto const & token : tokens)
|
for(auto const & token: tokens)
|
||||||
{
|
{
|
||||||
if (!token.isValid)
|
if(!token.isValid)
|
||||||
{
|
{
|
||||||
std::printf("Syntax error ");
|
std::printf("Syntax error ");
|
||||||
PrintBadToken(token, lines);
|
PrintBadToken(token, lines);
|
||||||
syntaxError = true;
|
syntaxError = true;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
return !(syntaxError || tokenizationError);
|
return !(syntaxError || tokenizationError);
|
||||||
}
|
}
|
||||||
|
|
||||||
bool Wassembler::CompileToBytes(
|
bool Wassembler::CompileToBytes(
|
||||||
std::vector<Token::Token> const & tokens,
|
std::vector<Token::Token> const & tokens,
|
||||||
std::vector<std::string> const & lines,
|
std::vector<std::string> const & lines,
|
||||||
std::vector<std::uint8_t> & bytes) const
|
std::vector<std::uint8_t> & bytes) const
|
||||||
{
|
{
|
||||||
Compile::Compiler compiler;
|
Compile::Compiler compiler;
|
||||||
try
|
try
|
||||||
{
|
{
|
||||||
compiler.Compile(tokens, bytes);
|
compiler.Compile(tokens, bytes);
|
||||||
}
|
}
|
||||||
catch(Compile::CompilationError & e)
|
catch(Compile::CompilationError & e)
|
||||||
{
|
{
|
||||||
std::printf("Semantic error ");
|
std::printf("Semantic error ");
|
||||||
PrintBadToken(e.errorToken, lines);
|
PrintBadToken(e.errorToken, lines);
|
||||||
|
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
void Wassembler::ExecuteCode(std::vector<uint8_t> const & bytes)
|
void Wassembler::ExecuteCode(std::vector<uint8_t> const & bytes)
|
||||||
{
|
{
|
||||||
vm.LoadCode(bytes, printTranslatedBytes);
|
vm.LoadCode(bytes, printTranslatedBytes);
|
||||||
// TODO clear memory?
|
// TODO clear memory?
|
||||||
vm.Run();
|
vm.Run();
|
||||||
}
|
}
|
||||||
|
|
||||||
bool Wassembler::CompileFile(
|
bool Wassembler::CompileFile(std::string const & filePath, std::vector<std::uint8_t> & bytes) const
|
||||||
std::string const & filePath,
|
|
||||||
std::vector<std::uint8_t> & bytes) const
|
|
||||||
{
|
{
|
||||||
std::vector<std::string> lines;
|
std::vector<std::string> lines;
|
||||||
if (!LoadTextFile(filePath, lines))
|
if(!LoadTextFile(filePath, lines))
|
||||||
{
|
{
|
||||||
std::printf("Error: Cannot open file %s for reading", filePath.c_str());
|
std::printf("Error: Cannot open file %s for reading", filePath.c_str());
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
if(!Preprocess(lines))
|
if(!Preprocess(lines))
|
||||||
{
|
{
|
||||||
std::puts("Aborting due to preprocessor error(s)");
|
std::puts("Aborting due to preprocessor error(s)");
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
std::vector<Token::Token> tokens;
|
std::vector<Token::Token> tokens;
|
||||||
if (!Tokenize(lines, tokens) || !CompileToBytes(tokens, lines, bytes))
|
if(!Tokenize(lines, tokens) || !CompileToBytes(tokens, lines, bytes))
|
||||||
{
|
{
|
||||||
std::puts("Aborting due to syntax error(s)");
|
std::puts("Aborting due to syntax error(s)");
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
void Wassembler::SetMemorySize(unsigned const size)
|
void Wassembler::SetMemorySize(unsigned const size) { vm.SetMemorySize(size); }
|
||||||
{
|
|
||||||
vm.SetMemorySize(size);
|
|
||||||
}
|
|
||||||
|
|
||||||
void Wassembler::EnableSubstitutionsLogging()
|
void Wassembler::EnableSubstitutionsLogging() { printSubstitutions = true; }
|
||||||
{
|
|
||||||
printSubstitutions = true;
|
|
||||||
}
|
|
||||||
|
|
||||||
void Wassembler::EnableTokensLogging()
|
void Wassembler::EnableTokensLogging() { printTokens = true; }
|
||||||
{
|
|
||||||
printTokens = true;
|
|
||||||
}
|
|
||||||
|
|
||||||
void Wassembler::EnableByteTranslationLogging()
|
void Wassembler::EnableByteTranslationLogging() { printTranslatedBytes = true; }
|
||||||
{
|
|
||||||
printTranslatedBytes = true;
|
|
||||||
}
|
|
||||||
|
|
||||||
bool Wassembler::CompileAndRun(std::string const & filePath)
|
bool Wassembler::CompileAndRun(std::string const & filePath)
|
||||||
{
|
{
|
||||||
std::vector<std::uint8_t> bytes;
|
std::vector<std::uint8_t> bytes;
|
||||||
if (filePath.size() > 4 && filePath.compare(filePath.size() - 4, 4, ".bin") == 0)
|
if(filePath.size() > 4 && filePath.compare(filePath.size() - 4, 4, ".bin") == 0)
|
||||||
{
|
{
|
||||||
std::ifstream inputFile(filePath);
|
std::ifstream inputFile(filePath);
|
||||||
if (!inputFile.is_open())
|
if(!inputFile.is_open())
|
||||||
{
|
{
|
||||||
std::printf("Error: Cannot open file %s for reading", filePath.c_str());
|
std::printf("Error: Cannot open file %s for reading", filePath.c_str());
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
std::size_t previousSize = 0;
|
std::size_t previousSize = 0;
|
||||||
bytes.resize(100);
|
bytes.resize(100);
|
||||||
while(inputFile.read(reinterpret_cast<char*>(&bytes[previousSize]), 100))
|
while(inputFile.read(reinterpret_cast<char *>(&bytes[previousSize]), 100))
|
||||||
{
|
{
|
||||||
previousSize = bytes.size();
|
previousSize = bytes.size();
|
||||||
bytes.resize(bytes.size() + 100);
|
bytes.resize(bytes.size() + 100);
|
||||||
}
|
}
|
||||||
|
|
||||||
bytes.resize(bytes.size() - (100 - inputFile.gcount()));
|
bytes.resize(bytes.size() - (100 - inputFile.gcount()));
|
||||||
}
|
}
|
||||||
else if (filePath.size() > 5 &&
|
else if(filePath.size() > 5 && filePath.compare(filePath.size() - 5, 5, ".wasm") == 0)
|
||||||
filePath.compare(filePath.size() - 5, 5, ".wasm") == 0)
|
{
|
||||||
{
|
if(!CompileFile(filePath, bytes))
|
||||||
if (!CompileFile(filePath, bytes))
|
{
|
||||||
{
|
return false;
|
||||||
return false;
|
}
|
||||||
}
|
}
|
||||||
}
|
else
|
||||||
else
|
{
|
||||||
{
|
std::printf("Error: unrecognized file extension on input file %s", filePath.c_str());
|
||||||
std::printf(
|
|
||||||
"Error: unrecognized file extension on input file %s",
|
|
||||||
filePath.c_str());
|
|
||||||
|
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
try
|
try
|
||||||
{
|
{
|
||||||
ExecuteCode(bytes);
|
ExecuteCode(bytes);
|
||||||
}
|
}
|
||||||
catch (Execute::RuntimeError const & e)
|
catch(Execute::RuntimeError const & e)
|
||||||
{
|
{
|
||||||
std::puts(e.GetMessage().c_str());
|
std::puts(e.GetMessage().c_str());
|
||||||
std::puts("Aborting due to runtime error(s)");
|
std::puts("Aborting due to runtime error(s)");
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
bool Wassembler::CompileToFile(
|
bool Wassembler::CompileToFile(std::string const & inputFilePath, std::string const & outputFilePath)
|
||||||
std::string const & inputFilePath,
|
|
||||||
std::string const & outputFilePath)
|
|
||||||
{
|
{
|
||||||
std::vector<std::uint8_t> bytes;
|
std::vector<std::uint8_t> bytes;
|
||||||
if (!CompileFile(inputFilePath, bytes))
|
if(!CompileFile(inputFilePath, bytes))
|
||||||
{
|
{
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
std::ofstream output(outputFilePath, std::ios::binary | std::ios::trunc);
|
std::ofstream output(outputFilePath, std::ios::binary | std::ios::trunc);
|
||||||
if (!output.is_open())
|
if(!output.is_open())
|
||||||
{
|
{
|
||||||
std::printf("Error: Cannot open file %s for writing", outputFilePath.c_str());
|
std::printf("Error: Cannot open file %s for writing", outputFilePath.c_str());
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!output.write(reinterpret_cast<char*>(bytes.data()), bytes.size()))
|
if(!output.write(reinterpret_cast<char *>(bytes.data()), bytes.size()))
|
||||||
{
|
{
|
||||||
std::printf("Error: An error occurred whilst writing to %s", outputFilePath.c_str());
|
std::printf("Error: An error occurred whilst writing to %s", outputFilePath.c_str());
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
Reference in New Issue
Block a user