@@ -647,7 +647,19 @@ pub const Lua = struct {
647
647
/// Returns the acceptable index index converted into an equivalent absolute index
648
648
/// See https://www.lua.org/manual/5.4/manual.html#lua_absindex
649
649
pub fn absIndex (lua : * Lua , index : i32 ) i32 {
650
- return c .lua_absindex (lua .state , index );
650
+ switch (lang ) {
651
+ .lua51 , .luajit = > {
652
+ if (index > 0 or index <= registry_index ) {
653
+ return index ;
654
+ } else {
655
+ const result = lua .getTop () + 1 + index ;
656
+ return @intCast (result );
657
+ }
658
+ },
659
+ else = > {
660
+ return c .lua_absindex (lua .state , index );
661
+ },
662
+ }
651
663
}
652
664
653
665
/// Performs an arithmetic or bitwise operation over the value(s) at the top of the stack,
@@ -3013,6 +3025,252 @@ pub const Lua = struct {
3013
3025
pub fn openBit32 (lua : * Lua ) void {
3014
3026
_ = c .luaopen_bit32 (lua .state );
3015
3027
}
3028
+
3029
+ /// Pushes any valid zig value onto the stack,
3030
+ /// Works with ints, floats, booleans, structs,
3031
+ /// optionals, and strings
3032
+ pub fn pushAny (lua : * Lua , value : anytype ) ! void {
3033
+ switch (@typeInfo (@TypeOf (value ))) {
3034
+ .Int , .ComptimeInt = > {
3035
+ lua .pushInteger (@intCast (value ));
3036
+ },
3037
+ .Float , .ComptimeFloat = > {
3038
+ lua .pushNumber (@floatCast (value ));
3039
+ },
3040
+ .Pointer = > | info | {
3041
+ switch (info .size ) {
3042
+ .One = > {
3043
+ if (@typeInfo (info .child ) == .Array ) {
3044
+ if (@typeInfo (info .child ).Array .child != u8 ) {
3045
+ @compileError ("only u8 arrays can be pushed" );
3046
+ }
3047
+ _ = lua .pushString (&(value .* ));
3048
+ } else {
3049
+ if (info .is_const ) {
3050
+ @compileLog (value );
3051
+ @compileError ("Pointer must not be const" );
3052
+ }
3053
+ lua .pushLightUserdata (@ptrCast (value ));
3054
+ }
3055
+ },
3056
+ .C , .Many , .Slice = > {
3057
+ if (info .child != u8 ) {
3058
+ @compileError ("Only u8 slices (strings) are valid slice types" );
3059
+ }
3060
+ if (info .sentinel ) | sentinel | {
3061
+ const casted : * info.child = @ptrCast (@constCast (sentinel ));
3062
+ if (casted .* != 0 ) {
3063
+ @compileError ("Sentinel of slice must be a null terminator" );
3064
+ }
3065
+ _ = lua .pushString (value );
3066
+ } else {
3067
+ const null_terminated = try lua .allocator ().dupeZ (u8 , value );
3068
+ defer lua .allocator ().free (null_terminated );
3069
+ _ = lua .pushString (null_terminated );
3070
+ }
3071
+ },
3072
+ }
3073
+ },
3074
+ .Bool = > {
3075
+ lua .pushBoolean (value );
3076
+ },
3077
+ .Optional , .Null = > {
3078
+ if (value == null ) {
3079
+ lua .pushNil ();
3080
+ } else {
3081
+ try lua .pushAny (value .? );
3082
+ }
3083
+ },
3084
+ .Struct = > | info | {
3085
+ lua .createTable (0 , 0 );
3086
+ inline for (info .fields ) | field | {
3087
+ try lua .pushAny (field .name );
3088
+ try lua .pushAny (@field (value , field .name ));
3089
+ lua .setTable (-3 );
3090
+ }
3091
+ },
3092
+ .Fn = > {
3093
+ lua .autoPushFunction (value );
3094
+ },
3095
+ .Void = > {},
3096
+ else = > {
3097
+ @compileLog (value );
3098
+ @compileError ("Invalid type given" );
3099
+ },
3100
+ }
3101
+ }
3102
+
3103
+ /// Converts the specified index of the lua stack to the specified
3104
+ /// type if possible and returns it
3105
+ pub fn toAny (lua : * Lua , comptime T : type , index : i32 ) ! T {
3106
+
3107
+ //TODO implement enums
3108
+ switch (@typeInfo (T )) {
3109
+ .Int = > {
3110
+ switch (comptime lang ) {
3111
+ .lua51 , .luajit = > {
3112
+ const result = lua .toInteger (index );
3113
+ return @as (T , @intCast (result ));
3114
+ },
3115
+ else = > {
3116
+ const result = try lua .toInteger (index );
3117
+ return @as (T , @intCast (result ));
3118
+ },
3119
+ }
3120
+ },
3121
+ .Float = > {
3122
+ switch (comptime lang ) {
3123
+ .lua51 , .luajit = > {
3124
+ const result = lua .toNumber (index );
3125
+ return @as (T , @floatCast (result ));
3126
+ },
3127
+ else = > {
3128
+ const result = try lua .toNumber (index );
3129
+ return @as (T , @floatCast (result ));
3130
+ },
3131
+ }
3132
+ },
3133
+ .Pointer = > | param_info | {
3134
+ switch (param_info .size ) {
3135
+ .Slice , .Many = > {
3136
+ if (param_info .child != u8 ) {
3137
+ @compileError ("Only u8 arrays (strings) may be parameters" );
3138
+ }
3139
+ if (! param_info .is_const ) {
3140
+ @compileError ("Slice must be a const slice" );
3141
+ }
3142
+ const string : [* :0 ]const u8 = try lua .toString (index );
3143
+ const end = std .mem .indexOfSentinel (u8 , 0 , string );
3144
+
3145
+ if (param_info .sentinel == null ) {
3146
+ return string [0.. end ];
3147
+ } else {
3148
+ return string [0.. end :0 ];
3149
+ }
3150
+ },
3151
+ else = > {
3152
+ return try lua .toUserdata (param_info .child , index );
3153
+ },
3154
+ }
3155
+ },
3156
+ .Bool = > {
3157
+ return lua .toBoolean (index );
3158
+ },
3159
+ .Struct = > {
3160
+ return try lua .toStruct (T , index );
3161
+ },
3162
+ .Optional = > {
3163
+ if (lua .isNil (index )) {
3164
+ lua .pop (1 );
3165
+ return null ;
3166
+ } else {
3167
+ return try lua .toAny (@typeInfo (T ).Optional .child , index );
3168
+ }
3169
+ },
3170
+ else = > {
3171
+ @compileError ("Invalid parameter type" );
3172
+ },
3173
+ }
3174
+ }
3175
+
3176
+ /// Converts value at given index to a zig struct if possible
3177
+ fn toStruct (lua : * Lua , comptime T : type , raw_index : i32 ) ! T {
3178
+ const index = lua .absIndex (raw_index );
3179
+
3180
+ if (! lua .isTable (index )) {
3181
+ return error .ValueNotATable ;
3182
+ }
3183
+ std .debug .assert (lua .typeOf (index ) == .table );
3184
+
3185
+ var result : T = undefined ;
3186
+ inline for (@typeInfo (T ).Struct .fields ) | field | {
3187
+ const field_name = comptime field .name ++ "" ;
3188
+ _ = lua .pushString (field_name );
3189
+ std .debug .assert (lua .typeOf (index ) == .table );
3190
+ const lua_field_type = lua .getTable (index );
3191
+ if (lua_field_type == .nil ) {
3192
+ if (field .default_value ) | default_value | {
3193
+ @field (result , field .name ) = @as (* const field .type , @ptrCast (@alignCast (default_value ))).* ;
3194
+ } else {
3195
+ return error .LuaTableMissingValue ;
3196
+ }
3197
+ } else {
3198
+ @field (result , field .name ) = try lua .toAny (field .type , -1 );
3199
+ }
3200
+ }
3201
+
3202
+ return result ;
3203
+ }
3204
+
3205
+ ///automatically calls a lua function with the given arguments
3206
+ pub fn autoCall (lua : * Lua , comptime ReturnType : type , func_name : [:0 ]const u8 , args : anytype ) ! ReturnType {
3207
+ if (try lua .getGlobal (func_name ) != LuaType .function ) return error .InvalidFunctionName ;
3208
+
3209
+ inline for (args ) | arg | {
3210
+ try lua .pushAny (arg );
3211
+ }
3212
+
3213
+ const num_results = if (ReturnType == void ) 0 else 1 ;
3214
+ try lua .protectedCall (args .len , num_results , 0 );
3215
+ defer lua .setTop (0 );
3216
+
3217
+ return lua .toAny (ReturnType , -1 );
3218
+ }
3219
+
3220
+ //automatically generates a wrapper function
3221
+ fn GenerateInterface (comptime function : anytype ) type {
3222
+ const info = @typeInfo (@TypeOf (function ));
3223
+ if (info != .Fn ) {
3224
+ @compileLog (info );
3225
+ @compileLog (function );
3226
+ @compileError ("function pointer must be passed" );
3227
+ }
3228
+ return struct {
3229
+ pub fn interface (lua : * Lua ) i32 {
3230
+ var parameters : std .meta .ArgsTuple (@TypeOf (function )) = undefined ;
3231
+
3232
+ inline for (info .Fn .params , 0.. ) | param , i | {
3233
+ parameters [i ] = lua .toAny (param .type .? , (i + 1 )) catch | err | {
3234
+ lua .raiseErrorStr (@errorName (err ), .{});
3235
+ };
3236
+ }
3237
+
3238
+ if (@typeInfo (info .Fn .return_type .? ) == .ErrorUnion ) {
3239
+ const result = @call (.auto , function , parameters ) catch | err | {
3240
+ lua .raiseErrorStr (@errorName (err ), .{});
3241
+ };
3242
+ lua .pushAny (result ) catch | err | {
3243
+ lua .raiseErrorStr (@errorName (err ), .{});
3244
+ };
3245
+ } else {
3246
+ const result = @call (.auto , function , parameters );
3247
+ lua .pushAny (result ) catch | err | {
3248
+ lua .raiseErrorStr (@errorName (err ), .{});
3249
+ };
3250
+ }
3251
+
3252
+ return 1 ;
3253
+ }
3254
+ };
3255
+ }
3256
+
3257
+ ///generates the interface for and pushes a function to the stack
3258
+ pub fn autoPushFunction (lua : * Lua , function : anytype ) void {
3259
+ const Interface = GenerateInterface (function );
3260
+ lua .pushFunction (wrap (Interface .interface ));
3261
+ }
3262
+
3263
+ ///get any lua global
3264
+ pub fn get (lua : * Lua , comptime ReturnType : type , name : [:0 ]const u8 ) ! ReturnType {
3265
+ _ = try lua .getGlobal (name );
3266
+ return try lua .toAny (ReturnType , -1 );
3267
+ }
3268
+
3269
+ ///set any lua global
3270
+ pub fn set (lua : * Lua , name : [:0 ]const u8 , value : anytype ) ! void {
3271
+ try lua .pushAny (value );
3272
+ lua .setGlobal (name );
3273
+ }
3016
3274
};
3017
3275
3018
3276
/// A string buffer allowing for Zig code to build Lua strings piecemeal
0 commit comments