Large chunk of work in parser for getting expressions to work.
continuous-integration/drone/push Build is passing Details

This commit is contained in:
Deukhoofd 2020-11-08 15:41:18 +01:00
parent c20a1089a9
commit 5fb64e12e1
Signed by: Deukhoofd
GPG Key ID: F63E044490819F6F
12 changed files with 344 additions and 41 deletions

View File

@ -62,7 +62,7 @@ expr ::= exprterm {(mathop | compop | logicop | bitop) exprterm};
ternary ::= expr ['?' assign : assign];
return ::= 'return' [assign] ';';
exprstat ::= [assign] ';';
exprstat ::= assign ';';
continue ::= 'continue' ';';
break ::= 'break' ';';

View File

@ -54,6 +54,16 @@ namespace MalachScript {
BitwiseRightShift,
ArithmeticRightShift,
};
enum class PreOperator : uint8_t {
Negation,
Identity,
Inversion,
Increment,
Decrement,
BitwiseComplement,
Handle
};
}
#endif // MALACHSCRIPT_OPERATORS_HPP

View File

@ -4,6 +4,7 @@
#include <memory>
#include "../../CoreData/Identifier.hpp"
#include "../../TextSpan.hpp"
#include "../ParserDefines.hpp"
#include "LexTokenKind.hpp"
namespace MalachScript::Parser {
@ -28,30 +29,30 @@ namespace MalachScript::Parser {
};
class IntegerLiteral : public LexTokenImpl<LexTokenKind::IntegerLiteral> {
uint64_t _value;
ParseInt _value;
public:
IntegerLiteral(const TextSpan& span, uint64_t value)
IntegerLiteral(const TextSpan& span, ParseInt value)
: LexTokenImpl<LexTokenKind::IntegerLiteral>(span), _value(value) {}
[[nodiscard]] inline uint64_t GetValue() const noexcept { return _value; }
[[nodiscard]] inline ParseInt GetValue() const noexcept { return _value; }
};
class FloatLiteral : public LexTokenImpl<LexTokenKind::FloatLiteral> {
double _value;
ParseFloat _value;
public:
FloatLiteral(const TextSpan& span, double value)
FloatLiteral(const TextSpan& span, ParseFloat value)
: LexTokenImpl<LexTokenKind::FloatLiteral>(span), _value(value) {}
[[nodiscard]] inline double GetValue() const noexcept { return _value; }
[[nodiscard]] inline long double GetValue() const noexcept { return _value; }
};
class StringLiteral : public LexTokenImpl<LexTokenKind::StringLiteral> {
std::u8string _value;
ParseString _value;
public:
StringLiteral(const TextSpan& span, std::u8string value)
StringLiteral(const TextSpan& span, ParseString value)
: LexTokenImpl<LexTokenKind::StringLiteral>(span), _value(std::move(value)) {}
[[nodiscard]] inline const std::u8string& GetValue() const noexcept { return _value; }
[[nodiscard]] inline const ParseString& GetValue() const noexcept { return _value; }
};
class IdentifierToken : public LexTokenImpl<LexTokenKind::Identifier> {

View File

@ -338,16 +338,16 @@ namespace MalachScript::Parser {
return pow10[n];
}
LexToken* Lexer::LexDecimal(uint64_t initial) {
LexToken* Lexer::LexDecimal(ParseInt initial) {
auto start = _position;
uint64_t value = initial;
uint64_t decimalValue = 0;
uint64_t exponentValue = 0;
ParseInt value = initial;
ParseInt decimalValue = 0;
ParseInt exponentValue = 0;
uint8_t decimalLength = 0;
bool isDecimal = false;
bool isExponent = false;
while (true) {
auto v = (uint64_t)LexDecimalValue(Peek());
auto v = (ParseInt)LexDecimalValue(Peek());
if (v == 255) {
if (!isDecimal && Peek() == u8'.') {
isDecimal = true;
@ -376,7 +376,7 @@ namespace MalachScript::Parser {
}
}
if (isDecimal || isExponent) {
auto val = value + ((double)decimalValue / quick_pow10(decimalLength));
auto val = value + ((ParseFloat)decimalValue / quick_pow10(decimalLength));
if (isExponent) {
val *= pow(10, exponentValue);
}
@ -387,7 +387,7 @@ namespace MalachScript::Parser {
IntegerLiteral* Lexer::LexHexadecimal() {
auto start = _position;
uint64_t value = 0;
ParseInt value = 0;
while (true) {
auto v = LexHexadecimalValue(Peek());
if (v == 255) {
@ -401,7 +401,7 @@ namespace MalachScript::Parser {
}
IntegerLiteral* Lexer::LexOctal() {
auto start = _position;
uint64_t value = 0;
ParseInt value = 0;
while (true) {
auto v = LexOctalValue(Peek());
if (v == 255) {
@ -415,7 +415,7 @@ namespace MalachScript::Parser {
}
IntegerLiteral* Lexer::LexBinary() {
auto start = _position;
uint64_t value = 0;
ParseInt value = 0;
while (true) {
auto v = LexBinaryValue(Peek());
if (v == 255) {
@ -457,7 +457,7 @@ namespace MalachScript::Parser {
if (heredoc) {
Progress(2);
}
return Create<StringLiteral>(TextSpan(start, start + _position), std::u8string(_script.substr(start, offset)));
return Create<StringLiteral>(TextSpan(start, start + _position), ParseString(_script.substr(start, offset)));
}
LexToken* Lexer::LexKeywordOrIdentifier() {

View File

@ -204,11 +204,13 @@ namespace MalachScript::Parser {
}
Identifier identifier;
if (!ParseIdentifier(identifier, current)) {
delete typeStatement;
return false;
}
PROGRESS_TOKEN(current);
const ParsedStatement* paramList = nullptr;
if (!ParseParamList(paramList, current)) {
delete typeStatement;
return false;
}
bool isConst = false;
@ -285,7 +287,7 @@ namespace MalachScript::Parser {
return false;
}
while (current != nullptr && current->GetKind() == LexTokenKind::ColonColonSymbol) {
while (current->GetKind() == LexTokenKind::ColonColonSymbol) {
PROGRESS_TOKEN(current);
const auto* n = current;
if (ParseIdentifier(identifier, n)) {
@ -440,6 +442,7 @@ namespace MalachScript::Parser {
}
PROGRESS_TOKEN(current);
if (current->GetKind() != LexTokenKind::OpenCurlyParenthesisSymbol) {
delete typeStatement;
return false;
}
bool hasGet = false;
@ -464,8 +467,9 @@ namespace MalachScript::Parser {
if (!ParseStatBlock(getStatement, current)) {
this->LogError(Diagnostics::DiagnosticType::UnexpectedToken, current->GetSpan());
}
} else {
PROGRESS_TOKEN(current);
}
PROGRESS_TOKEN(current);
if (hasGet) {
this->LogError(Diagnostics::DiagnosticType::DoubleProperty,
@ -483,8 +487,9 @@ namespace MalachScript::Parser {
if (!ParseStatBlock(setStatement, current)) {
this->LogError(Diagnostics::DiagnosticType::UnexpectedToken, current->GetSpan());
}
} else {
PROGRESS_TOKEN(current);
}
PROGRESS_TOKEN(current);
if (hasSet) {
this->LogError(Diagnostics::DiagnosticType::DoubleProperty,
@ -541,7 +546,9 @@ namespace MalachScript::Parser {
bool Parser::ParseStatement(const ParsedStatement*& out, const LexToken*& currentToken) {
// TODO: All the other statements.
return ParseIfStatement(out, currentToken);
return ParseIfStatement(out, currentToken) || ParseReturn(out, currentToken) ||
ParseStatBlock(out, currentToken) || ParseExprStat(out, currentToken);
}
bool Parser::ParseVar([[maybe_unused]] const ParsedStatement*& out, const LexToken*& currentToken) {
@ -566,11 +573,7 @@ namespace MalachScript::Parser {
PROGRESS_TOKEN(current);
// TODO: Default values
// TODO: Creating multiple vars in a single line (int a, b, c)
if (current->GetKind() == LexTokenKind::SemicolonSymbol) {
PROGRESS_TOKEN(current);
} else {
LogError(Diagnostics::DiagnosticType::UnexpectedToken, current->GetSpan());
}
EXPECT_TOKEN(current, SemicolonSymbol);
out = new ParsedVarStatement(TextSpan(currentToken->GetSpan().GetStart(), current->GetSpan().GetEnd()), access,
typeStatement, identifier);
currentToken = current;
@ -582,6 +585,7 @@ namespace MalachScript::Parser {
if (current->GetKind() != LexTokenKind::OpenCurlyParenthesisSymbol) {
return false;
}
PROGRESS_TOKEN(current);
std::vector<const ParsedStatement*> statements;
while (true) {
if (current->GetKind() == LexTokenKind::CloseCurlyParenthesisSymbol) {
@ -699,6 +703,7 @@ namespace MalachScript::Parser {
}
out = new ParsedBinaryStatement<MathOperator>(
TextSpan(currentToken->GetSpan().GetStart(), current->GetSpan().GetEnd()), leftHand, mathOp, rightHand);
currentToken = current;
return true;
}
ComparisonOperator compOp;
@ -713,6 +718,7 @@ namespace MalachScript::Parser {
}
out = new ParsedBinaryStatement<ComparisonOperator>(
TextSpan(currentToken->GetSpan().GetStart(), current->GetSpan().GetEnd()), leftHand, compOp, rightHand);
currentToken = current;
return true;
}
LogicOperator logicOp;
@ -728,6 +734,7 @@ namespace MalachScript::Parser {
out = new ParsedBinaryStatement<LogicOperator>(
TextSpan(currentToken->GetSpan().GetStart(), current->GetSpan().GetEnd()), leftHand, logicOp,
rightHand);
currentToken = current;
return true;
}
BitOperator bitOp;
@ -742,6 +749,7 @@ namespace MalachScript::Parser {
}
out = new ParsedBinaryStatement<BitOperator>(
TextSpan(currentToken->GetSpan().GetStart(), current->GetSpan().GetEnd()), leftHand, bitOp, rightHand);
currentToken = current;
return true;
}
out = leftHand;
@ -751,6 +759,135 @@ namespace MalachScript::Parser {
bool Parser::ParseExprTerm([[maybe_unused]] const ParsedStatement*& out,
[[maybe_unused]] const LexToken*& currentToken) {
const auto* current = currentToken;
// TODO ([type '='] initlist)
PreOperator preOperator;
bool hasPreOp = ParsePreOp(preOperator, currentToken);
if (hasPreOp) {
PROGRESS_TOKEN(current);
}
const ParsedStatement* operand = nullptr;
if (!ParseExprValue(operand, current)) {
return false;
}
// TODO: remainder of
if (current->GetKind() == LexTokenKind::PlusPlusSymbol) {
operand = new ParsedIncrementStatement(TextSpan(operand->GetSpan().GetStart(), current->GetSpan().GetEnd()),
operand);
PROGRESS_TOKEN(current);
} else if (current->GetKind() == LexTokenKind::MinusMinusSymbol) {
operand = new ParsedDecrementStatement(TextSpan(operand->GetSpan().GetStart(), current->GetSpan().GetEnd()),
operand);
PROGRESS_TOKEN(current);
}
if (hasPreOp) {
// TODO: integrate pre operator
}
out = operand;
currentToken = current;
return true;
}
bool Parser::ParseExprValue(const ParsedStatement*& out, const LexToken*& currentToken) {
const auto* current = currentToken;
if (current->GetKind() == LexTokenKind::VoidKeyword) {
PROGRESS_TOKEN(current);
out = new ParsedVoidStatement(currentToken->GetSpan());
currentToken = current;
return true;
}
// TODO: constructcall
// TODO: funccall
if (ParseVarAccess(out, current)) {
currentToken = current;
return true;
}
// TODO: cast
if (ParseLiteral(out, current)) {
currentToken = current;
return true;
}
if (current->GetKind() == LexTokenKind::OpenParenthesisSymbol) {
PROGRESS_TOKEN(current);
if (!ParseAssign(out, current)) {
LogError(Diagnostics::DiagnosticType::UnexpectedToken, current->GetSpan());
}
EXPECT_TOKEN(current, CloseParenthesisSymbol);
return true;
}
// TODO: lambda
return false;
}
bool Parser::ParseLiteral(const ParsedStatement*& out, const LexToken*& currentToken) {
switch (currentToken->GetKind()) {
case LexTokenKind::IntegerLiteral:
out = new ParsedLiteralStatement<ParseInt>(
currentToken->GetSpan(), static_cast<const IntegerLiteral*>(currentToken)->GetValue());
PROGRESS_TOKEN(currentToken);
return true;
case LexTokenKind::FloatLiteral:
out = new ParsedLiteralStatement<ParseFloat>(
currentToken->GetSpan(), static_cast<const FloatLiteral*>(currentToken)->GetValue());
PROGRESS_TOKEN(currentToken);
return true;
case LexTokenKind::StringLiteral:
out = new ParsedLiteralStatement<ParseString>(
currentToken->GetSpan(), static_cast<const StringLiteral*>(currentToken)->GetValue());
PROGRESS_TOKEN(currentToken);
return true;
case LexTokenKind::TrueKeyword:
out = new ParsedLiteralStatement<bool>(currentToken->GetSpan(), true);
PROGRESS_TOKEN(currentToken);
return true;
case LexTokenKind::FalseKeyword:
out = new ParsedLiteralStatement<bool>(currentToken->GetSpan(), false);
PROGRESS_TOKEN(currentToken);
return true;
case LexTokenKind::NullKeyword:
out = new ParsedLiteralStatement<void*>(currentToken->GetSpan(), nullptr);
PROGRESS_TOKEN(currentToken);
return true;
default: return false;
}
}
bool Parser::ParseReturn(const ParsedStatement*& out, const LexToken*& currentToken) {
auto start = currentToken->GetSpan().GetStart();
if (currentToken->GetKind() != LexTokenKind::ReturnKeyword) {
return false;
}
PROGRESS_TOKEN(currentToken);
const ParsedStatement* returnBody = nullptr;
ParseAssign(returnBody, currentToken);
EXPECT_TOKEN(currentToken, SemicolonSymbol);
out = new ParsedReturnStatement(TextSpan(start, currentToken->GetSpan().GetEnd()), returnBody);
return true;
}
bool Parser::ParseExprStat(const ParsedStatement*& out, const LexToken*& currentToken) {
if (!ParseAssign(out, currentToken)){
return false;
}
EXPECT_TOKEN(currentToken, SemicolonSymbol);
return true;
}
bool Parser::ParseVarAccess(const ParsedStatement*& out, const LexToken*& currentToken) {
std::vector<Identifier> scope;
auto start = currentToken->GetSpan().GetStart();
if (ParseScope(scope, currentToken)) {
out = new ParsedVarAccessStatement(TextSpan(start, currentToken->GetSpan().GetEnd()), scope);
return true;
}
Identifier identifier;
if (ParseIdentifier(identifier, currentToken)) {
scope.clear();
scope.push_back(identifier);
out = new ParsedVarAccessStatement(currentToken->GetSpan(), scope);
PROGRESS_TOKEN(currentToken);
return true;
}
return false;
}
}

View File

@ -117,24 +117,35 @@ namespace MalachScript::Parser {
bool ParseType(const ParsedStatement*& out, const LexToken*& currentToken);
bool ParseAssign(const ParsedStatement*& out, const LexToken*& currentToken);
// InitList
// ExprPreOp
inline static bool ParsePreOp(PreOperator& op, const LexToken*& token) {
switch (token->GetKind()) {
case LexTokenKind::MinusSymbol: op = PreOperator::Negation; return true;
case LexTokenKind::PlusSymbol: op = PreOperator::Identity; return true;
case LexTokenKind::ExclamationMarkSymbol: op = PreOperator::Inversion; return true;
case LexTokenKind::PlusPlusSymbol: op = PreOperator::Increment; return true;
case LexTokenKind::MinusMinusSymbol: op = PreOperator::Decrement; return true;
case LexTokenKind::TildeSymbol: op = PreOperator::BitwiseComplement; return true;
case LexTokenKind::AtSymbol: op = PreOperator::Handle; return true;
default: return false;
}
}
// ArgList
// FuncCall
// ConstructCall
// VarAccess
bool ParseVarAccess(const ParsedStatement*& out, const LexToken*& currentToken);
// Cast
// Literal
bool ParseLiteral(const ParsedStatement*& out, const LexToken*& currentToken);
bool ParseTypeMod(TypeMod& typeMod, const LexToken*& currentToken);
// Lambda
// ExprValue
bool ParseExprValue(const ParsedStatement*& out, const LexToken*& currentToken);
// ExprPostOp
bool ParseExprTerm(const ParsedStatement*& out, const LexToken*& currentToken);
bool ParseExpr(const ParsedStatement*& out, const LexToken*& currentToken);
bool ParseTernary(const ParsedStatement*& out, const LexToken*& currentToken);
// Return
// ExprStat
bool ParseReturn(const ParsedStatement*& out, const LexToken*& currentToken);
bool ParseExprStat(const ParsedStatement*& out, const LexToken*& currentToken);
// Continue
// Break

View File

@ -0,0 +1,10 @@
#ifndef MALACHSCRIPT_PARSERDEFINES_HPP
#define MALACHSCRIPT_PARSERDEFINES_HPP
#include <cstdint>
using ParseInt = uintmax_t;
using ParseFloat = long double;
using ParseString = std::u8string;
#endif // MALACHSCRIPT_PARSERDEFINES_HPP

View File

@ -307,6 +307,57 @@ namespace MalachScript::Parser {
TOperator _operator;
std::unique_ptr<const ParsedStatement> _rightHand;
};
class ParsedVoidStatement : public ParsedStatementImpl<ParsedStatementKind::Void> {
public:
ParsedVoidStatement(const TextSpan& span) : ParsedStatementImpl(span) {}
};
template <class T> class ParsedLiteralStatement : public ParsedStatementImpl<ParsedStatementKind::Literal> {
public:
ParsedLiteralStatement(const TextSpan& span, T literalValue)
: ParsedStatementImpl(span), _literalValue(literalValue) {}
[[nodiscard]] inline const T& GetLiteralValue() const noexcept { return _literalValue; }
private:
T _literalValue;
};
class ParsedReturnStatement : public ParsedStatementImpl<ParsedStatementKind::Return>{
public:
ParsedReturnStatement(const TextSpan& span, const ParsedStatement* statement)
: ParsedStatementImpl(span), _statement(statement) {}
private:
std::unique_ptr<const ParsedStatement> _statement;
};
class ParsedVarAccessStatement : public ParsedStatementImpl<ParsedStatementKind::VarAccess>{
public:
ParsedVarAccessStatement(const TextSpan& span, std::vector<Identifier> scope)
: ParsedStatementImpl(span), _scope(std::move(scope)) {}
private:
std::vector<Identifier> _scope;
};
class ParsedIncrementStatement : public ParsedStatementImpl<ParsedStatementKind::Increment>{
public:
ParsedIncrementStatement(const TextSpan& span, const ParsedStatement* statement)
: ParsedStatementImpl(span), _statement(statement) {}
private:
std::unique_ptr<const ParsedStatement> _statement;
};
class ParsedDecrementStatement : public ParsedStatementImpl<ParsedStatementKind::Decrement>{
public:
ParsedDecrementStatement(const TextSpan& span, const ParsedStatement* statement)
: ParsedStatementImpl(span), _statement(statement) {}
private:
std::unique_ptr<const ParsedStatement> _statement;
};
}
#endif // MALACHSCRIPT_PARSEDSTATEMENT_HPP

View File

@ -15,7 +15,13 @@ namespace MalachScript::Parser {
StatBlock,
If,
Assign,
BinaryExpression
BinaryExpression,
Void,
Literal,
Return,
VarAccess,
Increment,
Decrement,
};
}

View File

@ -34,10 +34,10 @@ INTEGER_TEST("0D123456", 123456);
INTEGER_TEST("50000000000", 50000000000);
// Decimal float lexing
FLOAT_TEST("123.456", 123.456);
FLOAT_TEST("0.456", 0.456);
FLOAT_TEST("0.456e12", 0.456e12);
FLOAT_TEST("0.456E12", 0.456E12);
FLOAT_TEST("123.456", 123.456L);
FLOAT_TEST("0.456", 0.456L);
FLOAT_TEST("0.456e12", 0.456e12L);
FLOAT_TEST("0.456E12", 0.456E12L);
// Hexadecimal lexing
INTEGER_TEST("0x0", 0);

View File

@ -48,4 +48,4 @@ PARSE_TEST("Parse class with virtprop", "class foobar { private bool foo { get;
REQUIRE(virtPropStatement->HasSet());
REQUIRE(virtPropStatement->GetGetStatement() == nullptr);
REQUIRE(virtPropStatement->GetSetStatement() == nullptr);
})
})

View File

@ -128,4 +128,81 @@ PARSER_TEST(
REQUIRE(FuncAttrHelpers::Contains(virtPropStatement->GetSetFuncAttr(), FuncAttr::Override));
REQUIRE(virtPropStatement->GetGetStatement() == nullptr);
REQUIRE(virtPropStatement->GetSetStatement() == nullptr);
})
/// Parse class foobar {
// int i;
// bool foo {
// get {
// if (true) return true;
// return false;
// }
// set{
// if (1 == 1) i++;
// i--;
// }
// }
//}
PARSER_TEST(
"Virtprops with bodies",
PARSER_TEST_TOKENS(new Parser::LexTokenImpl<Parser::LexTokenKind::ClassKeyword>(TextSpan(0, 0)),
new Parser::IdentifierToken(TextSpan(0, 0), u8"foobar"),
new Parser::LexTokenImpl<Parser::LexTokenKind::OpenCurlyParenthesisSymbol>(TextSpan(0, 0)),
new Parser::IdentifierToken(TextSpan(0, 0), u8"int"),
new Parser::IdentifierToken(TextSpan(0, 0), u8"i"),
new Parser::LexTokenImpl<Parser::LexTokenKind::SemicolonSymbol>(TextSpan(0, 0)),
new Parser::IdentifierToken(TextSpan(0, 0), u8"bool"),
new Parser::IdentifierToken(TextSpan(0, 0), u8"foo"),
new Parser::LexTokenImpl<Parser::LexTokenKind::OpenCurlyParenthesisSymbol>(TextSpan(0, 0)),
new Parser::LexTokenImpl<Parser::LexTokenKind::GetKeyword>(TextSpan(0, 0)),
new Parser::LexTokenImpl<Parser::LexTokenKind::OpenCurlyParenthesisSymbol>(TextSpan(0, 0)),
new Parser::LexTokenImpl<Parser::LexTokenKind::IfKeyword>(TextSpan(0, 0)),
new Parser::LexTokenImpl<Parser::LexTokenKind::OpenParenthesisSymbol>(TextSpan(0, 0)),
new Parser::LexTokenImpl<Parser::LexTokenKind::TrueKeyword>(TextSpan(0, 0)),
new Parser::LexTokenImpl<Parser::LexTokenKind::CloseParenthesisSymbol>(TextSpan(0, 0)),
new Parser::LexTokenImpl<Parser::LexTokenKind::ReturnKeyword>(TextSpan(0, 0)),
new Parser::LexTokenImpl<Parser::LexTokenKind::TrueKeyword>(TextSpan(0, 0)),
new Parser::LexTokenImpl<Parser::LexTokenKind::SemicolonSymbol>(TextSpan(0, 0)),
new Parser::LexTokenImpl<Parser::LexTokenKind::ReturnKeyword>(TextSpan(0, 0)),
new Parser::LexTokenImpl<Parser::LexTokenKind::FalseKeyword>(TextSpan(0, 0)),
new Parser::LexTokenImpl<Parser::LexTokenKind::SemicolonSymbol>(TextSpan(0, 0)),
new Parser::LexTokenImpl<Parser::LexTokenKind::CloseCurlyParenthesisSymbol>(TextSpan(0, 0)),
new Parser::LexTokenImpl<Parser::LexTokenKind::SetKeyword>(TextSpan(0, 0)),
new Parser::LexTokenImpl<Parser::LexTokenKind::OpenCurlyParenthesisSymbol>(TextSpan(0, 0)),
new Parser::LexTokenImpl<Parser::LexTokenKind::IfKeyword>(TextSpan(0, 0)),
new Parser::LexTokenImpl<Parser::LexTokenKind::OpenParenthesisSymbol>(TextSpan(0, 0)),
new Parser::IntegerLiteral(TextSpan(0, 0), 1),
new Parser::LexTokenImpl<Parser::LexTokenKind::EqualsEqualsSymbol>(TextSpan(0, 0)),
new Parser::IntegerLiteral(TextSpan(0, 0), 1),
new Parser::LexTokenImpl<Parser::LexTokenKind::CloseParenthesisSymbol>(TextSpan(0, 0)),
new Parser::IdentifierToken(TextSpan(0, 0), u8"i"),
new Parser::LexTokenImpl<Parser::LexTokenKind::PlusPlusSymbol>(TextSpan(0, 0)),
new Parser::LexTokenImpl<Parser::LexTokenKind::SemicolonSymbol>(TextSpan(0, 0)),
new Parser::IdentifierToken(TextSpan(0, 0), u8"i"),
new Parser::LexTokenImpl<Parser::LexTokenKind::MinusMinusSymbol>(TextSpan(0, 0)),
new Parser::LexTokenImpl<Parser::LexTokenKind::SemicolonSymbol>(TextSpan(0, 0)),
new Parser::LexTokenImpl<Parser::LexTokenKind::CloseCurlyParenthesisSymbol>(TextSpan(0, 0)),
new Parser::LexTokenImpl<Parser::LexTokenKind::CloseCurlyParenthesisSymbol>(TextSpan(0, 0)),
new Parser::LexTokenImpl<Parser::LexTokenKind::CloseCurlyParenthesisSymbol>(TextSpan(0, 0))),
{
REQUIRE(script->GetStatements().size() == 1);
auto firstStatement = script->GetStatements()[0].get();
REQUIRE(firstStatement->GetKind() == Parser::ParsedStatementKind::Class);
auto firstClassStatement =
dynamic_cast<const MalachScript::Parser::ParsedClassStatement*>(firstStatement)->GetBody()[1].get();
REQUIRE(firstClassStatement->GetKind() == Parser::ParsedStatementKind::VirtProp);
auto virtPropStatement =
dynamic_cast<const MalachScript::Parser::ParsedVirtPropStatement*>(firstClassStatement);
REQUIRE(virtPropStatement->GetAccess() == MalachScript::AccessModifier::Public);
REQUIRE(virtPropStatement->GetIdentifier().GetString() == u8"foo");
REQUIRE(virtPropStatement->HasGet());
REQUIRE(virtPropStatement->HasSet());
REQUIRE_FALSE(virtPropStatement->IsGetConst());
REQUIRE_FALSE(virtPropStatement->IsSetConst());
REQUIRE(virtPropStatement->GetGetStatement() != nullptr);
REQUIRE(virtPropStatement->GetSetStatement() != nullptr);
})