diff options
Diffstat (limited to 'modules/gdscript/gd_parser.cpp')
-rw-r--r-- | modules/gdscript/gd_parser.cpp | 781 |
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; |