1
0
mirror of https://github.com/godotengine/godot.git synced 2025-11-04 12:00:25 +00:00

-moved script to modules

This commit is contained in:
Juan Linietsky
2014-02-24 09:53:33 -03:00
parent 51609ffc04
commit 4b07eb8deb
43 changed files with 597 additions and 148 deletions

7
modules/gdscript/SCsub Normal file
View File

@@ -0,0 +1,7 @@
Import('env')
env.add_source_files(env.modules_sources,"*.cpp")
Export('env')

View File

@@ -0,0 +1,11 @@
def can_build(platform):
return True
def configure(env):
pass

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,181 @@
/*************************************************************************/
/* gd_compiler.h */
/*************************************************************************/
/* This file is part of: */
/* GODOT ENGINE */
/* http://www.godotengine.org */
/*************************************************************************/
/* Copyright (c) 2007-2014 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 */
/* "Software"), to deal in the Software without restriction, including */
/* without limitation the rights to use, copy, modify, merge, publish, */
/* distribute, sublicense, and/or sell copies of the Software, and to */
/* permit persons to whom the Software is furnished to do so, subject to */
/* the following conditions: */
/* */
/* The above copyright notice and this permission notice shall be */
/* included in all copies or substantial portions of the Software. */
/* */
/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */
/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */
/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.*/
/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */
/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */
/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */
/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */
/*************************************************************************/
#ifndef GD_COMPILER_H
#define GD_COMPILER_H
#include "gd_parser.h"
#include "gd_script.h"
class GDCompiler {
const GDParser *parser;
struct CodeGen {
GDScript *script;
const GDParser::ClassNode *class_node;
const GDParser::FunctionNode *function_node;
bool debug_stack;
List< Map<StringName,int> > stack_id_stack;
Map<StringName,int> stack_identifiers;
List<GDFunction::StackDebug> stack_debug;
List< Map<StringName,int> > block_identifier_stack;
Map<StringName,int> block_identifiers;
void add_stack_identifier(const StringName& p_id,int p_stackpos) {
stack_identifiers[p_id]=p_stackpos;
if (debug_stack) {
block_identifiers[p_id]=p_stackpos;
GDFunction::StackDebug sd;
sd.added=true;
sd.line=current_line;
sd.identifier=p_id;
sd.pos=p_stackpos;
stack_debug.push_back(sd);
}
}
void push_stack_identifiers() {
stack_id_stack.push_back( stack_identifiers );
if (debug_stack) {
block_identifier_stack.push_back(block_identifiers);
block_identifiers.clear();
}
}
void pop_stack_identifiers() {
stack_identifiers = stack_id_stack.back()->get();
stack_id_stack.pop_back();
if (debug_stack) {
for (Map<StringName,int>::Element *E=block_identifiers.front();E;E=E->next()) {
GDFunction::StackDebug sd;
sd.added=false;
sd.identifier=E->key();
sd.line=current_line;
sd.pos=E->get();
stack_debug.push_back(sd);
}
block_identifiers=block_identifier_stack.back()->get();
block_identifier_stack.pop_back();
}
}
// int get_identifier_pos(const StringName& p_dentifier) const;
HashMap<Variant,int,VariantHasher> constant_map;
Map<StringName,int> name_map;
int get_name_map_pos(const StringName& p_identifier) {
int ret;
if (!name_map.has(p_identifier)) {
ret=name_map.size();
name_map[p_identifier]=ret;
} else {
ret=name_map[p_identifier];
}
return ret;
}
int get_constant_pos(const Variant& p_constant) {
if (constant_map.has(p_constant))
return constant_map[p_constant];
int pos = constant_map.size();
constant_map[p_constant]=pos;
return pos;
}
Vector<int> opcodes;
void alloc_stack(int p_level) { if (p_level >= stack_max) stack_max=p_level+1; }
void alloc_call(int p_params) { if (p_params >= call_max) call_max=p_params; }
int current_line;
int stack_max;
int call_max;
};
#if 0
void _create_index(const GDParser::OperatorNode *on);
void _create_call(const GDParser::OperatorNode *on);
int _parse_expression(const GDParser::Node *p_expr,CodeGen& codegen);
void _parse_block(GDParser::BlockNode *p_block);
void _parse_function(GDParser::FunctionNode *p_func);
Ref<GDScript> _parse_class(GDParser::ClassNode *p_class);
#endif
void _set_error(const String& p_error,const GDParser::Node *p_node);
bool _create_unary_operator(CodeGen& codegen,const GDParser::OperatorNode *on,Variant::Operator op, int p_stack_level);
bool _create_binary_operator(CodeGen& codegen,const GDParser::OperatorNode *on,Variant::Operator op, int p_stack_level);
//int _parse_subexpression(CodeGen& codegen,const GDParser::BlockNode *p_block,const GDParser::Node *p_expression);
int _parse_assign_right_expression(CodeGen& codegen,const GDParser::OperatorNode *p_expression, int p_stack_level);
int _parse_expression(CodeGen& codegen,const GDParser::Node *p_expression, int p_stack_level,bool p_root=false);
Error _parse_block(CodeGen& codegen,const GDParser::BlockNode *p_block,int p_stack_level=0,int p_break_addr=-1,int p_continue_addr=-1);
Error _parse_function(GDScript *p_script,const GDParser::ClassNode *p_class,const GDParser::FunctionNode *p_func);
Error _parse_class(GDScript *p_script,GDScript *p_owner,const GDParser::ClassNode *p_class);
int err_line;
int err_column;
StringName source;
String error;
public:
Error compile(const GDParser *p_parser,GDScript *p_script);
String get_error() const;
int get_error_line() const;
int get_error_column() const;
GDCompiler();
};
#endif // COMPILER_H

View File

@@ -0,0 +1,789 @@
/*************************************************************************/
/* gd_editor.cpp */
/*************************************************************************/
/* This file is part of: */
/* GODOT ENGINE */
/* http://www.godotengine.org */
/*************************************************************************/
/* Copyright (c) 2007-2014 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 */
/* "Software"), to deal in the Software without restriction, including */
/* without limitation the rights to use, copy, modify, merge, publish, */
/* distribute, sublicense, and/or sell copies of the Software, and to */
/* permit persons to whom the Software is furnished to do so, subject to */
/* the following conditions: */
/* */
/* The above copyright notice and this permission notice shall be */
/* included in all copies or substantial portions of the Software. */
/* */
/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */
/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */
/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.*/
/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */
/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */
/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */
/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */
/*************************************************************************/
#include "gd_script.h"
#include "gd_compiler.h"
void GDScriptLanguage::get_comment_delimiters(List<String> *p_delimiters) const {
p_delimiters->push_back("#");
}
void GDScriptLanguage::get_string_delimiters(List<String> *p_delimiters) const {
p_delimiters->push_back("\" \"");
p_delimiters->push_back("' '");
}
String GDScriptLanguage::get_template(const String& p_class_name, const String& p_base_class_name) const {
String _template = String()+
"\nextends %BASE%\n\n"+
"# member variables here, example:\n"+
"# var a=2\n"+
"# var b=\"textvar\"\n\n"+
"func _ready():\n"+
"\t# Initalization here\n"+
"\tpass\n"+
"\n"+
"\n";
return _template.replace("%BASE%",p_base_class_name);
}
bool GDScriptLanguage::validate(const String& p_script, int &r_line_error,int &r_col_error,String& r_test_error, const String& p_path,List<String> *r_functions) const {
GDParser parser;
Error err = parser.parse(p_script,p_path.get_base_dir());
if (err) {
r_line_error=parser.get_error_line();
r_col_error=parser.get_error_column();
r_test_error=parser.get_error();
return false;
} else {
const GDParser::Node *root = parser.get_parse_tree();
ERR_FAIL_COND_V(root->type!=GDParser::Node::TYPE_CLASS,false);
const GDParser::ClassNode *cl = static_cast<const GDParser::ClassNode*>(root);
Map<int,String> funcs;
for(int i=0;i<cl->functions.size();i++) {
funcs[cl->functions[i]->line]=cl->functions[i]->name;
}
for(int i=0;i<cl->static_functions.size();i++) {
funcs[cl->static_functions[i]->line]=cl->static_functions[i]->name;
}
for (Map<int,String>::Element *E=funcs.front();E;E=E->next()) {
r_functions->push_back(E->get()+":"+itos(E->key()));
}
}
return true;
}
bool GDScriptLanguage::has_named_classes() const {
return false;
}
int GDScriptLanguage::find_function(const String& p_function,const String& p_code) const {
GDTokenizer tokenizer;
tokenizer.set_code(p_code);
int indent=0;
while(tokenizer.get_token()!=GDTokenizer::TK_EOF && tokenizer.get_token()!=GDTokenizer::TK_ERROR) {
if (tokenizer.get_token()==GDTokenizer::TK_NEWLINE) {
indent=tokenizer.get_token_line_indent();
}
if (indent==0 && tokenizer.get_token()==GDTokenizer::TK_PR_FUNCTION && tokenizer.get_token(1)==GDTokenizer::TK_IDENTIFIER) {
String identifier = tokenizer.get_token_identifier(1);
if (identifier==p_function) {
return tokenizer.get_token_line();
}
}
tokenizer.advance();
}
return -1;
}
Script *GDScriptLanguage::create_script() const {
return memnew( GDScript );
}
/* DEBUGGER FUNCTIONS */
bool GDScriptLanguage::debug_break_parse(const String& p_file, int p_line,const String& p_error) {
//break because of parse error
if (ScriptDebugger::get_singleton() && Thread::get_caller_ID()==Thread::get_main_ID()) {
_debug_parse_err_line=p_line;
_debug_parse_err_file=p_file;
_debug_error=p_error;
ScriptDebugger::get_singleton()->debug(this,false);
return true;
} else {
return false;
}
}
bool GDScriptLanguage::debug_break(const String& p_error,bool p_allow_continue) {
if (ScriptDebugger::get_singleton() && Thread::get_caller_ID()==Thread::get_main_ID()) {
_debug_parse_err_line=-1;
_debug_parse_err_file="";
_debug_error=p_error;
ScriptDebugger::get_singleton()->debug(this,p_allow_continue);
return true;
} else {
return false;
}
}
String GDScriptLanguage::debug_get_error() const {
return _debug_error;
}
int GDScriptLanguage::debug_get_stack_level_count() const {
if (_debug_parse_err_line>=0)
return 1;
return _debug_call_stack_pos;
}
int GDScriptLanguage::debug_get_stack_level_line(int p_level) const {
if (_debug_parse_err_line>=0)
return _debug_parse_err_line;
ERR_FAIL_INDEX_V(p_level,_debug_call_stack_pos,-1);
int l = _debug_call_stack_pos - p_level -1;
return *(_call_stack[l].line);
}
String GDScriptLanguage::debug_get_stack_level_function(int p_level) const {
if (_debug_parse_err_line>=0)
return "";
ERR_FAIL_INDEX_V(p_level,_debug_call_stack_pos,"");
int l = _debug_call_stack_pos - p_level -1;
return _call_stack[l].function->get_name();
}
String GDScriptLanguage::debug_get_stack_level_source(int p_level) const {
if (_debug_parse_err_line>=0)
return _debug_parse_err_file;
ERR_FAIL_INDEX_V(p_level,_debug_call_stack_pos,"");
int l = _debug_call_stack_pos - p_level -1;
return _call_stack[l].function->get_script()->get_path();
}
void GDScriptLanguage::debug_get_stack_level_locals(int p_level,List<String> *p_locals, List<Variant> *p_values, int p_max_subitems,int p_max_depth) {
if (_debug_parse_err_line>=0)
return;
ERR_FAIL_INDEX(p_level,_debug_call_stack_pos);
int l = _debug_call_stack_pos - p_level -1;
GDFunction *f = _call_stack[l].function;
List<Pair<StringName,int> > locals;
f->debug_get_stack_member_state(*_call_stack[l].line,&locals);
for( List<Pair<StringName,int> >::Element *E = locals.front();E;E=E->next() ) {
p_locals->push_back(E->get().first);
p_values->push_back(_call_stack[l].stack[E->get().second]);
}
}
void GDScriptLanguage::debug_get_stack_level_members(int p_level,List<String> *p_members, List<Variant> *p_values, int p_max_subitems,int p_max_depth) {
if (_debug_parse_err_line>=0)
return;
ERR_FAIL_INDEX(p_level,_debug_call_stack_pos);
int l = _debug_call_stack_pos - p_level -1;
GDInstance *instance = _call_stack[l].instance;
if (!instance)
return;
Ref<GDScript> script = instance->get_script();
ERR_FAIL_COND( script.is_null() );
const Map<StringName,int>& mi = script->debug_get_member_indices();
for(const Map<StringName,int>::Element *E=mi.front();E;E=E->next()) {
p_members->push_back(E->key());
p_values->push_back( instance->debug_get_member_by_index(E->get()));
}
}
void GDScriptLanguage::debug_get_globals(List<String> *p_locals, List<Variant> *p_values, int p_max_subitems,int p_max_depth) {
//no globals are really reachable in gdscript
}
String GDScriptLanguage::debug_parse_stack_level_expression(int p_level,const String& p_expression,int p_max_subitems,int p_max_depth) {
if (_debug_parse_err_line>=0)
return "";
return "";
}
void GDScriptLanguage::get_recognized_extensions(List<String> *p_extensions) const {
p_extensions->push_back("gd");
}
void GDScriptLanguage::get_public_functions(List<MethodInfo> *p_functions) const {
for(int i=0;i<GDFunctions::FUNC_MAX;i++) {
p_functions->push_back(GDFunctions::get_info(GDFunctions::Function(i)));
}
}
void GDScriptLanguage::get_public_constants(List<Pair<String,Variant> > *p_constants) const {
Pair<String,Variant> pi;
pi.first="PI";
pi.second=Math_PI;
p_constants->push_back(pi);
}
String GDScriptLanguage::make_function(const String& p_class,const String& p_name,const StringArray& p_args) const {
String s="func "+p_name+"(";
if (p_args.size()) {
s+=" ";
for(int i=0;i<p_args.size();i++) {
if (i>0)
s+=", ";
s+=p_args[i];
}
s+=" ";
}
s+="):\n\tpass # replace with function body\n";
return s;
}
static void _parse_native_symbols(const StringName& p_native,bool p_static,List<String>* r_options) {
if (!p_static) {
List<MethodInfo> methods;
ObjectTypeDB::get_method_list(p_native,&methods);
for(List<MethodInfo>::Element *E=methods.front();E;E=E->next()) {
if (!E->get().name.begins_with("_")) {
r_options->push_back(E->get().name);
}
}
}
List<String> constants;
ObjectTypeDB::get_integer_constant_list(p_native,&constants);
for(List<String>::Element *E=constants.front();E;E=E->next()) {
r_options->push_back(E->get());
}
}
static bool _parse_script_symbols(const Ref<GDScript>& p_script,bool p_static,List<String>* r_options,List<String>::Element *p_indices);
static bool _parse_completion_variant(const Variant& p_var,List<String>* r_options,List<String>::Element *p_indices) {
if (p_indices) {
bool ok;
Variant si = p_var.get(p_indices->get(),&ok);
if (!ok)
return false;
return _parse_completion_variant(si,r_options,p_indices->next());
} else {
switch(p_var.get_type()) {
case Variant::DICTIONARY: {
Dictionary d=p_var;
List<Variant> vl;
d.get_key_list(&vl);
for (List<Variant>::Element *E=vl.front();E;E=E->next()) {
if (E->get().get_type()==Variant::STRING)
r_options->push_back(E->get());
}
List<MethodInfo> ml;
p_var.get_method_list(&ml);
for(List<MethodInfo>::Element *E=ml.front();E;E=E->next()) {
r_options->push_back(E->get().name);
}
} break;
case Variant::OBJECT: {
Object *o=p_var;
if (o) {
print_line("OBJECT: "+o->get_type());
if (p_var.is_ref() && o->cast_to<GDScript>()) {
Ref<GDScript> gds = p_var;
_parse_script_symbols(gds,true,r_options,NULL);
} else if (o->is_type("GDNativeClass")){
GDNativeClass *gnc = o->cast_to<GDNativeClass>();
_parse_native_symbols(gnc->get_name(),false,r_options);
} else {
print_line("REGULAR BLEND");
_parse_native_symbols(o->get_type(),false,r_options);
}
}
} break;
default: {
List<PropertyInfo> pi;
p_var.get_property_list(&pi);
for(List<PropertyInfo>::Element *E=pi.front();E;E=E->next()) {
r_options->push_back(E->get().name);
}
List<StringName> cl;
p_var.get_numeric_constants_for_type(p_var.get_type(),&cl);
for(List<StringName>::Element *E=cl.front();E;E=E->next()) {
r_options->push_back(E->get());
}
List<MethodInfo> ml;
p_var.get_method_list(&ml);
for(List<MethodInfo>::Element *E=ml.front();E;E=E->next()) {
r_options->push_back(E->get().name);
}
} break;
}
return true;
}
}
static void _parse_expression_node(const GDParser::Node *p_node,List<String>* r_options,List<String>::Element *p_indices) {
if (p_node->type==GDParser::Node::TYPE_CONSTANT) {
const GDParser::ConstantNode *cn=static_cast<const GDParser::ConstantNode *>(p_node);
_parse_completion_variant(cn->value,r_options,p_indices?p_indices->next():NULL);
} else if (p_node->type==GDParser::Node::TYPE_DICTIONARY) {
const GDParser::DictionaryNode *dn=static_cast<const GDParser::DictionaryNode*>(p_node);
for(int i=0;i<dn->elements.size();i++) {
if (dn->elements[i].key->type==GDParser::Node::TYPE_CONSTANT) {
const GDParser::ConstantNode *cn=static_cast<const GDParser::ConstantNode *>(dn->elements[i].key);
if (cn->value.get_type()==Variant::STRING) {
String str=cn->value;
if (p_indices) {
if (str==p_indices->get()) {
_parse_expression_node(dn->elements[i].value,r_options,p_indices->next());
return;
}
} else {
r_options->push_back(str);
}
}
}
}
}
}
static bool _parse_completion_block(const GDParser::BlockNode *p_block,int p_line,List<String>* r_options,List<String>::Element *p_indices) {
for(int i=0;i<p_block->sub_blocks.size();i++) {
//parse inner first
if (p_line>=p_block->sub_blocks[i]->line && (p_line<=p_block->sub_blocks[i]->end_line || p_block->sub_blocks[i]->end_line==-1)) {
if (_parse_completion_block(p_block->sub_blocks[i],p_line,r_options,p_indices))
return true;
}
}
if (p_indices) {
//parse indices in expressions :|
for (int i=0;i<p_block->statements.size();i++) {
if (p_block->statements[i]->line>p_line)
break;
if (p_block->statements[i]->type==GDParser::BlockNode::TYPE_LOCAL_VAR) {
const GDParser::LocalVarNode *lv=static_cast<const GDParser::LocalVarNode *>(p_block->statements[i]);
if (lv->assign && String(lv->name)==p_indices->get()) {
_parse_expression_node(lv->assign,r_options,p_indices->next());
return true;
}
}
}
} else {
for(int i=0;i<p_block->variables.size();i++) {
//parse variables second
if (p_line>=p_block->variable_lines[i]) {
r_options->push_back(p_block->variables[i]);
}
else break;
}
}
return false;
}
static bool _parse_script_symbols(const Ref<GDScript>& p_script,bool p_static,List<String>* r_options,List<String>::Element *p_indices) {
//for (Map<StringName,Ref<GDScript> >::Element ?
if (!p_static && !p_indices) {
for(const Set<StringName>::Element *E=p_script->get_members().front();E;E=E->next()) {
r_options->push_back(E->get());
}
}
for (const Map<StringName,Variant >::Element *E=p_script->get_constants().front();E;E=E->next()) {
if( p_indices) {
if (p_indices->get()==String(E->get())) {
_parse_completion_variant(E->get(),r_options,p_indices->next());
return true;
}
} else {
r_options->push_back(E->key());
}
}
if (!p_indices){
for (const Map<StringName,GDFunction>::Element *E=p_script->get_member_functions().front();E;E=E->next()) {
if (E->get().is_static() || !p_static)
r_options->push_back(E->key());
}
}
if (p_script->get_base().is_valid()){
if (_parse_script_symbols(p_script->get_base(),p_static,r_options,p_indices))
return true;
} else if (p_script->get_native().is_valid() && !p_indices) {
_parse_native_symbols(p_script->get_native()->get_name(),p_static,r_options);
}
return false;
}
static bool _parse_completion_class(const String& p_base_path,const GDParser::ClassNode *p_class,int p_line,List<String>* r_options,List<String>::Element *p_indices) {
static const char*_type_names[Variant::VARIANT_MAX]={
"null","bool","int","float","String","Vector2","Rect2","Vector3","Matrix32","Plane","Quat","AABB","Matrix3","Trasnform",
"Color","Image","NodePath","RID","Object","InputEvent","Dictionary","Array","RawArray","IntArray","FloatArray","StringArray",
"Vector2Array","Vector3Array","ColorArray"};
if (p_indices && !p_indices->next()) {
for(int i=0;i<Variant::VARIANT_MAX;i++) {
if (p_indices->get()==_type_names[i]) {
List<StringName> ic;
Variant::get_numeric_constants_for_type(Variant::Type(i),&ic);
for(List<StringName>::Element *E=ic.front();E;E=E->next()) {
r_options->push_back(E->get());
}
return true;
}
}
}
for(int i=0;i<p_class->subclasses.size();i++) {
if (p_line>=p_class->subclasses[i]->line && (p_line<=p_class->subclasses[i]->end_line || p_class->subclasses[i]->end_line==-1)) {
if (_parse_completion_class(p_base_path,p_class->subclasses[i],p_line,r_options,p_indices))
return true;
}
}
bool in_static_func=false;
for(int i=0;i<p_class->functions.size();i++) {
const GDParser::FunctionNode *fu = p_class->functions[i];
if (p_line>=fu->body->line && (p_line<=fu->body->end_line || fu->body->end_line==-1)) {
//if in function, first block stuff from outer to inner
if (_parse_completion_block(fu->body,p_line,r_options,p_indices))
return true;
//then function arguments
if (!p_indices) {
for(int j=0;j<fu->arguments.size();j++) {
r_options->push_back(fu->arguments[j]);
}
}
}
}
for(int i=0;i<p_class->static_functions.size();i++) {
const GDParser::FunctionNode *fu = p_class->static_functions[i];
if (p_line>=fu->body->line && (p_line<=fu->body->end_line || fu->body->end_line==-1)) {
//if in function, first block stuff from outer to inne
if (_parse_completion_block(fu->body,p_line,r_options,p_indices))
return true;
//then function arguments
if (!p_indices) {
for(int j=0;j<fu->arguments.size();j++) {
r_options->push_back(fu->arguments[j]);
}
}
in_static_func=true;
}
}
//add all local names
if (!p_indices) {
if (!in_static_func) {
for(int i=0;i<p_class->variables.size();i++) {
r_options->push_back(p_class->variables[i].identifier);
}
}
for(int i=0;i<p_class->constant_expressions.size();i++) {
r_options->push_back(p_class->constant_expressions[i].identifier);
}
if (!in_static_func) {
for(int i=0;i<p_class->functions.size();i++) {
r_options->push_back(p_class->functions[i]->name);
}
}
for(int i=0;i<p_class->static_functions.size();i++) {
r_options->push_back(p_class->static_functions[i]->name);
}
}
if (p_class->extends_used) {
//do inheritance
String path = p_class->extends_file;
Ref<GDScript> script;
Ref<GDNativeClass> native;
if (path!="") {
//path (and optionally subclasses)
script = ResourceLoader::load(path);
if (script.is_null()) {
return false;
}
if (p_class->extends_class.size()) {
for(int i=0;i<p_class->extends_class.size();i++) {
String sub = p_class->extends_class[i];
if (script->get_subclasses().has(sub)) {
script=script->get_subclasses()[sub];
} else {
return false;
}
}
}
} else {
ERR_FAIL_COND_V(p_class->extends_class.size()==0,false);
//look around for the subclasses
String base=p_class->extends_class[0];
Ref<GDScript> base_class;
#if 0
while(p) {
if (p->subclasses.has(base)) {
base_class=p->subclasses[base];
break;
}
p=p->_owner;
}
if (base_class.is_valid()) {
for(int i=1;i<p_class->extends_class.size();i++) {
String subclass=p_class->extends_class[i];
if (base_class->subclasses.has(subclass)) {
base_class=base_class->subclasses[subclass];
} else {
_set_error("Could not find subclass: "+subclass,p_class);
return ERR_FILE_NOT_FOUND;
}
}
} else {
#endif
if (p_class->extends_class.size()>1) {
return false;
}
//if not found, try engine classes
if (!GDScriptLanguage::get_singleton()->get_global_map().has(base)) {
return false;
}
int base_idx = GDScriptLanguage::get_singleton()->get_global_map()[base];
native = GDScriptLanguage::get_singleton()->get_global_array()[base_idx];
if (!native.is_valid()) {
return false;
}
#if 0
}
#endif
}
if (script.is_valid()) {
if (_parse_script_symbols(script,in_static_func,r_options,p_indices))
return true;
} else if (native.is_valid() && !p_indices) {
_parse_native_symbols(native->get_name(),in_static_func,r_options);
}
}
return false;
}
Error GDScriptLanguage::complete_keyword(const String& p_code, int p_line, const String& p_base_path, const String& p_base, List<String>* r_options) {
GDParser p;
Error err = p.parse(p_code,p_base_path);
// don't care much about error I guess
const GDParser::Node* root = p.get_parse_tree();
ERR_FAIL_COND_V(root->type!=GDParser::Node::TYPE_CLASS,ERR_INVALID_DATA);
const GDParser::ClassNode *cl = static_cast<const GDParser::ClassNode*>(root);
List<String> indices;
Vector<String> spl = p_base.split(".");
for(int i=0;i<spl.size()-1;i++) {
indices.push_back(spl[i]);
}
if (_parse_completion_class(p_base,cl,p_line,r_options,indices.front()))
return OK;
//and the globals x_x?
for(Map<StringName,int>::Element *E=globals.front();E;E=E->next()) {
if (!indices.empty()) {
if (String(E->key())==indices.front()->get()) {
_parse_completion_variant(global_array[E->get()],r_options,indices.front()->next());
return OK;
}
} else {
r_options->push_back(E->key());
}
}
return OK;
}

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,103 @@
/*************************************************************************/
/* gd_functions.h */
/*************************************************************************/
/* This file is part of: */
/* GODOT ENGINE */
/* http://www.godotengine.org */
/*************************************************************************/
/* Copyright (c) 2007-2014 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 */
/* "Software"), to deal in the Software without restriction, including */
/* without limitation the rights to use, copy, modify, merge, publish, */
/* distribute, sublicense, and/or sell copies of the Software, and to */
/* permit persons to whom the Software is furnished to do so, subject to */
/* the following conditions: */
/* */
/* The above copyright notice and this permission notice shall be */
/* included in all copies or substantial portions of the Software. */
/* */
/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */
/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */
/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.*/
/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */
/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */
/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */
/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */
/*************************************************************************/
#ifndef GD_FUNCTIONS_H
#define GD_FUNCTIONS_H
#include "variant.h"
class GDFunctions {
public:
enum Function {
MATH_SIN,
MATH_COS,
MATH_TAN,
MATH_SINH,
MATH_COSH,
MATH_TANH,
MATH_ASIN,
MATH_ACOS,
MATH_ATAN,
MATH_ATAN2,
MATH_SQRT,
MATH_FMOD,
MATH_FPOSMOD,
MATH_FLOOR,
MATH_CEIL,
MATH_ROUND,
MATH_ABS,
MATH_SIGN,
MATH_POW,
MATH_LOG,
MATH_EXP,
MATH_ISNAN,
MATH_ISINF,
MATH_EASE,
MATH_DECIMALS,
MATH_STEPIFY,
MATH_LERP,
MATH_DECTIME,
MATH_RANDOMIZE,
MATH_RAND,
MATH_RANDF,
MATH_RANDOM,
MATH_RANDSEED,
MATH_DEG2RAD,
MATH_RAD2DEG,
MATH_LINEAR2DB,
MATH_DB2LINEAR,
LOGIC_MAX,
LOGIC_MIN,
LOGIC_CLAMP,
LOGIC_NEAREST_PO2,
OBJ_WEAKREF,
TYPE_CONVERT,
TYPE_OF,
TEXT_STR,
TEXT_PRINT,
TEXT_PRINT_TABBED,
TEXT_PRINTERR,
TEXT_PRINTRAW,
GEN_RANGE,
RESOURCE_LOAD,
INST2DICT,
DICT2INST,
PRINT_STACK,
FUNC_MAX
};
static const char *get_func_name(Function p_func);
static void call(Function p_func,const Variant **p_args,int p_arg_count,Variant &r_ret,Variant::CallError &r_error);
static bool is_deterministic(Function p_func);
static MethodInfo get_info(Function p_func);
};
#endif // GD_FUNCTIONS_H

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,397 @@
/*************************************************************************/
/* gd_parser.h */
/*************************************************************************/
/* This file is part of: */
/* GODOT ENGINE */
/* http://www.godotengine.org */
/*************************************************************************/
/* Copyright (c) 2007-2014 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 */
/* "Software"), to deal in the Software without restriction, including */
/* without limitation the rights to use, copy, modify, merge, publish, */
/* distribute, sublicense, and/or sell copies of the Software, and to */
/* permit persons to whom the Software is furnished to do so, subject to */
/* the following conditions: */
/* */
/* The above copyright notice and this permission notice shall be */
/* included in all copies or substantial portions of the Software. */
/* */
/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */
/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */
/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.*/
/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */
/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */
/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */
/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */
/*************************************************************************/
#ifndef GD_PARSER_H
#define GD_PARSER_H
#include "gd_tokenizer.h"
#include "gd_functions.h"
#include "map.h"
#include "object.h"
class GDParser {
public:
struct Node {
enum Type {
TYPE_CLASS,
TYPE_FUNCTION,
TYPE_BUILT_IN_FUNCTION,
TYPE_BLOCK,
TYPE_IDENTIFIER,
TYPE_TYPE,
TYPE_CONSTANT,
TYPE_ARRAY,
TYPE_DICTIONARY,
TYPE_SELF,
TYPE_OPERATOR,
TYPE_CONTROL_FLOW,
TYPE_LOCAL_VAR,
TYPE_ASSERT,
TYPE_NEWLINE,
};
Node * next;
int line;
int column;
Type type;
virtual ~Node() {}
};
struct FunctionNode;
struct BlockNode;
struct ClassNode : public Node {
bool tool;
StringName name;
bool extends_used;
StringName extends_file;
Vector<StringName> extends_class;
struct Member {
PropertyInfo _export;
#ifdef TOOLS_ENABLED
Variant default_value;
#endif
StringName identifier;
};
struct Constant {
StringName identifier;
Node *expression;
};
Vector<ClassNode*> subclasses;
Vector<Member> variables;
Vector<Constant> constant_expressions;
Vector<FunctionNode*> functions;
Vector<FunctionNode*> static_functions;
BlockNode *initializer;
//Vector<Node*> initializers;
int end_line;
ClassNode() { tool=false; type=TYPE_CLASS; extends_used=false; end_line=-1;}
};
struct FunctionNode : public Node {
bool _static;
StringName name;
Vector<StringName> arguments;
Vector<Node*> default_values;
BlockNode *body;
FunctionNode() { type=TYPE_FUNCTION; _static=false; }
};
struct BlockNode : public Node {
Map<StringName,int> locals;
List<Node*> statements;
Vector<StringName> variables;
Vector<int> variable_lines;
//the following is useful for code completion
List<BlockNode*> sub_blocks;
int end_line;
BlockNode() { type=TYPE_BLOCK; end_line=-1;}
};
struct TypeNode : public Node {
Variant::Type vtype;
TypeNode() { type=TYPE_TYPE; }
};
struct BuiltInFunctionNode : public Node {
GDFunctions::Function function;
BuiltInFunctionNode() { type=TYPE_BUILT_IN_FUNCTION; }
};
struct IdentifierNode : public Node {
StringName name;
IdentifierNode() { type=TYPE_IDENTIFIER; }
};
struct LocalVarNode : public Node {
StringName name;
Node *assign;
LocalVarNode() { type=TYPE_LOCAL_VAR; assign=NULL;}
};
struct ConstantNode : public Node {
Variant value;
ConstantNode() { type=TYPE_CONSTANT; }
};
struct ArrayNode : public Node {
Vector<Node*> elements;
ArrayNode() { type=TYPE_ARRAY; }
};
struct DictionaryNode : public Node {
struct Pair {
Node *key;
Node *value;
};
Vector<Pair> elements;
DictionaryNode() { type=TYPE_DICTIONARY; }
};
struct SelfNode : public Node {
SelfNode() { type=TYPE_SELF; }
};
struct OperatorNode : public Node {
enum Operator {
//call/constructor operator
OP_CALL,
OP_PARENT_CALL,
OP_EXTENDS,
//indexing operator
OP_INDEX,
OP_INDEX_NAMED,
//unary operators
OP_NEG,
OP_NOT,
OP_BIT_INVERT,
OP_PREINC,
OP_PREDEC,
OP_INC,
OP_DEC,
//binary operators (in precedence order)
OP_IN,
OP_EQUAL,
OP_NOT_EQUAL,
OP_LESS,
OP_LESS_EQUAL,
OP_GREATER,
OP_GREATER_EQUAL,
OP_AND,
OP_OR,
OP_ADD,
OP_SUB,
OP_MUL,
OP_DIV,
OP_MOD,
OP_SHIFT_LEFT,
OP_SHIFT_RIGHT,
OP_ASSIGN,
OP_ASSIGN_ADD,
OP_ASSIGN_SUB,
OP_ASSIGN_MUL,
OP_ASSIGN_DIV,
OP_ASSIGN_MOD,
OP_ASSIGN_SHIFT_LEFT,
OP_ASSIGN_SHIFT_RIGHT,
OP_ASSIGN_BIT_AND,
OP_ASSIGN_BIT_OR,
OP_ASSIGN_BIT_XOR,
OP_BIT_AND,
OP_BIT_OR,
OP_BIT_XOR
};
Operator op;
Vector<Node*> arguments;
OperatorNode() { type=TYPE_OPERATOR; }
};
struct ControlFlowNode : public Node {
enum CFType {
CF_IF,
CF_FOR,
CF_WHILE,
CF_SWITCH,
CF_BREAK,
CF_CONTINUE,
CF_RETURN
};
CFType cf_type;
Vector<Node*> arguments;
BlockNode *body;
BlockNode *body_else;
ControlFlowNode *_else; //used for if
ControlFlowNode() { type=TYPE_CONTROL_FLOW; cf_type=CF_IF; body=NULL; body_else=NULL;}
};
struct AssertNode : public Node {
Node* condition;
AssertNode() { type=TYPE_ASSERT; }
};
struct NewLineNode : public Node {
int line;
NewLineNode() { type=TYPE_NEWLINE; }
};
struct Expression {
bool is_op;
union {
OperatorNode::Operator op;
Node *node;
};
};
/*
struct OperatorNode : public Node {
DataType return_cache;
Operator op;
Vector<Node*> arguments;
virtual DataType get_datatype() const { return return_cache; }
OperatorNode() { type=TYPE_OPERATOR; return_cache=TYPE_VOID; }
};
struct VariableNode : public Node {
DataType datatype_cache;
StringName name;
virtual DataType get_datatype() const { return datatype_cache; }
VariableNode() { type=TYPE_VARIABLE; datatype_cache=TYPE_VOID; }
};
struct ConstantNode : public Node {
DataType datatype;
Variant value;
virtual DataType get_datatype() const { return datatype; }
ConstantNode() { type=TYPE_CONSTANT; }
};
struct BlockNode : public Node {
Map<StringName,DataType> variables;
List<Node*> statements;
BlockNode() { type=TYPE_BLOCK; }
};
struct ControlFlowNode : public Node {
FlowOperation flow_op;
Vector<Node*> statements;
ControlFlowNode() { type=TYPE_CONTROL_FLOW; flow_op=FLOW_OP_IF;}
};
struct MemberNode : public Node {
DataType datatype;
StringName name;
Node* owner;
virtual DataType get_datatype() const { return datatype; }
MemberNode() { type=TYPE_MEMBER; }
};
struct ProgramNode : public Node {
struct Function {
StringName name;
FunctionNode*function;
};
Map<StringName,DataType> builtin_variables;
Map<StringName,DataType> preexisting_variables;
Vector<Function> functions;
BlockNode *body;
ProgramNode() { type=TYPE_PROGRAM; }
};
*/
private:
GDTokenizer tokenizer;
Node *head;
Node *list;
template<class T>
T* alloc_node();
bool error_set;
String error;
int error_line;
int error_column;
List<int> tab_level;
String base_path;
PropertyInfo current_export;
void _set_error(const String& p_error, int p_line=-1, int p_column=-1);
bool _parse_arguments(Node* p_parent,Vector<Node*>& p_args,bool p_static);
bool _enter_indent_block(BlockNode *p_block=NULL);
bool _parse_newline();
Node* _parse_expression(Node *p_parent,bool p_static,bool p_allow_assign=false);
Node* _reduce_expression(Node *p_node,bool p_to_const=false);
Node* _parse_and_reduce_expression(Node *p_parent,bool p_static,bool p_reduce_const=false,bool p_allow_assign=false);
void _parse_block(BlockNode *p_block,bool p_static);
void _parse_extends(ClassNode *p_class);
void _parse_class(ClassNode *p_class);
bool _end_statement();
public:
String get_error() const;
int get_error_line() const;
int get_error_column() const;
Error parse(const String& p_code,const String& p_base_path="");
const Node *get_parse_tree() const;
void clear();
GDParser();
~GDParser();
};
#endif // PARSER_H

View File

@@ -0,0 +1,34 @@
/*************************************************************************/
/* gd_pretty_print.cpp */
/*************************************************************************/
/* This file is part of: */
/* GODOT ENGINE */
/* http://www.godotengine.org */
/*************************************************************************/
/* Copyright (c) 2007-2014 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 */
/* "Software"), to deal in the Software without restriction, including */
/* without limitation the rights to use, copy, modify, merge, publish, */
/* distribute, sublicense, and/or sell copies of the Software, and to */
/* permit persons to whom the Software is furnished to do so, subject to */
/* the following conditions: */
/* */
/* The above copyright notice and this permission notice shall be */
/* included in all copies or substantial portions of the Software. */
/* */
/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */
/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */
/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.*/
/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */
/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */
/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */
/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */
/*************************************************************************/
#include "gd_pretty_print.h"
GDPrettyPrint::GDPrettyPrint() {
}

View File

@@ -0,0 +1,40 @@
/*************************************************************************/
/* gd_pretty_print.h */
/*************************************************************************/
/* This file is part of: */
/* GODOT ENGINE */
/* http://www.godotengine.org */
/*************************************************************************/
/* Copyright (c) 2007-2014 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 */
/* "Software"), to deal in the Software without restriction, including */
/* without limitation the rights to use, copy, modify, merge, publish, */
/* distribute, sublicense, and/or sell copies of the Software, and to */
/* permit persons to whom the Software is furnished to do so, subject to */
/* the following conditions: */
/* */
/* The above copyright notice and this permission notice shall be */
/* included in all copies or substantial portions of the Software. */
/* */
/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */
/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */
/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.*/
/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */
/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */
/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */
/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */
/*************************************************************************/
#ifndef GD_PRETTY_PRINT_H
#define GD_PRETTY_PRINT_H
class GDPrettyPrint {
public:
GDPrettyPrint();
};
#endif // GD_PRETTY_PRINT_H

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,473 @@
/*************************************************************************/
/* gd_script.h */
/*************************************************************************/
/* This file is part of: */
/* GODOT ENGINE */
/* http://www.godotengine.org */
/*************************************************************************/
/* Copyright (c) 2007-2014 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 */
/* "Software"), to deal in the Software without restriction, including */
/* without limitation the rights to use, copy, modify, merge, publish, */
/* distribute, sublicense, and/or sell copies of the Software, and to */
/* permit persons to whom the Software is furnished to do so, subject to */
/* the following conditions: */
/* */
/* The above copyright notice and this permission notice shall be */
/* included in all copies or substantial portions of the Software. */
/* */
/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */
/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */
/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.*/
/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */
/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */
/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */
/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */
/*************************************************************************/
#ifndef GD_SCRIPT_H
#define GD_SCRIPT_H
#include "script_language.h"
#include "io/resource_loader.h"
#include "io/resource_saver.h"
#include "os/thread.h"
#include "pair.h"
class GDInstance;
class GDScript;
class GDFunction {
public:
enum Opcode {
OPCODE_OPERATOR,
OPCODE_EXTENDS_TEST,
OPCODE_SET,
OPCODE_GET,
OPCODE_SET_NAMED,
OPCODE_GET_NAMED,
OPCODE_ASSIGN,
OPCODE_ASSIGN_TRUE,
OPCODE_ASSIGN_FALSE,
OPCODE_CONSTRUCT, //only for basic types!!
OPCODE_CONSTRUCT_ARRAY,
OPCODE_CONSTRUCT_DICTIONARY,
OPCODE_CALL,
OPCODE_CALL_RETURN,
OPCODE_CALL_BUILT_IN,
OPCODE_CALL_SELF,
OPCODE_CALL_SELF_BASE,
OPCODE_JUMP,
OPCODE_JUMP_IF,
OPCODE_JUMP_IF_NOT,
OPCODE_JUMP_TO_DEF_ARGUMENT,
OPCODE_RETURN,
OPCODE_ITERATE_BEGIN,
OPCODE_ITERATE,
OPCODE_ASSERT,
OPCODE_LINE,
OPCODE_END
};
enum Address {
ADDR_BITS=24,
ADDR_MASK=((1<<ADDR_BITS)-1),
ADDR_TYPE_MASK=~ADDR_MASK,
ADDR_TYPE_SELF=0,
ADDR_TYPE_MEMBER=1,
ADDR_TYPE_CLASS_CONSTANT=2,
ADDR_TYPE_LOCAL_CONSTANT=3,
ADDR_TYPE_STACK=4,
ADDR_TYPE_STACK_VARIABLE=5,
ADDR_TYPE_GLOBAL=6,
ADDR_TYPE_NIL=7
};
struct StackDebug {
int line;
int pos;
bool added;
StringName identifier;
};
private:
friend class GDCompiler;
StringName source;
mutable Variant nil;
mutable Variant *_constants_ptr;
int _constant_count;
const StringName *_global_names_ptr;
int _global_names_count;
const int *_default_arg_ptr;
int _default_arg_count;
const int *_code_ptr;
int _code_size;
int _argument_count;
int _stack_size;
int _call_size;
int _initial_line;
bool _static;
GDScript *_script;
StringName name;
Vector<Variant> constants;
Vector<StringName> global_names;
Vector<int> default_arguments;
Vector<int> code;
List<StackDebug> stack_debug;
_FORCE_INLINE_ Variant *_get_variant(int p_address,GDInstance *p_instance,GDScript *p_script,Variant &self,Variant *p_stack,String& r_error) const;
_FORCE_INLINE_ String _get_call_error(const Variant::CallError& p_err, const String& p_where,const Variant**argptrs) const;
public:
_FORCE_INLINE_ bool is_static() const { return _static; }
const int* get_code() const; //used for debug
int get_code_size() const;
Variant get_constant(int p_idx) const;
StringName get_global_name(int p_idx) const;
StringName get_name() const;
int get_max_stack_size() const;
int get_default_argument_count() const;
int get_default_argument_addr(int p_idx) const;
GDScript *get_script() const { return _script; }
void debug_get_stack_member_state(int p_line,List<Pair<StringName,int> > *r_stackvars) const;
_FORCE_INLINE_ bool is_empty() const { return _code_size==0; }
int get_argument_count() const { return _argument_count; }
Variant call(GDInstance *p_instance,const Variant **p_args, int p_argcount,Variant::CallError& r_err);
GDFunction();
};
class GDNativeClass : public Reference {
OBJ_TYPE(GDNativeClass,Reference);
StringName name;
protected:
bool _get(const StringName& p_name,Variant &r_ret) const;
static void _bind_methods();
public:
_FORCE_INLINE_ const StringName& get_name() const { return name; }
Variant _new();
Object *instance();
GDNativeClass(const StringName& p_name);
};
class GDScript : public Script {
OBJ_TYPE(GDScript,Script);
bool tool;
bool valid;
friend class GDInstance;
friend class GDFunction;
friend class GDCompiler;
friend class GDFunctions;
Ref<GDNativeClass> native;
Ref<GDScript> base;
GDScript *_base; //fast pointer access
GDScript *_owner; //for subclasses
Set<StringName> members; //members are just indices to the instanced script.
Map<StringName,Variant> constants;
Map<StringName,GDFunction> member_functions;
Map<StringName,int> member_indices; //members are just indices to the instanced script.
Map<StringName,Ref<GDScript> > subclasses;
#ifdef TOOLS_ENABLED
Map<StringName,Variant> member_default_values;
#endif
Map<StringName,PropertyInfo> member_info;
GDFunction *initializer; //direct pointer to _init , faster to locate
int subclass_count;
Set<Object*> instances;
//exported members
String source;
String path;
String name;
GDInstance* _create_instance(const Variant** p_args,int p_argcount,Object *p_owner,bool p_isref);
void _set_subclass_path(Ref<GDScript>& p_sc,const String& p_path);
#ifdef TOOLS_ENABLED
Set<PlaceHolderScriptInstance*> placeholders;
void _update_placeholder(PlaceHolderScriptInstance *p_placeholder);
virtual void _placeholder_erased(PlaceHolderScriptInstance *p_placeholder);
#endif
protected:
bool _get(const StringName& p_name,Variant &r_ret) const;
bool _set(const StringName& p_name, const Variant& p_value);
void _get_property_list(List<PropertyInfo> *p_properties) const;
Variant call(const StringName& p_method,const Variant** p_args,int p_argcount,Variant::CallError &r_error);
// void call_multilevel(const StringName& p_method,const Variant** p_args,int p_argcount);
static void _bind_methods();
public:
const Map<StringName,Ref<GDScript> >& get_subclasses() const { return subclasses; }
const Map<StringName,Variant >& get_constants() const { return constants; }
const Set<StringName>& get_members() const { return members; }
const Map<StringName,GDFunction>& get_member_functions() const { return member_functions; }
const Ref<GDNativeClass>& get_native() const { return native; }
bool is_tool() const { return tool; }
Ref<GDScript> get_base() const;
const Map<StringName,int>& debug_get_member_indices() const { return member_indices; }
const Map<StringName,GDFunction>& debug_get_member_functions() const; //this is debug only
StringName debug_get_member_by_index(int p_idx) const;
Variant _new(const Variant** p_args,int p_argcount,Variant::CallError& r_error);
virtual bool can_instance() const;
virtual StringName get_instance_base_type() const; // this may not work in all scripts, will return empty if so
virtual ScriptInstance* instance_create(Object *p_this);
virtual bool instance_has(const Object *p_this) const;
virtual bool has_source_code() const;
virtual String get_source_code() const;
virtual void set_source_code(const String& p_code);
virtual Error reload();
virtual String get_node_type() const;
void set_script_path(const String& p_path) { path=p_path; } //because subclasses need a path too...
Error load_source_code(const String& p_path);
virtual ScriptLanguage *get_language() const;
GDScript();
};
class GDInstance : public ScriptInstance {
friend class GDScript;
friend class GDFunction;
friend class GDFunctions;
Object *owner;
Ref<GDScript> script;
Vector<Variant> members;
bool base_ref;
void _ml_call_reversed(GDScript *sptr,const StringName& p_method,const Variant** p_args,int p_argcount);
public:
virtual bool set(const StringName& p_name, const Variant& p_value);
virtual bool get(const StringName& p_name, Variant &r_ret) const;
virtual void get_property_list(List<PropertyInfo> *p_properties) const;
virtual void get_method_list(List<MethodInfo> *p_list) const;
virtual bool has_method(const StringName& p_method) const;
virtual Variant call(const StringName& p_method,const Variant** p_args,int p_argcount,Variant::CallError &r_error);
virtual void call_multilevel(const StringName& p_method,const Variant** p_args,int p_argcount);
virtual void call_multilevel_reversed(const StringName& p_method,const Variant** p_args,int p_argcount);
Variant debug_get_member_by_index(int p_idx) const { return members[p_idx]; }
virtual void notification(int p_notification);
virtual Ref<Script> get_script() const;
virtual ScriptLanguage *get_language();
void set_path(const String& p_path);
GDInstance();
~GDInstance();
};
class GDScriptLanguage : public ScriptLanguage {
static GDScriptLanguage *singleton;
Variant* _global_array;
Vector<Variant> global_array;
Map<StringName,int> globals;
struct CallLevel {
Variant *stack;
GDFunction *function;
GDInstance *instance;
int *ip;
int *line;
};
int _debug_parse_err_line;
String _debug_parse_err_file;
String _debug_error;
int _debug_call_stack_pos;
int _debug_max_call_stack;
CallLevel *_call_stack;
void _add_global(const StringName& p_name,const Variant& p_value);
public:
int calls;
bool debug_break(const String& p_error,bool p_allow_continue=true);
bool debug_break_parse(const String& p_file, int p_line,const String& p_error);
_FORCE_INLINE_ void enter_function(GDInstance *p_instance,GDFunction *p_function, Variant *p_stack, int *p_ip, int *p_line) {
if (Thread::get_main_ID()!=Thread::get_caller_ID())
return; //no support for other threads than main for now
if (ScriptDebugger::get_singleton()->get_lines_left()>0 && ScriptDebugger::get_singleton()->get_depth()>=0)
ScriptDebugger::get_singleton()->set_depth( ScriptDebugger::get_singleton()->get_depth() +1 );
if (_debug_call_stack_pos >= _debug_max_call_stack) {
//stack overflow
_debug_error="Stack Overflow (Stack Size: "+itos(_debug_max_call_stack)+")";
ScriptDebugger::get_singleton()->debug(this);
return;
}
_call_stack[_debug_call_stack_pos].stack=p_stack;
_call_stack[_debug_call_stack_pos].instance=p_instance;
_call_stack[_debug_call_stack_pos].function=p_function;
_call_stack[_debug_call_stack_pos].ip=p_ip;
_call_stack[_debug_call_stack_pos].line=p_line;
_debug_call_stack_pos++;
}
_FORCE_INLINE_ void exit_function() {
if (Thread::get_main_ID()!=Thread::get_caller_ID())
return; //no support for other threads than main for now
if (ScriptDebugger::get_singleton()->get_lines_left()>0 && ScriptDebugger::get_singleton()->get_depth()>=0)
ScriptDebugger::get_singleton()->set_depth( ScriptDebugger::get_singleton()->get_depth() -1 );
if (_debug_call_stack_pos==0) {
_debug_error="Stack Underflow (Engine Bug)";
ScriptDebugger::get_singleton()->debug(this);
return;
}
_debug_call_stack_pos--;
}
struct {
StringName _init;
StringName _notification;
StringName _set;
StringName _get;
StringName _get_property_list;
StringName _script_source;
} strings;
_FORCE_INLINE_ int get_global_array_size() const { return global_array.size(); }
_FORCE_INLINE_ Variant* get_global_array() { return _global_array; }
_FORCE_INLINE_ const Map<StringName,int>& get_global_map() { return globals; }
_FORCE_INLINE_ static GDScriptLanguage *get_singleton() { return singleton; }
virtual String get_name() const;
/* LANGUAGE FUNCTIONS */
virtual void init();
virtual String get_type() const;
virtual String get_extension() const;
virtual Error execute_file(const String& p_path) ;
virtual void finish();
/* EDITOR FUNCTIONS */
virtual void get_reserved_words(List<String> *p_words) const;
virtual void get_comment_delimiters(List<String> *p_delimiters) const;
virtual void get_string_delimiters(List<String> *p_delimiters) const;
virtual String get_template(const String& p_class_name, const String& p_base_class_name) const;
virtual bool validate(const String& p_script,int &r_line_error,int &r_col_error,String& r_test_error, const String& p_path="",List<String> *r_functions=NULL) const;
virtual Script *create_script() const;
virtual bool has_named_classes() const;
virtual int find_function(const String& p_function,const String& p_code) const;
virtual String make_function(const String& p_class,const String& p_name,const StringArray& p_args) const;
virtual Error complete_keyword(const String& p_code, int p_line, const String& p_base_path,const String& p_keyword, List<String>* r_options);
/* DEBUGGER FUNCTIONS */
virtual String debug_get_error() const;
virtual int debug_get_stack_level_count() const;
virtual int debug_get_stack_level_line(int p_level) const;
virtual String debug_get_stack_level_function(int p_level) const;
virtual String debug_get_stack_level_source(int p_level) const;
virtual void debug_get_stack_level_locals(int p_level,List<String> *p_locals, List<Variant> *p_values, int p_max_subitems=-1,int p_max_depth=-1);
virtual void debug_get_stack_level_members(int p_level,List<String> *p_members, List<Variant> *p_values, int p_max_subitems=-1,int p_max_depth=-1);
virtual void debug_get_globals(List<String> *p_locals, List<Variant> *p_values, int p_max_subitems=-1,int p_max_depth=-1);
virtual String debug_parse_stack_level_expression(int p_level,const String& p_expression,int p_max_subitems=-1,int p_max_depth=-1);
virtual void frame();
virtual void get_public_functions(List<MethodInfo> *p_functions) const;
virtual void get_public_constants(List<Pair<String,Variant> > *p_constants) const;
/* LOADER FUNCTIONS */
virtual void get_recognized_extensions(List<String> *p_extensions) const;
GDScriptLanguage();
~GDScriptLanguage();
};
class ResourceFormatLoaderGDScript : public ResourceFormatLoader {
public:
virtual RES load(const String &p_path,const String& p_original_path="");
virtual void get_recognized_extensions(List<String> *p_extensions) const;
virtual bool handles_type(const String& p_type) const;
virtual String get_resource_type(const String &p_path) const;
};
class ResourceFormatSaverGDScript : public ResourceFormatSaver {
public:
virtual Error save(const String &p_path,const RES& p_resource,uint32_t p_flags=0);
virtual void get_recognized_extensions(const RES& p_resource,List<String> *p_extensions) const;
virtual bool recognize(const RES& p_resource) const;
};
#endif // GD_SCRIPT_H

View File

@@ -0,0 +1,973 @@
/*************************************************************************/
/* gd_tokenizer.cpp */
/*************************************************************************/
/* This file is part of: */
/* GODOT ENGINE */
/* http://www.godotengine.org */
/*************************************************************************/
/* Copyright (c) 2007-2014 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 */
/* "Software"), to deal in the Software without restriction, including */
/* without limitation the rights to use, copy, modify, merge, publish, */
/* distribute, sublicense, and/or sell copies of the Software, and to */
/* permit persons to whom the Software is furnished to do so, subject to */
/* the following conditions: */
/* */
/* The above copyright notice and this permission notice shall be */
/* included in all copies or substantial portions of the Software. */
/* */
/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */
/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */
/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.*/
/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */
/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */
/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */
/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */
/*************************************************************************/
#include "gd_tokenizer.h"
#include "print_string.h"
#include "gd_functions.h"
const char* GDTokenizer::token_names[TK_MAX]={
"Empty",
"Identifier",
"Constant",
"Self",
"Built-In Type",
"Built-In Func",
"In",
"'=='",
"'!='",
"'<'",
"'<='",
"'>'",
"'>='",
"'and'",
"'or'",
"'not'",
"'+'",
"'-'",
"'*'",
"'/'",
"'%'",
"'<<'",
"'>>'",
"'='",
"'+='",
"'-='",
"'*='",
"'/='",
"'%='",
"'<<='",
"'>>='",
"'&='",
"'|='",
"'^='",
"'&'",
"'|'",
"'^'",
"'~'",
//"Plus Plus",
//"Minus Minus",
"if",
"elif",
"else",
"for",
"do",
"while",
"switch",
"case",
"break",
"continue",
"pass",
"return",
"func",
"class",
"extends",
"tool",
"static",
"export",
"const",
"var",
"preload",
"assert",
"'['",
"']'",
"'{'",
"'}'",
"'('",
"')'",
"','",
"';'",
"'.'",
"'?'",
"':'",
"'\\n'",
"Error",
"EOF"};
const char *GDTokenizer::get_token_name(Token p_token) {
ERR_FAIL_INDEX_V(p_token,TK_MAX,"<error>");
return token_names[p_token];
}
static bool _is_text_char(CharType c) {
return (c>='a' && c<='z') || (c>='A' && c<='Z') || (c>='0' && c<='9') || c=='_';
}
static bool _is_number(CharType c) {
return (c>='0' && c<='9');
}
static bool _is_hex(CharType c) {
return (c>='0' && c<='9') || (c>='a' && c<='f') || (c>='A' && c<='F');
}
void GDTokenizer::_make_token(Token p_type) {
TokenData &tk=tk_rb[tk_rb_pos];
tk.type=p_type;
tk.line=line;
tk.col=column;
tk_rb_pos=(tk_rb_pos+1)%TK_RB_SIZE;
}
void GDTokenizer::_make_identifier(const StringName& p_identifier) {
TokenData &tk=tk_rb[tk_rb_pos];
tk.type=TK_IDENTIFIER;
tk.identifier=p_identifier;
tk.line=line;
tk.col=column;
tk_rb_pos=(tk_rb_pos+1)%TK_RB_SIZE;
}
void GDTokenizer::_make_built_in_func(GDFunctions::Function p_func) {
TokenData &tk=tk_rb[tk_rb_pos];
tk.type=TK_BUILT_IN_FUNC;
tk.func=p_func;
tk.line=line;
tk.col=column;
tk_rb_pos=(tk_rb_pos+1)%TK_RB_SIZE;
}
void GDTokenizer::_make_constant(const Variant& p_constant) {
TokenData &tk=tk_rb[tk_rb_pos];
tk.type=TK_CONSTANT;
tk.constant=p_constant;
tk.line=line;
tk.col=column;
tk_rb_pos=(tk_rb_pos+1)%TK_RB_SIZE;
}
void GDTokenizer::_make_type(const Variant::Type& p_type) {
TokenData &tk=tk_rb[tk_rb_pos];
tk.type=TK_BUILT_IN_TYPE;
tk.vtype=p_type;
tk.line=line;
tk.col=column;
tk_rb_pos=(tk_rb_pos+1)%TK_RB_SIZE;
}
void GDTokenizer::_make_error(const String& p_error) {
error_flag=true;
last_error=p_error;
TokenData &tk=tk_rb[tk_rb_pos];
tk.type=TK_ERROR;
tk.constant=p_error;
tk.line=line;
tk.col=column;
tk_rb_pos=(tk_rb_pos+1)%TK_RB_SIZE;
}
void GDTokenizer::_make_newline(int p_spaces) {
TokenData &tk=tk_rb[tk_rb_pos];
tk.type=TK_NEWLINE;
tk.constant=p_spaces;
tk.line=line;
tk.col=column;
tk_rb_pos=(tk_rb_pos+1)%TK_RB_SIZE;
}
void GDTokenizer::_advance() {
if (error_flag) {
//parser broke
_make_error(last_error);
return;
}
if (code_pos>=len) {
_make_token(TK_EOF);
return;
}
#define GETCHAR(m_ofs) ((m_ofs+code_pos)>=len?0:_code[m_ofs+code_pos])
#define INCPOS(m_amount) { code_pos+=m_amount; column+=m_amount; }
while (true) {
bool is_node_path=false;
switch(GETCHAR(0)) {
case 0:
_make_token(TK_EOF);
break;
case '\t':
case '\r':
case ' ':
INCPOS(1);
continue;
case '\n': {
line++;
INCPOS(1);
column=0;
int i=0;
while(GETCHAR(i)==' ' || GETCHAR(i)=='\t') {
i++;
}
_make_newline(i);
return;
}
#if 1 //py style tokenizer
case '#': { // line comment skip
while(GETCHAR(0)!='\n') {
code_pos++;
if (GETCHAR(0)==0) { //end of file
_make_error("Unterminated Comment");
return;
}
}
INCPOS(1);
column=0;
line++;
int i=0;
while(GETCHAR(i)==' ' || GETCHAR(i)=='\t') {
i++;
}
_make_newline(i);
return;
} break;
#endif
case '/': {
switch(GETCHAR(1)) {
#if 0 // c style tokenizer
case '*': { // block comment
int pos = code_pos+2;
int new_line=line;
int new_col=column+2;
while(true) {
if (_code[pos]=='0') {
_make_error("Unterminated Comment");
code_pos=pos;
return;
}
if (_code[pos]=='*' && _code[pos+1]=='/') {
new_col+=2;
pos+=2; //compensate
break;
} else if (_code[pos]=='\n') {
new_line++;
new_col=0;
} else {
new_col++;
}
pos++;
}
column=new_col;
line=new_line;
code_pos=pos;
continue;
} break;
case '/': { // line comment skip
while(GETCHAR(0)!='\n') {
code_pos++;
if (GETCHAR(0)==0) { //end of file
_make_error("Unterminated Comment");
return;
}
}
INCPOS(1);
column=0;
line++;
continue;
} break;
#endif
case '=': { // diveq
_make_token(TK_OP_ASSIGN_DIV);
INCPOS(1);
} break;
default:
_make_token(TK_OP_DIV);
}
} break;
case '=': {
if (GETCHAR(1)=='=') {
_make_token(TK_OP_EQUAL);
INCPOS(1);
} else
_make_token(TK_OP_ASSIGN);
} break;
case '<': {
if (GETCHAR(1)=='=') {
_make_token(TK_OP_LESS_EQUAL);
INCPOS(1);
} else if (GETCHAR(1)=='<') {
if (GETCHAR(2)=='=') {
_make_token(TK_OP_ASSIGN_SHIFT_LEFT);
INCPOS(1);
} else {
_make_token(TK_OP_SHIFT_LEFT);
}
INCPOS(1);
} else
_make_token(TK_OP_LESS);
} break;
case '>': {
if (GETCHAR(1)=='=') {
_make_token(TK_OP_GREATER_EQUAL);
INCPOS(1);
} else if (GETCHAR(1)=='>') {
if (GETCHAR(2)=='=') {
_make_token(TK_OP_ASSIGN_SHIFT_RIGHT);
INCPOS(1);
} else {
_make_token(TK_OP_SHIFT_RIGHT);
}
INCPOS(1);
} else {
_make_token(TK_OP_GREATER);
}
} break;
case '!': {
if (GETCHAR(1)=='=') {
_make_token(TK_OP_NOT_EQUAL);
INCPOS(1);
} else {
_make_token(TK_OP_NOT);
}
} break;
//case '"' //string - no strings in shader
//case '\'' //string - no strings in shader
case '{':
_make_token(TK_CURLY_BRACKET_OPEN);
break;
case '}':
_make_token(TK_CURLY_BRACKET_CLOSE);
break;
case '[':
_make_token(TK_BRACKET_OPEN);
break;
case ']':
_make_token(TK_BRACKET_CLOSE);
break;
case '(':
_make_token(TK_PARENTHESIS_OPEN);
break;
case ')':
_make_token(TK_PARENTHESIS_CLOSE);
break;
case ',':
_make_token(TK_COMMA);
break;
case ';':
_make_token(TK_SEMICOLON);
break;
case '?':
_make_token(TK_QUESTION_MARK);
break;
case ':':
_make_token(TK_COLON); //for methods maybe but now useless.
break;
case '^': {
if (GETCHAR(1)=='=') {
_make_token(TK_OP_ASSIGN_BIT_XOR);
INCPOS(1);
} else {
_make_token(TK_OP_BIT_XOR);
}
} break;
case '~':
_make_token(TK_OP_BIT_INVERT);
break;
case '&': {
if (GETCHAR(1)=='&') {
_make_token(TK_OP_AND);
INCPOS(1);
} else if (GETCHAR(1)=='=') {
_make_token(TK_OP_ASSIGN_BIT_AND);
INCPOS(1);
} else {
_make_token(TK_OP_BIT_AND);
}
} break;
case '|': {
if (GETCHAR(1)=='|') {
_make_token(TK_OP_OR);
INCPOS(1);
} else if (GETCHAR(1)=='=') {
_make_token(TK_OP_ASSIGN_BIT_OR);
INCPOS(1);
} else {
_make_token(TK_OP_BIT_OR);
}
} break;
case '*': {
if (GETCHAR(1)=='=') {
_make_token(TK_OP_ASSIGN_MUL);
INCPOS(1);
} else {
_make_token(TK_OP_MUL);
}
} break;
case '+': {
if (GETCHAR(1)=='=') {
_make_token(TK_OP_ASSIGN_ADD);
INCPOS(1);
//} else if (GETCHAR(1)=='+') {
// _make_token(TK_OP_PLUS_PLUS);
// INCPOS(1);
} else {
_make_token(TK_OP_ADD);
}
} break;
case '-': {
if (GETCHAR(1)=='=') {
_make_token(TK_OP_ASSIGN_SUB);
INCPOS(1);
//} else if (GETCHAR(1)=='-') {
// _make_token(TK_OP_MINUS_MINUS);
// INCPOS(1);
} else {
_make_token(TK_OP_SUB);
}
} break;
case '%': {
if (GETCHAR(1)=='=') {
_make_token(TK_OP_ASSIGN_MOD);
INCPOS(1);
} else {
_make_token(TK_OP_MOD);
}
} break;
case '@':
if (CharType(GETCHAR(1))!='"') {
_make_error("Unexpected '@'");
return;
}
INCPOS(1);
is_node_path=true;
case '"': {
int i=1;
String str;
while(true) {
if (CharType(GETCHAR(i)==0)) {
_make_error("Unterminated String");
return;
} else if (CharType(GETCHAR(i)=='"')) {
break;
} else if (CharType(GETCHAR(i)=='\\')) {
//escaped characters...
i++;
CharType next = GETCHAR(i);
if (next==0) {
_make_error("Unterminated String");
return;
}
CharType res=0;
switch(next) {
case 'a': res=7; break;
case 'b': res=8; break;
case 't': res=9; break;
case 'n': res=10; break;
case 'v': res=11; break;
case 'f': res=12; break;
case 'r': res=13; break;
case '\'': res='\''; break;
case '\"': res='\"'; break;
case '\\': res='\\'; break;
case 'x': {
//hexnumbarh - oct is deprecated
int read=0;
for(int j=0;j<4;j++) {
CharType c = GETCHAR(i+j);
if (c==0) {
_make_error("Unterminated String");
return;
}
if (!_is_hex(c)) {
if (j==0 || !(j&1)) {
_make_error("Malformed hex constant in string");
return;
} else
break;
}
CharType v;
if (c>='0' && c<='9') {
v=c-'0';
} else if (c>='a' && c<='f') {
v=c-'a';
v+=10;
} else if (c>='A' && c<='F') {
v=c-'A';
v+=10;
} else {
ERR_PRINT("BUG");
v=0;
}
res<<=4;
res|=v;
read++;
}
i+=read-1;
} break;
default: {
_make_error("Invalid escape sequence");
return;
} break;
}
str+=res;
} else {
str+=CharType(GETCHAR(i));
}
i++;
}
INCPOS(i);
if (is_node_path) {
_make_constant(NodePath(str));
} else {
_make_constant(str);
}
} break;
default: {
if (_is_number(GETCHAR(0)) || (GETCHAR(0)=='.' && _is_number(GETCHAR(1)))) {
// parse number
bool period_found=false;
bool exponent_found=false;
bool hexa_found=false;
bool sign_found=false;
String str;
int i=0;
while(true) {
if (GETCHAR(i)=='.') {
if (period_found || exponent_found) {
_make_error("Invalid numeric constant at '.'");
return;
}
period_found=true;
} else if (GETCHAR(i)=='x') {
if (hexa_found || str.length()!=1 || !( (i==1 && str[0]=='0') || (i==2 && str[1]=='0' && str[0]=='-') ) ) {
_make_error("Invalid numeric constant at 'x'");
return;
}
hexa_found=true;
} else if (!hexa_found && GETCHAR(i)=='e') {
if (hexa_found || exponent_found) {
_make_error("Invalid numeric constant at 'e'");
return;
}
exponent_found=true;
} else if (_is_number(GETCHAR(i))) {
//all ok
} else if (hexa_found && _is_hex(GETCHAR(i))) {
} else if ((GETCHAR(i)=='-' || GETCHAR(i)=='+') && exponent_found) {
if (sign_found) {
_make_error("Invalid numeric constant at '-'");
return;
}
sign_found=true;
} else
break;
str+=CharType(GETCHAR(i));
i++;
}
if (!( _is_number(str[str.length()-1]) || (hexa_found && _is_hex(str[str.length()-1])))) {
_make_error("Invalid numeric constant: "+str);
return;
}
INCPOS(str.length());
if (hexa_found) {
int val = str.hex_to_int();
_make_constant(val);
} else if (period_found) {
real_t val = str.to_double();
//print_line("*%*%*%*% to convert: "+str+" result: "+rtos(val));
_make_constant(val);
} else {
int val = str.to_int();
_make_constant(val);
}
return;
}
if (GETCHAR(0)=='.') {
//parse period
_make_token(TK_PERIOD);
break;
}
if (_is_text_char(GETCHAR(0))) {
// parse identifier
String str;
str+=CharType(GETCHAR(0));
int i=1;
while(_is_text_char(GETCHAR(i))) {
str+=CharType(GETCHAR(i));
i++;
}
bool identifier=false;
if (str=="null") {
_make_constant(Variant());
} else if (str=="true") {
_make_constant(true);
} else if (str=="false") {
_make_constant(false);
} else {
bool found=false;
struct _bit { Variant::Type type; const char *text;};
//built in types
static const _bit type_list[]={
//types
{Variant::BOOL,"bool"},
{Variant::INT,"int"},
{Variant::REAL,"float"},
{Variant::STRING,"String"},
{Variant::VECTOR2,"vec2"},
{Variant::VECTOR2,"Vector2"},
{Variant::RECT2,"Rect2"},
{Variant::MATRIX32,"Matrix32"},
{Variant::MATRIX32,"mat32"},
{Variant::VECTOR3,"vec3"},
{Variant::VECTOR3,"Vector3"},
{Variant::_AABB,"AABB"},
{Variant::_AABB,"Rect3"},
{Variant::PLANE,"Plane"},
{Variant::QUAT,"Quat"},
{Variant::MATRIX3,"mat3"},
{Variant::MATRIX3,"Matrix3"},
{Variant::TRANSFORM,"trn"},
{Variant::TRANSFORM,"Transform"},
{Variant::COLOR,"Color"},
{Variant::IMAGE,"Image"},
{Variant::_RID,"RID"},
{Variant::OBJECT,"Object"},
{Variant::INPUT_EVENT,"InputEvent"},
{Variant::DICTIONARY,"dict"},
{Variant::DICTIONARY,"Dictionary"},
{Variant::ARRAY,"Array"},
{Variant::RAW_ARRAY,"RawArray"},
{Variant::INT_ARRAY,"IntArray"},
{Variant::REAL_ARRAY,"FloatArray"},
{Variant::STRING_ARRAY,"StringArray"},
{Variant::VECTOR2_ARRAY,"Vector2Array"},
{Variant::VECTOR3_ARRAY,"Vector3Array"},
{Variant::COLOR_ARRAY,"ColorArray"},
{Variant::VARIANT_MAX,NULL},
};
{
int idx=0;
while(type_list[idx].text) {
if (str==type_list[idx].text) {
_make_type(type_list[idx].type);
found=true;
break;
}
idx++;
}
}
if (!found) {
//built in func?
for(int i=0;i<GDFunctions::FUNC_MAX;i++) {
if (str==GDFunctions::get_func_name(GDFunctions::Function(i))) {
_make_built_in_func(GDFunctions::Function(i));
found=true;
break;
}
}
//keywor
}
if (!found) {
struct _kws { Token token; const char *text;};
static const _kws keyword_list[]={
//ops
{TK_OP_IN,"in"},
{TK_OP_NOT,"not"},
{TK_OP_OR,"or"},
{TK_OP_AND,"and"},
//func
{TK_PR_FUNCTION,"func"},
{TK_PR_FUNCTION,"function"},
{TK_PR_CLASS,"class"},
{TK_PR_EXTENDS,"extends"},
{TK_PR_TOOL,"tool"},
{TK_PR_STATIC,"static"},
{TK_PR_EXPORT,"export"},
{TK_PR_VAR,"var"},
{TK_PR_PRELOAD,"preload"},
{TK_PR_ASSERT,"assert"},
{TK_PR_CONST,"const"},
//controlflow
{TK_CF_IF,"if"},
{TK_CF_ELIF,"elif"},
{TK_CF_ELSE,"else"},
{TK_CF_FOR,"for"},
{TK_CF_WHILE,"while"},
{TK_CF_DO,"do"},
{TK_CF_SWITCH,"switch"},
{TK_CF_BREAK,"break"},
{TK_CF_CONTINUE,"continue"},
{TK_CF_RETURN,"return"},
{TK_CF_PASS,"pass"},
{TK_SELF,"self"},
{TK_ERROR,NULL}
};
int idx=0;
found=false;
while(keyword_list[idx].text) {
if (str==keyword_list[idx].text) {
_make_token(keyword_list[idx].token);
found=true;
break;
}
idx++;
}
}
if (!found)
identifier=true;
}
if (identifier) {
_make_identifier(str);
}
INCPOS(str.length());
return;
}
_make_error("Unknown character");
return;
} break;
}
INCPOS(1);
break;
}
}
void GDTokenizer::set_code(const String& p_code) {
code=p_code;
len = p_code.length();
if (len) {
_code=&code[0];
} else {
_code=NULL;
}
code_pos=0;
line=1; //it is stand-ar-ized that lines begin in 1 in code..
column=0;
tk_rb_pos=0;
error_flag=false;
last_error="";
for(int i=0;i<MAX_LOOKAHEAD+1;i++)
_advance();
}
GDTokenizer::Token GDTokenizer::get_token(int p_offset) const {
ERR_FAIL_COND_V( p_offset <= -MAX_LOOKAHEAD, TK_ERROR);
ERR_FAIL_COND_V( p_offset >= MAX_LOOKAHEAD, TK_ERROR);
int ofs = (TK_RB_SIZE + tk_rb_pos + p_offset - MAX_LOOKAHEAD -1)%TK_RB_SIZE;
return tk_rb[ofs].type;
}
int GDTokenizer::get_token_line(int p_offset) const {
ERR_FAIL_COND_V( p_offset <= -MAX_LOOKAHEAD, -1);
ERR_FAIL_COND_V( p_offset >= MAX_LOOKAHEAD, -1);
int ofs = (TK_RB_SIZE + tk_rb_pos + p_offset - MAX_LOOKAHEAD -1)%TK_RB_SIZE;
return tk_rb[ofs].line;
}
int GDTokenizer::get_token_column(int p_offset) const {
ERR_FAIL_COND_V( p_offset <= -MAX_LOOKAHEAD, -1);
ERR_FAIL_COND_V( p_offset >= MAX_LOOKAHEAD, -1);
int ofs = (TK_RB_SIZE + tk_rb_pos + p_offset - MAX_LOOKAHEAD -1)%TK_RB_SIZE;
return tk_rb[ofs].col;
}
const Variant& GDTokenizer::get_token_constant(int p_offset) const {
ERR_FAIL_COND_V( p_offset <= -MAX_LOOKAHEAD, tk_rb[0].constant);
ERR_FAIL_COND_V( p_offset >= MAX_LOOKAHEAD, tk_rb[0].constant);
int ofs = (TK_RB_SIZE + tk_rb_pos + p_offset - MAX_LOOKAHEAD -1)%TK_RB_SIZE;
ERR_FAIL_COND_V(tk_rb[ofs].type!=TK_CONSTANT,tk_rb[0].constant);
return tk_rb[ofs].constant;
}
StringName GDTokenizer::get_token_identifier(int p_offset) const {
ERR_FAIL_COND_V( p_offset <= -MAX_LOOKAHEAD, StringName());
ERR_FAIL_COND_V( p_offset >= MAX_LOOKAHEAD, StringName());
int ofs = (TK_RB_SIZE + tk_rb_pos + p_offset - MAX_LOOKAHEAD -1)%TK_RB_SIZE;
ERR_FAIL_COND_V(tk_rb[ofs].type!=TK_IDENTIFIER,StringName());
return tk_rb[ofs].identifier;
}
GDFunctions::Function GDTokenizer::get_token_built_in_func(int p_offset) const {
ERR_FAIL_COND_V( p_offset <= -MAX_LOOKAHEAD, GDFunctions::FUNC_MAX);
ERR_FAIL_COND_V( p_offset >= MAX_LOOKAHEAD, GDFunctions::FUNC_MAX);
int ofs = (TK_RB_SIZE + tk_rb_pos + p_offset - MAX_LOOKAHEAD -1)%TK_RB_SIZE;
ERR_FAIL_COND_V(tk_rb[ofs].type!=TK_BUILT_IN_FUNC,GDFunctions::FUNC_MAX);
return tk_rb[ofs].func;
}
Variant::Type GDTokenizer::get_token_type(int p_offset) const {
ERR_FAIL_COND_V( p_offset <= -MAX_LOOKAHEAD, Variant::NIL);
ERR_FAIL_COND_V( p_offset >= MAX_LOOKAHEAD, Variant::NIL);
int ofs = (TK_RB_SIZE + tk_rb_pos + p_offset - MAX_LOOKAHEAD -1)%TK_RB_SIZE;
ERR_FAIL_COND_V(tk_rb[ofs].type!=TK_BUILT_IN_TYPE,Variant::NIL);
return tk_rb[ofs].vtype;
}
int GDTokenizer::get_token_line_indent(int p_offset) const {
ERR_FAIL_COND_V( p_offset <= -MAX_LOOKAHEAD, 0);
ERR_FAIL_COND_V( p_offset >= MAX_LOOKAHEAD, 0);
int ofs = (TK_RB_SIZE + tk_rb_pos + p_offset - MAX_LOOKAHEAD -1)%TK_RB_SIZE;
ERR_FAIL_COND_V(tk_rb[ofs].type!=TK_NEWLINE,0);
return tk_rb[ofs].constant;
}
String GDTokenizer::get_token_error(int p_offset) const {
ERR_FAIL_COND_V( p_offset <= -MAX_LOOKAHEAD, String());
ERR_FAIL_COND_V( p_offset >= MAX_LOOKAHEAD, String());
int ofs = (TK_RB_SIZE + tk_rb_pos + p_offset - MAX_LOOKAHEAD -1)%TK_RB_SIZE;
ERR_FAIL_COND_V(tk_rb[ofs].type!=TK_ERROR,String());
return tk_rb[ofs].constant;
}
void GDTokenizer::advance(int p_amount) {
ERR_FAIL_COND( p_amount <=0 );
for(int i=0;i<p_amount;i++)
_advance();
}

View File

@@ -0,0 +1,181 @@
/*************************************************************************/
/* gd_tokenizer.h */
/*************************************************************************/
/* This file is part of: */
/* GODOT ENGINE */
/* http://www.godotengine.org */
/*************************************************************************/
/* Copyright (c) 2007-2014 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 */
/* "Software"), to deal in the Software without restriction, including */
/* without limitation the rights to use, copy, modify, merge, publish, */
/* distribute, sublicense, and/or sell copies of the Software, and to */
/* permit persons to whom the Software is furnished to do so, subject to */
/* the following conditions: */
/* */
/* The above copyright notice and this permission notice shall be */
/* included in all copies or substantial portions of the Software. */
/* */
/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */
/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */
/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.*/
/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */
/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */
/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */
/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */
/*************************************************************************/
#ifndef GD_TOKENIZER_H
#define GD_TOKENIZER_H
#include "ustring.h"
#include "variant.h"
#include "string_db.h"
#include "gd_functions.h"
class GDTokenizer {
public:
enum Token {
TK_EMPTY,
TK_IDENTIFIER,
TK_CONSTANT,
TK_SELF,
TK_BUILT_IN_TYPE,
TK_BUILT_IN_FUNC,
TK_OP_IN,
TK_OP_EQUAL,
TK_OP_NOT_EQUAL,
TK_OP_LESS,
TK_OP_LESS_EQUAL,
TK_OP_GREATER,
TK_OP_GREATER_EQUAL,
TK_OP_AND,
TK_OP_OR,
TK_OP_NOT,
TK_OP_ADD,
TK_OP_SUB,
TK_OP_MUL,
TK_OP_DIV,
TK_OP_MOD,
TK_OP_SHIFT_LEFT,
TK_OP_SHIFT_RIGHT,
TK_OP_ASSIGN,
TK_OP_ASSIGN_ADD,
TK_OP_ASSIGN_SUB,
TK_OP_ASSIGN_MUL,
TK_OP_ASSIGN_DIV,
TK_OP_ASSIGN_MOD,
TK_OP_ASSIGN_SHIFT_LEFT,
TK_OP_ASSIGN_SHIFT_RIGHT,
TK_OP_ASSIGN_BIT_AND,
TK_OP_ASSIGN_BIT_OR,
TK_OP_ASSIGN_BIT_XOR,
TK_OP_BIT_AND,
TK_OP_BIT_OR,
TK_OP_BIT_XOR,
TK_OP_BIT_INVERT,
//TK_OP_PLUS_PLUS,
//TK_OP_MINUS_MINUS,
TK_CF_IF,
TK_CF_ELIF,
TK_CF_ELSE,
TK_CF_FOR,
TK_CF_DO,
TK_CF_WHILE,
TK_CF_SWITCH,
TK_CF_CASE,
TK_CF_BREAK,
TK_CF_CONTINUE,
TK_CF_PASS,
TK_CF_RETURN,
TK_PR_FUNCTION,
TK_PR_CLASS,
TK_PR_EXTENDS,
TK_PR_TOOL,
TK_PR_STATIC,
TK_PR_EXPORT,
TK_PR_CONST,
TK_PR_VAR,
TK_PR_PRELOAD,
TK_PR_ASSERT,
TK_BRACKET_OPEN,
TK_BRACKET_CLOSE,
TK_CURLY_BRACKET_OPEN,
TK_CURLY_BRACKET_CLOSE,
TK_PARENTHESIS_OPEN,
TK_PARENTHESIS_CLOSE,
TK_COMMA,
TK_SEMICOLON,
TK_PERIOD,
TK_QUESTION_MARK,
TK_COLON,
TK_NEWLINE,
TK_ERROR,
TK_EOF,
TK_MAX
};
private:
static const char* token_names[TK_MAX];
enum {
MAX_LOOKAHEAD=4,
TK_RB_SIZE=MAX_LOOKAHEAD*2+1
};
struct TokenData {
Token type;
StringName identifier; //for identifier types
Variant constant; //for constant types
union {
Variant::Type vtype; //for type types
GDFunctions::Function func; //function for built in functions
};
int line,col;
TokenData() { type = TK_EMPTY; line=col=0; vtype=Variant::NIL; }
};
void _make_token(Token p_type);
void _make_newline(int p_spaces=0);
void _make_identifier(const StringName& p_identifier);
void _make_built_in_func(GDFunctions::Function p_func);
void _make_constant(const Variant& p_constant);
void _make_type(const Variant::Type& p_type);
void _make_error(const String& p_error);
String code;
int len;
int code_pos;
const CharType *_code;
int line;
int column;
TokenData tk_rb[TK_RB_SIZE*2+1];
int tk_rb_pos;
String last_error;
bool error_flag;
void _advance();
public:
static const char *get_token_name(Token p_token);
void set_code(const String& p_code);
Token get_token(int p_offset=0) const;
const Variant& get_token_constant(int p_offset=0) const;
StringName get_token_identifier(int p_offset=0) const;
GDFunctions::Function get_token_built_in_func(int p_offset=0) const;
Variant::Type get_token_type(int p_offset=0) const;
int get_token_line(int p_offset=0) const;
int get_token_column(int p_offset=0) const;
int get_token_line_indent(int p_offset=0) const;
String get_token_error(int p_offset=0) const;
void advance(int p_amount=1);
};
#endif // TOKENIZER_H

View File

@@ -0,0 +1,46 @@
/*************************************************/
/* register_script_types.cpp */
/*************************************************/
/* This file is part of: */
/* GODOT ENGINE */
/*************************************************/
/* Source code within this file is: */
/* (c) 2007-2010 Juan Linietsky, Ariel Manzur */
/* All Rights Reserved. */
/*************************************************/
#include "register_types.h"
#include "gd_script.h"
#include "io/resource_loader.h"
GDScriptLanguage *script_language_gd=NULL;
ResourceFormatLoaderGDScript *resource_loader_gd=NULL;
ResourceFormatSaverGDScript *resource_saver_gd=NULL;
void register_gdscript_types() {
script_language_gd=memnew( GDScriptLanguage );
script_language_gd->init();
ScriptServer::register_language(script_language_gd);
ObjectTypeDB::register_type<GDScript>();
resource_loader_gd=memnew( ResourceFormatLoaderGDScript );
ResourceLoader::add_resource_format_loader(resource_loader_gd);
resource_saver_gd=memnew( ResourceFormatSaverGDScript );
ResourceSaver::add_resource_format_saver(resource_saver_gd);
}
void unregister_gdscript_types() {
if (script_language_gd)
memdelete( script_language_gd );
if (resource_loader_gd)
memdelete( resource_loader_gd );
if (resource_saver_gd)
memdelete( resource_saver_gd );
}

View File

@@ -0,0 +1,30 @@
/*************************************************************************/
/* register_types.h */
/*************************************************************************/
/* This file is part of: */
/* GODOT ENGINE */
/* http://www.godotengine.org */
/*************************************************************************/
/* Copyright (c) 2007-2014 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 */
/* "Software"), to deal in the Software without restriction, including */
/* without limitation the rights to use, copy, modify, merge, publish, */
/* distribute, sublicense, and/or sell copies of the Software, and to */
/* permit persons to whom the Software is furnished to do so, subject to */
/* the following conditions: */
/* */
/* The above copyright notice and this permission notice shall be */
/* included in all copies or substantial portions of the Software. */
/* */
/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */
/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */
/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.*/
/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */
/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */
/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */
/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */
/*************************************************************************/
void register_gdscript_types();
void unregister_gdscript_types();

View File

@@ -0,0 +1,7 @@
Import('env')
env.add_source_files(env.modules_sources,"*.cpp")
Export('env')

View File

@@ -0,0 +1,11 @@
def can_build(platform):
return True
def configure(env):
pass

View File

@@ -0,0 +1,498 @@
/*************************************************************************/
/* multi_script.cpp */
/*************************************************************************/
/* This file is part of: */
/* GODOT ENGINE */
/* http://www.godotengine.org */
/*************************************************************************/
/* Copyright (c) 2007-2014 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 */
/* "Software"), to deal in the Software without restriction, including */
/* without limitation the rights to use, copy, modify, merge, publish, */
/* distribute, sublicense, and/or sell copies of the Software, and to */
/* permit persons to whom the Software is furnished to do so, subject to */
/* the following conditions: */
/* */
/* The above copyright notice and this permission notice shall be */
/* included in all copies or substantial portions of the Software. */
/* */
/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */
/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */
/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.*/
/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */
/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */
/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */
/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */
/*************************************************************************/
#include "multi_script.h"
bool MultiScriptInstance::set(const StringName& p_name, const Variant& p_value) {
ScriptInstance **sarr = instances.ptr();
int sc = instances.size();
for(int i=0;i<sc;i++) {
if (!sarr[i])
continue;
bool found = sarr[i]->set(p_name,p_value);
if (found)
return true;
}
if (String(p_name).begins_with("script_")) {
bool valid;
owner->set(p_name,p_value,&valid);
return valid;
}
return false;
}
bool MultiScriptInstance::get(const StringName& p_name, Variant &r_ret) const{
ScriptInstance **sarr = instances.ptr();
int sc = instances.size();
for(int i=0;i<sc;i++) {
if (!sarr[i])
continue;
bool found = sarr[i]->get(p_name,r_ret);
if (found)
return true;
}
if (String(p_name).begins_with("script_")) {
bool valid;
r_ret=owner->get(p_name,&valid);
return valid;
}
return false;
}
void MultiScriptInstance::get_property_list(List<PropertyInfo> *p_properties) const{
ScriptInstance **sarr = instances.ptr();
int sc = instances.size();
Set<String> existing;
for(int i=0;i<sc;i++) {
if (!sarr[i])
continue;
List<PropertyInfo> pl;
sarr[i]->get_property_list(&pl);
for(List<PropertyInfo>::Element *E=pl.front();E;E=E->next()) {
if (existing.has(E->get().name))
continue;
p_properties->push_back(E->get());
existing.insert(E->get().name);
}
}
p_properties->push_back( PropertyInfo(Variant::NIL,"Scripts",PROPERTY_HINT_NONE,String(),PROPERTY_USAGE_CATEGORY) );
for(int i=0;i<owner->scripts.size();i++) {
p_properties->push_back( PropertyInfo(Variant::OBJECT,"script_"+String::chr('a'+i),PROPERTY_HINT_RESOURCE_TYPE,"Script",PROPERTY_USAGE_EDITOR) );
}
if (owner->scripts.size()<25) {
p_properties->push_back( PropertyInfo(Variant::OBJECT,"script_"+String::chr('a'+(owner->scripts.size())),PROPERTY_HINT_RESOURCE_TYPE,"Script",PROPERTY_USAGE_EDITOR) );
}
}
void MultiScriptInstance::get_method_list(List<MethodInfo> *p_list) const{
ScriptInstance **sarr = instances.ptr();
int sc = instances.size();
Set<StringName> existing;
for(int i=0;i<sc;i++) {
if (!sarr[i])
continue;
List<MethodInfo> ml;
sarr[i]->get_method_list(&ml);
for(List<MethodInfo>::Element *E=ml.front();E;E=E->next()) {
if (existing.has(E->get().name))
continue;
p_list->push_back(E->get());
existing.insert(E->get().name);
}
}
}
bool MultiScriptInstance::has_method(const StringName& p_method) const{
ScriptInstance **sarr = instances.ptr();
int sc = instances.size();
for(int i=0;i<sc;i++) {
if (!sarr[i])
continue;
if (sarr[i]->has_method(p_method))
return true;
}
return false;
}
Variant MultiScriptInstance::call(const StringName& p_method,const Variant** p_args,int p_argcount,Variant::CallError &r_error) {
ScriptInstance **sarr = instances.ptr();
int sc = instances.size();
for(int i=0;i<sc;i++) {
if (!sarr[i])
continue;
Variant r = sarr[i]->call(p_method,p_args,p_argcount,r_error);
if (r_error.error==Variant::CallError::CALL_OK)
return r;
else if (r_error.error!=Variant::CallError::CALL_ERROR_INVALID_METHOD)
return r;
}
r_error.error=Variant::CallError::CALL_ERROR_INVALID_METHOD;
return Variant();
}
void MultiScriptInstance::call_multilevel(const StringName& p_method,const Variant** p_args,int p_argcount){
ScriptInstance **sarr = instances.ptr();
int sc = instances.size();
for(int i=0;i<sc;i++) {
if (!sarr[i])
continue;
sarr[i]->call_multilevel(p_method,p_args,p_argcount);
}
}
void MultiScriptInstance::notification(int p_notification){
ScriptInstance **sarr = instances.ptr();
int sc = instances.size();
for(int i=0;i<sc;i++) {
if (!sarr[i])
continue;
sarr[i]->notification(p_notification);
}
}
Ref<Script> MultiScriptInstance::get_script() const {
return owner;
}
ScriptLanguage *MultiScriptInstance::get_language() {
return MultiScriptLanguage::get_singleton();
}
MultiScriptInstance::~MultiScriptInstance() {
owner->remove_instance(object);
}
///////////////////
bool MultiScript::is_tool() const {
for(int i=0;i<scripts.size();i++) {
if (scripts[i]->is_tool())
return true;
}
return false;
}
bool MultiScript::_set(const StringName& p_name, const Variant& p_value) {
_THREAD_SAFE_METHOD_
String s = String(p_name);
if (s.begins_with("script_")) {
int idx = s[7];
if (idx==0)
return false;
idx-='a';
ERR_FAIL_COND_V(idx<0,false);
Ref<Script> s = p_value;
if (idx<scripts.size()) {
if (s.is_null())
remove_script(idx);
else
set_script(idx,s);
} else if (idx==scripts.size()) {
if (s.is_null())
return false;
add_script(s);
} else
return false;
return true;
}
return false;
}
bool MultiScript::_get(const StringName& p_name,Variant &r_ret) const{
_THREAD_SAFE_METHOD_
String s = String(p_name);
if (s.begins_with("script_")) {
int idx = s[7];
if (idx==0)
return false;
idx-='a';
ERR_FAIL_COND_V(idx<0,false);
if (idx<scripts.size()) {
r_ret=get_script(idx);
return true;
} else if (idx==scripts.size()) {
r_ret=Ref<Script>();
return true;
}
}
return false;
}
void MultiScript::_get_property_list( List<PropertyInfo> *p_list) const{
_THREAD_SAFE_METHOD_
for(int i=0;i<scripts.size();i++) {
p_list->push_back( PropertyInfo(Variant::OBJECT,"script_"+String::chr('a'+i),PROPERTY_HINT_RESOURCE_TYPE,"Script") );
}
if (scripts.size()<25) {
p_list->push_back( PropertyInfo(Variant::OBJECT,"script_"+String::chr('a'+(scripts.size())),PROPERTY_HINT_RESOURCE_TYPE,"Script") );
}
}
void MultiScript::set_script(int p_idx,const Ref<Script>& p_script ) {
_THREAD_SAFE_METHOD_
ERR_FAIL_INDEX(p_idx,scripts.size());
ERR_FAIL_COND( p_script.is_null() );
scripts[p_idx]=p_script;
Ref<Script> s=p_script;
for (Map<Object*,MultiScriptInstance*>::Element *E=instances.front();E;E=E->next()) {
MultiScriptInstance*msi=E->get();
ScriptInstance *si = msi->instances[p_idx];
if (si) {
msi->instances[p_idx]=NULL;
memdelete(si);
}
if (p_script->can_instance())
msi->instances[p_idx]=s->instance_create(msi->object);
}
}
Ref<Script> MultiScript::get_script(int p_idx) const{
_THREAD_SAFE_METHOD_
ERR_FAIL_INDEX_V(p_idx,scripts.size(),Ref<Script>());
return scripts[p_idx];
}
void MultiScript::add_script(const Ref<Script>& p_script){
_THREAD_SAFE_METHOD_
ERR_FAIL_COND( p_script.is_null() );
scripts.push_back(p_script);
Ref<Script> s=p_script;
for (Map<Object*,MultiScriptInstance*>::Element *E=instances.front();E;E=E->next()) {
MultiScriptInstance*msi=E->get();
if (p_script->can_instance())
msi->instances.push_back( s->instance_create(msi->object) );
else
msi->instances.push_back(NULL);
msi->object->_change_notify();
}
_change_notify();
}
void MultiScript::remove_script(int p_idx) {
_THREAD_SAFE_METHOD_
ERR_FAIL_INDEX(p_idx,scripts.size());
scripts.remove(p_idx);
for (Map<Object*,MultiScriptInstance*>::Element *E=instances.front();E;E=E->next()) {
MultiScriptInstance*msi=E->get();
ScriptInstance *si = msi->instances[p_idx];
msi->instances.remove(p_idx);
if (si) {
memdelete(si);
}
msi->object->_change_notify();
}
}
void MultiScript::remove_instance(Object *p_object) {
_THREAD_SAFE_METHOD_
instances.erase(p_object);
}
bool MultiScript::can_instance() const {
return true;
}
StringName MultiScript::get_instance_base_type() const {
return StringName();
}
ScriptInstance* MultiScript::instance_create(Object *p_this) {
_THREAD_SAFE_METHOD_
MultiScriptInstance *msi = memnew( MultiScriptInstance );
msi->object=p_this;
msi->owner=this;
for(int i=0;i<scripts.size();i++) {
ScriptInstance *si;
if (scripts[i]->can_instance())
si = scripts[i]->instance_create(p_this);
else
si=NULL;
msi->instances.push_back(si);
}
instances[p_this]=msi;
p_this->_change_notify();
return msi;
}
bool MultiScript::instance_has(const Object *p_this) const {
_THREAD_SAFE_METHOD_
return instances.has((Object*)p_this);
}
bool MultiScript::has_source_code() const {
return false;
}
String MultiScript::get_source_code() const {
return "";
}
void MultiScript::set_source_code(const String& p_code) {
}
Error MultiScript::reload() {
for(int i=0;i<scripts.size();i++)
scripts[i]->reload();
return OK;
}
String MultiScript::get_node_type() const {
return "";
}
void MultiScript::_bind_methods() {
}
ScriptLanguage *MultiScript::get_language() const {
return MultiScriptLanguage::get_singleton();
}
///////////////
MultiScript::MultiScript() {
}
MultiScriptLanguage *MultiScriptLanguage::singleton=NULL;

View File

@@ -0,0 +1,158 @@
/*************************************************************************/
/* multi_script.h */
/*************************************************************************/
/* This file is part of: */
/* GODOT ENGINE */
/* http://www.godotengine.org */
/*************************************************************************/
/* Copyright (c) 2007-2014 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 */
/* "Software"), to deal in the Software without restriction, including */
/* without limitation the rights to use, copy, modify, merge, publish, */
/* distribute, sublicense, and/or sell copies of the Software, and to */
/* permit persons to whom the Software is furnished to do so, subject to */
/* the following conditions: */
/* */
/* The above copyright notice and this permission notice shall be */
/* included in all copies or substantial portions of the Software. */
/* */
/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */
/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */
/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.*/
/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */
/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */
/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */
/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */
/*************************************************************************/
#ifndef MULTI_SCRIPT_H
#define MULTI_SCRIPT_H
#include "script_language.h"
#include "os/thread_safe.h"
class MultiScript;
class MultiScriptInstance : public ScriptInstance {
friend class MultiScript;
mutable Vector<ScriptInstance*> instances;
Object *object;
mutable MultiScript *owner;
public:
virtual bool set(const StringName& p_name, const Variant& p_value);
virtual bool get(const StringName& p_name, Variant &r_ret) const;
virtual void get_property_list(List<PropertyInfo> *p_properties) const;
virtual void get_method_list(List<MethodInfo> *p_list) const;
virtual bool has_method(const StringName& p_method) const;
virtual Variant call(const StringName& p_method,const Variant** p_args,int p_argcount,Variant::CallError &r_error);
virtual void call_multilevel(const StringName& p_method,const Variant** p_args,int p_argcount);
virtual void notification(int p_notification);
virtual Ref<Script> get_script() const;
virtual ScriptLanguage *get_language();
virtual ~MultiScriptInstance();
};
class MultiScript : public Script {
_THREAD_SAFE_CLASS_
friend class MultiScriptInstance;
OBJ_TYPE( MultiScript,Script);
Vector<Ref<Script> > scripts;
Map<Object*,MultiScriptInstance*> instances;
protected:
bool _set(const StringName& p_name, const Variant& p_value);
bool _get(const StringName& p_name,Variant &r_ret) const;
void _get_property_list( List<PropertyInfo> *p_list) const;
static void _bind_methods();
public:
void remove_instance(Object *p_object);
virtual bool can_instance() const;
virtual StringName get_instance_base_type() const;
virtual ScriptInstance* instance_create(Object *p_this);
virtual bool instance_has(const Object *p_this) const;
virtual bool has_source_code() const;
virtual String get_source_code() const;
virtual void set_source_code(const String& p_code);
virtual Error reload();
virtual bool is_tool() const;
virtual String get_node_type() const;
void set_script(int p_idx,const Ref<Script>& p_script );
Ref<Script> get_script(int p_idx) const;
void remove_script(int p_idx);
void add_script(const Ref<Script>& p_script);
virtual ScriptLanguage *get_language() const;
MultiScript();
};
class MultiScriptLanguage : public ScriptLanguage {
static MultiScriptLanguage *singleton;
public:
static _FORCE_INLINE_ MultiScriptLanguage *get_singleton() { return singleton; }
virtual String get_name() const { return "MultiScript"; }
/* LANGUAGE FUNCTIONS */
virtual void init() {}
virtual String get_type() const { return "MultiScript"; }
virtual String get_extension() const { return ""; }
virtual Error execute_file(const String& p_path) { return OK; }
virtual void finish() {}
/* EDITOR FUNCTIONS */
virtual void get_reserved_words(List<String> *p_words) const {}
virtual void get_comment_delimiters(List<String> *p_delimiters) const {}
virtual void get_string_delimiters(List<String> *p_delimiters) const {}
virtual String get_template(const String& p_class_name, const String& p_base_class_name) const { return ""; }
virtual bool validate(const String& p_script, int &r_line_error,int &r_col_error,String& r_test_error,const String& p_path="",List<String>* r_fn=NULL) const { return true; }
virtual Script *create_script() const { return memnew( MultiScript ); }
virtual bool has_named_classes() const { return false; }
virtual int find_function(const String& p_function,const String& p_code) const { return -1; }
virtual String make_function(const String& p_class,const String& p_name,const StringArray& p_args) const { return ""; }
/* DEBUGGER FUNCTIONS */
virtual String debug_get_error() const { return ""; }
virtual int debug_get_stack_level_count() const { return 0; }
virtual int debug_get_stack_level_line(int p_level) const { return 0; }
virtual String debug_get_stack_level_function(int p_level) const { return ""; }
virtual String debug_get_stack_level_source(int p_level) const { return ""; }
virtual void debug_get_stack_level_locals(int p_level,List<String> *p_locals, List<Variant> *p_values, int p_max_subitems=-1,int p_max_depth=-1) {}
virtual void debug_get_stack_level_members(int p_level,List<String> *p_members, List<Variant> *p_values, int p_max_subitems=-1,int p_max_depth=-1) {}
virtual void debug_get_globals(List<String> *p_locals, List<Variant> *p_values, int p_max_subitems=-1,int p_max_depth=-1) {}
virtual String debug_parse_stack_level_expression(int p_level,const String& p_expression,int p_max_subitems=-1,int p_max_depth=-1) { return ""; }
/* LOADER FUNCTIONS */
virtual void get_recognized_extensions(List<String> *p_extensions) const {}
virtual void get_public_functions(List<MethodInfo> *p_functions) const {}
virtual void get_public_constants(List<Pair<String,Variant> > *p_constants) const {}
MultiScriptLanguage() { singleton=this; }
virtual ~MultiScriptLanguage() {};
};
#endif // MULTI_SCRIPT_H

View File

@@ -0,0 +1,32 @@
/*************************************************/
/* register_script_types.cpp */
/*************************************************/
/* This file is part of: */
/* GODOT ENGINE */
/*************************************************/
/* Source code within this file is: */
/* (c) 2007-2010 Juan Linietsky, Ariel Manzur */
/* All Rights Reserved. */
/*************************************************/
#include "register_types.h"
#include "multi_script.h"
#include "io/resource_loader.h"
static MultiScriptLanguage *script_multi_script=NULL;
void register_multiscript_types() {
script_multi_script = memnew( MultiScriptLanguage );
ScriptServer::register_language(script_multi_script);
ObjectTypeDB::register_type<MultiScript>();
}
void unregister_multiscript_types() {
if (script_multi_script);
memdelete(script_multi_script);
}

View File

@@ -0,0 +1,30 @@
/*************************************************************************/
/* register_types.h */
/*************************************************************************/
/* This file is part of: */
/* GODOT ENGINE */
/* http://www.godotengine.org */
/*************************************************************************/
/* Copyright (c) 2007-2014 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 */
/* "Software"), to deal in the Software without restriction, including */
/* without limitation the rights to use, copy, modify, merge, publish, */
/* distribute, sublicense, and/or sell copies of the Software, and to */
/* permit persons to whom the Software is furnished to do so, subject to */
/* the following conditions: */
/* */
/* The above copyright notice and this permission notice shall be */
/* included in all copies or substantial portions of the Software. */
/* */
/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */
/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */
/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.*/
/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */
/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */
/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */
/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */
/*************************************************************************/
void register_multiscript_types();
void unregister_multiscript_types();