1
0
mirror of synced 2024-11-12 10:10:53 +01:00

pattern: Added parameter packs

This commit is contained in:
WerWolv 2022-01-30 15:18:45 +01:00
parent 111c4b71aa
commit 69bd438fe1
7 changed files with 115 additions and 38 deletions

View File

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

View File

@ -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 &parameterPack = 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> &params) -> 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> &params) -> 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 {

View File

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

View File

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

View File

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

View File

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

View File

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