138 lines
4.6 KiB
Python
138 lines
4.6 KiB
Python
import re
|
|
from pathlib import Path
|
|
from pprint import pprint
|
|
|
|
RX_COMMAND = re.compile(r'(?P<returns>CommandCost|std::tuple<CommandCost, [^>]*>) (?P<name>Cmd\w*)\((?P<args>[^)]*)\);')
|
|
RX_DEF_TRAIT = re.compile(r'DEF_CMD_TRAIT\((?P<constant>\w+),\s+(?P<function>\w+),\s+[^,]*,\s+(?P<category>\w+)\)')
|
|
RX_ARG = re.compile(r'(?P<type>(:?const |)[\w:]* &?)(?P<name>\w*)')
|
|
RX_CAMEL_TO_SNAKE = re.compile(r'(?<!^)(?=[A-Z])')
|
|
|
|
FILES = [
|
|
'src/misc_cmd.h',
|
|
'src/object_cmd.h',
|
|
'src/order_cmd.h',
|
|
'src/rail_cmd.h',
|
|
'src/road_cmd.h',
|
|
'src/station_cmd.h',
|
|
'src/town_cmd.h',
|
|
'src/tunnelbridge_cmd.h',
|
|
]
|
|
|
|
BASE_DIR = Path(__file__).parent
|
|
OUTPUT = BASE_DIR / 'src/citymania/generated/cm_gen_commands'
|
|
|
|
|
|
def parse_commands():
|
|
res = []
|
|
for f in FILES:
|
|
data = open(BASE_DIR / f).read()
|
|
traits = {}
|
|
for constant, name, category in RX_DEF_TRAIT.findall(data):
|
|
traits[name] = constant
|
|
for returns, name, args_str in RX_COMMAND.findall(data):
|
|
if returns.startswith('std::tuple'):
|
|
ret_type = returns[24: -1]
|
|
else:
|
|
ret_type = None
|
|
args = [RX_ARG.fullmatch(x).group('type', 'name') for x in args_str.split(', ')]
|
|
args = args[1:] # flags
|
|
first_tile_arg = (args[0][0].strip() == 'TileIndex')
|
|
if first_tile_arg:
|
|
args = args[1:]
|
|
print(name, traits[name], args)
|
|
res.append({
|
|
'name': name[3:],
|
|
'constant': traits[name],
|
|
'args': args,
|
|
'first_tile_arg': first_tile_arg,
|
|
'returns': ret_type,
|
|
})
|
|
return res
|
|
|
|
|
|
def run():
|
|
commands = parse_commands()
|
|
with open(OUTPUT.with_suffix('.hpp'), 'w') as f:
|
|
f.write(
|
|
'// This file is generated by gen_commands.py, do not edit\n\n'
|
|
'#ifndef CM_GEN_COMMANDS_HPP\n'
|
|
'#define CM_GEN_COMMANDS_HPP\n'
|
|
'#include "../cm_command_type.hpp"\n'
|
|
'namespace citymania {\n'
|
|
'namespace cmd {\n\n'
|
|
)
|
|
for cmd in commands:
|
|
name = cmd['name']
|
|
args_list = ', '.join(f'{at}{an}' for at, an in cmd['args'])
|
|
args_init = ', '.join(f'{an}{{{an}}}' for _, an in cmd['args'])
|
|
f.write(
|
|
f'class {name}: public Command {{\n'
|
|
f'public:\n'
|
|
)
|
|
for at, an in cmd['args']:
|
|
f.write(f' {at}{an};\n')
|
|
f.write(
|
|
f'\n'
|
|
f' {name}({args_list})\n'
|
|
f' :{args_init} {{}}\n'
|
|
)
|
|
if cmd.get('first_tile_arg'):
|
|
f.write(
|
|
f' {name}(TileIndex tile, {args_list})\n'
|
|
f' :Command{{tile}}, {args_init} {{}}\n'
|
|
)
|
|
f.write(
|
|
f' ~{name}() override {{}}\n'
|
|
f'\n'
|
|
f' bool DoPost() override;\n'
|
|
f' bool DoTest() override;\n'
|
|
f'}};\n\n'
|
|
)
|
|
f.write(
|
|
'} // namespace cmd\n'
|
|
'} // namespace citymania\n'
|
|
'#endif\n'
|
|
)
|
|
|
|
with open(OUTPUT.with_suffix('.cpp'), 'w') as f:
|
|
f.write(
|
|
'// This file is generated by gen_commands.py, do not edit\n\n'
|
|
'#include "../../stdafx.h"\n'
|
|
'#include "cm_gen_commands.hpp"\n'
|
|
)
|
|
for fn in FILES:
|
|
f.write(f'#include "../../{fn}"\n')
|
|
f.write(
|
|
'namespace citymania {\n'
|
|
'namespace cmd {\n\n'
|
|
)
|
|
for cmd in commands:
|
|
name = cmd['name']
|
|
constant = cmd['constant']
|
|
# constant = 'CMD_' + RX_CAMEL_TO_SNAKE.sub('_', name).upper()
|
|
args_list = ', '.join(f'this->{an}' for _, an in cmd['args'])
|
|
test_args_list = args_list
|
|
if cmd.get('first_tile_arg'):
|
|
test_args_list = f'this->tile, ' + args_list
|
|
|
|
cost_getter = '' if cmd['returns'] is None else 'std::get<0>'
|
|
|
|
f.write(
|
|
f'bool {name}::DoPost() {{\n'
|
|
f' return ::Command<{constant}>::Post(this->error, this->tile, {args_list});\n'
|
|
'}\n'
|
|
)
|
|
f.write(
|
|
f'bool {name}::DoTest() {{\n'
|
|
f' return {cost_getter}(::Command<{constant}>::Do(DC_NONE, {test_args_list})).Succeeded();\n'
|
|
'}\n'
|
|
)
|
|
f.write('\n')
|
|
f.write(
|
|
'} // namespace cmd\n'
|
|
'} // namespace citymania\n'
|
|
)
|
|
|
|
if __name__ == "__main__":
|
|
run()
|