From 11486823240bbd63864c8b509173b394ac75738b Mon Sep 17 00:00:00 2001 From: Tijmen van Nesselrooij Date: Sat, 23 Nov 2019 16:47:16 +0100 Subject: [PATCH] Added direct memory access --- README.md | 13 +++- bin/example.wasm | 16 ++++- include/execute/error.hpp | 8 +++ include/interpret/errors.hpp | 10 +++ include/interpret/value.hpp | 20 +++--- include/token/token.hpp | 33 +++++++-- include/token/tokentype.hpp | 3 +- src/interpret/error.cpp | 12 +++- src/interpret/interpreter.cpp | 11 ++- src/interpret/operanddefinitions.cpp | 36 +++++++--- src/interpret/statement.cpp | 14 ++-- src/interpret/value.cpp | 70 ++++++++++++++---- src/token/token.cpp | 104 ++++++++++++++++++++++----- src/token/tokenizer.cpp | 71 ++++++++++++------ 14 files changed, 329 insertions(+), 92 deletions(-) diff --git a/README.md b/README.md index e1e63bd..1d3af9f 100644 --- a/README.md +++ b/README.md @@ -14,6 +14,7 @@ - `;` for end of statement (mandatory) - `[label]:` for labels - `#[text]` for comments: any text is ignored till a newline (`\n`) is found +- `[[%register|$value]]` for accessing memory - Elements must be separated by whitespace character - Good: `add $2 $5 %A;` - Bad: `add $2$5%A;` @@ -34,6 +35,12 @@ lti %B $10; jmp loop; ``` +Read the integer at memory location `1024` into register A: +``` +seti %A [$1024]; +``` +Remember not to use spaces inside the `[` brackets. + ## Reserved symbols The following whitespace characters are used to separate symbols: @@ -82,6 +89,8 @@ All symbols are reserved keywords and can therefore NOT be used as labels. ## Interupts -- 0..9 range: +- [0..9] Output to STDOUT - `0` put value of register A as ASCII character on stdout - - `1` put value of register A as decimal integer on stdout \ No newline at end of file + - `1` put value of register A as decimal integer on stdout + - `2` put value of register A as hexadecimal integer on stdout + - `3` put the string pointed at by register A for the amount of characters defined by register B on stdout \ No newline at end of file diff --git a/bin/example.wasm b/bin/example.wasm index 0d4a369..d0bf8f3 100644 --- a/bin/example.wasm +++ b/bin/example.wasm @@ -53,4 +53,18 @@ gti %B $0; jmp world_loop; seti %A $10; # newline -int $0; \ No newline at end of file +int $0; + +seti %B $1024; +seti [%B] $50; +seti %A $66; +seti [$10] %A; + +seti %A [$10]; +int $1; + +seti %A $10; # newline +int $0; + +seti %A [$1024]; +int $1; diff --git a/include/execute/error.hpp b/include/execute/error.hpp index cfc305e..2b3b26c 100644 --- a/include/execute/error.hpp +++ b/include/execute/error.hpp @@ -16,4 +16,12 @@ namespace Execute { }; + + namespace Internal + { + struct BadValueType : RuntimeError + { + + }; + } } \ No newline at end of file diff --git a/include/interpret/errors.hpp b/include/interpret/errors.hpp index f07171d..cd7f267 100644 --- a/include/interpret/errors.hpp +++ b/include/interpret/errors.hpp @@ -32,11 +32,21 @@ namespace Interpret ExpectedImmediate(Token::Token const & token); }; + struct ExpectedImmediateOrMemory : public TokenError + { + ExpectedImmediateOrMemory(Token::Token const & token); + }; + struct ExpectedRegister : public TokenError { ExpectedRegister(Token::Token const & token); }; + struct ExpectedRegisterOrMemory : public TokenError + { + ExpectedRegisterOrMemory(Token::Token const & token); + }; + struct ExpectedOperand : public TokenError { ExpectedOperand(Token::Token const & token); diff --git a/include/interpret/value.hpp b/include/interpret/value.hpp index 0b7d589..a57647a 100644 --- a/include/interpret/value.hpp +++ b/include/interpret/value.hpp @@ -1,5 +1,5 @@ #pragma once -#include +#include #include namespace Interpret @@ -7,19 +7,23 @@ namespace Interpret enum class ValueType { Register, - ImmediateInteger + ImmediateInteger, + MemoryLocation + }; + + enum class ValueDataType + { + Register, + Immediate }; struct Value { ValueType type; - union - { - int registerIndex; - int integer; - }; + ValueDataType dataType; + int data; - int & GetValue(Execute::Registers & registers); + int & GetValue(Execute::State & state, Execute::Registers & registers); void CreateFromToken(Token::Token const & token); }; diff --git a/include/token/token.hpp b/include/token/token.hpp index 85c460e..4f9b8b7 100644 --- a/include/token/token.hpp +++ b/include/token/token.hpp @@ -6,22 +6,43 @@ namespace Token { + enum class TokenValueType + { + None = 0, + Integer, + Operand, + Register, + String + }; + struct Token { + private: + 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, 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(TokenType type, OperandType const OperandType, bool validness, int const lineNumber, int const lineColumn); + + public: int const lineNumber; int const lineColumn; TokenType type; + TokenValueType const valueType; bool isValid; std::variant data; - Token(int const lineNumber, int const lineColumn); - Token(int const lineNumber, int const lineColumn, OperandType operatorType, bool validness); - Token(int const lineNumber, int const lineColumn, RegisterType registerType, bool validness); - Token(int const lineNumber, int const lineColumn, int value, bool validness); - Token(int const lineNumber, int const lineColumn, std::string const & value, bool validness); - Token(Token const & other); + static Token CreateUnknownToken(int const lineNumber, int const lineColumn); + static Token CreateStatementEndToken(int const lineNumber, int const lineColumn); + static Token CreateLabelToken(std::string const & string, bool isValid, int const lineNumber, int const lineColumn); + static Token CreateImmediateValueToken(int const value, bool isValid, int const lineNumber, int const lineColumn); + static Token CreateRegisterToken(RegisterType const registerType, int const lineNumber, int const lineColumn); + static Token CreateOperandToken(OperandType const operandType, int const lineNumber, int const lineColumn); + static Token CreateMemoryToken(RegisterType const registerType, int const lineNumber, int const lineColumn); + static Token CreateMemoryToken(int const value, bool isValid, int const lineNumber, int const lineColumn); + void DebugPrint() const; }; } diff --git a/include/token/tokentype.hpp b/include/token/tokentype.hpp index e0289c8..5ee79fb 100644 --- a/include/token/tokentype.hpp +++ b/include/token/tokentype.hpp @@ -9,6 +9,7 @@ namespace Token ImmediateInteger, Register, StatementEnd, - Label + Label, + Memory }; } \ No newline at end of file diff --git a/src/interpret/error.cpp b/src/interpret/error.cpp index b107ad0..b2e526e 100644 --- a/src/interpret/error.cpp +++ b/src/interpret/error.cpp @@ -19,7 +19,7 @@ namespace Interpret } ExpectedValue::ExpectedValue(Token::Token const & token) - : TokenError(token, "Expected an immediate value or register") + : TokenError(token, "Expected an immediate value, a register or a memory location") { } @@ -28,11 +28,21 @@ namespace Interpret { } + ExpectedImmediateOrMemory::ExpectedImmediateOrMemory(Token::Token const & token) + : TokenError(token, "Expected an immediate value or a memory location") + { + } + ExpectedRegister::ExpectedRegister(Token::Token const & token) : TokenError(token, "Expected a register") { } + ExpectedRegisterOrMemory::ExpectedRegisterOrMemory(Token::Token const & token) + : TokenError(token, "Expected a register or a memory location") + { + } + ExpectedOperand::ExpectedOperand(Token::Token const & token) : TokenError(token, "Expected an operand") { diff --git a/src/interpret/interpreter.cpp b/src/interpret/interpreter.cpp index 960f4e1..dff659a 100644 --- a/src/interpret/interpreter.cpp +++ b/src/interpret/interpreter.cpp @@ -4,6 +4,15 @@ namespace Interpret { + bool IsArgumentToken(Token::Token const & t) + { + return + t.type == Token::TokenType::ImmediateInteger || + t.type == Token::TokenType::Register || + t.type == Token::TokenType::Label || + t.type == Token::TokenType::Memory; + } + void Interpreter::Interpret(std::vector const & tokens, Code & code) { enum class InterpreterState @@ -46,7 +55,7 @@ namespace Interpret break; case InterpreterState::FindArguments: - if (token.type == Token::TokenType::ImmediateInteger || token.type == Token::TokenType::Register || token.type == Token::TokenType::Label) + if (IsArgumentToken(token)) { expectedNumberOfArguments -= 1; if (expectedNumberOfArguments < 1) diff --git a/src/interpret/operanddefinitions.cpp b/src/interpret/operanddefinitions.cpp index 5fa2849..5cdc6a9 100644 --- a/src/interpret/operanddefinitions.cpp +++ b/src/interpret/operanddefinitions.cpp @@ -31,10 +31,24 @@ namespace Interpret throw ExpectedRegister(token); } - Value GetImmediateOrRegisterArgument(unsigned const index, std::vector const & tokens) + Value GetRegisterOrMemoryArgument(unsigned const index, std::vector const & tokens) { auto const & token = tokens[index]; - if (token.type == Token::TokenType::ImmediateInteger || token.type == Token::TokenType::Register) + if (token.type == Token::TokenType::Register || token.type == Token::TokenType::Memory) + { + Value v; + v.CreateFromToken(token); + + return v; + } + + throw ExpectedRegisterOrMemory(token); + } + + Value GetValueArgument(unsigned const index, std::vector const & tokens) + { + auto const & token = tokens[index]; + if (token.type == Token::TokenType::ImmediateInteger || token.type == Token::TokenType::Register || token.type == Token::TokenType::Memory) { Value v; v.CreateFromToken(token); @@ -47,15 +61,15 @@ namespace Interpret void AddArithmeticArguments(ArithmeticStatement & statement, unsigned const operandIndex, std::vector const & tokens) { - statement.firstArgument = GetImmediateOrRegisterArgument(operandIndex + 1u, tokens); - statement.secondArgument = GetImmediateOrRegisterArgument(operandIndex + 2u, tokens); - statement.thirdArgument = GetRegisterArgument(operandIndex + 3u, tokens); + statement.firstArgument = GetValueArgument(operandIndex + 1u, tokens); + statement.secondArgument = GetValueArgument(operandIndex + 2u, tokens); + statement.thirdArgument = GetRegisterOrMemoryArgument(operandIndex + 3u, tokens); } void AddLogicArguments(ControlFlowStatement & statement, unsigned const operandIndex, std::vector const & tokens) { - statement.firstArgument = GetImmediateOrRegisterArgument(operandIndex + 1u, tokens); - statement.secondArgument = GetImmediateOrRegisterArgument(operandIndex + 2u, tokens); + statement.firstArgument = GetValueArgument(operandIndex + 1u, tokens); + statement.secondArgument = GetValueArgument(operandIndex + 2u, tokens); } std::unique_ptr ExtractStatement(unsigned const operandIndex, std::vector const & tokens) @@ -196,8 +210,8 @@ namespace Interpret case Token::OperandType::SetInteger: { auto statement = std::make_unique(); - statement->firstArgument = GetRegisterArgument(operandIndex + 1u, tokens); - statement->secondArgument = GetImmediateOrRegisterArgument(operandIndex + 2u, tokens); + statement->firstArgument = GetRegisterOrMemoryArgument(operandIndex + 1u, tokens); + statement->secondArgument = GetValueArgument(operandIndex + 2u, tokens); return statement; } @@ -205,7 +219,7 @@ namespace Interpret case Token::OperandType::Interrupt: { auto statement = std::make_unique(); - statement->firstArgument = GetImmediateOrRegisterArgument(operandIndex + 1u, tokens); + statement->firstArgument = GetValueArgument(operandIndex + 1u, tokens); return statement; } @@ -221,7 +235,7 @@ namespace Interpret case Token::OperandType::PushInteger: { auto statement = std::make_unique(); - statement->firstArgument = GetImmediateOrRegisterArgument(operandIndex + 1u, tokens); + statement->firstArgument = GetValueArgument(operandIndex + 1u, tokens); return statement; } diff --git a/src/interpret/statement.cpp b/src/interpret/statement.cpp index 91ff4b9..e39314b 100644 --- a/src/interpret/statement.cpp +++ b/src/interpret/statement.cpp @@ -10,17 +10,17 @@ namespace Interpret void OneArgumentStatement::Execute(Execute::Flags & flags, Execute::State & state, Execute::Registers & registers) { - function(flags, firstArgument.GetValue(registers)); + function(flags, firstArgument.GetValue(state, registers)); } void ControlFlowStatement::Execute(Execute::Flags & flags, Execute::State & state, Execute::Registers & registers) { - function(state, firstArgument.GetValue(registers), secondArgument.GetValue(registers)); + function(state, firstArgument.GetValue(state, registers), secondArgument.GetValue(state, registers)); } void ArithmeticStatement::Execute(Execute::Flags & flags, Execute::State & state, Execute::Registers & registers) { - function(firstArgument.GetValue(registers), secondArgument.GetValue(registers), thirdArgument.GetValue(registers)); + function(firstArgument.GetValue(state, registers), secondArgument.GetValue(state, registers), thirdArgument.GetValue(state, registers)); } void JumpStatement::Execute(Execute::Flags & flags, Execute::State & state, Execute::Registers & registers) @@ -43,12 +43,12 @@ namespace Interpret void SetStatement::Execute(Execute::Flags & flags, Execute::State & state, Execute::Registers & registers) { - firstArgument.GetValue(registers) = secondArgument.GetValue(registers); + firstArgument.GetValue(state, registers) = secondArgument.GetValue(state, registers); } void InterruptStatement::Execute(Execute::Flags & flags, Execute::State & state, Execute::Registers & registers) { - state.interrupts[firstArgument.GetValue(registers)](registers); + state.interrupts[firstArgument.GetValue(state, registers)](registers); } void PopStatement::Execute(Execute::Flags & flags, Execute::State & state, Execute::Registers & registers) @@ -59,7 +59,7 @@ namespace Interpret } auto const value = *(reinterpret_cast(state.memory.data() + (state.stackPointer - 1u))); - firstArgument.GetValue(registers) = value; + firstArgument.GetValue(state, registers) = value; state.stackPointer -= sizeof(int); } @@ -71,7 +71,7 @@ namespace Interpret throw Execute::StackOverflow(); } - *(reinterpret_cast(state.memory.data() + state.stackPointer + (sizeof(int) - 1))) = firstArgument.GetValue(registers); + *(reinterpret_cast(state.memory.data() + state.stackPointer + (sizeof(int) - 1))) = firstArgument.GetValue(state, registers); state.stackPointer += sizeof(int); } diff --git a/src/interpret/value.cpp b/src/interpret/value.cpp index 4d4103b..4d15432 100644 --- a/src/interpret/value.cpp +++ b/src/interpret/value.cpp @@ -1,32 +1,74 @@ +#include #include #include namespace Interpret { - int & Value::GetValue(Execute::Registers & registers) + int & Value::GetValue(Execute::State & state, Execute::Registers & registers) { - if (type == ValueType::ImmediateInteger) + switch(type) { - return integer; - } + case ValueType::ImmediateInteger: + return data; - return registers.registers[registerIndex]; + case ValueType::Register: + return registers.registers[data]; + + case ValueType::MemoryLocation: + if (dataType == ValueDataType::Register) + { + return *reinterpret_cast(state.memory.data() + registers.registers[data]); + } + else if (dataType == ValueDataType::Immediate) + { + return *reinterpret_cast(state.memory.data() + data); + } + else + { + throw Execute::Internal::BadValueType(); + } + break; + + default: + throw Execute::Internal::BadValueType(); + } } void Value::CreateFromToken(Token::Token const & token) { - if (token.type == Token::TokenType::ImmediateInteger) + switch(token.type) { + case Token::TokenType::ImmediateInteger: type = ValueType::ImmediateInteger; - integer = std::get(token.data); - } - else if (token.type == Token::TokenType::Register) - { + dataType = ValueDataType::Immediate; + data = std::get(token.data); + break; + + case Token::TokenType::Register: type = ValueType::Register; - registerIndex = static_cast(std::get(token.data)); - } - else - { + dataType = ValueDataType::Register; + data = static_cast(std::get(token.data)); + break; + + case Token::TokenType::Memory: + type = ValueType::MemoryLocation; + if (token.valueType == Token::TokenValueType::Integer) + { + dataType = ValueDataType::Immediate; + data = std::get(token.data); + } + else if(token.valueType == Token::TokenValueType::Register) + { + dataType = ValueDataType::Register; + data = static_cast(std::get(token.data)); + } + else + { + throw Internal::BadTokenForValue(token); + } + break; + + default: throw Internal::BadTokenForValue(token); } } diff --git a/src/token/token.cpp b/src/token/token.cpp index 2f58616..af09870 100644 --- a/src/token/token.cpp +++ b/src/token/token.cpp @@ -3,47 +3,53 @@ namespace Token { - Token::Token(int const _lineNumber, int const _lineColumn) + Token::Token(TokenType _type, bool validness, int const _lineNumber, int const _lineColumn) : lineNumber(_lineNumber), lineColumn(_lineColumn), - type(TokenType::Unknown), - isValid(false) - { - } - - Token::Token(int const _lineNumber, int const _lineColumn, OperandType _operatorType, bool validness) - : lineNumber(_lineNumber), - lineColumn(_lineColumn), - type(TokenType::Operand), + type(_type), + valueType(TokenValueType::None), isValid(validness), - data(_operatorType) + data(0) { } - Token::Token(int const _lineNumber, int const _lineColumn, RegisterType _registerType, bool validness) + Token::Token(TokenType _type, std::string const & string, bool validness, int const _lineNumber, int const _lineColumn) : lineNumber(_lineNumber), lineColumn(_lineColumn), - type(TokenType::Register), + type(_type), + valueType(TokenValueType::String), isValid(validness), - data(_registerType) + data(string) { } - Token::Token(int const _lineNumber, int const _lineColumn, int value, bool validness) + Token::Token(TokenType _type, int value, bool validness, int const _lineNumber, int const _lineColumn) : lineNumber(_lineNumber), lineColumn(_lineColumn), - type(TokenType::ImmediateInteger), + type(_type), + valueType(TokenValueType::Integer), isValid(validness), data(value) { } - Token::Token(int const _lineNumber, int const _lineColumn, std::string const & value, bool validness) + Token::Token(TokenType _type, RegisterType const registerType, bool validness, int const _lineNumber, int const _lineColumn) : lineNumber(_lineNumber), lineColumn(_lineColumn), - type(TokenType::Label), + type(_type), + valueType(TokenValueType::Register), isValid(validness), - data(value) + data(registerType) + { + } + + Token::Token(TokenType _type, OperandType const operandType, bool validness, int const _lineNumber, int const _lineColumn) + : lineNumber(_lineNumber), + lineColumn(_lineColumn), + type(_type), + valueType(TokenValueType::Operand), + isValid(validness), + data(operandType) { } @@ -52,10 +58,51 @@ namespace Token lineColumn(other.lineColumn), type(other.type), isValid(other.isValid), + valueType(other.valueType), data(other.data) { } + Token Token::CreateUnknownToken(int const lineNumber, int const lineColumn) + { + return Token(TokenType::Unknown, false, lineNumber, lineColumn); + } + + Token Token::CreateStatementEndToken(int const lineNumber, int const lineColumn) + { + return Token(TokenType::StatementEnd, true, lineNumber, lineColumn); + } + + Token Token::CreateLabelToken(std::string const & string, bool isValid, int const lineNumber, int const lineColumn) + { + return Token(TokenType::Label, string, isValid, lineNumber, lineColumn); + } + + Token Token::CreateImmediateValueToken(int const value, bool isValid, int const lineNumber, int const lineColumn) + { + return Token(TokenType::ImmediateInteger, value, isValid, lineNumber, lineColumn); + } + + Token Token::CreateRegisterToken(RegisterType const registerType, int const lineNumber, int const lineColumn) + { + return Token(TokenType::Register, registerType, registerType != RegisterType::Unknown, lineNumber, lineColumn); + } + + Token Token::CreateOperandToken(OperandType const operandType, int const lineNumber, int const lineColumn) + { + return Token(TokenType::Operand, operandType, operandType != OperandType::Unknown, lineNumber, lineColumn); + } + + Token Token::CreateMemoryToken(RegisterType const registerType, int const lineNumber, int const lineColumn) + { + return Token(TokenType::Memory, registerType, registerType != RegisterType::Unknown, lineNumber, lineColumn); + } + + Token Token::CreateMemoryToken(int const value, bool isValid, int const lineNumber, int const lineColumn) + { + return Token(TokenType::Memory, value, isValid, lineNumber, lineColumn); + } + void Token::DebugPrint() const { std::putc(' ', stdout); @@ -122,6 +169,25 @@ namespace Token std::printf("LABEL=%s", std::get(data).c_str()); break; + case TokenType::Memory: + { + switch(valueType) + { + case TokenValueType::Integer: + std::printf("[$%i]", std::get(data)); + break; + + case TokenValueType::Register: + std::printf("[%%%i]", static_cast(std::get(data))); + break; + + default: + std::printf("[UNKNOWN_TYPE]"); + break; + } + } + break; + case TokenType::Unknown: default: std::printf("UNKNOWN_TOKEN"); diff --git a/src/token/tokenizer.cpp b/src/token/tokenizer.cpp index 8ef0d2b..78e798f 100644 --- a/src/token/tokenizer.cpp +++ b/src/token/tokenizer.cpp @@ -8,58 +8,87 @@ namespace Token return c == '\n' || c == ' ' || c == '\t' || c == '\r'; } + std::tuple TryParse(std::string const & string) + { + try + { + int value = std::stoi(string); + return std::make_tuple(value, true); + } + catch(std::invalid_argument &) + { + return std::make_tuple(0, false); + } + } + Token ExtractToken(std::string const & string, int const lineNumber, int const lineColumn) { if (string.size() == 0) { - return Token(lineNumber, lineColumn); + return Token::CreateUnknownToken(lineNumber, lineColumn); } char const prefix = string[0]; if (prefix == '$') { - int value = 0; - try - { - value = std::stoi(string.substr(1, string.size())); - } - catch(std::invalid_argument &) - { - return Token(lineNumber, lineColumn, 0, false); - } - - return Token(lineNumber, lineColumn, value, true); + auto const result = TryParse(string.substr(1, string.size())); + return Token::CreateImmediateValueToken(std::get<0>(result), std::get<1>(result), lineNumber, lineColumn); } if (prefix == '%') { - RegisterType const rtype = GetRegisterType(string.substr(1, string.size())); - return Token(lineNumber, lineColumn, rtype, rtype != RegisterType::Unknown); + return Token::CreateRegisterToken(GetRegisterType(string.substr(1, string.size())), lineNumber, lineColumn); } if (prefix == ';') { - Token token(lineNumber, lineColumn); - token.type = TokenType::StatementEnd; - token.isValid = true; - return token; + return Token::CreateStatementEndToken(lineNumber, lineColumn); } char const postfix = string[string.size() - 1]; if (postfix == ':') { // TODO check if label is an Operand? - return Token(lineNumber, lineColumn, string.substr(0, string.size() - 1), true); + return Token::CreateLabelToken(string.substr(0, string.size() - 1), true, lineNumber, lineColumn); + } + + if (prefix == '[' && postfix == ']') + { + if(string.size() < 4) + { + return Token::CreateMemoryToken(0, false, lineNumber, lineColumn + 2u); + } + + char const memoryPrefix = string[1]; + std::string const valueString = string.substr(2, string.size() - 3u); + if (memoryPrefix == '$') + { + auto const result = TryParse(valueString); + return Token::CreateMemoryToken(std::get<0>(result), std::get<1>(result), lineNumber, lineColumn); + } + else if (memoryPrefix == '%') + { + return Token::CreateMemoryToken(GetRegisterType(valueString), lineNumber, lineColumn); + } + else + { + return Token::CreateMemoryToken(0, false, lineNumber, lineColumn + 1u); + } + } + else if (prefix == '[' || postfix == ']') + { + int const errorLineColumn = (prefix == '[') ? lineColumn : (lineColumn + string.size() - 1u); + return Token::CreateMemoryToken(0, false, lineNumber, errorLineColumn); } OperandType const opType = GetOperandType(string); if (opType != OperandType::Unknown) { - return Token(lineNumber, lineColumn, opType, true); + return Token::CreateOperandToken(opType, lineNumber, lineColumn); } // Last resort: it must be a label - return Token(lineNumber, lineColumn, string, true); + return Token::CreateLabelToken(string, true, lineNumber, lineColumn); } void Tokenizer::Tokenize(std::string const & line, int const lineNumber, std::vector & tokens)