From 4c5f2a715ceb6941362d72c7cb75b1b6f078e4d5 Mon Sep 17 00:00:00 2001 From: tapple Date: Sat, 29 Aug 2026 12:12:23 +0200 Subject: [PATCH 1/2] Minor Ruleset Improvements --- generated/cpp/ruleset_builder_descriptors.cpp | 2 +- generated/lua_keywords_pretty.xml | 10 +- generated/secondlife.d.luau | 36 +- generated/syntax/slua.tmLanguage | 2 +- generated/syntax/slua.tmLanguage.json | 2 +- lsl_definitions.yaml | 6 +- lsl_definitions/generators/lscript.py | 191 ++++----- lsl_definitions/lsl.py | 9 +- lsl_definitions/slua.py | 370 +++++++++--------- slua_definitions.yaml | 14 +- 10 files changed, 328 insertions(+), 314 deletions(-) diff --git a/generated/cpp/ruleset_builder_descriptors.cpp b/generated/cpp/ruleset_builder_descriptors.cpp index f5d71f4a..60b5d47a 100644 --- a/generated/cpp/ruleset_builder_descriptors.cpp +++ b/generated/cpp/ruleset_builder_descriptors.cpp @@ -1,4 +1,4 @@ -// http-params +// http-request-params static const RulesetParamDescriptor kHTTPRequestParamsDescs[] = { {"method", 's', 0}, {"mimetype", 's', 1}, diff --git a/generated/lua_keywords_pretty.xml b/generated/lua_keywords_pretty.xml index 46cb4bfd..fc3680b1 100644 --- a/generated/lua_keywords_pretty.xml +++ b/generated/lua_keywords_pretty.xml @@ -223,15 +223,15 @@ export type DateTypeResult = { tooltip HTTP request parameter table. Pass to ll.HTTPRequest() to configure an HTTP request. - MediaParams + ParticleParams tooltip - Prim face media parameter table. Pass to llprim.setMedia() to configure media on a face. + Particle system parameter table. Pass to llprim.setParticleSystem() to emit particles. - ParticleParams + PrimMediaParams tooltip - Particle system parameter table. Pass to llprim.setParticleSystem() to emit particles. + Prim face media parameter table. Pass to llprim.setMedia() to configure media on a face. list @@ -14750,7 +14750,7 @@ Does an arithmetic shift: The most significant bit of n is propagated during the tooltip type - MediaParams? + PrimMediaParams? diff --git a/generated/secondlife.d.luau b/generated/secondlife.d.luau index bf3840f8..f771da9b 100644 --- a/generated/secondlife.d.luau +++ b/generated/secondlife.d.luau @@ -85,6 +85,18 @@ type LLJsonDecodeOptionsWithPath = { reviver: (key: string | number, value: any, parent: {}?, ctx: {path: {string | number}}) -> any } type LLJsonDecodeOptions = LLJsonDecodeOptionsWithoutPath | LLJsonDecodeOptionsWithPath +export type HttpRequestParams = { + method: string?, + mimetype: string?, + max_body_length: number?, + verify_cert: boolean?, + verbose_throttle: boolean?, + custom_header: {[string]: string | number | boolean | vector | quaternion}?, + pragma_no_cache: boolean?, + user_agent: string?, + accept: {string}?, + extended_error: boolean?, +} export type ParticleParams = { flags: number?, color_begin: vector?, @@ -124,7 +136,7 @@ export type ParticleParams = { emissive: boolean?, ribbon: boolean?, } -export type MediaParams = { +export type PrimMediaParams = { alt_image_enable: boolean?, controls: number?, current_url: string?, @@ -141,18 +153,6 @@ export type MediaParams = { perms_interact: number?, perms_control: number?, } -export type HttpRequestParams = { - method: string?, - mimetype: string?, - max_body_length: number?, - verify_cert: boolean?, - verbose_throttle: boolean?, - custom_header: {[string]: string | number | boolean | vector | quaternion}?, - pragma_no_cache: boolean?, - user_agent: string?, - accept: {string}?, - extended_error: boolean?, -} declare extern type DetectedEvent with read index: number @@ -505,7 +505,7 @@ declare lljson: { declare llprim: { ParamsSetter: PrimParamsSetterTypeMeta, setParticleSystem: (params: ParticleParams?, link: number?) -> (), - setMedia: (face: number, params: MediaParams?, link: number?) -> number, + setMedia: (face: number, params: PrimMediaParams?, link: number?) -> number, } @@ -1123,7 +1123,7 @@ declare ll: { SetLinkCamera: (link: number, eye: vector, at: vector) -> (), SetLinkColor: (link: number, color: vector, face: number) -> (), SetLinkGLTFOverrides: (link: number, face: number, params: list) -> (), - SetLinkMedia: (link: number, face: number, params: list | MediaParams) -> number, + SetLinkMedia: (link: number, face: number, params: list | PrimMediaParams) -> number, SetLinkPrimitiveParams: @[deprecated {use='ll.SetLinkPrimitiveParamsFast'}](link: number, rules: list) -> (), SetLinkPrimitiveParamsFast: (link: number, rules: list) -> (), SetLinkRenderMaterial: (link: number, material: string | uuid, face: number) -> (), @@ -1139,7 +1139,7 @@ declare ll: { SetPayPrice: (price: number, quick_pay_buttons: {number}) -> (), SetPhysicsMaterial: (mask: number, gravity_multiplier: number, restitution: number, friction: number, density: number) -> (), SetPos: (pos: vector) -> (), - SetPrimMediaParams: (face: number, params: list | MediaParams) -> number, + SetPrimMediaParams: (face: number, params: list | PrimMediaParams) -> number, SetPrimURL: @[deprecated {use='ll.SetPrimMediaParams'}](url: string) -> (), SetPrimitiveParams: @[deprecated {use='ll.SetLinkPrimitiveParamsFast'}](rules: list) -> (), SetRegionPos: (position: vector) -> boolean, @@ -1648,7 +1648,7 @@ declare llcompat: { SetLinkCamera: @deprecated (link: number, eye: vector, at: vector) -> (), SetLinkColor: @deprecated (link: number, color: vector, face: number) -> (), SetLinkGLTFOverrides: @deprecated (link: number, face: number, params: list) -> (), - SetLinkMedia: @deprecated (link: number, face: number, params: list | MediaParams) -> number, + SetLinkMedia: @deprecated (link: number, face: number, params: list | PrimMediaParams) -> number, SetLinkPrimitiveParams: @deprecated (link: number, rules: list) -> (), SetLinkPrimitiveParamsFast: @deprecated (link: number, rules: list) -> (), SetLinkRenderMaterial: @deprecated (link: number, material: string | uuid, face: number) -> (), @@ -1665,7 +1665,7 @@ declare llcompat: { SetPayPrice: @deprecated (price: number, quick_pay_buttons: {number}) -> (), SetPhysicsMaterial: @deprecated (mask: number, gravity_multiplier: number, restitution: number, friction: number, density: number) -> (), SetPos: @deprecated (pos: vector) -> (), - SetPrimMediaParams: @deprecated (face: number, params: list | MediaParams) -> number, + SetPrimMediaParams: @deprecated (face: number, params: list | PrimMediaParams) -> number, SetPrimURL: @deprecated (url: string) -> (), SetPrimitiveParams: @deprecated (rules: list) -> (), SetRegionPos: @deprecated (position: vector) -> number, diff --git a/generated/syntax/slua.tmLanguage b/generated/syntax/slua.tmLanguage index b05c6c08..dbee7ab1 100644 --- a/generated/syntax/slua.tmLanguage +++ b/generated/syntax/slua.tmLanguage @@ -1282,7 +1282,7 @@ match - \b(nil|any|boolean|buffer|integer|never|number|string|thread|unknown|quaternion|uuid|vector|DetectedEvent|rotation|list|DateTypeArg|DateTypeResult|ParticleParams|MediaParams|HttpRequestParams)\b + \b(nil|any|boolean|buffer|integer|never|number|string|thread|unknown|quaternion|uuid|vector|DetectedEvent|rotation|list|DateTypeArg|DateTypeResult|HttpRequestParams|ParticleParams|PrimMediaParams)\b name support.type.primitive.luau diff --git a/generated/syntax/slua.tmLanguage.json b/generated/syntax/slua.tmLanguage.json index 17abe891..5a729858 100644 --- a/generated/syntax/slua.tmLanguage.json +++ b/generated/syntax/slua.tmLanguage.json @@ -831,7 +831,7 @@ "name": "constant.language.boolean.true.luau" }, { - "match": "\\b(nil|any|boolean|buffer|integer|never|number|string|thread|unknown|quaternion|uuid|vector|DetectedEvent|rotation|list|DateTypeArg|DateTypeResult|ParticleParams|MediaParams|HttpRequestParams)\\b", + "match": "\\b(nil|any|boolean|buffer|integer|never|number|string|thread|unknown|quaternion|uuid|vector|DetectedEvent|rotation|list|DateTypeArg|DateTypeResult|HttpRequestParams|ParticleParams|PrimMediaParams)\\b", "name": "support.type.primitive.luau" }, { diff --git a/lsl_definitions.yaml b/lsl_definitions.yaml index 6423042f..ac243b34 100644 --- a/lsl_definitions.yaml +++ b/lsl_definitions.yaml @@ -12540,7 +12540,7 @@ functions: or an HTTPRequestParams dictionary. type: list param-semantics: HTTPRequestParam - ruleset: http-params + ruleset: http-request-params - body: tooltip: String contents of the request body. type: string @@ -18598,7 +18598,7 @@ builder-rulesets: - [ asset, sound ] - [ float, volume ] - http-params: + http-request-params: type: table enum: HTTPRequestParam filler-tokens: [http] @@ -18621,5 +18621,5 @@ builder-rulesets: enum: PrimMediaParam lua-module: llprim lua-fn: setMedia - lua-type: MediaParams + lua-type: PrimMediaParams dispatch-fn: SetLinkMedia diff --git a/lsl_definitions/generators/lscript.py b/lsl_definitions/generators/lscript.py index 67121ad8..3eb840dc 100644 --- a/lsl_definitions/generators/lscript.py +++ b/lsl_definitions/generators/lscript.py @@ -1162,111 +1162,112 @@ def _build_wrapper( function_body_lines = [] for ruleset_name, ruleset_data in definitions.builder_rulesets.items(): - if ruleset_data.get("type", "builder") != "table": - continue - - enum_name = ruleset_data["enum"] - dispatch_fn = ruleset_data.get("dispatch-fn") - - # Descriptor array/def names derived from the enum name. - # e.g. ParticleParam -> kParticleParamsDescs, kParticleParamsDef - array_name = f"k{enum_name}sDescs" - def_name = f"k{enum_name}sDef" + try: + if ruleset_data.get("type", "builder") != "table": + continue - desc_lines = [] - for desc in expand_table_ruleset(definitions, ruleset_name): - name = desc.pretty_name if desc.pretty_name else desc.strict_name - sem = _semantic_map[desc.value_type] - desc_lines.append(f" {{\"{name}\", '{sem}', {desc.tag}}},") + enum_name = ruleset_data["enum"] + dispatch_fn = ruleset_data.get("dispatch-fn") - param_body = "\n".join(desc_lines) - file_scope = ( - f"// {ruleset_name}\n" - f"static const RulesetParamDescriptor {array_name}[] = {{\n{param_body}\n}};\n" - ) - - flag_enum_name = ruleset_data.get("flag-enum") - flag_field = ruleset_data.get("flag-field") - if flag_enum_name and flag_field: - flag_enum = definitions.enums[flag_enum_name] - filler_tokens = set(ruleset_data.get("filler-tokens", [])) - prefix = flag_enum.prefix + # Descriptor array/def names derived from the enum name. + # e.g. ParticleParam -> kParticleParamsDescs, kParticleParamsDef + array_name = f"k{enum_name}sDescs" + def_name = f"k{enum_name}sDef" - # Find field_tag from the already-computed param descriptors. - field_tag = None + desc_lines = [] for desc in expand_table_ruleset(definitions, ruleset_name): - eff_name = desc.pretty_name if desc.pretty_name else desc.strict_name - if eff_name == flag_field: - field_tag = desc.tag - break - if field_tag is None: - raise ValueError( - f"{ruleset_name}: flag-field '{flag_field}' not found in descriptor list" - ) - - # Enumerate flag constants sorted by numeric value. - flag_consts = sorted( - ( - c - for c in definitions.constants.values() - if any(e.name == flag_enum_name for e in c.member_of) and not c.private - ), - key=lambda c: int(c.value, 0), + name = desc.pretty_name if desc.pretty_name else desc.strict_name + sem = _semantic_map[desc.value_type] + desc_lines.append(f" {{\"{name}\", '{sem}', {desc.tag}}},") + + param_body = "\n".join(desc_lines) + file_scope = ( + f"// {ruleset_name}\n" + f"static const RulesetParamDescriptor {array_name}[] = {{\n{param_body}\n}};\n" ) - flag_suffix = (ruleset_data.get("flag-mask") or "").lower() - flag_array_name = f"k{enum_name}FlagDescs" - flag_lines = [] - for const in flag_consts: - if const.pretty_name: - prop_name = const.pretty_name - else: - name = const.name - strict = (name[len(prefix) :] if name.startswith(prefix) else name).lower() - if flag_suffix and strict.endswith(flag_suffix): - strict = strict[: -len(flag_suffix)] - tokens = [t for t in strict.split("_") if t not in filler_tokens] - prop_name = "_".join(tokens) if tokens else strict - mask = int(const.value, 0) - flag_lines.append(f' {{"{prop_name}", 0x{mask:x}, {field_tag}}},') - - flag_body = "\n".join(flag_lines) - file_scope += ( - f"static const RulesetFlagDescriptor {flag_array_name}[] = {{\n{flag_body}\n}};\n" - ) + flag_enum_name = ruleset_data.get("flag-enum") + flag_field = ruleset_data.get("flag-field") + if flag_enum_name and flag_field: + flag_enum = definitions.enums[flag_enum_name] + filler_tokens = set(ruleset_data.get("filler-tokens", [])) + prefix = flag_enum.prefix + + # Find field_tag from the already-computed param descriptors. + field_tag = None + for desc in expand_table_ruleset(definitions, ruleset_name): + eff_name = desc.pretty_name if desc.pretty_name else desc.strict_name + if eff_name == flag_field: + field_tag = desc.tag + break + if field_tag is None: + raise ValueError( + f"{ruleset_name}: flag-field '{flag_field}' not found in descriptor list" + ) - # Build def with flags in a single static initializer (file scope, NOT static). - file_scope += ( - f"RulesetBuilderDef* {def_name} = []() {{\n" - f" auto* d = ruleset_builder_def_build({array_name}, std::size({array_name}));\n" - f" ruleset_builder_def_add_flags(d, {flag_array_name}, std::size({flag_array_name}));\n" - f" return d;\n" - f"}}();\n" - ) - else: - # Simple def pointer (file scope, NOT static). - file_scope += ( - f"RulesetBuilderDef* {def_name} = " - f"ruleset_builder_def_build({array_name}, std::size({array_name}));\n" - ) + # Enumerate flag constants sorted by numeric value. + flag_consts = sorted( + ( + c + for c in definitions.constants.values() + if any(e.name == flag_enum_name for e in c.member_of) and not c.private + ), + key=lambda c: int(c.value, 0), + ) - file_scope_sections.append(file_scope) + flag_suffix = (ruleset_data.get("flag-mask") or "").lower() + flag_array_name = f"k{enum_name}FlagDescs" + flag_lines = [] + for const in flag_consts: + if const.pretty_name: + prop_name = const.pretty_name + else: + name = const.name + strict = (name[len(prefix) :] if name.startswith(prefix) else name).lower() + if flag_suffix and strict.endswith(flag_suffix): + strict = strict[: -len(flag_suffix)] + tokens = [t for t in strict.split("_") if t not in filler_tokens] + prop_name = "_".join(tokens) if tokens else strict + mask = int(const.value, 0) + flag_lines.append(f' {{"{prop_name}", 0x{mask:x}, {field_tag}}},') + + flag_body = "\n".join(flag_lines) + file_scope += f"static const RulesetFlagDescriptor {flag_array_name}[] = {{\n{flag_body}\n}};\n" + + # Build def with flags in a single static initializer (file scope, NOT static). + file_scope += ( + f"RulesetBuilderDef* {def_name} = []() {{\n" + f" auto* d = ruleset_builder_def_build({array_name}, std::size({array_name}));\n" + f" ruleset_builder_def_add_flags(d, {flag_array_name}, std::size({flag_array_name}));\n" + f" return d;\n" + f"}}();\n" + ) + else: + # Simple def pointer (file scope, NOT static). + file_scope += ( + f"RulesetBuilderDef* {def_name} = " + f"ruleset_builder_def_build({array_name}, std::size({array_name}));\n" + ) - # Build lambda and registration (inside function body) - if dispatch_fn is not None: - lua_module = ruleset_data["lua-module"] - lua_fn = ruleset_data["lua-fn"] - prefix_args, suffix_args, has_link, ret_type = _inspect_dispatch_fn(dispatch_fn) - cpp_name = _lua_fn_to_cpp_name(lua_fn) + file_scope_sections.append(file_scope) - wrapper = _build_wrapper( - cpp_name, dispatch_fn, prefix_args, suffix_args, has_link, ret_type - ) - function_body_lines.append(f" {wrapper}") - function_body_lines.append( - f' slua_register_ruleset_fn(L, "{lua_module}", "{lua_fn}", {cpp_name}, {def_name});' - ) - function_body_lines.append("") + # Build lambda and registration (inside function body) + if dispatch_fn is not None: + lua_module = ruleset_data["lua-module"] + lua_fn = ruleset_data["lua-fn"] + prefix_args, suffix_args, has_link, ret_type = _inspect_dispatch_fn(dispatch_fn) + cpp_name = _lua_fn_to_cpp_name(lua_fn) + + wrapper = _build_wrapper( + cpp_name, dispatch_fn, prefix_args, suffix_args, has_link, ret_type + ) + function_body_lines.append(f" {wrapper}") + function_body_lines.append( + f' slua_register_ruleset_fn(L, "{lua_module}", "{lua_fn}", {cpp_name}, {def_name});' + ) + function_body_lines.append("") + except Exception as e: + raise ValueError(f"In ruleset {ruleset_name!r}: {e}") from e # Combine: file scope declarations, then the function result = "\n".join(file_scope_sections) diff --git a/lsl_definitions/lsl.py b/lsl_definitions/lsl.py index d549cebb..66df4953 100644 --- a/lsl_definitions/lsl.py +++ b/lsl_definitions/lsl.py @@ -293,11 +293,10 @@ def compute_slua_type(self, event: bool = False, builder_rulesets: dict | None = # If this argument has a ruleset annotation and we have rulesets data, # create a union type: | RulesetType if self.ruleset and builder_rulesets: - ruleset_data = builder_rulesets.get(self.ruleset) - if ruleset_data: - lua_type = ruleset_data.get("lua-type") - if lua_type: - return f"{base_type} | {lua_type}" + ruleset_data = builder_rulesets[self.ruleset] + lua_type = ruleset_data.get("lua-type") + if lua_type: + return f"{base_type} | {lua_type}" return base_type diff --git a/lsl_definitions/slua.py b/lsl_definitions/slua.py index 7ff65020..ac01f0f9 100644 --- a/lsl_definitions/slua.py +++ b/lsl_definitions/slua.py @@ -510,62 +510,65 @@ def replace_list(type: str) -> str: return type for event in lsl.events.values(): - if event.slua_removed: - continue - event_func = SLuaFunction( - name=event.name, - comment=event.tooltip, - private=event.private, - deprecated=event.deprecated or event.slua_deprecated, - parameters=[ - SLuaParameter( - name=a.name, - comment=a.tooltip, - type=self.validate_type(replace_list(a.compute_slua_type(event=True))), - ) - for a in event.arguments - ], - ) - if event.detected_semantics: - LLDetectedEventName_alias.selene_type.append(event.name) - type_def = "LLDetectedEventHandler?" - else: - LLNonDetectedEventName_alias.selene_type.append(event.name) - type_def = event_func.type_def_string - overload_parameters = [ - SLuaParameter("self", type="LLEvents"), - SLuaParameter("event", type=f'"{event.name}"'), - SLuaParameter("callback", type=type_def), - ] - if not solverV2: - for register_func in LLEvents_class.methods: - if register_func.name in {"on", "once"}: - register_func.overloads.append( - SLuaFunctionOverload( - name=register_func.name, - comment=event.tooltip, - parameters=overload_parameters, - return_type=type_def, + try: + if event.slua_removed: + continue + event_func = SLuaFunction( + name=event.name, + comment=event.tooltip, + private=event.private, + deprecated=event.deprecated or event.slua_deprecated, + parameters=[ + SLuaParameter( + name=a.name, + comment=a.tooltip, + type=self.validate_type(replace_list(a.compute_slua_type(event=True))), + ) + for a in event.arguments + ], + ) + if event.detected_semantics: + LLDetectedEventName_alias.selene_type.append(event.name) + type_def = "LLDetectedEventHandler?" + else: + LLNonDetectedEventName_alias.selene_type.append(event.name) + type_def = event_func.type_def_string + overload_parameters = [ + SLuaParameter("self", type="LLEvents"), + SLuaParameter("event", type=f'"{event.name}"'), + SLuaParameter("callback", type=type_def), + ] + if not solverV2: + for register_func in LLEvents_class.methods: + if register_func.name in {"on", "once"}: + register_func.overloads.append( + SLuaFunctionOverload( + name=register_func.name, + comment=event.tooltip, + parameters=overload_parameters, + return_type=type_def, + ) ) - ) - elif register_func.name == "off": - register_func.overloads.append( - SLuaFunctionOverload( - name=register_func.name, - comment=event.tooltip, - parameters=overload_parameters, - return_type=register_func.return_type, + elif register_func.name == "off": + register_func.overloads.append( + SLuaFunctionOverload( + name=register_func.name, + comment=event.tooltip, + parameters=overload_parameters, + return_type=register_func.return_type, + ) ) - ) - type_def = f"({type_def})?" - event_prop = SLuaProperty( - name=event.name, - comment=event.tooltip, - type=type_def, - modifiable="override-fields", - private=event.private, - ) - LLEvents_class.properties[event_prop.name] = event_prop + type_def = f"({type_def})?" + event_prop = SLuaProperty( + name=event.name, + comment=event.tooltip, + type=type_def, + modifiable="override-fields", + private=event.private, + ) + LLEvents_class.properties[event_prop.name] = event_prop + except Exception as e: + raise ValueError(f"In ll event {event.name!r}: {e}") from e LLDetectedEventName_alias.definition = " | ".join( f'"{name}"' for name in LLDetectedEventName_alias.selene_type @@ -606,73 +609,81 @@ def replace_list(type: str) -> str: DetectedEvent_class = self.base_classes["DetectedEvent"] for func in lsl.functions.values(): - semantic_prefix = ( - "(Index semantics) " if func.index_semantics or func.detected_semantics else "" - ) - known_types = self.validate_type_params(func.type_arguments) - ll_func = SLuaFunction( - name=func.compute_slua_name(with_module=False), - comment=func.compute_slua_tooltip(), - deprecated=func.deprecated or func.slua_deprecated, - private=func.private, - type_parameters=func.type_arguments, - parameters=[ - SLuaParameter( - name=a.name, - comment=a.tooltip, - type=self.validate_type( - a.compute_slua_type(builder_rulesets=lsl.builder_rulesets), - known_types, - ), - ) - for a in func.arguments - ], - return_type=self.validate_type(replace_list(func.compute_slua_type()), known_types), - must_use=func.must_use or func.pure, - ) - llcompat_func = SLuaFunction( - name=ll_func.name, - comment=semantic_prefix + func.compute_slua_tooltip(llcompat=True), - deprecated=Deprecated(), - private=ll_func.private, - type_parameters=ll_func.type_parameters, - parameters=ll_func.parameters, - return_type=self.validate_type( - replace_list(func.compute_slua_type(llcompat=True)), known_types - ), - must_use=ll_func.must_use, - ) - if not func.slua_removed: - ll_module.functions[ll_func.name] = ll_func - llcompat_module.functions[llcompat_func.name] = llcompat_func - if func.detected_semantics: - name = ll_func.name.replace("Detected", "Get") - name = name[0].lower() + name[1:] - ll_func.deprecated = Deprecated(use=name) - detected_func = SLuaFunction( - name=name, - comment=ll_func.comment, - deprecated=None, + try: + semantic_prefix = ( + "(Index semantics) " if func.index_semantics or func.detected_semantics else "" + ) + known_types = self.validate_type_params(func.type_arguments) + ll_func = SLuaFunction( + name=func.compute_slua_name(with_module=False), + comment=func.compute_slua_tooltip(), + deprecated=func.deprecated or func.slua_deprecated, + private=func.private, + type_parameters=func.type_arguments, + parameters=[ + SLuaParameter( + name=a.name, + comment=a.tooltip, + type=self.validate_type( + a.compute_slua_type(builder_rulesets=lsl.builder_rulesets), + known_types, + ), + ) + for a in func.arguments + ], + return_type=self.validate_type( + replace_list(func.compute_slua_type()), known_types + ), + must_use=func.must_use or func.pure, + ) + llcompat_func = SLuaFunction( + name=ll_func.name, + comment=semantic_prefix + func.compute_slua_tooltip(llcompat=True), + deprecated=Deprecated(), private=ll_func.private, type_parameters=ll_func.type_parameters, - parameters=ll_func.parameters[:], - return_type=ll_func.return_type, + parameters=ll_func.parameters, + return_type=self.validate_type( + replace_list(func.compute_slua_type(llcompat=True)), known_types + ), must_use=ll_func.must_use, ) - detected_func.parameters[0] = SLuaParameter(name="self") - DetectedEvent_class.methods[detected_func.name] = detected_func + if not func.slua_removed: + ll_module.functions[ll_func.name] = ll_func + llcompat_module.functions[llcompat_func.name] = llcompat_func + if func.detected_semantics: + name = ll_func.name.replace("Detected", "Get") + name = name[0].lower() + name[1:] + ll_func.deprecated = Deprecated(use=name) + detected_func = SLuaFunction( + name=name, + comment=ll_func.comment, + deprecated=None, + private=ll_func.private, + type_parameters=ll_func.type_parameters, + parameters=ll_func.parameters[:], + return_type=ll_func.return_type, + must_use=ll_func.must_use, + ) + detected_func.parameters[0] = SLuaParameter(name="self") + DetectedEvent_class.methods[detected_func.name] = detected_func + except Exception as e: + raise ValueError(f"In ll function {func.name!r}: {e}") from e for const in lsl.constants.values(): - if const.slua_removed: - continue - prop = SLuaProperty( - name=const.name, - comment=const.tooltip, - type=self.validate_type(const.slua_type or const.type.meta.slua_name), - value=const.slua_literal, - private=const.private, - ) - self.global_constants[prop.name] = prop + try: + if const.slua_removed: + continue + prop = SLuaProperty( + name=const.name, + comment=const.tooltip, + type=self.validate_type(const.slua_type or const.type.meta.slua_name), + value=const.slua_literal, + private=const.private, + ) + self.global_constants[prop.name] = prop + except Exception as e: + raise ValueError(f"In ll constant {const.name!r}: {e}") from e def _generate_spp_builder_class(self, lsl: LSLDefinitions) -> None: """Expand the `prim-params` ruleset into the fluent SPP builder class and attach it to self.""" @@ -709,76 +720,79 @@ def make_fluent_method(builder_spec: BuilderSpec, method: BuilderMethod) -> SLua builder_class.methods.update(methods) def _generate_ruleset_builder_classes(self, lsl: LSLDefinitions) -> None: - """Inject typed properties into fluent builder classes or type aliases. + """Inject typed properties into table-type functions. - For each table-type builder ruleset that names a lua-type, finds the matching + For each table-type function ruleset that names a lua-type, finds the matching SLuaClassDeclaration in self.classes (and populates its properties list) OR a SLuaTypeAlias in self.type_aliases (and rebuilds its definition string) from expand_table_ruleset() plus any flag enum members. """ for ruleset_name, ruleset_data in lsl.builder_rulesets.items(): - if ruleset_data.get("type") != "table": - continue - lua_type = ruleset_data.get("lua-type") - if not lua_type: - continue - - cls = self.classes.get(lua_type, None) - alias = self.type_aliases.get(lua_type, None) - if cls is None and alias is None: - continue - - # Collect (prop_name, luau_type) pairs - props: list[tuple[str, str]] = [] - - # Regular properties from expand_table_ruleset - for desc in expand_table_ruleset(lsl, ruleset_name): - prop_name = desc.pretty_name if desc.pretty_name else desc.strict_name - luau_base = _TABLE_RULESET_TYPE_MAP.get(desc.value_type, "any") - props.append((prop_name, f"{luau_base}?")) - - # Flag enum boolean properties - flag_enum_name = ruleset_data.get("flag-enum") - if flag_enum_name: - flag_enum = lsl.enums[flag_enum_name] - prefix = flag_enum.prefix - flag_suffix = (ruleset_data.get("flag-mask") or "").lower() - filler_tokens = set(ruleset_data.get("filler-tokens", [])) - flag_consts = sorted( - ( - c - for c in lsl.constants.values() - if any(e.name == flag_enum_name for e in c.member_of) and not c.private - ), - key=lambda c: int(c.value, 0), - ) - for const in flag_consts: - if const.pretty_name: - prop_name = const.pretty_name - else: - strict = ( - const.name[len(prefix) :] - if const.name.startswith(prefix) - else const.name - ).lower() - if flag_suffix and strict.endswith(flag_suffix): - strict = strict[: -len(flag_suffix)] - tokens = [t for t in strict.split("_") if t not in filler_tokens] - prop_name = "_".join(tokens) if tokens else strict - props.append((prop_name, "boolean?")) - - if cls is not None: - for prop_name, prop_type in props: - cls.properties.append( - SLuaProperty(name=prop_name, type=prop_type, modifiable="full-write") + try: + if ruleset_data.get("type") != "table": + continue + lua_type = ruleset_data.get("lua-type") + if not lua_type: + continue + + cls = self.classes.get(lua_type, None) + alias = self.type_aliases.get(lua_type, None) + if cls is None and alias is None: + raise KeyError(f"No such class or alias {lua_type!r}") + + # Collect (prop_name, luau_type) pairs + props: list[tuple[str, str]] = [] + + # Regular properties from expand_table_ruleset + for desc in expand_table_ruleset(lsl, ruleset_name): + prop_name = desc.pretty_name if desc.pretty_name else desc.strict_name + luau_base = _TABLE_RULESET_TYPE_MAP.get(desc.value_type, "any") + props.append((prop_name, f"{luau_base}?")) + + # Flag enum boolean properties + flag_enum_name = ruleset_data.get("flag-enum") + if flag_enum_name: + flag_enum = lsl.enums[flag_enum_name] + prefix = flag_enum.prefix + flag_suffix = (ruleset_data.get("flag-mask") or "").lower() + filler_tokens = set(ruleset_data.get("filler-tokens", [])) + flag_consts = sorted( + ( + c + for c in lsl.constants.values() + if any(e.name == flag_enum_name for e in c.member_of) and not c.private + ), + key=lambda c: int(c.value, 0), ) - else: - # Rebuild the type alias definition as a typed table literal. - lines = ["{"] - for prop_name, prop_type in props: - lines.append(f" {prop_name}: {prop_type},") - lines.append("}") - alias.definition = "\n".join(lines) + for const in flag_consts: + if const.pretty_name: + prop_name = const.pretty_name + else: + strict = ( + const.name[len(prefix) :] + if const.name.startswith(prefix) + else const.name + ).lower() + if flag_suffix and strict.endswith(flag_suffix): + strict = strict[: -len(flag_suffix)] + tokens = [t for t in strict.split("_") if t not in filler_tokens] + prop_name = "_".join(tokens) if tokens else strict + props.append((prop_name, "boolean?")) + + if cls is not None: + for prop_name, prop_type in props: + cls.properties.append( + SLuaProperty(name=prop_name, type=prop_type, modifiable="full-write") + ) + else: + # Rebuild the type alias definition as a typed table literal. + lines = ["{"] + for prop_name, prop_type in props: + lines.append(f" {prop_name}: {prop_type},") + lines.append("}") + alias.definition = "\n".join(lines) + except Exception as e: + raise ValueError(f"In ruleset {ruleset_name!r}: {e}") from e class SLuaDefinitionParser: diff --git a/slua_definitions.yaml b/slua_definitions.yaml index 2d3193b3..65b39fe9 100644 --- a/slua_definitions.yaml +++ b/slua_definitions.yaml @@ -247,21 +247,21 @@ type-aliases: comment: Configuration options for lljson decoding definition: "LLJsonDecodeOptionsWithoutPath | LLJsonDecodeOptionsWithPath" selene-type: any +- name: HttpRequestParams + comment: HTTP request parameter table. Pass to ll.HTTPRequest() to configure an HTTP request. + export: true + definition: '{[string]: any}' # rebuilt at generation time from expand_table_ruleset("http-params") + selene-type: table - name: ParticleParams comment: Particle system parameter table. Pass to llprim.setParticleSystem() to emit particles. export: true definition: '{[string]: any}' # rebuilt at generation time from expand_table_ruleset("particle-params") selene-type: table -- name: MediaParams +- name: PrimMediaParams comment: Prim face media parameter table. Pass to llprim.setMedia() to configure media on a face. export: true definition: '{[string]: any}' # rebuilt at generation time from expand_table_ruleset("prim-media-params") selene-type: table -- name: HttpRequestParams - comment: HTTP request parameter table. Pass to ll.HTTPRequest() to configure an HTTP request. - export: true - definition: '{[string]: any}' # rebuilt at generation time from expand_table_ruleset("http-params") - selene-type: table classes: - name: LLEvents comment: Event registration and management class for Second Life events. Supports adding multiple handlers per event and dynamic registration. @@ -2146,7 +2146,7 @@ modules: - name: face type: number - name: params - type: MediaParams? + type: PrimMediaParams? - name: link type: number? return-type: number From 6f09c1cef0708bc154ac4df89173a259835982f3 Mon Sep 17 00:00:00 2001 From: tapple Date: Sat, 29 Aug 2026 13:25:10 +0200 Subject: [PATCH 2/2] sort type fields alphabetically --- generated/secondlife.d.luau | 78 ++++++++++++++++++------------------- lsl_definitions/slua.py | 5 ++- 2 files changed, 42 insertions(+), 41 deletions(-) diff --git a/generated/secondlife.d.luau b/generated/secondlife.d.luau index f771da9b..1720e9d7 100644 --- a/generated/secondlife.d.luau +++ b/generated/secondlife.d.luau @@ -86,72 +86,72 @@ type LLJsonDecodeOptionsWithPath = { } type LLJsonDecodeOptions = LLJsonDecodeOptionsWithoutPath | LLJsonDecodeOptionsWithPath export type HttpRequestParams = { + accept: {string}?, + custom_header: {[string]: string | number | boolean | vector | quaternion}?, + extended_error: boolean?, + max_body_length: number?, method: string?, mimetype: string?, - max_body_length: number?, - verify_cert: boolean?, - verbose_throttle: boolean?, - custom_header: {[string]: string | number | boolean | vector | quaternion}?, pragma_no_cache: boolean?, user_agent: string?, - accept: {string}?, - extended_error: boolean?, + verbose_throttle: boolean?, + verify_cert: boolean?, } export type ParticleParams = { - flags: number?, - color_begin: vector?, + accel: vector?, alpha_begin: number?, - color_end: vector?, alpha_end: number?, - scale_begin: vector?, - scale_end: vector?, - part_max_age: number?, - accel: vector?, - pattern: number?, + angle_begin: number?, + angle_end: number?, angle_inner: number?, angle_outer: number?, - texture: (string | uuid)?, - burst_rate: number?, + blend_func_dest: number?, + blend_func_source: number?, + bounce: boolean?, burst_count: number?, burst_radius: number?, - burst_speed_min: number?, + burst_rate: number?, burst_speed_max: number?, - src_max_age: number?, - target_key: (string | uuid)?, - omega: vector?, - angle_begin: number?, - angle_end: number?, - blend_func_source: number?, - blend_func_dest: number?, - glow_begin: number?, - glow_end: number?, + burst_speed_min: number?, + color_begin: vector?, + color_end: vector?, color_interp: boolean?, - scale_interp: boolean?, - bounce: boolean?, - wind: boolean?, + emissive: boolean?, + flags: number?, follow: boolean?, follow_velocity: boolean?, - target_pos: boolean?, - target_linear: boolean?, - emissive: boolean?, + glow_begin: number?, + glow_end: number?, + omega: vector?, + part_max_age: number?, + pattern: number?, ribbon: boolean?, + scale_begin: vector?, + scale_end: vector?, + scale_interp: boolean?, + src_max_age: number?, + target_key: (string | uuid)?, + target_linear: boolean?, + target_pos: boolean?, + texture: (string | uuid)?, + wind: boolean?, } export type PrimMediaParams = { alt_image_enable: boolean?, - controls: number?, - current_url: string?, - home_url: string?, auto_loop: boolean?, auto_play: boolean?, auto_scale: boolean?, auto_zoom: boolean?, + controls: number?, + current_url: string?, first_click_interact: boolean?, - width: number?, height: number?, - whitelist_enable: boolean?, - whitelist: {string}?, - perms_interact: number?, + home_url: string?, perms_control: number?, + perms_interact: number?, + whitelist: {string}?, + whitelist_enable: boolean?, + width: number?, } declare extern type DetectedEvent with diff --git a/lsl_definitions/slua.py b/lsl_definitions/slua.py index ac01f0f9..496f1552 100644 --- a/lsl_definitions/slua.py +++ b/lsl_definitions/slua.py @@ -471,7 +471,7 @@ def finalize(self, lsl: LSLDefinitions) -> None: """ self.generate_ll_modules(lsl) self._generate_spp_builder_class(lsl) - self._generate_ruleset_builder_classes(lsl) + self._generate_ruleset_type_structs(lsl) def generate_ll_modules(self, lsl: LSLDefinitions, solverV2: bool = True) -> None: """ @@ -719,7 +719,7 @@ def make_fluent_method(builder_spec: BuilderSpec, method: BuilderMethod) -> SLua builder_class = self.classes[spec.class_name] builder_class.methods.update(methods) - def _generate_ruleset_builder_classes(self, lsl: LSLDefinitions) -> None: + def _generate_ruleset_type_structs(self, lsl: LSLDefinitions) -> None: """Inject typed properties into table-type functions. For each table-type function ruleset that names a lua-type, finds the matching @@ -779,6 +779,7 @@ def _generate_ruleset_builder_classes(self, lsl: LSLDefinitions) -> None: prop_name = "_".join(tokens) if tokens else strict props.append((prop_name, "boolean?")) + props.sort() if cls is not None: for prop_name, prop_type in props: cls.properties.append(