pattern: Added parameter packs
This commit is contained in:
parent
111c4b71aa
commit
69bd438fe1
@ -95,6 +95,7 @@ namespace hex {
|
|||||||
constexpr static u32 UnlimitedParameters = 0xFFFF'FFFF;
|
constexpr static u32 UnlimitedParameters = 0xFFFF'FFFF;
|
||||||
constexpr static u32 MoreParametersThan = 0x8000'0000;
|
constexpr static u32 MoreParametersThan = 0x8000'0000;
|
||||||
constexpr static u32 LessParametersThan = 0x4000'0000;
|
constexpr static u32 LessParametersThan = 0x4000'0000;
|
||||||
|
constexpr static u32 ExactlyOrMoreParametersThan = 0x2000'0000;
|
||||||
constexpr static u32 NoParameters = 0x0000'0000;
|
constexpr static u32 NoParameters = 0x0000'0000;
|
||||||
|
|
||||||
using Namespace = std::vector<std::string>;
|
using Namespace = std::vector<std::string>;
|
||||||
|
@ -1527,6 +1527,22 @@ namespace hex::pl {
|
|||||||
std::vector<std::pair<std::string, ASTNode *>> m_entries;
|
std::vector<std::pair<std::string, ASTNode *>> m_entries;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
class ASTNodeParameterPack : public ASTNode {
|
||||||
|
public:
|
||||||
|
ASTNodeParameterPack(const std::vector<Token::Literal> &values) : m_values(values) {}
|
||||||
|
|
||||||
|
[[nodiscard]] ASTNode *clone() const override {
|
||||||
|
return new ASTNodeParameterPack(*this);
|
||||||
|
}
|
||||||
|
|
||||||
|
const std::vector<Token::Literal> &getValues() const {
|
||||||
|
return this->m_values;
|
||||||
|
}
|
||||||
|
|
||||||
|
private:
|
||||||
|
std::vector<Token::Literal> m_values;
|
||||||
|
};
|
||||||
|
|
||||||
class ASTNodeRValue : public ASTNode {
|
class ASTNodeRValue : public ASTNode {
|
||||||
public:
|
public:
|
||||||
using Path = std::vector<std::variant<std::string, ASTNode *>>;
|
using Path = std::vector<std::variant<std::string, ASTNode *>>;
|
||||||
@ -1554,6 +1570,10 @@ namespace hex::pl {
|
|||||||
if (this->getPath().size() == 1) {
|
if (this->getPath().size() == 1) {
|
||||||
if (auto name = std::get_if<std::string>(&this->getPath().front()); name != nullptr) {
|
if (auto name = std::get_if<std::string>(&this->getPath().front()); name != nullptr) {
|
||||||
if (*name == "$") return new ASTNodeLiteral(u128(evaluator->dataOffset()));
|
if (*name == "$") return new ASTNodeLiteral(u128(evaluator->dataOffset()));
|
||||||
|
|
||||||
|
auto ¶meterPack = evaluator->getScope(0).parameterPack;
|
||||||
|
if (parameterPack && *name == parameterPack->name)
|
||||||
|
return new ASTNodeParameterPack(parameterPack->values);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -1640,6 +1660,7 @@ namespace hex::pl {
|
|||||||
PatternData *currPattern = nullptr;
|
PatternData *currPattern = nullptr;
|
||||||
i32 scopeIndex = 0;
|
i32 scopeIndex = 0;
|
||||||
|
|
||||||
|
|
||||||
if (!evaluator->isGlobalScope()) {
|
if (!evaluator->isGlobalScope()) {
|
||||||
auto globalScope = evaluator->getGlobalScope().scope;
|
auto globalScope = evaluator->getGlobalScope().scope;
|
||||||
std::copy(globalScope->begin(), globalScope->end(), std::back_inserter(searchScope));
|
std::copy(globalScope->begin(), globalScope->end(), std::back_inserter(searchScope));
|
||||||
@ -1697,9 +1718,10 @@ namespace hex::pl {
|
|||||||
if (name == "$")
|
if (name == "$")
|
||||||
LogConsole::abortEvaluation("invalid use of placeholder operator in rvalue");
|
LogConsole::abortEvaluation("invalid use of placeholder operator in rvalue");
|
||||||
|
|
||||||
if (!found)
|
if (!found) {
|
||||||
LogConsole::abortEvaluation(hex::format("no variable named '{}' found", name), this);
|
LogConsole::abortEvaluation(hex::format("no variable named '{}' found", name), this);
|
||||||
}
|
}
|
||||||
|
}
|
||||||
} else {
|
} else {
|
||||||
// Array indexing
|
// Array indexing
|
||||||
auto index = dynamic_cast<ASTNodeLiteral *>(std::get<ASTNode *>(part)->evaluate(evaluator));
|
auto index = dynamic_cast<ASTNodeLiteral *>(std::get<ASTNode *>(part)->evaluate(evaluator));
|
||||||
@ -1973,10 +1995,16 @@ namespace hex::pl {
|
|||||||
auto expression = param->evaluate(evaluator);
|
auto expression = param->evaluate(evaluator);
|
||||||
ON_SCOPE_EXIT { delete expression; };
|
ON_SCOPE_EXIT { delete expression; };
|
||||||
|
|
||||||
auto literal = dynamic_cast<ASTNodeLiteral *>(expression->evaluate(evaluator));
|
if (auto literal = dynamic_cast<ASTNodeLiteral *>(expression->evaluate(evaluator))) {
|
||||||
ON_SCOPE_EXIT { delete literal; };
|
|
||||||
|
|
||||||
evaluatedParams.push_back(literal->getValue());
|
evaluatedParams.push_back(literal->getValue());
|
||||||
|
delete literal;
|
||||||
|
} else if (auto parameterPack = dynamic_cast<ASTNodeParameterPack *>(expression->evaluate(evaluator))) {
|
||||||
|
for (auto &value : parameterPack->getValues()) {
|
||||||
|
evaluatedParams.push_back(value);
|
||||||
|
}
|
||||||
|
|
||||||
|
delete parameterPack;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
auto &customFunctions = evaluator->getCustomFunctions();
|
auto &customFunctions = evaluator->getCustomFunctions();
|
||||||
@ -1993,10 +2021,13 @@ namespace hex::pl {
|
|||||||
; // Don't check parameter count
|
; // Don't check parameter count
|
||||||
} else if (function.parameterCount & ContentRegistry::PatternLanguage::LessParametersThan) {
|
} else if (function.parameterCount & ContentRegistry::PatternLanguage::LessParametersThan) {
|
||||||
if (evaluatedParams.size() >= (function.parameterCount & ~ContentRegistry::PatternLanguage::LessParametersThan))
|
if (evaluatedParams.size() >= (function.parameterCount & ~ContentRegistry::PatternLanguage::LessParametersThan))
|
||||||
LogConsole::abortEvaluation(hex::format("too many parameters for function '{0}'. Expected {1}", this->m_functionName, function.parameterCount & ~ContentRegistry::PatternLanguage::LessParametersThan), this);
|
LogConsole::abortEvaluation(hex::format("too many parameters for function '{0}'. Expected less than {1}", this->m_functionName, function.parameterCount & ~ContentRegistry::PatternLanguage::LessParametersThan), this);
|
||||||
} else if (function.parameterCount & ContentRegistry::PatternLanguage::MoreParametersThan) {
|
} else if (function.parameterCount & ContentRegistry::PatternLanguage::MoreParametersThan) {
|
||||||
if (evaluatedParams.size() <= (function.parameterCount & ~ContentRegistry::PatternLanguage::MoreParametersThan))
|
if (evaluatedParams.size() <= (function.parameterCount & ~ContentRegistry::PatternLanguage::MoreParametersThan))
|
||||||
LogConsole::abortEvaluation(hex::format("too few parameters for function '{0}'. Expected {1}", this->m_functionName, function.parameterCount & ~ContentRegistry::PatternLanguage::MoreParametersThan), this);
|
LogConsole::abortEvaluation(hex::format("too few parameters for function '{0}'. Expected more than {1}", this->m_functionName, function.parameterCount & ~ContentRegistry::PatternLanguage::MoreParametersThan), this);
|
||||||
|
} else if (function.parameterCount & ContentRegistry::PatternLanguage::ExactlyOrMoreParametersThan) {
|
||||||
|
if (evaluatedParams.size() < (function.parameterCount & ~ContentRegistry::PatternLanguage::ExactlyOrMoreParametersThan))
|
||||||
|
LogConsole::abortEvaluation(hex::format("too few parameters for function '{0}'. Expected more than {1}", this->m_functionName, (function.parameterCount - 1) & ~ContentRegistry::PatternLanguage::ExactlyOrMoreParametersThan), this);
|
||||||
} else if (function.parameterCount != evaluatedParams.size()) {
|
} else if (function.parameterCount != evaluatedParams.size()) {
|
||||||
LogConsole::abortEvaluation(hex::format("invalid number of parameters for function '{0}'. Expected {1}", this->m_functionName, function.parameterCount), this);
|
LogConsole::abortEvaluation(hex::format("invalid number of parameters for function '{0}'. Expected {1}", this->m_functionName, function.parameterCount), this);
|
||||||
}
|
}
|
||||||
@ -2193,8 +2224,8 @@ namespace hex::pl {
|
|||||||
|
|
||||||
class ASTNodeFunctionDefinition : public ASTNode {
|
class ASTNodeFunctionDefinition : public ASTNode {
|
||||||
public:
|
public:
|
||||||
ASTNodeFunctionDefinition(std::string name, std::vector<std::pair<std::string, ASTNode *>> params, std::vector<ASTNode *> body)
|
ASTNodeFunctionDefinition(std::string name, std::vector<std::pair<std::string, ASTNode *>> params, std::vector<ASTNode *> body, std::optional<std::string> parameterPack)
|
||||||
: m_name(std::move(name)), m_params(std::move(params)), m_body(std::move(body)) {
|
: m_name(std::move(name)), m_params(std::move(params)), m_body(std::move(body)), m_parameterPack(std::move(parameterPack)) {
|
||||||
}
|
}
|
||||||
|
|
||||||
ASTNodeFunctionDefinition(const ASTNodeFunctionDefinition &other) : ASTNode(other) {
|
ASTNodeFunctionDefinition(const ASTNodeFunctionDefinition &other) : ASTNode(other) {
|
||||||
@ -2235,7 +2266,12 @@ namespace hex::pl {
|
|||||||
|
|
||||||
[[nodiscard]] ASTNode *evaluate(Evaluator *evaluator) const override {
|
[[nodiscard]] ASTNode *evaluate(Evaluator *evaluator) const override {
|
||||||
|
|
||||||
evaluator->addCustomFunction(this->m_name, this->m_params.size(), [this](Evaluator *ctx, const std::vector<Token::Literal> ¶ms) -> std::optional<Token::Literal> {
|
size_t paramCount = this->m_params.size();
|
||||||
|
|
||||||
|
if (this->m_parameterPack.has_value())
|
||||||
|
paramCount |= ContentRegistry::PatternLanguage::ExactlyOrMoreParametersThan;
|
||||||
|
|
||||||
|
evaluator->addCustomFunction(this->m_name, paramCount, [this](Evaluator *ctx, const std::vector<Token::Literal> ¶ms) -> std::optional<Token::Literal> {
|
||||||
std::vector<PatternData *> variables;
|
std::vector<PatternData *> variables;
|
||||||
|
|
||||||
ctx->pushScope(nullptr, variables);
|
ctx->pushScope(nullptr, variables);
|
||||||
@ -2246,12 +2282,19 @@ namespace hex::pl {
|
|||||||
ctx->popScope();
|
ctx->popScope();
|
||||||
};
|
};
|
||||||
|
|
||||||
u32 paramIndex = 0;
|
if (this->m_parameterPack.has_value()) {
|
||||||
for (const auto &[name, type] : this->m_params) {
|
std::vector<Token::Literal> parameterPackContent;
|
||||||
|
for (u32 paramIndex = this->m_params.size(); paramIndex < params.size(); paramIndex++)
|
||||||
|
parameterPackContent.push_back(params[paramIndex]);
|
||||||
|
|
||||||
|
ctx->createParameterPack(this->m_parameterPack.value(), parameterPackContent);
|
||||||
|
}
|
||||||
|
|
||||||
|
for (u32 paramIndex = 0; paramIndex < this->m_params.size(); paramIndex++) {
|
||||||
|
const auto &[name, type] = this->m_params[paramIndex];
|
||||||
|
|
||||||
ctx->createVariable(name, type, params[paramIndex]);
|
ctx->createVariable(name, type, params[paramIndex]);
|
||||||
ctx->setVariable(name, params[paramIndex]);
|
ctx->setVariable(name, params[paramIndex]);
|
||||||
|
|
||||||
paramIndex++;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
for (auto statement : this->m_body) {
|
for (auto statement : this->m_body) {
|
||||||
@ -2283,6 +2326,7 @@ namespace hex::pl {
|
|||||||
std::string m_name;
|
std::string m_name;
|
||||||
std::vector<std::pair<std::string, ASTNode *>> m_params;
|
std::vector<std::pair<std::string, ASTNode *>> m_params;
|
||||||
std::vector<ASTNode *> m_body;
|
std::vector<ASTNode *> m_body;
|
||||||
|
std::optional<std::string> m_parameterPack;
|
||||||
};
|
};
|
||||||
|
|
||||||
class ASTNodeCompoundStatement : public ASTNode {
|
class ASTNodeCompoundStatement : public ASTNode {
|
||||||
|
@ -46,9 +46,15 @@ namespace hex::pl {
|
|||||||
return this->m_console;
|
return this->m_console;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
struct ParameterPack {
|
||||||
|
std::string name;
|
||||||
|
std::vector<Token::Literal> values;
|
||||||
|
};
|
||||||
|
|
||||||
struct Scope {
|
struct Scope {
|
||||||
PatternData *parent;
|
PatternData *parent;
|
||||||
std::vector<PatternData *> *scope;
|
std::vector<PatternData *> *scope;
|
||||||
|
std::optional<ParameterPack> parameterPack;
|
||||||
};
|
};
|
||||||
void pushScope(PatternData *parent, std::vector<PatternData *> &scope) {
|
void pushScope(PatternData *parent, std::vector<PatternData *> &scope) {
|
||||||
if (this->m_scopes.size() > this->getEvaluationDepth())
|
if (this->m_scopes.size() > this->getEvaluationDepth())
|
||||||
@ -63,11 +69,19 @@ namespace hex::pl {
|
|||||||
this->m_scopes.pop_back();
|
this->m_scopes.pop_back();
|
||||||
}
|
}
|
||||||
|
|
||||||
const Scope &getScope(i32 index) {
|
Scope &getScope(i32 index) {
|
||||||
return this->m_scopes[this->m_scopes.size() - 1 + index];
|
return this->m_scopes[this->m_scopes.size() - 1 + index];
|
||||||
}
|
}
|
||||||
|
|
||||||
const Scope &getGlobalScope() {
|
const Scope &getScope(i32 index) const {
|
||||||
|
return this->m_scopes[this->m_scopes.size() - 1 + index];
|
||||||
|
}
|
||||||
|
|
||||||
|
Scope &getGlobalScope() {
|
||||||
|
return this->m_scopes.front();
|
||||||
|
}
|
||||||
|
|
||||||
|
const Scope &getGlobalScope() const {
|
||||||
return this->m_scopes.front();
|
return this->m_scopes.front();
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -167,6 +181,7 @@ namespace hex::pl {
|
|||||||
return this->m_stack;
|
return this->m_stack;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void createParameterPack(const std::string &name, const std::vector<Token::Literal> &values);
|
||||||
void createVariable(const std::string &name, ASTNode *type, const std::optional<Token::Literal> &value = std::nullopt, bool outVariable = false);
|
void createVariable(const std::string &name, ASTNode *type, const std::optional<Token::Literal> &value = std::nullopt, bool outVariable = false);
|
||||||
void setVariable(const std::string &name, const Token::Literal &value);
|
void setVariable(const std::string &name, const Token::Literal &value);
|
||||||
|
|
||||||
|
@ -342,6 +342,7 @@ namespace hex::pl {
|
|||||||
#define VALUETYPE_UNSIGNED COMPONENT(ValueType, Unsigned)
|
#define VALUETYPE_UNSIGNED COMPONENT(ValueType, Unsigned)
|
||||||
#define VALUETYPE_SIGNED COMPONENT(ValueType, Signed)
|
#define VALUETYPE_SIGNED COMPONENT(ValueType, Signed)
|
||||||
#define VALUETYPE_FLOATINGPOINT COMPONENT(ValueType, FloatingPoint)
|
#define VALUETYPE_FLOATINGPOINT COMPONENT(ValueType, FloatingPoint)
|
||||||
|
#define VALUETYPE_AUTO COMPONENT(ValueType, Auto)
|
||||||
#define VALUETYPE_ANY COMPONENT(ValueType, Any)
|
#define VALUETYPE_ANY COMPONENT(ValueType, Any)
|
||||||
|
|
||||||
#define SEPARATOR_ROUNDBRACKETOPEN COMPONENT(Separator, RoundBracketOpen)
|
#define SEPARATOR_ROUNDBRACKETOPEN COMPONENT(Separator, RoundBracketOpen)
|
||||||
|
@ -6,6 +6,13 @@ namespace hex::pl {
|
|||||||
|
|
||||||
Evaluator *PatternCreationLimiter::s_evaluator = nullptr;
|
Evaluator *PatternCreationLimiter::s_evaluator = nullptr;
|
||||||
|
|
||||||
|
void Evaluator::createParameterPack(const std::string &name, const std::vector<Token::Literal> &values) {
|
||||||
|
this->getScope(0).parameterPack = ParameterPack {
|
||||||
|
name,
|
||||||
|
values
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
void Evaluator::createVariable(const std::string &name, ASTNode *type, const std::optional<Token::Literal> &value, bool outVariable) {
|
void Evaluator::createVariable(const std::string &name, ASTNode *type, const std::optional<Token::Literal> &value, bool outVariable) {
|
||||||
auto &variables = *this->getScope(0).scope;
|
auto &variables = *this->getScope(0).scope;
|
||||||
for (auto &variable : variables) {
|
for (auto &variable : variables) {
|
||||||
@ -15,7 +22,7 @@ namespace hex::pl {
|
|||||||
}
|
}
|
||||||
|
|
||||||
auto startOffset = this->dataOffset();
|
auto startOffset = this->dataOffset();
|
||||||
auto pattern = type->createPatterns(this).front();
|
auto pattern = type == nullptr ? nullptr : type->createPatterns(this).front();
|
||||||
this->dataOffset() = startOffset;
|
this->dataOffset() = startOffset;
|
||||||
|
|
||||||
if (pattern == nullptr) {
|
if (pattern == nullptr) {
|
||||||
@ -38,7 +45,7 @@ namespace hex::pl {
|
|||||||
else if (std::get_if<std::string>(&value.value()) != nullptr)
|
else if (std::get_if<std::string>(&value.value()) != nullptr)
|
||||||
pattern = new PatternDataString(0, 1);
|
pattern = new PatternDataString(0, 1);
|
||||||
else
|
else
|
||||||
__builtin_unreachable();
|
LogConsole::abortEvaluation("cannot determine type of auto variable", type);
|
||||||
}
|
}
|
||||||
|
|
||||||
pattern->setVariableName(name);
|
pattern->setVariableName(name);
|
||||||
|
@ -13,7 +13,10 @@ namespace hex::pl {
|
|||||||
}
|
}
|
||||||
|
|
||||||
[[noreturn]] void LogConsole::abortEvaluation(const std::string &message, const ASTNode *node) {
|
[[noreturn]] void LogConsole::abortEvaluation(const std::string &message, const ASTNode *node) {
|
||||||
throw EvaluateError(static_cast<const ASTNode *>(node)->getLineNumber(), message);
|
if (node == nullptr)
|
||||||
|
abortEvaluation(message);
|
||||||
|
else
|
||||||
|
throw EvaluateError(node->getLineNumber(), message);
|
||||||
}
|
}
|
||||||
|
|
||||||
void LogConsole::clear() {
|
void LogConsole::clear() {
|
||||||
|
@ -431,11 +431,20 @@ namespace hex::pl {
|
|||||||
ASTNode *Parser::parseFunctionDefinition() {
|
ASTNode *Parser::parseFunctionDefinition() {
|
||||||
const auto &functionName = getValue<Token::Identifier>(-2).get();
|
const auto &functionName = getValue<Token::Identifier>(-2).get();
|
||||||
std::vector<std::pair<std::string, ASTNode *>> params;
|
std::vector<std::pair<std::string, ASTNode *>> params;
|
||||||
|
std::optional<std::string> parameterPack;
|
||||||
|
|
||||||
// Parse parameter list
|
// Parse parameter list
|
||||||
bool hasParams = !peek(SEPARATOR_ROUNDBRACKETCLOSE);
|
bool hasParams = !peek(SEPARATOR_ROUNDBRACKETCLOSE);
|
||||||
u32 unnamedParamCount = 0;
|
u32 unnamedParamCount = 0;
|
||||||
while (hasParams) {
|
while (hasParams) {
|
||||||
|
if (MATCHES(sequence(VALUETYPE_AUTO, SEPARATOR_DOT, SEPARATOR_DOT, SEPARATOR_DOT, IDENTIFIER))) {
|
||||||
|
parameterPack = getValue<Token::Identifier>(-1).get();
|
||||||
|
|
||||||
|
if (MATCHES(sequence(SEPARATOR_COMMA)))
|
||||||
|
throwParseError("parameter pack can only appear at end of parameter list");
|
||||||
|
|
||||||
|
break;
|
||||||
|
} else {
|
||||||
auto type = parseType(true);
|
auto type = parseType(true);
|
||||||
|
|
||||||
if (MATCHES(sequence(IDENTIFIER)))
|
if (MATCHES(sequence(IDENTIFIER)))
|
||||||
@ -446,16 +455,13 @@ namespace hex::pl {
|
|||||||
}
|
}
|
||||||
|
|
||||||
if (!MATCHES(sequence(SEPARATOR_COMMA))) {
|
if (!MATCHES(sequence(SEPARATOR_COMMA))) {
|
||||||
if (MATCHES(sequence(SEPARATOR_ROUNDBRACKETCLOSE)))
|
|
||||||
break;
|
break;
|
||||||
else
|
|
||||||
throwParseError("expected closing ')' after parameter list");
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if (!hasParams) {
|
}
|
||||||
|
|
||||||
if (!MATCHES(sequence(SEPARATOR_ROUNDBRACKETCLOSE)))
|
if (!MATCHES(sequence(SEPARATOR_ROUNDBRACKETCLOSE)))
|
||||||
throwParseError("expected closing ')' after parameter list");
|
throwParseError("expected closing ')' after parameter list");
|
||||||
}
|
|
||||||
|
|
||||||
if (!MATCHES(sequence(SEPARATOR_CURLYBRACKETOPEN)))
|
if (!MATCHES(sequence(SEPARATOR_CURLYBRACKETOPEN)))
|
||||||
throwParseError("expected opening '{' after function definition");
|
throwParseError("expected opening '{' after function definition");
|
||||||
@ -473,7 +479,7 @@ namespace hex::pl {
|
|||||||
}
|
}
|
||||||
|
|
||||||
bodyCleanup.release();
|
bodyCleanup.release();
|
||||||
return create(new ASTNodeFunctionDefinition(getNamespacePrefixedName(functionName), params, body));
|
return create(new ASTNodeFunctionDefinition(getNamespacePrefixedName(functionName), params, body, parameterPack));
|
||||||
}
|
}
|
||||||
|
|
||||||
ASTNode *Parser::parseFunctionVariableDecl() {
|
ASTNode *Parser::parseFunctionVariableDecl() {
|
||||||
|
Loading…
Reference in New Issue
Block a user