Skip to content

Instantly share code, notes, and snippets.

@nobrowser
Created February 5, 2016 18:29
Show Gist options
  • Save nobrowser/b527f0aada512be7b9f0 to your computer and use it in GitHub Desktop.
Save nobrowser/b527f0aada512be7b9f0 to your computer and use it in GitHub Desktop.
translate s-expressions to tk/wish scripts.
#! /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