summaryrefslogtreecommitdiff
path: root/modules/gdscript/gdscript_parser.h
diff options
context:
space:
mode:
Diffstat (limited to 'modules/gdscript/gdscript_parser.h')
-rw-r--r--modules/gdscript/gdscript_parser.h1672
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