1"""
2 pygments.lexers.gdscript
3 ~~~~~~~~~~~~~~~~~~~~~~~~
4
5 Lexer for GDScript.
6
7 Modified by Daniel J. Ramirez <djrmuv@gmail.com> based on the original
8 python.py.
9
10 :copyright: Copyright 2006-2025 by the Pygments team, see AUTHORS.
11 :license: BSD, see LICENSE for details.
12"""
13
14import re
15
16from pygments.lexer import RegexLexer, include, bygroups, default, words, \
17 combined
18from pygments.token import Text, Comment, Operator, Keyword, Name, String, \
19 Number, Punctuation, Whitespace
20
21__all__ = ["GDScriptLexer"]
22
23
24class GDScriptLexer(RegexLexer):
25 """
26 For GDScript source code.
27 """
28
29 name = "GDScript"
30 url = 'https://www.godotengine.org'
31 aliases = ["gdscript", "gd"]
32 filenames = ["*.gd"]
33 mimetypes = ["text/x-gdscript", "application/x-gdscript"]
34 version_added = ''
35
36 def innerstring_rules(ttype):
37 return [
38 # the old style '%s' % (...) string formatting
39 (r"%(\(\w+\))?[-#0 +]*([0-9]+|[*])?(\.([0-9]+|[*]))?"
40 "[hlL]?[E-GXc-giorsux%]",
41 String.Interpol),
42 # backslashes, quotes and formatting signs must be parsed one at a time
43 (r'[^\\\'"%\n]+', ttype),
44 (r'[\'"\\]', ttype),
45 # unhandled string formatting sign
46 (r"%", ttype),
47 # newlines are an error (use "nl" state)
48 ]
49
50 tokens = {
51 "root": [
52 (r"\n", Whitespace),
53 (r'^(\s*)([rRuUbB]{,2})("""(?:.|\n)*?""")',
54 bygroups(Whitespace, String.Affix, String.Doc)),
55 (r"^(\s*)([rRuUbB]{,2})('''(?:.|\n)*?''')",
56 bygroups(Whitespace, String.Affix, String.Doc)),
57 (r"[^\S\n]+", Whitespace),
58 (r"#.*$", Comment.Single),
59 (r"[]{}:(),;[]", Punctuation),
60 (r"(\\)(\n)", bygroups(Text, Whitespace)),
61 (r"\\", Text),
62 (r"(in|and|or|not)\b", Operator.Word),
63 (r"!=|==|<<|>>|&&|\+=|-=|\*=|/=|%=|&=|\|=|\|\||[-~+/*%=<>&^.!|$]",
64 Operator),
65 include("keywords"),
66 (r"(func)(\s+)", bygroups(Keyword, Whitespace), "funcname"),
67 (r"(class)(\s+)", bygroups(Keyword, Whitespace), "classname"),
68 include("builtins"),
69 ('([rR]|[uUbB][rR]|[rR][uUbB])(""")',
70 bygroups(String.Affix, String.Double),
71 "tdqs"),
72 ("([rR]|[uUbB][rR]|[rR][uUbB])(''')",
73 bygroups(String.Affix, String.Single),
74 "tsqs"),
75 ('([rR]|[uUbB][rR]|[rR][uUbB])(")',
76 bygroups(String.Affix, String.Double),
77 "dqs"),
78 ("([rR]|[uUbB][rR]|[rR][uUbB])(')",
79 bygroups(String.Affix, String.Single),
80 "sqs"),
81 ('([uUbB]?)(""")',
82 bygroups(String.Affix, String.Double),
83 combined("stringescape", "tdqs")),
84 ("([uUbB]?)(''')",
85 bygroups(String.Affix, String.Single),
86 combined("stringescape", "tsqs")),
87 ('([uUbB]?)(")',
88 bygroups(String.Affix, String.Double),
89 combined("stringescape", "dqs")),
90 ("([uUbB]?)(')",
91 bygroups(String.Affix, String.Single),
92 combined("stringescape", "sqs")),
93 include("name"),
94 include("numbers"),
95 ],
96 "keywords": [
97 (words(("and", "in", "not", "or", "as", "breakpoint", "class",
98 "class_name", "extends", "is", "func", "setget", "signal",
99 "tool", "const", "enum", "export", "onready", "static",
100 "var", "break", "continue", "if", "elif", "else", "for",
101 "pass", "return", "match", "while", "remote", "master",
102 "puppet", "remotesync", "mastersync", "puppetsync"),
103 suffix=r"\b"), Keyword),
104 ],
105 "builtins": [
106 (words(("Color8", "ColorN", "abs", "acos", "asin", "assert", "atan",
107 "atan2", "bytes2var", "ceil", "char", "clamp", "convert",
108 "cos", "cosh", "db2linear", "decimals", "dectime", "deg2rad",
109 "dict2inst", "ease", "exp", "floor", "fmod", "fposmod",
110 "funcref", "hash", "inst2dict", "instance_from_id", "is_inf",
111 "is_nan", "lerp", "linear2db", "load", "log", "max", "min",
112 "nearest_po2", "pow", "preload", "print", "print_stack",
113 "printerr", "printraw", "prints", "printt", "rad2deg",
114 "rand_range", "rand_seed", "randf", "randi", "randomize",
115 "range", "round", "seed", "sign", "sin", "sinh", "sqrt",
116 "stepify", "str", "str2var", "tan", "tan", "tanh",
117 "type_exist", "typeof", "var2bytes", "var2str", "weakref",
118 "yield"), prefix=r"(?<!\.)", suffix=r"\b"),
119 Name.Builtin),
120 (r"((?<!\.)(self|false|true)|(PI|TAU|NAN|INF)" r")\b",
121 Name.Builtin.Pseudo),
122 (words(("bool", "int", "float", "String", "NodePath", "Vector2",
123 "Rect2", "Transform2D", "Vector3", "Rect3", "Plane", "Quat",
124 "Basis", "Transform", "Color", "RID", "Object", "NodePath",
125 "Dictionary", "Array", "PackedByteArray", "PackedInt32Array",
126 "PackedInt64Array", "PackedFloat32Array", "PackedFloat64Array",
127 "PackedStringArray", "PackedVector2Array", "PackedVector3Array",
128 "PackedColorArray", "null", "void"),
129 prefix=r"(?<!\.)", suffix=r"\b"),
130 Name.Builtin.Type),
131 ],
132 "numbers": [
133 (r"(\d+\.\d*|\d*\.\d+)([eE][+-]?[0-9]+)?j?", Number.Float),
134 (r"\d+[eE][+-]?[0-9]+j?", Number.Float),
135 (r"0[xX][a-fA-F0-9]+", Number.Hex),
136 (r"\d+j?", Number.Integer),
137 ],
138 "name": [(r"[a-zA-Z_]\w*", Name)],
139 "funcname": [(r"[a-zA-Z_]\w*", Name.Function, "#pop"), default("#pop")],
140 "classname": [(r"[a-zA-Z_]\w*", Name.Class, "#pop")],
141 "stringescape": [
142 (
143 r'\\([\\abfnrtv"\']|\n|N\{.*?\}|u[a-fA-F0-9]{4}|'
144 r"U[a-fA-F0-9]{8}|x[a-fA-F0-9]{2}|[0-7]{1,3})",
145 String.Escape,
146 )
147 ],
148 "strings-single": innerstring_rules(String.Single),
149 "strings-double": innerstring_rules(String.Double),
150 "dqs": [
151 (r'"', String.Double, "#pop"),
152 (r'\\\\|\\"|\\\n', String.Escape), # included here for raw strings
153 include("strings-double"),
154 ],
155 "sqs": [
156 (r"'", String.Single, "#pop"),
157 (r"\\\\|\\'|\\\n", String.Escape), # included here for raw strings
158 include("strings-single"),
159 ],
160 "tdqs": [
161 (r'"""', String.Double, "#pop"),
162 include("strings-double"),
163 (r"\n", Whitespace),
164 ],
165 "tsqs": [
166 (r"'''", String.Single, "#pop"),
167 include("strings-single"),
168 (r"\n", Whitespace),
169 ],
170 }
171
172 def analyse_text(text):
173 score = 0.0
174
175 if re.search(
176 r"func (_ready|_init|_input|_process|_unhandled_input)", text
177 ):
178 score += 0.8
179
180 if re.search(
181 r"(extends |class_name |onready |preload|load|setget|func [^_])",
182 text
183 ):
184 score += 0.4
185
186 if re.search(r"(var|const|enum|export|signal|tool)", text):
187 score += 0.2
188
189 return min(score, 1.0)