summaryrefslogtreecommitdiff
path: root/modules/gdscript/gd_parser.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'modules/gdscript/gd_parser.cpp')
-rw-r--r--modules/gdscript/gd_parser.cpp781
1 files changed, 662 insertions, 119 deletions
diff --git a/modules/gdscript/gd_parser.cpp b/modules/gdscript/gd_parser.cpp
index 40c262c503..c55bfee591 100644
--- a/modules/gdscript/gd_parser.cpp
+++ b/modules/gdscript/gd_parser.cpp
@@ -5,7 +5,7 @@
/* GODOT ENGINE */
/* http://www.godotengine.org */
/*************************************************************************/
-/* Copyright (c) 2007-2014 Juan Linietsky, Ariel Manzur. */
+/* Copyright (c) 2007-2015 Juan Linietsky, Ariel Manzur. */
/* */
/* Permission is hereby granted, free of charge, to any person obtaining */
/* a copy of this software and associated documentation files (the */
@@ -30,19 +30,7 @@
#include "print_string.h"
#include "io/resource_loader.h"
#include "os/file_access.h"
-/* TODO:
-
- *Property reduce constant expressions
- *Implement missing operators in variant?
- *constructor
- */
-
-/*
- todo:
- fix post ++,--
- make sure ++,-- don't work on constant expressions
- seems passing parent node as param is not needed
- */
+#include "script_language.h"
template<class T>
T* GDParser::alloc_node() {
@@ -77,8 +65,10 @@ bool GDParser::_enter_indent_block(BlockNode* p_block) {
if (tokenizer->get_token()!=GDTokenizer::TK_COLON) {
-
- _set_error("':' expected at end of line.");
+ // report location at the previous token (on the previous line)
+ int error_line = tokenizer->get_token_line(-1);
+ int error_column = tokenizer->get_token_column(-1);
+ _set_error("':' expected at end of line.",error_line,error_column);
return false;
}
tokenizer->advance();
@@ -116,14 +106,28 @@ bool GDParser::_enter_indent_block(BlockNode* p_block) {
}
}
-bool GDParser::_parse_arguments(Node* p_parent,Vector<Node*>& p_args,bool p_static) {
+bool GDParser::_parse_arguments(Node* p_parent,Vector<Node*>& p_args,bool p_static,bool p_can_codecomplete) {
if (tokenizer->get_token()==GDTokenizer::TK_PARENTHESIS_CLOSE) {
tokenizer->advance();
} else {
+ int argidx=0;
+
while(true) {
+ if (tokenizer->get_token()==GDTokenizer::TK_CURSOR) {
+ _make_completable_call(argidx);
+ completion_node=p_parent;
+ } else if (tokenizer->get_token()==GDTokenizer::TK_CONSTANT && tokenizer->get_token_constant().get_type()==Variant::STRING && tokenizer->get_token(1)==GDTokenizer::TK_CURSOR) {
+ //completing a string argument..
+ completion_cursor=tokenizer->get_token_constant();
+
+ _make_completable_call(argidx);
+ completion_node=p_parent;
+ tokenizer->advance(1);
+ return false;
+ }
Node*arg = _parse_expression(p_parent,p_static);
if (!arg)
@@ -144,6 +148,7 @@ bool GDParser::_parse_arguments(Node* p_parent,Vector<Node*>& p_args,bool p_stat
}
tokenizer->advance();
+ argidx++;
} else {
// something is broken
_set_error("Expected ',' or ')'");
@@ -158,6 +163,50 @@ bool GDParser::_parse_arguments(Node* p_parent,Vector<Node*>& p_args,bool p_stat
}
+void GDParser::_make_completable_call(int p_arg) {
+
+ completion_cursor=StringName();
+ completion_type=COMPLETION_CALL_ARGUMENTS;
+ completion_class=current_class;
+ completion_function=current_function;
+ completion_line=tokenizer->get_token_line();
+ completion_argument=p_arg;
+ completion_block=current_block;
+ completion_found=true;
+ tokenizer->advance();
+
+}
+
+
+bool GDParser::_get_completable_identifier(CompletionType p_type,StringName& identifier) {
+
+ identifier=StringName();
+ if (tokenizer->get_token()==GDTokenizer::TK_IDENTIFIER) {
+ identifier=tokenizer->get_token_identifier();
+ tokenizer->advance();
+ }
+ if (tokenizer->get_token()==GDTokenizer::TK_CURSOR) {
+
+ completion_cursor=identifier;
+ completion_type=p_type;
+ completion_class=current_class;
+ completion_function=current_function;
+ completion_line=tokenizer->get_token_line();
+ completion_block=current_block;
+ completion_found=true;
+ tokenizer->advance();
+
+ if (tokenizer->get_token()==GDTokenizer::TK_IDENTIFIER) {
+ identifier=identifier.operator String() + tokenizer->get_token_identifier().operator String();
+ tokenizer->advance();
+ }
+
+ return true;
+ }
+
+ return false;
+}
+
GDParser::Node* GDParser::_parse_expression(Node *p_parent,bool p_static,bool p_allow_assign) {
@@ -199,6 +248,9 @@ GDParser::Node* GDParser::_parse_expression(Node *p_parent,bool p_static,bool p_
tokenizer->advance();
expr=subexpr;
+ } else if (tokenizer->get_token()==GDTokenizer::TK_CURSOR) {
+ tokenizer->advance();
+ continue; //no point in cursor in the middle of expression
} else if (tokenizer->get_token()==GDTokenizer::TK_CONSTANT) {
@@ -225,13 +277,24 @@ GDParser::Node* GDParser::_parse_expression(Node *p_parent,bool p_static,bool p_
String path = tokenizer->get_token_constant();
if (!path.is_abs_path() && base_path!="")
path=base_path+"/"+path;
- path = path.replace("///","//");
+ path = path.replace("///","//").simplify_path();
+ if (path==self_path) {
+
+ _set_error("Can't preload itself (use 'get_script()').");
+ return NULL;
+
+ }
+
Ref<Resource> res;
if (!validating) {
//this can be too slow for just validating code
- res = ResourceLoader::load(path);
+ if (for_completion && ScriptCodeCompletionCache::get_sigleton()) {
+ res = ScriptCodeCompletionCache::get_sigleton()->get_cached_resource(path);
+ } else {
+ res = ResourceLoader::load(path);
+ }
if (!res.is_valid()) {
_set_error("Can't preload resource at path: "+path);
return NULL;
@@ -256,6 +319,55 @@ GDParser::Node* GDParser::_parse_expression(Node *p_parent,bool p_static,bool p_
tokenizer->advance();
expr=constant;
+ } else if (tokenizer->get_token()==GDTokenizer::TK_PR_YIELD) {
+
+ //constant defined by tokenizer
+
+ tokenizer->advance();
+ if (tokenizer->get_token()!=GDTokenizer::TK_PARENTHESIS_OPEN) {
+ _set_error("Expected '(' after 'yield'");
+ return NULL;
+ }
+
+ tokenizer->advance();
+
+ OperatorNode *yield = alloc_node<OperatorNode>();
+ yield->op=OperatorNode::OP_YIELD;
+
+ if (tokenizer->get_token()==GDTokenizer::TK_PARENTHESIS_CLOSE) {
+ expr=yield;
+ tokenizer->advance();
+ } else {
+
+ Node *object = _parse_and_reduce_expression(p_parent,p_static);
+ if (!object)
+ return NULL;
+ yield->arguments.push_back(object);
+
+ if (tokenizer->get_token()!=GDTokenizer::TK_COMMA) {
+
+ _set_error("Expected ',' after first argument of 'yield'");
+ return NULL;
+ }
+
+ tokenizer->advance();
+
+ Node *signal = _parse_and_reduce_expression(p_parent,p_static);
+ if (!signal)
+ return NULL;
+ yield->arguments.push_back(signal);
+
+ if (tokenizer->get_token()!=GDTokenizer::TK_PARENTHESIS_CLOSE) {
+
+ _set_error("Expected ')' after second argument of 'yield'");
+ return NULL;
+ }
+
+ tokenizer->advance();
+
+ expr=yield;
+ }
+
} else if (tokenizer->get_token()==GDTokenizer::TK_SELF) {
@@ -271,12 +383,19 @@ GDParser::Node* GDParser::_parse_expression(Node *p_parent,bool p_static,bool p_
Variant::Type bi_type = tokenizer->get_token_type();
tokenizer->advance(2);
- if (tokenizer->get_token()!=GDTokenizer::TK_IDENTIFIER) {
+
+ StringName identifier;
+
+ if (_get_completable_identifier(COMPLETION_BUILT_IN_TYPE_CONSTANT,identifier)) {
+
+ completion_built_in_constant=bi_type;
+ }
+
+ if (identifier==StringName()) {
_set_error("Built-in type constant expected after '.'");
return NULL;
}
- StringName identifier = tokenizer->get_token_identifier();
if (!Variant::has_numeric_constant(bi_type,identifier)) {
_set_error("Static constant '"+identifier.operator String()+"' not present in built-in type "+Variant::get_type_name(bi_type)+".");
@@ -286,7 +405,7 @@ GDParser::Node* GDParser::_parse_expression(Node *p_parent,bool p_static,bool p_
ConstantNode *cn = alloc_node<ConstantNode>();
cn->value=Variant::get_numeric_constant_value(bi_type,identifier);
expr=cn;
- tokenizer->advance();
+
} else if (tokenizer->get_token(1)==GDTokenizer::TK_PARENTHESIS_OPEN && (tokenizer->get_token()==GDTokenizer::TK_BUILT_IN_TYPE || tokenizer->get_token()==GDTokenizer::TK_IDENTIFIER || tokenizer->get_token()==GDTokenizer::TK_BUILT_IN_FUNC)) {
//function or constructor
@@ -299,23 +418,35 @@ GDParser::Node* GDParser::_parse_expression(Node *p_parent,bool p_static,bool p_
TypeNode *tn = alloc_node<TypeNode>();
tn->vtype=tokenizer->get_token_type();
op->arguments.push_back(tn);
+ tokenizer->advance(2);
} else if (tokenizer->get_token()==GDTokenizer::TK_BUILT_IN_FUNC) {
BuiltInFunctionNode *bn = alloc_node<BuiltInFunctionNode>();
bn->function=tokenizer->get_token_built_in_func();
op->arguments.push_back(bn);
+ tokenizer->advance(2);
} else {
SelfNode *self = alloc_node<SelfNode>();
op->arguments.push_back(self);
+ StringName identifier;
+ if (_get_completable_identifier(COMPLETION_FUNCTION,identifier)) {
+
+ }
+
IdentifierNode* id = alloc_node<IdentifierNode>();
- id->name=tokenizer->get_token_identifier();
+ id->name=identifier;
op->arguments.push_back(id);
+ tokenizer->advance(1);
}
- tokenizer->advance(2);
- if (!_parse_arguments(op,op->arguments,p_static))
+ if (tokenizer->get_token()==GDTokenizer::TK_CURSOR) {
+ _make_completable_call(0);
+ completion_node=op;
+
+ }
+ if (!_parse_arguments(op,op->arguments,p_static,true))
return NULL;
expr=op;
@@ -323,10 +454,28 @@ GDParser::Node* GDParser::_parse_expression(Node *p_parent,bool p_static,bool p_
} else if (tokenizer->get_token()==GDTokenizer::TK_IDENTIFIER) {
//identifier (reference)
- IdentifierNode *id = alloc_node<IdentifierNode>();
- id->name=tokenizer->get_token_identifier();
- tokenizer->advance();
- expr=id;
+ const ClassNode* cln = static_cast<const ClassNode*>(get_parse_tree());
+ bool bfn = false;
+ StringName identifier;
+ if (_get_completable_identifier(COMPLETION_IDENTIFIER,identifier)) {
+
+ }
+
+ for( int i=0; i<cln->constant_expressions.size(); ++i ) {
+
+ if( cln->constant_expressions[i].identifier == identifier ) {
+
+ expr = cln->constant_expressions[i].expression;
+ bfn = true;
+ break;
+ }
+ }
+
+ if ( !bfn ) {
+ IdentifierNode *id = alloc_node<IdentifierNode>();
+ id->name = identifier;
+ expr = id;
+ }
} else if (/*tokenizer->get_token()==GDTokenizer::TK_OP_ADD ||*/ tokenizer->get_token()==GDTokenizer::TK_OP_SUB || tokenizer->get_token()==GDTokenizer::TK_OP_NOT || tokenizer->get_token()==GDTokenizer::TK_OP_BIT_INVERT) {
@@ -528,7 +677,7 @@ GDParser::Node* GDParser::_parse_expression(Node *p_parent,bool p_static,bool p_
expr=dict;
- } else if (tokenizer->get_token()==GDTokenizer::TK_PERIOD && tokenizer->get_token(1)==GDTokenizer::TK_IDENTIFIER && tokenizer->get_token(2)==GDTokenizer::TK_PARENTHESIS_OPEN) {
+ } else if (tokenizer->get_token()==GDTokenizer::TK_PERIOD && (tokenizer->get_token(1)==GDTokenizer::TK_IDENTIFIER || tokenizer->get_token(1)==GDTokenizer::TK_CURSOR) && tokenizer->get_token(2)==GDTokenizer::TK_PARENTHESIS_OPEN) {
// parent call
tokenizer->advance(); //goto identifier
@@ -539,12 +688,16 @@ GDParser::Node* GDParser::_parse_expression(Node *p_parent,bool p_static,bool p_
/*SelfNode *self = alloc_node<SelfNode>();
op->arguments.push_back(self);
forbidden for now */
+ StringName identifier;
+ if (_get_completable_identifier(COMPLETION_PARENT_FUNCTION,identifier)) {
+ //indexing stuff
+ }
- IdentifierNode* id = alloc_node<IdentifierNode>();
- id->name=tokenizer->get_token_identifier();
+ IdentifierNode *id = alloc_node<IdentifierNode>();
+ id->name=identifier;
op->arguments.push_back(id);
- tokenizer->advance(2);
+ tokenizer->advance(1);
if (!_parse_arguments(op,op->arguments,p_static))
return NULL;
@@ -579,7 +732,7 @@ GDParser::Node* GDParser::_parse_expression(Node *p_parent,bool p_static,bool p_
//indexing using "."
- if (tokenizer->get_token(1)!=GDTokenizer::TK_IDENTIFIER && tokenizer->get_token(1)!=GDTokenizer::TK_BUILT_IN_FUNC ) {
+ if (tokenizer->get_token(1)!=GDTokenizer::TK_CURSOR && tokenizer->get_token(1)!=GDTokenizer::TK_IDENTIFIER && tokenizer->get_token(1)!=GDTokenizer::TK_BUILT_IN_FUNC ) {
_set_error("Expected identifier as member");
return NULL;
} else if (tokenizer->get_token(2)==GDTokenizer::TK_PARENTHESIS_OPEN) {
@@ -587,37 +740,67 @@ GDParser::Node* GDParser::_parse_expression(Node *p_parent,bool p_static,bool p_
OperatorNode * op = alloc_node<OperatorNode>();
op->op=OperatorNode::OP_CALL;
+ tokenizer->advance();
+
IdentifierNode * id = alloc_node<IdentifierNode>();
- if (tokenizer->get_token(1)==GDTokenizer::TK_BUILT_IN_FUNC ) {
+ if (tokenizer->get_token()==GDTokenizer::TK_BUILT_IN_FUNC ) {
//small hack so built in funcs don't obfuscate methods
- id->name=GDFunctions::get_func_name(tokenizer->get_token_built_in_func(1));
+ id->name=GDFunctions::get_func_name(tokenizer->get_token_built_in_func());
+ tokenizer->advance();
+
} else {
- id->name=tokenizer->get_token_identifier(1);
+ StringName identifier;
+ if (_get_completable_identifier(COMPLETION_METHOD,identifier)) {
+ completion_node=op;
+ //indexing stuff
+ }
+
+ id->name=identifier;
}
op->arguments.push_back(expr); // call what
op->arguments.push_back(id); // call func
//get arguments
- tokenizer->advance(3);
- if (!_parse_arguments(op,op->arguments,p_static))
+ tokenizer->advance(1);
+ if (tokenizer->get_token()==GDTokenizer::TK_CURSOR) {
+ _make_completable_call(0);
+ completion_node=op;
+
+ }
+ if (!_parse_arguments(op,op->arguments,p_static,true))
return NULL;
expr=op;
} else {
//simple indexing!
+
+
OperatorNode * op = alloc_node<OperatorNode>();
op->op=OperatorNode::OP_INDEX_NAMED;
+ tokenizer->advance();
+
+
+ StringName identifier;
+ if (_get_completable_identifier(COMPLETION_INDEX,identifier)) {
+
+ if (identifier==StringName()) {
+ identifier="@temp"; //so it parses allright
+ }
+ completion_node=op;
+
+ //indexing stuff
+ }
IdentifierNode * id = alloc_node<IdentifierNode>();
- id->name=tokenizer->get_token_identifier(1);
+ id->name=identifier;
op->arguments.push_back(expr);
op->arguments.push_back(id);
expr=op;
- tokenizer->advance(2);
+
}
} else if (tokenizer->get_token()==GDTokenizer::TK_BRACKET_OPEN) {
@@ -909,6 +1092,7 @@ GDParser::Node* GDParser::_reduce_expression(Node *p_node,bool p_to_const) {
ConstantNode *cn = alloc_node<ConstantNode>();
Array arr(!p_to_const);
+ //print_line("mk array "+itos(!p_to_const));
arr.resize(an->elements.size());
for(int i=0;i<an->elements.size();i++) {
ConstantNode *acn = static_cast<ConstantNode*>(an->elements[i]);
@@ -1067,6 +1251,10 @@ GDParser::Node* GDParser::_reduce_expression(Node *p_node,bool p_to_const) {
}
return op; //don't reduce yet
+
+ } else if (op->op==OperatorNode::OP_YIELD) {
+ return op;
+
} else if (op->op==OperatorNode::OP_INDEX) {
//can reduce indices into constant arrays or dictionaries
@@ -1089,7 +1277,7 @@ GDParser::Node* GDParser::_reduce_expression(Node *p_node,bool p_to_const) {
cn->value=v;
return cn;
- } else if (op->arguments[0]->type==Node::TYPE_CONSTANT && op->arguments[1]->type==Node::TYPE_IDENTIFIER) {
+ } /*else if (op->arguments[0]->type==Node::TYPE_CONSTANT && op->arguments[1]->type==Node::TYPE_IDENTIFIER) {
ConstantNode *ca = static_cast<ConstantNode*>(op->arguments[0]);
IdentifierNode *ib = static_cast<IdentifierNode*>(op->arguments[1]);
@@ -1104,10 +1292,31 @@ GDParser::Node* GDParser::_reduce_expression(Node *p_node,bool p_to_const) {
ConstantNode *cn = alloc_node<ConstantNode>();
cn->value=v;
return cn;
+ }*/
+
+ return op;
+
+ } else if (op->op==OperatorNode::OP_INDEX_NAMED) {
+
+ if (op->arguments[0]->type==Node::TYPE_CONSTANT && op->arguments[1]->type==Node::TYPE_IDENTIFIER) {
+
+ ConstantNode *ca = static_cast<ConstantNode*>(op->arguments[0]);
+ IdentifierNode *ib = static_cast<IdentifierNode*>(op->arguments[1]);
+
+ bool valid;
+ Variant v = ca->value.get_named(ib->name,&valid);
+ if (!valid) {
+ _set_error("invalid index '"+String(ib->name)+"' in constant expression");
+ return op;
+ }
+ ConstantNode *cn = alloc_node<ConstantNode>();
+ cn->value=v;
+ return cn;
}
return op;
+
}
//validate assignment (don't assign to cosntant expression
@@ -1126,7 +1335,7 @@ GDParser::Node* GDParser::_reduce_expression(Node *p_node,bool p_to_const) {
case OperatorNode::OP_ASSIGN_BIT_XOR: {
if (op->arguments[0]->type==Node::TYPE_CONSTANT) {
- _set_error("Can't assign to constant");
+ _set_error("Can't assign to constant",tokenizer->get_token_line()-1);
return op;
}
@@ -1209,6 +1418,24 @@ GDParser::Node* GDParser::_parse_and_reduce_expression(Node *p_parent,bool p_sta
return expr;
}
+bool GDParser::_recover_from_completion() {
+
+ if (!completion_found) {
+ return false; //can't recover if no completion
+ }
+ //skip stuff until newline
+ while(tokenizer->get_token()!=GDTokenizer::TK_NEWLINE && tokenizer->get_token()!=GDTokenizer::TK_EOF && tokenizer->get_token()!=GDTokenizer::TK_ERROR) {
+ tokenizer->advance();
+ }
+ completion_found=false;
+ error_set=false;
+ if(tokenizer->get_token() == GDTokenizer::TK_ERROR){
+ error_set = true;
+ }
+
+ return true;
+}
+
void GDParser::_parse_block(BlockNode *p_block,bool p_static) {
int indent_level = tab_level.back()->get();
@@ -1270,7 +1497,7 @@ void GDParser::_parse_block(BlockNode *p_block,bool p_static) {
} break;
case GDTokenizer::TK_CF_PASS: {
- if (tokenizer->get_token(1)!=GDTokenizer::TK_SEMICOLON && tokenizer->get_token(1)!=GDTokenizer::TK_NEWLINE ) {
+ if (tokenizer->get_token(1)!=GDTokenizer::TK_SEMICOLON && tokenizer->get_token(1)!=GDTokenizer::TK_NEWLINE && tokenizer->get_token(1)!=GDTokenizer::TK_EOF) {
_set_error("Expected ';' or <NewLine>.");
return;
@@ -1306,8 +1533,14 @@ void GDParser::_parse_block(BlockNode *p_block,bool p_static) {
Node *subexpr=NULL;
subexpr = _parse_and_reduce_expression(p_block,p_static);
- if (!subexpr)
+ if (!subexpr) {
+ if (_recover_from_completion()) {
+ break;
+ }
return;
+ }
+
+
lv->assign=subexpr;
assigned=subexpr;
@@ -1328,16 +1561,22 @@ void GDParser::_parse_block(BlockNode *p_block,bool p_static) {
op->arguments.push_back(assigned);
p_block->statements.push_back(op);
- _end_statement();
-
+ if (!_end_statement()) {
+ _set_error("Expected end of statement (var)");
+ return;
+ }
} break;
case GDTokenizer::TK_CF_IF: {
tokenizer->advance();
Node *condition = _parse_and_reduce_expression(p_block,p_static);
- if (!condition)
+ if (!condition) {
+ if (_recover_from_completion()) {
+ break;
+ }
return;
+ }
ControlFlowNode *cf_if = alloc_node<ControlFlowNode>();
@@ -1345,6 +1584,7 @@ void GDParser::_parse_block(BlockNode *p_block,bool p_static) {
cf_if->arguments.push_back(condition);
cf_if->body = alloc_node<BlockNode>();
+ cf_if->body->parent_block=p_block;
p_block->sub_blocks.push_back(cf_if->body);
if (!_enter_indent_block(cf_if->body)) {
@@ -1352,7 +1592,10 @@ void GDParser::_parse_block(BlockNode *p_block,bool p_static) {
return;
}
+ current_block=cf_if->body;
_parse_block(cf_if->body,p_static);
+ current_block=p_block;
+
if (error_set)
return;
p_block->statements.push_back(cf_if);
@@ -1379,6 +1622,7 @@ void GDParser::_parse_block(BlockNode *p_block,bool p_static) {
tokenizer->advance();
cf_if->body_else=alloc_node<BlockNode>();
+ cf_if->body_else->parent_block=p_block;
p_block->sub_blocks.push_back(cf_if->body_else);
ControlFlowNode *cf_else = alloc_node<ControlFlowNode>();
@@ -1386,14 +1630,19 @@ void GDParser::_parse_block(BlockNode *p_block,bool p_static) {
//condition
Node *condition = _parse_and_reduce_expression(p_block,p_static);
- if (!condition)
+ if (!condition) {
+ if (_recover_from_completion()) {
+ break;
+ }
return;
+ }
cf_else->arguments.push_back(condition);
cf_else->cf_type=ControlFlowNode::CF_IF;
cf_if->body_else->statements.push_back(cf_else);
cf_if=cf_else;
cf_if->body=alloc_node<BlockNode>();
+ cf_if->body->parent_block=p_block;
p_block->sub_blocks.push_back(cf_if->body);
@@ -1402,7 +1651,9 @@ void GDParser::_parse_block(BlockNode *p_block,bool p_static) {
return;
}
+ current_block=cf_else->body;
_parse_block(cf_else->body,p_static);
+ current_block=p_block;
if (error_set)
return;
@@ -1418,13 +1669,16 @@ void GDParser::_parse_block(BlockNode *p_block,bool p_static) {
tokenizer->advance();
cf_if->body_else=alloc_node<BlockNode>();
+ cf_if->body_else->parent_block=p_block;
p_block->sub_blocks.push_back(cf_if->body_else);
if (!_enter_indent_block(cf_if->body_else)) {
p_block->end_line=tokenizer->get_token_line();
return;
}
+ current_block=cf_if->body_else;
_parse_block(cf_if->body_else,p_static);
+ current_block=p_block;
if (error_set)
return;
@@ -1442,8 +1696,12 @@ void GDParser::_parse_block(BlockNode *p_block,bool p_static) {
tokenizer->advance();
Node *condition = _parse_and_reduce_expression(p_block,p_static);
- if (!condition)
+ if (!condition) {
+ if (_recover_from_completion()) {
+ break;
+ }
return;
+ }
ControlFlowNode *cf_while = alloc_node<ControlFlowNode>();
@@ -1451,6 +1709,7 @@ void GDParser::_parse_block(BlockNode *p_block,bool p_static) {
cf_while->arguments.push_back(condition);
cf_while->body = alloc_node<BlockNode>();
+ cf_while->body->parent_block=p_block;
p_block->sub_blocks.push_back(cf_while->body);
if (!_enter_indent_block(cf_while->body)) {
@@ -1458,7 +1717,9 @@ void GDParser::_parse_block(BlockNode *p_block,bool p_static) {
return;
}
+ current_block=cf_while->body;
_parse_block(cf_while->body,p_static);
+ current_block=p_block;
if (error_set)
return;
p_block->statements.push_back(cf_while);
@@ -1485,8 +1746,12 @@ void GDParser::_parse_block(BlockNode *p_block,bool p_static) {
tokenizer->advance();
Node *container = _parse_and_reduce_expression(p_block,p_static);
- if (!container)
+ if (!container) {
+ if (_recover_from_completion()) {
+ break;
+ }
return;
+ }
ControlFlowNode *cf_for = alloc_node<ControlFlowNode>();
@@ -1495,6 +1760,7 @@ void GDParser::_parse_block(BlockNode *p_block,bool p_static) {
cf_for->arguments.push_back(container);
cf_for->body = alloc_node<BlockNode>();
+ cf_for->body->parent_block=p_block;
p_block->sub_blocks.push_back(cf_for->body);
if (!_enter_indent_block(cf_for->body)) {
@@ -1502,7 +1768,10 @@ void GDParser::_parse_block(BlockNode *p_block,bool p_static) {
return;
}
+ current_block=cf_for->body;
_parse_block(cf_for->body,p_static);
+ current_block=p_block;
+
if (error_set)
return;
p_block->statements.push_back(cf_for);
@@ -1546,8 +1815,12 @@ void GDParser::_parse_block(BlockNode *p_block,bool p_static) {
} else {
//expect expression
Node *retexpr = _parse_and_reduce_expression(p_block,p_static);
- if (!retexpr)
+ if (!retexpr) {
+ if (_recover_from_completion()) {
+ break;
+ }
return;
+ }
cf_return->arguments.push_back(retexpr);
p_block->statements.push_back(cf_return);
if (!_end_statement()) {
@@ -1562,8 +1835,12 @@ void GDParser::_parse_block(BlockNode *p_block,bool p_static) {
tokenizer->advance();
Node *condition = _parse_and_reduce_expression(p_block,p_static);
- if (!condition)
+ if (!condition) {
+ if (_recover_from_completion()) {
+ break;
+ }
return;
+ }
AssertNode *an = alloc_node<AssertNode>();
an->condition=condition;
p_block->statements.push_back(an);
@@ -1576,8 +1853,12 @@ void GDParser::_parse_block(BlockNode *p_block,bool p_static) {
default: {
Node *expression = _parse_and_reduce_expression(p_block,p_static,false,true);
- if (!expression)
+ if (!expression) {
+ if (_recover_from_completion()) {
+ break;
+ }
return;
+ }
p_block->statements.push_back(expression);
if (!_end_statement()) {
_set_error("Expected end of statement after expression.");
@@ -1661,9 +1942,15 @@ void GDParser::_parse_extends(ClassNode *p_class) {
p_class->extends_used=true;
- //see if inheritance happens from a file
tokenizer->advance();
+ if (tokenizer->get_token()==GDTokenizer::TK_BUILT_IN_TYPE && tokenizer->get_token_type()==Variant::OBJECT) {
+ p_class->extends_class.push_back(Variant::get_type_name(Variant::OBJECT));
+ tokenizer->advance();
+ return;
+ }
+
+ // see if inheritance happens from a file
if (tokenizer->get_token()==GDTokenizer::TK_CONSTANT) {
Variant constant = tokenizer->get_token_constant();
@@ -1736,8 +2023,10 @@ void GDParser::_parse_class(ClassNode *p_class) {
_parse_extends(p_class);
if (error_set)
return;
- _end_statement();
-
+ if (!_end_statement()) {
+ _set_error("Expected end of statement after extends");
+ return;
+ }
} break;
case GDTokenizer::TK_PR_TOOL: {
@@ -1768,7 +2057,9 @@ void GDParser::_parse_class(ClassNode *p_class) {
ClassNode *newclass = alloc_node<ClassNode>();
newclass->initializer = alloc_node<BlockNode>();
+ newclass->initializer->parent_class=newclass;
newclass->name=name;
+ newclass->owner=p_class;
p_class->subclasses.push_back(newclass);
@@ -1785,7 +2076,9 @@ void GDParser::_parse_class(ClassNode *p_class) {
_set_error("Indented block expected.");
return;
}
+ current_class=newclass;
_parse_class(newclass);
+ current_class=p_class;
} break;
/* this is for functions....
@@ -1814,14 +2107,20 @@ void GDParser::_parse_class(ClassNode *p_class) {
}
- if (tokenizer->get_token(1)!=GDTokenizer::TK_IDENTIFIER) {
+ tokenizer->advance();
+ StringName name;
+
+ if (_get_completable_identifier(COMPLETION_VIRTUAL_FUNC,name)) {
+
+ }
+
+
+ if (name==StringName()) {
_set_error("Expected identifier after 'func' (syntax: 'func <identifier>([arguments]):' ).");
return;
}
- StringName name = tokenizer->get_token_identifier(1);
-
for(int i=0;i<p_class->functions.size();i++) {
if (p_class->functions[i]->name==name) {
_set_error("Function '"+String(name)+"' already exists in this class (at line: "+itos(p_class->functions[i]->line)+").");
@@ -1832,7 +2131,7 @@ void GDParser::_parse_class(ClassNode *p_class) {
_set_error("Function '"+String(name)+"' already exists in this class (at line: "+itos(p_class->static_functions[i]->line)+").");
}
}
- tokenizer->advance(2);
+
if (tokenizer->get_token()!=GDTokenizer::TK_PARENTHESIS_OPEN) {
@@ -1923,6 +2222,7 @@ void GDParser::_parse_class(ClassNode *p_class) {
tokenizer->advance();
BlockNode *block = alloc_node<BlockNode>();
+ block->parent_class=p_class;
if (name=="_init") {
@@ -1998,10 +2298,61 @@ void GDParser::_parse_class(ClassNode *p_class) {
p_class->functions.push_back(function);
- _parse_block(block,_static);
+ current_function=function;
function->body=block;
+ current_block=block;
+ _parse_block(block,_static);
+ current_block=NULL;
+
//arguments
} break;
+ case GDTokenizer::TK_PR_SIGNAL: {
+ tokenizer->advance();
+
+ if (tokenizer->get_token()!=GDTokenizer::TK_IDENTIFIER) {
+ _set_error("Expected identifier after 'signal'.");
+ return;
+ }
+
+ ClassNode::Signal sig;
+ sig.name = tokenizer->get_token_identifier();
+ tokenizer->advance();
+
+
+ if (tokenizer->get_token()==GDTokenizer::TK_PARENTHESIS_OPEN) {
+ tokenizer->advance();
+ while(true) {
+
+
+ if (tokenizer->get_token()==GDTokenizer::TK_PARENTHESIS_CLOSE) {
+ tokenizer->advance();
+ break;
+ }
+
+ if (tokenizer->get_token()!=GDTokenizer::TK_IDENTIFIER) {
+ _set_error("Expected identifier in signal argument.");
+ return;
+ }
+
+ sig.arguments.push_back(tokenizer->get_token_identifier());
+ tokenizer->advance();
+
+ if (tokenizer->get_token()==GDTokenizer::TK_COMMA) {
+ tokenizer->advance();
+ } else if (tokenizer->get_token()!=GDTokenizer::TK_PARENTHESIS_CLOSE) {
+ _set_error("Expected ',' or ')' after signal parameter identifier.");
+ return;
+ }
+ }
+ }
+
+ p_class->_signals.push_back(sig);
+
+ if (!_end_statement()) {
+ _set_error("Expected end of statement (signal)");
+ return;
+ }
+ } break;
case GDTokenizer::TK_PR_EXPORT: {
tokenizer->advance();
@@ -2026,6 +2377,17 @@ void GDParser::_parse_class(ClassNode *p_class) {
case Variant::INT: {
+ if (tokenizer->get_token()==GDTokenizer::TK_IDENTIFIER && tokenizer->get_token_identifier()=="FLAGS") {
+
+ current_export.hint=PROPERTY_HINT_ALL_FLAGS;
+ tokenizer->advance();
+ if (tokenizer->get_token()!=GDTokenizer::TK_PARENTHESIS_CLOSE) {
+ _set_error("Expected ')' in hint.");
+ return;
+ }
+ break;
+ }
+
if (tokenizer->get_token()==GDTokenizer::TK_CONSTANT && tokenizer->get_token_constant().get_type()==Variant::STRING) {
//enumeration
current_export.hint=PROPERTY_HINT_ENUM;
@@ -2036,6 +2398,7 @@ void GDParser::_parse_class(ClassNode *p_class) {
current_export=PropertyInfo();
_set_error("Expected a string constant in enumeration hint.");
+ return;
}
String c = tokenizer->get_token_constant();
@@ -2053,6 +2416,7 @@ void GDParser::_parse_class(ClassNode *p_class) {
if (tokenizer->get_token()!=GDTokenizer::TK_COMMA) {
current_export=PropertyInfo();
_set_error("Expected ')' or ',' in enumeration hint.");
+ return;
}
tokenizer->advance();
@@ -2062,19 +2426,36 @@ void GDParser::_parse_class(ClassNode *p_class) {
break;
}
- };
+ }; //fallthrough to use the same
case Variant::REAL: {
+ if (tokenizer->get_token()==GDTokenizer::TK_IDENTIFIER && tokenizer->get_token_identifier()=="EASE") {
+ current_export.hint=PROPERTY_HINT_EXP_EASING;
+ tokenizer->advance();
+ if (tokenizer->get_token()!=GDTokenizer::TK_PARENTHESIS_CLOSE) {
+ _set_error("Expected ')' in hint.");
+ return;
+ }
+ break;
+ }
+
+ float sign=1.0;
+
+ if (tokenizer->get_token()==GDTokenizer::TK_OP_SUB) {
+ sign=-1;
+ tokenizer->advance();
+ }
if (tokenizer->get_token()!=GDTokenizer::TK_CONSTANT || !tokenizer->get_token_constant().is_num()) {
current_export=PropertyInfo();
_set_error("Expected a range in numeric hint.");
+ return;
}
//enumeration
current_export.hint=PROPERTY_HINT_RANGE;
- current_export.hint_string=tokenizer->get_token_constant().operator String();
+ current_export.hint_string=rtos(sign*double(tokenizer->get_token_constant()));
tokenizer->advance();
if (tokenizer->get_token()==GDTokenizer::TK_PARENTHESIS_CLOSE) {
@@ -2086,17 +2467,25 @@ void GDParser::_parse_class(ClassNode *p_class) {
current_export=PropertyInfo();
_set_error("Expected ',' or ')' in numeric range hint.");
+ return;
}
tokenizer->advance();
+ sign=1.0;
+ if (tokenizer->get_token()==GDTokenizer::TK_OP_SUB) {
+ sign=-1;
+ tokenizer->advance();
+ }
+
if (tokenizer->get_token()!=GDTokenizer::TK_CONSTANT || !tokenizer->get_token_constant().is_num()) {
current_export=PropertyInfo();
_set_error("Expected a number as upper bound in numeric range hint.");
+ return;
}
- current_export.hint_string+=","+tokenizer->get_token_constant().operator String();
+ current_export.hint_string+=","+rtos(sign*double(tokenizer->get_token_constant()));
tokenizer->advance();
if (tokenizer->get_token()==GDTokenizer::TK_PARENTHESIS_CLOSE)
@@ -2106,17 +2495,24 @@ void GDParser::_parse_class(ClassNode *p_class) {
current_export=PropertyInfo();
_set_error("Expected ',' or ')' in numeric range hint.");
+ return;
}
tokenizer->advance();
+ sign=1.0;
+ if (tokenizer->get_token()==GDTokenizer::TK_OP_SUB) {
+ sign=-1;
+ tokenizer->advance();
+ }
if (tokenizer->get_token()!=GDTokenizer::TK_CONSTANT || !tokenizer->get_token_constant().is_num()) {
current_export=PropertyInfo();
_set_error("Expected a number as step in numeric range hint.");
+ return;
}
- current_export.hint_string+=","+tokenizer->get_token_constant().operator String();
+ current_export.hint_string+=","+rtos(sign*double(tokenizer->get_token_constant()));
tokenizer->advance();
} break;
@@ -2132,6 +2528,7 @@ void GDParser::_parse_class(ClassNode *p_class) {
current_export=PropertyInfo();
_set_error("Expected a string constant in enumeration hint.");
+ return;
}
String c = tokenizer->get_token_constant();
@@ -2192,6 +2589,17 @@ void GDParser::_parse_class(ClassNode *p_class) {
}
break;
}
+
+ if (tokenizer->get_token()==GDTokenizer::TK_IDENTIFIER && tokenizer->get_token_identifier()=="MULTILINE") {
+
+ current_export.hint=PROPERTY_HINT_MULTILINE_TEXT;
+ tokenizer->advance();
+ if (tokenizer->get_token()!=GDTokenizer::TK_PARENTHESIS_CLOSE) {
+ _set_error("Expected ')' in hint.");
+ return;
+ }
+ break;
+ }
} break;
case Variant::COLOR: {
@@ -2279,86 +2687,129 @@ void GDParser::_parse_class(ClassNode *p_class) {
}
member.identifier=tokenizer->get_token_identifier();
+ member.expression=NULL;
member._export.name=member.identifier;
+ member.line=tokenizer->get_token_line();
tokenizer->advance();
- p_class->variables.push_back(member);
+ if (tokenizer->get_token()==GDTokenizer::TK_OP_ASSIGN) {
- if (tokenizer->get_token()!=GDTokenizer::TK_OP_ASSIGN) {
+#ifdef DEBUG_ENABLED
+ int line = tokenizer->get_token_line();
+#endif
+ tokenizer->advance();
- if (autoexport) {
+ Node *subexpr=NULL;
- _set_error("Type-less export needs a constant expression assigned to infer type.");
+ subexpr = _parse_and_reduce_expression(p_class,false,autoexport);
+ if (!subexpr) {
+ if (_recover_from_completion()) {
+ break;
+ }
return;
}
- break;
- }
-#ifdef DEBUG_ENABLED
- int line = tokenizer->get_token_line();
-#endif
- tokenizer->advance();
- Node *subexpr=NULL;
+ member.expression=subexpr;
- subexpr = _parse_and_reduce_expression(p_class,false);
- if (!subexpr)
- return;
+ if (autoexport) {
+ if (1)/*(subexpr->type==Node::TYPE_ARRAY) {
- if (autoexport) {
- if (subexpr->type==Node::TYPE_ARRAY) {
+ member._export.type=Variant::ARRAY;
- p_class->variables[p_class->variables.size()-1]._export.type=Variant::ARRAY;
+ } else if (subexpr->type==Node::TYPE_DICTIONARY) {
- } else if (subexpr->type==Node::TYPE_DICTIONARY) {
+ member._export.type=Variant::DICTIONARY;
- p_class->variables[p_class->variables.size()-1]._export.type=Variant::DICTIONARY;
+ } else*/ {
- } else {
+ if (subexpr->type!=Node::TYPE_CONSTANT) {
- if (subexpr->type!=Node::TYPE_CONSTANT) {
+ _set_error("Type-less export needs a constant expression assigned to infer type.");
+ return;
+ }
- _set_error("Type-less export needs a constant expression assigned to infer type.");
- return;
+ ConstantNode *cn = static_cast<ConstantNode*>(subexpr);
+ if (cn->value.get_type()==Variant::NIL) {
+
+ _set_error("Can't accept a null constant expression for infering export type.");
+ return;
+ }
+ member._export.type=cn->value.get_type();
}
+ }
+#ifdef TOOLS_ENABLED
+ if (subexpr->type==Node::TYPE_CONSTANT && member._export.type!=Variant::NIL) {
ConstantNode *cn = static_cast<ConstantNode*>(subexpr);
- if (cn->value.get_type()==Variant::NIL) {
-
- _set_error("Can't accept a null constant expression for infering export type.");
- return;
+ if (cn->value.get_type()!=Variant::NIL) {
+ member.default_value=cn->value;
}
- p_class->variables[p_class->variables.size()-1]._export.type=cn->value.get_type();
}
- }
-#ifdef TOOLS_ENABLED
- if (subexpr->type==Node::TYPE_CONSTANT && p_class->variables[p_class->variables.size()-1]._export.type!=Variant::NIL) {
+#endif
+
+ IdentifierNode *id = alloc_node<IdentifierNode>();
+ id->name=member.identifier;
+
+ OperatorNode *op = alloc_node<OperatorNode>();
+ op->op=OperatorNode::OP_INIT_ASSIGN;
+ op->arguments.push_back(id);
+ op->arguments.push_back(subexpr);
+
+#ifdef DEBUG_ENABLED
+ NewLineNode *nl = alloc_node<NewLineNode>();
+ nl->line=line;
+ p_class->initializer->statements.push_back(nl);
+#endif
+ p_class->initializer->statements.push_back(op);
+
+
+
+ } else {
+
+ if (autoexport) {
- ConstantNode *cn = static_cast<ConstantNode*>(subexpr);
- if (cn->value.get_type()!=Variant::NIL) {
- p_class->variables[p_class->variables.size()-1].default_value=cn->value;
+ _set_error("Type-less export needs a constant expression assigned to infer type.");
+ return;
}
+
}
-#endif
+ if (tokenizer->get_token()==GDTokenizer::TK_PR_SETGET) {
- IdentifierNode *id = alloc_node<IdentifierNode>();
- id->name=member.identifier;
+ tokenizer->advance();
- OperatorNode *op = alloc_node<OperatorNode>();
- op->op=OperatorNode::OP_ASSIGN;
- op->arguments.push_back(id);
- op->arguments.push_back(subexpr);
+ if (tokenizer->get_token()!=GDTokenizer::TK_COMMA) {
+ //just comma means using only getter
+ if (tokenizer->get_token()!=GDTokenizer::TK_IDENTIFIER) {
+ _set_error("Expected identifier for setter function after 'notify'.");
+ }
-#ifdef DEBUG_ENABLED
- NewLineNode *nl = alloc_node<NewLineNode>();
- nl->line=line;
- p_class->initializer->statements.push_back(nl);
-#endif
- p_class->initializer->statements.push_back(op);
+ member.setter=tokenizer->get_token_identifier();
+
+ tokenizer->advance();
+ }
+
+ if (tokenizer->get_token()==GDTokenizer::TK_COMMA) {
+ //there is a getter
+ tokenizer->advance();
+
+ if (tokenizer->get_token()!=GDTokenizer::TK_IDENTIFIER) {
+ _set_error("Expected identifier for getter function after ','.");
+ }
+
+ member.getter=tokenizer->get_token_identifier();
+ tokenizer->advance();
- _end_statement();
+ }
+ }
+ p_class->variables.push_back(member);
+
+ if (!_end_statement()) {
+ _set_error("Expected end of statement (continue)");
+ return;
+ }
} break;
case GDTokenizer::TK_PR_CONST: {
//variale declaration and (eventual) initialization
@@ -2385,8 +2836,12 @@ void GDParser::_parse_class(ClassNode *p_class) {
Node *subexpr=NULL;
subexpr = _parse_and_reduce_expression(p_class,true,true);
- if (!subexpr)
+ if (!subexpr) {
+ if (_recover_from_completion()) {
+ break;
+ }
return;
+ }
if (subexpr->type!=Node::TYPE_CONSTANT) {
_set_error("Expected constant expression");
@@ -2395,8 +2850,10 @@ void GDParser::_parse_class(ClassNode *p_class) {
p_class->constant_expressions.push_back(constant);
- _end_statement();
-
+ if (!_end_statement()) {
+ _set_error("Expected end of statement (constant)");
+ return;
+ }
} break;
@@ -2453,6 +2910,8 @@ Error GDParser::_parse(const String& p_base_path) {
//assume class
ClassNode *main_class = alloc_node<ClassNode>();
main_class->initializer = alloc_node<BlockNode>();
+ main_class->initializer->parent_class=main_class;
+ current_class=main_class;
_parse_class(main_class);
@@ -2468,8 +2927,21 @@ Error GDParser::_parse(const String& p_base_path) {
return OK;
}
-Error GDParser::parse_bytecode(const Vector<uint8_t> &p_bytecode,const String& p_base_path) {
+Error GDParser::parse_bytecode(const Vector<uint8_t> &p_bytecode,const String& p_base_path, const String &p_self_path) {
+ for_completion=false;
+ validating=false;
+ completion_type=COMPLETION_NONE;
+ completion_node=NULL;
+ completion_class=NULL;
+ completion_function=NULL;
+ completion_block=NULL;
+ completion_found=false;
+ current_block=NULL;
+ current_class=NULL;
+ current_function=NULL;
+
+ self_path=p_self_path;
GDTokenizerBuffer *tb = memnew( GDTokenizerBuffer );
tb->set_code_buffer(p_bytecode);
tokenizer=tb;
@@ -2480,13 +2952,25 @@ Error GDParser::parse_bytecode(const Vector<uint8_t> &p_bytecode,const String& p
}
-Error GDParser::parse(const String& p_code,const String& p_base_path,bool p_just_validate) {
+Error GDParser::parse(const String& p_code, const String& p_base_path, bool p_just_validate, const String &p_self_path,bool p_for_completion) {
+
+ completion_type=COMPLETION_NONE;
+ completion_node=NULL;
+ completion_class=NULL;
+ completion_function=NULL;
+ completion_block=NULL;
+ completion_found=false;
+ current_block=NULL;
+ current_class=NULL;
+ current_function=NULL;
+ self_path=p_self_path;
GDTokenizerText *tt = memnew( GDTokenizerText );
tt->set_code(p_code);
validating=p_just_validate;
+ for_completion=p_for_completion;
tokenizer=tt;
Error ret = _parse(p_base_path);
memdelete(tt);
@@ -2511,7 +2995,20 @@ void GDParser::clear() {
head=NULL;
list=NULL;
+ completion_type=COMPLETION_NONE;
+ completion_node=NULL;
+ completion_class=NULL;
+ completion_function=NULL;
+ completion_block=NULL;
+ current_block=NULL;
+ current_class=NULL;
+
+ completion_found=false;
+
+ current_function=NULL;
+
validating=false;
+ for_completion=false;
error_set=false;
tab_level.clear();
tab_level.push_back(0);
@@ -2524,6 +3021,52 @@ void GDParser::clear() {
}
+
+GDParser::CompletionType GDParser::get_completion_type() {
+
+ return completion_type;
+}
+
+StringName GDParser::get_completion_cursor() {
+
+ return completion_cursor;
+}
+
+int GDParser::get_completion_line() {
+
+ return completion_line;
+}
+
+Variant::Type GDParser::get_completion_built_in_constant(){
+
+ return completion_built_in_constant;
+}
+
+GDParser::Node *GDParser::get_completion_node(){
+
+ return completion_node;
+}
+
+GDParser::BlockNode *GDParser::get_completion_block() {
+
+ return completion_block;
+}
+
+GDParser::ClassNode *GDParser::get_completion_class(){
+
+ return completion_class;
+}
+
+GDParser::FunctionNode *GDParser::get_completion_function(){
+
+ return completion_function;
+}
+
+int GDParser::get_completion_argument_index() {
+
+ return completion_argument;
+}
+
GDParser::GDParser() {
head=NULL;