redscript-mc 1.2.16 → 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/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "redscript-mc",
3
- "version": "1.2.16",
3
+ "version": "1.2.17",
4
4
  "description": "A high-level programming language that compiles to Minecraft datapacks",
5
5
  "main": "dist/index.js",
6
6
  "bin": {
@@ -1029,16 +1029,13 @@ export const BUILTIN_METADATA: Record<string, BuiltinDef> = {
1029
1029
  export function builtinToDeclaration(def: BuiltinDef): string {
1030
1030
  const lines: string[] = []
1031
1031
 
1032
- // Doc comments
1032
+ // Doc comments (English only)
1033
1033
  lines.push(`/// ${def.doc}`)
1034
- if (def.docZh) {
1035
- lines.push(`/// ${def.docZh}`)
1036
- }
1037
1034
 
1038
1035
  // Param docs
1039
1036
  for (const p of def.params) {
1040
- const opt = p.required ? '' : '?'
1041
- lines.push(`/// @param ${p.name}${opt} ${p.doc}`)
1037
+ const optTag = p.required ? '' : ' (optional)'
1038
+ lines.push(`/// @param ${p.name} ${p.doc}${optTag}`)
1042
1039
  }
1043
1040
 
1044
1041
  // Returns
@@ -1051,13 +1048,9 @@ export function builtinToDeclaration(def: BuiltinDef): string {
1051
1048
  lines.push(`/// @example ${ex.split('\n')[0]}`)
1052
1049
  }
1053
1050
 
1054
- // Signature
1051
+ // Signature - use default value syntax instead of ? for optional params
1055
1052
  const paramStrs = def.params.map(p => {
1056
- const opt = p.required ? '' : '?'
1057
1053
  let type = p.type
1058
- // Map to .d.mcrs types
1059
- if (type === 'selector') type = 'selector'
1060
- if (type === 'BlockPos') type = 'BlockPos'
1061
1054
  if (type === 'effect') type = 'string'
1062
1055
  if (type === 'sound') type = 'string'
1063
1056
  if (type === 'block') type = 'string'
@@ -1065,8 +1058,10 @@ export function builtinToDeclaration(def: BuiltinDef): string {
1065
1058
  if (type === 'entity') type = 'string'
1066
1059
  if (type === 'dimension') type = 'string'
1067
1060
  if (type === 'nbt') type = 'string'
1068
- if (type === 'coord') type = 'coord'
1069
- return `${p.name}${opt}: ${type}`
1061
+ if (!p.required && p.default !== undefined) {
1062
+ return `${p.name}: ${type} = ${p.default}`
1063
+ }
1064
+ return `${p.name}: ${type}`
1070
1065
  })
1071
1066
 
1072
1067
  const retType = def.returns
@@ -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
  }
@@ -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
  }
@@ -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
- this.expect(':')
264
- const type = this.parseType()
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
- return this.withLoc({ name, type, value }, constToken)
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 {