Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Fix errors in grammar syntax #416

Merged
merged 12 commits into from
Oct 25, 2022
4 changes: 0 additions & 4 deletions syntaxes/GDResource.tmLanguage.json
Original file line number Diff line number Diff line change
@@ -1,10 +1,6 @@
{
"version": "v0.6.0",
"scopeName": "source.gdresource",
"uuid": "e076faa2-3c52-42fa-a8e6-9a7c453c1a5b",
"information_for_contributors": [
"aster: galaster@foxmail.com"
],
"patterns": [
{
"include": "#embedded_shader"
Expand Down
2 changes: 1 addition & 1 deletion syntaxes/GDScript.tmLanguage.json
Original file line number Diff line number Diff line change
Expand Up @@ -286,7 +286,7 @@
"in_keyword": {
"patterns": [
{
"match": "\\b(?<=for\\s[\\w]*\\s)(in)\\b",
"match": "\\b(?:for\\s[\\w]*\\s)(in)\\b",
"name": "keyword.control.gdscript"

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I didn't test this, but I suspect that it will now consider the whole thing as a keyword.
E.g. for i in array then for i in is a keyword. It shouldn't mark the loop variable (i in this case) as a keyword.

The correct thing to do here is to join this with the for keyword so that one rule recognizes both keywords. You don't need to remove for from control_flow (for should still be a keyword even before in is typed) as long as this is before so it has higher priority; but change this rule to something along the lines of:

{
  "match": "\\b(for)\\s+([a-zA-Z_]\\w*)\\s+(in)\\b",
  "captures": {
    "1": { "name": "keyword.control.gdscript" },
    "2": { "name": "variable.name.gdscript" },
    "3": { "name": "keyword.control.gdscript" }
  }
},

I changed \w (which matches [a-zA-Z0-9_] including numbers) since I believe identifiers can't start with a number (correct me if I'm wrong). It's probably worth checking this on other places in the code too. I'm assuming identifiers allow ASCII only.
I also allowed more than one space in the syntax.
Btw, using a tool like https://regex101.com/ while you write it really helps with understanding nuances in regexes and checking that it will really do what is expected, and prevent against corner cases.

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I cannot believe that I missed this. You were correct, i was purple.

image

Your rule worked great. I removed the middle capture group and it's name block, because we don't actually use the variable.name tag for variables. Python doesn't tag variable names, they're just white, so that's the behavior I want to imitate.

I changed \w (which matches [a-zA-Z0-9_] including numbers) since I believe identifiers can't start with a number (correct me if I'm wrong). It's probably worth checking this on other places in the code too. I'm assuming identifiers allow ASCII only.

Many other places used the full form [a-zA-Z_][a-zA-Z_0-9]*, but I just replaced them with [a-zA-Z_]\\w*. I wish we could give a block like this a custom name so we don't have to copy it everywhere.

Btw, using a tool like regex101.com while you write it really helps with understanding nuances in regexes and checking that it will really do what is expected, and prevent against corner cases.

I started off doing this, but having to add all the extra escapes to move a rule over to vscode was too annoying so I stopped. I've thought about switching to yaml files, since the regex strings won't need escapes, but there was some problem with that too.

Copy link

@AlfishSoftware AlfishSoftware Sep 16, 2022

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It's probably worth checking this on other places in the code too.

Many other places used the full form [a-zA-Z_][a-zA-Z_0-9]*, but I just replaced them with [a-zA-Z_]\\w*.

I was actually referring to places capturing identifiers with just \w+, which would allow names starting with numbers, e.g. 1whatever, 0e2f, 0xFACADE. I took a look and saw it in 2 places.
So, unless this is on purpose for some reason, this seems like it makes more sense:

Line 360:

"match": "(:)\\s*([a-zA-Z_]\\w*)?",

Line 375:

"match": "(setget)\\s+([a-zA-Z_]\\w*)?(?:[,]\\s*([a-zA-Z_]\\w*)?)?",

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Another good catch, thanks. I found some other places where identifier names were being matched with [[:alpha:]_]\\w*, so I standardized all of them to [a-zA-Z_]\\w*.

},
{
Expand Down
68 changes: 41 additions & 27 deletions syntaxes/GDShader.tmLanguage.json
Original file line number Diff line number Diff line change
Expand Up @@ -76,7 +76,7 @@
},
"classifier": {
"name": "meta.classifier.gdshader",
"begin": "(?=\b(?:shader_type|render_mode)\b)",
"begin": "(?=\\b(?:shader_type|render_mode)\\b)",
"patterns": [
{
"include": "#comment"
Expand All @@ -95,11 +95,11 @@
},
"classifierKeyword": {
"name": "keyword.language.classifier.gdshader",
"match": "\b(?:shader_type|render_mode)\b"
"match": "\\b(?:shader_type|render_mode)\\b"
},
"identifierClassification": {
"name": "entity.other.inherited-class.gdshader",
"match": "\b[a-z_]+\b"
"match": "\\b[a-z_]+\\b"
},
"definition": {
"patterns": [
Expand Down Expand Up @@ -133,7 +133,7 @@
]
},
"structDefinition": {
"begin": "(?=\b(?:struct)\b)",
"begin": "(?=\\b(?:struct)\\b)",
"patterns": [
{
"include": "#comment"
Expand All @@ -155,11 +155,11 @@
},
"structKeyword": {
"name": "keyword.other.struct.gdshader",
"match": "\b(?:struct)\b"
"match": "\\b(?:struct)\\b"
},
"structName": {
"name": "entity.name.type.struct.gdshader",
"match": "\b[a-zA-Z_]\\w*\b"
"match": "\\b[a-zA-Z_]\\w*\\b"
},
"structDefinitionBlock": {
"name": "meta.definition.block.struct.gdshader",
Expand Down Expand Up @@ -190,7 +190,7 @@
},
"fieldDefinition": {
"name": "meta.definition.field.gdshader",
"begin": "\b[a-zA-Z_]\\w*\b",
"begin": "\\b[a-zA-Z_]\\w*\\b",
"beginCaptures": {
"0": {
"patterns": [
Expand Down Expand Up @@ -225,7 +225,7 @@
},
"fieldName": {
"name": "entity.name.variable.field.gdshader",
"match": "\b[a-zA-Z_]\\w*\b"
"match": "\\b[a-zA-Z_]\\w*\\b"
},
"keyword": {
"patterns": [
Expand Down Expand Up @@ -254,23 +254,23 @@
},
"controlKeyword": {
"name": "keyword.control.gdshader",
"match": "\b(?:if|else|do|while|for|continue|break|switch|case|default|return|discard)\b"
"match": "\\b(?:if|else|do|while|for|continue|break|switch|case|default|return|discard)\\b"
},
"modifierKeyword": {
"name": "storage.modifier.gdshader",
"match": "\b(?:const|global|instance|uniform|varying|in|out|inout|flat|smooth)\b"
"match": "\\b(?:const|global|instance|uniform|varying|in|out|inout|flat|smooth)\\b"
},
"precisionKeyword": {
"name": "storage.type.built-in.primitive.precision.gdshader",
"match": "\b(?:low|medium|high)p\b"
"match": "\\b(?:low|medium|high)p\\b"
},
"typeKeyword": {
"name": "support.type.gdshader",
"match": "\b(?:void|bool|[biu]?vec[234]|u?int|float|mat[234]|[iu]?sampler(?:3D|2D(?:Array)?)|samplerCube)\b"
"match": "\\b(?:void|bool|[biu]?vec[234]|u?int|float|mat[234]|[iu]?sampler(?:3D|2D(?:Array)?)|samplerCube)\\b"
},
"hintKeyword": {
"name": "support.type.annotation.gdshader",
"match": "\b(?:source_color|hint_(?:color|range|(?:black_)?albedo|normal|(?:default_)?(?:white|black)|aniso|anisotropy|roughness_(?:[rgba]|normal|gray))|filter_(?:nearest|linear)(?:_mipmap(?:_anisotropic)?)?|repeat_(?:en|dis)able)\b"
"match": "\\b(?:source_color|hint_(?:color|range|(?:black_)?albedo|normal|(?:default_)?(?:white|black)|aniso|anisotropy|roughness_(?:[rgba]|normal|gray))|filter_(?:nearest|linear)(?:_mipmap(?:_anisotropic)?)?|repeat_(?:en|dis)able)\\b"
},
"element": {
"patterns": [
Expand Down Expand Up @@ -314,51 +314,65 @@
},
"literalFloat": {
"name": "constant.numeric.float.gdshader",
"match": "\b(?:\\d+[eE][-+]?\\d+|(?:\\d*[.]\\d+|\\d+[.])(?:[eE][-+]?\\d+)?)[fF]?"
"match": "\\b(?:\\d+[eE][-+]?\\d+|(?:\\d*[.]\\d+|\\d+[.])(?:[eE][-+]?\\d+)?)[fF]?"
},
"literalInt": {
"name": "constant.numeric.integer.gdshader",
"match": "\b(?:0[xX][0-9A-Fa-f]+|\\d+[uU]?)\b"
"match": "\\b(?:0[xX][0-9A-Fa-f]+|\\d+[uU]?)\\b"
},
"literalBool": {
"name": "constant.language.boolean.gdshader",
"match": "\b(?:false|true)\b"
"match": "\\b(?:false|true)\\b"
},
"identifierType": {
"name": "entity.name.type.gdshader",
"match": "\b[a-zA-Z_]\\w*(?=(?:\\s*\\[\\s*\\w*\\s*\\])?\\s+[a-zA-Z_]\\w*\b)"
"match": "\\b[a-zA-Z_]\\w*(?=(?:\\s*\\[\\s*\\w*\\s*\\])?\\s+[a-zA-Z_]\\w*\\b)"
},
"constructor": {
"name": "entity.name.type.constructor.gdshader",
"match": "\b[a-zA-Z_]\\w*(?=\\s*\\[\\s*\\w*\\s*\\]\\s*[(])|\b[A-Z]\\w*(?=\\s*[(])"
"match": "\\b[a-zA-Z_]\\w*(?=\\s*\\[\\s*\\w*\\s*\\]\\s*[(])|\\b[A-Z]\\w*(?=\\s*[(])"
},
"processorFunction": {
"name": "support.function.gdshader",
"match": "\b(?:vertex|fragment|light|start|process|sky|fog)(?=(?:\\s|/\\*(?:\\*(?!/)|[^*])*\\*/)*[(])"
"match": "\\b(?:vertex|fragment|light|start|process|sky|fog)(?=(?:\\s|/\\*(?:\\*(?!/)|[^*])*\\*/)*[(])"
},
"identifierFunction": {
"name": "entity.name.function.gdshader",
"match": "\b[a-zA-Z_]\\w*(?=(?:\\s|/\\*(?:\\*(?!/)|[^*])*\\*/)*[(])"
"match": "\\b[a-zA-Z_]\\w*(?=(?:\\s|/\\*(?:\\*(?!/)|[^*])*\\*/)*[(])"
},
"swizzling": {
"name": "variable.other.property.gdshader",
"match": "(?<=[.]\\s*)(?:[xyzw]{2,4}|[rgba]{2,4}|[stpq]{2,4})\b"
"match": "([.])\\s*([xyzw]{2,4}|[rgba]{2,4}|[stpq]{2,4})\\b",
"captures": {
"1": {
"name": "punctuation.accessor.gdshader"
},
"2": {
"name": "variable.other.property.gdshader"
}
}
},
"identifierField": {
"name": "entity.name.variable.field.gdshader",
"match": "(?<=[.]\\s*)[a-zA-Z_]\\w*\b"
"match": "([.])\\s*([a-zA-Z_]\\w*)\\b(?!\\s*\\()",
"captures": {
"1": {
"name": "punctuation.accessor.gdshader"
},
"2": {
"name": "entity.name.variable.field.gdshader"
}
}
},
"constantFloat": {
"name": "constant.language.float.gdshader",
"match": "\b(?:E|PI|TAU)\b"
"match": "\\b(?:E|PI|TAU)\\b"
},
"languageVariable": {
"name": "variable.language.gdshader",
"match": "\b(?:[A-Z][A-Z_0-9]*)\b"
"match": "\\b(?:[A-Z][A-Z_0-9]*)\\b"
},
"identifierVariable": {
"name": "variable.name.gdshader",
"match": "\b[a-zA-Z_]\\w*\b"
"match": "\\b[a-zA-Z_]\\w*\\b"
},
"separator": {
"patterns": [
Expand Down
193 changes: 193 additions & 0 deletions syntaxes/examples/example2.gdshader
Original file line number Diff line number Diff line change
@@ -0,0 +1,193 @@
shader_type spatial;
render_mode wireframe;

const lowp vec3 v[1] = lowp vec3[1] ( vec3(0, 0, 1) );

void fn() {
// The required amount of scalars
vec4 a0 = vec4(0.0, 1.0, 2.0, 3.0);
// Complementary vectors and/or scalars
vec4 a1 = vec4(vec2(0.0, 1.0), vec2(2.0, 3.0));
vec4 a2 = vec4(vec3(0.0, 1.0, 2.0), 3.0);
// A single scalar for the whole vector
vec4 a3 = vec4(0.0);

mat2 m2 = mat2(vec2(1.0, 0.0), vec2(0.0, 1.0));
mat3 m3 = mat3(vec3(1.0, 0.0, 0.0), vec3(0.0, 1.0, 0.0), vec3(0.0, 0.0, 1.0));
mat4 identity = mat4(1.0);

mat3 basis = mat3(identity);
mat4 m4 = mat4(basis);
mat2 m2a = mat2(m4);

vec4 a = vec4(0.0, 1.0, 2.0, 3.0);
vec3 b = a.rgb; // Creates a vec3 with vec4 components.
vec3 b1 = a.ggg; // Also valid; creates a vec3 and fills it with a single vec4 component.
vec3 b2 = a.bgr; // "b" will be vec3(2.0, 1.0, 0.0).
vec3 b3 = a.xyz; // Also rgba, xyzw are equivalent.
vec3 b4 = a.stp; // And stpq (for texture coordinates).
b.bgr = a.rgb; // Valid assignment. "b"'s "blue" component will be "a"'s "red" and vice versa.

lowp vec4 v0 = vec4(0.0, 1.0, 2.0, 3.0); // low precision, usually 8 bits per component mapped to 0-1
mediump vec4 v1 = vec4(0.0, 1.0, 2.0, 3.0); // medium precision, usually 16 bits or half float
highp vec4 v2 = vec4(0.0, 1.0, 2.0, 3.0); // high precision, uses full float or integer range (default)

const vec2 aa = vec2(0.0, 1.0);
vec2 bb;
bb = aa; // valid

const vec2 V1 = vec2(1, 1), V2 = vec2(2, 2);

float fa = 1.0;
float fb = 1.0f;
float fc = 1e-1;

uint ua = 1u;
uint ub = uint(1);

bool cond = false;
// `if` and `else`.
if (cond) {
} else {
}
// Ternary operator.
// This is an expression that behaves like `if`/`else` and returns the value.
// If `cond` evaluates to `true`, `result` will be `9`.
// Otherwise, `result` will be `5`.
int i, result = cond ? 9 : 5;
// `switch`.
switch (i) { // `i` should be a signed integer expression.
case -1:
break;
case 0:
return; // `break` or `return` to avoid running the next `case`.
case 1: // Fallthrough (no `break` or `return`): will run the next `case`.
case 2:
break;
//...
default: // Only run if no `case` above matches. Optional.
break;
}
// `for` loop. Best used when the number of elements to iterate on
// is known in advance.
for (int i = 0; i < 10; i++) {
}
// `while` loop. Best used when the number of elements to iterate on
// is not known in advance.
while (cond) {
}
// `do while`. Like `while`, but always runs at least once even if `cond`
// never evaluates to `true`.
do {
} while (cond);
}

const float PI_ = 3.14159265358979323846;

struct PointLight {
vec3 position;
vec3 color;
float intensity;
};

struct Scene {
PointLight lights[2];
};

const Scene scene = Scene(PointLight[2](
PointLight(vec3(0.0, 0.0, 0.0), vec3(1.0, 0.0, 0.0), 1.0),
PointLight(vec3(0.0, 0.0, 0.0), vec3(1.0, 0.0, 0.0), 1.0)
));

Scene construct_scene(PointLight light1, PointLight light2) {
return Scene({light1, light2});
}

varying flat vec3 some_color;

varying float var_arr[3];

varying smooth vec3 some_light;

uniform float some_value;

uniform vec4 color : hint_color;
uniform float amount : hint_range(0, 1);
uniform vec4 other_color : hint_color = vec4(1.0);

uniform vec4 some_vector = vec4(0.0);
uniform vec4 some_color2 : hint_color = vec4(1.0);

void vertex() {
const float arr[] = { 1.0, 0.5, 0.0 };
COLOR.r = arr[0]; // valid

float arr2[3];
arr2[0] = 1.0; // setter
COLOR.r = arr2[0]; // getter

PointLight light;
light.position = vec3(0.0);
light.color = vec3(1.0, 0.0, 0.0);
light.intensity = 0.5;

COLOR.rgb = construct_scene(
PointLight(vec3(0.0, 0.0, 0.0), vec3(1.0, 0.0, 0.0), 1.0),
PointLight(vec3(0.0, 0.0, 0.0), vec3(1.0, 0.0, 1.0), 1.0)
).lights[0].color;

some_color = NORMAL; // Make the normal the color.

var_arr[0] = 1.0;
var_arr[1] = 0.0;
}

void fragment() {
float arr[3];

float float_arr[3] = float[3] (1.0, 0.5, 0.0); // first constructor
int int_arr[3] = int[] (2, 1, 0); // second constructor
vec2 vec2_arr[3] = { vec2(1.0, 1.0), vec2(0.5, 0.5), vec2(0.0, 0.0) }; // third constructor
bool bool_arr[] = { true, true, false }; // fourth constructor - size is defined automatically from the element count

float a[3] = float[3] (1.0, 0.5, 0.0),
b[2] = { 1.0, 0.5 },
c[] = { 0.7 },
d = 0.0,
e[5];

float arr2[] = { 0.0, 1.0, 0.5, -1.0 };
for (int i = 0; i < arr2.length(); i++) {
}

ALBEDO = v[0];

PointLight light = PointLight(vec3(0.0), vec3(1.0, 0.0, 0.0), 0.5);

ALBEDO = scene.lights[0].color;

const float EPSILON = 0.0001, value = 0.f;
if (value >= 0.3 - EPSILON && value <= 0.3 + EPSILON) {
discard;
}

ALBEDO = vec3(var_arr[0], var_arr[1], var_arr[2]); // red color

some_light = ALBEDO * 100.0; // Make a shining light.
}

void sum2(int a, in int b, inout int result) {
result = a + b;
}
void sub2(const int a, const in int b, out int result) {
result = a - b;
}

global uniform sampler2D global1;
instance uniform int un = 0;

void light() {
DIFFUSE_LIGHT = some_color * 100.; // optionally

DIFFUSE_LIGHT = some_light;
}