pattern matcher: Implemented backend
changed comments
This commit is contained in:
parent
d445f0639f
commit
e781a7e07e
4 changed files with 375 additions and 289 deletions
|
@ -980,7 +980,7 @@ int GDCompiler::_parse_expression(CodeGen& codegen,const GDParser::Node *p_expre
|
||||||
} break;
|
} break;
|
||||||
//TYPE_TYPE,
|
//TYPE_TYPE,
|
||||||
default: {
|
default: {
|
||||||
|
|
||||||
ERR_EXPLAIN("Bug in bytecode compiler, unexpected node in parse tree while parsing expression.");
|
ERR_EXPLAIN("Bug in bytecode compiler, unexpected node in parse tree while parsing expression.");
|
||||||
ERR_FAIL_V(-1); //unreachable code
|
ERR_FAIL_V(-1); //unreachable code
|
||||||
} break;
|
} break;
|
||||||
|
@ -1020,9 +1020,68 @@ Error GDCompiler::_parse_block(CodeGen& codegen,const GDParser::BlockNode *p_blo
|
||||||
switch(cf->cf_type) {
|
switch(cf->cf_type) {
|
||||||
|
|
||||||
case GDParser::ControlFlowNode::CF_MATCH: {
|
case GDParser::ControlFlowNode::CF_MATCH: {
|
||||||
Error err = _parse_block(codegen,cf->match->compiled_block,p_stack_level,p_break_addr,p_continue_addr);
|
GDParser::MatchNode *match = cf->match;
|
||||||
if (err)
|
|
||||||
return err;
|
GDParser::IdentifierNode *id = memnew(GDParser::IdentifierNode);
|
||||||
|
id->name = "#match_value";
|
||||||
|
|
||||||
|
// var #match_value
|
||||||
|
// copied because there is no _parse_statement :(
|
||||||
|
codegen.add_stack_identifier(id->name, p_stack_level++);
|
||||||
|
codegen.alloc_stack(p_stack_level);
|
||||||
|
new_identifiers++;
|
||||||
|
|
||||||
|
GDParser::OperatorNode *op = memnew(GDParser::OperatorNode);
|
||||||
|
op->op=GDParser::OperatorNode::OP_ASSIGN;
|
||||||
|
op->arguments.push_back(id);
|
||||||
|
op->arguments.push_back(match->val_to_match);
|
||||||
|
|
||||||
|
int ret = _parse_expression(codegen, op, p_stack_level);
|
||||||
|
if (ret < 0) {
|
||||||
|
return ERR_PARSE_ERROR;
|
||||||
|
}
|
||||||
|
|
||||||
|
// break address
|
||||||
|
codegen.opcodes.push_back(GDFunction::OPCODE_JUMP);
|
||||||
|
codegen.opcodes.push_back(codegen.opcodes.size() + 3);
|
||||||
|
int break_addr = codegen.opcodes.size();
|
||||||
|
codegen.opcodes.push_back(GDFunction::OPCODE_JUMP);
|
||||||
|
codegen.opcodes.push_back(0); // break addr
|
||||||
|
|
||||||
|
for (int j = 0; j < match->compiled_pattern_branches.size(); j++) {
|
||||||
|
GDParser::MatchNode::CompiledPatternBranch branch = match->compiled_pattern_branches[j];
|
||||||
|
|
||||||
|
// jump over continue
|
||||||
|
// jump unconditionally
|
||||||
|
// continue address
|
||||||
|
// compile the condition
|
||||||
|
int ret = _parse_expression(codegen, branch.compiled_pattern, p_stack_level);
|
||||||
|
if (ret < 0) {
|
||||||
|
return ERR_PARSE_ERROR;
|
||||||
|
}
|
||||||
|
|
||||||
|
codegen.opcodes.push_back(GDFunction::OPCODE_JUMP_IF);
|
||||||
|
codegen.opcodes.push_back(ret);
|
||||||
|
codegen.opcodes.push_back(codegen.opcodes.size() + 3);
|
||||||
|
int continue_addr = codegen.opcodes.size();
|
||||||
|
codegen.opcodes.push_back(GDFunction::OPCODE_JUMP);
|
||||||
|
codegen.opcodes.push_back(0);
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
Error err = _parse_block(codegen, branch.body, p_stack_level, p_break_addr, continue_addr);
|
||||||
|
if (err) {
|
||||||
|
return ERR_PARSE_ERROR;
|
||||||
|
}
|
||||||
|
|
||||||
|
codegen.opcodes.push_back(GDFunction::OPCODE_JUMP);
|
||||||
|
codegen.opcodes.push_back(break_addr);
|
||||||
|
|
||||||
|
codegen.opcodes[continue_addr + 1] = codegen.opcodes.size();
|
||||||
|
}
|
||||||
|
|
||||||
|
codegen.opcodes[break_addr + 1] = codegen.opcodes.size();
|
||||||
|
|
||||||
|
|
||||||
} break;
|
} break;
|
||||||
|
|
||||||
|
|
|
@ -1581,7 +1581,7 @@ GDParser::PatternNode *GDParser::_parse_pattern(bool p_static)
|
||||||
}
|
}
|
||||||
|
|
||||||
switch (token) {
|
switch (token) {
|
||||||
// all the constants like strings and numbers
|
// dictionary
|
||||||
case GDTokenizer::TK_BRACKET_OPEN: {
|
case GDTokenizer::TK_BRACKET_OPEN: {
|
||||||
tokenizer->advance();
|
tokenizer->advance();
|
||||||
pattern->pt_type = GDParser::PatternNode::PT_ARRAY;
|
pattern->pt_type = GDParser::PatternNode::PT_ARRAY;
|
||||||
|
@ -1629,14 +1629,14 @@ GDParser::PatternNode *GDParser::_parse_pattern(bool p_static)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
} break;
|
} break;
|
||||||
|
// bind
|
||||||
case GDTokenizer::TK_PR_VAR: {
|
case GDTokenizer::TK_PR_VAR: {
|
||||||
tokenizer->advance();
|
tokenizer->advance();
|
||||||
pattern->pt_type = GDParser::PatternNode::PT_BIND;
|
pattern->pt_type = GDParser::PatternNode::PT_BIND;
|
||||||
pattern->bind = tokenizer->get_token_identifier();
|
pattern->bind = tokenizer->get_token_identifier();
|
||||||
tokenizer->advance();
|
tokenizer->advance();
|
||||||
} break;
|
} break;
|
||||||
|
// array
|
||||||
case GDTokenizer::TK_CURLY_BRACKET_OPEN: {
|
case GDTokenizer::TK_CURLY_BRACKET_OPEN: {
|
||||||
tokenizer->advance();
|
tokenizer->advance();
|
||||||
pattern->pt_type = GDParser::PatternNode::PT_DICTIONARY;
|
pattern->pt_type = GDParser::PatternNode::PT_DICTIONARY;
|
||||||
|
@ -1703,7 +1703,7 @@ GDParser::PatternNode *GDParser::_parse_pattern(bool p_static)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
} break;
|
} break;
|
||||||
|
// all the constants like strings and numbers
|
||||||
default: {
|
default: {
|
||||||
Node *value = _parse_and_reduce_expression(pattern, p_static);
|
Node *value = _parse_and_reduce_expression(pattern, p_static);
|
||||||
if (error_set) {
|
if (error_set) {
|
||||||
|
@ -1714,6 +1714,12 @@ GDParser::PatternNode *GDParser::_parse_pattern(bool p_static)
|
||||||
pattern->pt_type = PatternNode::PT_WILDCARD;
|
pattern->pt_type = PatternNode::PT_WILDCARD;
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (value->type != Node::TYPE_IDENTIFIER && value->type != Node::TYPE_CONSTANT) {
|
||||||
|
_set_error("Only constant expressions or variables allowed in a pattern");
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
|
||||||
pattern->pt_type = PatternNode::PT_CONSTANT;
|
pattern->pt_type = PatternNode::PT_CONSTANT;
|
||||||
pattern->constant = value;
|
pattern->constant = value;
|
||||||
} break;
|
} break;
|
||||||
|
@ -1744,11 +1750,19 @@ void GDParser::_parse_pattern_block(BlockNode *p_block, Vector<PatternBranchNode
|
||||||
|
|
||||||
PatternBranchNode *branch = alloc_node<PatternBranchNode>();
|
PatternBranchNode *branch = alloc_node<PatternBranchNode>();
|
||||||
|
|
||||||
branch->pattern = _parse_pattern(p_static);
|
branch->patterns.push_back(_parse_pattern(p_static));
|
||||||
if (!branch->pattern) {
|
if (!branch->patterns[0]) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
while (tokenizer->get_token() == GDTokenizer::TK_COMMA) {
|
||||||
|
tokenizer->advance();
|
||||||
|
branch->patterns.push_back(_parse_pattern(p_static));
|
||||||
|
if (!branch->patterns[branch->patterns.size() - 1]) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
if(!_enter_indent_block()) {
|
if(!_enter_indent_block()) {
|
||||||
_set_error("Expected block in pattern branch");
|
_set_error("Expected block in pattern branch");
|
||||||
return;
|
return;
|
||||||
|
@ -1767,264 +1781,246 @@ void GDParser::_parse_pattern_block(BlockNode *p_block, Vector<PatternBranchNode
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void GDParser::_generate_array_pattern(PatternNode *p_array_pattern, Node *p_value_to_match, Node *&p_resulting_node, Map<StringName, Node*> &p_bindings)
|
|
||||||
{
|
|
||||||
bool open_ended = false;
|
|
||||||
|
|
||||||
if (p_array_pattern->array.size() > 0) {
|
|
||||||
if (p_array_pattern->array[p_array_pattern->array.size() - 1]->pt_type == PatternNode::PT_IGNORE_REST) {
|
|
||||||
open_ended = true;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// check length
|
|
||||||
|
|
||||||
// typeof(value_to_match) == TYPE_ARRAY && value_to_match.size() >= length
|
|
||||||
// typeof(value_to_match) == TYPE_ARRAY && value_to_match.size() == length
|
|
||||||
|
|
||||||
{
|
|
||||||
|
|
||||||
// typecheck
|
|
||||||
BuiltInFunctionNode *typeof_node = alloc_node<BuiltInFunctionNode>();
|
|
||||||
typeof_node->function = GDFunctions::TYPE_OF;
|
|
||||||
|
|
||||||
OperatorNode *typeof_match_value = alloc_node<OperatorNode>();
|
|
||||||
typeof_match_value->op = OperatorNode::OP_CALL;
|
|
||||||
typeof_match_value->arguments.push_back(typeof_node);
|
|
||||||
typeof_match_value->arguments.push_back(p_value_to_match);
|
|
||||||
|
|
||||||
IdentifierNode *typeof_array = alloc_node<IdentifierNode>();
|
|
||||||
typeof_array->name = "TYPE_ARRAY";
|
|
||||||
|
|
||||||
OperatorNode *type_comp = alloc_node<OperatorNode>();
|
|
||||||
type_comp->op = OperatorNode::OP_EQUAL;
|
|
||||||
type_comp->arguments.push_back(typeof_match_value);
|
|
||||||
type_comp->arguments.push_back(typeof_array);
|
|
||||||
|
|
||||||
|
|
||||||
ConstantNode *length = alloc_node<ConstantNode>();
|
|
||||||
length->value = Variant(open_ended ? p_array_pattern->array.size() - 1 : p_array_pattern->array.size());
|
|
||||||
|
|
||||||
|
|
||||||
OperatorNode *call = alloc_node<OperatorNode>();
|
|
||||||
call->op = OperatorNode::OP_CALL;
|
|
||||||
call->arguments.push_back(p_value_to_match);
|
|
||||||
|
|
||||||
IdentifierNode *size = alloc_node<IdentifierNode>();
|
|
||||||
size->name = "size";
|
|
||||||
call->arguments.push_back(size);
|
|
||||||
|
|
||||||
OperatorNode *length_comparison = alloc_node<OperatorNode>();
|
|
||||||
length_comparison->op = open_ended ? OperatorNode::OP_GREATER_EQUAL : OperatorNode::OP_EQUAL;
|
|
||||||
length_comparison->arguments.push_back(call);
|
|
||||||
length_comparison->arguments.push_back(length);
|
|
||||||
|
|
||||||
OperatorNode *type_and_length_comparison = alloc_node<OperatorNode>();
|
|
||||||
type_and_length_comparison->op = OperatorNode::OP_AND;
|
|
||||||
type_and_length_comparison->arguments.push_back(type_comp);
|
|
||||||
type_and_length_comparison->arguments.push_back(length_comparison);
|
|
||||||
|
|
||||||
p_resulting_node = type_and_length_comparison;
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
for (int i = 0; i < p_array_pattern->array.size(); i++) {
|
|
||||||
PatternNode *pattern = p_array_pattern->array[i];
|
|
||||||
|
|
||||||
Node *condition = NULL;
|
|
||||||
|
|
||||||
ConstantNode *index = alloc_node<ConstantNode>();
|
|
||||||
index->value = Variant(i);
|
|
||||||
|
|
||||||
OperatorNode *indexed_value = alloc_node<OperatorNode>();
|
|
||||||
indexed_value->op = OperatorNode::OP_INDEX;
|
|
||||||
indexed_value->arguments.push_back(p_value_to_match);
|
|
||||||
indexed_value->arguments.push_back(index);
|
|
||||||
|
|
||||||
_generate_pattern(pattern, indexed_value, condition, p_bindings);
|
|
||||||
|
|
||||||
OperatorNode *and_node = alloc_node<OperatorNode>();
|
|
||||||
and_node->op = OperatorNode::OP_AND;
|
|
||||||
and_node->arguments.push_back(p_resulting_node);
|
|
||||||
and_node->arguments.push_back(condition);
|
|
||||||
|
|
||||||
p_resulting_node = and_node;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
void GDParser::_generate_bind_pattern(PatternNode *p_bind_pattern, Node *p_value_to_match, Map<StringName, Node*> &p_bindings)
|
|
||||||
{
|
|
||||||
p_bindings[p_bind_pattern->bind] = p_value_to_match;
|
|
||||||
}
|
|
||||||
|
|
||||||
void GDParser::_generate_constant_pattern(PatternNode *p_constant_pattern, Node *p_value_to_match, Node *&p_resulting_node)
|
|
||||||
{
|
|
||||||
BuiltInFunctionNode *typeof_node = alloc_node<BuiltInFunctionNode>();
|
|
||||||
typeof_node->function = GDFunctions::TYPE_OF;
|
|
||||||
|
|
||||||
OperatorNode *typeof_match_value = alloc_node<OperatorNode>();
|
|
||||||
typeof_match_value->op = OperatorNode::OP_CALL;
|
|
||||||
typeof_match_value->arguments.push_back(typeof_node);
|
|
||||||
typeof_match_value->arguments.push_back(p_value_to_match);
|
|
||||||
|
|
||||||
OperatorNode *typeof_pattern_value = alloc_node<OperatorNode>();
|
|
||||||
typeof_pattern_value->op = OperatorNode::OP_CALL;
|
|
||||||
typeof_pattern_value->arguments.push_back(typeof_node);
|
|
||||||
typeof_pattern_value->arguments.push_back(p_constant_pattern->constant);
|
|
||||||
|
|
||||||
OperatorNode *type_comp = alloc_node<OperatorNode>();
|
|
||||||
type_comp->op = OperatorNode::OP_EQUAL;
|
|
||||||
type_comp->arguments.push_back(typeof_match_value);
|
|
||||||
type_comp->arguments.push_back(typeof_pattern_value);
|
|
||||||
|
|
||||||
OperatorNode *value_comp = alloc_node<OperatorNode>();
|
|
||||||
value_comp->op = OperatorNode::OP_EQUAL;
|
|
||||||
value_comp->arguments.push_back(p_constant_pattern->constant);
|
|
||||||
value_comp->arguments.push_back(p_value_to_match);
|
|
||||||
|
|
||||||
|
|
||||||
OperatorNode *comparison = alloc_node<OperatorNode>();
|
|
||||||
comparison->op = OperatorNode::OP_AND;
|
|
||||||
comparison->arguments.push_back(type_comp);
|
|
||||||
comparison->arguments.push_back(value_comp);
|
|
||||||
|
|
||||||
p_resulting_node = comparison;
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
void GDParser::_generate_dict_pattern(PatternNode *p_dict_pattern, Node *p_value_to_match, Node *&p_resulting_node, Map<StringName, Node*> &p_bindings)
|
|
||||||
{
|
|
||||||
bool open_ended = false;
|
|
||||||
|
|
||||||
if (p_dict_pattern->array.size() > 0) {
|
|
||||||
open_ended = true;
|
|
||||||
print_line("open dictionary");
|
|
||||||
}
|
|
||||||
|
|
||||||
// check length
|
|
||||||
|
|
||||||
// typeof(value_to_match) == TYPE_DICTIONARY && value_to_match.size() >= length
|
|
||||||
// typeof(value_to_match) == TYPE_DICTIONARY && value_to_match.size() == length
|
|
||||||
|
|
||||||
|
|
||||||
{
|
|
||||||
|
|
||||||
// typecheck
|
|
||||||
BuiltInFunctionNode *typeof_node = alloc_node<BuiltInFunctionNode>();
|
|
||||||
typeof_node->function = GDFunctions::TYPE_OF;
|
|
||||||
|
|
||||||
OperatorNode *typeof_match_value = alloc_node<OperatorNode>();
|
|
||||||
typeof_match_value->op = OperatorNode::OP_CALL;
|
|
||||||
typeof_match_value->arguments.push_back(typeof_node);
|
|
||||||
typeof_match_value->arguments.push_back(p_value_to_match);
|
|
||||||
|
|
||||||
IdentifierNode *typeof_dictionary = alloc_node<IdentifierNode>();
|
|
||||||
typeof_dictionary->name = "TYPE_DICTIONARY";
|
|
||||||
|
|
||||||
OperatorNode *type_comp = alloc_node<OperatorNode>();
|
|
||||||
type_comp->op = OperatorNode::OP_EQUAL;
|
|
||||||
type_comp->arguments.push_back(typeof_match_value);
|
|
||||||
type_comp->arguments.push_back(typeof_dictionary);
|
|
||||||
|
|
||||||
|
|
||||||
ConstantNode *length = alloc_node<ConstantNode>();
|
|
||||||
length->value = Variant(open_ended ? p_dict_pattern->dictionary.size() - 1 : p_dict_pattern->dictionary.size());
|
|
||||||
|
|
||||||
|
|
||||||
OperatorNode *call = alloc_node<OperatorNode>();
|
|
||||||
call->op = OperatorNode::OP_CALL;
|
|
||||||
call->arguments.push_back(p_value_to_match);
|
|
||||||
|
|
||||||
IdentifierNode *size = alloc_node<IdentifierNode>();
|
|
||||||
size->name = "size";
|
|
||||||
call->arguments.push_back(size);
|
|
||||||
|
|
||||||
OperatorNode *length_comparison = alloc_node<OperatorNode>();
|
|
||||||
length_comparison->op = open_ended ? OperatorNode::OP_GREATER_EQUAL : OperatorNode::OP_EQUAL;
|
|
||||||
length_comparison->arguments.push_back(call);
|
|
||||||
length_comparison->arguments.push_back(length);
|
|
||||||
|
|
||||||
OperatorNode *type_and_length_comparison = alloc_node<OperatorNode>();
|
|
||||||
type_and_length_comparison->op = OperatorNode::OP_AND;
|
|
||||||
type_and_length_comparison->arguments.push_back(type_comp);
|
|
||||||
type_and_length_comparison->arguments.push_back(length_comparison);
|
|
||||||
|
|
||||||
p_resulting_node = type_and_length_comparison;
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
for (Map<ConstantNode*, PatternNode*>::Element *e = p_dict_pattern->dictionary.front(); e; e = e->next()) {
|
|
||||||
|
|
||||||
Node *condition = NULL;
|
|
||||||
|
|
||||||
// chech for has, then for pattern
|
|
||||||
|
|
||||||
IdentifierNode *has = alloc_node<IdentifierNode>();
|
|
||||||
has->name = "has";
|
|
||||||
|
|
||||||
OperatorNode *has_call = alloc_node<OperatorNode>();
|
|
||||||
has_call->op = OperatorNode::OP_CALL;
|
|
||||||
has_call->arguments.push_back(p_value_to_match);
|
|
||||||
has_call->arguments.push_back(has);
|
|
||||||
has_call->arguments.push_back(e->key());
|
|
||||||
|
|
||||||
|
|
||||||
if (e->value()) {
|
|
||||||
|
|
||||||
OperatorNode *indexed_value = alloc_node<OperatorNode>();
|
|
||||||
indexed_value->op = OperatorNode::OP_INDEX;
|
|
||||||
indexed_value->arguments.push_back(p_value_to_match);
|
|
||||||
indexed_value->arguments.push_back(e->key());
|
|
||||||
|
|
||||||
_generate_pattern(e->value(), indexed_value, condition, p_bindings);
|
|
||||||
|
|
||||||
OperatorNode *has_and_pattern = alloc_node<OperatorNode>();
|
|
||||||
has_and_pattern->op = OperatorNode::OP_AND;
|
|
||||||
has_and_pattern->arguments.push_back(has_call);
|
|
||||||
has_and_pattern->arguments.push_back(condition);
|
|
||||||
|
|
||||||
condition = has_and_pattern;
|
|
||||||
|
|
||||||
} else {
|
|
||||||
condition = has_call;
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
OperatorNode *and_node = alloc_node<OperatorNode>();
|
|
||||||
and_node->op = OperatorNode::OP_AND;
|
|
||||||
and_node->arguments.push_back(p_resulting_node);
|
|
||||||
and_node->arguments.push_back(condition);
|
|
||||||
|
|
||||||
p_resulting_node = and_node;
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
void GDParser::_generate_pattern(PatternNode *p_pattern, Node *p_node_to_match, Node *&p_resulting_node, Map<StringName, Node*> &p_bindings)
|
void GDParser::_generate_pattern(PatternNode *p_pattern, Node *p_node_to_match, Node *&p_resulting_node, Map<StringName, Node*> &p_bindings)
|
||||||
{
|
{
|
||||||
switch (p_pattern->pt_type) {
|
switch (p_pattern->pt_type) {
|
||||||
case PatternNode::PT_CONSTANT: {
|
case PatternNode::PT_CONSTANT: {
|
||||||
_generate_constant_pattern(p_pattern, p_node_to_match, p_resulting_node);
|
|
||||||
|
// typecheck
|
||||||
|
BuiltInFunctionNode *typeof_node = alloc_node<BuiltInFunctionNode>();
|
||||||
|
typeof_node->function = GDFunctions::TYPE_OF;
|
||||||
|
|
||||||
|
OperatorNode *typeof_match_value = alloc_node<OperatorNode>();
|
||||||
|
typeof_match_value->op = OperatorNode::OP_CALL;
|
||||||
|
typeof_match_value->arguments.push_back(typeof_node);
|
||||||
|
typeof_match_value->arguments.push_back(p_node_to_match);
|
||||||
|
|
||||||
|
OperatorNode *typeof_pattern_value = alloc_node<OperatorNode>();
|
||||||
|
typeof_pattern_value->op = OperatorNode::OP_CALL;
|
||||||
|
typeof_pattern_value->arguments.push_back(typeof_node);
|
||||||
|
typeof_pattern_value->arguments.push_back(p_pattern->constant);
|
||||||
|
|
||||||
|
OperatorNode *type_comp = alloc_node<OperatorNode>();
|
||||||
|
type_comp->op = OperatorNode::OP_EQUAL;
|
||||||
|
type_comp->arguments.push_back(typeof_match_value);
|
||||||
|
type_comp->arguments.push_back(typeof_pattern_value);
|
||||||
|
|
||||||
|
|
||||||
|
// comare the actual values
|
||||||
|
OperatorNode *value_comp = alloc_node<OperatorNode>();
|
||||||
|
value_comp->op = OperatorNode::OP_EQUAL;
|
||||||
|
value_comp->arguments.push_back(p_pattern->constant);
|
||||||
|
value_comp->arguments.push_back(p_node_to_match);
|
||||||
|
|
||||||
|
|
||||||
|
OperatorNode *comparison = alloc_node<OperatorNode>();
|
||||||
|
comparison->op = OperatorNode::OP_AND;
|
||||||
|
comparison->arguments.push_back(type_comp);
|
||||||
|
comparison->arguments.push_back(value_comp);
|
||||||
|
|
||||||
|
p_resulting_node = comparison;
|
||||||
|
|
||||||
} break;
|
} break;
|
||||||
case PatternNode::PT_BIND: {
|
case PatternNode::PT_BIND: {
|
||||||
_generate_bind_pattern(p_pattern, p_node_to_match, p_bindings);
|
p_bindings[p_pattern->bind] = p_node_to_match;
|
||||||
|
|
||||||
|
// a bind always matches
|
||||||
ConstantNode *true_value = alloc_node<ConstantNode>();
|
ConstantNode *true_value = alloc_node<ConstantNode>();
|
||||||
true_value->value = Variant(true);
|
true_value->value = Variant(true);
|
||||||
p_resulting_node = true_value;
|
p_resulting_node = true_value;
|
||||||
} break;
|
} break;
|
||||||
case PatternNode::PT_ARRAY: {
|
case PatternNode::PT_ARRAY: {
|
||||||
_generate_array_pattern(p_pattern, p_node_to_match, p_resulting_node, p_bindings);
|
|
||||||
|
bool open_ended = false;
|
||||||
|
|
||||||
|
if (p_pattern->array.size() > 0) {
|
||||||
|
if (p_pattern->array[p_pattern->array.size() - 1]->pt_type == PatternNode::PT_IGNORE_REST) {
|
||||||
|
open_ended = true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// typeof(value_to_match) == TYPE_ARRAY && value_to_match.size() >= length
|
||||||
|
// typeof(value_to_match) == TYPE_ARRAY && value_to_match.size() == length
|
||||||
|
|
||||||
|
{
|
||||||
|
// typecheck
|
||||||
|
BuiltInFunctionNode *typeof_node = alloc_node<BuiltInFunctionNode>();
|
||||||
|
typeof_node->function = GDFunctions::TYPE_OF;
|
||||||
|
|
||||||
|
OperatorNode *typeof_match_value = alloc_node<OperatorNode>();
|
||||||
|
typeof_match_value->op = OperatorNode::OP_CALL;
|
||||||
|
typeof_match_value->arguments.push_back(typeof_node);
|
||||||
|
typeof_match_value->arguments.push_back(p_node_to_match);
|
||||||
|
|
||||||
|
IdentifierNode *typeof_array = alloc_node<IdentifierNode>();
|
||||||
|
typeof_array->name = "TYPE_ARRAY";
|
||||||
|
|
||||||
|
OperatorNode *type_comp = alloc_node<OperatorNode>();
|
||||||
|
type_comp->op = OperatorNode::OP_EQUAL;
|
||||||
|
type_comp->arguments.push_back(typeof_match_value);
|
||||||
|
type_comp->arguments.push_back(typeof_array);
|
||||||
|
|
||||||
|
|
||||||
|
// size
|
||||||
|
ConstantNode *length = alloc_node<ConstantNode>();
|
||||||
|
length->value = Variant(open_ended ? p_pattern->array.size() - 1 : p_pattern->array.size());
|
||||||
|
|
||||||
|
OperatorNode *call = alloc_node<OperatorNode>();
|
||||||
|
call->op = OperatorNode::OP_CALL;
|
||||||
|
call->arguments.push_back(p_node_to_match);
|
||||||
|
|
||||||
|
IdentifierNode *size = alloc_node<IdentifierNode>();
|
||||||
|
size->name = "size";
|
||||||
|
call->arguments.push_back(size);
|
||||||
|
|
||||||
|
OperatorNode *length_comparison = alloc_node<OperatorNode>();
|
||||||
|
length_comparison->op = open_ended ? OperatorNode::OP_GREATER_EQUAL : OperatorNode::OP_EQUAL;
|
||||||
|
length_comparison->arguments.push_back(call);
|
||||||
|
length_comparison->arguments.push_back(length);
|
||||||
|
|
||||||
|
OperatorNode *type_and_length_comparison = alloc_node<OperatorNode>();
|
||||||
|
type_and_length_comparison->op = OperatorNode::OP_AND;
|
||||||
|
type_and_length_comparison->arguments.push_back(type_comp);
|
||||||
|
type_and_length_comparison->arguments.push_back(length_comparison);
|
||||||
|
|
||||||
|
p_resulting_node = type_and_length_comparison;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
for (int i = 0; i < p_pattern->array.size(); i++) {
|
||||||
|
PatternNode *pattern = p_pattern->array[i];
|
||||||
|
|
||||||
|
Node *condition = NULL;
|
||||||
|
|
||||||
|
ConstantNode *index = alloc_node<ConstantNode>();
|
||||||
|
index->value = Variant(i);
|
||||||
|
|
||||||
|
OperatorNode *indexed_value = alloc_node<OperatorNode>();
|
||||||
|
indexed_value->op = OperatorNode::OP_INDEX;
|
||||||
|
indexed_value->arguments.push_back(p_node_to_match);
|
||||||
|
indexed_value->arguments.push_back(index);
|
||||||
|
|
||||||
|
_generate_pattern(pattern, indexed_value, condition, p_bindings);
|
||||||
|
|
||||||
|
// concatenate all the patterns with &&
|
||||||
|
OperatorNode *and_node = alloc_node<OperatorNode>();
|
||||||
|
and_node->op = OperatorNode::OP_AND;
|
||||||
|
and_node->arguments.push_back(p_resulting_node);
|
||||||
|
and_node->arguments.push_back(condition);
|
||||||
|
|
||||||
|
p_resulting_node = and_node;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
} break;
|
} break;
|
||||||
case PatternNode::PT_DICTIONARY: {
|
case PatternNode::PT_DICTIONARY: {
|
||||||
_generate_dict_pattern(p_pattern, p_node_to_match, p_resulting_node, p_bindings);
|
|
||||||
|
bool open_ended = false;
|
||||||
|
|
||||||
|
if (p_pattern->array.size() > 0) {
|
||||||
|
open_ended = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
// typeof(value_to_match) == TYPE_DICTIONARY && value_to_match.size() >= length
|
||||||
|
// typeof(value_to_match) == TYPE_DICTIONARY && value_to_match.size() == length
|
||||||
|
|
||||||
|
|
||||||
|
{
|
||||||
|
// typecheck
|
||||||
|
BuiltInFunctionNode *typeof_node = alloc_node<BuiltInFunctionNode>();
|
||||||
|
typeof_node->function = GDFunctions::TYPE_OF;
|
||||||
|
|
||||||
|
OperatorNode *typeof_match_value = alloc_node<OperatorNode>();
|
||||||
|
typeof_match_value->op = OperatorNode::OP_CALL;
|
||||||
|
typeof_match_value->arguments.push_back(typeof_node);
|
||||||
|
typeof_match_value->arguments.push_back(p_node_to_match);
|
||||||
|
|
||||||
|
IdentifierNode *typeof_dictionary = alloc_node<IdentifierNode>();
|
||||||
|
typeof_dictionary->name = "TYPE_DICTIONARY";
|
||||||
|
|
||||||
|
OperatorNode *type_comp = alloc_node<OperatorNode>();
|
||||||
|
type_comp->op = OperatorNode::OP_EQUAL;
|
||||||
|
type_comp->arguments.push_back(typeof_match_value);
|
||||||
|
type_comp->arguments.push_back(typeof_dictionary);
|
||||||
|
|
||||||
|
// size
|
||||||
|
ConstantNode *length = alloc_node<ConstantNode>();
|
||||||
|
length->value = Variant(open_ended ? p_pattern->dictionary.size() - 1 : p_pattern->dictionary.size());
|
||||||
|
|
||||||
|
OperatorNode *call = alloc_node<OperatorNode>();
|
||||||
|
call->op = OperatorNode::OP_CALL;
|
||||||
|
call->arguments.push_back(p_node_to_match);
|
||||||
|
|
||||||
|
IdentifierNode *size = alloc_node<IdentifierNode>();
|
||||||
|
size->name = "size";
|
||||||
|
call->arguments.push_back(size);
|
||||||
|
|
||||||
|
OperatorNode *length_comparison = alloc_node<OperatorNode>();
|
||||||
|
length_comparison->op = open_ended ? OperatorNode::OP_GREATER_EQUAL : OperatorNode::OP_EQUAL;
|
||||||
|
length_comparison->arguments.push_back(call);
|
||||||
|
length_comparison->arguments.push_back(length);
|
||||||
|
|
||||||
|
OperatorNode *type_and_length_comparison = alloc_node<OperatorNode>();
|
||||||
|
type_and_length_comparison->op = OperatorNode::OP_AND;
|
||||||
|
type_and_length_comparison->arguments.push_back(type_comp);
|
||||||
|
type_and_length_comparison->arguments.push_back(length_comparison);
|
||||||
|
|
||||||
|
p_resulting_node = type_and_length_comparison;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
for (Map<ConstantNode*, PatternNode*>::Element *e = p_pattern->dictionary.front(); e; e = e->next()) {
|
||||||
|
|
||||||
|
Node *condition = NULL;
|
||||||
|
|
||||||
|
// chech for has, then for pattern
|
||||||
|
|
||||||
|
IdentifierNode *has = alloc_node<IdentifierNode>();
|
||||||
|
has->name = "has";
|
||||||
|
|
||||||
|
OperatorNode *has_call = alloc_node<OperatorNode>();
|
||||||
|
has_call->op = OperatorNode::OP_CALL;
|
||||||
|
has_call->arguments.push_back(p_node_to_match);
|
||||||
|
has_call->arguments.push_back(has);
|
||||||
|
has_call->arguments.push_back(e->key());
|
||||||
|
|
||||||
|
|
||||||
|
if (e->value()) {
|
||||||
|
|
||||||
|
OperatorNode *indexed_value = alloc_node<OperatorNode>();
|
||||||
|
indexed_value->op = OperatorNode::OP_INDEX;
|
||||||
|
indexed_value->arguments.push_back(p_node_to_match);
|
||||||
|
indexed_value->arguments.push_back(e->key());
|
||||||
|
|
||||||
|
_generate_pattern(e->value(), indexed_value, condition, p_bindings);
|
||||||
|
|
||||||
|
OperatorNode *has_and_pattern = alloc_node<OperatorNode>();
|
||||||
|
has_and_pattern->op = OperatorNode::OP_AND;
|
||||||
|
has_and_pattern->arguments.push_back(has_call);
|
||||||
|
has_and_pattern->arguments.push_back(condition);
|
||||||
|
|
||||||
|
condition = has_and_pattern;
|
||||||
|
|
||||||
|
} else {
|
||||||
|
condition = has_call;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
// concatenate all the patterns with &&
|
||||||
|
OperatorNode *and_node = alloc_node<OperatorNode>();
|
||||||
|
and_node->op = OperatorNode::OP_AND;
|
||||||
|
and_node->arguments.push_back(p_resulting_node);
|
||||||
|
and_node->arguments.push_back(condition);
|
||||||
|
|
||||||
|
p_resulting_node = and_node;
|
||||||
|
}
|
||||||
|
|
||||||
} break;
|
} break;
|
||||||
|
case PatternNode::PT_IGNORE_REST:
|
||||||
case PatternNode::PT_WILDCARD: {
|
case PatternNode::PT_WILDCARD: {
|
||||||
// simply generate a `true`
|
// simply generate a `true`
|
||||||
ConstantNode *true_value = alloc_node<ConstantNode>();
|
ConstantNode *true_value = alloc_node<ConstantNode>();
|
||||||
|
@ -2039,37 +2035,70 @@ void GDParser::_generate_pattern(PatternNode *p_pattern, Node *p_node_to_match,
|
||||||
|
|
||||||
void GDParser::_transform_match_statment(BlockNode *p_block, MatchNode *p_match_statement)
|
void GDParser::_transform_match_statment(BlockNode *p_block, MatchNode *p_match_statement)
|
||||||
{
|
{
|
||||||
LocalVarNode *val_to_match = alloc_node<LocalVarNode>();
|
|
||||||
val_to_match->name = "#match_value"; // use a name that can't be referenced in GDscript
|
|
||||||
val_to_match->assign = p_match_statement->val_to_match;
|
|
||||||
|
|
||||||
p_block->statements.push_back(val_to_match);
|
|
||||||
|
|
||||||
|
|
||||||
IdentifierNode *id = alloc_node<IdentifierNode>();
|
IdentifierNode *id = alloc_node<IdentifierNode>();
|
||||||
id->name=val_to_match->name;
|
id->name = "#match_value";
|
||||||
|
|
||||||
OperatorNode *op = alloc_node<OperatorNode>();
|
|
||||||
op->op=OperatorNode::OP_ASSIGN;
|
|
||||||
op->arguments.push_back(id);
|
|
||||||
op->arguments.push_back(val_to_match->assign);
|
|
||||||
p_block->statements.push_back(op);
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
for (int i = 0; i < p_match_statement->branches.size(); i++) {
|
for (int i = 0; i < p_match_statement->branches.size(); i++) {
|
||||||
|
|
||||||
PatternBranchNode *branch = p_match_statement->branches[i];
|
PatternBranchNode *branch = p_match_statement->branches[i];
|
||||||
Map<StringName, Node*> bindings;
|
|
||||||
Node *resulting_node;
|
|
||||||
_generate_pattern(branch->pattern, id, resulting_node, bindings);
|
|
||||||
|
|
||||||
// TEMP: if's for testing
|
MatchNode::CompiledPatternBranch compiled_branch;
|
||||||
ControlFlowNode *cf_if = alloc_node<ControlFlowNode>();
|
compiled_branch.compiled_pattern = NULL;
|
||||||
cf_if->cf_type = ControlFlowNode::CF_IF;
|
|
||||||
cf_if->arguments.push_back(resulting_node);
|
|
||||||
cf_if->body = branch->body;
|
|
||||||
|
|
||||||
p_block->statements.push_back(cf_if);
|
Map<StringName, Node*> binding;
|
||||||
|
|
||||||
|
for (int j = 0; j < branch->patterns.size(); j++) {
|
||||||
|
PatternNode *pattern = branch->patterns[j];
|
||||||
|
|
||||||
|
Map<StringName, Node*> bindings;
|
||||||
|
Node *resulting_node;
|
||||||
|
_generate_pattern(pattern, id, resulting_node, bindings);
|
||||||
|
|
||||||
|
if (!binding.empty() && !bindings.empty()) {
|
||||||
|
_set_error("Multipatterns can't contain bindings");
|
||||||
|
return;
|
||||||
|
} else {
|
||||||
|
binding = bindings;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (compiled_branch.compiled_pattern) {
|
||||||
|
OperatorNode *or_node = alloc_node<OperatorNode>();
|
||||||
|
or_node->op = OperatorNode::OP_OR;
|
||||||
|
or_node->arguments.push_back(compiled_branch.compiled_pattern);
|
||||||
|
or_node->arguments.push_back(resulting_node);
|
||||||
|
|
||||||
|
compiled_branch.compiled_pattern = or_node;
|
||||||
|
} else {
|
||||||
|
// single pattern | first one
|
||||||
|
compiled_branch.compiled_pattern = resulting_node;
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
// prepare the body ...hehe
|
||||||
|
for (Map<StringName, Node*>::Element *e = binding.front(); e; e = e->next()) {
|
||||||
|
LocalVarNode *local_var = alloc_node<LocalVarNode>();
|
||||||
|
local_var->name = e->key();
|
||||||
|
local_var->assign = e->value();
|
||||||
|
|
||||||
|
|
||||||
|
IdentifierNode *id = alloc_node<IdentifierNode>();
|
||||||
|
id->name = local_var->name;
|
||||||
|
|
||||||
|
OperatorNode *op = alloc_node<OperatorNode>();
|
||||||
|
op->op=OperatorNode::OP_ASSIGN;
|
||||||
|
op->arguments.push_back(id);
|
||||||
|
op->arguments.push_back(local_var->assign);
|
||||||
|
|
||||||
|
branch->body->statements.push_front(op);
|
||||||
|
branch->body->statements.push_front(local_var);
|
||||||
|
}
|
||||||
|
|
||||||
|
compiled_branch.body = branch->body;
|
||||||
|
|
||||||
|
|
||||||
|
p_match_statement->compiled_pattern_branches.push_back(compiled_branch);
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
@ -2509,8 +2538,6 @@ void GDParser::_parse_block(BlockNode *p_block,bool p_static) {
|
||||||
|
|
||||||
_transform_match_statment(compiled_branches, match_node);
|
_transform_match_statment(compiled_branches, match_node);
|
||||||
|
|
||||||
match_node->compiled_block = compiled_branches;
|
|
||||||
|
|
||||||
ControlFlowNode *match_cf_node = alloc_node<ControlFlowNode>();
|
ControlFlowNode *match_cf_node = alloc_node<ControlFlowNode>();
|
||||||
match_cf_node->cf_type = ControlFlowNode::CF_MATCH;
|
match_cf_node->cf_type = ControlFlowNode::CF_MATCH;
|
||||||
match_cf_node->match = match_node;
|
match_cf_node->match = match_node;
|
||||||
|
|
|
@ -280,7 +280,7 @@ public:
|
||||||
};
|
};
|
||||||
|
|
||||||
struct PatternBranchNode : public Node {
|
struct PatternBranchNode : public Node {
|
||||||
PatternNode *pattern;
|
Vector<PatternNode*> patterns;
|
||||||
BlockNode *body;
|
BlockNode *body;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@ -288,7 +288,12 @@ public:
|
||||||
Node *val_to_match;
|
Node *val_to_match;
|
||||||
Vector<PatternBranchNode*> branches;
|
Vector<PatternBranchNode*> branches;
|
||||||
|
|
||||||
BlockNode *compiled_block;
|
struct CompiledPatternBranch {
|
||||||
|
Node *compiled_pattern;
|
||||||
|
BlockNode *body;
|
||||||
|
};
|
||||||
|
|
||||||
|
Vector<CompiledPatternBranch> compiled_pattern_branches;
|
||||||
};
|
};
|
||||||
|
|
||||||
struct ControlFlowNode : public Node {
|
struct ControlFlowNode : public Node {
|
||||||
|
@ -487,19 +492,13 @@ private:
|
||||||
Node* _parse_and_reduce_expression(Node *p_parent,bool p_static,bool p_reduce_const=false,bool p_allow_assign=false);
|
Node* _parse_and_reduce_expression(Node *p_parent,bool p_static,bool p_reduce_const=false,bool p_allow_assign=false);
|
||||||
|
|
||||||
|
|
||||||
// TODO
|
|
||||||
void _parse_pattern_block(BlockNode *p_block, Vector<PatternBranchNode*> &p_branches, bool p_static);
|
|
||||||
|
|
||||||
PatternNode *_parse_pattern(bool p_static);
|
PatternNode *_parse_pattern(bool p_static);
|
||||||
|
void _parse_pattern_block(BlockNode *p_block, Vector<PatternBranchNode*> &p_branches, bool p_static);
|
||||||
void _transform_match_statment(BlockNode *p_block, MatchNode *p_match_statement);
|
void _transform_match_statment(BlockNode *p_block, MatchNode *p_match_statement);
|
||||||
|
|
||||||
void _generate_pattern(PatternNode *p_pattern, Node *p_node_to_match, Node *&p_resulting_node, Map<StringName, Node*> &p_bindings);
|
void _generate_pattern(PatternNode *p_pattern, Node *p_node_to_match, Node *&p_resulting_node, Map<StringName, Node*> &p_bindings);
|
||||||
|
|
||||||
void _generate_array_pattern(PatternNode *p_array_pattern, Node *p_value_to_match, Node *&p_resulting_node, Map<StringName, Node*> &p_bindings);
|
|
||||||
void _generate_bind_pattern(PatternNode *p_bind_pattern, Node *p_value_to_match, Map<StringName, Node*> &p_bindings);
|
|
||||||
void _generate_constant_pattern(PatternNode *p_constant_pattern, Node *p_value_to_match, Node *&p_resulting_node);
|
|
||||||
void _generate_dict_pattern(PatternNode *p_dict_pattern, Node *p_value_to_match, Node *&p_resulting_node, Map<StringName, Node*> &p_bindings);
|
|
||||||
|
|
||||||
|
|
||||||
void _parse_block(BlockNode *p_block,bool p_static);
|
void _parse_block(BlockNode *p_block,bool p_static);
|
||||||
void _parse_extends(ClassNode *p_class);
|
void _parse_extends(ClassNode *p_class);
|
||||||
|
|
|
@ -1894,6 +1894,7 @@ void GDScriptLanguage::get_reserved_words(List<String> *p_words) const {
|
||||||
"for",
|
"for",
|
||||||
"pass",
|
"pass",
|
||||||
"return",
|
"return",
|
||||||
|
"match",
|
||||||
"while",
|
"while",
|
||||||
"remote",
|
"remote",
|
||||||
"sync",
|
"sync",
|
||||||
|
|
Loading…
Reference in a new issue