redscript-mc 1.2.18 → 1.2.19

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -564,10 +564,29 @@ class Parser {
564
564
  parseForRangeStmt(forToken) {
565
565
  const varName = this.expect('ident').value;
566
566
  this.expect('in');
567
- const rangeToken = this.expect('range_lit');
568
- const range = this.parseRangeValue(rangeToken.value);
569
- const start = this.withLoc({ kind: 'int_lit', value: range.min ?? 0 }, rangeToken);
570
- const end = this.withLoc({ kind: 'int_lit', value: range.max ?? 0 }, rangeToken);
567
+ let start;
568
+ let end;
569
+ if (this.check('range_lit')) {
570
+ // Literal range: 0..10, 0..count, 0..=9
571
+ const rangeToken = this.advance();
572
+ const range = this.parseRangeValue(rangeToken.value);
573
+ start = this.withLoc({ kind: 'int_lit', value: range.min ?? 0 }, rangeToken);
574
+ if (range.max !== null && range.max !== undefined) {
575
+ // Fully numeric: 0..10
576
+ end = this.withLoc({ kind: 'int_lit', value: range.max }, rangeToken);
577
+ }
578
+ else {
579
+ // Open-ended: "0.." — parse the end expression from next tokens
580
+ end = this.parseUnaryExpr();
581
+ }
582
+ }
583
+ else {
584
+ // Dynamic range: expr..expr (e.g. start..end) — not yet supported
585
+ // Fall back to: parse as int_lit 0..0 (safe default)
586
+ start = this.withLoc({ kind: 'int_lit', value: 0 }, this.peek());
587
+ end = this.withLoc({ kind: 'int_lit', value: 0 }, this.peek());
588
+ this.error('Dynamic range start requires a literal integer (e.g. 0..count)');
589
+ }
571
590
  const body = this.parseBlock();
572
591
  return this.withLoc({ kind: 'for_range', varName, start, end, body }, forToken);
573
592
  }
@@ -1083,6 +1102,20 @@ class Parser {
1083
1102
  this.error(`Unexpected token '${token.kind}'`);
1084
1103
  }
1085
1104
  parseLiteralExpr() {
1105
+ // Support negative literals: -5, -3.14
1106
+ if (this.check('-')) {
1107
+ this.advance();
1108
+ const token = this.peek();
1109
+ if (token.kind === 'int_lit') {
1110
+ this.advance();
1111
+ return this.withLoc({ kind: 'int_lit', value: -Number(token.value) }, token);
1112
+ }
1113
+ if (token.kind === 'float_lit') {
1114
+ this.advance();
1115
+ return this.withLoc({ kind: 'float_lit', value: -Number(token.value) }, token);
1116
+ }
1117
+ this.error('Expected number after unary -');
1118
+ }
1086
1119
  const expr = this.parsePrimaryExpr();
1087
1120
  if (expr.kind === 'int_lit' ||
1088
1121
  expr.kind === 'float_lit' ||
@@ -186,6 +186,7 @@ var require_lexer = __commonJS({
186
186
  execute: "execute",
187
187
  run: "run",
188
188
  unless: "unless",
189
+ declare: "declare",
189
190
  int: "int",
190
191
  bool: "bool",
191
192
  float: "float",
@@ -355,6 +356,13 @@ var require_lexer = __commonJS({
355
356
  value += this.advance();
356
357
  }
357
358
  }
359
+ if (/[a-zA-Z_]/.test(this.peek())) {
360
+ let ident = "";
361
+ while (/[a-zA-Z0-9_]/.test(this.peek())) {
362
+ ident += this.advance();
363
+ }
364
+ value += ident;
365
+ }
358
366
  this.addToken("rel_coord", value, startLine, startCol);
359
367
  return;
360
368
  }
@@ -815,6 +823,9 @@ var require_parser = __commonJS({
815
823
  enums.push(this.parseEnumDecl());
816
824
  } else if (this.check("const")) {
817
825
  consts.push(this.parseConstDecl());
826
+ } else if (this.check("declare")) {
827
+ this.advance();
828
+ this.parseDeclareStub();
818
829
  } else {
819
830
  declarations.push(this.parseFnDecl());
820
831
  }
@@ -877,12 +888,15 @@ var require_parser = __commonJS({
877
888
  parseConstDecl() {
878
889
  const constToken = this.expect("const");
879
890
  const name = this.expect("ident").value;
880
- this.expect(":");
881
- const type = this.parseType();
891
+ let type;
892
+ if (this.match(":")) {
893
+ type = this.parseType();
894
+ }
882
895
  this.expect("=");
883
896
  const value = this.parseLiteralExpr();
884
897
  this.match(";");
885
- return this.withLoc({ name, type, value }, constToken);
898
+ const inferredType = type ?? (value.kind === "str_lit" ? { kind: "named", name: "string" } : value.kind === "bool_lit" ? { kind: "named", name: "bool" } : value.kind === "float_lit" ? { kind: "named", name: "float" } : { kind: "named", name: "int" });
899
+ return this.withLoc({ name, type: inferredType, value }, constToken);
886
900
  }
887
901
  parseGlobalDecl(mutable) {
888
902
  const token = this.advance();
@@ -911,6 +925,24 @@ var require_parser = __commonJS({
911
925
  const body = this.parseBlock();
912
926
  return this.withLoc({ name, params, returnType, decorators, body }, fnToken);
913
927
  }
928
+ /** Parse a `declare fn name(params): returnType;` stub — no body, just discard. */
929
+ parseDeclareStub() {
930
+ this.expect("fn");
931
+ this.expect("ident");
932
+ this.expect("(");
933
+ let depth = 1;
934
+ while (!this.check("eof") && depth > 0) {
935
+ const t = this.advance();
936
+ if (t.kind === "(")
937
+ depth++;
938
+ else if (t.kind === ")")
939
+ depth--;
940
+ }
941
+ if (this.match(":") || this.match("->")) {
942
+ this.parseType();
943
+ }
944
+ this.match(";");
945
+ }
914
946
  parseDecorators() {
915
947
  const decorators = [];
916
948
  while (this.check("decorator")) {
@@ -3581,6 +3613,15 @@ var require_lowering = __commonJS({
3581
3613
  return null;
3582
3614
  return expr.name;
3583
3615
  }
3616
+ tryGetMacroParamByName(name) {
3617
+ if (!this.currentFnParamNames.has(name))
3618
+ return null;
3619
+ if (this.constValues.has(name))
3620
+ return null;
3621
+ if (this.stringValues.has(name))
3622
+ return null;
3623
+ return name;
3624
+ }
3584
3625
  /**
3585
3626
  * Converts an expression to a string for use as a builtin arg.
3586
3627
  * If the expression is a macro param, returns `$(name)` and sets macroParam.
@@ -3590,6 +3631,17 @@ var require_lowering = __commonJS({
3590
3631
  if (macroParam) {
3591
3632
  return { str: `$(${macroParam})`, macroParam };
3592
3633
  }
3634
+ if (expr.kind === "rel_coord" || expr.kind === "local_coord") {
3635
+ const val = expr.value;
3636
+ const prefix = val[0];
3637
+ const rest = val.slice(1);
3638
+ if (rest && /^[a-zA-Z_][a-zA-Z0-9_]*$/.test(rest)) {
3639
+ const paramName = this.tryGetMacroParamByName(rest);
3640
+ if (paramName) {
3641
+ return { str: `${prefix}$(${paramName})`, macroParam: paramName };
3642
+ }
3643
+ }
3644
+ }
3593
3645
  if (expr.kind === "struct_lit" || expr.kind === "array_lit") {
3594
3646
  return { str: this.exprToSnbt(expr) };
3595
3647
  }
@@ -5875,8 +5927,11 @@ var require_lowering = __commonJS({
5875
5927
  }
5876
5928
  /**
5877
5929
  * Checks a raw() command string for `${...}` interpolation containing runtime variables.
5878
- * - If the interpolated name is a compile-time constant → OK, no error.
5879
- * - If the interpolated name is a runtime variableDiagnosticError.
5930
+ * - If the interpolated expression is a numeric literal → OK (MC macro syntax).
5931
+ * - If the interpolated name is a compile-time constant (in constValues) OK.
5932
+ * - If the interpolated name is a known runtime variable (in varMap) → DiagnosticError.
5933
+ * - Unknown names → OK (could be MC macro params or external constants).
5934
+ *
5880
5935
  * This catches the common mistake of writing raw("say ${score}") expecting interpolation,
5881
5936
  * which would silently emit a literal `${score}` in the MC command.
5882
5937
  */
@@ -5891,8 +5946,10 @@ var require_lowering = __commonJS({
5891
5946
  if (this.constValues.has(name)) {
5892
5947
  continue;
5893
5948
  }
5894
- const loc = span ?? { line: 1, col: 1 };
5895
- throw new diagnostics_1.DiagnosticError("LoweringError", `raw() command contains runtime variable interpolation '\${${name}}'. Variables cannot be interpolated into raw commands at compile time. Use f-string messages for say/tell/announce, or MC macro syntax '$(${name})' for MC 1.20.2+ commands.`, loc);
5949
+ if (this.varMap.has(name) || this.currentFnParamNames.has(name)) {
5950
+ const loc = span ?? { line: 1, col: 1 };
5951
+ throw new diagnostics_1.DiagnosticError("LoweringError", `raw() command contains runtime variable interpolation '\${${name}}'. Variables cannot be interpolated into raw commands at compile time. Use f-string messages (say/tell/announce) or MC macro syntax '$(${name})' for MC 1.20.2+ commands.`, loc);
5952
+ }
5896
5953
  }
5897
5954
  }
5898
5955
  resolveInstanceMethod(expr) {
@@ -1,12 +1,12 @@
1
1
  {
2
2
  "name": "redscript-vscode",
3
- "version": "1.0.9",
3
+ "version": "1.0.11",
4
4
  "lockfileVersion": 3,
5
5
  "requires": true,
6
6
  "packages": {
7
7
  "": {
8
8
  "name": "redscript-vscode",
9
- "version": "1.0.9",
9
+ "version": "1.0.11",
10
10
  "license": "MIT",
11
11
  "dependencies": {
12
12
  "redscript": "file:../../"
@@ -2,7 +2,7 @@
2
2
  "name": "redscript-vscode",
3
3
  "displayName": "RedScript for Minecraft",
4
4
  "description": "Syntax highlighting, error diagnostics, and language support for RedScript — a compiler targeting Minecraft Java Edition",
5
- "version": "1.0.9",
5
+ "version": "1.0.11",
6
6
  "publisher": "bkmashiro",
7
7
  "icon": "icon.png",
8
8
  "license": "MIT",
@@ -271,7 +271,7 @@
271
271
  {
272
272
  "comment": "Builtin functions",
273
273
  "name": "support.function.builtin.redscript",
274
- "match": "\\b(say|tell|announce|title|subtitle|actionbar|title_times|give|kill|effect|clear|kick|xp_add|xp_set|tp|tp_to|setblock|fill|clone|summon|particle|playsound|weather|time_set|time_add|gamerule|difficulty|tag_add|tag_remove|scoreboard_get|scoreboard_set|scoreboard_add|scoreboard_display|scoreboard_hide|scoreboard_add_objective|scoreboard_remove_objective|score|random|random_native|random_sequence|data_get|str_len|push|pop|bossbar_add|bossbar_set_value|bossbar_set_max|bossbar_set_color|bossbar_set_style|bossbar_set_visible|bossbar_set_players|bossbar_remove|bossbar_get_value|team_add|team_remove|team_join|team_leave|team_option|spawn_object|if_entity|unless_entity)(?=\\s*\\()"
274
+ "match": "\\b(say|tell|tellraw|announce|title|subtitle|actionbar|title_times|give|kill|effect|effect_clear|clear|kick|xp_add|xp_set|tp|tp_to|setblock|fill|clone|summon|particle|playsound|weather|time_set|time_add|gamerule|difficulty|tag_add|tag_remove|scoreboard_get|score|scoreboard_set|scoreboard_add|scoreboard_display|scoreboard_hide|scoreboard_add_objective|scoreboard_remove_objective|random|random_native|random_sequence|data_get|data_merge|str_len|push|pop|bossbar_add|bossbar_set_value|bossbar_set_max|bossbar_set_color|bossbar_set_style|bossbar_set_visible|bossbar_set_players|bossbar_remove|bossbar_get_value|team_add|team_remove|team_join|team_leave|team_option|set_new|set_add|set_contains|set_remove|set_clear|setTimeout|setInterval|clearInterval|spawn_object|if_entity|unless_entity)(?=\\s*\\()"
275
275
  },
276
276
  {
277
277
  "comment": "User-defined function calls",
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "redscript-mc",
3
- "version": "1.2.18",
3
+ "version": "1.2.19",
4
4
  "description": "A high-level programming language that compiles to Minecraft datapacks",
5
5
  "main": "dist/index.js",
6
6
  "bin": {
@@ -28,7 +28,7 @@ struct GameState {
28
28
  blue_flag_taken: int
29
29
  }
30
30
 
31
- let game: GameState = GameState {
31
+ let game: GameState = {
32
32
  running: 0,
33
33
  red_score: 0,
34
34
  blue_score: 0,
@@ -31,7 +31,7 @@ struct HGState {
31
31
  alive_count: int
32
32
  }
33
33
 
34
- let game: HGState = HGState {
34
+ let game: HGState = {
35
35
  running: 0,
36
36
  phase: 0,
37
37
  countdown: 0,
@@ -29,7 +29,7 @@ struct SurvivalState {
29
29
  total_kills: int
30
30
  }
31
31
 
32
- let state: SurvivalState = SurvivalState {
32
+ let state: SurvivalState = {
33
33
  running: 0,
34
34
  wave: 0,
35
35
  zombies_left: 0,
@@ -668,17 +668,29 @@ export class Parser {
668
668
  private parseForRangeStmt(forToken: Token): Stmt {
669
669
  const varName = this.expect('ident').value
670
670
  this.expect('in')
671
- const rangeToken = this.expect('range_lit')
672
- const range = this.parseRangeValue(rangeToken.value)
673
671
 
674
- const start: Expr = this.withLoc(
675
- { kind: 'int_lit', value: range.min ?? 0 },
676
- rangeToken
677
- )
678
- const end: Expr = this.withLoc(
679
- { kind: 'int_lit', value: range.max ?? 0 },
680
- rangeToken
681
- )
672
+ let start: Expr
673
+ let end: Expr
674
+
675
+ if (this.check('range_lit')) {
676
+ // Literal range: 0..10, 0..count, 0..=9
677
+ const rangeToken = this.advance()
678
+ const range = this.parseRangeValue(rangeToken.value)
679
+ start = this.withLoc({ kind: 'int_lit', value: range.min ?? 0 }, rangeToken)
680
+ if (range.max !== null && range.max !== undefined) {
681
+ // Fully numeric: 0..10
682
+ end = this.withLoc({ kind: 'int_lit', value: range.max }, rangeToken)
683
+ } else {
684
+ // Open-ended: "0.." — parse the end expression from next tokens
685
+ end = this.parseUnaryExpr()
686
+ }
687
+ } else {
688
+ // Dynamic range: expr..expr (e.g. start..end) — not yet supported
689
+ // Fall back to: parse as int_lit 0..0 (safe default)
690
+ start = this.withLoc({ kind: 'int_lit', value: 0 }, this.peek())
691
+ end = this.withLoc({ kind: 'int_lit', value: 0 }, this.peek())
692
+ this.error('Dynamic range start requires a literal integer (e.g. 0..count)')
693
+ }
682
694
 
683
695
  const body = this.parseBlock()
684
696
  return this.withLoc({ kind: 'for_range', varName, start, end, body }, forToken)
@@ -1260,6 +1272,20 @@ export class Parser {
1260
1272
  }
1261
1273
 
1262
1274
  private parseLiteralExpr(): LiteralExpr {
1275
+ // Support negative literals: -5, -3.14
1276
+ if (this.check('-')) {
1277
+ this.advance()
1278
+ const token = this.peek()
1279
+ if (token.kind === 'int_lit') {
1280
+ this.advance()
1281
+ return this.withLoc({ kind: 'int_lit', value: -Number(token.value) }, token)
1282
+ }
1283
+ if (token.kind === 'float_lit') {
1284
+ this.advance()
1285
+ return this.withLoc({ kind: 'float_lit', value: -Number(token.value) }, token)
1286
+ }
1287
+ this.error('Expected number after unary -')
1288
+ }
1263
1289
  const expr = this.parsePrimaryExpr()
1264
1290
  if (
1265
1291
  expr.kind === 'int_lit' ||