diff options
Diffstat (limited to 'examples/plugins/nivis.❄')
| -rw-r--r-- | examples/plugins/nivis.❄ | 295 |
1 files changed, 0 insertions, 295 deletions
diff --git a/examples/plugins/nivis.❄ b/examples/plugins/nivis.❄ deleted file mode 100644 index 783fdb1..0000000 --- a/examples/plugins/nivis.❄ +++ /dev/null @@ -1,295 +0,0 @@ -""" SPI - Simple Pascal Interpreter """ - -############################################################################### -# # -# LEXER # -# # -############################################################################### - -# Token types -# -# EOF (end-of-file) token is used to indicate that -# there is no more input left for lexical analysis -from iamai import Plugin -from HydroRoll.utils import HydroDice - -INTEGER, PLUS, MINUS, MUL, DIV, LPAREN, RPAREN, EOF = ( - "INTEGER", - "PLUS", - "MINUS", - "MUL", - "DIV", - "(", - ")", - "EOF", -) - -DICE = "DICE" - - -class Token(object): - """A single token from the lexer.""" - - def __init__(self, _type, _value): - self.type = _type - self.value = _value - - def __str__(self): - """String representation of the class instance. - - Examples: - Token(INTEGER, 3) - Token(PLUS, '+') - Token(MUL, '*') - """ - return f"Token({self.type}, {repr(self.value)})" - - def __repr__(self): - return self.__str__() - - -class Lexer(object): - """A lexer for the Psi language.""" - - def __init__(self, text): - # client string input, e.g. "4 + 2 * 3 - 6 / 2" - self.text = text - # self.pos is an index into self.text - self.pos = 0 - self.current_char = self.text[self.pos] - - def error(self): - """Raise an exception at the current position.""" - raise ValueError("Invalid character") - - def advance(self): - """Advance the `pos` pointer and set the `current_char` variable.""" - self.pos += 1 - if self.pos > len(self.text) - 1: - self.current_char = None # Indicates end of input - else: - self.current_char = self.text[self.pos] - - def skip_whitespace(self): - while self.current_char is not None and self.current_char.isspace(): - self.advance() - - def integer(self): - """Return a (multidigit) integer consumed from the input.""" - result = "" - while self.current_char is not None and self.current_char.isdigit(): - result += self.current_char - self.advance() - return int(result) - - def get_next_token(self): - """Lexical analyzer (also known as scanner or tokenizer)""" - while self.current_char is not None: - if self.current_char.isspace(): - self.skip_whitespace() - continue - - token_type = { - "+": PLUS, - "-": MINUS, - "d": DICE, - "*": MUL, - "/": DIV, - "(": LPAREN, - ")": RPAREN, - }.get(self.current_char) - - if token_type: - self.advance() - return Token(token_type, self.current_char) - - if self.current_char.isdigit(): - return Token(INTEGER, self.integer()) - - self.error() - - return Token(EOF, None) - - -############################################################################### -# # -# PARSER # -# # -############################################################################### - - -class AST(object): - pass - - -class BinOp(AST): - def __init__(self, left, op, right): - self.left = left - self.token = self.op = op - self.right = right - - -class Num(AST): - def __init__(self, token): - self.token = token - self.value = token.value - - -class UnaryOp(AST): - def __init__(self, op, expr): - self.token = self.op = op - self.expr = expr - - -class Parser(object): - def __init__(self, lexer): - self.lexer = lexer - # set current token to the first token taken from the input - self.current_token = self.lexer.get_next_token() - - def error(self): - raise Exception("Invalid syntax") - - def eat(self, token_type): - # compare the current token type with the passed token - # type and if they match then "eat" the current token - # and assign the next token to the self.current_token, - # otherwise raise an exception. - if self.current_token.type == token_type: - self.current_token = self.lexer.get_next_token() - else: - self.error() - - def factor(self): - """factor : (PLUS | MINUS | DICE) factor | INTEGER | LPAREN expr RPAREN""" - token = self.current_token - if token.type == PLUS: - self.eat(PLUS) - node = UnaryOp(token, self.factor()) - return node - elif token.type == MINUS: - self.eat(MINUS) - node = UnaryOp(token, self.factor()) - return node - elif token.type == DICE: - self.eat(DICE) - left = Num(Token(INTEGER, 1)) # 默认骰子个数为1 - right = self.factor() - node = BinOp(left, token, right) - return node - elif token.type == INTEGER: - self.eat(INTEGER) - return Num(token) - elif token.type == LPAREN: - self.eat(LPAREN) - node = self.expr() - self.eat(RPAREN) - return node - - def term(self): - """term : factor ((MUL | DIV) factor)*""" - node = self.factor() - - while self.current_token.type in (MUL, DIV): - token = self.current_token - if token.type == MUL: - self.eat(MUL) - elif token.type == DIV: - self.eat(DIV) - - node = BinOp(left=node, op=token, right=self.factor()) - - return node - - def expr(self): - """ - expr : term ((PLUS | MINUS) term)* - term : factor ((MUL | DIV) factor)* - factor : (PLUS | MINUS) factor | INTEGER | LPAREN expr RPAREN - """ - node = self.term() - - while self.current_token.type in (PLUS, MINUS): - token = self.current_token - if token.type == PLUS: - self.eat(PLUS) - elif token.type == MINUS: - self.eat(MINUS) - - node = BinOp(left=node, op=token, right=self.term()) - - return node - - def parse(self): - node = self.expr() - if self.current_token.type != EOF: - self.error() - return node - - -############################################################################### -# # -# INTERPRETER # -# # -############################################################################### - - -class NodeVisitor(object): - def visit(self, node): - method_name = "visit_" + type(node).__name__ - visitor = getattr(self, method_name, self.generic_visit) - return visitor(node) - - def generic_visit(self, node): - raise Exception("No visit_{} method".format(type(node).__name__)) - - -class Interpreter(NodeVisitor): - def __init__(self, parser): - self.parser = parser - - def visit_BinOp(self, node): - if node.op.type == PLUS: - return self.visit(node.left) + self.visit(node.right) - elif node.op.type == MINUS: - return self.visit(node.left) - self.visit(node.right) - elif node.op.type == DICE: - return int( - HydroDice(1).roll_dice( - _counts=self.visit(node.left), - _sides=self.visit(node.right), - streamline=True, - ) - ) - elif node.op.type == MUL: - return self.visit(node.left) * self.visit(node.right) - elif node.op.type == DIV: - return self.visit(node.left) // self.visit(node.right) - - def visit_Num(self, node): - return node.value - - def visit_UnaryOp(self, node): - op = node.op.type - if op == PLUS: - return +self.visit(node.expr) - elif op == MINUS: - return -self.visit(node.expr) - - def interpret(self): - tree = self.parser.parse() - if tree is None: - return "" - return self.visit(tree) - - -class Psi(Plugin): - async def handle(self) -> None: - lexer = Lexer(self.event.message.get_plain_text()[4:]) - parser = Parser(lexer) - interpreter = Interpreter(parser) - result = interpreter.interpret() - await self.event.reply(str(result)) - - async def rule(self) -> bool: - return self.event.type == "message" and self.event.message.startswith(".psi") |
