Chart parser solves the solved problem again
I have written tons of parsers, and many of them aren't very utilizable by others. Often you need to know lot about their internals before you can use them. To make Lever's approach shine I made a python library with an interface anyone could grasp. For a lever programmer, the syntax and grammar of his language is an user interface.
Chartparser may seem a bit boring if you're not into creating your own language right now. It's anything but though! Languages are UX! And user interfaces or experiences are the sexy part of programming.
Now I show you four examples on what you can do with the chartparser module, despite most of these are notorious overkill.
There are python -samples in between, but if you're not into python, you may skip them. They just demonstrate how you could employ chartparser to do these kind of things.
Vi -style editing
In vi-style editing, you'd feed the parser input until the parser is done, while displaying the input on the screen. If the user presses ESC, you would instantiate a new parser.
Once the parser.acceptable is True, the command would be traversed from the parse tree and then run.
from chartparser import Nonterminal, Terminal, Rule, preprocess
simple_vi_command = Nonterminal("simple_vi_command")
digits = Nonterminal("digits")
move_left = Terminal("move_left")
move_right = Terminal("move_right")
digit = Terminal("digit")
grammar = [
Rule(simple_vi_command, [digits, move_left], vi_motion)
Rule(simple_vi_command, [digits, move_right], vi_motion)
Rule(digits, [], no_digits),
Rule(digits, [digit], one_digit),
Rule(digits, [digit_arg, digit], combine_digits),
]
parser = preprocess(grammar, accept=simple_vi_command)()
parser.step(digit, "0")
parser.step(digit, "1")
parser.step(move_left, "h")
def post(rule, args):
if callable(rule.annotation):
return rule.annotation(*args)
return [rule] + args
def blank(symbol):
if symbol == digits:
return 0
assert False
command = parser.traverse(post, blank)
command.run()
Brawl combo detection
In a brawl game, you'd most likely keep the ordinary motion separate, and you could have your combos described in the parser. The delay in player input, or no input states for given input would reset the parser.
from chartparser import Nonterminal, Terminal, Rule, preprocess
combo = Nonterminal("combo")
grammar = [
Rule(combo, [up, up, down, left], uppercut_punch),
Rule(combo, [up, down, up, right], monkey_steals_the_scallion),
]
new_parser = preprocess(grammar, accept=combo)
parser = new_parser()
# feed the parser with input, and reset if input delays.
if parser.expecting(motion):
parser.step(motion, None)
else:
parser = new_parser()
if parser.expecting(motion):
parser.step(motion, None)
Calculator
This one is obvious and ordinary use of a parser! But lets have it anyway.
grammar = [
Rule(addition, [multiplication], pass_through),
Rule(addition, [multiplication, plus, addition], binary_op),
Rule(multiplication, [digits], pass_through),
Rule(multiplication, [digits, star, digits], binary_op),
Rule(digits, [digit], single_digit)
Rule(digits, [digits, digit], tenbase_concat)
]
Interactive building of commands
In an adventure or role playing game, you might have several commands that are context dependent and have player call them to interact with the world.
grammar = [
Rule(action, [eat, consumable]),
Rule(action, [put, item, to, container]),
Rule(action, [get, item, from, container]),
Rule(action, [give, item, to, personality]),
Rule(action, [talk, to, personality]),
]
parser = preprocess(grammar, accept=action)()
print "available actions:", parser.expect
The interactions could get really complex and you might want to hide interactions that aren't possible at the moment. You could have several rules for each interaction and drop or include them according to whether they appear in the scene. parser.expect
contains the terminals that progress the parser state. That would let you present only the actions the player can do at the moment in your user interface.
Finishing touch
Parsing is relatively underpresented aspect of GUI today. But it is a powerful concept that appears often in UI and can untangle an extreme mess when used with caution. And assuming that you have a fitting generic purpose parser in your library catalog.