Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

basic mvp of bash repr #8

Open
wants to merge 3 commits into
base: master
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
20 changes: 15 additions & 5 deletions app/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,8 @@
from antlr_plsql import ast as plsql_ast
from antlr_tsql import ast as tsql_ast
import ast as python_ast
from .ast_dump import dump_node
from .ast_dump import dump_node, dump_bash, dump_vorpal_bash, get_vorpal_bash_ast
import bashlex

def get_parser(parser_name):
parsers = {'plsql': plsql_ast, 'tsql': tsql_ast}
Expand All @@ -24,15 +25,24 @@ def get_ast(code, start, parser_name):
return tsql_ast.parse(code, start)
elif parser_name == "python":
return python_ast.parse(code)
elif parser_name == "bash-simple":
return bashlex.parse(code)
elif parser_name == "bash-verbose":
return bashlex.parse(code)
elif parser_name == "bash-vorpal":
return get_vorpal_bash_ast(code)

return None

# Views -----------------------------------------------------------------------
from flask import Flask, request, url_for, redirect, jsonify, make_response
import yaml

def str_or_dump(ast):
if isinstance(ast, str): return {'type': 'PYTHON_OBJECT', 'data': {"": ast}}
def str_or_dump(ast, parser):
if parser == 'bash-simple': return dump_bash(ast)
elif parser == 'bash-verbose': return dump_bash(ast, v = True)
elif parser == 'bash-vorpal': return dump_vorpal_bash(ast)
elif isinstance(ast, str): return {'type': 'PYTHON_OBJECT', 'data': {"": ast}}
elif hasattr(ast, '_dump'): return ast._dump()
else: return dump_node(ast)

Expand All @@ -48,7 +58,7 @@ def ast_postgres():
ast = get_ast(args['code'], args['start'], args['parser'])
if ast is None: return make_response("Incorrect parser name", 400)

return jsonify(str_or_dump(ast))
return jsonify(str_or_dump(ast, args['parser']))

@app.route('/ast-from-config', methods = ['GET', 'POST'])
def ast_from_config():
Expand All @@ -63,7 +73,7 @@ def ast_from_config():

out = {}
for k, v in trees.items():
json_asts = [str_or_dump(tree) for tree in v]
json_asts = [str_or_dump(tree, args['parser_name']) for tree in v]
sql_cmds = code[k]
zipped = zip(sql_cmds, json_asts)
# entry with attrs code: sql_cmd, ast: json_ast
Expand Down
49 changes: 49 additions & 0 deletions app/ast_dump.py
Original file line number Diff line number Diff line change
Expand Up @@ -15,3 +15,52 @@ def dump_node(obj):
return [dump_node(x) for x in obj]
else:
return obj

import bashlex
def dump_bash(obj, parent_cls = bashlex.ast.node, v = False):
# pull element out of single entry lists
if isinstance(obj, (list, tuple)) and len(obj) == 1: obj = obj[0]
# dump to dict
if isinstance(obj, parent_cls):
if obj.kind in ['word', 'reservedword'] and not v:
return obj.word
fields = OrderedDict()
for name in [el for el in obj.__dict__.keys() if el not in ('kind', 'pos')]:
attr = getattr(obj, name)
if isinstance(attr, parent_cls): fields[name] = dump_bash(attr, parent_cls, v)
elif isinstance(attr, list) and len(attr) == 0: continue
elif isinstance(attr, list): fields[name] = [dump_bash(x, parent_cls, v) for x in attr]
else: fields[name] = attr
return {'type': obj.kind, 'data': fields}
elif isinstance(obj, list):
return [dump_bash(x, parent_cls, v) for x in obj]
else: raise Exception("received non-node object?")


def dump_vorpal_bash(obj, v = False):
# pull element out of single entry lists
if isinstance(obj, (list, tuple)) and len(obj) == 1: obj = obj[0]
# dump to dict
if isinstance(obj, list):
return [dump_vorpal_bash(x, v) for x in obj]
elif isinstance(obj, dict):
if obj['type'] in ['Word'] and not v:
return obj['text']
fields = OrderedDict()
for name in [el for el in obj.keys() if el not in ('loc', 'type')]:
attr = obj[name]
if isinstance(attr, dict): fields[name] = dump_vorpal_bash(attr, v)
elif isinstance(attr, list) and len(attr) == 0: continue
elif isinstance(attr, list): fields[name] = [dump_vorpal_bash(x, v) for x in attr]
else: fields[name] = attr
return {'type': obj['type'], 'data': fields}
else: raise Exception("received non-node object?")

import execjs
import os
node_path = os.getcwd() + '/node_modules'
os.environ['NODE_PATH'] = node_path + ':' + os.environ.get('NODE_PATH', "")
def get_vorpal_bash_ast(src):
node = execjs.get('node')
return node.eval("""require('bash-parser')(`%s`)"""%src.replace('"', r'\"'))

24 changes: 24 additions & 0 deletions app/bash_dump_node.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
from collections import OrderedDict

def dump_node(obj, parent_cls):
# pull element out of single entry lists
if isinstance(obj, (list, tuple)) and len(obj) == 1: obj = obj[0]
# dump to dict
if isinstance(obj, parent_cls):
fields = OrderedDict()
for name in [el for el in obj.__dict__.keys() if el != 'kind']:
attr = getattr(obj, name)
if isinstance(attr, parent_cls): fields[name] = dump_node(attr, parent_cls)
elif isinstance(attr, list) and len(attr) == 0: continue
elif isinstance(attr, list): fields[name] = [dump_node(x, parent_cls) for x in attr]
else: fields[name] = attr
return {'type': obj.kind, 'data': fields}
elif isinstance(obj, list):
return [dump_node(x) for x in obj]
else: raise Exception("received non-node object?")

import bashlex

parts = bashlex.parse('true && echo hey')

out = dump_node(parts, bashlex.ast.node)
20 changes: 19 additions & 1 deletion app/static/src/editor.vue
Original file line number Diff line number Diff line change
Expand Up @@ -38,16 +38,34 @@ var request = require('superagent')
var grammars = [
{
name: 'plsql',
show_parse: true,
funcs: require('../grammar/antlr_plsql/js/index.js').default,
start: 'sql_script'
},
{
name: 'tsql',
show_parse: true,
funcs: require('../grammar/antlr_tsql/js/index.js').default,
start: 'tsql_file'
},
{
name: 'python',
show_parse: false,
start: 'NA'
},
{
name: 'bash-simple',
show_parse: false,
start: 'NA'
},
{
name: 'bash-verbose',
show_parse: false,
start: 'NA'
},
{
name: 'bash-vorpal',
show_parse: false,
start: 'NA'
}
]
Expand Down Expand Up @@ -128,7 +146,7 @@ export default {

parseCode () {
var grammar = this.crntGrammar.funcs
if (this.crntGrammar.name != "python")
if (this.crntGrammar.show_parse)
this.codeData = parseFromGrammar(grammar, this.code, this.parserStart)
else
this.codeData = {}
Expand Down
4 changes: 3 additions & 1 deletion package.json
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,9 @@
"version": "1.0.0",
"description": "",
"main": "index.js",
"dependencies": {},
"dependencies": {
"bash-parser": "^0.5.0"
},
"devDependencies": {},
"scripts": {
"test": "echo \"Error: no test specified\" && exit 1"
Expand Down
2 changes: 2 additions & 0 deletions requirements.txt
Original file line number Diff line number Diff line change
Expand Up @@ -3,3 +3,5 @@ gevent
flask
whitenoise
pyyaml
bashlex
PyExecJS