redscript-mc 1.2.15 → 1.2.17
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/CHANGELOG.md +39 -0
- package/README.md +13 -6
- package/builtins.d.mcrs +494 -0
- package/dist/builtins/metadata.d.ts +36 -0
- package/dist/builtins/metadata.js +1014 -0
- package/dist/cli.js +16 -6
- package/dist/lexer/index.js +9 -0
- package/dist/lowering/index.d.ts +12 -0
- package/dist/lowering/index.js +56 -0
- package/dist/parser/index.js +10 -3
- package/editors/vscode/builtins.d.mcrs +494 -0
- package/editors/vscode/out/extension.js +797 -80
- package/editors/vscode/package-lock.json +2 -2
- package/editors/vscode/package.json +1 -1
- package/editors/vscode/src/symbols.ts +68 -0
- package/editors/vscode/syntaxes/redscript.tmLanguage.json +1 -1
- package/package.json +1 -1
- package/src/builtins/metadata.ts +1118 -0
- package/src/cli.ts +17 -6
- package/src/lexer/index.ts +9 -0
- package/src/lowering/index.ts +59 -0
- package/src/parser/index.ts +12 -3
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
|
|
38
|
-
watch
|
|
39
|
-
check
|
|
40
|
-
fmt
|
|
41
|
-
|
|
42
|
-
|
|
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
|
package/src/lexer/index.ts
CHANGED
|
@@ -298,6 +298,15 @@ export class Lexer {
|
|
|
298
298
|
value += this.advance()
|
|
299
299
|
}
|
|
300
300
|
}
|
|
301
|
+
// Check for ident (e.g. ~height → macro variable offset)
|
|
302
|
+
if (/[a-zA-Z_]/.test(this.peek())) {
|
|
303
|
+
let ident = ''
|
|
304
|
+
while (/[a-zA-Z0-9_]/.test(this.peek())) {
|
|
305
|
+
ident += this.advance()
|
|
306
|
+
}
|
|
307
|
+
// Store as rel_coord with embedded ident: ~height
|
|
308
|
+
value += ident
|
|
309
|
+
}
|
|
301
310
|
this.addToken('rel_coord', value, startLine, startCol)
|
|
302
311
|
return
|
|
303
312
|
}
|
package/src/lowering/index.ts
CHANGED
|
@@ -371,6 +371,13 @@ export class Lowering {
|
|
|
371
371
|
return expr.name
|
|
372
372
|
}
|
|
373
373
|
|
|
374
|
+
private tryGetMacroParamByName(name: string): string | null {
|
|
375
|
+
if (!this.currentFnParamNames.has(name)) return null
|
|
376
|
+
if (this.constValues.has(name)) return null
|
|
377
|
+
if (this.stringValues.has(name)) return null
|
|
378
|
+
return name
|
|
379
|
+
}
|
|
380
|
+
|
|
374
381
|
/**
|
|
375
382
|
* Converts an expression to a string for use as a builtin arg.
|
|
376
383
|
* If the expression is a macro param, returns `$(name)` and sets macroParam.
|
|
@@ -380,6 +387,19 @@ export class Lowering {
|
|
|
380
387
|
if (macroParam) {
|
|
381
388
|
return { str: `$(${macroParam})`, macroParam }
|
|
382
389
|
}
|
|
390
|
+
// Handle ~ident (e.g. ~height) - relative coord with variable offset
|
|
391
|
+
if (expr.kind === 'rel_coord' || expr.kind === 'local_coord') {
|
|
392
|
+
const val = expr.value // e.g. "~height" or "^depth"
|
|
393
|
+
const prefix = val[0] // ~ or ^
|
|
394
|
+
const rest = val.slice(1)
|
|
395
|
+
// If rest is an identifier (not a number), treat as macro param
|
|
396
|
+
if (rest && /^[a-zA-Z_][a-zA-Z0-9_]*$/.test(rest)) {
|
|
397
|
+
const paramName = this.tryGetMacroParamByName(rest)
|
|
398
|
+
if (paramName) {
|
|
399
|
+
return { str: `${prefix}$(${paramName})`, macroParam: paramName }
|
|
400
|
+
}
|
|
401
|
+
}
|
|
402
|
+
}
|
|
383
403
|
if (expr.kind === 'struct_lit' || expr.kind === 'array_lit') {
|
|
384
404
|
return { str: this.exprToSnbt(expr) }
|
|
385
405
|
}
|
|
@@ -785,6 +805,7 @@ export class Lowering {
|
|
|
785
805
|
this.lowerExecuteStmt(stmt)
|
|
786
806
|
break
|
|
787
807
|
case 'raw':
|
|
808
|
+
this.checkRawCommandInterpolation(stmt.cmd, stmt.span)
|
|
788
809
|
this.builder.emitRaw(stmt.cmd)
|
|
789
810
|
break
|
|
790
811
|
}
|
|
@@ -3253,6 +3274,44 @@ export class Lowering {
|
|
|
3253
3274
|
return undefined
|
|
3254
3275
|
}
|
|
3255
3276
|
|
|
3277
|
+
/**
|
|
3278
|
+
* Checks a raw() command string for `${...}` interpolation containing runtime variables.
|
|
3279
|
+
* - If the interpolated expression is a numeric literal → OK (MC macro syntax).
|
|
3280
|
+
* - If the interpolated name is a compile-time constant (in constValues) → OK.
|
|
3281
|
+
* - If the interpolated name is a known runtime variable (in varMap) → DiagnosticError.
|
|
3282
|
+
* - Unknown names → OK (could be MC macro params or external constants).
|
|
3283
|
+
*
|
|
3284
|
+
* This catches the common mistake of writing raw("say ${score}") expecting interpolation,
|
|
3285
|
+
* which would silently emit a literal `${score}` in the MC command.
|
|
3286
|
+
*/
|
|
3287
|
+
private checkRawCommandInterpolation(cmd: string, span?: Span): void {
|
|
3288
|
+
const interpRe = /\$\{([^}]+)\}/g
|
|
3289
|
+
let match: RegExpExecArray | null
|
|
3290
|
+
while ((match = interpRe.exec(cmd)) !== null) {
|
|
3291
|
+
const name = match[1].trim()
|
|
3292
|
+
// Numeric/boolean literals are fine (intentional MC macro syntax)
|
|
3293
|
+
if (/^\d+(\.\d+)?$/.test(name) || name === 'true' || name === 'false') {
|
|
3294
|
+
continue
|
|
3295
|
+
}
|
|
3296
|
+
// Compile-time constants are fine
|
|
3297
|
+
if (this.constValues.has(name)) {
|
|
3298
|
+
continue
|
|
3299
|
+
}
|
|
3300
|
+
// Only error if it's a known runtime variable (in varMap or function params)
|
|
3301
|
+
// Unknown identifiers are left alone (could be MC macro params the user intends)
|
|
3302
|
+
if (this.varMap.has(name) || this.currentFnParamNames.has(name)) {
|
|
3303
|
+
const loc = span ?? { line: 1, col: 1 }
|
|
3304
|
+
throw new DiagnosticError(
|
|
3305
|
+
'LoweringError',
|
|
3306
|
+
`raw() command contains runtime variable interpolation '\${${name}}'. ` +
|
|
3307
|
+
`Variables cannot be interpolated into raw commands at compile time. ` +
|
|
3308
|
+
`Use f-string messages (say/tell/announce) or MC macro syntax '$(${name})' for MC 1.20.2+ commands.`,
|
|
3309
|
+
loc
|
|
3310
|
+
)
|
|
3311
|
+
}
|
|
3312
|
+
}
|
|
3313
|
+
}
|
|
3314
|
+
|
|
3256
3315
|
private resolveInstanceMethod(expr: Extract<Expr, { kind: 'call' }>): { fn: FnDecl; loweredName: string } | null {
|
|
3257
3316
|
const receiver = expr.args[0]
|
|
3258
3317
|
if (!receiver) {
|
package/src/parser/index.ts
CHANGED
|
@@ -260,12 +260,21 @@ export class Parser {
|
|
|
260
260
|
private parseConstDecl(): ConstDecl {
|
|
261
261
|
const constToken = this.expect('const')
|
|
262
262
|
const name = this.expect('ident').value
|
|
263
|
-
|
|
264
|
-
|
|
263
|
+
let type: TypeNode | undefined
|
|
264
|
+
if (this.match(':')) {
|
|
265
|
+
type = this.parseType()
|
|
266
|
+
}
|
|
265
267
|
this.expect('=')
|
|
266
268
|
const value = this.parseLiteralExpr()
|
|
267
269
|
this.match(';')
|
|
268
|
-
|
|
270
|
+
// Infer type from value if not provided
|
|
271
|
+
const inferredType: TypeNode = type ?? (
|
|
272
|
+
value.kind === 'str_lit' ? { kind: 'named', name: 'string' } :
|
|
273
|
+
value.kind === 'bool_lit' ? { kind: 'named', name: 'bool' } :
|
|
274
|
+
value.kind === 'float_lit' ? { kind: 'named', name: 'float' } :
|
|
275
|
+
{ kind: 'named', name: 'int' }
|
|
276
|
+
)
|
|
277
|
+
return this.withLoc({ name, type: inferredType, value }, constToken)
|
|
269
278
|
}
|
|
270
279
|
|
|
271
280
|
private parseGlobalDecl(mutable: boolean): GlobalDecl {
|