diff options
Diffstat (limited to 'modules/gdscript/gdscript_parser.h')
-rw-r--r-- | modules/gdscript/gdscript_parser.h | 1672 |
1 files changed, 1166 insertions, 506 deletions
diff --git a/modules/gdscript/gdscript_parser.h b/modules/gdscript/gdscript_parser.h index c74d7dd856..4c9473c7bd 100644 --- a/modules/gdscript/gdscript_parser.h +++ b/modules/gdscript/gdscript_parser.h @@ -31,664 +31,1324 @@ #ifndef GDSCRIPT_PARSER_H #define GDSCRIPT_PARSER_H +#include "core/hash_map.h" +#include "core/io/multiplayer_api.h" +#include "core/list.h" #include "core/map.h" -#include "core/object.h" +#include "core/reference.h" +#include "core/resource.h" #include "core/script_language.h" +#include "core/string_name.h" +#include "core/ustring.h" +#include "core/variant.h" +#include "core/vector.h" +#include "gdscript_cache.h" #include "gdscript_functions.h" #include "gdscript_tokenizer.h" -struct GDScriptDataType; -struct GDScriptWarning; +#ifdef DEBUG_ENABLED +#include "core/string_builder.h" +#include "gdscript_warning.h" +#endif // DEBUG_ENABLED class GDScriptParser { + struct AnnotationInfo; + public: + // Forward-declare all parser nodes, to avoid ordering issues. + struct AnnotationNode; + struct ArrayNode; + struct AssertNode; + struct AssignmentNode; + struct AwaitNode; + struct BinaryOpNode; + struct BreakNode; + struct BreakpointNode; + struct CallNode; + struct CastNode; struct ClassNode; + struct ConstantNode; + struct ContinueNode; + struct DictionaryNode; + struct EnumNode; + struct ExpressionNode; + struct ForNode; + struct FunctionNode; + struct GetNodeNode; + struct IdentifierNode; + struct IfNode; + struct LiteralNode; + struct MatchNode; + struct MatchBranchNode; + struct ParameterNode; + struct PassNode; + struct PatternNode; + struct PreloadNode; + struct ReturnNode; + struct SelfNode; + struct SignalNode; + struct SubscriptNode; + struct SuiteNode; + struct TernaryOpNode; + struct TypeNode; + struct UnaryOpNode; + struct VariableNode; + struct WhileNode; struct DataType { - enum { + enum Kind { BUILTIN, NATIVE, SCRIPT, - GDSCRIPT, - CLASS, - UNRESOLVED - } kind; + CLASS, // GDScript. + ENUM, // Full enumeration. + ENUM_VALUE, // Value from enumeration. + VARIANT, // Can be any type. + UNRESOLVED, + // TODO: Enum + }; + Kind kind = UNRESOLVED; + + enum TypeSource { + UNDETECTED, // Can be any type. + INFERRED, // Has inferred type, but still dynamic. + ANNOTATED_EXPLICIT, // Has a specific type annotated. + ANNOTATED_INFERRED, // Has a static type but comes from the assigned value. + }; + TypeSource type_source = UNDETECTED; - bool has_type; - bool is_constant; - bool is_meta_type; // Whether the value can be used as a type - bool infer_type; - bool may_yield; // For function calls + bool is_constant = false; + bool is_meta_type = false; + bool is_coroutine = false; // For function calls. - Variant::Type builtin_type; + Variant::Type builtin_type = Variant::NIL; StringName native_type; + StringName enum_type; // Enum name or the value name in an enum. Ref<Script> script_type; - ClassNode *class_type; + String script_path; + ClassNode *class_type = nullptr; + MethodInfo method_info; // For callable/signals. + HashMap<StringName, int> enum_values; // For enums. + + _FORCE_INLINE_ bool is_set() const { return kind != UNRESOLVED; } + _FORCE_INLINE_ bool has_no_type() const { return type_source == UNDETECTED; } + _FORCE_INLINE_ bool is_variant() const { return kind == VARIANT || kind == UNRESOLVED; } + _FORCE_INLINE_ bool is_hard_type() const { return type_source > INFERRED; } String to_string() const; - bool operator==(const DataType &other) const { - if (!has_type || !other.has_type) { - return true; // Can be considered equal for parsing purpose + bool operator==(const DataType &p_other) const { + if (type_source == UNDETECTED || p_other.type_source == UNDETECTED) { + return true; // Can be consireded equal for parsing purposes. + } + + if (type_source == INFERRED || p_other.type_source == INFERRED) { + return true; // Can be consireded equal for parsing purposes. } - if (kind != other.kind) { + + if (kind != p_other.kind) { return false; } + switch (kind) { - case BUILTIN: { - return builtin_type == other.builtin_type; - } break; - case NATIVE: { - return native_type == other.native_type; - } break; - case GDSCRIPT: - case SCRIPT: { - return script_type == other.script_type; - } break; - case CLASS: { - return class_type == other.class_type; - } break; - case UNRESOLVED: { - } break; + case VARIANT: + return true; // All variants are the same. + case BUILTIN: + return builtin_type == p_other.builtin_type; + case NATIVE: + case ENUM: + return native_type == p_other.native_type; + case ENUM_VALUE: + return native_type == p_other.native_type && enum_type == p_other.enum_type; + case SCRIPT: + return script_type == p_other.script_type; + case CLASS: + return class_type == p_other.class_type; + case UNRESOLVED: + break; } + return false; } - DataType() : - kind(UNRESOLVED), - has_type(false), - is_constant(false), - is_meta_type(false), - infer_type(false), - may_yield(false), - builtin_type(Variant::NIL), - class_type(NULL) {} + bool operator!=(const DataType &p_other) const { + return !(this->operator==(p_other)); + } }; - struct Node { + struct ParserError { + // TODO: Do I really need a "type"? + // enum Type { + // NO_ERROR, + // EMPTY_FILE, + // CLASS_NAME_USED_TWICE, + // EXTENDS_USED_TWICE, + // EXPECTED_END_STATEMENT, + // }; + // Type type = NO_ERROR; + String message; + int line = 0, column = 0; + }; + 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_CAST, - TYPE_ASSERT, - TYPE_BREAKPOINT, - TYPE_NEWLINE, + NONE, + ANNOTATION, + ARRAY, + ASSERT, + ASSIGNMENT, + AWAIT, + BINARY_OPERATOR, + BREAK, + BREAKPOINT, + CALL, + CAST, + CLASS, + CONSTANT, + CONTINUE, + DICTIONARY, + ENUM, + FOR, + FUNCTION, + GET_NODE, + IDENTIFIER, + IF, + LITERAL, + MATCH, + MATCH_BRANCH, + PARAMETER, + PASS, + PATTERN, + PRELOAD, + RETURN, + SELF, + SIGNAL, + SUBSCRIPT, + SUITE, + TERNARY_OPERATOR, + TYPE, + UNARY_OPERATOR, + VARIABLE, + WHILE, }; - Node *next; - int line; - int column; - Type type; + Type type = NONE; + int start_line = 0, end_line = 0; + int start_column = 0, end_column = 0; + int leftmost_column = 0, rightmost_column = 0; + Node *next = nullptr; + List<AnnotationNode *> annotations; - virtual DataType get_datatype() const { return DataType(); } - virtual void set_datatype(const DataType &p_datatype) {} + DataType datatype; + + virtual DataType get_datatype() const { return datatype; } + virtual void set_datatype(const DataType &p_datatype) { datatype = p_datatype; } + + virtual bool is_expression() const { return false; } virtual ~Node() {} }; - struct FunctionNode; - struct BlockNode; - struct ConstantNode; - struct LocalVarNode; - struct OperatorNode; + struct ExpressionNode : public Node { + // Base type for all expression kinds. + bool reduced = false; + bool is_constant = false; + Variant reduced_value; - struct ClassNode : public Node { + virtual bool is_expression() const { return true; } + virtual ~ExpressionNode() {} + + protected: + ExpressionNode() {} + }; - bool tool; + struct AnnotationNode : public Node { StringName name; - bool extends_used; - bool classname_used; - StringName extends_file; - Vector<StringName> extends_class; - DataType base_type; - String icon_path; + Vector<ExpressionNode *> arguments; + Vector<Variant> resolved_arguments; - struct Member { - PropertyInfo _export; -#ifdef TOOLS_ENABLED - Variant default_value; -#endif - StringName identifier; - DataType data_type; - StringName setter; - StringName getter; - int line; - Node *expression; - OperatorNode *initial_assignment; - MultiplayerAPI::RPCMode rpc_mode; - int usages; - }; + AnnotationInfo *info = nullptr; - struct Constant { - Node *expression; - DataType type; - }; + bool apply(GDScriptParser *p_this, Node *p_target) const; + bool applies_to(uint32_t p_target_kinds) const; - struct Signal { - StringName name; - Vector<StringName> arguments; - int emissions; - int line; - }; + AnnotationNode() { + type = ANNOTATION; + } + }; - Vector<ClassNode *> subclasses; - Vector<Member> variables; - Map<StringName, Constant> constant_expressions; - Vector<FunctionNode *> functions; - Vector<FunctionNode *> static_functions; - Vector<Signal> _signals; - BlockNode *initializer; - BlockNode *ready; - ClassNode *owner; - //Vector<Node*> initializers; - int end_line; + struct ArrayNode : public ExpressionNode { + Vector<ExpressionNode *> elements; - ClassNode() { - tool = false; - type = TYPE_CLASS; - extends_used = false; - classname_used = false; - end_line = -1; - owner = NULL; + ArrayNode() { + type = ARRAY; } }; - struct FunctionNode : public Node { + struct AssertNode : public Node { + ExpressionNode *condition = nullptr; + LiteralNode *message = nullptr; - bool _static; - MultiplayerAPI::RPCMode rpc_mode; - bool has_yield; - bool has_unreachable_code; - StringName name; - DataType return_type; - Vector<StringName> arguments; - Vector<DataType> argument_types; - Vector<Node *> default_values; - BlockNode *body; -#ifdef DEBUG_ENABLED - Vector<int> arguments_usage; -#endif // DEBUG_ENABLED + AssertNode() { + type = ASSERT; + } + }; - virtual DataType get_datatype() const { return return_type; } - virtual void set_datatype(const DataType &p_datatype) { return_type = p_datatype; } - int get_required_argument_count() { return arguments.size() - default_values.size(); } + struct AssignmentNode : public ExpressionNode { + // Assignment is not really an expression but it's easier to parse as if it were. + enum Operation { + OP_NONE, + OP_ADDITION, + OP_SUBTRACTION, + OP_MULTIPLICATION, + OP_DIVISION, + OP_MODULO, + OP_BIT_SHIFT_LEFT, + OP_BIT_SHIFT_RIGHT, + OP_BIT_AND, + OP_BIT_OR, + OP_BIT_XOR, + }; - FunctionNode() { - type = TYPE_FUNCTION; - _static = false; - rpc_mode = MultiplayerAPI::RPC_MODE_DISABLED; - has_yield = false; - has_unreachable_code = false; + Operation operation = OP_NONE; + Variant::Operator variant_op = Variant::OP_MAX; + ExpressionNode *assignee = nullptr; + ExpressionNode *assigned_value = nullptr; + + AssignmentNode() { + type = ASSIGNMENT; } }; - struct BlockNode : public Node { + struct AwaitNode : public ExpressionNode { + ExpressionNode *to_await = nullptr; - ClassNode *parent_class; - BlockNode *parent_block; - List<Node *> statements; - Map<StringName, LocalVarNode *> variables; - bool has_return; + AwaitNode() { + type = AWAIT; + } + }; - Node *if_condition; //tiny hack to improve code completion on if () blocks + struct BinaryOpNode : public ExpressionNode { + enum OpType { + OP_ADDITION, + OP_SUBTRACTION, + OP_MULTIPLICATION, + OP_DIVISION, + OP_MODULO, + OP_BIT_LEFT_SHIFT, + OP_BIT_RIGHT_SHIFT, + OP_BIT_AND, + OP_BIT_OR, + OP_BIT_XOR, + OP_LOGIC_AND, + OP_LOGIC_OR, + OP_TYPE_TEST, + OP_CONTENT_TEST, + OP_COMP_EQUAL, + OP_COMP_NOT_EQUAL, + OP_COMP_LESS, + OP_COMP_LESS_EQUAL, + OP_COMP_GREATER, + OP_COMP_GREATER_EQUAL, + }; - //the following is useful for code completion - List<BlockNode *> sub_blocks; - int end_line; - BlockNode() { - if_condition = NULL; - type = TYPE_BLOCK; - end_line = -1; - parent_block = NULL; - parent_class = NULL; - has_return = false; + OpType operation; + Variant::Operator variant_op = Variant::OP_MAX; + ExpressionNode *left_operand = nullptr; + ExpressionNode *right_operand = nullptr; + + BinaryOpNode() { + type = BINARY_OPERATOR; } }; - struct TypeNode : public Node { - - Variant::Type vtype; - TypeNode() { type = TYPE_TYPE; } + struct BreakNode : public Node { + BreakNode() { + type = BREAK; + } }; - struct BuiltInFunctionNode : public Node { - GDScriptFunctions::Function function; - BuiltInFunctionNode() { type = TYPE_BUILT_IN_FUNCTION; } + + struct BreakpointNode : public Node { + BreakpointNode() { + type = BREAKPOINT; + } }; - struct IdentifierNode : public Node { + struct CallNode : public ExpressionNode { + ExpressionNode *callee = nullptr; + Vector<ExpressionNode *> arguments; + StringName function_name; + bool is_super = false; - StringName name; - BlockNode *declared_block; // Simplify lookup by checking if it is declared locally - DataType datatype; - virtual DataType get_datatype() const { return datatype; } - virtual void set_datatype(const DataType &p_datatype) { datatype = p_datatype; } - IdentifierNode() { - type = TYPE_IDENTIFIER; - declared_block = NULL; + CallNode() { + type = CALL; + } + + Type get_callee_type() const { + if (callee == nullptr) { + return Type::NONE; + } else { + return callee->type; + } } }; - struct LocalVarNode : public Node { + struct CastNode : public ExpressionNode { + ExpressionNode *operand = nullptr; + TypeNode *cast_type = nullptr; - StringName name; - Node *assign; - OperatorNode *assign_op; - int assignments; - int usages; - DataType datatype; - virtual DataType get_datatype() const { return datatype; } - virtual void set_datatype(const DataType &p_datatype) { datatype = p_datatype; } - LocalVarNode() { - type = TYPE_LOCAL_VAR; - assign = NULL; - assign_op = NULL; - assignments = 0; - usages = 0; + CastNode() { + type = CAST; } }; - struct ConstantNode : public Node { - Variant value; - DataType datatype; - virtual DataType get_datatype() const { return datatype; } - virtual void set_datatype(const DataType &p_datatype) { datatype = p_datatype; } - ConstantNode() { type = TYPE_CONSTANT; } + struct EnumNode : public Node { + struct Value { + IdentifierNode *identifier = nullptr; + ExpressionNode *custom_value = nullptr; + EnumNode *parent_enum = nullptr; + int index = -1; + bool resolved = false; + int value = 0; + int line = 0; + int leftmost_column = 0; + int rightmost_column = 0; + }; + IdentifierNode *identifier = nullptr; + Vector<Value> values; + + EnumNode() { + type = ENUM; + } }; - struct ArrayNode : public Node { + struct ClassNode : public Node { + struct Member { + enum Type { + UNDEFINED, + CLASS, + CONSTANT, + FUNCTION, + SIGNAL, + VARIABLE, + ENUM, + ENUM_VALUE, // For unnamed enums. + }; + + Type type = UNDEFINED; + + union { + ClassNode *m_class = nullptr; + ConstantNode *constant; + FunctionNode *function; + SignalNode *signal; + VariableNode *variable; + EnumNode *m_enum; + }; + EnumNode::Value enum_value; + + String get_type_name() const { + switch (type) { + case UNDEFINED: + return "???"; + case CLASS: + return "class"; + case CONSTANT: + return "constant"; + case FUNCTION: + return "function"; + case SIGNAL: + return "signal"; + case VARIABLE: + return "variable"; + case ENUM: + return "enum"; + case ENUM_VALUE: + return "enum value"; + } + return ""; + } + + int get_line() const { + switch (type) { + case CLASS: + return m_class->start_line; + case CONSTANT: + return constant->start_line; + case FUNCTION: + return function->start_line; + case VARIABLE: + return variable->start_line; + case ENUM_VALUE: + return enum_value.line; + case ENUM: + return m_enum->start_line; + case SIGNAL: + return signal->start_line; + case UNDEFINED: + ERR_FAIL_V_MSG(-1, "Reaching undefined member type."); + } + ERR_FAIL_V_MSG(-1, "Reaching unhandled type."); + } - Vector<Node *> elements; - DataType datatype; - virtual DataType get_datatype() const { return datatype; } - virtual void set_datatype(const DataType &p_datatype) { datatype = p_datatype; } - ArrayNode() { - type = TYPE_ARRAY; - datatype.has_type = true; - datatype.kind = DataType::BUILTIN; - datatype.builtin_type = Variant::ARRAY; + DataType get_datatype() const { + switch (type) { + case CLASS: + return m_class->get_datatype(); + case CONSTANT: + return constant->get_datatype(); + case FUNCTION: + return function->get_datatype(); + case VARIABLE: + return variable->get_datatype(); + case ENUM: + return m_enum->get_datatype(); + case ENUM_VALUE: { + // Always integer. + DataType type; + type.type_source = DataType::ANNOTATED_EXPLICIT; + type.kind = DataType::BUILTIN; + type.builtin_type = Variant::INT; + return type; + } + case SIGNAL: { + DataType type; + type.type_source = DataType::ANNOTATED_EXPLICIT; + type.kind = DataType::BUILTIN; + type.builtin_type = Variant::SIGNAL; + // TODO: Add parameter info. + return type; + } + case UNDEFINED: + return DataType(); + } + ERR_FAIL_V_MSG(DataType(), "Reaching unhandled type."); + } + + Member() {} + + Member(ClassNode *p_class) { + type = CLASS; + m_class = p_class; + } + Member(ConstantNode *p_constant) { + type = CONSTANT; + constant = p_constant; + } + Member(VariableNode *p_variable) { + type = VARIABLE; + variable = p_variable; + } + Member(SignalNode *p_signal) { + type = SIGNAL; + signal = p_signal; + } + Member(FunctionNode *p_function) { + type = FUNCTION; + function = p_function; + } + Member(EnumNode *p_enum) { + type = ENUM; + m_enum = p_enum; + } + Member(const EnumNode::Value &p_enum_value) { + type = ENUM_VALUE; + enum_value = p_enum_value; + } + }; + + IdentifierNode *identifier = nullptr; + String icon_path; + Vector<Member> members; + HashMap<StringName, int> members_indices; + ClassNode *outer = nullptr; + bool extends_used = false; + bool onready_used = false; + String extends_path; + Vector<StringName> extends; // List for indexing: extends A.B.C + DataType base_type; + String fqcn; // Fully-qualified class name. Identifies uniquely any class in the project. + + bool resolved_interface = false; + bool resolved_body = false; + + Member get_member(const StringName &p_name) const { + return members[members_indices[p_name]]; + } + bool has_member(const StringName &p_name) const { + return members_indices.has(p_name); + } + bool has_function(const StringName &p_name) const { + return has_member(p_name) && members[members_indices[p_name]].type == Member::FUNCTION; + } + template <class T> + void add_member(T *p_member_node) { + members_indices[p_member_node->identifier->name] = members.size(); + members.push_back(Member(p_member_node)); + } + void add_member(const EnumNode::Value &p_enum_value) { + members_indices[p_enum_value.identifier->name] = members.size(); + members.push_back(Member(p_enum_value)); + } + + ClassNode() { + type = CLASS; } }; - struct DictionaryNode : public Node { + struct ConstantNode : public Node { + IdentifierNode *identifier = nullptr; + ExpressionNode *initializer = nullptr; + TypeNode *datatype_specifier = nullptr; + bool infer_datatype = false; + int usages = 0; + + ConstantNode() { + type = CONSTANT; + } + }; + + struct ContinueNode : public Node { + bool is_for_match = false; + ContinueNode() { + type = CONTINUE; + } + }; + struct DictionaryNode : public ExpressionNode { struct Pair { + ExpressionNode *key = nullptr; + ExpressionNode *value = nullptr; + }; + Vector<Pair> elements; - Node *key; - Node *value; + enum Style { + LUA_TABLE, + PYTHON_DICT, }; + Style style = PYTHON_DICT; - Vector<Pair> elements; - DataType datatype; - virtual DataType get_datatype() const { return datatype; } - virtual void set_datatype(const DataType &p_datatype) { datatype = p_datatype; } DictionaryNode() { - type = TYPE_DICTIONARY; - datatype.has_type = true; - datatype.kind = DataType::BUILTIN; - datatype.builtin_type = Variant::DICTIONARY; - } - }; - - struct SelfNode : public Node { - SelfNode() { type = TYPE_SELF; } - }; - - struct OperatorNode : public Node { - enum Operator { - //call/constructor operator - OP_CALL, - OP_PARENT_CALL, - OP_YIELD, - OP_IS, - OP_IS_BUILTIN, - //indexing operator - OP_INDEX, - OP_INDEX_NAMED, - //unary operators - OP_NEG, - OP_POS, - OP_NOT, - OP_BIT_INVERT, - //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_INIT_ASSIGN, - 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, - //ternary operators - OP_TERNARY_IF, - OP_TERNARY_ELSE, - }; + type = DICTIONARY; + } + }; - Operator op; + struct ForNode : public Node { + IdentifierNode *variable = nullptr; + ExpressionNode *list = nullptr; + SuiteNode *loop = nullptr; - Vector<Node *> arguments; - DataType datatype; - virtual DataType get_datatype() const { return datatype; } - virtual void set_datatype(const DataType &p_datatype) { datatype = p_datatype; } - OperatorNode() { type = TYPE_OPERATOR; } + ForNode() { + type = FOR; + } }; - struct PatternNode : public Node { + struct FunctionNode : public Node { + IdentifierNode *identifier = nullptr; + Vector<ParameterNode *> parameters; + HashMap<StringName, int> parameters_indices; + TypeNode *return_type = nullptr; + SuiteNode *body = nullptr; + bool is_static = false; + bool is_coroutine = false; + MultiplayerAPI::RPCMode rpc_mode = MultiplayerAPI::RPC_MODE_DISABLED; + MethodInfo info; + + bool resolved_signature = false; + bool resolved_body = false; - enum PatternType { - PT_CONSTANT, - PT_BIND, - PT_DICTIONARY, - PT_ARRAY, - PT_IGNORE_REST, - PT_WILDCARD + FunctionNode() { + type = FUNCTION; + } + }; + + struct GetNodeNode : public ExpressionNode { + LiteralNode *string = nullptr; + Vector<IdentifierNode *> chain; + + GetNodeNode() { + type = GET_NODE; + } + }; + + struct IdentifierNode : public ExpressionNode { + StringName name; + + enum Source { + UNDEFINED_SOURCE, + FUNCTION_PARAMETER, + LOCAL_CONSTANT, + LOCAL_VARIABLE, + LOCAL_ITERATOR, // `for` loop iterator. + LOCAL_BIND, // Pattern bind. + MEMBER_VARIABLE, + MEMBER_CONSTANT, }; + Source source = UNDEFINED_SOURCE; - PatternType pt_type; + union { + ParameterNode *parameter_source = nullptr; + ConstantNode *constant_source; + VariableNode *variable_source; + IdentifierNode *bind_source; + }; - Node *constant; - StringName bind; - Map<ConstantNode *, PatternNode *> dictionary; - Vector<PatternNode *> array; + int usages = 0; // Useful for binds/iterator variable. + + IdentifierNode() { + type = IDENTIFIER; + } }; - struct PatternBranchNode : public Node { - Vector<PatternNode *> patterns; - BlockNode *body; + struct IfNode : public Node { + ExpressionNode *condition = nullptr; + SuiteNode *true_block = nullptr; + SuiteNode *false_block = nullptr; + + IfNode() { + type = IF; + } + }; + + struct LiteralNode : public ExpressionNode { + Variant value; + + LiteralNode() { + type = LITERAL; + } }; struct MatchNode : public Node { - Node *val_to_match; - Vector<PatternBranchNode *> branches; + ExpressionNode *test = nullptr; + Vector<MatchBranchNode *> branches; - struct CompiledPatternBranch { - Node *compiled_pattern; - BlockNode *body; - }; + MatchNode() { + type = MATCH; + } + }; - Vector<CompiledPatternBranch> compiled_pattern_branches; + struct MatchBranchNode : public Node { + Vector<PatternNode *> patterns; + SuiteNode *block; + bool has_wildcard = false; + + MatchBranchNode() { + type = MATCH_BRANCH; + } }; - struct ControlFlowNode : public Node { - enum CFType { - CF_IF, - CF_FOR, - CF_WHILE, - CF_BREAK, - CF_CONTINUE, - CF_RETURN, - CF_MATCH + struct ParameterNode : public Node { + IdentifierNode *identifier = nullptr; + ExpressionNode *default_value = nullptr; + TypeNode *datatype_specifier = nullptr; + bool infer_datatype = false; + int usages = 0; + + ParameterNode() { + type = PARAMETER; + } + }; + + struct PassNode : public Node { + PassNode() { + type = PASS; + } + }; + + struct PatternNode : public Node { + enum Type { + PT_LITERAL, + PT_EXPRESSION, + PT_BIND, + PT_ARRAY, + PT_DICTIONARY, + PT_REST, + PT_WILDCARD, }; + Type pattern_type = PT_LITERAL; - CFType cf_type; - Vector<Node *> arguments; - BlockNode *body; - BlockNode *body_else; + union { + LiteralNode *literal = nullptr; + IdentifierNode *bind; + ExpressionNode *expression; + }; + Vector<PatternNode *> array; + bool rest_used = false; // For array/dict patterns. - MatchNode *match; + struct Pair { + ExpressionNode *key = nullptr; + PatternNode *value_pattern = nullptr; + }; + Vector<Pair> dictionary; + + HashMap<StringName, IdentifierNode *> binds; - ControlFlowNode *_else; //used for if - ControlFlowNode() { - type = TYPE_CONTROL_FLOW; - cf_type = CF_IF; - body = NULL; - body_else = NULL; + bool has_bind(const StringName &p_name); + IdentifierNode *get_bind(const StringName &p_name); + + PatternNode() { + type = PATTERN; } }; + struct PreloadNode : public ExpressionNode { + ExpressionNode *path = nullptr; + String resolved_path; + Ref<Resource> resource; - struct CastNode : public Node { - Node *source_node; - DataType cast_type; - DataType return_type; - virtual DataType get_datatype() const { return return_type; } - virtual void set_datatype(const DataType &p_datatype) { return_type = p_datatype; } - CastNode() { type = TYPE_CAST; } + PreloadNode() { + type = PRELOAD; + } }; - struct AssertNode : public Node { - Node *condition; - Node *message; - AssertNode() : - condition(0), - message(0) { - type = TYPE_ASSERT; + struct ReturnNode : public Node { + ExpressionNode *return_value = nullptr; + + ReturnNode() { + type = RETURN; } }; - struct BreakpointNode : public Node { - BreakpointNode() { type = TYPE_BREAKPOINT; } - }; + struct SelfNode : public ExpressionNode { + ClassNode *current_class = nullptr; - struct NewLineNode : public Node { - NewLineNode() { type = TYPE_NEWLINE; } + SelfNode() { + type = SELF; + } }; - struct Expression { + struct SignalNode : public Node { + IdentifierNode *identifier = nullptr; + Vector<ParameterNode *> parameters; + HashMap<StringName, int> parameters_indices; - bool is_op; + SignalNode() { + type = SIGNAL; + } + }; + + struct SubscriptNode : public ExpressionNode { + ExpressionNode *base = nullptr; union { - OperatorNode::Operator op; - Node *node; + ExpressionNode *index = nullptr; + IdentifierNode *attribute; }; - }; - enum CompletionType { - COMPLETION_NONE, - COMPLETION_BUILT_IN_TYPE_CONSTANT, - COMPLETION_GET_NODE, - COMPLETION_FUNCTION, - COMPLETION_IDENTIFIER, - COMPLETION_PARENT_FUNCTION, - COMPLETION_METHOD, - COMPLETION_CALL_ARGUMENTS, - COMPLETION_RESOURCE_PATH, - COMPLETION_INDEX, - COMPLETION_VIRTUAL_FUNC, - COMPLETION_YIELD, - COMPLETION_ASSIGN, - COMPLETION_TYPE_HINT, - COMPLETION_TYPE_HINT_INDEX, + bool is_attribute = false; + + SubscriptNode() { + type = SUBSCRIPT; + } }; -private: - GDScriptTokenizer *tokenizer; + struct SuiteNode : public Node { + SuiteNode *parent_block = nullptr; + Vector<Node *> statements; + struct Local { + enum Type { + UNDEFINED, + CONSTANT, + VARIABLE, + PARAMETER, + FOR_VARIABLE, + PATTERN_BIND, + }; + Type type = UNDEFINED; + union { + ConstantNode *constant = nullptr; + VariableNode *variable; + ParameterNode *parameter; + IdentifierNode *bind; + }; + StringName name; - Node *head; - Node *list; - template <class T> - T *alloc_node(); - - bool validating; - bool for_completion; - int parenthesis; - bool error_set; - String error; - int error_line; - int error_column; - bool check_types; - bool dependencies_only; - List<String> dependencies; -#ifdef DEBUG_ENABLED - Set<int> *safe_lines; -#endif // DEBUG_ENABLED + int start_line = 0, end_line = 0; + int start_column = 0, end_column = 0; + int leftmost_column = 0, rightmost_column = 0; + + DataType get_datatype() const; + String get_name() const; + + Local() {} + Local(ConstantNode *p_constant) { + type = CONSTANT; + constant = p_constant; + name = p_constant->identifier->name; + + start_line = p_constant->start_line; + end_line = p_constant->end_line; + start_column = p_constant->start_column; + end_column = p_constant->end_column; + leftmost_column = p_constant->leftmost_column; + rightmost_column = p_constant->rightmost_column; + } + Local(VariableNode *p_variable) { + type = VARIABLE; + variable = p_variable; + name = p_variable->identifier->name; + + start_line = p_variable->start_line; + end_line = p_variable->end_line; + start_column = p_variable->start_column; + end_column = p_variable->end_column; + leftmost_column = p_variable->leftmost_column; + rightmost_column = p_variable->rightmost_column; + } + Local(ParameterNode *p_parameter) { + type = PARAMETER; + parameter = p_parameter; + name = p_parameter->identifier->name; + + start_line = p_parameter->start_line; + end_line = p_parameter->end_line; + start_column = p_parameter->start_column; + end_column = p_parameter->end_column; + leftmost_column = p_parameter->leftmost_column; + rightmost_column = p_parameter->rightmost_column; + } + Local(IdentifierNode *p_identifier) { + type = FOR_VARIABLE; + bind = p_identifier; + name = p_identifier->name; + + start_line = p_identifier->start_line; + end_line = p_identifier->end_line; + start_column = p_identifier->start_column; + end_column = p_identifier->end_column; + leftmost_column = p_identifier->leftmost_column; + rightmost_column = p_identifier->rightmost_column; + } + }; + Local empty; + Vector<Local> locals; + HashMap<StringName, int> locals_indices; + + FunctionNode *parent_function = nullptr; + ForNode *parent_for = nullptr; + IfNode *parent_if = nullptr; + + bool has_return = false; + bool has_continue = false; + bool has_unreachable_code = false; // Just so warnings aren't given more than once per block. + + bool has_local(const StringName &p_name) const; + const Local &get_local(const StringName &p_name) const; + template <class T> + void add_local(T *p_local) { + locals_indices[p_local->identifier->name] = locals.size(); + locals.push_back(Local(p_local)); + } + void add_local(const Local &p_local) { + locals_indices[p_local.name] = locals.size(); + locals.push_back(p_local); + } -#ifdef DEBUG_ENABLED - List<GDScriptWarning> warnings; -#endif // DEBUG_ENABLED + SuiteNode() { + type = SUITE; + } + }; - int pending_newline; + struct TernaryOpNode : public ExpressionNode { + // Only one ternary operation exists, so no abstraction here. + ExpressionNode *condition = nullptr; + ExpressionNode *true_expr = nullptr; + ExpressionNode *false_expr = nullptr; - struct IndentLevel { - int indent; - int tabs; + TernaryOpNode() { + type = TERNARY_OPERATOR; + } + }; + + struct TypeNode : public Node { + Vector<IdentifierNode *> type_chain; - bool is_mixed(IndentLevel other) { - return ( - (indent == other.indent && tabs != other.tabs) || - (indent > other.indent && tabs < other.tabs) || - (indent < other.indent && tabs > other.tabs)); + TypeNode() { + type = TYPE; } + }; - IndentLevel() : - indent(0), - tabs(0) {} + struct UnaryOpNode : public ExpressionNode { + enum OpType { + OP_POSITIVE, + OP_NEGATIVE, + OP_COMPLEMENT, + OP_LOGIC_NOT, + }; + + OpType operation; + Variant::Operator variant_op = Variant::OP_MAX; + ExpressionNode *operand = nullptr; - IndentLevel(int p_indent, int p_tabs) : - indent(p_indent), - tabs(p_tabs) {} + UnaryOpNode() { + type = UNARY_OPERATOR; + } }; - List<IndentLevel> indent_level; + struct VariableNode : public Node { + enum PropertyStyle { + PROP_NONE, + PROP_INLINE, + PROP_SETGET, + }; - String base_path; - String self_path; + IdentifierNode *identifier = nullptr; + ExpressionNode *initializer = nullptr; + TypeNode *datatype_specifier = nullptr; + bool infer_datatype = false; - ClassNode *current_class; - FunctionNode *current_function; - BlockNode *current_block; + PropertyStyle property = PROP_NONE; + union { + SuiteNode *setter = nullptr; + IdentifierNode *setter_pointer; + }; + IdentifierNode *setter_parameter = nullptr; + union { + SuiteNode *getter = nullptr; + IdentifierNode *getter_pointer; + }; - bool _get_completable_identifier(CompletionType p_type, StringName &identifier); - void _make_completable_call(int p_arg); + bool exported = false; + bool onready = false; + PropertyInfo export_info; + MultiplayerAPI::RPCMode rpc_mode = MultiplayerAPI::RPC_MODE_DISABLED; + int assignments = 0; + int usages = 0; - CompletionType completion_type; - StringName completion_cursor; - Variant::Type completion_built_in_constant; - Node *completion_node; - ClassNode *completion_class; - FunctionNode *completion_function; - BlockNode *completion_block; - int completion_line; - int completion_argument; - bool completion_found; - bool completion_ident_is_call; + VariableNode() { + type = VARIABLE; + } + }; - PropertyInfo current_export; + struct WhileNode : public Node { + ExpressionNode *condition = nullptr; + SuiteNode *loop = nullptr; - MultiplayerAPI::RPCMode rpc_mode; + WhileNode() { + type = WHILE; + } + }; - void _set_error(const String &p_error, int p_line = -1, int p_column = -1); -#ifdef DEBUG_ENABLED - void _add_warning(int p_code, int p_line = -1, const String &p_symbol1 = String(), const String &p_symbol2 = String(), const String &p_symbol3 = String(), const String &p_symbol4 = String()); - void _add_warning(int p_code, int p_line, const Vector<String> &p_symbols); -#endif // DEBUG_ENABLED - bool _recover_from_completion(); - - bool _parse_arguments(Node *p_parent, Vector<Node *> &p_args, bool p_static, bool p_can_codecomplete = false, bool p_parsing_constant = false); - 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, bool p_parsing_constant = 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); - - PatternNode *_parse_pattern(bool p_static); - void _parse_pattern_block(BlockNode *p_block, Vector<PatternBranchNode *> &p_branches, bool p_static); - void _transform_match_statment(MatchNode *p_match_statement); - void _generate_pattern(PatternNode *p_pattern, Node *p_node_to_match, Node *&p_resulting_node, Map<StringName, Node *> &p_bindings); - - void _parse_block(BlockNode *p_block, bool p_static); - void _parse_extends(ClassNode *p_class); - void _parse_class(ClassNode *p_class); - bool _end_statement(); - - void _determine_inheritance(ClassNode *p_class, bool p_recursive = true); - bool _parse_type(DataType &r_type, bool p_can_be_void = false); - DataType _resolve_type(const DataType &p_source, int p_line); - DataType _type_from_variant(const Variant &p_value) const; - DataType _type_from_property(const PropertyInfo &p_property, bool p_nil_is_variant = true) const; - DataType _type_from_gdtype(const GDScriptDataType &p_gdtype) const; - DataType _get_operation_type(const Variant::Operator p_op, const DataType &p_a, const DataType &p_b, bool &r_valid) const; - Variant::Operator _get_variant_operation(const OperatorNode::Operator &p_op) const; - bool _get_function_signature(DataType &p_base_type, const StringName &p_function, DataType &r_return_type, List<DataType> &r_arg_types, int &r_default_arg_count, bool &r_static, bool &r_vararg) const; - bool _get_member_type(const DataType &p_base_type, const StringName &p_member, DataType &r_member_type) const; - bool _is_type_compatible(const DataType &p_container, const DataType &p_expression, bool p_allow_implicit_conversion = false) const; - Node *_get_default_value_for_type(const DataType &p_type, int p_line = -1); - - DataType _reduce_node_type(Node *p_node); - DataType _reduce_function_call_type(const OperatorNode *p_call); - DataType _reduce_identifier_type(const DataType *p_base_type, const StringName &p_identifier, int p_line, bool p_is_indexing); - void _check_class_level_types(ClassNode *p_class); - void _check_class_blocks_types(ClassNode *p_class); - void _check_function_types(FunctionNode *p_function); - void _check_block_types(BlockNode *p_block); - _FORCE_INLINE_ void _mark_line_as_safe(int p_line) const { -#ifdef DEBUG_ENABLED - if (safe_lines) safe_lines->insert(p_line); -#endif // DEBUG_ENABLED - } - _FORCE_INLINE_ void _mark_line_as_unsafe(int p_line) const { -#ifdef DEBUG_ENABLED - if (safe_lines) safe_lines->erase(p_line); -#endif // DEBUG_ENABLED - } + enum CompletionType { + COMPLETION_NONE, + COMPLETION_ANNOTATION, // Annotation (following @). + COMPLETION_ANNOTATION_ARGUMENTS, // Annotation arguments hint. + COMPLETION_ASSIGN, // Assignment based on type (e.g. enum values). + COMPLETION_ATTRIBUTE, // After id.| to look for members. + COMPLETION_ATTRIBUTE_METHOD, // After id.| to look for methods. + COMPLETION_BUILT_IN_TYPE_CONSTANT, // Constants inside a built-in type (e.g. Color.blue). + COMPLETION_CALL_ARGUMENTS, // Complete with nodes, input actions, enum values (or usual expressions). + // TODO: COMPLETION_DECLARATION, // Potential declaration (var, const, func). + COMPLETION_GET_NODE, // Get node with $ notation. + COMPLETION_IDENTIFIER, // List available identifiers in scope. + COMPLETION_INHERIT_TYPE, // Type after extends. Exclude non-viable types (built-ins, enums, void). Includes subtypes using the argument index. + COMPLETION_METHOD, // List available methods in scope. + COMPLETION_OVERRIDE_METHOD, // Override implementation, also for native virtuals. + COMPLETION_PROPERTY_DECLARATION, // Property declaration (get, set). + COMPLETION_PROPERTY_DECLARATION_OR_TYPE, // Property declaration (get, set) or a type hint. + COMPLETION_PROPERTY_METHOD, // Property setter or getter (list available methods). + COMPLETION_RESOURCE_PATH, // For load/preload. + COMPLETION_SUBSCRIPT, // Inside id[|]. + COMPLETION_SUPER_METHOD, // After super. + COMPLETION_TYPE_ATTRIBUTE, // Attribute in type name (Type.|). + COMPLETION_TYPE_NAME, // Name of type (after :). + COMPLETION_TYPE_NAME_OR_VOID, // Same as TYPE_NAME, but allows void (in function return type). + }; - Error _parse(const String &p_base_path); + struct CompletionContext { + CompletionType type = COMPLETION_NONE; + ClassNode *current_class = nullptr; + FunctionNode *current_function = nullptr; + SuiteNode *current_suite = nullptr; + int current_line = -1; + int current_argument = -1; + Variant::Type builtin_type = Variant::VARIANT_MAX; + Node *node = nullptr; + Object *base = nullptr; + List<Ref<GDScriptParserRef>> dependent_parsers; + }; -public: - bool has_error() const; - String get_error() const; - int get_error_line() const; - int get_error_column() const; + struct CompletionCall { + Node *call = nullptr; + int argument = -1; + }; + +private: + friend class GDScriptAnalyzer; + + bool _is_tool = false; + String script_path; + bool for_completion = false; + bool panic_mode = false; + bool can_break = false; + bool can_continue = false; + bool is_continue_match = false; // Whether a `continue` will act on a `match`. + bool is_ignoring_warnings = false; + List<bool> multiline_stack; + + ClassNode *head = nullptr; + Node *list = nullptr; + List<ParserError> errors; #ifdef DEBUG_ENABLED - const List<GDScriptWarning> &get_warnings() const { return warnings; } -#endif // DEBUG_ENABLED - Error parse(const String &p_code, const String &p_base_path = "", bool p_just_validate = false, const String &p_self_path = "", bool p_for_completion = false, Set<int> *r_safe_lines = NULL, bool p_dependencies_only = false); - Error parse_bytecode(const Vector<uint8_t> &p_bytecode, const String &p_base_path = "", const String &p_self_path = ""); + List<GDScriptWarning> warnings; + Set<String> ignored_warnings; + Set<int> unsafe_lines; +#endif - bool is_tool_script() const; - const Node *get_parse_tree() const; + GDScriptTokenizer tokenizer; + GDScriptTokenizer::Token previous; + GDScriptTokenizer::Token current; + + ClassNode *current_class = nullptr; + FunctionNode *current_function = nullptr; + SuiteNode *current_suite = nullptr; + + CompletionContext completion_context; + CompletionCall completion_call; + List<CompletionCall> completion_call_stack; + bool passed_cursor = false; + + typedef bool (GDScriptParser::*AnnotationAction)(const AnnotationNode *p_annotation, Node *p_target); + struct AnnotationInfo { + enum TargetKind { + NONE = 0, + SCRIPT = 1 << 0, + CLASS = 1 << 1, + VARIABLE = 1 << 2, + CONSTANT = 1 << 3, + SIGNAL = 1 << 4, + FUNCTION = 1 << 5, + STATEMENT = 1 << 6, + CLASS_LEVEL = CLASS | VARIABLE | FUNCTION, + }; + uint32_t target_kind = 0; // Flags. + AnnotationAction apply = nullptr; + MethodInfo info; + }; + HashMap<StringName, AnnotationInfo> valid_annotations; + List<AnnotationNode *> annotation_stack; + + typedef ExpressionNode *(GDScriptParser::*ParseFunction)(ExpressionNode *p_previous_operand, bool p_can_assign); + // Higher value means higher precedence (i.e. is evaluated first). + enum Precedence { + PREC_NONE, + PREC_ASSIGNMENT, + PREC_CAST, + PREC_TERNARY, + PREC_LOGIC_OR, + PREC_LOGIC_AND, + PREC_LOGIC_NOT, + PREC_CONTENT_TEST, + PREC_COMPARISON, + PREC_BIT_OR, + PREC_BIT_XOR, + PREC_BIT_AND, + PREC_BIT_SHIFT, + PREC_SUBTRACTION, + PREC_ADDITION, + PREC_FACTOR, + PREC_SIGN, + PREC_BIT_NOT, + PREC_TYPE_TEST, + PREC_AWAIT, + PREC_CALL, + PREC_ATTRIBUTE, + PREC_SUBSCRIPT, + PREC_PRIMARY, + }; + struct ParseRule { + ParseFunction prefix = nullptr; + ParseFunction infix = nullptr; + Precedence precedence = PREC_NONE; + }; + static ParseRule *get_rule(GDScriptTokenizer::Token::Type p_token_type); - //completion info + template <class T> + T *alloc_node() { + T *node = memnew(T); - CompletionType get_completion_type(); - StringName get_completion_cursor(); - int get_completion_line(); - Variant::Type get_completion_built_in_constant(); - Node *get_completion_node(); - ClassNode *get_completion_class(); - BlockNode *get_completion_block(); - FunctionNode *get_completion_function(); - int get_completion_argument_index(); - int get_completion_identifier_is_function(); + node->next = list; + list = node; - const List<String> &get_dependencies() const { return dependencies; } + // TODO: Properly set positions for all nodes. + node->start_line = previous.start_line; + node->end_line = previous.end_line; + node->start_column = previous.start_column; + node->end_column = previous.end_column; + node->leftmost_column = previous.leftmost_column; + node->rightmost_column = previous.rightmost_column; + return node; + } void clear(); + void push_error(const String &p_message, const Node *p_origin = nullptr); +#ifdef DEBUG_ENABLED + void push_warning(const Node *p_source, GDScriptWarning::Code p_code, const String &p_symbol1 = String(), const String &p_symbol2 = String(), const String &p_symbol3 = String(), const String &p_symbol4 = String()); + void push_warning(const Node *p_source, GDScriptWarning::Code p_code, const Vector<String> &p_symbols); +#endif + + void make_completion_context(CompletionType p_type, Node *p_node, int p_argument = -1, bool p_force = false); + void make_completion_context(CompletionType p_type, Variant::Type p_builtin_type, bool p_force = false); + void push_completion_call(Node *p_call); + void pop_completion_call(); + void set_last_completion_call_arg(int p_argument); + + GDScriptTokenizer::Token advance(); + bool match(GDScriptTokenizer::Token::Type p_token_type); + bool check(GDScriptTokenizer::Token::Type p_token_type); + bool consume(GDScriptTokenizer::Token::Type p_token_type, const String &p_error_message); + bool is_at_end(); + bool is_statement_end(); + void end_statement(const String &p_context); + void synchronize(); + void push_multiline(bool p_state); + void pop_multiline(); + + // Main blocks. + void parse_program(); + ClassNode *parse_class(); + void parse_class_name(); + void parse_extends(); + void parse_class_body(); + template <class T> + void parse_class_member(T *(GDScriptParser::*p_parse_function)(), AnnotationInfo::TargetKind p_target, const String &p_member_kind); + SignalNode *parse_signal(); + EnumNode *parse_enum(); + ParameterNode *parse_parameter(); + FunctionNode *parse_function(); + SuiteNode *parse_suite(const String &p_context, SuiteNode *p_suite = nullptr); + // Annotations + AnnotationNode *parse_annotation(uint32_t p_valid_targets); + bool register_annotation(const MethodInfo &p_info, uint32_t p_target_kinds, AnnotationAction p_apply, int p_optional_arguments = 0, bool p_is_vararg = false); + bool validate_annotation_arguments(AnnotationNode *p_annotation); + void clear_unused_annotations(); + bool tool_annotation(const AnnotationNode *p_annotation, Node *p_target); + bool icon_annotation(const AnnotationNode *p_annotation, Node *p_target); + bool onready_annotation(const AnnotationNode *p_annotation, Node *p_target); + template <PropertyHint t_hint, Variant::Type t_type> + bool export_annotations(const AnnotationNode *p_annotation, Node *p_target); + bool warning_annotations(const AnnotationNode *p_annotation, Node *p_target); + template <MultiplayerAPI::RPCMode t_mode> + bool network_annotations(const AnnotationNode *p_annotation, Node *p_target); + // Statements. + Node *parse_statement(); + VariableNode *parse_variable(); + VariableNode *parse_variable(bool p_allow_property); + VariableNode *parse_property(VariableNode *p_variable, bool p_need_indent); + void parse_property_getter(VariableNode *p_variable); + void parse_property_setter(VariableNode *p_variable); + ConstantNode *parse_constant(); + AssertNode *parse_assert(); + BreakNode *parse_break(); + ContinueNode *parse_continue(); + ForNode *parse_for(); + IfNode *parse_if(const String &p_token = "if"); + MatchNode *parse_match(); + MatchBranchNode *parse_match_branch(); + PatternNode *parse_match_pattern(PatternNode *p_root_pattern = nullptr); + WhileNode *parse_while(); + // Expressions. + ExpressionNode *parse_expression(bool p_can_assign, bool p_stop_on_assign = false); + ExpressionNode *parse_precedence(Precedence p_precedence, bool p_can_assign, bool p_stop_on_assign = false); + ExpressionNode *parse_literal(ExpressionNode *p_previous_operand, bool p_can_assign); + LiteralNode *parse_literal(); + ExpressionNode *parse_self(ExpressionNode *p_previous_operand, bool p_can_assign); + ExpressionNode *parse_identifier(ExpressionNode *p_previous_operand, bool p_can_assign); + IdentifierNode *parse_identifier(); + ExpressionNode *parse_builtin_constant(ExpressionNode *p_previous_operand, bool p_can_assign); + ExpressionNode *parse_unary_operator(ExpressionNode *p_previous_operand, bool p_can_assign); + ExpressionNode *parse_binary_operator(ExpressionNode *p_previous_operand, bool p_can_assign); + ExpressionNode *parse_ternary_operator(ExpressionNode *p_previous_operand, bool p_can_assign); + ExpressionNode *parse_assignment(ExpressionNode *p_previous_operand, bool p_can_assign); + ExpressionNode *parse_array(ExpressionNode *p_previous_operand, bool p_can_assign); + ExpressionNode *parse_dictionary(ExpressionNode *p_previous_operand, bool p_can_assign); + ExpressionNode *parse_call(ExpressionNode *p_previous_operand, bool p_can_assign); + ExpressionNode *parse_get_node(ExpressionNode *p_previous_operand, bool p_can_assign); + ExpressionNode *parse_preload(ExpressionNode *p_previous_operand, bool p_can_assign); + ExpressionNode *parse_grouping(ExpressionNode *p_previous_operand, bool p_can_assign); + ExpressionNode *parse_cast(ExpressionNode *p_previous_operand, bool p_can_assign); + ExpressionNode *parse_await(ExpressionNode *p_previous_operand, bool p_can_assign); + ExpressionNode *parse_attribute(ExpressionNode *p_previous_operand, bool p_can_assign); + ExpressionNode *parse_subscript(ExpressionNode *p_previous_operand, bool p_can_assign); + ExpressionNode *parse_invalid_token(ExpressionNode *p_previous_operand, bool p_can_assign); + TypeNode *parse_type(bool p_allow_void = false); + +public: + Error parse(const String &p_source_code, const String &p_script_path, bool p_for_completion); + ClassNode *get_tree() const { return head; } + bool is_tool() const { return _is_tool; } + static Variant::Type get_builtin_type(const StringName &p_type); + static GDScriptFunctions::Function get_builtin_function(const StringName &p_name); + + CompletionContext get_completion_context() const { return completion_context; } + CompletionCall get_completion_call() const { return completion_call; } + void get_annotation_list(List<MethodInfo> *r_annotations) const; + + const List<ParserError> &get_errors() const { return errors; } + const List<String> get_dependencies() const { + // TODO: Keep track of deps. + return List<String>(); + } +#ifdef DEBUG_ENABLED + const List<GDScriptWarning> &get_warnings() const { return warnings; } + const Set<int> &get_unsafe_lines() const { return unsafe_lines; } + int get_last_line_number() const { return current.end_line; } +#endif + GDScriptParser(); ~GDScriptParser(); + +#ifdef DEBUG_ENABLED + class TreePrinter { + int indent_level = 0; + String indent; + StringBuilder printed; + bool pending_indent = false; + + void increase_indent(); + void decrease_indent(); + void push_line(const String &p_line = String()); + void push_text(const String &p_text); + + void print_annotation(AnnotationNode *p_annotation); + void print_array(ArrayNode *p_array); + void print_assert(AssertNode *p_assert); + void print_assignment(AssignmentNode *p_assignment); + void print_await(AwaitNode *p_await); + void print_binary_op(BinaryOpNode *p_binary_op); + void print_call(CallNode *p_call); + void print_cast(CastNode *p_cast); + void print_class(ClassNode *p_class); + void print_constant(ConstantNode *p_constant); + void print_dictionary(DictionaryNode *p_dictionary); + void print_expression(ExpressionNode *p_expression); + void print_enum(EnumNode *p_enum); + void print_for(ForNode *p_for); + void print_function(FunctionNode *p_function); + void print_get_node(GetNodeNode *p_get_node); + void print_if(IfNode *p_if, bool p_is_elif = false); + void print_identifier(IdentifierNode *p_identifier); + void print_literal(LiteralNode *p_literal); + void print_match(MatchNode *p_match); + void print_match_branch(MatchBranchNode *p_match_branch); + void print_match_pattern(PatternNode *p_match_pattern); + void print_parameter(ParameterNode *p_parameter); + void print_preload(PreloadNode *p_preload); + void print_return(ReturnNode *p_return); + void print_self(SelfNode *p_self); + void print_signal(SignalNode *p_signal); + void print_statement(Node *p_statement); + void print_subscript(SubscriptNode *p_subscript); + void print_suite(SuiteNode *p_suite); + void print_type(TypeNode *p_type); + void print_ternary_op(TernaryOpNode *p_ternary_op); + void print_unary_op(UnaryOpNode *p_unary_op); + void print_variable(VariableNode *p_variable); + void print_while(WhileNode *p_while); + + public: + void print_tree(const GDScriptParser &p_parser); + }; +#endif // DEBUG_ENABLED + static void cleanup(); }; #endif // GDSCRIPT_PARSER_H |