summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--.gitignore8
-rw-r--r--gen_antlr.sh5
-rw-r--r--guild_cog.py5
-rwxr-xr-xlaunch.sh7
-rw-r--r--main.py13
-rw-r--r--query/Query.g433
-rw-r--r--query/__init__.py1
-rw-r--r--query/parse.py113
-rw-r--r--requirements.txt1
-rw-r--r--stream_cog.py26
10 files changed, 208 insertions, 4 deletions
diff --git a/.gitignore b/.gitignore
index 2365604..7a934f3 100644
--- a/.gitignore
+++ b/.gitignore
@@ -1,2 +1,10 @@
token.txt
venv/
+
+query/Query.tokens
+query/QueryLexer.py
+query/QueryLexer.tokens
+query/QueryListener.py
+query/QueryParser.py
+query/debug.txt
+query/QueryVisitor.py
diff --git a/gen_antlr.sh b/gen_antlr.sh
new file mode 100644
index 0000000..f743bac
--- /dev/null
+++ b/gen_antlr.sh
@@ -0,0 +1,5 @@
+#!/usr/bin/env bash
+root=$(dirname $0)/query
+cd ${root}
+antlr4 -visitor -Dlanguage=Python3 Query.g4
+
diff --git a/guild_cog.py b/guild_cog.py
new file mode 100644
index 0000000..c79be6e
--- /dev/null
+++ b/guild_cog.py
@@ -0,0 +1,5 @@
+from discord.ext import commands
+
+
+def setup(bot: commands.Bot):
+ pass
diff --git a/launch.sh b/launch.sh
new file mode 100755
index 0000000..d046481
--- /dev/null
+++ b/launch.sh
@@ -0,0 +1,7 @@
+#!/usr/bin/env bash
+root=$(dirname $0)
+cd ${root}
+${root}/venv/bin/python ${root}/main.py "$@" &>$HOME/logs/discordbot
+
+
+
diff --git a/main.py b/main.py
index 6385fa5..0b5ee9c 100644
--- a/main.py
+++ b/main.py
@@ -2,7 +2,7 @@ from functools import wraps
from typing import List
import discord.utils
-from discord import Object
+from discord import Object, Guild
from discord.ext import commands
import discord
@@ -203,13 +203,18 @@ async def dump_roles(ctx: commands.Context, roles: List[discord.Role], quiet: bo
@bot.command(pass_context=True, name="roles")
@check_guild
-async def roles_cmd(ctx: commands.Context, quiet: bool = False):
- guild: discord.Guild = ctx.guild
+async def roles_cmd(ctx: commands.Context, guild: Guild = None,quiet: bool = False):
+ if guild is None:
+ guild = ctx.guild
roles: List[discord.Role] = guild.roles
await dump_roles(ctx, roles, quiet)
await ctx.react('✅')
-
+MODULES = [
+ 'stream_cog'
+]
+for module in MODULES:
+ bot.load_extension(module)
if __name__ == '__main__':
with open('token.txt') as f:
diff --git a/query/Query.g4 b/query/Query.g4
new file mode 100644
index 0000000..a13a80f
--- /dev/null
+++ b/query/Query.g4
@@ -0,0 +1,33 @@
+
+grammar Query;
+
+prog: stat+ EOF? ;
+
+stat: expr # rawExpr
+ | ID '=' expr # assign
+ ;
+
+expr: expr '.' ID # access
+ | expr '(' (args=expr*) ')' # call
+ | expr op=('*'|'/') expr # MulDiv
+ | expr op=('+'|'-') expr # AddSub
+ | INT # int
+ | STRINGLITERAL # string
+ | ID # id
+ | '(' expr ')' # parens
+ | '{' (args=arguments)? prog '}' # func
+ ;
+
+arguments: ID (',' ID)* '->';
+
+STRINGLITERAL : '"' ( StringEscapeSeq | ~( '\\' | '"' | '\r' | '\n' ) )* '"' ;
+StringEscapeSeq : '\\' ( 't' | 'n' | 'r' | '"' | '\\' | '$' | ('0'..'9')) ;
+
+MUL : '*' ; // assigns token name to '*' used above in grammar
+DIV : '/' ;
+ADD : '+' ;
+SUB : '-' ;
+ID : [a-zA-Z]+ ; // match identifiers
+INT : [0-9]+ ; // match integers
+NEWLINE:'\r'? '\n' -> skip ; // return newlines to parser (is end-statement signal)
+WS : [ \t]+ -> skip ; // toss out whitespace \ No newline at end of file
diff --git a/query/__init__.py b/query/__init__.py
new file mode 100644
index 0000000..1b51639
--- /dev/null
+++ b/query/__init__.py
@@ -0,0 +1 @@
+from .parse import parse
diff --git a/query/parse.py b/query/parse.py
new file mode 100644
index 0000000..b509044
--- /dev/null
+++ b/query/parse.py
@@ -0,0 +1,113 @@
+from typing import List
+
+from antlr4 import *
+
+from query.QueryLexer import QueryLexer
+from query.QueryParser import QueryParser
+from query.QueryVisitor import QueryVisitor
+
+
+def make_function(outer_scope, argument_names: List[str], parser: QueryParser, prog: QueryParser.ProgContext):
+ def function(*args):
+ visitor = MyQueryVisitor(parser)
+ visitor.memory = outer_scope.copy()
+ for i in range(len(args)):
+ visitor.memory[argument_names[i]] = args[i]
+ return visitor.for_result(prog)
+
+ return function
+
+
+class Return(Exception):
+ def __init__(self, thing):
+ self.ret = thing
+
+
+class MyQueryVisitor(QueryVisitor):
+ def __init__(self, parser: QueryParser):
+ self.memory = {
+ }
+ self.parser: QueryParser = parser
+ self.last_expr = None
+
+ def for_result(self, ctx):
+ try:
+ self.visit(ctx)
+ except Return as ret:
+ return ret.ret
+ return self.last_expr
+
+ def visitProg(self, ctx: QueryParser.ProgContext):
+ for stat in ctx.getChildren(lambda child: isinstance(child, QueryParser.StatContext)):
+ self.visit(stat)
+
+ def visitFunc(self, ctx: QueryParser.FuncContext):
+ args = ctx.arguments().getText()[:-2].split(',')
+ return make_function(self.memory, args, self.parser, ctx.prog())
+
+ def visitAssign(self, ctx):
+ name = ctx.ID().getText()
+ value = self.visit(ctx.expr())
+ self.memory[name] = value
+ return value
+
+ def visitRawExpr(self, ctx: QueryParser.RawExprContext):
+ value = self.visit(ctx.expr())
+ self.last_expr = value
+ return value
+
+ def visitInt(self, ctx):
+ return int(ctx.INT().getText())
+
+ def visitId(self, ctx):
+ name = ctx.ID().getText()
+ if name in self.memory:
+ return self.memory[name]
+ return 0
+
+ def visitAccess(self, ctx: QueryParser.AccessContext):
+ thing = self.visit(ctx.expr())
+ return getattr(thing, ctx.ID(), 0)
+
+ def visitMulDiv(self, ctx):
+ left = (self.visit(ctx.expr(0)))
+ right = (self.visit(ctx.expr(1)))
+ if ctx.op.type == QueryParser.MUL:
+ return left * right
+ return left / right
+
+ def visitString(self, ctx: QueryParser.StringContext):
+ return eval(ctx.getText())
+
+ def visitAddSub(self, ctx):
+ left = int(self.visit(ctx.expr(0)))
+ right = int(self.visit(ctx.expr(1)))
+ if ctx.op.type == QueryParser.ADD:
+ return left + right
+ return left - right
+
+ def visitParens(self, ctx):
+ return self.visit(ctx.expr())
+
+ def visitCall(self, ctx: QueryParser.CallContext):
+ to_call = self.visit(ctx.expr(0))
+ args = [self.visit(arg) for arg in ctx.getChildren(lambda x: isinstance(x, QueryParser.ExprContext))][1:]
+ return to_call(*args)
+
+ def visit(self, tree):
+ return super(MyQueryVisitor, self).visit(tree)
+
+
+def parse(text, **kwargs):
+ parser = QueryParser(CommonTokenStream(QueryLexer(InputStream(text))))
+ tree = parser.prog()
+ visitor = MyQueryVisitor(parser)
+ for key, value in kwargs.items():
+ visitor.memory[key] = value
+ return visitor.for_result(tree)
+
+
+if __name__ == '__main__':
+ with open('debug.txt') as handle:
+ content = handle.read()
+ print(parse(content))
diff --git a/requirements.txt b/requirements.txt
index db6f91f..950ae7f 100644
--- a/requirements.txt
+++ b/requirements.txt
@@ -1 +1,2 @@
https://github.com/Rapptz/discord.py/archive/rewrite.zip
+antlr4-python3-runtime
diff --git a/stream_cog.py b/stream_cog.py
new file mode 100644
index 0000000..20ae77c
--- /dev/null
+++ b/stream_cog.py
@@ -0,0 +1,26 @@
+import discord
+from discord.ext import commands
+from discord.ext.commands import Context as CommandContext
+
+from query import parse
+
+
+class StreamCog(object):
+ def __init__(self, bot):
+ self.bot: commands.Bot = bot
+
+ @commands.command()
+ async def stream(self, ctx: CommandContext, *, to_eval):
+ val = parse(to_eval,
+ guild=ctx.guild,
+ channel=ctx.channel,
+ author=ctx.author,
+ bot=self.bot,
+ client=self.bot,
+ discord=discord,
+ )
+ await ctx.send(repr(val))
+
+
+def setup(bot: commands.Bot):
+ bot.add_cog(StreamCog(bot))