Format with clang

This commit is contained in:
2021-11-09 19:41:10 +01:00
parent ee016636f7
commit 61906b3c80
38 changed files with 2277 additions and 2364 deletions

107
.clang-format Normal file
View 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
View 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
View File

@@ -0,0 +1,5 @@
-xc++
-Wall
-Wextra
-Iinclude/
-std=c++17

View File

@@ -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);
};
} }

View File

@@ -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);
}; };
} }

View File

@@ -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);
};
} }

View File

@@ -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
}; };
} }

View File

@@ -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);
};
} }

View File

@@ -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);
} }

View File

@@ -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?
}; };
} }

View File

@@ -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();
}; };
} }

View File

@@ -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;
}; };
} }

View File

@@ -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;
}; };

View File

@@ -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);
}; };
} }

View File

@@ -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);
} }

View File

@@ -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);
} }

View File

@@ -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;
}; };
} }

View File

@@ -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);
};
} }

View File

@@ -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
}; };
} }

View File

@@ -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);
} }
} }

View File

@@ -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;
}; };

View File

@@ -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})

View File

@@ -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;
} }
} }

View File

@@ -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);
} }
} }

View File

@@ -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");
} }
} }

View File

@@ -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);
}
} }

View File

@@ -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);
} }
} }
} }

View File

@@ -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;
} }
} }

View File

@@ -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; }
} }

View File

@@ -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;
} }

View File

@@ -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());
}
} }

View File

@@ -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) { }
{
}
} }

View File

@@ -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;
} }
} }

View File

@@ -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;
} }
} }

View File

@@ -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;
} }
} }
} }

View File

@@ -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;
} }
} }
} }
} }

View File

@@ -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;
} }
} }
} }

View File

@@ -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;
} }