renpy-parser: add support for May 24 syntax
This commit is contained in:
parent
e3ffcce26f
commit
9112e74b8b
@ -30,6 +30,9 @@ namespace language {
|
||||
show,
|
||||
voice,
|
||||
with,
|
||||
stop,
|
||||
pause,
|
||||
hide,
|
||||
};
|
||||
|
||||
struct jump {
|
||||
@ -70,6 +73,18 @@ namespace language {
|
||||
struct with {
|
||||
};
|
||||
|
||||
struct stop {
|
||||
uint32_t channelIndex;
|
||||
};
|
||||
|
||||
struct pause {
|
||||
float duration;
|
||||
};
|
||||
|
||||
struct hide {
|
||||
uint32_t imageIndex;
|
||||
};
|
||||
|
||||
struct statement {
|
||||
enum type type;
|
||||
union {
|
||||
|
||||
@ -45,6 +45,11 @@ class TT(Enum):
|
||||
RETURN = auto()
|
||||
INIT = auto()
|
||||
FADEOUT = auto()
|
||||
TRANSFORM = auto()
|
||||
STOP = auto()
|
||||
NOLOOP = auto()
|
||||
PAUSE = auto()
|
||||
HIDE = auto()
|
||||
|
||||
keywords = {
|
||||
b"play": TT.PLAY,
|
||||
@ -61,6 +66,11 @@ keywords = {
|
||||
b"return": TT.RETURN,
|
||||
b"init": TT.INIT,
|
||||
b"fadeout": TT.FADEOUT,
|
||||
b"transform": TT.TRANSFORM,
|
||||
b"stop": TT.STOP,
|
||||
b"noloop": TT.NOLOOP,
|
||||
b"pause": TT.PAUSE,
|
||||
b"hide": TT.HIDE,
|
||||
}
|
||||
|
||||
@dataclass
|
||||
|
||||
@ -56,6 +56,14 @@ class Play:
|
||||
channel: lex.Token
|
||||
path: lex.Token
|
||||
fadeout: lex.Token
|
||||
noloop: bool
|
||||
|
||||
__repr__ = lexeme_repr
|
||||
|
||||
@dataclass
|
||||
class Stop:
|
||||
channel: lex.Token
|
||||
fadeout: lex.Token
|
||||
|
||||
__repr__ = lexeme_repr
|
||||
|
||||
@ -88,6 +96,7 @@ class Voice:
|
||||
class Show:
|
||||
what: lex.Token
|
||||
transform: lex.Token
|
||||
properties: list[tuple[lex.Token, lex.Token]]
|
||||
|
||||
__repr__ = lexeme_repr
|
||||
|
||||
@ -102,6 +111,18 @@ class Jump:
|
||||
|
||||
__repr__ = lexeme_repr
|
||||
|
||||
@dataclass
|
||||
class Pause:
|
||||
duration: lex.Token
|
||||
|
||||
__repr__ = lexeme_repr
|
||||
|
||||
@dataclass
|
||||
class Hide:
|
||||
what: lex.Token
|
||||
|
||||
__repr__ = lexeme_repr
|
||||
|
||||
@dataclass
|
||||
class Return:
|
||||
pass
|
||||
@ -242,11 +263,16 @@ def parse_play(tokens, index):
|
||||
if fadeout.type != TT.NUMBER:
|
||||
raise ParseException("expected number", fadeout)
|
||||
index += 2
|
||||
noloop = False
|
||||
if token.type == TT.NOLOOP:
|
||||
noloop = True
|
||||
index += 1
|
||||
|
||||
play = Play(
|
||||
channel = channel,
|
||||
path = path,
|
||||
fadeout = fadeout
|
||||
fadeout = fadeout,
|
||||
noloop = noloop,
|
||||
)
|
||||
return index, play
|
||||
|
||||
@ -290,6 +316,12 @@ def parse_voice(tokens, index):
|
||||
return index + 1, voice
|
||||
|
||||
def parse_show(tokens, index):
|
||||
show = tokens[index + 0]
|
||||
if show.type != TT.SHOW:
|
||||
raise ParseException("expected show", show)
|
||||
|
||||
index += 1
|
||||
|
||||
index, what = parse_lhs(tokens, index)
|
||||
|
||||
at = tokens[index + 0]
|
||||
@ -300,11 +332,36 @@ def parse_show(tokens, index):
|
||||
if transform.type != TT.IDENTIFIER:
|
||||
raise ParseException("expected identifier", transform)
|
||||
|
||||
index += 2
|
||||
|
||||
properties = []
|
||||
if tokens[index + 0].type == TT.COLON:
|
||||
index += 1
|
||||
while index < len(tokens):
|
||||
token = tokens[index + 0]
|
||||
if token.type == TT.NEWLINE:
|
||||
index += 1
|
||||
continue
|
||||
|
||||
if token.position.column <= show.position.column:
|
||||
break
|
||||
|
||||
if token.type != TT.IDENTIFIER:
|
||||
raise ParseException("expected identifier")
|
||||
|
||||
number = tokens[index + 1]
|
||||
if number.type != TT.NUMBER:
|
||||
raise ParseException("expected number")
|
||||
|
||||
properties.append((token, number))
|
||||
index += 2
|
||||
|
||||
show = Show(
|
||||
what = what,
|
||||
transform = transform
|
||||
transform = transform,
|
||||
properties = properties
|
||||
)
|
||||
return index + 2, show
|
||||
return index, show
|
||||
|
||||
def parse_menu(tokens, index):
|
||||
menu = tokens[index + 0]
|
||||
@ -369,7 +426,7 @@ def parse_init(tokens, index):
|
||||
|
||||
colon = tokens[index + 1]
|
||||
if colon.type != TT.COLON:
|
||||
raise ParseException("expected identifier", colon)
|
||||
raise ParseException("expected colon", colon)
|
||||
|
||||
index += 2
|
||||
|
||||
@ -388,6 +445,74 @@ def parse_init(tokens, index):
|
||||
|
||||
return index, None
|
||||
|
||||
def parse_transform(tokens, index):
|
||||
transform = tokens[index + 0]
|
||||
if transform.type != TT.TRANSFORM:
|
||||
raise ParseException("expected transform", init)
|
||||
|
||||
identifier = tokens[index + 1]
|
||||
if identifier.type != TT.IDENTIFIER:
|
||||
raise ParseException("expected identifier", identifier)
|
||||
|
||||
colon = tokens[index + 2]
|
||||
if colon.type != TT.COLON:
|
||||
raise ParseException("expected colon", colon)
|
||||
|
||||
index += 3
|
||||
|
||||
# skip all tokens inside block
|
||||
while index < len(tokens):
|
||||
token = tokens[index]
|
||||
if token.type == TT.NEWLINE:
|
||||
index += 1
|
||||
continue
|
||||
|
||||
if token.position.column < transform.position.column:
|
||||
raise ParseException("invalid init block dedent", token)
|
||||
if token.position.column == transform.position.column:
|
||||
break
|
||||
index += 1
|
||||
|
||||
return index, None
|
||||
|
||||
def parse_stop(tokens, index):
|
||||
channel = tokens[index + 0]
|
||||
if channel.type != TT.IDENTIFIER:
|
||||
raise ParseException("expected identifier", channel)
|
||||
|
||||
index += 1
|
||||
token = tokens[index]
|
||||
fadeout = None
|
||||
if token.type == TT.FADEOUT:
|
||||
fadeout = tokens[index + 1]
|
||||
if fadeout.type != TT.NUMBER:
|
||||
raise ParseException("expected number", fadeout)
|
||||
index += 2
|
||||
|
||||
stop = Stop(
|
||||
channel = channel,
|
||||
fadeout = fadeout
|
||||
)
|
||||
return index, stop
|
||||
|
||||
def parse_pause(tokens, index):
|
||||
duration = tokens[index + 0]
|
||||
if duration.type != TT.NUMBER:
|
||||
raise ParseException("expected number", duration)
|
||||
|
||||
pause = Pause(
|
||||
duration = duration
|
||||
)
|
||||
return index + 1, pause
|
||||
|
||||
def parse_hide(tokens, index):
|
||||
index, what = parse_lhs(tokens, index)
|
||||
|
||||
hide = Hide(
|
||||
what = what
|
||||
)
|
||||
return index + 1, hide
|
||||
|
||||
def parse_one(tokens, index):
|
||||
token = tokens[index]
|
||||
if token.type == TT.NEWLINE:
|
||||
@ -421,7 +546,7 @@ def parse_one(tokens, index):
|
||||
index, ast = parse_voice(tokens, index + 1)
|
||||
return index, ast
|
||||
elif token.type == TT.SHOW:
|
||||
index, ast = parse_show(tokens, index + 1)
|
||||
index, ast = parse_show(tokens, index)
|
||||
return index, ast
|
||||
elif token.type == TT.MENU:
|
||||
index, ast = parse_menu(tokens, index)
|
||||
@ -434,6 +559,18 @@ def parse_one(tokens, index):
|
||||
elif token.type == TT.INIT:
|
||||
index, ast = parse_init(tokens, index)
|
||||
return index, ast
|
||||
elif token.type == TT.TRANSFORM:
|
||||
index, ast = parse_transform(tokens, index)
|
||||
return index, ast
|
||||
elif token.type == TT.STOP:
|
||||
index, ast = parse_stop(tokens, index + 1)
|
||||
return index, ast
|
||||
elif token.type == TT.PAUSE:
|
||||
index, ast = parse_pause(tokens, index + 1)
|
||||
return index, ast
|
||||
elif token.type == TT.HIDE:
|
||||
index, ast = parse_hide(tokens, index + 1)
|
||||
return index, ast
|
||||
else:
|
||||
raise ParseException("unexpected token", token)
|
||||
|
||||
|
||||
@ -43,6 +43,9 @@ simple_statement_types = {
|
||||
parse.Show,
|
||||
parse.Voice,
|
||||
parse.With,
|
||||
parse.Stop,
|
||||
parse.Pause,
|
||||
parse.Hide,
|
||||
}
|
||||
|
||||
def pass1(state, ast):
|
||||
@ -111,7 +114,7 @@ def pass2_statement(state, pc, statement):
|
||||
if type(statement) is parse.Play:
|
||||
comment = statement.path.lexeme.decode('utf-8')
|
||||
audio_index = state.audio_lookup[statement.path.lexeme]
|
||||
yield f"{{ .type = type::play, .play = {{ .audioIndex = {audio_index} }} }}, // {pc} {comment}"
|
||||
yield f"{{ .type = type::play, .play = {{ .audioIndex = {audio_index}, /* FIXME channel */ }} }}, // {pc} {comment}"
|
||||
elif type(statement) is parse.Scene:
|
||||
key = lhs_key(statement.name)
|
||||
image_index = state.images_lookup[key]
|
||||
@ -152,6 +155,16 @@ def pass2_statement(state, pc, statement):
|
||||
yield f"{{ .type = type::jump, .jump = {{ .statementIndex = {statement_index} }} }}, // {pc} {comment}"
|
||||
elif type(statement) is parse.Return:
|
||||
yield f"{{ .type = type::_return }}, // {pc}"
|
||||
elif type(statement) is parse.Stop:
|
||||
yield f"{{ .type = type::stop, .stop = {{ /* FIXME channel */ }} }}, // {pc}"
|
||||
elif type(statement) is parse.Pause:
|
||||
duration = statement.duration.lexeme
|
||||
yield f"{{ .type = type::pause, .pause = {{ .duration = {duration} }} }}, // {pc}"
|
||||
elif type(statement) is parse.Hide:
|
||||
key = lhs_key(statement.what)
|
||||
image_index = state.images_lookup[key]
|
||||
comment = ".".join(k.decode('utf-8') for k in key)
|
||||
yield f"{{ .type = type::hide, .hide = {{ .imageIndex = {image_index} }} }}, // {pc} {comment}"
|
||||
else:
|
||||
pass
|
||||
assert False, (type(statement), statement)
|
||||
@ -161,12 +174,14 @@ def pass2_statements(state):
|
||||
for pc, statement in enumerate(state.statements):
|
||||
yield from pass2_statement(state, pc, statement)
|
||||
yield "};"
|
||||
yield "constexpr int statements_length = (sizeof (statements)) / (sizeof (statements[0]));"
|
||||
|
||||
def pass2_strings(state):
|
||||
yield "char const * const strings[] = {"
|
||||
for string, i in sorted(state.string_lookup.items(), key=lambda kv: kv[1]):
|
||||
yield f"\"{string.decode('utf-8')}\", // {i}"
|
||||
yield "};"
|
||||
yield "constexpr int strings_length = (sizeof (strings)) / (sizeof (strings[0]));"
|
||||
|
||||
def pass2_characters(state):
|
||||
yield "const character characters[] = {"
|
||||
@ -174,24 +189,42 @@ def pass2_characters(state):
|
||||
character_name, = character.value.args
|
||||
yield f"{{ .characterName = \"{character_name.lexeme.decode('utf-8')}\" }}, // {i}"
|
||||
yield "};"
|
||||
yield "constexpr int characters_length = (sizeof (characters)) / (sizeof (characters[0]));"
|
||||
|
||||
def pass2_audio(state):
|
||||
yield "const audio audio[] = {"
|
||||
for audio, i in sorted(state.audio_lookup.items(), key=lambda kv: kv[1]):
|
||||
yield f"{{ .path = \"{audio.decode('utf-8')}\" }}, // {i}"
|
||||
orig_path = audio.decode('utf-8')
|
||||
path = orig_path
|
||||
if path.endswith(".mp3"):
|
||||
path = path.removesuffix(".mp3")
|
||||
elif path.endswith(".ogg"):
|
||||
path = path.removesuffix(".ogg")
|
||||
else:
|
||||
assert False, path
|
||||
yield f"{{ .path = \"{path}.opus\" }}, // {i} {orig_path}"
|
||||
yield "};"
|
||||
yield "constexpr int audio_length = (sizeof (audio)) / (sizeof (audio[0]));"
|
||||
|
||||
def pass2_images(state):
|
||||
yield "const image image[] = {"
|
||||
yield "const image images[] = {"
|
||||
for i, image in enumerate(state.images):
|
||||
yield f"{{ .path = \"{image.path.lexeme.decode('utf-8')}\" }}, // {i}"
|
||||
orig_path = image.path.lexeme.decode('utf-8')
|
||||
path = orig_path
|
||||
if path.endswith(".png"):
|
||||
path = path.removesuffix(".png")
|
||||
else:
|
||||
assert False, path
|
||||
yield f"{{ .path = \"{path}.dds\" }}, // {i} {orig_path}"
|
||||
yield "};"
|
||||
yield "constexpr int images_length = (sizeof (images)) / (sizeof (images[0]));"
|
||||
|
||||
def pass2_options(state):
|
||||
yield "const option options[] = {"
|
||||
for i, (lexeme, statement_index) in sorted(state.entries.items(), key=lambda kv: kv[0]):
|
||||
yield f"{{ .string = \"{lexeme.decode('utf-8')}\", .statementIndex = {statement_index} }}, // {i}"
|
||||
yield "};"
|
||||
yield "constexpr int options_length = (sizeof (options)) / (sizeof (options[0]));"
|
||||
|
||||
def pass2(state):
|
||||
yield "#include \"statement.h\""
|
||||
@ -224,9 +257,11 @@ def main():
|
||||
global_identifiers = set(),
|
||||
)
|
||||
try:
|
||||
ast_list = list(parse.parse_all(tokens))
|
||||
ast_list = []
|
||||
for ast in parse.parse_all(tokens):
|
||||
ast_list.append(ast)
|
||||
except parse.ParseException as e:
|
||||
print(e, e.token)
|
||||
print(e, e.token, file=sys.stderr)
|
||||
raise
|
||||
|
||||
for t in ast_list:
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user