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