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.h1321
1 files changed, 829 insertions, 492 deletions
diff --git a/modules/gdscript/gdscript_parser.h b/modules/gdscript/gdscript_parser.h
index 7dedb6d6f9..09cb17f010 100644
--- a/modules/gdscript/gdscript_parser.h
+++ b/modules/gdscript/gdscript_parser.h
@@ -31,665 +31,1002 @@
#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_functions.h"
#include "gdscript_tokenizer.h"
-struct GDScriptDataType;
-struct GDScriptWarning;
+#ifdef DEBUG_ENABLED
+#include "core/string_builder.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 Kind {
BUILTIN,
NATIVE,
SCRIPT,
- GDSCRIPT,
- CLASS,
- UNRESOLVED
+ CLASS, // GDScript.
+ UNRESOLVED,
};
-
Kind kind = UNRESOLVED;
- bool has_type = false;
+ 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 is_constant = false;
- bool is_meta_type = false; // Whether the value can be used as a type
+ bool is_meta_type = false;
bool infer_type = false;
- bool may_yield = false; // For function calls
Variant::Type builtin_type = Variant::NIL;
StringName native_type;
Ref<Script> script_type;
- ClassNode *class_type = nullptr;
+ ClassNode *gdscript_type = nullptr;
+ _FORCE_INLINE_ bool is_set() const { return type_source != UNDETECTED; }
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 BUILTIN:
+ return builtin_type == p_other.builtin_type;
+ case NATIVE:
+ return native_type == p_other.native_type;
+ case SCRIPT:
+ return script_type == p_other.script_type;
+ case CLASS:
+ return gdscript_type == p_other.gdscript_type;
+ case UNRESOLVED:
+ break;
}
+
return false;
}
+ };
- DataType() {}
+ 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 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) {}
+ 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.
+ virtual bool is_expression() const { return true; }
+ virtual ~ExpressionNode() {}
- struct ClassNode : public Node {
- bool tool;
+ protected:
+ ExpressionNode() {}
+ };
+
+ 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 = nullptr;
+ ArrayNode() {
+ type = ARRAY;
}
};
- struct FunctionNode : public Node {
- 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
-
- 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 AssertNode : public Node {
+ ExpressionNode *condition = nullptr;
+ LiteralNode *message = nullptr;
- FunctionNode() {
- type = TYPE_FUNCTION;
- _static = false;
- rpc_mode = MultiplayerAPI::RPC_MODE_DISABLED;
- has_yield = false;
- has_unreachable_code = false;
+ AssertNode() {
+ type = ASSERT;
}
};
- struct BlockNode : public Node {
- ClassNode *parent_class = nullptr;
- BlockNode *parent_block = nullptr;
- List<Node *> statements;
- Map<StringName, LocalVarNode *> variables;
- bool has_return = false;
- bool can_break = false;
- bool can_continue = false;
-
- Node *if_condition = nullptr; //tiny hack to improve code completion on if () blocks
+ 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,
+ };
- //the following is useful for code completion
- List<BlockNode *> sub_blocks;
- int end_line = -1;
+ Operation operation = OP_NONE;
+ ExpressionNode *assignee = nullptr;
+ ExpressionNode *assigned_value = nullptr;
- BlockNode() {
- type = TYPE_BLOCK;
+ AssignmentNode() {
+ type = ASSIGNMENT;
}
};
- struct TypeNode : public Node {
- Variant::Type vtype;
+ struct AwaitNode : public ExpressionNode {
+ ExpressionNode *to_await = nullptr;
- TypeNode() {
- type = TYPE_TYPE;
+ AwaitNode() {
+ type = AWAIT;
}
};
- struct BuiltInFunctionNode : public Node {
- GDScriptFunctions::Function function;
+ 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,
+ };
- BuiltInFunctionNode() {
- type = TYPE_BUILT_IN_FUNCTION;
+ OpType operation;
+ ExpressionNode *left_operand = nullptr;
+ ExpressionNode *right_operand = nullptr;
+
+ BinaryOpNode() {
+ type = BINARY_OPERATOR;
}
};
- struct IdentifierNode : public Node {
- StringName name;
- BlockNode *declared_block = nullptr; // 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; }
+ struct BreakNode : public Node {
+ BreakNode() {
+ type = BREAK;
+ }
+ };
- IdentifierNode() {
- type = TYPE_IDENTIFIER;
+ struct BreakpointNode : public Node {
+ BreakpointNode() {
+ type = BREAKPOINT;
}
};
- struct LocalVarNode : public Node {
- StringName name;
- Node *assign = nullptr;
- OperatorNode *assign_op = nullptr;
- int assignments = 0;
- int usages = 0;
- DataType datatype;
- virtual DataType get_datatype() const { return datatype; }
- virtual void set_datatype(const DataType &p_datatype) { datatype = p_datatype; }
+ struct CallNode : public ExpressionNode {
+ ExpressionNode *callee = nullptr;
+ Vector<ExpressionNode *> arguments;
+ bool is_super = false;
- LocalVarNode() {
- type = TYPE_LOCAL_VAR;
+ CallNode() {
+ type = CALL;
}
};
- 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; }
+ struct CastNode : public ExpressionNode {
+ ExpressionNode *operand = nullptr;
+ TypeNode *cast_type = nullptr;
- ConstantNode() {
- type = TYPE_CONSTANT;
+ CastNode() {
+ type = CAST;
}
};
- struct ArrayNode : public Node {
- Vector<Node *> elements;
- DataType datatype;
- virtual DataType get_datatype() const { return datatype; }
- virtual void set_datatype(const DataType &p_datatype) { datatype = p_datatype; }
+ struct EnumNode : public Node {
+ struct Value {
+ IdentifierNode *identifier = nullptr;
+ LiteralNode *custom_value = nullptr;
+ int value = 0;
+ };
+ IdentifierNode *identifier = nullptr;
+ Vector<Value> values;
- ArrayNode() {
- type = TYPE_ARRAY;
- datatype.has_type = true;
- datatype.kind = DataType::BUILTIN;
- datatype.builtin_type = Variant::ARRAY;
+ EnumNode() {
+ type = ENUM;
}
};
- struct DictionaryNode : public Node {
- struct Pair {
- Node *key;
- Node *value;
+ 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 "";
+ }
+
+ 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;
+ }
};
- Vector<Pair> elements;
- DataType datatype;
- virtual DataType get_datatype() const { return datatype; }
- virtual void set_datatype(const DataType &p_datatype) { datatype = p_datatype; }
+ 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;
- DictionaryNode() {
- type = TYPE_DICTIONARY;
- datatype.has_type = true;
- datatype.kind = DataType::BUILTIN;
- datatype.builtin_type = Variant::DICTIONARY;
+ Member get_member(const StringName &p_name) const {
+ return members[members_indices[p_name]];
+ }
+ 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));
+ }
+ virtual DataType get_datatype() const {
+ return base_type;
+ }
+ virtual void set_datatype(const DataType &p_datatype) {
+ base_type = p_datatype;
+ }
+
+ ClassNode() {
+ type = CLASS;
}
};
- 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,
- };
+ struct ConstantNode : public Node {
+ IdentifierNode *identifier = nullptr;
+ ExpressionNode *initializer = nullptr;
+ TypeNode *datatype_specifier = nullptr;
+ bool infer_datatype = false;
- Operator op;
+ ConstantNode() {
+ type = CONSTANT;
+ }
+ };
- 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;
+ struct ContinueNode : public Node {
+ ContinueNode() {
+ type = CONTINUE;
}
};
- struct PatternNode : public Node {
- enum PatternType {
- PT_CONSTANT,
- PT_BIND,
- PT_DICTIONARY,
- PT_ARRAY,
- PT_IGNORE_REST,
- PT_WILDCARD
+ struct DictionaryNode : public ExpressionNode {
+ struct Pair {
+ ExpressionNode *key = nullptr;
+ ExpressionNode *value = nullptr;
};
+ Vector<Pair> elements;
- PatternType pt_type;
+ enum Style {
+ LUA_TABLE,
+ PYTHON_DICT,
+ };
+ Style style = PYTHON_DICT;
- Node *constant;
- StringName bind;
- Map<ConstantNode *, PatternNode *> dictionary;
- Vector<PatternNode *> array;
+ DictionaryNode() {
+ type = DICTIONARY;
+ }
};
- struct PatternBranchNode : public Node {
- Vector<PatternNode *> patterns;
- BlockNode *body;
+ struct ForNode : public Node {
+ IdentifierNode *variable = nullptr;
+ ExpressionNode *list = nullptr;
+ SuiteNode *loop = nullptr;
+
+ ForNode() {
+ type = FOR;
+ }
};
- struct MatchNode : public Node {
- Node *val_to_match;
- Vector<PatternBranchNode *> branches;
+ 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;
+ MultiplayerAPI::RPCMode rpc_mode = MultiplayerAPI::RPC_MODE_DISABLED;
- struct CompiledPatternBranch {
- Node *compiled_pattern;
- BlockNode *body;
- };
+ FunctionNode() {
+ type = FUNCTION;
+ }
+ };
- Vector<CompiledPatternBranch> compiled_pattern_branches;
+ struct GetNodeNode : public ExpressionNode {
+ LiteralNode *string = nullptr;
+ Vector<IdentifierNode *> chain;
+
+ GetNodeNode() {
+ type = GET_NODE;
+ }
};
- struct ControlFlowNode : public Node {
- enum CFType {
- CF_IF,
- CF_FOR,
- CF_WHILE,
- CF_BREAK,
- CF_CONTINUE,
- CF_RETURN,
- CF_MATCH
- };
+ struct IdentifierNode : public ExpressionNode {
+ StringName name;
- CFType cf_type = CF_IF;
- Vector<Node *> arguments;
- BlockNode *body = nullptr;
- BlockNode *body_else = nullptr;
+ IdentifierNode() {
+ type = IDENTIFIER;
+ }
+ };
- MatchNode *match;
+ struct IfNode : public Node {
+ ExpressionNode *condition = nullptr;
+ SuiteNode *true_block = nullptr;
+ SuiteNode *false_block = nullptr;
- ControlFlowNode *_else; //used for if
+ IfNode() {
+ type = IF;
+ }
+ };
- ControlFlowNode() {
- type = TYPE_CONTROL_FLOW;
+ struct LiteralNode : public ExpressionNode {
+ Variant value;
+
+ LiteralNode() {
+ type = LITERAL;
}
};
- 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; }
+ struct MatchNode : public Node {
+ ExpressionNode *test = nullptr;
+ Vector<MatchBranchNode *> branches;
- CastNode() {
- type = TYPE_CAST;
+ MatchNode() {
+ type = MATCH;
}
};
- struct AssertNode : public Node {
- Node *condition = nullptr;
- Node *message = nullptr;
+ struct MatchBranchNode : public Node {
+ Vector<PatternNode *> patterns;
+ SuiteNode *block;
- AssertNode() {
- type = TYPE_ASSERT;
+ MatchBranchNode() {
+ type = MATCH_BRANCH;
}
};
- struct BreakpointNode : public Node {
- BreakpointNode() {
- type = TYPE_BREAKPOINT;
+ struct ParameterNode : public Node {
+ IdentifierNode *identifier = nullptr;
+ ExpressionNode *default_value = nullptr;
+ TypeNode *datatype_specifier = nullptr;
+ bool infer_datatype = false;
+
+ ParameterNode() {
+ type = PARAMETER;
}
};
- struct NewLineNode : public Node {
- NewLineNode() {
- type = TYPE_NEWLINE;
+ struct PassNode : public Node {
+ PassNode() {
+ type = PASS;
}
};
- struct Expression {
- bool is_op;
+ 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;
+
union {
- OperatorNode::Operator op;
- Node *node;
+ LiteralNode *literal = nullptr;
+ IdentifierNode *bind;
+ ExpressionNode *expression;
};
- };
+ Vector<PatternNode *> array;
+ bool rest_used = false; // For array/dict patterns.
- 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,
+ struct Pair {
+ ExpressionNode *key = nullptr;
+ PatternNode *value_pattern = nullptr;
+ };
+ Vector<Pair> dictionary;
+
+ PatternNode() {
+ type = PATTERN;
+ }
};
+ struct PreloadNode : public ExpressionNode {
+ ExpressionNode *path = nullptr;
+ String resolved_path;
+ Ref<Resource> resource;
-private:
- GDScriptTokenizer *tokenizer;
+ PreloadNode() {
+ type = PRELOAD;
+ }
+ };
- Node *head;
- Node *list;
- template <class T>
- T *alloc_node();
+ struct ReturnNode : public Node {
+ ExpressionNode *return_value = nullptr;
- 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
+ ReturnNode() {
+ type = RETURN;
+ }
+ };
-#ifdef DEBUG_ENABLED
- List<GDScriptWarning> warnings;
-#endif // DEBUG_ENABLED
+ struct SelfNode : public ExpressionNode {
+ ClassNode *current_class = nullptr;
- int pending_newline;
+ SelfNode() {
+ type = SELF;
+ }
+ };
- struct IndentLevel {
- int indent = 0;
- int tabs = 0;
+ struct SignalNode : public Node {
+ IdentifierNode *identifier = nullptr;
+ Vector<ParameterNode *> parameters;
+ HashMap<StringName, int> parameters_indices;
- bool is_mixed(IndentLevel other) {
- return (
- (indent == other.indent && tabs != other.tabs) ||
- (indent > other.indent && tabs < other.tabs) ||
- (indent < other.indent && tabs > other.tabs));
+ SignalNode() {
+ type = SIGNAL;
}
+ };
- IndentLevel() {}
+ struct SubscriptNode : public ExpressionNode {
+ ExpressionNode *base = nullptr;
+ union {
+ ExpressionNode *index = nullptr;
+ IdentifierNode *attribute;
+ };
- IndentLevel(int p_indent, int p_tabs) :
- indent(p_indent),
- tabs(p_tabs) {}
+ bool is_attribute = false;
+
+ SubscriptNode() {
+ type = SUBSCRIPT;
+ }
};
- List<IndentLevel> indent_level;
+ struct SuiteNode : public Node {
+ SuiteNode *parent_block = nullptr;
+ Vector<Node *> statements;
+ struct Local {
+ enum Type {
+ UNDEFINED,
+ CONSTANT,
+ VARIABLE,
+ };
+ Type type = UNDEFINED;
+ union {
+ ConstantNode *constant = nullptr;
+ VariableNode *variable;
+ };
+
+ Local() {}
+ Local(ConstantNode *p_constant) {
+ type = CONSTANT;
+ constant = p_constant;
+ }
+ Local(VariableNode *p_variable) {
+ type = VARIABLE;
+ variable = p_variable;
+ }
+ };
+ Local empty;
+ Vector<Local> locals;
+ HashMap<StringName, int> locals_indices;
+
+ 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));
+ }
- String base_path;
- String self_path;
+ SuiteNode() {
+ type = SUITE;
+ }
+ };
- ClassNode *current_class;
- FunctionNode *current_function;
- BlockNode *current_block;
+ struct TernaryOpNode : public ExpressionNode {
+ // Only one ternary operation exists, so no abstraction here.
+ ExpressionNode *condition = nullptr;
+ ExpressionNode *true_expr = nullptr;
+ ExpressionNode *false_expr = nullptr;
- bool _get_completable_identifier(CompletionType p_type, StringName &identifier);
- void _make_completable_call(int p_arg);
+ TernaryOpNode() {
+ type = TERNARY_OPERATOR;
+ }
+ };
- 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;
+ struct TypeNode : public Node {
+ IdentifierNode *type_base = nullptr;
+ SubscriptNode *type_specifier = nullptr;
- PropertyInfo current_export;
+ TypeNode() {
+ type = TYPE;
+ }
+ };
- MultiplayerAPI::RPCMode rpc_mode;
+ struct UnaryOpNode : public ExpressionNode {
+ enum OpType {
+ OP_POSITIVE,
+ OP_NEGATIVE,
+ OP_COMPLEMENT,
+ OP_LOGIC_NOT,
+ };
- 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 = nullptr);
- 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);
- bool _reduce_export_var_type(Variant &p_value, int p_line = 0);
-
- 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 _set_end_statement_error(String p_name);
-
- 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, bool *r_is_const = nullptr) 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);
+ OpType operation;
+ ExpressionNode *operand = nullptr;
+
+ UnaryOpNode() {
+ type = UNARY_OPERATOR;
}
-#endif // DEBUG_ENABLED
- }
+ };
- Error _parse(const String &p_base_path);
+ struct VariableNode : public Node {
+ IdentifierNode *identifier = nullptr;
+ ExpressionNode *initializer = nullptr;
+ TypeNode *datatype_specifier = nullptr;
+ bool infer_datatype = false;
+ bool exported = false;
+ bool onready = false;
+ PropertyInfo export_info;
+ MultiplayerAPI::RPCMode rpc_mode = MultiplayerAPI::RPC_MODE_DISABLED;
+
+ VariableNode() {
+ type = VARIABLE;
+ }
+ };
-public:
- bool has_error() const;
- String get_error() const;
- int get_error_line() const;
- int get_error_column() const;
-#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 = nullptr, bool p_dependencies_only = false);
- Error parse_bytecode(const Vector<uint8_t> &p_bytecode, const String &p_base_path = "", const String &p_self_path = "");
+ struct WhileNode : public Node {
+ ExpressionNode *condition = nullptr;
+ SuiteNode *loop = nullptr;
- bool is_tool_script() const;
- const Node *get_parse_tree() const;
+ WhileNode() {
+ type = WHILE;
+ }
+ };
- //completion info
+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;
+ List<bool> multiline_stack;
+
+ ClassNode *head = nullptr;
+ Node *list = nullptr;
+ List<ParserError> errors;
+
+ GDScriptTokenizer tokenizer;
+ GDScriptTokenizer::Token previous;
+ GDScriptTokenizer::Token current;
+
+ ClassNode *current_class = nullptr;
+ FunctionNode *current_function = nullptr;
+ SuiteNode *current_suite = nullptr;
+
+ 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);
- 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();
- bool get_completion_identifier_is_function();
+ template <class T>
+ T *alloc_node();
+ void clear();
+ void push_error(const String &p_message, const Node *p_origin = nullptr);
+
+ 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);
+ // 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();
+ 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();
+ 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);
- const List<String> &get_dependencies() const { return dependencies; }
+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);
+
+ const List<ParserError> &get_errors() const { return errors; }
+ const List<String> get_dependencies() const {
+ // TODO: Keep track of deps.
+ return List<String>();
+ }
- void clear();
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
};
#endif // GDSCRIPT_PARSER_H