redscript-mc 1.2.14 → 1.2.16

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.
package/src/cli.ts CHANGED
@@ -14,6 +14,7 @@ import { generateCommandBlocks } from './codegen/cmdblock'
14
14
  import { compileToStructure } from './codegen/structure'
15
15
  import { formatError } from './diagnostics'
16
16
  import { startRepl } from './repl'
17
+ import { generateDts } from './builtins/metadata'
17
18
  import type { OptimizationStats } from './optimizer/commands'
18
19
  import * as fs from 'fs'
19
20
  import * as path from 'path'
@@ -30,16 +31,18 @@ Usage:
30
31
  redscript watch <dir> [-o <outdir>] [--namespace <ns>] [--hot-reload <url>]
31
32
  redscript check <file>
32
33
  redscript fmt <file.mcrs> [file2.mcrs ...]
34
+ redscript generate-dts [-o <file>]
33
35
  redscript repl
34
36
  redscript version
35
37
 
36
38
  Commands:
37
- compile Compile a RedScript file to a Minecraft datapack
38
- watch Watch a directory for .mcrs file changes, recompile, and hot reload
39
- check Check a RedScript file for errors without generating output
40
- fmt Auto-format RedScript source files
41
- repl Start an interactive RedScript REPL
42
- version Print the RedScript version
39
+ compile Compile a RedScript file to a Minecraft datapack
40
+ watch Watch a directory for .mcrs file changes, recompile, and hot reload
41
+ check Check a RedScript file for errors without generating output
42
+ fmt Auto-format RedScript source files
43
+ generate-dts Generate builtin function declaration file (builtins.d.mcrs)
44
+ repl Start an interactive RedScript REPL
45
+ version Print the RedScript version
43
46
 
44
47
  Options:
45
48
  -o, --output <path> Output directory or file path, depending on target
@@ -441,6 +444,14 @@ async function main(): Promise<void> {
441
444
  break
442
445
  }
443
446
 
447
+ case 'generate-dts': {
448
+ const output = parsed.output ?? 'builtins.d.mcrs'
449
+ const dtsContent = generateDts()
450
+ fs.writeFileSync(output, dtsContent, 'utf-8')
451
+ console.log(`Generated ${output}`)
452
+ break
453
+ }
454
+
444
455
  case 'repl':
445
456
  await startRepl(parsed.namespace ?? 'repl')
446
457
  break
@@ -25,10 +25,8 @@ import { EVENT_TYPES, getEventParamSpecs, isEventTypeName } from '../events/type
25
25
  // require MC macro syntax when called with runtime variables.
26
26
  // ---------------------------------------------------------------------------
27
27
 
28
- const MACRO_AWARE_BUILTINS = new Set([
29
- 'summon', 'particle', 'setblock', 'fill', 'clone',
30
- 'playsound', 'tp', 'tp_to', 'effect', 'effect_clear', 'give',
31
- ])
28
+ // All builtins support macro parameters - any arg that's a function param
29
+ // will automatically use MC 1.20.2+ macro syntax when needed
32
30
 
33
31
  // ---------------------------------------------------------------------------
34
32
  // Builtin Functions
@@ -335,7 +333,7 @@ export class Lowering {
335
333
  }
336
334
 
337
335
  private preScanExpr(expr: Expr, paramNames: Set<string>, macroParams: Set<string>): void {
338
- if (expr.kind === 'call' && MACRO_AWARE_BUILTINS.has(expr.fn)) {
336
+ if (expr.kind === 'call' && BUILTINS[expr.fn] !== undefined) {
339
337
  // All ident args to macro-aware builtins that are params → macro params
340
338
  for (const arg of expr.args) {
341
339
  if (arg.kind === 'ident' && paramNames.has(arg.name)) {
@@ -787,6 +785,7 @@ export class Lowering {
787
785
  this.lowerExecuteStmt(stmt)
788
786
  break
789
787
  case 'raw':
788
+ this.checkRawCommandInterpolation(stmt.cmd, stmt.span)
790
789
  this.builder.emitRaw(stmt.cmd)
791
790
  break
792
791
  }
@@ -2528,30 +2527,16 @@ export class Lowering {
2528
2527
  return { kind: 'const', value: 0 }
2529
2528
  }
2530
2529
 
2531
- // For macro-aware builtins, check if any arg is a param needing macro treatment
2532
- if (MACRO_AWARE_BUILTINS.has(name)) {
2533
- const argResults = args.map(arg => this.exprToBuiltinArg(arg))
2534
- const hasMacroArg = argResults.some(r => r.macroParam !== undefined)
2535
- if (hasMacroArg) {
2536
- argResults.forEach(r => { if (r.macroParam) this.currentFnMacroParams.add(r.macroParam) })
2537
- }
2538
- const strArgs = argResults.map(r => r.str)
2539
- const cmd = BUILTINS[name]?.(strArgs)
2540
- if (cmd) {
2541
- this.builder.emitRaw(hasMacroArg ? `$${cmd}` : cmd)
2542
- }
2543
- return { kind: 'const', value: 0 }
2530
+ // All builtins support macro params - check if any arg is a param needing macro treatment
2531
+ const argResults = args.map(arg => this.exprToBuiltinArg(arg))
2532
+ const hasMacroArg = argResults.some(r => r.macroParam !== undefined)
2533
+ if (hasMacroArg) {
2534
+ argResults.forEach(r => { if (r.macroParam) this.currentFnMacroParams.add(r.macroParam) })
2544
2535
  }
2545
-
2546
- // Convert args to strings for builtin (use SNBT for struct/array literals)
2547
- const strArgs = args.map(arg =>
2548
- arg.kind === 'struct_lit' || arg.kind === 'array_lit'
2549
- ? this.exprToSnbt(arg)
2550
- : this.exprToString(arg)
2551
- )
2552
- const cmd = BUILTINS[name](strArgs)
2536
+ const strArgs = argResults.map(r => r.str)
2537
+ const cmd = BUILTINS[name]?.(strArgs)
2553
2538
  if (cmd) {
2554
- this.builder.emitRaw(cmd)
2539
+ this.builder.emitRaw(hasMacroArg ? `$${cmd}` : cmd)
2555
2540
  }
2556
2541
 
2557
2542
  return { kind: 'const', value: 0 }
@@ -3269,6 +3254,44 @@ export class Lowering {
3269
3254
  return undefined
3270
3255
  }
3271
3256
 
3257
+ /**
3258
+ * Checks a raw() command string for `${...}` interpolation containing runtime variables.
3259
+ * - If the interpolated expression is a numeric literal → OK (MC macro syntax).
3260
+ * - If the interpolated name is a compile-time constant (in constValues) → OK.
3261
+ * - If the interpolated name is a known runtime variable (in varMap) → DiagnosticError.
3262
+ * - Unknown names → OK (could be MC macro params or external constants).
3263
+ *
3264
+ * This catches the common mistake of writing raw("say ${score}") expecting interpolation,
3265
+ * which would silently emit a literal `${score}` in the MC command.
3266
+ */
3267
+ private checkRawCommandInterpolation(cmd: string, span?: Span): void {
3268
+ const interpRe = /\$\{([^}]+)\}/g
3269
+ let match: RegExpExecArray | null
3270
+ while ((match = interpRe.exec(cmd)) !== null) {
3271
+ const name = match[1].trim()
3272
+ // Numeric/boolean literals are fine (intentional MC macro syntax)
3273
+ if (/^\d+(\.\d+)?$/.test(name) || name === 'true' || name === 'false') {
3274
+ continue
3275
+ }
3276
+ // Compile-time constants are fine
3277
+ if (this.constValues.has(name)) {
3278
+ continue
3279
+ }
3280
+ // Only error if it's a known runtime variable (in varMap or function params)
3281
+ // Unknown identifiers are left alone (could be MC macro params the user intends)
3282
+ if (this.varMap.has(name) || this.currentFnParamNames.has(name)) {
3283
+ const loc = span ?? { line: 1, col: 1 }
3284
+ throw new DiagnosticError(
3285
+ 'LoweringError',
3286
+ `raw() command contains runtime variable interpolation '\${${name}}'. ` +
3287
+ `Variables cannot be interpolated into raw commands at compile time. ` +
3288
+ `Use f-string messages (say/tell/announce) or MC macro syntax '$(${name})' for MC 1.20.2+ commands.`,
3289
+ loc
3290
+ )
3291
+ }
3292
+ }
3293
+ }
3294
+
3272
3295
  private resolveInstanceMethod(expr: Extract<Expr, { kind: 'call' }>): { fn: FnDecl; loweredName: string } | null {
3273
3296
  const receiver = expr.args[0]
3274
3297
  if (!receiver) {