From c690c2b9ed1d4a02d26fe74ef7b10f478e4d8fd0 Mon Sep 17 00:00:00 2001 From: Jonathan Schleifer Date: Fri, 7 Mar 2025 21:02:39 +0000 Subject: [PATCH] Clean up identifiers FossilOrigin-Name: d35fd65699e79d96fc0588fff075f73b0dd7eb6ebfa0f6cffd0b0a3940e5fecd --- src/Alias.h | 15 ++ src/Alias.m | 15 ++ src/Command.h | 18 ++ src/Command.mm | 131 ++++++++++++ src/Ident.h | 18 -- src/Ident.m | 4 - src/Identifier.h | 12 ++ src/Identifier.m | 12 ++ src/Variable.h | 20 ++ src/Variable.m | 21 ++ src/{command.mm => commands.mm} | 367 ++++++++++---------------------- src/meson.build | 7 +- 12 files changed, 364 insertions(+), 276 deletions(-) create mode 100644 src/Alias.h create mode 100644 src/Alias.m create mode 100644 src/Command.h create mode 100644 src/Command.mm delete mode 100644 src/Ident.h delete mode 100644 src/Ident.m create mode 100644 src/Identifier.h create mode 100644 src/Identifier.m create mode 100644 src/Variable.h create mode 100644 src/Variable.m rename src/{command.mm => commands.mm} (61%) diff --git a/src/Alias.h b/src/Alias.h new file mode 100644 index 0000000..b798d44 --- /dev/null +++ b/src/Alias.h @@ -0,0 +1,15 @@ +#import "Identifier.h" + +OF_ASSUME_NONNULL_BEGIN + +@interface Alias : Identifier +@property (copy, nonatomic) OFString *action; +@property (readonly, nonatomic) bool persisted; + +- (instancetype)initWithName:(OFString *)name OF_UNAVAILABLE; +- (instancetype)initWithName:(OFString *)name + action:(OFString *)action + persisted:(bool)persisted; +@end + +OF_ASSUME_NONNULL_END diff --git a/src/Alias.m b/src/Alias.m new file mode 100644 index 0000000..d8750f6 --- /dev/null +++ b/src/Alias.m @@ -0,0 +1,15 @@ +#import "Alias.h" + +@implementation Alias +- (instancetype)initWithName:(OFString *)name + action:(OFString *)action + persisted:(bool)persisted +{ + self = [super initWithName:name]; + + _action = [action copy]; + _persisted = persisted; + + return self; +} +@end diff --git a/src/Command.h b/src/Command.h new file mode 100644 index 0000000..8cabbf5 --- /dev/null +++ b/src/Command.h @@ -0,0 +1,18 @@ +#import "Identifier.h" + +OF_ASSUME_NONNULL_BEGIN + +@interface Command : Identifier +@property (readonly, nonatomic) void (*function)(); +@property (readonly, nonatomic) int argumentsTypes; + +- (instancetype)initWithName:(OFString *)name OF_UNAVAILABLE; +- (instancetype)initWithName:(OFString *)name + function:(void (*)())function + argumentsTypes:(int)argumentsTypes; +- (int)callWithArguments:(char *_Nonnull *_Nonnull)arguments + numArguments:(size_t)numArguments + isDown:(bool)isDown; +@end + +OF_ASSUME_NONNULL_END diff --git a/src/Command.mm b/src/Command.mm new file mode 100644 index 0000000..e6709e8 --- /dev/null +++ b/src/Command.mm @@ -0,0 +1,131 @@ +#import "Command.h" + +#include + +@implementation Command +- (instancetype)initWithName:(OFString *)name + function:(void (*)())function + argumentsTypes:(int)argumentsTypes +{ + self = [super initWithName:name]; + + _function = function; + _argumentsTypes = argumentsTypes; + + return self; +} + +- (int)callWithArguments:(char **)arguments + numArguments:(size_t)numArguments + isDown:(bool)isDown +{ + switch (_argumentsTypes) { + case ARG_1INT: + if (isDown) + ((void(__cdecl *)(int))_function)(ATOI(arguments[1])); + break; + case ARG_2INT: + if (isDown) + ((void(__cdecl *)(int, int))_function)( + ATOI(arguments[1]), ATOI(arguments[2])); + break; + case ARG_3INT: + if (isDown) + ((void(__cdecl *)(int, int, int))_function)( + ATOI(arguments[1]), ATOI(arguments[2]), + ATOI(arguments[3])); + break; + case ARG_4INT: + if (isDown) + ((void(__cdecl *)(int, int, int, int))_function)( + ATOI(arguments[1]), ATOI(arguments[2]), + ATOI(arguments[3]), ATOI(arguments[4])); + break; + case ARG_NONE: + if (isDown) + ((void(__cdecl *)())_function)(); + break; + case ARG_1STR: + if (isDown) { + @autoreleasepool { + ((void(__cdecl *)(OFString *))_function)( + @(arguments[1])); + } + } + break; + case ARG_2STR: + if (isDown) { + @autoreleasepool { + ((void(__cdecl *)( + OFString *, OFString *))_function)( + @(arguments[1]), @(arguments[2])); + } + } + break; + case ARG_3STR: + if (isDown) { + @autoreleasepool { + ((void(__cdecl *)(OFString *, OFString *, + OFString *))_function)(@(arguments[1]), + @(arguments[2]), @(arguments[3])); + } + } + break; + case ARG_5STR: + if (isDown) { + @autoreleasepool { + ((void(__cdecl *)(OFString *, OFString *, + OFString *, OFString *, + OFString *))_function)(@(arguments[1]), + @(arguments[2]), @(arguments[3]), + @(arguments[4]), @(arguments[5])); + } + } + break; + case ARG_DOWN: + ((void(__cdecl *)(bool))_function)(isDown); + break; + case ARG_DWN1: + ((void(__cdecl *)(bool, char *))_function)( + isDown, arguments[1]); + break; + case ARG_1EXP: + if (isDown) + return ((int(__cdecl *)(int))_function)( + execute(arguments[1])); + break; + case ARG_2EXP: + if (isDown) + return ((int(__cdecl *)(int, int))_function)( + execute(arguments[1]), execute(arguments[2])); + break; + case ARG_1EST: + if (isDown) + return ((int(__cdecl *)(char *))_function)( + arguments[1]); + break; + case ARG_2EST: + if (isDown) + return ((int(__cdecl *)(char *, char *))_function)( + arguments[1], arguments[2]); + break; + case ARG_VARI: + if (isDown) { + // limit, remove + string r; + r[0] = 0; + for (int i = 1; i < numArguments; i++) { + // make string-list out of all arguments + strcat_s(r, arguments[i]); + if (i == numArguments - 1) + break; + strcat_s(r, " "); + } + ((void(__cdecl *)(char *))_function)(r); + } + break; + } + + return 0; +} +@end diff --git a/src/Ident.h b/src/Ident.h deleted file mode 100644 index 0e72269..0000000 --- a/src/Ident.h +++ /dev/null @@ -1,18 +0,0 @@ -#import - -OF_ASSUME_NONNULL_BEGIN - -enum IdentType { ID_VAR, ID_COMMAND, ID_ALIAS }; - -@interface Ident : OFObject -@property (nonatomic) enum IdentType type; -@property (copy, nonatomic) OFString *name; -@property (nonatomic) int min, max; // ID_VAR -@property (nonatomic) int *storage; // ID_VAR -@property (nonatomic) void (*fun)(); // ID_VAR, ID_COMMAND -@property (nonatomic) int narg; // ID_VAR, ID_COMMAND -@property (copy, nonatomic) OFString *action; // ID_ALIAS -@property (nonatomic) bool persist; -@end - -OF_ASSUME_NONNULL_END diff --git a/src/Ident.m b/src/Ident.m deleted file mode 100644 index d9adcaa..0000000 --- a/src/Ident.m +++ /dev/null @@ -1,4 +0,0 @@ -#import "Ident.h" - -@implementation Ident -@end diff --git a/src/Identifier.h b/src/Identifier.h new file mode 100644 index 0000000..554425e --- /dev/null +++ b/src/Identifier.h @@ -0,0 +1,12 @@ +#import + +OF_ASSUME_NONNULL_BEGIN + +@interface Identifier : OFObject +@property (readonly, copy, nonatomic) OFString *name; + +- (instancetype)init OF_UNAVAILABLE; +- (instancetype)initWithName:(OFString *)name; +@end + +OF_ASSUME_NONNULL_END diff --git a/src/Identifier.m b/src/Identifier.m new file mode 100644 index 0000000..2057724 --- /dev/null +++ b/src/Identifier.m @@ -0,0 +1,12 @@ +#import "Identifier.h" + +@implementation Identifier +- (instancetype)initWithName:(OFString *)name +{ + self = [super init]; + + _name = [name copy]; + + return self; +} +@end diff --git a/src/Variable.h b/src/Variable.h new file mode 100644 index 0000000..8fbbf1f --- /dev/null +++ b/src/Variable.h @@ -0,0 +1,20 @@ +#import "Identifier.h" + +OF_ASSUME_NONNULL_BEGIN + +@interface Variable : Identifier +@property (readonly, nonatomic) int min, max; +@property (readonly, nonatomic) int *storage; +@property (readonly, nonatomic) void (*function)(); +@property (readonly, nonatomic) bool persisted; + +- (instancetype)initWithName:(OFString *)name OF_UNAVAILABLE; +- (instancetype)initWithName:(OFString *)name + min:(int)min + max:(int)max + storage:(int *)storage + function:(void (*)())function + persisted:(bool)persisted; +@end + +OF_ASSUME_NONNULL_END diff --git a/src/Variable.m b/src/Variable.m new file mode 100644 index 0000000..6d2c67d --- /dev/null +++ b/src/Variable.m @@ -0,0 +1,21 @@ +#import "Variable.h" + +@implementation Variable +- (instancetype)initWithName:(OFString *)name + min:(int)min + max:(int)max + storage:(int *)storage + function:(void (*)())function + persisted:(bool)persisted +{ + self = [super initWithName:name]; + + _min = min; + _max = max; + _storage = storage; + _function = function; + _persisted = persisted; + + return self; +} +@end diff --git a/src/command.mm b/src/commands.mm similarity index 61% rename from src/command.mm rename to src/commands.mm index c64ea07..a546bc6 100644 --- a/src/command.mm +++ b/src/commands.mm @@ -5,7 +5,10 @@ #include -#import "Ident.h" +#import "Alias.h" +#import "Command.h" +#import "Identifier.h" +#import "Variable.h" void itoa(char *s, int i) @@ -21,24 +24,25 @@ exchangestr(char *o, const char *n) } // contains ALL vars/commands/aliases -OFMutableDictionary *idents; +static OFMutableDictionary *identifiers; void alias(OFString *name, OFString *action) { - Ident *b = idents[name]; + Alias *alias = identifiers[name]; - if (b == nil) { - Ident *b = [[Ident alloc] init]; - b.type = ID_ALIAS; - b.name = name; - b.action = action; - b.persist = true; + if (alias == nil) { + alias = [[Alias alloc] initWithName:name + action:action + persisted:true]; - idents[b.name] = b; + if (identifiers == nil) + identifiers = [[OFMutableDictionary alloc] init]; + + identifiers[name] = alias; } else { - if (b.type == ID_ALIAS) - b.action = action; + if ([alias isKindOfClass:[Alias class]]) + alias.action = action; else conoutf( @"cannot redefine builtin %@ with an alias", name); @@ -47,22 +51,20 @@ alias(OFString *name, OFString *action) COMMAND(alias, ARG_2STR) int -variable(OFString *name, int min, int cur, int max, int *storage, void (*fun)(), - bool persist) +variable(OFString *name, int min, int cur, int max, int *storage, + void (*function)(), bool persisted) { - if (idents == nil) - idents = [[OFMutableDictionary alloc] init]; + Variable *variable = [[Variable alloc] initWithName:name + min:min + max:max + storage:storage + function:function + persisted:persisted]; - Ident *v = [[Ident alloc] init]; - v.type = ID_VAR; - v.name = name; - v.min = min; - v.max = max; - v.storage = storage; - v.fun = fun; - v.persist = persist; + if (identifiers == nil) + identifiers = [[OFMutableDictionary alloc] init]; - idents[name] = v; + identifiers[name] = variable; return cur; } @@ -70,43 +72,43 @@ variable(OFString *name, int min, int cur, int max, int *storage, void (*fun)(), void setvar(OFString *name, int i) { - *idents[name].storage = i; + *[identifiers[name] storage] = i; } int getvar(OFString *name) { - return *idents[name].storage; + return *[identifiers[name] storage]; } bool identexists(OFString *name) { - return (idents[name] != nil); + return (identifiers[name] != nil); } OFString * getalias(OFString *name) { - Ident *i = idents[name]; - return i != nil && i.type == ID_ALIAS ? i.action : nil; + Alias *alias = identifiers[name]; + + if ([alias isKindOfClass:[Alias class]]) + return alias.action; + + return nil; } bool -addcommand(OFString *name, void (*fun)(), int narg) +addcommand(OFString *name, void (*function)(), int argumentsTypes) { - if (idents == nil) - idents = [[OFMutableDictionary alloc] init]; + Command *command = [[Command alloc] initWithName:name + function:function + argumentsTypes:argumentsTypes]; - @autoreleasepool { - Ident *c = [[Ident alloc] init]; - c.type = ID_COMMAND; - c.name = name; - c.fun = fun; - c.narg = narg; + if (identifiers == nil) + identifiers = [[OFMutableDictionary alloc] init]; - idents[name] = c; - } + identifiers[name] = command; return false; } @@ -171,18 +173,14 @@ char * lookup(char *n) // find value of ident referenced with $ in exp { @autoreleasepool { - Ident *ID = idents[@(n + 1)]; + __kindof Identifier *identifier = identifiers[@(n + 1)]; - if (ID != nil) { - switch (ID.type) { - case ID_VAR: - string t; - itoa(t, *(ID.storage)); - return exchangestr(n, t); - case ID_ALIAS: - return exchangestr(n, ID.action.UTF8String); - } - } + if ([identifier isKindOfClass:[Variable class]]) { + string t; + itoa(t, *[identifier storage]); + return exchangestr(n, t); + } else if ([identifier isKindOfClass:[Alias class]]) + return exchangestr(n, [identifier action].UTF8String); } conoutf(@"unknown alias lookup: %s", n + 1); @@ -223,185 +221,37 @@ execute(char *p, bool isdown) // all evaluation happens here, recursively continue; // empty statement @autoreleasepool { - Ident *ID = idents[@(c)]; + __kindof Identifier *identifier = identifiers[@(c)]; - if (ID == nil) { + if (identifier == nil) { val = ATOI(c); if (!val && *c != '0') conoutf(@"unknown command: %s", c); } else { - switch (ID.type) { - // game defined commands - case ID_COMMAND: + if ([identifier + isKindOfClass:[Command class]]) { + // game defined commands // use very ad-hoc function signature, // and just call it - switch (ID.narg) { - case ARG_1INT: - if (isdown) - ((void(__cdecl *)( - int))ID.fun)( - ATOI(w[1])); - break; - case ARG_2INT: - if (isdown) - ((void(__cdecl *)( - int, int))ID.fun)( - ATOI(w[1]), - ATOI(w[2])); - break; - case ARG_3INT: - if (isdown) - ((void(__cdecl *)(int, - int, int))ID.fun)( - ATOI(w[1]), - ATOI(w[2]), - ATOI(w[3])); - break; - case ARG_4INT: - if (isdown) - ((void(__cdecl *)(int, - int, int, - int))ID.fun)( - ATOI(w[1]), - ATOI(w[2]), - ATOI(w[3]), - ATOI(w[4])); - break; - case ARG_NONE: - if (isdown) - ((void(__cdecl *)()) - ID.fun)(); - break; - case ARG_1STR: - if (isdown) { - @autoreleasepool { - ((void( - __cdecl *)( - OFString *)) - ID.fun)( - @(w[1])); - } - } - break; - case ARG_2STR: - if (isdown) { - @autoreleasepool { - ((void( - __cdecl *)( - OFString *, - OFString *)) - ID.fun)( - @(w[1]), - @(w[2])); - } - } - break; - case ARG_3STR: - if (isdown) { - @autoreleasepool { - ((void( - __cdecl *)( - OFString *, - OFString *, - OFString *)) - ID.fun)( - @(w[1]), - @(w[2]), - @(w[3])); - } - } - break; - case ARG_5STR: - if (isdown) { - @autoreleasepool { - ((void( - __cdecl *)( - OFString *, - OFString *, - OFString *, - OFString *, - OFString *)) - ID.fun)( - @(w[1]), - @(w[2]), - @(w[3]), - @(w[4]), - @(w[5])); - } - } - break; - case ARG_DOWN: - ((void(__cdecl *)(bool))ID.fun)( - isdown); - break; - case ARG_DWN1: - ((void(__cdecl *)( - bool, char *))ID.fun)( - isdown, w[1]); - break; - case ARG_1EXP: - if (isdown) - val = ((int(__cdecl *)( - int))ID.fun)( - execute(w[1])); - break; - case ARG_2EXP: - if (isdown) - val = ((int(__cdecl *)( - int, int))ID.fun)( - execute(w[1]), - execute(w[2])); - break; - case ARG_1EST: - if (isdown) - val = ((int(__cdecl *)( - char *))ID.fun)( - w[1]); - break; - case ARG_2EST: - if (isdown) - val = ((int(__cdecl *)( - char *, - char *))ID.fun)( - w[1], w[2]); - break; - case ARG_VARI: - if (isdown) { - // limit, remove - string r; - r[0] = 0; - for (int i = 1; - i < numargs; i++) { - // make - // string-list - // out of all - // arguments - strcat_s( - r, w[i]); - if (i == - numargs - 1) - break; - strcat_s( - r, " "); - } - ((void(__cdecl *)( - char *))ID.fun)(r); - break; - } - } - break; - - // game defined variables - case ID_VAR: + val = [identifier + callWithArguments:w + numArguments:numargs + isDown:isdown]; + } else if ([identifier + isKindOfClass:[Variable + class]]) { + // game defined variables if (isdown) { if (!w[1][0]) // var with no value // just prints its // current value conoutf(@"%s = %d", c, - *ID.storage); + *[identifier + storage]); else { - if (ID.min > ID.max) { + if ([identifier min] > + [identifier max]) { conoutf( @"variable " @"is " @@ -411,17 +261,22 @@ execute(char *p, bool isdown) // all evaluation happens here, recursively int i1 = ATOI(w[1]); if (i1 < - ID.min || + [identifier + min] || i1 > - ID.max) { + [identifier + max]) { // clamp // to // valid // range i1 = - i1 < ID.min - ? ID.min - : ID.max; + i1 < [identifier + min] + ? [identifier + min] + : [identifier + max]; conoutf( @"v" @"a" @@ -452,26 +307,30 @@ execute(char *p, bool isdown) // all evaluation happens here, recursively @"%" @"d", c, - ID.min, - ID.max); + [identifier + min], + [identifier + max]); } - *ID.storage = + *[identifier + storage] = i1; } - if (ID.fun) + if ([identifier + function] != + NULL) // call trigger // function if // available ((void(__cdecl - *)())ID - .fun)(); + *)())[identifier + function])(); } } - break; - - // alias, also used as functions and (global) - // variables - case ID_ALIAS: + } else if ([identifier + isKindOfClass:[Alias class]]) { + // alias, also used as functions and + // (global) variables for (int i = 1; i < numargs; i++) { @autoreleasepool { // set any arguments as @@ -486,8 +345,8 @@ execute(char *p, bool isdown) // all evaluation happens here, recursively } // create new string here because alias // could rebind itself - char *action = - newstring(ID.action.UTF8String); + char *action = newstring( + [identifier action].UTF8String); val = execute(action, isdown); gp()->deallocstr(action); break; @@ -500,7 +359,7 @@ execute(char *p, bool isdown) // all evaluation happens here, recursively return val; } -// tab-completion of all idents +// tab-completion of all identifiers int completesize = 0, completeidx = 0; @@ -533,13 +392,13 @@ complete(OFString *s_) } __block int idx = 0; - [idents enumerateKeysAndObjectsUsingBlock:^( - OFString *name, Ident *ident, bool *stop) { - if (strncmp(ident.name.UTF8String, s + 1, + [identifiers enumerateKeysAndObjectsUsingBlock:^( + OFString *name, Identifier *identifier, bool *stop) { + if (strncmp(identifier.name.UTF8String, s + 1, completesize) == 0 && idx++ == completeidx) { strcpy_s(s, "/"); - strcat_s(s, ident.name.UTF8String); + strcat_s(s, identifier.name.UTF8String); } }]; @@ -602,24 +461,28 @@ writecfg() writeclientinfo(stream); [stream writeString:@"\n"]; - [idents enumerateKeysAndObjectsUsingBlock:^( - OFString *name, Ident *ident, bool *stop) { - if (ident.type == ID_VAR && ident.persist) { - [stream - writeFormat:@"%@ %d\n", ident.name, *ident.storage]; - } + [identifiers enumerateKeysAndObjectsUsingBlock:^( + OFString *name, __kindof Identifier *identifier, bool *stop) { + if (![identifier isKindOfClass:[Variable class]] || + ![identifier persisted]) + return; + + [stream writeFormat:@"%@ %d\n", identifier.name, + *[identifier storage]]; }]; [stream writeString:@"\n"]; writebinds(stream); [stream writeString:@"\n"]; - [idents enumerateKeysAndObjectsUsingBlock:^( - OFString *name, Ident *ident, bool *stop) { - if (ident.type == ID_ALIAS && - ![ident.name hasPrefix:@"nextmap_"]) - [stream writeFormat:@"alias \"%@\" [%@]\n", ident.name, - ident.action]; + [identifiers enumerateKeysAndObjectsUsingBlock:^( + OFString *name, __kindof Identifier *identifier, bool *stop) { + if (![identifier isKindOfClass:[Alias class]] || + [identifier.name hasPrefix:@"nextmap_"]) + return; + + [stream writeFormat:@"alias \"%@\" [%@]\n", identifier.name, + [identifier action]]; }]; [stream close]; diff --git a/src/meson.build b/src/meson.build index 06f9bb0..6f88c40 100644 --- a/src/meson.build +++ b/src/meson.build @@ -1,17 +1,20 @@ executable('client', [ + 'Alias.m', + 'Command.mm', 'Cube.mm', - 'Ident.m', + 'Identifier.m', 'KeyMapping.m', 'MD2.mm', 'MapModelInfo.m', 'Menu.m', 'MenuItem.m', + 'Variable.m', 'client.mm', 'clientextras.mm', 'clientgame.mm', 'clients2c.mm', - 'command.mm', + 'commands.mm', 'console.mm', 'editing.mm', 'entities.mm',