mirror of
https://github.com/pmret/papermario.git
synced 2024-11-08 12:02:30 +01:00
implement SCRIPT(...) dsl
This commit is contained in:
parent
54c337596b
commit
83153acb59
1
.gitignore
vendored
1
.gitignore
vendored
@ -1,6 +1,7 @@
|
||||
# Python
|
||||
__pycache__/
|
||||
venv/
|
||||
.lark_cache*
|
||||
|
||||
# Misc
|
||||
.idea/
|
||||
|
3
.vscode/c_cpp_properties.json
vendored
3
.vscode/c_cpp_properties.json
vendored
@ -8,7 +8,8 @@
|
||||
],
|
||||
"defines": [
|
||||
"F3DEX_GBI_2",
|
||||
"_LANGUAGE_C"
|
||||
"_LANGUAGE_C",
|
||||
"SCRIPT(...)={}"
|
||||
],
|
||||
"cStandard": "c89",
|
||||
"cppStandard": "c++17",
|
||||
|
1
.vscode/extensions.json
vendored
1
.vscode/extensions.json
vendored
@ -3,6 +3,7 @@
|
||||
"ms-vscode.cpptools",
|
||||
"nanaian.vscode-star-rod",
|
||||
"notskm.clang-tidy",
|
||||
"EditorConfig.EditorConfig",
|
||||
],
|
||||
"unwantedRecommendations": [
|
||||
"llvm-vs-code-extensions.vscode-clangd",
|
||||
|
3
.vscode/settings.json
vendored
3
.vscode/settings.json
vendored
@ -1,6 +1,4 @@
|
||||
{
|
||||
"editor.detectIndentation": false,
|
||||
"editor.insertSpaces": true,
|
||||
"files.eol": "\n",
|
||||
"files.insertFinalNewline": true,
|
||||
"editor.rulers": [120],
|
||||
@ -10,6 +8,7 @@
|
||||
"-Iinclude/PR",
|
||||
"-Isrc",
|
||||
"-D_LANGUAGE_C",
|
||||
"-DSCRIPT(...)={}",
|
||||
],
|
||||
"python.pythonPath": "/usr/bin/python3",
|
||||
}
|
||||
|
2
Doxyfile
2
Doxyfile
@ -103,7 +103,7 @@ GENERATE_LATEX = NO
|
||||
ENABLE_PREPROCESSING = YES
|
||||
MACRO_EXPANSION = YES
|
||||
EXPAND_ONLY_PREDEF = YES
|
||||
PREDEFINED = DOXYGEN NON_MATCHING __attribute__((x))=
|
||||
PREDEFINED = DOXYGEN NON_MATCHING SCRIPT(...)={} __attribute__((x))=
|
||||
EXPAND_AS_DEFINED = INCLUDE_ASM UNK_TYPE UNK_PTR UNK_RET UNK_FUN_ARG UNK_FUN_PTR UNK_ARGS M
|
||||
SKIP_FUNCTION_MACROS = YES
|
||||
|
||||
|
2
Makefile
2
Makefile
@ -97,7 +97,7 @@ $(BUILD_DIR)/%.s.o: %.s
|
||||
$(AS) $(ASFLAGS) -o $@ $<
|
||||
|
||||
$(BUILD_DIR)/%.c.o: %.c $(H_FILES)
|
||||
cpp $(CPPFLAGS) $< | $(CC) $(CFLAGS) -o - | $(OLD_AS) $(OLDASFLAGS) - -o $@
|
||||
cpp $(CPPFLAGS) $< | tools/compile_dsl_macros.py | $(CC) $(CFLAGS) -o - | $(OLD_AS) $(OLDASFLAGS) - -o $@
|
||||
|
||||
$(BUILD_DIR)/%.bin.o: %.bin
|
||||
$(LD) -r -b binary -o $@ $<
|
||||
|
@ -1,6 +1,6 @@
|
||||
#!/bin/bash
|
||||
|
||||
COMPILER_OPTS="-std=gnu89 -Iinclude -Isrc -D_LANGUAGE_C"
|
||||
COMPILER_OPTS="-std=gnu89 -Iinclude -Isrc -D_LANGUAGE_C -DSCRIPT(...)={}"
|
||||
|
||||
shopt -s globstar
|
||||
|
||||
|
@ -1,2 +1,3 @@
|
||||
capstone
|
||||
PyYAML
|
||||
lark-parser
|
||||
|
@ -205,30 +205,22 @@ NpcGroupList M(npcGroupList) = {
|
||||
NPC_GROUP_LIST_END(),
|
||||
};
|
||||
|
||||
// *INDENT-OFF*
|
||||
Script M(ReadEastSign) = {
|
||||
SI_CALL(func_800441F0, SI_VAR(0)),
|
||||
SI_IF_EQ(SI_VAR(0), 1),
|
||||
SI_RETURN(),
|
||||
SI_END_IF(),
|
||||
Script M(ReadEastSign) = SCRIPT({
|
||||
func_800441F0(SI_VAR(0))
|
||||
if SI_VAR(0) == 1 {
|
||||
return
|
||||
}
|
||||
|
||||
SI_GROUP(0),
|
||||
setgroup 0
|
||||
|
||||
SI_CALL(func_802D5830, 1),
|
||||
SI_CALL(DisablePlayerInput, 1),
|
||||
SI_CALL(ShowMessageAtScreenPos, MessageID_SIGN_GOOMBA_KINGS_FORTRESS_AHEAD, 160, 40),
|
||||
SI_CALL(DisablePlayerInput, 0),
|
||||
SI_CALL(func_802D5830, 0),
|
||||
func_802D5830(1)
|
||||
DisablePlayerInput(1)
|
||||
ShowMessageAtScreenPos(MessageID_SIGN_GOOMBA_KINGS_FORTRESS_AHEAD, 160, 40)
|
||||
DisablePlayerInput(0)
|
||||
func_802D5830(0)
|
||||
});
|
||||
|
||||
SI_RETURN(),
|
||||
SI_END(),
|
||||
};
|
||||
|
||||
Script M(MakeEntities) = {
|
||||
SI_CALL(MakeEntity, 0x802EAFDC, 436, 0, -42, 0, 0x80000000),
|
||||
SI_CALL(AssignScript, &M(ReadEastSign)),
|
||||
|
||||
SI_RETURN(),
|
||||
SI_END(),
|
||||
};
|
||||
// *INDENT-ON*
|
||||
Script M(MakeEntities) = SCRIPT({
|
||||
MakeEntity(0x802EAFDC, 436, 0, -42, 0, 0x80000000)
|
||||
AssignScript(M(ReadEastSign))
|
||||
});
|
||||
|
229
tools/compile_dsl_macros.py
Executable file
229
tools/compile_dsl_macros.py
Executable file
@ -0,0 +1,229 @@
|
||||
#! /usr/bin/python3
|
||||
|
||||
from sys import stdin, stderr
|
||||
from lark import Lark, exceptions, Transformer, v_args
|
||||
|
||||
def eprint(*args, **kwargs):
|
||||
print(*args, file=stderr, **kwargs)
|
||||
|
||||
"""
|
||||
write_buf = ""
|
||||
def write(s):
|
||||
global write_buf
|
||||
write_buf += s
|
||||
def flush():
|
||||
global write_buf
|
||||
print(write_buf, end="")
|
||||
write_buf = ""
|
||||
"""
|
||||
def write(s):
|
||||
print(s, end="")
|
||||
def flush():
|
||||
pass
|
||||
#"""
|
||||
|
||||
script_parser = Lark(r"""
|
||||
|
||||
block: "{" line* "}"
|
||||
|
||||
?line: call
|
||||
| if_stmt
|
||||
| "return" -> return_stmt
|
||||
| "setgroup" expr -> setgroup
|
||||
|
||||
call: CNAME "(" [expr ("," expr)* [","]] ")"
|
||||
|
||||
if_stmt: "if" expr if_op expr block ["else" block]
|
||||
|
||||
?if_op: "==" -> if_op_eq
|
||||
|
||||
?expr: c_const_expr
|
||||
| ESCAPED_STRING
|
||||
| SIGNED_INT
|
||||
| DECIMAL
|
||||
| HEX_INT
|
||||
| CNAME
|
||||
|
||||
c_const_expr: "(" (c_const_expr | NOT_PARENS)+ ")"
|
||||
NOT_PARENS: /[^()]+/
|
||||
|
||||
%import common.CNAME
|
||||
%import common.SIGNED_INT
|
||||
%import common.DECIMAL
|
||||
%import common.HEXDIGIT
|
||||
%import common.ESCAPED_STRING
|
||||
|
||||
HEX_INT: ["+"|"-"] "0x" HEXDIGIT+
|
||||
|
||||
LINE_COMMENT: "//" /[^\n]*/ NEWLINE
|
||||
%ignore LINE_COMMENT
|
||||
|
||||
%import common.WS
|
||||
%import common.NEWLINE
|
||||
%ignore WS
|
||||
|
||||
""", start="block")#, parser="lalr", cache=True)
|
||||
|
||||
def si_cmd(opcode, *args):
|
||||
return [opcode, len(args), *args]
|
||||
|
||||
@v_args(inline=True)
|
||||
class Compile(Transformer):
|
||||
SIGNED_INT = str
|
||||
DECIMAL = float # TODO: fixed
|
||||
HEX_INT = str
|
||||
ESCAPED_STRING = eval
|
||||
|
||||
def CNAME(self, name):
|
||||
return f"(Bytecode)({name})"
|
||||
|
||||
NOT_PARENS = str
|
||||
def c_const_expr(self, *args): # usually a macro expansion
|
||||
return f"(Bytecode)({' '.join(args)})"
|
||||
|
||||
def block(self, *lines):
|
||||
flat = []
|
||||
for line in lines:
|
||||
if type(line) != list:
|
||||
eprint(f"uncompiled: {line}")
|
||||
else:
|
||||
flat += line
|
||||
return flat
|
||||
|
||||
def call(self, func, *args):
|
||||
# TODO: type checking etc
|
||||
return si_cmd(0x43, func, *args)
|
||||
|
||||
def if_stmt(self, a, op, b, block):
|
||||
return si_cmd(op, a, b) + block + si_cmd(0x13)
|
||||
def if_op_eq(self): return 0x0A
|
||||
|
||||
def return_stmt(self): return si_cmd(0x02)
|
||||
|
||||
def setgroup(self, group):
|
||||
return si_cmd(0x4D, group)
|
||||
|
||||
def compile_script(s):
|
||||
tree = script_parser.parse(s)
|
||||
#eprint(tree.pretty())
|
||||
return Compile().transform(tree) + si_cmd(0x02) + si_cmd(0x01)
|
||||
|
||||
def read_until_closing_paren(depth=1, lex_strings=False):
|
||||
text = ""
|
||||
|
||||
in_string = False
|
||||
string_escape = False
|
||||
|
||||
while True:
|
||||
char = stdin.read(1)
|
||||
|
||||
if len(char) == 0:
|
||||
# EOF
|
||||
return text
|
||||
|
||||
if string_escape == True:
|
||||
string_escape = False
|
||||
elif char == "(" and not in_string:
|
||||
depth += 1
|
||||
elif char == ")" and not in_string:
|
||||
depth -= 1
|
||||
if depth == 0:
|
||||
break
|
||||
elif char == '"' and lex_strings:
|
||||
in_string = not in_string
|
||||
elif char == "\\" and in_string:
|
||||
string_escape = True
|
||||
|
||||
text += char
|
||||
|
||||
return text
|
||||
|
||||
def read_line():
|
||||
line = ""
|
||||
|
||||
while True:
|
||||
char = stdin.read(1)
|
||||
|
||||
if len(char) == 0:
|
||||
# EOF
|
||||
return line
|
||||
|
||||
if char == "\n":
|
||||
break
|
||||
|
||||
line += char
|
||||
|
||||
return line
|
||||
|
||||
# Expects output from C preprocessor on stdin
|
||||
if __name__ == "__main__":
|
||||
line_no = 1
|
||||
char_no = 1
|
||||
file_info = []
|
||||
error = False
|
||||
|
||||
macro_name = "" # captures recent UPPER_CASE identifier
|
||||
prev_char = ""
|
||||
while True:
|
||||
char = stdin.read(1)
|
||||
|
||||
if len(char) == 0:
|
||||
# EOF
|
||||
write(macro_name)
|
||||
if error:
|
||||
exit(1)
|
||||
else:
|
||||
#with open("debug.i", "w") as i:
|
||||
# i.write(write_buf)
|
||||
flush()
|
||||
exit(0)
|
||||
|
||||
if char == "#" and (prev_char == "\n" or prev_char == ""):
|
||||
# cpp line/file marker
|
||||
line = read_line()
|
||||
line_split = line[1:].split(" ")
|
||||
|
||||
line_no = int(line_split[0])
|
||||
file_info = line_split[1:]
|
||||
|
||||
write("#" + line + "\n")
|
||||
elif char == "(":
|
||||
# SCRIPT(...)
|
||||
if macro_name == "SCRIPT":
|
||||
script_source = read_until_closing_paren(lex_strings=True)
|
||||
|
||||
try:
|
||||
bytecode = compile_script(script_source)
|
||||
|
||||
write("{")
|
||||
for word in bytecode:
|
||||
write(f"{word},")
|
||||
write("}")
|
||||
except exceptions.UnexpectedToken as e:
|
||||
line = e.line + line_no
|
||||
filename = file_info[0][1:-1]
|
||||
|
||||
eprint(e.get_context(script_source))
|
||||
eprint(f"{filename}:{line}: script parse error: unexpected `{e.token}'")
|
||||
|
||||
error = True
|
||||
|
||||
line_no += script_source.count("\n")
|
||||
write(f"\n# {line_no} {file_info[0]}\n")
|
||||
else:
|
||||
# leave non-macro in source
|
||||
write(macro_name + char)
|
||||
|
||||
macro_name = ""
|
||||
elif char == "_" or (char >= 'A' and char <= 'Z'):
|
||||
macro_name += char
|
||||
else:
|
||||
write(macro_name + char)
|
||||
macro_name = ""
|
||||
|
||||
if char == "\n":
|
||||
char_no = 0
|
||||
line_no += 1
|
||||
|
||||
char_no += 1
|
||||
prev_char = char
|
@ -2,9 +2,9 @@ name: "Paper Mario (North America)"
|
||||
basename: "papermario"
|
||||
options:
|
||||
find-file-boundaries: True
|
||||
pycparser_flags: ["-Iinclude", "-D_LANGUAGE_C", "-ffreestanding", "-DF3DEX_GBI_2", "-DSPLAT"]
|
||||
compiler: "GCC"
|
||||
mnemonic_ljust: 10
|
||||
o_as_suffix: yes
|
||||
segments:
|
||||
- name: header
|
||||
type: header
|
||||
|
Loading…
Reference in New Issue
Block a user