aboutsummaryrefslogtreecommitdiffstatshomepage
path: root/examples/plugins/nivis.❄
diff options
context:
space:
mode:
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")