Created
February 5, 2016 18:29
-
-
Save nobrowser/b527f0aada512be7b9f0 to your computer and use it in GitHub Desktop.
translate s-expressions to tk/wish scripts.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
#! /usr/bin/python | |
# This script translates s-expressions (ie. the Lisp/Scheme data notation) | |
# into tk scripts with nested widgets. Example input: | |
# | |
# ;; --*-scheme-*-- | |
# (frame | |
# :grid v | |
# (frame | |
# :grid h | |
# (label :-text "File: ") | |
# (label :-textvariable "filename")) | |
# (frame | |
# :grid h | |
# (label :-text "Comment: ") | |
# (entry :-width 80 :-textvariable "comment")) | |
# (frame | |
# :grid h | |
# (button :-text "OK" :-command "echo_comment_and_exit") | |
# (button :-text "Cancel" :-command "exit 1"))) | |
# | |
# This produces the following output: | |
# | |
# frame .g__0 | |
# grid .g__0 -column 0 -row 0 | |
# frame .g__0.g__1 | |
# grid .g__0.g__1 -column 0 -row 0 | |
# frame .g__0.g__2 | |
# grid .g__0.g__2 -column 0 -row 1 | |
# frame .g__0.g__3 | |
# grid .g__0.g__3 -column 0 -row 2 | |
# label .g__0.g__1.g__4 -text "File: " | |
# grid .g__0.g__1.g__4 -column 0 -row 0 | |
# label .g__0.g__1.g__5 -textvariable "filename" | |
# grid .g__0.g__1.g__5 -column 1 -row 0 | |
# label .g__0.g__2.g__6 -text "Comment: " | |
# grid .g__0.g__2.g__6 -column 0 -row 0 | |
# entry .g__0.g__2.g__7 -width "80" -textvariable "comment" | |
# grid .g__0.g__2.g__7 -column 1 -row 0 | |
# button .g__0.g__3.g__8 -command "echo_comment_and_exit" -text "OK" | |
# grid .g__0.g__3.g__8 -column 0 -row 0 | |
# button .g__0.g__3.g__9 -command "exit 1" -text "Cancel" | |
# grid .g__0.g__3.g__9 -column 1 -row 0 | |
import sexpdata as SE | |
import sys | |
from Queue import Queue | |
from argparse import ArgumentParser | |
VAR_FORMAT = '''set {n} "{v}" | |
proc echo_{n} {{}} {{ | |
puts $::{n} | |
}} | |
proc echo_{n}_and_exit {{}} {{ | |
puts $::{n} | |
exit 0 | |
}} | |
''' | |
class InvalidSpecError(Exception): | |
pass | |
def gensym(): | |
g = 0 | |
while True: | |
yield ('g__%s' % g) | |
g += 1 | |
Gensym = gensym() | |
def generate_subdata(iterable): | |
iterator = iterable.__iter__() | |
pair_valid = True | |
val = None | |
ungot = None | |
try: | |
while True: | |
item = ungot or iterator.next() | |
ungot = None | |
if isinstance(item, list): | |
yield item | |
val = None | |
pair_valid = False | |
elif isinstance(item, SE.Symbol): | |
val = item._val | |
if not pair_valid: | |
raise InvalidSpecError('symbol after sublist: %s' % val) | |
item = iterator.next() | |
if isinstance(item, list): | |
yield (val, True) | |
val = None | |
ungot = item | |
else: | |
yield (val, item) | |
val = None | |
except StopIteration: | |
if val is not None: | |
yield (val, True) | |
def data2tk(data, parent, gridtype, gridindex, io): | |
if not isinstance(data[0], SE.Symbol): | |
raise InvalidSpecError('constructor is not a symbol: %s' % data[0]) | |
constructor = data[0]._val | |
tk_attribs = {} | |
x_attribs = {} | |
kids = [] | |
for sd in generate_subdata(data[1:]): | |
if isinstance(sd, tuple): | |
k, v = sd | |
if isinstance(v, SE.Symbol): | |
v = v._val | |
if k.startswith(':-'): | |
tk_attribs[k[1:]] = v | |
elif k.startswith(':'): | |
x_attribs[k[1:]] = v | |
else: | |
# ignore these for now, perhaps reserve for extension mechanism | |
pass | |
elif isinstance(sd, list): | |
kids.append(sd) | |
tkid = '.'.join((parent, Gensym.next())) | |
construct_cmd = ' '.join((constructor, tkid)) | |
for k, v in tk_attribs.items(): | |
construct_cmd += ' {k} "{v}"'.format(k=k, v=v) | |
construct_cmd += '\n' | |
io.write(construct_cmd) | |
if gridtype == 'h': | |
args = {'i': tkid, 'c': gridindex, 'r': 0} | |
elif gridtype == 'v': | |
args = {'i': tkid, 'c': 0, 'r': gridindex} | |
else: | |
args = {'i': tkid, 'c': 0, 'r': 0} | |
io.write('grid {i} -column {c} -row {r}\n'.format(**args)) | |
if 'obvar' in x_attribs: | |
io.write('set {o} "{i}"\n'.format(o=x_attribs['obvar'], i=tkid)) | |
kg = x_attribs.get('grid', None) | |
return enumerate([(k, tkid, kg) for k in kids]) | |
def process_var(outstr, variable): | |
if not '=' in variable: | |
n = variable | |
v = '' | |
else: | |
n, v = variable.split('=', 1) | |
outstr.write(VAR_FORMAT.format(n=n, v=v)) | |
def process_spec(instr, outstr, title, variables): | |
if title is not None: | |
outstr.write('wm title . "{t}"\n'.format(t=title)) | |
for v in variables: | |
process_var(outstr, v) | |
data = SE.load(instr) | |
q = Queue() | |
e = data2tk(data, '', None, 0, outstr) | |
for k in e: | |
q.put(k) | |
while not q.empty(): | |
gi, tp = q.get() | |
k, tkid, kg = tp | |
e = data2tk(k, tkid, kg, gi, outstr) | |
for k in e: | |
q.put(k) | |
def main(): | |
ap = ArgumentParser(description='Translate sexp notation into wish script.') | |
ap.add_argument('-o', '--outfile', help='write to OUTFILE not stdout') | |
ap.add_argument('-T', '--title', help='give TITLE to toplevel window') | |
ap.add_argument('-v', '--var', action='append', default=[], | |
help='declare and set VAR [format: NAME=VALUE]') | |
ap.add_argument('infile', nargs='?', help='read from INFILE not stdin') | |
args = ap.parse_args() | |
with open(args.outfile or '/dev/stdout', 'w') as outstr: | |
with open(args.infile or '/dev/stdin') as instr: | |
process_spec(instr, outstr, args.title, args.var) | |
if __name__ == "__main__": | |
main() |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment