initial renpy parser
This commit is contained in:
parent
bb6f76cf72
commit
610aff4af6
57
renpy-parser/language/statement.h
Normal file
57
renpy-parser/language/statement.h
Normal file
@ -0,0 +1,57 @@
|
||||
#include <stdint.h>
|
||||
|
||||
namespace language::statement {
|
||||
enum type {
|
||||
show,
|
||||
voice,
|
||||
music,
|
||||
text,
|
||||
menu,
|
||||
jump,
|
||||
};
|
||||
|
||||
struct show {
|
||||
uint32_t imageIndex;
|
||||
uint32_t transformIndex;
|
||||
};
|
||||
|
||||
struct voice {
|
||||
uint32_t audioIndex;
|
||||
};
|
||||
|
||||
struct music {
|
||||
uint32_t channelIndex;
|
||||
uint32_t audioIndex;
|
||||
};
|
||||
|
||||
struct say {
|
||||
uint32_t characterIndex;
|
||||
uint32_t stringIndex;
|
||||
};
|
||||
|
||||
struct option {
|
||||
uint32_t stringIndex;
|
||||
uint32_t statementIndex;
|
||||
};
|
||||
|
||||
struct menu {
|
||||
uint32_t count;
|
||||
uint32_t optionIndex;
|
||||
};
|
||||
|
||||
struct jump {
|
||||
uint32_t statementIndex;
|
||||
};
|
||||
|
||||
struct statement {
|
||||
enum statement_type type;
|
||||
union {
|
||||
show show;
|
||||
voice voice;
|
||||
music music;
|
||||
say say;
|
||||
menu menu;
|
||||
jump jump;
|
||||
};
|
||||
};
|
||||
}
|
||||
231
renpy-parser/lex.py
Normal file
231
renpy-parser/lex.py
Normal file
@ -0,0 +1,231 @@
|
||||
import string
|
||||
from dataclasses import dataclass
|
||||
from enum import Enum, auto
|
||||
import sys
|
||||
|
||||
whitespace = set([ord(' '), ord('\r')])
|
||||
string_digits = set(ord(i) for i in string.digits)
|
||||
identifier_start = set(map(ord, string.ascii_letters + "_"))
|
||||
identifier = identifier_start | string_digits
|
||||
|
||||
@dataclass
|
||||
class Position:
|
||||
offset: int
|
||||
line: int
|
||||
column: int
|
||||
|
||||
def from_position(p):
|
||||
return Position(p.offset, p.line, p.column)
|
||||
|
||||
class TT(Enum):
|
||||
KEYWORD = auto()
|
||||
NEWLINE = auto()
|
||||
COLON = auto()
|
||||
STRING = auto()
|
||||
DOT = auto()
|
||||
EQUAL = auto()
|
||||
COMMA = auto()
|
||||
LPAREN = auto()
|
||||
RPAREN = auto()
|
||||
NUMBER = auto()
|
||||
IDENTIFIER = auto()
|
||||
WITH = auto()
|
||||
|
||||
# keywords
|
||||
PLAY = auto()
|
||||
VOICE = auto()
|
||||
SCENE = auto()
|
||||
SHOW = auto()
|
||||
AT = auto()
|
||||
DEFINE = auto()
|
||||
IMAGE = auto()
|
||||
LABEL = auto()
|
||||
MENU = auto()
|
||||
JUMP = auto()
|
||||
RETURN = auto()
|
||||
INIT = auto()
|
||||
FADEOUT = auto()
|
||||
|
||||
keywords = {
|
||||
b"play": TT.PLAY,
|
||||
b"voice": TT.VOICE,
|
||||
b"scene": TT.SCENE,
|
||||
b"show": TT.SHOW,
|
||||
b"at": TT.AT,
|
||||
b"define": TT.DEFINE,
|
||||
b"image": TT.IMAGE,
|
||||
b"label": TT.LABEL,
|
||||
b"with": TT.WITH,
|
||||
b"menu": TT.MENU,
|
||||
b"jump": TT.JUMP,
|
||||
b"return": TT.RETURN,
|
||||
b"init": TT.INIT,
|
||||
b"fadeout": TT.FADEOUT,
|
||||
}
|
||||
|
||||
@dataclass
|
||||
class Token:
|
||||
position: Position
|
||||
lexeme: bytes
|
||||
type: TT
|
||||
|
||||
def parse_string(mem, position):
|
||||
offset = position.offset
|
||||
assert mem[offset] == ord('"'), (position, chr(mem[offset]))
|
||||
offset += 1
|
||||
start = offset
|
||||
while mem[offset] != ord('"'):
|
||||
assert mem[offset] != ord("\n")
|
||||
offset += 1
|
||||
assert mem[offset] == ord('"'), position
|
||||
s = bytes(mem[start:offset])
|
||||
token = Token(
|
||||
position = position,
|
||||
lexeme = s,
|
||||
type = TT.STRING
|
||||
)
|
||||
next_position = Position(
|
||||
offset = offset + 1,
|
||||
line = position.line,
|
||||
column = position.column + 2 + len(s)
|
||||
)
|
||||
return next_position, token
|
||||
|
||||
def parse_number(mem, position):
|
||||
offset = position.offset
|
||||
whole = []
|
||||
fraction = []
|
||||
sign = 1
|
||||
if mem[offset] == ord('-'):
|
||||
sign = -1
|
||||
offset += 1
|
||||
# whole
|
||||
while True:
|
||||
c = mem[offset]
|
||||
if c in string_digits:
|
||||
whole.append(c)
|
||||
offset += 1
|
||||
elif c == ord('.'):
|
||||
assert whole != [], chr(c)
|
||||
offset += 1
|
||||
break
|
||||
else:
|
||||
assert whole != [], chr(c)
|
||||
number = sign * int(bytes(whole))
|
||||
next_position = Position(
|
||||
offset = offset,
|
||||
line = position.line,
|
||||
column = position.column + 1 + (offset - position.offset)
|
||||
)
|
||||
return next_position, Token(position, number, TT.NUMBER)
|
||||
# fraction
|
||||
while True:
|
||||
c = mem[offset]
|
||||
if c in string_digits:
|
||||
fraction.append(c)
|
||||
offset += 1
|
||||
else:
|
||||
assert fraction != [], chr(c)
|
||||
w = int(bytes(whole))
|
||||
f = int(bytes(fraction)) / (10 ** len(fraction))
|
||||
number = sign * (w + f)
|
||||
next_position = Position(
|
||||
offset = offset,
|
||||
line = position.line,
|
||||
column = position.column + 1 + (offset - position.offset)
|
||||
)
|
||||
return next_position, Token(position, number, TT.NUMBER)
|
||||
|
||||
def disambiguate_keyword(lexeme):
|
||||
if lexeme in keywords:
|
||||
return keywords[lexeme]
|
||||
else:
|
||||
return TT.IDENTIFIER
|
||||
|
||||
def parse_identifier(mem, position):
|
||||
offset = position.offset
|
||||
l = []
|
||||
while True:
|
||||
c = mem[offset]
|
||||
if c in identifier:
|
||||
l.append(c)
|
||||
offset += 1
|
||||
else:
|
||||
assert l != []
|
||||
lexeme = bytes(l)
|
||||
next_position = Position(
|
||||
offset = offset,
|
||||
line = position.line,
|
||||
column = position.column + 1 + len(lexeme)
|
||||
)
|
||||
token_type = disambiguate_keyword(lexeme)
|
||||
return next_position, Token(position, lexeme, token_type)
|
||||
|
||||
def next_token(mem, position):
|
||||
position = Position.from_position(position)
|
||||
while True:
|
||||
if position.offset >= len(mem):
|
||||
return position, None
|
||||
c = mem[position.offset]
|
||||
|
||||
if c in whitespace:
|
||||
position.offset += 1
|
||||
position.column += 1
|
||||
else:
|
||||
break
|
||||
|
||||
next_position = Position.from_position(position)
|
||||
next_position.offset += 1
|
||||
next_position.column += 1
|
||||
|
||||
if c >= 128:
|
||||
print(f"warning: invalid garbage byte {hex(c)} at {position}")
|
||||
next_position.column = position.column
|
||||
return next_token(mem, next_position)
|
||||
if c == ord('\n'):
|
||||
next_position.line += 1
|
||||
next_position.column = 0
|
||||
return next_position, Token(position, b'\n', TT.NEWLINE)
|
||||
if c == ord(':'):
|
||||
return next_position, Token(position, b':', TT.COLON)
|
||||
if c == ord('.'):
|
||||
return next_position, Token(position, b':', TT.DOT)
|
||||
if c == ord('='):
|
||||
return next_position, Token(position, b'=', TT.EQUAL)
|
||||
if c == ord(','):
|
||||
return next_position, Token(position, b',', TT.COMMA)
|
||||
if c == ord('('):
|
||||
return next_position, Token(position, b'(', TT.LPAREN)
|
||||
if c == ord(')'):
|
||||
return next_position, Token(position, b')', TT.RPAREN)
|
||||
if c == ord('"'):
|
||||
return parse_string(mem, position)
|
||||
if c == ord('#'):
|
||||
offset = position.offset + 1
|
||||
while mem[offset] != ord('\n'):
|
||||
offset += 1
|
||||
return next_token(mem, Position(offset + 1, position.line + 1, 0))
|
||||
if c in string_digits or c == ord('-'):
|
||||
return parse_number(mem, position)
|
||||
if c in identifier_start:
|
||||
return parse_identifier(mem, position)
|
||||
assert not "unexpected character", (position, chr(c), hex(c))
|
||||
|
||||
def tokenize(mem):
|
||||
position = Position(offset = 0, line = 1, column = 0)
|
||||
while True:
|
||||
position, token = next_token(mem, position)
|
||||
if token is None:
|
||||
return
|
||||
else:
|
||||
yield token
|
||||
|
||||
def main():
|
||||
with open(sys.argv[1], 'rb') as f:
|
||||
mem = memoryview(f.read())
|
||||
|
||||
for token in tokenize(mem):
|
||||
print(token.lexeme, token.type)
|
||||
|
||||
if __name__ == "__main__":
|
||||
main()
|
||||
475
renpy-parser/parse.py
Normal file
475
renpy-parser/parse.py
Normal file
@ -0,0 +1,475 @@
|
||||
import sys
|
||||
from pprint import pprint
|
||||
import lex
|
||||
from lex import TT
|
||||
from dataclasses import dataclass
|
||||
|
||||
class ParseException(Exception):
|
||||
def __init__(self, message, token):
|
||||
super().__init__(message)
|
||||
self.token = token
|
||||
|
||||
def get_lexeme(v):
|
||||
if type(v) is list:
|
||||
return list(get_lexeme(i) for i in v)
|
||||
elif type(v) is tuple:
|
||||
return tuple(get_lexeme(i) for i in v)
|
||||
elif type(v) is lex.Token:
|
||||
return v.lexeme
|
||||
else:
|
||||
return v
|
||||
|
||||
def lexeme_repr(self):
|
||||
kws = [f"{key}={get_lexeme(value)!r}" for key, value in self.__dict__.items()]
|
||||
return "{}({})".format(type(self).__name__, ", ".join(kws))
|
||||
|
||||
@dataclass
|
||||
class FunctionCall:
|
||||
name: lex.Token
|
||||
args: list[lex.Token]
|
||||
kwargs: list[tuple[lex.Token, lex.Token]]
|
||||
|
||||
__repr__ = lexeme_repr
|
||||
|
||||
@dataclass
|
||||
class Image:
|
||||
name: list[lex.Token]
|
||||
path: lex.Token
|
||||
|
||||
__repr__ = lexeme_repr
|
||||
|
||||
@dataclass
|
||||
class Define:
|
||||
name: list[lex.Token]
|
||||
value: 'Expression'
|
||||
|
||||
__repr__ = lexeme_repr
|
||||
|
||||
@dataclass
|
||||
class Label:
|
||||
name: lex.Token
|
||||
|
||||
__repr__ = lexeme_repr
|
||||
|
||||
@dataclass
|
||||
class Play:
|
||||
channel: lex.Token
|
||||
path: lex.Token
|
||||
fadeout: lex.Token
|
||||
|
||||
__repr__ = lexeme_repr
|
||||
|
||||
@dataclass
|
||||
class Scene:
|
||||
name: lex.Token
|
||||
|
||||
__repr__ = lexeme_repr
|
||||
|
||||
@dataclass
|
||||
class With:
|
||||
function_call: FunctionCall
|
||||
|
||||
__repr__ = lexeme_repr
|
||||
|
||||
@dataclass
|
||||
class Say:
|
||||
speaker: lex.Token
|
||||
text: lex.Token
|
||||
|
||||
__repr__ = lexeme_repr
|
||||
|
||||
@dataclass
|
||||
class Voice:
|
||||
path: lex.Token
|
||||
|
||||
__repr__ = lexeme_repr
|
||||
|
||||
@dataclass
|
||||
class Show:
|
||||
what: lex.Token
|
||||
transform: lex.Token
|
||||
|
||||
__repr__ = lexeme_repr
|
||||
|
||||
@dataclass
|
||||
class Menu:
|
||||
entries: tuple[lex.Token, list['Statement']]
|
||||
__repr__ = lexeme_repr
|
||||
|
||||
@dataclass
|
||||
class Jump:
|
||||
target: lex.Token
|
||||
|
||||
__repr__ = lexeme_repr
|
||||
|
||||
@dataclass
|
||||
class Return:
|
||||
pass
|
||||
|
||||
def parse_lhs(tokens, index):
|
||||
identifier = tokens[index]
|
||||
if identifier.type != TT.IDENTIFIER:
|
||||
raise ParseException("expected identifier", identifier)
|
||||
lhs = [identifier]
|
||||
index += 1
|
||||
while tokens[index].type == TT.DOT:
|
||||
identifier = tokens[index + 1]
|
||||
if identifier.type != TT.IDENTIFIER:
|
||||
raise ParseException("expected identifier", identifier)
|
||||
lhs.append(identifier)
|
||||
index += 2
|
||||
return index, lhs
|
||||
|
||||
def parse_image(tokens, index):
|
||||
index, lhs = parse_lhs(tokens, index)
|
||||
equal = tokens[index + 0]
|
||||
if equal.type != TT.EQUAL:
|
||||
raise ParseException("expected equal", equal)
|
||||
string = tokens[index + 1]
|
||||
if string.type != TT.STRING:
|
||||
raise ParseException("expected string", string)
|
||||
|
||||
image = Image(
|
||||
name = lhs,
|
||||
path = string,
|
||||
)
|
||||
return index + 2, image
|
||||
|
||||
def parse_function_call(tokens, index):
|
||||
name = tokens[index + 0]
|
||||
if name.type != TT.IDENTIFIER:
|
||||
raise ParseException("expected identifier", name)
|
||||
lparen = tokens[index + 1]
|
||||
if lparen.type != TT.LPAREN:
|
||||
raise ParseException("expected lparen", lparen)
|
||||
index += 2
|
||||
|
||||
# args
|
||||
args = []
|
||||
while tokens[index].type != TT.RPAREN:
|
||||
token = tokens[index]
|
||||
if token.type == TT.STRING:
|
||||
args.append(token)
|
||||
elif token.type == TT.NUMBER:
|
||||
args.append(token)
|
||||
elif token.type == TT.NEWLINE:
|
||||
pass
|
||||
else:
|
||||
break
|
||||
index += 1
|
||||
if tokens[index].type != TT.COMMA:
|
||||
break
|
||||
index += 1
|
||||
|
||||
# kwargs
|
||||
kwargs = []
|
||||
while tokens[index].type != TT.RPAREN:
|
||||
identifier = tokens[index + 0]
|
||||
if identifier.type != TT.IDENTIFIER:
|
||||
raise ParseException("expected function call kwargs identifier", identifier)
|
||||
equal = tokens[index + 1]
|
||||
if equal.type != TT.EQUAL:
|
||||
raise ParseException("expected function call kwargs equal", equal)
|
||||
string = tokens[index + 2]
|
||||
if string.type != TT.STRING:
|
||||
raise ParseException("expected function call kwargs string", string)
|
||||
kwargs.append((identifier, string))
|
||||
index += 3
|
||||
if tokens[index].type != TT.COMMA:
|
||||
break
|
||||
|
||||
rparen = tokens[index]
|
||||
if rparen.type != TT.RPAREN:
|
||||
raise ParseException("expected rparen", rparen)
|
||||
index += 1
|
||||
|
||||
function_call = FunctionCall(
|
||||
name = name,
|
||||
args = args,
|
||||
kwargs = kwargs
|
||||
)
|
||||
return index, function_call
|
||||
|
||||
def parse_rhs(tokens, index):
|
||||
token = tokens[index]
|
||||
peek = tokens[index+1]
|
||||
if token.type == TT.NUMBER:
|
||||
return index + 1, token
|
||||
elif token.type == TT.IDENTIFIER and peek.type == TT.LPAREN:
|
||||
return parse_function_call(tokens, index)
|
||||
else:
|
||||
raise ParseException("expected rhs expression", token)
|
||||
|
||||
def parse_define(tokens, index):
|
||||
index, lhs = parse_lhs(tokens, index)
|
||||
equal = tokens[index + 0]
|
||||
if equal.type != TT.EQUAL:
|
||||
raise ParseException("expected equal", equal)
|
||||
index, rhs = parse_rhs(tokens, index + 1)
|
||||
define = Define(
|
||||
name = lhs,
|
||||
value = rhs
|
||||
)
|
||||
return index, define
|
||||
|
||||
def parse_label(tokens, index):
|
||||
name = tokens[index + 0]
|
||||
if name.type != TT.IDENTIFIER:
|
||||
raise ParseException("expected identifier", name)
|
||||
|
||||
colon = tokens[index + 1]
|
||||
if colon.type != TT.COLON:
|
||||
raise ParseException("expected colon", colon)
|
||||
|
||||
label = Label(
|
||||
name = name
|
||||
)
|
||||
|
||||
return index + 2, label
|
||||
|
||||
def parse_play(tokens, index):
|
||||
channel = tokens[index + 0]
|
||||
if channel.type != TT.IDENTIFIER:
|
||||
raise ParseException("expected identifier", channel)
|
||||
|
||||
path = tokens[index + 1]
|
||||
if path.type != TT.STRING:
|
||||
raise ParseException("expected string", path)
|
||||
|
||||
index += 2
|
||||
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
|
||||
|
||||
play = Play(
|
||||
channel = channel,
|
||||
path = path,
|
||||
fadeout = fadeout
|
||||
)
|
||||
return index, play
|
||||
|
||||
def parse_scene(tokens, index):
|
||||
name = tokens[index + 0]
|
||||
if name.type != TT.IDENTIFIER:
|
||||
raise ParseException("expected identifier", name)
|
||||
|
||||
scene = Scene(
|
||||
name = name,
|
||||
)
|
||||
return index + 1, scene
|
||||
|
||||
def parse_with(tokens, index):
|
||||
index, function_call = parse_function_call(tokens, index)
|
||||
|
||||
_with = With(
|
||||
function_call = function_call
|
||||
)
|
||||
|
||||
return index, _with
|
||||
|
||||
def parse_say(tokens, index):
|
||||
speaker = tokens[index + 0]
|
||||
if speaker.type != TT.IDENTIFIER:
|
||||
raise ParseException("expected identifier", name)
|
||||
|
||||
text = tokens[index + 1]
|
||||
if text.type != TT.STRING:
|
||||
raise ParseException("expected string", text)
|
||||
|
||||
say = Say(
|
||||
speaker = speaker,
|
||||
text = text
|
||||
)
|
||||
|
||||
return index + 2, say
|
||||
|
||||
def parse_voice(tokens, index):
|
||||
path = tokens[index]
|
||||
if path.type != TT.STRING:
|
||||
raise ParseException("expected string", path)
|
||||
|
||||
voice = Voice(
|
||||
path = path,
|
||||
)
|
||||
return index + 1, voice
|
||||
|
||||
def parse_show(tokens, index):
|
||||
what = tokens[index + 0]
|
||||
if what.type != TT.IDENTIFIER:
|
||||
raise ParseException("expected identifier", path)
|
||||
|
||||
at = tokens[index + 1]
|
||||
if at.type != TT.AT:
|
||||
raise ParseException("expected at", at)
|
||||
|
||||
transform = tokens[index + 2]
|
||||
if transform.type != TT.IDENTIFIER:
|
||||
raise ParseException("expected identifier", transform)
|
||||
|
||||
show = Show(
|
||||
what = what,
|
||||
transform = transform
|
||||
)
|
||||
return index + 3, show
|
||||
|
||||
def parse_menu(tokens, index):
|
||||
menu = tokens[index + 0]
|
||||
if menu.type != TT.MENU:
|
||||
raise ParseException("expected menu", menu)
|
||||
|
||||
colon = tokens[index + 1]
|
||||
if colon.type != TT.COLON:
|
||||
raise ParseException("expected colon", colon)
|
||||
|
||||
index = index + 2
|
||||
menu_entries = []
|
||||
menu_entry_string = None
|
||||
menu_entry_statements = None
|
||||
while index < len(tokens):
|
||||
token = tokens[index+0]
|
||||
if token.type == TT.NEWLINE:
|
||||
index += 1
|
||||
continue
|
||||
peek = tokens[index+1]
|
||||
|
||||
if token.position.column < menu.position.column:
|
||||
raise ParseException("invalid block dedent", token)
|
||||
if token.position.column == menu.position.column:
|
||||
break
|
||||
|
||||
if token.type == TT.STRING:
|
||||
if peek.type != TT.COLON:
|
||||
raise ParseException("expected colon", peek)
|
||||
if menu_entry_string is not None:
|
||||
menu_entries.append((menu_entry_string, menu_entry_statements))
|
||||
menu_entry_string = token
|
||||
menu_entry_statements = []
|
||||
index += 2
|
||||
else:
|
||||
if menu_entry_statements is None:
|
||||
raise ParseException("expected menu option", token)
|
||||
|
||||
index, ast = parse_one(tokens, index)
|
||||
if ast is not None:
|
||||
menu_entry_statements.append(ast)
|
||||
|
||||
if menu_entry_string is not None:
|
||||
menu_entries.append((menu_entry_string, menu_entry_statements))
|
||||
|
||||
menu = Menu(
|
||||
entries = menu_entries,
|
||||
)
|
||||
|
||||
return index, menu
|
||||
|
||||
def parse_jump(tokens, index):
|
||||
target = tokens[index + 0]
|
||||
if target.type != TT.IDENTIFIER:
|
||||
raise ParseException("expected identifier", target)
|
||||
|
||||
jump = Jump(
|
||||
target = target,
|
||||
)
|
||||
return index + 1, jump
|
||||
|
||||
def parse_init(tokens, index):
|
||||
init = tokens[index + 0]
|
||||
if init.type != TT.INIT:
|
||||
raise ParseException("expected init", init)
|
||||
|
||||
colon = tokens[index + 1]
|
||||
if colon.type != TT.COLON:
|
||||
raise ParseException("expected identifier", colon)
|
||||
|
||||
index += 2
|
||||
|
||||
# skip all tokens inside block
|
||||
while index < len(tokens):
|
||||
token = tokens[index]
|
||||
if token.type == TT.NEWLINE:
|
||||
index += 1
|
||||
continue
|
||||
|
||||
if token.position.column < init.position.column:
|
||||
raise ParseException("invalid block dedent", token)
|
||||
if token.position.column == init.position.column:
|
||||
break
|
||||
index += 1
|
||||
|
||||
return index, None
|
||||
|
||||
def parse_one(tokens, index):
|
||||
token = tokens[index]
|
||||
if token.type == TT.NEWLINE:
|
||||
index = index + 1
|
||||
if index < len(tokens):
|
||||
return parse_one(tokens, index)
|
||||
else:
|
||||
return index, None
|
||||
elif token.type == TT.IMAGE:
|
||||
index, ast = parse_image(tokens, index + 1)
|
||||
return index, ast
|
||||
elif token.type == TT.DEFINE:
|
||||
index, ast = parse_define(tokens, index + 1)
|
||||
return index, ast
|
||||
elif token.type == TT.LABEL:
|
||||
index, ast = parse_label(tokens, index + 1)
|
||||
return index, ast
|
||||
elif token.type == TT.PLAY:
|
||||
index, ast = parse_play(tokens, index + 1)
|
||||
return index, ast
|
||||
elif token.type == TT.SCENE:
|
||||
index, ast = parse_scene(tokens, index + 1)
|
||||
return index, ast
|
||||
elif token.type == TT.WITH:
|
||||
index, ast = parse_with(tokens, index + 1)
|
||||
return index, ast
|
||||
elif token.type == TT.IDENTIFIER:
|
||||
index, ast = parse_say(tokens, index)
|
||||
return index, ast
|
||||
elif token.type == TT.VOICE:
|
||||
index, ast = parse_voice(tokens, index + 1)
|
||||
return index, ast
|
||||
elif token.type == TT.SHOW:
|
||||
index, ast = parse_show(tokens, index + 1)
|
||||
return index, ast
|
||||
elif token.type == TT.MENU:
|
||||
index, ast = parse_menu(tokens, index)
|
||||
return index, ast
|
||||
elif token.type == TT.JUMP:
|
||||
index, ast = parse_jump(tokens, index + 1)
|
||||
return index, ast
|
||||
elif token.type == TT.RETURN:
|
||||
return index + 1, Return()
|
||||
elif token.type == TT.INIT:
|
||||
index, ast = parse_init(tokens, index)
|
||||
return index, ast
|
||||
else:
|
||||
raise ParseException("unexpected token", token)
|
||||
|
||||
def parse_all(tokens):
|
||||
index = 0
|
||||
while index < len(tokens):
|
||||
index, ast = parse_one(tokens, index)
|
||||
if ast is not None:
|
||||
yield ast
|
||||
|
||||
def main():
|
||||
with open(sys.argv[1], 'rb') as f:
|
||||
mem = memoryview(f.read())
|
||||
|
||||
tokens = list(lex.tokenize(mem))
|
||||
try:
|
||||
ast = parse_all(tokens)
|
||||
for t in ast:
|
||||
pprint(t)
|
||||
except ParseException as e:
|
||||
print(e, e.token)
|
||||
raise
|
||||
|
||||
if __name__ == "__main__":
|
||||
main()
|
||||
Loading…
x
Reference in New Issue
Block a user