2014-02-10 02:10:30 +01:00
/*************************************************************************/
/* gd_compiler.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_compiler.h"
# include "gd_script.h"
/* TODO:
* AND and OR need early abort
- Inheritance properly process ( done ? )
* create built in initializer and constructor
* assign operators
* build arrays and dictionaries
* call parent constructor
*/
void GDCompiler : : _set_error ( const String & p_error , const GDParser : : Node * p_node ) {
if ( error ! = " " )
return ;
error = p_error ;
2015-01-03 04:57:02 +01:00
if ( p_node ) {
err_line = p_node - > line ;
err_column = p_node - > column ;
} else {
err_line = 0 ;
err_column = 0 ;
}
2014-02-10 02:10:30 +01:00
}
bool GDCompiler : : _create_unary_operator ( CodeGen & codegen , const GDParser : : OperatorNode * on , Variant : : Operator op , int p_stack_level ) {
ERR_FAIL_COND_V ( on - > arguments . size ( ) ! = 1 , false ) ;
int src_address_a = _parse_expression ( codegen , on - > arguments [ 0 ] , p_stack_level ) ;
if ( src_address_a < 0 )
return false ;
codegen . opcodes . push_back ( GDFunction : : OPCODE_OPERATOR ) ; // perform operator
codegen . opcodes . push_back ( op ) ; //which operator
codegen . opcodes . push_back ( src_address_a ) ; // argument 1
2014-09-15 16:33:30 +02:00
codegen . opcodes . push_back ( src_address_a ) ; // argument 2 (repeated)
//codegen.opcodes.push_back(GDFunction::ADDR_TYPE_NIL); // argument 2 (unary only takes one parameter)
2014-02-10 02:10:30 +01:00
return true ;
}
2014-11-02 15:31:01 +01:00
bool GDCompiler : : _create_binary_operator ( CodeGen & codegen , const GDParser : : OperatorNode * on , Variant : : Operator op , int p_stack_level , bool p_initializer ) {
2014-02-10 02:10:30 +01:00
ERR_FAIL_COND_V ( on - > arguments . size ( ) ! = 2 , false ) ;
2014-11-02 15:31:01 +01:00
int src_address_a = _parse_expression ( codegen , on - > arguments [ 0 ] , p_stack_level , false , p_initializer ) ;
2014-02-10 02:10:30 +01:00
if ( src_address_a < 0 )
return false ;
if ( src_address_a & GDFunction : : ADDR_TYPE_STACK < < GDFunction : : ADDR_BITS )
p_stack_level + + ; //uses stack for return, increase stack
2014-11-02 15:31:01 +01:00
int src_address_b = _parse_expression ( codegen , on - > arguments [ 1 ] , p_stack_level , false , p_initializer ) ;
2014-02-10 02:10:30 +01:00
if ( src_address_b < 0 )
return false ;
codegen . opcodes . push_back ( GDFunction : : OPCODE_OPERATOR ) ; // perform operator
codegen . opcodes . push_back ( op ) ; //which operator
codegen . opcodes . push_back ( src_address_a ) ; // argument 1
codegen . opcodes . push_back ( src_address_b ) ; // argument 2 (unary only takes one parameter)
return true ;
}
/*
int GDCompiler : : _parse_subexpression ( CodeGen & codegen , const GDParser : : Node * p_expression ) {
int ret = _parse_expression ( codegen , p_expression ) ;
if ( ret < 0 )
return ret ;
if ( ret & ( GDFunction : : ADDR_TYPE_STACK < < GDFunction : : ADDR_BITS ) ) {
codegen . stack_level + + ;
codegen . check_max_stack_level ( ) ;
//stack was used, keep value
}
return ret ;
}
*/
int GDCompiler : : _parse_assign_right_expression ( CodeGen & codegen , const GDParser : : OperatorNode * p_expression , int p_stack_level ) {
Variant : : Operator var_op = Variant : : OP_MAX ;
2014-11-02 15:31:01 +01:00
2014-02-10 02:10:30 +01:00
switch ( p_expression - > op ) {
case GDParser : : OperatorNode : : OP_ASSIGN_ADD : var_op = Variant : : OP_ADD ; break ;
case GDParser : : OperatorNode : : OP_ASSIGN_SUB : var_op = Variant : : OP_SUBSTRACT ; break ;
case GDParser : : OperatorNode : : OP_ASSIGN_MUL : var_op = Variant : : OP_MULTIPLY ; break ;
case GDParser : : OperatorNode : : OP_ASSIGN_DIV : var_op = Variant : : OP_DIVIDE ; break ;
case GDParser : : OperatorNode : : OP_ASSIGN_MOD : var_op = Variant : : OP_MODULE ; break ;
case GDParser : : OperatorNode : : OP_ASSIGN_SHIFT_LEFT : var_op = Variant : : OP_SHIFT_LEFT ; break ;
case GDParser : : OperatorNode : : OP_ASSIGN_SHIFT_RIGHT : var_op = Variant : : OP_SHIFT_RIGHT ; break ;
case GDParser : : OperatorNode : : OP_ASSIGN_BIT_AND : var_op = Variant : : OP_BIT_AND ; break ;
case GDParser : : OperatorNode : : OP_ASSIGN_BIT_OR : var_op = Variant : : OP_BIT_OR ; break ;
case GDParser : : OperatorNode : : OP_ASSIGN_BIT_XOR : var_op = Variant : : OP_BIT_XOR ; break ;
2014-11-02 15:31:01 +01:00
case GDParser : : OperatorNode : : OP_INIT_ASSIGN :
2014-02-10 02:10:30 +01:00
case GDParser : : OperatorNode : : OP_ASSIGN : {
//none
} break ;
default : {
ERR_FAIL_V ( - 1 ) ;
}
}
2014-11-02 15:31:01 +01:00
bool initializer = p_expression - > op = = GDParser : : OperatorNode : : OP_INIT_ASSIGN ;
2014-02-10 02:10:30 +01:00
if ( var_op = = Variant : : OP_MAX ) {
2014-11-02 15:31:01 +01:00
return _parse_expression ( codegen , p_expression - > arguments [ 1 ] , p_stack_level , false , initializer ) ;
2014-02-10 02:10:30 +01:00
}
2014-11-02 15:31:01 +01:00
if ( ! _create_binary_operator ( codegen , p_expression , var_op , p_stack_level , initializer ) )
2014-02-10 02:10:30 +01:00
return - 1 ;
int dst_addr = ( p_stack_level ) | ( GDFunction : : ADDR_TYPE_STACK < < GDFunction : : ADDR_BITS ) ;
codegen . opcodes . push_back ( dst_addr ) ; // append the stack level as destination address of the opcode
codegen . alloc_stack ( p_stack_level ) ;
return dst_addr ;
}
2014-11-02 15:31:01 +01:00
int GDCompiler : : _parse_expression ( CodeGen & codegen , const GDParser : : Node * p_expression , int p_stack_level , bool p_root , bool p_initializer ) {
2014-02-10 02:10:30 +01:00
switch ( p_expression - > type ) {
//should parse variable declaration and adjust stack accordingly...
case GDParser : : Node : : TYPE_IDENTIFIER : {
//return identifier
//wait, identifier could be a local variable or something else... careful here, must reference properly
//as stack may be more interesting to work with
//This could be made much simpler by just indexing "self", but done this way (with custom self-addressing modes) increases peformance a lot.
const GDParser : : IdentifierNode * in = static_cast < const GDParser : : IdentifierNode * > ( p_expression ) ;
StringName identifier = in - > name ;
// TRY STACK!
2014-11-02 15:31:01 +01:00
if ( ! p_initializer & & codegen . stack_identifiers . has ( identifier ) ) {
2014-02-10 02:10:30 +01:00
int pos = codegen . stack_identifiers [ identifier ] ;
return pos | ( GDFunction : : ADDR_TYPE_STACK_VARIABLE < < GDFunction : : ADDR_BITS ) ;
}
2014-11-02 15:31:01 +01:00
//TRY MEMBERS!
2014-02-10 02:10:30 +01:00
if ( ! codegen . function_node | | ! codegen . function_node - > _static ) {
// TRY MEMBER VARIABLES!
//static function
if ( codegen . script - > member_indices . has ( identifier ) ) {
2014-10-28 02:54:32 +01:00
int idx = codegen . script - > member_indices [ identifier ] . index ;
2014-02-10 02:10:30 +01:00
return idx | ( GDFunction : : ADDR_TYPE_MEMBER < < GDFunction : : ADDR_BITS ) ; //argument (stack root)
}
}
//TRY CLASS CONSTANTS
2014-05-02 17:40:34 +02:00
GDScript * owner = codegen . script ;
while ( owner ) {
2014-02-10 02:10:30 +01:00
2014-05-02 17:40:34 +02:00
GDScript * scr = owner ;
GDNativeClass * nc = NULL ;
while ( scr ) {
2014-02-10 02:10:30 +01:00
2014-05-02 17:40:34 +02:00
if ( scr - > constants . has ( identifier ) ) {
//int idx=scr->constants[identifier];
int idx = codegen . get_name_map_pos ( identifier ) ;
return idx | ( GDFunction : : ADDR_TYPE_CLASS_CONSTANT < < GDFunction : : ADDR_BITS ) ; //argument (stack root)
}
if ( scr - > native . is_valid ( ) )
nc = scr - > native . ptr ( ) ;
scr = scr - > _base ;
2014-02-10 02:10:30 +01:00
}
2014-05-02 17:40:34 +02:00
// CLASS C++ Integer Constant
2014-02-10 02:10:30 +01:00
2014-05-02 17:40:34 +02:00
if ( nc ) {
2014-02-10 02:10:30 +01:00
2014-05-02 17:40:34 +02:00
bool success = false ;
int constant = ObjectTypeDB : : get_integer_constant ( nc - > get_name ( ) , identifier , & success ) ;
if ( success ) {
Variant key = constant ;
int idx ;
2014-02-10 02:10:30 +01:00
2014-05-02 17:40:34 +02:00
if ( ! codegen . constant_map . has ( key ) ) {
2014-02-10 02:10:30 +01:00
2014-05-02 17:40:34 +02:00
idx = codegen . constant_map . size ( ) ;
codegen . constant_map [ key ] = idx ;
2014-02-10 02:10:30 +01:00
2014-05-02 17:40:34 +02:00
} else {
idx = codegen . constant_map [ key ] ;
}
return idx | ( GDFunction : : ADDR_TYPE_LOCAL_CONSTANT < < GDFunction : : ADDR_BITS ) ; //make it a local constant (faster access)
2014-02-10 02:10:30 +01:00
}
}
2014-05-02 17:40:34 +02:00
owner = owner - > _owner ;
2014-02-10 02:10:30 +01:00
}
2014-05-02 17:40:34 +02:00
/*
handled in constants now
if ( codegen . script - > subclasses . has ( identifier ) ) {
2014-02-10 02:10:30 +01:00
//same with a subclass, make it a local constant.
int idx = codegen . get_constant_pos ( codegen . script - > subclasses [ identifier ] ) ;
return idx | ( GDFunction : : ADDR_TYPE_LOCAL_CONSTANT < < GDFunction : : ADDR_BITS ) ; //make it a local constant (faster access)
2014-05-02 17:40:34 +02:00
} */
2014-02-10 02:10:30 +01:00
if ( GDScriptLanguage : : get_singleton ( ) - > get_global_map ( ) . has ( identifier ) ) {
int idx = GDScriptLanguage : : get_singleton ( ) - > get_global_map ( ) [ identifier ] ;
return idx | ( GDFunction : : ADDR_TYPE_GLOBAL < < GDFunction : : ADDR_BITS ) ; //argument (stack root)
}
//not found, error
_set_error ( " Identifier not found: " + String ( identifier ) , p_expression ) ;
return - 1 ;
} break ;
case GDParser : : Node : : TYPE_CONSTANT : {
//return constant
const GDParser : : ConstantNode * cn = static_cast < const GDParser : : ConstantNode * > ( p_expression ) ;
int idx ;
if ( ! codegen . constant_map . has ( cn - > value ) ) {
idx = codegen . constant_map . size ( ) ;
codegen . constant_map [ cn - > value ] = idx ;
} else {
idx = codegen . constant_map [ cn - > value ] ;
}
return idx | ( GDFunction : : ADDR_TYPE_LOCAL_CONSTANT < < GDFunction : : ADDR_BITS ) ; //argument (stack root)
} break ;
case GDParser : : Node : : TYPE_SELF : {
//return constant
if ( codegen . function_node & & codegen . function_node - > _static ) {
_set_error ( " 'self' not present in static function! " , p_expression ) ;
return - 1 ;
}
return ( GDFunction : : ADDR_TYPE_SELF < < GDFunction : : ADDR_BITS ) ;
} break ;
case GDParser : : Node : : TYPE_ARRAY : {
const GDParser : : ArrayNode * an = static_cast < const GDParser : : ArrayNode * > ( p_expression ) ;
Vector < int > values ;
int slevel = p_stack_level ;
for ( int i = 0 ; i < an - > elements . size ( ) ; i + + ) {
int ret = _parse_expression ( codegen , an - > elements [ i ] , slevel ) ;
if ( ret < 0 )
return ret ;
if ( ret & GDFunction : : ADDR_TYPE_STACK < < GDFunction : : ADDR_BITS ) {
slevel + + ;
codegen . alloc_stack ( slevel ) ;
}
values . push_back ( ret ) ;
}
codegen . opcodes . push_back ( GDFunction : : OPCODE_CONSTRUCT_ARRAY ) ;
codegen . opcodes . push_back ( values . size ( ) ) ;
for ( int i = 0 ; i < values . size ( ) ; i + + )
codegen . opcodes . push_back ( values [ i ] ) ;
int dst_addr = ( p_stack_level ) | ( GDFunction : : ADDR_TYPE_STACK < < GDFunction : : ADDR_BITS ) ;
codegen . opcodes . push_back ( dst_addr ) ; // append the stack level as destination address of the opcode
codegen . alloc_stack ( p_stack_level ) ;
return dst_addr ;
} break ;
case GDParser : : Node : : TYPE_DICTIONARY : {
const GDParser : : DictionaryNode * dn = static_cast < const GDParser : : DictionaryNode * > ( p_expression ) ;
Vector < int > values ;
int slevel = p_stack_level ;
for ( int i = 0 ; i < dn - > elements . size ( ) ; i + + ) {
int ret = _parse_expression ( codegen , dn - > elements [ i ] . key , slevel ) ;
if ( ret < 0 )
return ret ;
if ( ret & GDFunction : : ADDR_TYPE_STACK < < GDFunction : : ADDR_BITS ) {
slevel + + ;
codegen . alloc_stack ( slevel ) ;
}
values . push_back ( ret ) ;
ret = _parse_expression ( codegen , dn - > elements [ i ] . value , slevel ) ;
if ( ret < 0 )
return ret ;
if ( ret & GDFunction : : ADDR_TYPE_STACK < < GDFunction : : ADDR_BITS ) {
slevel + + ;
codegen . alloc_stack ( slevel ) ;
}
values . push_back ( ret ) ;
}
codegen . opcodes . push_back ( GDFunction : : OPCODE_CONSTRUCT_DICTIONARY ) ;
codegen . opcodes . push_back ( dn - > elements . size ( ) ) ;
for ( int i = 0 ; i < values . size ( ) ; i + + )
codegen . opcodes . push_back ( values [ i ] ) ;
int dst_addr = ( p_stack_level ) | ( GDFunction : : ADDR_TYPE_STACK < < GDFunction : : ADDR_BITS ) ;
codegen . opcodes . push_back ( dst_addr ) ; // append the stack level as destination address of the opcode
codegen . alloc_stack ( p_stack_level ) ;
return dst_addr ;
} break ;
case GDParser : : Node : : TYPE_OPERATOR : {
//hell breaks loose
const GDParser : : OperatorNode * on = static_cast < const GDParser : : OperatorNode * > ( p_expression ) ;
switch ( on - > op ) {
//call/constructor operator
case GDParser : : OperatorNode : : OP_PARENT_CALL : {
ERR_FAIL_COND_V ( on - > arguments . size ( ) < 1 , - 1 ) ;
const GDParser : : IdentifierNode * in = ( const GDParser : : IdentifierNode * ) on - > arguments [ 0 ] ;
Vector < int > arguments ;
int slevel = p_stack_level ;
for ( int i = 1 ; i < on - > arguments . size ( ) ; i + + ) {
int ret = _parse_expression ( codegen , on - > arguments [ i ] , slevel ) ;
if ( ret < 0 )
return ret ;
if ( ret & GDFunction : : ADDR_TYPE_STACK < < GDFunction : : ADDR_BITS ) {
slevel + + ;
codegen . alloc_stack ( slevel ) ;
}
arguments . push_back ( ret ) ;
}
//push call bytecode
codegen . opcodes . push_back ( GDFunction : : OPCODE_CALL_SELF_BASE ) ; // basic type constructor
codegen . opcodes . push_back ( codegen . get_name_map_pos ( in - > name ) ) ; //instance
codegen . opcodes . push_back ( arguments . size ( ) ) ; //argument count
codegen . alloc_call ( arguments . size ( ) ) ;
for ( int i = 0 ; i < arguments . size ( ) ; i + + )
codegen . opcodes . push_back ( arguments [ i ] ) ; //arguments
} break ;
case GDParser : : OperatorNode : : OP_CALL : {
if ( on - > arguments [ 0 ] - > type = = GDParser : : Node : : TYPE_TYPE ) {
//construct a basic type
ERR_FAIL_COND_V ( on - > arguments . size ( ) < 1 , - 1 ) ;
const GDParser : : TypeNode * tn = ( const GDParser : : TypeNode * ) on - > arguments [ 0 ] ;
int vtype = tn - > vtype ;
Vector < int > arguments ;
int slevel = p_stack_level ;
for ( int i = 1 ; i < on - > arguments . size ( ) ; i + + ) {
int ret = _parse_expression ( codegen , on - > arguments [ i ] , slevel ) ;
if ( ret < 0 )
return ret ;
if ( ret & GDFunction : : ADDR_TYPE_STACK < < GDFunction : : ADDR_BITS ) {
slevel + + ;
codegen . alloc_stack ( slevel ) ;
}
arguments . push_back ( ret ) ;
}
//push call bytecode
codegen . opcodes . push_back ( GDFunction : : OPCODE_CONSTRUCT ) ; // basic type constructor
codegen . opcodes . push_back ( vtype ) ; //instance
codegen . opcodes . push_back ( arguments . size ( ) ) ; //argument count
codegen . alloc_call ( arguments . size ( ) ) ;
for ( int i = 0 ; i < arguments . size ( ) ; i + + )
codegen . opcodes . push_back ( arguments [ i ] ) ; //arguments
} else if ( on - > arguments [ 0 ] - > type = = GDParser : : Node : : TYPE_BUILT_IN_FUNCTION ) {
//built in function
ERR_FAIL_COND_V ( on - > arguments . size ( ) < 1 , - 1 ) ;
Vector < int > arguments ;
int slevel = p_stack_level ;
for ( int i = 1 ; i < on - > arguments . size ( ) ; i + + ) {
int ret = _parse_expression ( codegen , on - > arguments [ i ] , slevel ) ;
if ( ret < 0 )
return ret ;
if ( ret & GDFunction : : ADDR_TYPE_STACK < < GDFunction : : ADDR_BITS ) {
slevel + + ;
codegen . alloc_stack ( slevel ) ;
}
arguments . push_back ( ret ) ;
}
codegen . opcodes . push_back ( GDFunction : : OPCODE_CALL_BUILT_IN ) ;
codegen . opcodes . push_back ( static_cast < const GDParser : : BuiltInFunctionNode * > ( on - > arguments [ 0 ] ) - > function ) ;
codegen . opcodes . push_back ( on - > arguments . size ( ) - 1 ) ;
codegen . alloc_call ( on - > arguments . size ( ) - 1 ) ;
for ( int i = 0 ; i < arguments . size ( ) ; i + + )
codegen . opcodes . push_back ( arguments [ i ] ) ;
} else {
//regular function
ERR_FAIL_COND_V ( on - > arguments . size ( ) < 2 , - 1 ) ;
const GDParser : : Node * instance = on - > arguments [ 0 ] ;
2014-05-01 16:34:10 +02:00
bool in_static = false ;
2014-02-10 02:10:30 +01:00
if ( instance - > type = = GDParser : : Node : : TYPE_SELF ) {
//room for optimization
}
Vector < int > arguments ;
int slevel = p_stack_level ;
for ( int i = 0 ; i < on - > arguments . size ( ) ; i + + ) {
int ret ;
2014-05-01 16:34:10 +02:00
if ( i = = 0 & & on - > arguments [ i ] - > type = = GDParser : : Node : : TYPE_SELF & & codegen . function_node & & codegen . function_node - > _static ) {
//static call to self
ret = ( GDFunction : : ADDR_TYPE_CLASS < < GDFunction : : ADDR_BITS ) ;
} else if ( i = = 1 ) {
2014-02-10 02:10:30 +01:00
if ( on - > arguments [ i ] - > type ! = GDParser : : Node : : TYPE_IDENTIFIER ) {
_set_error ( " Attempt to call a non-identifier. " , on ) ;
return - 1 ;
}
GDParser : : IdentifierNode * id = static_cast < GDParser : : IdentifierNode * > ( on - > arguments [ i ] ) ;
ret = codegen . get_name_map_pos ( id - > name ) ;
} else {
2014-05-01 16:34:10 +02:00
2014-02-10 02:10:30 +01:00
ret = _parse_expression ( codegen , on - > arguments [ i ] , slevel ) ;
if ( ret < 0 )
return ret ;
if ( ret & GDFunction : : ADDR_TYPE_STACK < < GDFunction : : ADDR_BITS ) {
slevel + + ;
codegen . alloc_stack ( slevel ) ;
}
}
arguments . push_back ( ret ) ;
}
codegen . opcodes . push_back ( p_root ? GDFunction : : OPCODE_CALL : GDFunction : : OPCODE_CALL_RETURN ) ; // perform operator
codegen . opcodes . push_back ( on - > arguments . size ( ) - 2 ) ;
codegen . alloc_call ( on - > arguments . size ( ) - 2 ) ;
for ( int i = 0 ; i < arguments . size ( ) ; i + + )
codegen . opcodes . push_back ( arguments [ i ] ) ;
}
} break ;
2014-09-15 16:33:30 +02:00
case GDParser : : OperatorNode : : OP_YIELD : {
ERR_FAIL_COND_V ( on - > arguments . size ( ) & & on - > arguments . size ( ) ! = 2 , - 1 ) ;
Vector < int > arguments ;
int slevel = p_stack_level ;
for ( int i = 0 ; i < on - > arguments . size ( ) ; i + + ) {
int ret = _parse_expression ( codegen , on - > arguments [ i ] , slevel ) ;
if ( ret < 0 )
return ret ;
if ( ret & GDFunction : : ADDR_TYPE_STACK < < GDFunction : : ADDR_BITS ) {
slevel + + ;
codegen . alloc_stack ( slevel ) ;
}
arguments . push_back ( ret ) ;
}
//push call bytecode
codegen . opcodes . push_back ( arguments . size ( ) = = 0 ? GDFunction : : OPCODE_YIELD : GDFunction : : OPCODE_YIELD_SIGNAL ) ; // basic type constructor
for ( int i = 0 ; i < arguments . size ( ) ; i + + )
codegen . opcodes . push_back ( arguments [ i ] ) ; //arguments
codegen . opcodes . push_back ( GDFunction : : OPCODE_YIELD_RESUME ) ;
//next will be where to place the result :)
} break ;
2014-02-10 02:10:30 +01:00
//indexing operator
case GDParser : : OperatorNode : : OP_INDEX :
case GDParser : : OperatorNode : : OP_INDEX_NAMED : {
ERR_FAIL_COND_V ( on - > arguments . size ( ) ! = 2 , - 1 ) ;
int slevel = p_stack_level ;
bool named = ( on - > op = = GDParser : : OperatorNode : : OP_INDEX_NAMED ) ;
int from = _parse_expression ( codegen , on - > arguments [ 0 ] , slevel ) ;
if ( from < 0 )
return from ;
int index ;
if ( named ) {
index = codegen . get_name_map_pos ( static_cast < GDParser : : IdentifierNode * > ( on - > arguments [ 1 ] ) - > name ) ;
} else {
if ( on - > arguments [ 1 ] - > type = = GDParser : : Node : : TYPE_CONSTANT & & static_cast < const GDParser : : ConstantNode * > ( on - > arguments [ 1 ] ) - > value . get_type ( ) = = Variant : : STRING ) {
//also, somehow, named (speed up anyway)
StringName name = static_cast < const GDParser : : ConstantNode * > ( on - > arguments [ 1 ] ) - > value ;
index = codegen . get_name_map_pos ( name ) ;
named = true ;
} else {
//regular indexing
if ( from & GDFunction : : ADDR_TYPE_STACK < < GDFunction : : ADDR_BITS ) {
slevel + + ;
codegen . alloc_stack ( slevel ) ;
}
index = _parse_expression ( codegen , on - > arguments [ 1 ] , slevel ) ;
if ( index < 0 )
return index ;
}
}
codegen . opcodes . push_back ( named ? GDFunction : : OPCODE_GET_NAMED : GDFunction : : OPCODE_GET ) ; // perform operator
codegen . opcodes . push_back ( from ) ; // argument 1
codegen . opcodes . push_back ( index ) ; // argument 2 (unary only takes one parameter)
} break ;
case GDParser : : OperatorNode : : OP_AND : {
// AND operator with early out on failure
int res = _parse_expression ( codegen , on - > arguments [ 0 ] , p_stack_level ) ;
if ( res < 0 )
return res ;
codegen . opcodes . push_back ( GDFunction : : OPCODE_JUMP_IF_NOT ) ;
codegen . opcodes . push_back ( res ) ;
int jump_fail_pos = codegen . opcodes . size ( ) ;
codegen . opcodes . push_back ( 0 ) ;
res = _parse_expression ( codegen , on - > arguments [ 1 ] , p_stack_level ) ;
if ( res < 0 )
return res ;
codegen . opcodes . push_back ( GDFunction : : OPCODE_JUMP_IF_NOT ) ;
codegen . opcodes . push_back ( res ) ;
int jump_fail_pos2 = codegen . opcodes . size ( ) ;
codegen . opcodes . push_back ( 0 ) ;
codegen . alloc_stack ( p_stack_level ) ; //it will be used..
codegen . opcodes . push_back ( GDFunction : : OPCODE_ASSIGN_TRUE ) ;
codegen . opcodes . push_back ( p_stack_level | GDFunction : : ADDR_TYPE_STACK < < GDFunction : : ADDR_BITS ) ;
codegen . opcodes . push_back ( GDFunction : : OPCODE_JUMP ) ;
codegen . opcodes . push_back ( codegen . opcodes . size ( ) + 3 ) ;
codegen . opcodes [ jump_fail_pos ] = codegen . opcodes . size ( ) ;
codegen . opcodes [ jump_fail_pos2 ] = codegen . opcodes . size ( ) ;
codegen . opcodes . push_back ( GDFunction : : OPCODE_ASSIGN_FALSE ) ;
codegen . opcodes . push_back ( p_stack_level | GDFunction : : ADDR_TYPE_STACK < < GDFunction : : ADDR_BITS ) ;
return p_stack_level | GDFunction : : ADDR_TYPE_STACK < < GDFunction : : ADDR_BITS ;
} break ;
case GDParser : : OperatorNode : : OP_OR : {
// OR operator with early out on success
int res = _parse_expression ( codegen , on - > arguments [ 0 ] , p_stack_level ) ;
if ( res < 0 )
return res ;
codegen . opcodes . push_back ( GDFunction : : OPCODE_JUMP_IF ) ;
codegen . opcodes . push_back ( res ) ;
int jump_success_pos = codegen . opcodes . size ( ) ;
codegen . opcodes . push_back ( 0 ) ;
res = _parse_expression ( codegen , on - > arguments [ 1 ] , p_stack_level ) ;
if ( res < 0 )
return res ;
codegen . opcodes . push_back ( GDFunction : : OPCODE_JUMP_IF ) ;
codegen . opcodes . push_back ( res ) ;
int jump_success_pos2 = codegen . opcodes . size ( ) ;
codegen . opcodes . push_back ( 0 ) ;
codegen . alloc_stack ( p_stack_level ) ; //it will be used..
codegen . opcodes . push_back ( GDFunction : : OPCODE_ASSIGN_FALSE ) ;
codegen . opcodes . push_back ( p_stack_level | GDFunction : : ADDR_TYPE_STACK < < GDFunction : : ADDR_BITS ) ;
codegen . opcodes . push_back ( GDFunction : : OPCODE_JUMP ) ;
codegen . opcodes . push_back ( codegen . opcodes . size ( ) + 3 ) ;
codegen . opcodes [ jump_success_pos ] = codegen . opcodes . size ( ) ;
codegen . opcodes [ jump_success_pos2 ] = codegen . opcodes . size ( ) ;
codegen . opcodes . push_back ( GDFunction : : OPCODE_ASSIGN_TRUE ) ;
codegen . opcodes . push_back ( p_stack_level | GDFunction : : ADDR_TYPE_STACK < < GDFunction : : ADDR_BITS ) ;
return p_stack_level | GDFunction : : ADDR_TYPE_STACK < < GDFunction : : ADDR_BITS ;
} break ;
//unary operators
case GDParser : : OperatorNode : : OP_NEG : { if ( ! _create_unary_operator ( codegen , on , Variant : : OP_NEGATE , p_stack_level ) ) return - 1 ; } break ;
case GDParser : : OperatorNode : : OP_NOT : { if ( ! _create_unary_operator ( codegen , on , Variant : : OP_NOT , p_stack_level ) ) return - 1 ; } break ;
case GDParser : : OperatorNode : : OP_BIT_INVERT : { if ( ! _create_unary_operator ( codegen , on , Variant : : OP_BIT_NEGATE , p_stack_level ) ) return - 1 ; } break ;
case GDParser : : OperatorNode : : OP_PREINC : { } break ; //?
case GDParser : : OperatorNode : : OP_PREDEC : { } break ;
case GDParser : : OperatorNode : : OP_INC : { } break ;
case GDParser : : OperatorNode : : OP_DEC : { } break ;
//binary operators (in precedence order)
case GDParser : : OperatorNode : : OP_IN : { if ( ! _create_binary_operator ( codegen , on , Variant : : OP_IN , p_stack_level ) ) return - 1 ; } break ;
case GDParser : : OperatorNode : : OP_EQUAL : { if ( ! _create_binary_operator ( codegen , on , Variant : : OP_EQUAL , p_stack_level ) ) return - 1 ; } break ;
case GDParser : : OperatorNode : : OP_NOT_EQUAL : { if ( ! _create_binary_operator ( codegen , on , Variant : : OP_NOT_EQUAL , p_stack_level ) ) return - 1 ; } break ;
case GDParser : : OperatorNode : : OP_LESS : { if ( ! _create_binary_operator ( codegen , on , Variant : : OP_LESS , p_stack_level ) ) return - 1 ; } break ;
case GDParser : : OperatorNode : : OP_LESS_EQUAL : { if ( ! _create_binary_operator ( codegen , on , Variant : : OP_LESS_EQUAL , p_stack_level ) ) return - 1 ; } break ;
case GDParser : : OperatorNode : : OP_GREATER : { if ( ! _create_binary_operator ( codegen , on , Variant : : OP_GREATER , p_stack_level ) ) return - 1 ; } break ;
case GDParser : : OperatorNode : : OP_GREATER_EQUAL : { if ( ! _create_binary_operator ( codegen , on , Variant : : OP_GREATER_EQUAL , p_stack_level ) ) return - 1 ; } break ;
case GDParser : : OperatorNode : : OP_ADD : { if ( ! _create_binary_operator ( codegen , on , Variant : : OP_ADD , p_stack_level ) ) return - 1 ; } break ;
case GDParser : : OperatorNode : : OP_SUB : { if ( ! _create_binary_operator ( codegen , on , Variant : : OP_SUBSTRACT , p_stack_level ) ) return - 1 ; } break ;
case GDParser : : OperatorNode : : OP_MUL : { if ( ! _create_binary_operator ( codegen , on , Variant : : OP_MULTIPLY , p_stack_level ) ) return - 1 ; } break ;
case GDParser : : OperatorNode : : OP_DIV : { if ( ! _create_binary_operator ( codegen , on , Variant : : OP_DIVIDE , p_stack_level ) ) return - 1 ; } break ;
case GDParser : : OperatorNode : : OP_MOD : { if ( ! _create_binary_operator ( codegen , on , Variant : : OP_MODULE , p_stack_level ) ) return - 1 ; } break ;
//case GDParser::OperatorNode::OP_SHIFT_LEFT: { if (!_create_binary_operator(codegen,on,Variant::OP_SHIFT_LEFT,p_stack_level)) return -1;} break;
//case GDParser::OperatorNode::OP_SHIFT_RIGHT: { if (!_create_binary_operator(codegen,on,Variant::OP_SHIFT_RIGHT,p_stack_level)) return -1;} break;
case GDParser : : OperatorNode : : OP_BIT_AND : { if ( ! _create_binary_operator ( codegen , on , Variant : : OP_BIT_AND , p_stack_level ) ) return - 1 ; } break ;
case GDParser : : OperatorNode : : OP_BIT_OR : { if ( ! _create_binary_operator ( codegen , on , Variant : : OP_BIT_OR , p_stack_level ) ) return - 1 ; } break ;
case GDParser : : OperatorNode : : OP_BIT_XOR : { if ( ! _create_binary_operator ( codegen , on , Variant : : OP_BIT_XOR , p_stack_level ) ) return - 1 ; } break ;
//shift
2014-09-15 16:33:30 +02:00
case GDParser : : OperatorNode : : OP_SHIFT_LEFT : { if ( ! _create_binary_operator ( codegen , on , Variant : : OP_SHIFT_LEFT , p_stack_level ) ) return - 1 ; } break ;
case GDParser : : OperatorNode : : OP_SHIFT_RIGHT : { if ( ! _create_binary_operator ( codegen , on , Variant : : OP_SHIFT_RIGHT , p_stack_level ) ) return - 1 ; } break ;
2014-02-10 02:10:30 +01:00
//assignment operators
case GDParser : : OperatorNode : : OP_ASSIGN_ADD :
case GDParser : : OperatorNode : : OP_ASSIGN_SUB :
case GDParser : : OperatorNode : : OP_ASSIGN_MUL :
case GDParser : : OperatorNode : : OP_ASSIGN_DIV :
case GDParser : : OperatorNode : : OP_ASSIGN_MOD :
case GDParser : : OperatorNode : : OP_ASSIGN_SHIFT_LEFT :
case GDParser : : OperatorNode : : OP_ASSIGN_SHIFT_RIGHT :
case GDParser : : OperatorNode : : OP_ASSIGN_BIT_AND :
case GDParser : : OperatorNode : : OP_ASSIGN_BIT_OR :
case GDParser : : OperatorNode : : OP_ASSIGN_BIT_XOR :
2014-11-02 15:31:01 +01:00
case GDParser : : OperatorNode : : OP_INIT_ASSIGN :
2014-02-10 02:10:30 +01:00
case GDParser : : OperatorNode : : OP_ASSIGN : {
ERR_FAIL_COND_V ( on - > arguments . size ( ) ! = 2 , - 1 ) ;
if ( on - > arguments [ 0 ] - > type = = GDParser : : Node : : TYPE_OPERATOR & & ( static_cast < GDParser : : OperatorNode * > ( on - > arguments [ 0 ] ) - > op = = GDParser : : OperatorNode : : OP_INDEX | | static_cast < GDParser : : OperatorNode * > ( on - > arguments [ 0 ] ) - > op = = GDParser : : OperatorNode : : OP_INDEX_NAMED ) ) {
//SET (chained) MODE!!
int slevel = p_stack_level ;
GDParser : : OperatorNode * op = static_cast < GDParser : : OperatorNode * > ( on - > arguments [ 0 ] ) ;
/* Find chain of sets */
List < GDParser : : OperatorNode * > chain ;
{
//create get/set chain
GDParser : : OperatorNode * n = op ;
while ( true ) {
chain . push_back ( n ) ;
if ( n - > arguments [ 0 ] - > type ! = GDParser : : Node : : TYPE_OPERATOR )
break ;
n = static_cast < GDParser : : OperatorNode * > ( n - > arguments [ 0 ] ) ;
if ( n - > op ! = GDParser : : OperatorNode : : OP_INDEX & & n - > op ! = GDParser : : OperatorNode : : OP_INDEX_NAMED )
break ;
}
}
/* Chain of gets */
//get at (potential) root stack pos, so it can be returned
int prev_pos = _parse_expression ( codegen , chain . back ( ) - > get ( ) - > arguments [ 0 ] , slevel ) ;
if ( prev_pos < 0 )
return prev_pos ;
int retval = prev_pos ;
2014-09-22 05:50:48 +02:00
//print_line("retval: "+itos(retval));
2014-02-10 02:10:30 +01:00
if ( retval & GDFunction : : ADDR_TYPE_STACK < < GDFunction : : ADDR_BITS ) {
slevel + + ;
codegen . alloc_stack ( slevel ) ;
}
Vector < int > setchain ;
2014-09-22 05:50:48 +02:00
int prev_key_idx = - 1 ;
2014-02-10 02:10:30 +01:00
for ( List < GDParser : : OperatorNode * > : : Element * E = chain . back ( ) ; E ; E = E - > prev ( ) ) {
if ( E = = chain . front ( ) ) //ignore first
break ;
bool named = E - > get ( ) - > op = = GDParser : : OperatorNode : : OP_INDEX_NAMED ;
int key_idx ;
if ( named ) {
2014-09-22 05:50:48 +02:00
key_idx = codegen . get_name_map_pos ( static_cast < const GDParser : : IdentifierNode * > ( E - > get ( ) - > arguments [ 1 ] ) - > name ) ;
//printf("named key %x\n",key_idx);
2014-02-10 02:10:30 +01:00
} else {
2014-09-22 05:50:48 +02:00
if ( prev_pos & ( GDFunction : : ADDR_TYPE_STACK < < GDFunction : : ADDR_BITS ) ) {
2014-02-10 02:10:30 +01:00
slevel + + ;
codegen . alloc_stack ( slevel ) ;
}
2014-09-22 05:50:48 +02:00
GDParser : : Node * key = E - > get ( ) - > arguments [ 1 ] ;
key_idx = _parse_expression ( codegen , key , slevel ) ;
//printf("expr key %x\n",key_idx);
//stack was raised here if retval was stack but..
2014-02-10 02:10:30 +01:00
}
if ( key_idx < 0 )
return key_idx ;
codegen . opcodes . push_back ( named ? GDFunction : : OPCODE_GET_NAMED : GDFunction : : OPCODE_GET ) ;
codegen . opcodes . push_back ( prev_pos ) ;
codegen . opcodes . push_back ( key_idx ) ;
slevel + + ;
codegen . alloc_stack ( slevel ) ;
int dst_pos = ( GDFunction : : ADDR_TYPE_STACK < < GDFunction : : ADDR_BITS ) | slevel ;
2014-09-22 05:50:48 +02:00
2014-02-10 02:10:30 +01:00
codegen . opcodes . push_back ( dst_pos ) ;
//add in reverse order, since it will be reverted
setchain . push_back ( dst_pos ) ;
setchain . push_back ( key_idx ) ;
setchain . push_back ( prev_pos ) ;
setchain . push_back ( named ? GDFunction : : OPCODE_SET_NAMED : GDFunction : : OPCODE_SET ) ;
prev_pos = dst_pos ;
2014-09-22 05:50:48 +02:00
prev_key_idx = key_idx ;
2014-02-10 02:10:30 +01:00
}
setchain . invert ( ) ;
int set_index ;
bool named = false ;
if ( static_cast < const GDParser : : OperatorNode * > ( op ) - > op = = GDParser : : OperatorNode : : OP_INDEX_NAMED ) {
set_index = codegen . get_name_map_pos ( static_cast < const GDParser : : IdentifierNode * > ( op - > arguments [ 1 ] ) - > name ) ;
named = true ;
} else {
set_index = _parse_expression ( codegen , op - > arguments [ 1 ] , slevel + 1 ) ;
named = false ;
}
if ( set_index < 0 )
return set_index ;
if ( set_index & GDFunction : : ADDR_TYPE_STACK < < GDFunction : : ADDR_BITS ) {
slevel + + ;
codegen . alloc_stack ( slevel ) ;
}
int set_value = _parse_assign_right_expression ( codegen , on , slevel + 1 ) ;
if ( set_value < 0 )
return set_value ;
codegen . opcodes . push_back ( named ? GDFunction : : OPCODE_SET_NAMED : GDFunction : : OPCODE_SET ) ;
codegen . opcodes . push_back ( prev_pos ) ;
codegen . opcodes . push_back ( set_index ) ;
codegen . opcodes . push_back ( set_value ) ;
for ( int i = 0 ; i < setchain . size ( ) ; i + = 4 ) {
codegen . opcodes . push_back ( setchain [ i + 0 ] ) ;
codegen . opcodes . push_back ( setchain [ i + 1 ] ) ;
codegen . opcodes . push_back ( setchain [ i + 2 ] ) ;
codegen . opcodes . push_back ( setchain [ i + 3 ] ) ;
}
return retval ;
} else {
//ASSIGNMENT MODE!!
int slevel = p_stack_level ;
2014-11-02 15:31:01 +01:00
int dst_address_a = _parse_expression ( codegen , on - > arguments [ 0 ] , slevel , false , on - > op = = GDParser : : OperatorNode : : OP_INIT_ASSIGN ) ;
2014-02-10 02:10:30 +01:00
if ( dst_address_a < 0 )
return - 1 ;
if ( dst_address_a & GDFunction : : ADDR_TYPE_STACK < < GDFunction : : ADDR_BITS ) {
slevel + + ;
codegen . alloc_stack ( slevel ) ;
}
int src_address_b = _parse_assign_right_expression ( codegen , on , slevel ) ;
if ( src_address_b < 0 )
return - 1 ;
codegen . opcodes . push_back ( GDFunction : : OPCODE_ASSIGN ) ; // perform operator
codegen . opcodes . push_back ( dst_address_a ) ; // argument 1
codegen . opcodes . push_back ( src_address_b ) ; // argument 2 (unary only takes one parameter)
return dst_address_a ; //if anything, returns wathever was assigned or correct stack position
}
} break ;
case GDParser : : OperatorNode : : OP_EXTENDS : {
ERR_FAIL_COND_V ( on - > arguments . size ( ) ! = 2 , false ) ;
int slevel = p_stack_level ;
int src_address_a = _parse_expression ( codegen , on - > arguments [ 0 ] , slevel ) ;
if ( src_address_a < 0 )
return - 1 ;
if ( src_address_a & GDFunction : : ADDR_TYPE_STACK < < GDFunction : : ADDR_BITS )
slevel + + ; //uses stack for return, increase stack
int src_address_b = _parse_expression ( codegen , on - > arguments [ 1 ] , slevel ) ;
if ( src_address_b < 0 )
return - 1 ;
codegen . opcodes . push_back ( GDFunction : : OPCODE_EXTENDS_TEST ) ; // perform operator
codegen . opcodes . push_back ( src_address_a ) ; // argument 1
codegen . opcodes . push_back ( src_address_b ) ; // argument 2 (unary only takes one parameter)
} break ;
default : {
ERR_EXPLAIN ( " Bug in bytecode compiler, unexpected operator # " + itos ( on - > op ) + " in parse tree while parsing expression. " ) ;
ERR_FAIL_V ( 0 ) ; //unreachable code
} break ;
}
int dst_addr = ( p_stack_level ) | ( GDFunction : : ADDR_TYPE_STACK < < GDFunction : : ADDR_BITS ) ;
codegen . opcodes . push_back ( dst_addr ) ; // append the stack level as destination address of the opcode
codegen . alloc_stack ( p_stack_level ) ;
return dst_addr ;
} break ;
//TYPE_TYPE,
default : {
ERR_EXPLAIN ( " Bug in bytecode compiler, unexpected node in parse tree while parsing expression. " ) ;
ERR_FAIL_V ( - 1 ) ; //unreachable code
} break ;
}
ERR_FAIL_V ( - 1 ) ; //unreachable code
}
Error GDCompiler : : _parse_block ( CodeGen & codegen , const GDParser : : BlockNode * p_block , int p_stack_level , int p_break_addr , int p_continue_addr ) {
codegen . push_stack_identifiers ( ) ;
int new_identifiers = 0 ;
codegen . current_line = p_block - > line ;
for ( int i = 0 ; i < p_block - > statements . size ( ) ; i + + ) {
const GDParser : : Node * s = p_block - > statements [ i ] ;
switch ( s - > type ) {
case GDParser : : Node : : TYPE_NEWLINE : {
const GDParser : : NewLineNode * nl = static_cast < const GDParser : : NewLineNode * > ( s ) ;
codegen . opcodes . push_back ( GDFunction : : OPCODE_LINE ) ;
codegen . opcodes . push_back ( nl - > line ) ;
codegen . current_line = nl - > line ;
} break ;
case GDParser : : Node : : TYPE_CONTROL_FLOW : {
// try subblocks
const GDParser : : ControlFlowNode * cf = static_cast < const GDParser : : ControlFlowNode * > ( s ) ;
switch ( cf - > cf_type ) {
case GDParser : : ControlFlowNode : : CF_IF : {
# ifdef DEBUG_ENABLED
codegen . opcodes . push_back ( GDFunction : : OPCODE_LINE ) ;
codegen . opcodes . push_back ( cf - > line ) ;
codegen . current_line = cf - > line ;
# endif
int ret = _parse_expression ( codegen , cf - > arguments [ 0 ] , p_stack_level , false ) ;
if ( ret < 0 )
return ERR_PARSE_ERROR ;
codegen . opcodes . push_back ( GDFunction : : OPCODE_JUMP_IF_NOT ) ;
codegen . opcodes . push_back ( ret ) ;
int else_addr = codegen . opcodes . size ( ) ;
codegen . opcodes . push_back ( 0 ) ; //temporary
Error err = _parse_block ( codegen , cf - > body , p_stack_level , p_break_addr , p_continue_addr ) ;
if ( err )
return err ;
if ( cf - > body_else ) {
codegen . opcodes . push_back ( GDFunction : : OPCODE_JUMP ) ;
int end_addr = codegen . opcodes . size ( ) ;
codegen . opcodes . push_back ( 0 ) ;
codegen . opcodes [ else_addr ] = codegen . opcodes . size ( ) ;
Error err = _parse_block ( codegen , cf - > body_else , p_stack_level , p_break_addr , p_continue_addr ) ;
if ( err )
return err ;
codegen . opcodes [ end_addr ] = codegen . opcodes . size ( ) ;
} else {
//end without else
codegen . opcodes [ else_addr ] = codegen . opcodes . size ( ) ;
}
} break ;
case GDParser : : ControlFlowNode : : CF_FOR : {
int slevel = p_stack_level ;
int iter_stack_pos = slevel ;
int iterator_pos = ( slevel + + ) | ( GDFunction : : ADDR_TYPE_STACK < < GDFunction : : ADDR_BITS ) ;
int counter_pos = ( slevel + + ) | ( GDFunction : : ADDR_TYPE_STACK < < GDFunction : : ADDR_BITS ) ;
int container_pos = ( slevel + + ) | ( GDFunction : : ADDR_TYPE_STACK < < GDFunction : : ADDR_BITS ) ;
codegen . alloc_stack ( slevel ) ;
codegen . push_stack_identifiers ( ) ;
codegen . add_stack_identifier ( static_cast < const GDParser : : IdentifierNode * > ( cf - > arguments [ 0 ] ) - > name , iter_stack_pos ) ;
int ret = _parse_expression ( codegen , cf - > arguments [ 1 ] , slevel , false ) ;
if ( ret < 0 )
return ERR_COMPILATION_FAILED ;
//assign container
codegen . opcodes . push_back ( GDFunction : : OPCODE_ASSIGN ) ;
codegen . opcodes . push_back ( container_pos ) ;
codegen . opcodes . push_back ( ret ) ;
//begin loop
codegen . opcodes . push_back ( GDFunction : : OPCODE_ITERATE_BEGIN ) ;
codegen . opcodes . push_back ( counter_pos ) ;
codegen . opcodes . push_back ( container_pos ) ;
codegen . opcodes . push_back ( codegen . opcodes . size ( ) + 4 ) ;
codegen . opcodes . push_back ( iterator_pos ) ;
codegen . opcodes . push_back ( GDFunction : : OPCODE_JUMP ) ; //skip code for next
codegen . opcodes . push_back ( codegen . opcodes . size ( ) + 8 ) ;
//break loop
int break_pos = codegen . opcodes . size ( ) ;
codegen . opcodes . push_back ( GDFunction : : OPCODE_JUMP ) ; //skip code for next
codegen . opcodes . push_back ( 0 ) ; //skip code for next
//next loop
int continue_pos = codegen . opcodes . size ( ) ;
codegen . opcodes . push_back ( GDFunction : : OPCODE_ITERATE ) ;
codegen . opcodes . push_back ( counter_pos ) ;
codegen . opcodes . push_back ( container_pos ) ;
codegen . opcodes . push_back ( break_pos ) ;
codegen . opcodes . push_back ( iterator_pos ) ;
Error err = _parse_block ( codegen , cf - > body , slevel , break_pos , continue_pos ) ;
if ( err )
return err ;
codegen . opcodes . push_back ( GDFunction : : OPCODE_JUMP ) ;
codegen . opcodes . push_back ( continue_pos ) ;
codegen . opcodes [ break_pos + 1 ] = codegen . opcodes . size ( ) ;
codegen . pop_stack_identifiers ( ) ;
} break ;
case GDParser : : ControlFlowNode : : CF_WHILE : {
codegen . opcodes . push_back ( GDFunction : : OPCODE_JUMP ) ;
codegen . opcodes . push_back ( codegen . opcodes . size ( ) + 3 ) ;
int break_addr = codegen . opcodes . size ( ) ;
codegen . opcodes . push_back ( GDFunction : : OPCODE_JUMP ) ;
codegen . opcodes . push_back ( 0 ) ;
int continue_addr = codegen . opcodes . size ( ) ;
int ret = _parse_expression ( codegen , cf - > arguments [ 0 ] , p_stack_level , false ) ;
if ( ret < 0 )
return ERR_PARSE_ERROR ;
codegen . opcodes . push_back ( GDFunction : : OPCODE_JUMP_IF_NOT ) ;
codegen . opcodes . push_back ( ret ) ;
codegen . opcodes . push_back ( break_addr ) ;
Error err = _parse_block ( codegen , cf - > body , p_stack_level , break_addr , continue_addr ) ;
if ( err )
return err ;
codegen . opcodes . push_back ( GDFunction : : OPCODE_JUMP ) ;
codegen . opcodes . push_back ( continue_addr ) ;
codegen . opcodes [ break_addr + 1 ] = codegen . opcodes . size ( ) ;
} break ;
case GDParser : : ControlFlowNode : : CF_SWITCH : {
} break ;
case GDParser : : ControlFlowNode : : CF_BREAK : {
if ( p_break_addr < 0 ) {
_set_error ( " 'break'' not within loop " , cf ) ;
return ERR_COMPILATION_FAILED ;
}
codegen . opcodes . push_back ( GDFunction : : OPCODE_JUMP ) ;
codegen . opcodes . push_back ( p_break_addr ) ;
} break ;
case GDParser : : ControlFlowNode : : CF_CONTINUE : {
if ( p_continue_addr < 0 ) {
_set_error ( " 'continue' not within loop " , cf ) ;
return ERR_COMPILATION_FAILED ;
}
codegen . opcodes . push_back ( GDFunction : : OPCODE_JUMP ) ;
codegen . opcodes . push_back ( p_continue_addr ) ;
} break ;
case GDParser : : ControlFlowNode : : CF_RETURN : {
int ret ;
if ( cf - > arguments . size ( ) ) {
ret = _parse_expression ( codegen , cf - > arguments [ 0 ] , p_stack_level , false ) ;
if ( ret < 0 )
return ERR_PARSE_ERROR ;
} else {
ret = GDFunction : : ADDR_TYPE_NIL < < GDFunction : : ADDR_BITS ;
}
codegen . opcodes . push_back ( GDFunction : : OPCODE_RETURN ) ;
codegen . opcodes . push_back ( ret ) ;
} break ;
}
} break ;
case GDParser : : Node : : TYPE_ASSERT : {
// try subblocks
const GDParser : : AssertNode * as = static_cast < const GDParser : : AssertNode * > ( s ) ;
int ret = _parse_expression ( codegen , as - > condition , p_stack_level , false ) ;
if ( ret < 0 )
return ERR_PARSE_ERROR ;
codegen . opcodes . push_back ( GDFunction : : OPCODE_ASSERT ) ;
codegen . opcodes . push_back ( ret ) ;
} break ;
case GDParser : : Node : : TYPE_LOCAL_VAR : {
const GDParser : : LocalVarNode * lv = static_cast < const GDParser : : LocalVarNode * > ( s ) ;
codegen . add_stack_identifier ( lv - > name , p_stack_level + + ) ;
codegen . alloc_stack ( p_stack_level ) ;
new_identifiers + + ;
} break ;
default : {
//expression
int ret = _parse_expression ( codegen , s , p_stack_level , true ) ;
if ( ret < 0 )
return ERR_PARSE_ERROR ;
} break ;
}
}
codegen . pop_stack_identifiers ( ) ;
return OK ;
}
Error GDCompiler : : _parse_function ( GDScript * p_script , const GDParser : : ClassNode * p_class , const GDParser : : FunctionNode * p_func ) {
Vector < int > bytecode ;
CodeGen codegen ;
codegen . class_node = p_class ;
codegen . script = p_script ;
codegen . function_node = p_func ;
codegen . stack_max = 0 ;
codegen . current_line = 0 ;
codegen . call_max = 0 ;
codegen . debug_stack = ScriptDebugger : : get_singleton ( ) ! = NULL ;
2014-12-17 02:31:57 +01:00
Vector < StringName > argnames ;
2014-02-10 02:10:30 +01:00
int stack_level = 0 ;
if ( p_func ) {
for ( int i = 0 ; i < p_func - > arguments . size ( ) ; i + + ) {
int idx = i ;
codegen . add_stack_identifier ( p_func - > arguments [ i ] , i ) ;
2014-12-17 02:31:57 +01:00
# ifdef TOOLS_ENABLED
argnames . push_back ( p_func - > arguments [ i ] ) ;
# endif
2014-02-10 02:10:30 +01:00
}
stack_level = p_func - > arguments . size ( ) ;
}
codegen . alloc_stack ( stack_level ) ;
/* Parse initializer -if applies- */
bool is_initializer = false | | ! p_func ;
if ( ! p_func | | String ( p_func - > name ) = = " _init " ) {
//parse initializer for class members
if ( ! p_func & & p_class - > extends_used & & p_script - > native . is_null ( ) ) {
//call implicit parent constructor
codegen . opcodes . push_back ( GDFunction : : OPCODE_CALL_SELF_BASE ) ;
codegen . opcodes . push_back ( codegen . get_name_map_pos ( " _init " ) ) ;
codegen . opcodes . push_back ( 0 ) ;
codegen . opcodes . push_back ( ( GDFunction : : ADDR_TYPE_STACK < < GDFunction : : ADDR_BITS ) | 0 ) ;
}
Error err = _parse_block ( codegen , p_class - > initializer , stack_level ) ;
if ( err )
return err ;
is_initializer = true ;
}
/* Parse default argument code -if applies- */
Vector < int > defarg_addr ;
StringName func_name ;
if ( p_func ) {
if ( p_func - > default_values . size ( ) ) {
codegen . opcodes . push_back ( GDFunction : : OPCODE_JUMP_TO_DEF_ARGUMENT ) ;
defarg_addr . push_back ( codegen . opcodes . size ( ) ) ;
for ( int i = 0 ; i < p_func - > default_values . size ( ) ; i + + ) {
_parse_expression ( codegen , p_func - > default_values [ i ] , stack_level , true ) ;
defarg_addr . push_back ( codegen . opcodes . size ( ) ) ;
}
defarg_addr . invert ( ) ;
}
Error err = _parse_block ( codegen , p_func - > body , stack_level ) ;
if ( err )
return err ;
func_name = p_func - > name ;
} else {
func_name = " _init " ;
}
codegen . opcodes . push_back ( GDFunction : : OPCODE_END ) ;
GDFunction * gdfunc = NULL ;
//if (String(p_func->name)=="") { //initializer func
// gdfunc = &p_script->initializer;
//} else { //regular func
p_script - > member_functions [ func_name ] = GDFunction ( ) ;
gdfunc = & p_script - > member_functions [ func_name ] ;
//}
if ( p_func )
gdfunc - > _static = p_func - > _static ;
2014-12-17 02:31:57 +01:00
# ifdef TOOLS_ENABLED
gdfunc - > arg_names = argnames ;
# endif
2014-02-10 02:10:30 +01:00
//constants
if ( codegen . constant_map . size ( ) ) {
gdfunc - > _constant_count = codegen . constant_map . size ( ) ;
gdfunc - > constants . resize ( codegen . constant_map . size ( ) ) ;
gdfunc - > _constants_ptr = & gdfunc - > constants [ 0 ] ;
const Variant * K = NULL ;
while ( ( K = codegen . constant_map . next ( K ) ) ) {
int idx = codegen . constant_map [ * K ] ;
gdfunc - > constants [ idx ] = * K ;
}
} else {
gdfunc - > _constants_ptr = NULL ;
gdfunc - > _constant_count = 0 ;
}
//global names
if ( codegen . name_map . size ( ) ) {
gdfunc - > global_names . resize ( codegen . name_map . size ( ) ) ;
gdfunc - > _global_names_ptr = & gdfunc - > global_names [ 0 ] ;
for ( Map < StringName , int > : : Element * E = codegen . name_map . front ( ) ; E ; E = E - > next ( ) ) {
gdfunc - > global_names [ E - > get ( ) ] = E - > key ( ) ;
}
gdfunc - > _global_names_count = gdfunc - > global_names . size ( ) ;
} else {
gdfunc - > _global_names_ptr = NULL ;
gdfunc - > _global_names_count = 0 ;
}
if ( codegen . opcodes . size ( ) ) {
gdfunc - > code = codegen . opcodes ;
gdfunc - > _code_ptr = & gdfunc - > code [ 0 ] ;
gdfunc - > _code_size = codegen . opcodes . size ( ) ;
} else {
gdfunc - > _code_ptr = NULL ;
gdfunc - > _code_size = 0 ;
}
if ( defarg_addr . size ( ) ) {
gdfunc - > default_arguments = defarg_addr ;
gdfunc - > _default_arg_count = defarg_addr . size ( ) ;
gdfunc - > _default_arg_ptr = & gdfunc - > default_arguments [ 0 ] ;
} else {
gdfunc - > _default_arg_count = 0 ;
gdfunc - > _default_arg_ptr = NULL ;
}
gdfunc - > _argument_count = p_func ? p_func - > arguments . size ( ) : 0 ;
gdfunc - > _stack_size = codegen . stack_max ;
gdfunc - > _call_size = codegen . call_max ;
gdfunc - > name = func_name ;
gdfunc - > _script = p_script ;
gdfunc - > source = source ;
2014-05-24 06:35:47 +02:00
# ifdef DEBUG_ENABLED
{
gdfunc - > func_cname = ( String ( source ) + " - " + String ( func_name ) ) . utf8 ( ) ;
gdfunc - > _func_cname = gdfunc - > func_cname . get_data ( ) ;
}
# endif
2014-02-10 02:10:30 +01:00
if ( p_func ) {
gdfunc - > _initial_line = p_func - > line ;
} else {
gdfunc - > _initial_line = 0 ;
}
if ( codegen . debug_stack )
gdfunc - > stack_debug = codegen . stack_debug ;
if ( is_initializer )
p_script - > initializer = gdfunc ;
return OK ;
}
Error GDCompiler : : _parse_class ( GDScript * p_script , GDScript * p_owner , const GDParser : : ClassNode * p_class ) {
p_script - > native = Ref < GDNativeClass > ( ) ;
p_script - > base = Ref < GDScript > ( ) ;
p_script - > _base = NULL ;
p_script - > members . clear ( ) ;
p_script - > constants . clear ( ) ;
p_script - > member_functions . clear ( ) ;
p_script - > member_indices . clear ( ) ;
p_script - > member_info . clear ( ) ;
p_script - > initializer = NULL ;
p_script - > subclasses . clear ( ) ;
p_script - > _owner = p_owner ;
p_script - > tool = p_class - > tool ;
p_script - > name = p_class - > name ;
int index_from = 0 ;
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)
2014-05-24 18:25:56 +02:00
if ( path . is_rel_path ( ) ) {
String base = p_script - > get_path ( ) ;
if ( base = = " " | | base . is_rel_path ( ) ) {
_set_error ( " Could not resolve relative path for parent class: " + path , p_class ) ;
return ERR_FILE_NOT_FOUND ;
}
path = base . get_base_dir ( ) . plus_file ( path ) ;
}
2014-02-10 02:10:30 +01:00
script = ResourceLoader : : load ( path ) ;
if ( script . is_null ( ) ) {
_set_error ( " Could not load base class: " + path , p_class ) ;
return ERR_FILE_NOT_FOUND ;
}
2014-06-16 15:22:26 +02:00
if ( ! script - > valid ) {
_set_error ( " Script not fully loaded (cyclic preload?): " + path , p_class ) ;
return ERR_BUSY ;
}
//print_line("EXTENDS PATH: "+path+" script is "+itos(script.is_valid())+" indices is "+itos(script->member_indices.size())+" valid? "+itos(script->valid));
2014-02-10 02:10:30 +01:00
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 - > subclasses . has ( sub ) ) {
script = script - > subclasses [ sub ] ;
} else {
_set_error ( " Could not find subclass: " + sub , p_class ) ;
return ERR_FILE_NOT_FOUND ;
}
}
}
} else {
ERR_FAIL_COND_V ( p_class - > extends_class . size ( ) = = 0 , ERR_BUG ) ;
//look around for the subclasses
String base = p_class - > extends_class [ 0 ] ;
GDScript * p = p_owner ;
Ref < GDScript > base_class ;
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 ;
}
}
script = base_class ;
} else {
if ( p_class - > extends_class . size ( ) > 1 ) {
_set_error ( " Invalid inheritance (unknown class+subclasses) " , p_class ) ;
return ERR_FILE_NOT_FOUND ;
}
//if not found, try engine classes
if ( ! GDScriptLanguage : : get_singleton ( ) - > get_global_map ( ) . has ( base ) ) {
_set_error ( " Unknown class: ' " + base + " ' " , p_class ) ;
return ERR_FILE_NOT_FOUND ;
}
int base_idx = GDScriptLanguage : : get_singleton ( ) - > get_global_map ( ) [ base ] ;
native = GDScriptLanguage : : get_singleton ( ) - > get_global_array ( ) [ base_idx ] ;
if ( ! native . is_valid ( ) ) {
_set_error ( " Global not a class: ' " + base + " ' " , p_class ) ;
return ERR_FILE_NOT_FOUND ;
}
}
}
if ( script . is_valid ( ) ) {
p_script - > base = script ;
p_script - > _base = p_script - > base . ptr ( ) ;
p_script - > member_indices = script - > member_indices ;
} else if ( native . is_valid ( ) ) {
p_script - > native = native ;
} else {
_set_error ( " Could not determine inheritance " , p_class ) ;
return ERR_FILE_NOT_FOUND ;
}
}
2014-08-14 15:31:38 +02:00
//print_line("Script: "+p_script->get_path()+" indices: "+itos(p_script->member_indices.size()));
2014-06-16 15:22:26 +02:00
2014-02-10 02:10:30 +01:00
for ( int i = 0 ; i < p_class - > variables . size ( ) ; i + + ) {
StringName name = p_class - > variables [ i ] . identifier ;
if ( p_script - > member_indices . has ( name ) ) {
_set_error ( " Member ' " + name + " ' already exists (in current or parent class) " , p_class ) ;
return ERR_ALREADY_EXISTS ;
}
if ( p_class - > variables [ i ] . _export . type ! = Variant : : NIL ) {
p_script - > member_info [ name ] = p_class - > variables [ i ] . _export ;
# ifdef TOOLS_ENABLED
if ( p_class - > variables [ i ] . default_value . get_type ( ) ! = Variant : : NIL ) {
p_script - > member_default_values [ name ] = p_class - > variables [ i ] . default_value ;
}
# endif
}
2014-10-28 02:54:32 +01:00
//int new_idx = p_script->member_indices.size();
GDScript : : MemberInfo minfo ;
minfo . index = p_script - > member_indices . size ( ) ;
minfo . setter = p_class - > variables [ i ] . setter ;
2015-01-03 04:57:02 +01:00
minfo . getter = p_class - > variables [ i ] . getter ;
2014-10-28 02:54:32 +01:00
p_script - > member_indices [ name ] = minfo ;
2014-02-10 02:10:30 +01:00
p_script - > members . insert ( name ) ;
}
for ( int i = 0 ; i < p_class - > constant_expressions . size ( ) ; i + + ) {
StringName name = p_class - > constant_expressions [ i ] . identifier ;
ERR_CONTINUE ( p_class - > constant_expressions [ i ] . expression - > type ! = GDParser : : Node : : TYPE_CONSTANT ) ;
GDParser : : ConstantNode * constant = static_cast < GDParser : : ConstantNode * > ( p_class - > constant_expressions [ i ] . expression ) ;
p_script - > constants . insert ( name , constant - > value ) ;
//p_script->constants[constant->value].make_const();
}
//parse sub-classes
for ( int i = 0 ; i < p_class - > subclasses . size ( ) ; i + + ) {
StringName name = p_class - > subclasses [ i ] - > name ;
Ref < GDScript > subclass = memnew ( GDScript ) ;
Error err = _parse_class ( subclass . ptr ( ) , p_script , p_class - > subclasses [ i ] ) ;
if ( err )
return err ;
2014-05-02 17:40:34 +02:00
p_script - > constants . insert ( name , subclass ) ; //once parsed, goes to the list of constants
2014-02-10 02:10:30 +01:00
p_script - > subclasses . insert ( name , subclass ) ;
}
//parse methods
bool has_initializer = false ;
for ( int i = 0 ; i < p_class - > functions . size ( ) ; i + + ) {
if ( ! has_initializer & & p_class - > functions [ i ] - > name = = " _init " )
has_initializer = true ;
Error err = _parse_function ( p_script , p_class , p_class - > functions [ i ] ) ;
if ( err )
return err ;
}
//parse static methods
for ( int i = 0 ; i < p_class - > static_functions . size ( ) ; i + + ) {
Error err = _parse_function ( p_script , p_class , p_class - > static_functions [ i ] ) ;
if ( err )
return err ;
}
if ( ! has_initializer ) {
//create a constructor
Error err = _parse_function ( p_script , p_class , NULL ) ;
if ( err )
return err ;
}
2015-01-03 04:57:02 +01:00
# ifdef DEBUG_ENABLED
//validate setters/getters if debug is enabled
for ( int i = 0 ; i < p_class - > variables . size ( ) ; i + + ) {
if ( p_class - > variables [ i ] . setter ) {
const Map < StringName , GDFunction > : : Element * E = p_script - > get_member_functions ( ) . find ( p_class - > variables [ i ] . setter ) ;
if ( ! E ) {
_set_error ( " Setter function ' " + String ( p_class - > variables [ i ] . setter ) + " ' not found in class. " , NULL ) ;
err_line = p_class - > variables [ i ] . line ;
err_column = 0 ;
return ERR_PARSE_ERROR ;
}
if ( E - > get ( ) . is_static ( ) ) {
_set_error ( " Setter function ' " + String ( p_class - > variables [ i ] . setter ) + " ' is static. " , NULL ) ;
err_line = p_class - > variables [ i ] . line ;
err_column = 0 ;
return ERR_PARSE_ERROR ;
}
}
if ( p_class - > variables [ i ] . getter ) {
const Map < StringName , GDFunction > : : Element * E = p_script - > get_member_functions ( ) . find ( p_class - > variables [ i ] . getter ) ;
if ( ! E ) {
_set_error ( " Getter function ' " + String ( p_class - > variables [ i ] . getter ) + " ' not found in class. " , NULL ) ;
err_line = p_class - > variables [ i ] . line ;
err_column = 0 ;
return ERR_PARSE_ERROR ;
}
if ( E - > get ( ) . is_static ( ) ) {
_set_error ( " Getter function ' " + String ( p_class - > variables [ i ] . getter ) + " ' is static. " , NULL ) ;
err_line = p_class - > variables [ i ] . line ;
err_column = 0 ;
return ERR_PARSE_ERROR ;
}
}
}
# endif
2014-02-10 02:10:30 +01:00
return OK ;
}
Error GDCompiler : : compile ( const GDParser * p_parser , GDScript * p_script ) {
err_line = - 1 ;
err_column = - 1 ;
error = " " ;
parser = p_parser ;
const GDParser : : Node * root = parser - > get_parse_tree ( ) ;
ERR_FAIL_COND_V ( root - > type ! = GDParser : : Node : : TYPE_CLASS , ERR_INVALID_DATA ) ;
source = p_script - > get_path ( ) ;
Error err = _parse_class ( p_script , NULL , static_cast < const GDParser : : ClassNode * > ( root ) ) ;
if ( err )
return err ;
return OK ;
}
String GDCompiler : : get_error ( ) const {
return error ;
}
int GDCompiler : : get_error_line ( ) const {
return err_line ;
}
int GDCompiler : : get_error_column ( ) const {
return err_column ;
}
GDCompiler : : GDCompiler ( )
{
}