use SCRIPT(...) everywhere

This commit is contained in:
Alex Bates 2020-10-23 18:39:38 +01:00
parent 218961279b
commit 652c52d3dd
No known key found for this signature in database
GPG Key ID: 5E11C2DB78877706
5 changed files with 487 additions and 178 deletions

View File

@ -121,7 +121,8 @@ else
# C
$(filter %.c.o,$(OBJECTS)): $(BUILD_DIR)/%.c.o: %.c
@mkdir -p $(shell dirname $@)
cpp $(CPPFLAGS) $< | tools/compile_dsl_macros.py | $(CC) $(CFLAGS) -o - | $(OLD_AS) $(OLDASFLAGS) - -o $@
cpp $(CPPFLAGS) $< | tools/compile_dsl_macros.py > $(BUILD_DIR)/$*.i
$(CC) $(CFLAGS) -o - $(BUILD_DIR)/$*.i | $(OLD_AS) $(OLDASFLAGS) - -o $@
# Assembly
$(filter %.s.o,$(OBJECTS)): $(BUILD_DIR)/%.s.o: %.s

View File

@ -134,7 +134,7 @@ typedef struct StatDrop {
{ F16(100), F16(30), attempts, F16(40) }, \
}
#define ANIMATION(sprite, palette, anim) (sprite << 16) + (palette << 8) + anim
#define ANIMATION(sprite, palette, anim) ((sprite << 16) + (palette << 8) + anim)
#define OVERRIDE_MOVEMENT_SPEED(speed) (speed * 32767)
#define NO_OVERRIDE_MOVEMENT_SPEED OVERRIDE_MOVEMENT_SPEED(-1)

View File

@ -142,14 +142,12 @@ typedef s32 ApiStatus;
#define SI_END_CHILD_THREAD() SI_CMD(0x59)
#define EXIT_WALK_SCRIPT(walkDistance, exitIdx, map, entryIdx) \
{ \
SI_GROUP(0x1B), \
SI_CALL(UseExitHeading, walkDistance, exitIdx), \
SI_EXEC(ExitWalk), \
SI_CALL(GotoMap, map, entryIdx), \
SI_WAIT_FRAMES(100), \
SI_RETURN(), \
SI_END(), \
}
SCRIPT({ \
group 0x1B; \
UseExitHeading(walkDistance, exitIdx); \
spawn ExitWalk; \
GotoMap(map, entryIdx); \
sleep 100; \
})
#endif

View File

@ -3,29 +3,25 @@
Script M(ExitWest) = EXIT_WALK_SCRIPT(60, 0, "kmr_07", 1);
Script M(ExitEast) = EXIT_WALK_SCRIPT(60, 1, "kmr_11", 0);
Script M(BindExits) = {
SI_BIND(M(ExitWest), TriggerFlag_FLOOR_ABOVE, 0 /* deili1 */, NULL),
SI_BIND(M(ExitEast), TriggerFlag_FLOOR_ABOVE, 3 /* deili2 */, NULL),
SI_RETURN(),
SI_END(),
};
Script M(BindExits) = SCRIPT({
bind M(ExitWest) to TriggerFlag_FLOOR_ABOVE 0 // deili1
bind M(ExitEast) to TriggerFlag_FLOOR_ABOVE 3 // deili2
});
Script M(Main) = {
SI_SET(SI_SAVE_VAR(425), 31),
SI_CALL(SetSpriteShading, -1),
SI_CALL(SetCamPerspective, 0, 3, 25, 16, 4096),
SI_CALL(SetCamBGColor, 0, 0, 0, 0),
SI_CALL(SetCamEnabled, 0, 1),
SI_CALL(MakeNpcs, 0, M(npcGroupList)),
SI_EXEC_WAIT(M(MakeEntities)),
SI_EXEC(M(PlayMusic)),
SI_SET(SI_VAR(0), M(BindExits)),
SI_EXEC(EnterWalk),
SI_WAIT_FRAMES(1),
SI_BIND(M(ReadWestSign), TriggerFlag_WALL_INTERACT, 10, NULL),
SI_RETURN(),
SI_END(),
};
Script M(Main) = SCRIPT({
SI_SAVE_VAR(425) = 31
SetSpriteShading(-1)
SetCamPerspective(0, 3, 25, 16, 4096)
SetCamBGColor(0, 0, 0, 0)
SetCamEnabled(0, 1)
MakeNpcs(0, M(npcGroupList))
await M(MakeEntities)
spawn M(PlayMusic)
SI_VAR(0) = M(BindExits)
spawn EnterWalk
sleep 1
bind M(ReadWestSign) to TriggerFlag_WALL_INTERACT 10
});
NpcAISettings M(goombaAISettings) = {
.moveSpeed = 1.5f,
@ -42,11 +38,9 @@ NpcAISettings M(goombaAISettings) = {
.unk_2C = TRUE,
};
Script M(GoombaAI) = {
SI_CALL(DoBasicAI, &M(goombaAISettings)),
SI_RETURN(),
SI_END(),
};
Script M(GoombaAI) = SCRIPT({
DoBasicAI(M(goombaAISettings))
});
NpcSettings M(goombaNpcSettings) = {
.height = 20,
@ -57,102 +51,95 @@ NpcSettings M(goombaNpcSettings) = {
.level = 5,
};
// *INDENT-OFF*
/// @bug The RETURN command is after the END command, so this script will never terminate.
Script M(ReadWestSign) = {
SI_GROUP(0),
/// @bug Never returns
Script M(ReadWestSign) = SCRIPT({
group 0
// "Eat a Mushroom to regain your energy!"
SI_SUSPEND_GROUP(1),
SI_CALL(DisablePlayerInput, TRUE),
SI_CALL(ShowMessageAtScreenPos, MessageID_SIGN_MUSHROOM_GOOMBA_TRAP, 160, 40),
SI_RESUME_GROUP(1),
suspend group 1
DisablePlayerInput(TRUE)
ShowMessageAtScreenPos(MessageID_SIGN_MUSHROOM_GOOMBA_TRAP, 160, 40)
resume group 1
SI_SET(SI_FLAG(0), FALSE),
SI_CALL(GetGoomba),
SI_IF_NE(SI_VAR(0), FALSE),
SI_CALL(GetNpcVar, NpcId_GOOMBA, 0, SI_VAR(0)),
SI_IF_EQ(SI_VAR(0), FALSE),
SI_FLAG(0) = FALSE
GetGoomba()
if SI_VAR(0) != FALSE {
GetNpcVar(NpcId_GOOMBA, 0, SI_VAR(0))
if SI_VAR(0) == FALSE {
// Trigger Goomba to peel off
SI_CALL(SetNpcVar, NpcId_GOOMBA, 0, TRUE),
SI_SET(SI_FLAG(0), TRUE),
SI_WAIT_FRAMES(10),
SI_END_IF(),
SI_END_IF(),
SI_CALL(DisablePlayerInput, FALSE),
SI_IF_EQ(SI_FLAG(0), TRUE),
SI_UNBIND_ME(),
SI_END_IF(),
SetNpcVar(NpcId_GOOMBA, 0, TRUE)
SI_FLAG(0) = TRUE
sleep 10
}
}
DisablePlayerInput(FALSE)
if SI_FLAG(0) == TRUE {
unbind
}
SI_END(),
SI_RETURN(),
};
break
return
});
Script M(GoombaIdle) = {
SI_WAIT_FRAMES(1),
Script M(GoombaIdle) = SCRIPT({
sleep 1
SI_CALL(SetSelfVar, 0, FALSE),
SI_CALL(SetNpcAnimation, NpcId_SELF, ANIMATION(SpriteId_GOOMBA, 0, 13)),
SI_CALL(EnableNpcShadow, NpcId_SELF, FALSE),
SI_CALL(SetSelfEnemyFlagBits, 0x00000020, TRUE),
SetSelfVar(0, FALSE)
SetNpcAnimation(NpcId_SELF, ANIMATION(SpriteId_GOOMBA, 0, 13))
EnableNpcShadow(NpcId_SELF, FALSE)
SetSelfEnemyFlagBits(0x00000020, TRUE)
// Wait until read_sign sets NPC var 0
SI_LABEL(0),
SI_CALL(GetSelfVar, 0, SI_VAR(0)),
SI_WAIT_FRAMES(1),
SI_IF_EQ(SI_VAR(0), FALSE),
SI_GOTO(0),
SI_END_IF(),
lbl:
GetSelfVar(0, SI_VAR(0))
sleep 1
if SI_VAR(0) == FALSE {
goto lbl
}
// Peel and jump off the sign
SI_CALL(SetNpcFlagBits, NpcId_SELF, 0x00240000, TRUE),
SI_WAIT_FRAMES(3),
SI_SET_F(SI_VAR(0), SI_FIXED(0.0f)),
SI_LOOP(9),
SI_ADD_F(SI_VAR(0), SI_FIXED(10.0f)),
SI_CALL(SetNpcRotation, NpcId_SELF, 0, SI_VAR(0), 0),
SI_WAIT_FRAMES(1),
SI_END_LOOP(),
SI_CALL(SetNpcAnimation, NpcId_SELF, ANIMATION(SpriteId_GOOMBA, 0, 0)),
SI_LOOP(9),
SI_ADD_F(SI_VAR(0), SI_FIXED(10.0f)),
SI_CALL(SetNpcRotation, NpcId_SELF, 0, SI_VAR(0), 0),
SI_WAIT_FRAMES(1),
SI_END_LOOP(),
SI_CALL(SetNpcAnimation, NpcId_SELF, ANIMATION(SpriteId_GOOMBA, 0, 7)),
SI_WAIT_FRAMES(20),
SI_CALL(SetNpcAnimation, NpcId_SELF, ANIMATION(SpriteId_GOOMBA, 0, 1)),
SI_CALL(PlaySoundAtNpc, NpcId_SELF, 248, 0),
SI_CALL(func_802CFE2C, NpcId_SELF, 8192),
SI_CALL(func_802CFD30, NpcId_SELF, 5, 6, 1, 1, 0),
SI_WAIT_FRAMES(12),
SI_WAIT_FRAMES(5),
SI_CALL(PlaySoundAtNpc, NpcId_SELF, 812, 0),
SI_CALL(EnableNpcShadow, NpcId_SELF, TRUE),
SI_CALL(SetNpcJumpscale, NpcId_SELF, SI_FIXED(0.6005859375f)),
SI_CALL(NpcJump0, NpcId_SELF, -35, 0, 30, 23),
SI_CALL(func_802CFD30, NpcId_SELF, 0, 0, 0, 0, 0),
SI_CALL(InterpNpcYaw, NpcId_SELF, 90, 0),
SI_CALL(SetNpcFlagBits, NpcId_SELF, 0x00240000, FALSE),
SI_CALL(SetSelfEnemyFlagBits, 0x00000020, FALSE),
SI_CALL(SetSelfEnemyFlagBits, 0x40000000, TRUE),
SetNpcFlagBits(NpcId_SELF, 0x00240000, TRUE)
sleep 3
SI_VAR(0) = 0.0
loop 9 {
SI_VAR(0) += 10.0
SetNpcRotation(NpcId_SELF, 0, SI_VAR(0), 0)
sleep 1
}
SetNpcAnimation(NpcId_SELF, ANIMATION(SpriteId_GOOMBA, 0, 0))
loop 9 {
SI_VAR(0) += 10.0
SetNpcRotation(NpcId_SELF, 0, SI_VAR(0), 0)
sleep 1
}
SetNpcAnimation(NpcId_SELF, ANIMATION(SpriteId_GOOMBA, 0, 7))
sleep 20
SetNpcAnimation(NpcId_SELF, ANIMATION(SpriteId_GOOMBA, 0, 1))
PlaySoundAtNpc(NpcId_SELF, 248, 0)
func_802CFE2C(NpcId_SELF, 8192)
func_802CFD30(NpcId_SELF, 5, 6, 1, 1, 0)
sleep 12
sleep 5
PlaySoundAtNpc(NpcId_SELF, 812, 0)
EnableNpcShadow(NpcId_SELF, TRUE)
SetNpcJumpscale(NpcId_SELF, 0.6005859375)
NpcJump0(NpcId_SELF, -35, 0, 30, 23)
func_802CFD30(NpcId_SELF, 0, 0, 0, 0, 0)
InterpNpcYaw(NpcId_SELF, 90, 0)
SetNpcFlagBits(NpcId_SELF, 0x00240000, FALSE)
SetSelfEnemyFlagBits(0x00000020, FALSE)
SetSelfEnemyFlagBits(0x40000000, TRUE)
// We're done jumping off; the player can read the sign again
SI_BIND(M(ReadWestSign), TriggerFlag_WALL_INTERACT, 10, NULL),
bind M(ReadWestSign) to TriggerFlag_WALL_INTERACT 10
// Behave like a normal enemy from now on
SI_CALL(BindNpcAI, NpcId_SELF, &M(GoombaAI)),
// Behave like a normal enemy from now o
BindNpcAI(NpcId_SELF, M(GoombaAI))
});
SI_RETURN(),
SI_END(),
};
Script M(GoombaInit) = {
SI_CALL(BindNpcIdle, NpcId_SELF, &M(GoombaIdle)),
SI_RETURN(),
SI_END(),
};
// *INDENT-ON*
Script M(GoombaInit) = SCRIPT({
BindNpcIdle(NpcId_SELF, M(GoombaIdle))
});
StaticNpc M(goombaNpc) = {
.id = NpcId_GOOMBA,
@ -211,7 +198,7 @@ Script M(ReadEastSign) = SCRIPT({
return
}
setgroup 0
group 0
func_802D5830(1)
DisablePlayerInput(1)

View File

@ -1,41 +1,68 @@
#! /usr/bin/python3
from sys import stdin, stderr
from lark import Lark, exceptions, Transformer, v_args
from lark import Lark, exceptions, Tree, Transformer, Visitor, v_args
from lark.visitors import Discard
import traceback
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
#"""
ANSI_RED = "\033[1;31;40m"
ANSI_RESET = "\u001b[0m"
def pairs(seq):
i = iter(seq)
prev = next(i)
for item in i:
yield prev, item
prev = item
script_parser = Lark(r"""
block: "{" line* "}"
block: "{" NEWLINE* (stmt STMT_SEP)* NEWLINE* "}"
?line: call
?stmt: call
| label ":" -> label_decl
| "goto" label -> label_goto
| if_stmt
| "return" -> return_stmt
| "setgroup" expr -> setgroup
| "return" -> return_stmt
| "break" -> break_stmt
| "sleep" expr -> sleep_stmt
| "spawn" expr -> spawn_stmt
| "await" expr -> await_stmt
| lhs "=" "spawn" expr -> spawn_set_stmt
| lhs set_op expr -> set_stmt
| lhs ":=" expr -> set_const_stmt
| bind_stmt
| bind_set_stmt
| "unbind" -> unbind_stmt
| "group" expr -> set_group
| suspend_stmt
| resume_stmt
| kill_stmt
| loop_stmt
call: CNAME "(" [expr ("," expr)* [","]] ")"
if_stmt: "if" expr if_op expr block ["else" block]
?if_op: "==" -> if_op_eq
| "!=" -> if_op_ne
suspend_stmt: "suspend" control_type expr ("," control_type expr)* [","]
resume_stmt: "resume" control_type expr ("," control_type expr)* [","]
kill_stmt: "kill" control_type expr ("," control_type expr)* [","]
?control_type: "group" -> control_type_group
| "others" -> control_type_others
| ["script"] -> control_type_script
bind_stmt: "bind" expr "to" expr expr
bind_set_stmt: lhs "=" "bind" expr "to" expr expr
loop_stmt: "loop" [expr] block
?expr: c_const_expr
| ESCAPED_STRING
@ -44,9 +71,23 @@ script_parser = Lark(r"""
| HEX_INT
| CNAME
c_const_expr: "(" (c_const_expr | NOT_PARENS)+ ")"
?lhs: c_const_expr
?set_op: "=" -> set_op_eq
| "+=" -> set_op_add
| "-=" -> set_op_sub
| "*=" -> set_op_mul
| "/=" -> set_op_div
| "%=" -> set_op_mod
c_const_expr: c_const_expr_internal
c_const_expr_internal: "(" (c_const_expr_internal | NOT_PARENS)+ ")"
NOT_PARENS: /[^()]+/
STMT_SEP: (NEWLINE+ | ";")
label: /[a-zA-Z0-9_]+/
%import common.CNAME
%import common.SIGNED_INT
%import common.DECIMAL
@ -58,55 +99,289 @@ script_parser = Lark(r"""
LINE_COMMENT: "//" /[^\n]*/ NEWLINE
%ignore LINE_COMMENT
%import common.WS
%import common.WS_INLINE
%import common.NEWLINE
%ignore WS
%ignore WS_INLINE
""", start="block")#, parser="lalr", cache=True)
""", start="block", propagate_positions=True)#, parser="lalr", cache=True)
def si_cmd(opcode, *args):
return [opcode, len(args), *args]
class Cmd():
def __init__(self, opcode, *args, **kwargs):
if opcode:
self.opcode = opcode
self.args = args
self.meta = kwargs.get("meta", None)
self.context = []
@v_args(inline=True)
def add_context(self, context):
self.context.append(context)
def to_bytecode(self):
return [ self.opcode, len(self.args), *self.args ]
def __str__(self):
return f"Cmd({self.opcode:02X}, {', '.join(map(str, self.args))})"
class BreakCmd(Cmd):
def __init__(self, **kwargs):
super().__init__(None, **kwargs)
@property
def opcode(self):
# are we in a switch or a loop?
context = None
for c in self.context:
if c in ("switch", "loop"):
context = c
if not context:
return 0x01 # END
elif context == "loop":
return 0x07 # SI_BREAK_LOOP
elif context == "switch":
return 0x22 # BREAK_CASE
def __str__(self):
return "BreakCmd"
class CompileError(Exception):
def __init__(self, message, meta):
super().__init__(message)
self.meta = meta
def is_fixed_var(v):
if type(v) == int:
if v <= -250000000:
return False
elif v <= -220000000:
return True
return False
class LabelAllocation(Visitor):
def __init__(self):
super().__init__()
self.labels = []
def label_decl(self, tree):
name = tree.children[0].children[0]
if name in self.labels:
raise CompileError(f"label `{name}' already declared", tree.meta)
self.labels.append(name)
@v_args(tree=True)
class Compile(Transformer):
SIGNED_INT = str
DECIMAL = float # TODO: fixed
HEX_INT = str
ESCAPED_STRING = eval
ESCAPED_STRING = str
def transform(self, tree):
self.alloc = LabelAllocation()
self.alloc.visit_topdown(tree)
return super().transform(tree)
def CNAME(self, name):
return f"(Bytecode)({name})"
return f"(Bytecode)(&{name})"
NOT_PARENS = str
def c_const_expr(self, *args): # usually a macro expansion
return f"(Bytecode)({' '.join(args)})"
def c_const_expr_internal(self, tree):
return f"({' '.join(tree.children)})"
def c_const_expr(self, tree):
return f"(Bytecode){tree.children[0]}"
def block(self, *lines):
def DECIMAL(self, v):
# fixed-point
return int((float(v) * 1024) - 230000000)
def block(self, tree):
# flatten children list
flat = []
for line in lines:
if type(line) != list:
eprint(f"uncompiled: {line}")
else:
flat += line
for node in tree.children:
if type(node) == list:
flat += node
elif isinstance(node, Cmd):
flat.append(node)
return flat
def call(self, func, *args):
def call(self, tree):
# TODO: type checking etc
return si_cmd(0x43, func, *args)
return Cmd(0x43, *tree.children, meta=tree.meta)
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 if_stmt(self, tree):
a, op, b, block = tree.children
for cmd in block:
if type(cmd) == Cmd:
cmd.add_context("if")
return [ Cmd(op, a, b, meta=tree.meta), *block, Cmd(0x13) ]
def if_op_eq(self, tree): return 0x0A
def if_op_ne(self, tree): return 0x0B
def return_stmt(self): return si_cmd(0x02)
def loop_stmt(self, tree):
expr = tree.children.pop(0) if len(tree.children) > 1 else 0
block = tree.children[0]
def setgroup(self, group):
return si_cmd(0x4D, group)
for cmd in block:
if type(cmd) == Cmd:
cmd.add_context("loop")
return [ Cmd(0x05, expr, meta=tree.meta), *block, Cmd(0x06) ]
def return_stmt(self, tree): return Cmd(0x02, meta=tree.meta)
def break_stmt(self, tree):
return BreakCmd(meta=tree.meta)
def set_group(self, tree): return Cmd(0x4D, tree.children[0], meta=tree.meta)
def suspend_stmt(self, tree):
commands = []
for opcodes, expr in pairs(tree.children):
if not "suspend" in opcodes:
raise CompileError(f"`suspend {opcodes['__control_type__']}' not supported", meta=tree.meta)
commands.append(Cmd(opcodes["suspend"], expr, meta=tree.meta))
return commands
def resume_stmt(self, tree):
commands = []
for opcodes, expr in pairs(tree.children):
if not "resume" in opcodes:
raise CompileError(f"`resume {opcodes['__control_type__']}' not supported", meta=tree.meta)
commands.append(Cmd(opcodes["resume"], expr, meta=tree.meta))
return commands
def kill_stmt(self, tree):
commands = []
for opcodes, expr in pairs(tree.children):
if not "kill" in opcodes:
raise CompileError(f"`kill {opcodes['__control_type__']}' not supported", meta=tree.meta)
commands.append(Cmd(opcodes["kill"], expr, meta=tree.meta))
return commands
def control_type_group(self, tree):
return {
"__control_type__": "group",
"suspend": 0x4F,
"resume": 0x50,
}
def control_type_others(self, tree):
return {
"__control_type__": "others",
"suspend": 0x51,
"resume": 0x52,
}
def control_type_script(self, tree):
return {
"__control_type__": "script",
"suspend": 0x53,
"resume": 0x54,
"kill": 0x49,
}
def sleep_stmt(self, tree):
return Cmd(0x08, tree.children[0], meta=tree.meta)
def bind_stmt(self, tree):
script, trigger, target = tree.children
return Cmd(0x47, script, trigger, target, 1, 0, meta=tree.meta)
def bind_set_stmt(self, tree):
ret, script, trigger, target = tree.children
return Cmd(0x47, script, trigger, target, 1, ret, meta=tree.meta)
def unbind_stmt(self, tree):
return Cmd(0x48, meta=tree.meta)
def spawn_stmt(self, tree):
return Cmd(0x44, tree.children[0], meta=tree.meta)
def spawn_set_stmt(self, tree):
lhs, script = tree.children
return Cmd(0x45, script, lhs, meta=tree.meta)
def await_stmt(self, tree):
return Cmd(0x46, tree.children[0], meta=tree.meta)
def set_stmt(self, tree):
lhs, opcodes, rhs = tree.children
if is_fixed_var(rhs):
opcode = opcodes.get("float", None)
if not opcode:
raise CompileError(f"operation `{opcodes['__op__']}' not supported for floats", tree.meta)
else:
opcode = opcodes.get("int", None)
if not opcode:
raise CompileError(f"operation `{opcodes['__op__']}' not supported for ints", tree.meta)
return Cmd(opcode, lhs, rhs)
def set_const_stmt(self, tree):
lhs, rhs = tree.children
return Cmd(0x25, lhs, rhs)
def set_op_eq(self, tree):
return {
"__op__": "=",
"int": 0x24,
"float": 0x26,
}
def set_op_add(self, tree):
return {
"__op__": "+",
"int": 0x27,
"float": 0x2C,
}
def set_op_sub(self, tree):
return {
"__op__": "-",
"int": 0x28,
"float": 0x2D,
}
def set_op_mul(self, tree):
return {
"__op__": "*",
"int": 0x29,
"float": 0x2E,
}
def set_op_div(self, tree):
return {
"__op__": "/",
"int": 0x2A,
"float": 0x2F,
}
def set_op_mod(self, tree):
return {
"__op__": "%",
"int": 0x2B,
}
def label_decl(self, tree):
label = tree.children[0]
return Cmd(0x03, label, meta=tree.meta)
def label_goto(self, tree):
label = tree.children[0]
return Cmd(0x04, label, meta=tree.meta)
def label(self, tree):
name = tree.children[0]
if name in self.alloc.labels:
return self.alloc.labels.index(name)
raise CompileError(f"label `{name}' is undeclared", tree.meta)
#define SI_SET_CONST(var, value) SI_CMD(0x25, var, value) // Does not get_variable
#define SI_SUB(a, b) SI_CMD(0x28, a, b) // -=
#define SI_MUL(a, b) SI_CMD(0x29, a, b) // *=
#define SI_DIV(a, b) SI_CMD(0x2A, a, b) // /=
#define SI_MOD(a, b) SI_CMD(0x2B, a, b) // %=
#define SI_SET_F(var, value) SI_CMD(0x26, var, value)
#define SI_SUB_F(a, b) SI_CMD(0x2D, a, b) // -=
#define SI_MUL_F(a, b) SI_CMD(0x2E, a, b) // *=
#define SI_DIV_F(a, b) SI_CMD(0x2F, a, b) // /=
def compile_script(s):
tree = script_parser.parse(s)
#eprint(tree.pretty())
return Compile().transform(tree) + si_cmd(0x02) + si_cmd(0x01)
commands = Compile().transform(tree)
for cmd in commands:
if not isinstance(cmd, Cmd):
raise Exception(f"uncompiled {cmd}")
# add RETURN END if no explicit END (top-level `break') was given
if next((cmd for cmd in commands if cmd.opcode == 0x01), None) == None:
commands += (Cmd(0x02), Cmd(0x01))
return commands
def read_until_closing_paren(depth=1, lex_strings=False):
text = ""
@ -155,6 +430,24 @@ def read_line():
return line
def gen_line_map(source, source_line_no = 1):
line_map = {}
output = ""
output_line_no = 1
for line in source.splitlines(True):
if line[0] == "#":
parts = line[2:-1].split(" ")
source_line_no = int(parts[0])
else:
line_map[output_line_no] = source_line_no
output += line
output_line_no += 1
source_line_no += 1
return output, line_map
# Expects output from C preprocessor on stdin
if __name__ == "__main__":
line_no = 1
@ -173,9 +466,6 @@ if __name__ == "__main__":
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 == ""):
@ -188,25 +478,58 @@ if __name__ == "__main__":
write("#" + line + "\n")
elif char == "(":
filename = file_info[0][1:-1]
# SCRIPT(...)
if macro_name == "SCRIPT":
script_source = read_until_closing_paren(lex_strings=True)
script_source, line_map = gen_line_map(read_until_closing_paren(lex_strings=True), source_line_no=line_no)
try:
bytecode = compile_script(script_source)
commands = compile_script(script_source)
write("{")
for word in bytecode:
write(f"{word},")
write("{\n")
for command in commands:
if command.meta:
write(f"# {line_map[command.meta.line]} {file_info[0]}\n")
write(" ")
for word in command.to_bytecode():
if type(word) == str:
write(word)
elif type(word) == int:
write(f"0x{word & 0xFFFFFFFF:X}")
else:
raise Exception(f"uncompiled: {command}")
write(", ")
write("\n")
write("}")
except exceptions.UnexpectedToken as e:
line = e.line + line_no
filename = file_info[0][1:-1]
except exceptions.UnexpectedEOF as e:
eprint(f"{filename}:{line_no}: {ANSI_RED}error{ANSI_RESET}: unterminated SCRIPT(...) macro")
error = True
except exceptions.UnexpectedCharacters as e:
eprint(e.line)
line = line_map[e.line]
char = script_source[e.pos_in_stream]
allowed = e.allowed
eprint(f"{filename}:{line}: {ANSI_RED}script parse error{ANSI_RESET}: unexpected `{char}', expected {' or '.join(allowed)}")
eprint(e.get_context(script_source))
eprint(f"{filename}:{line}: script parse error: unexpected `{e.token}'")
error = True
except exceptions.UnexpectedToken as e:
line = line_map[e.line]
eprint(f"{filename}:{line}: {ANSI_RED}script parse error{ANSI_RESET}: unexpected `{e.token}'")
eprint(e.get_context(script_source))
error = True
except exceptions.VisitError as e:
if type(e.orig_exc) == CompileError:
line = line_map[e.orig_exc.meta.line]
eprint(f"{filename}:{line}: {ANSI_RED}script compile error{ANSI_RESET}: {e.orig_exc}")
else:
eprint(f"{filename}:{line_no}: {ANSI_RED}internal script compilation error{ANSI_RESET}")
traceback.print_exc()
error = True
line_no += script_source.count("\n")
write(f"\n# {line_no} {file_info[0]}\n")