Implemented expression preprocessor and 'graph' statement.

parent 24f62cbb
graph_drawing @ 003a9e25
Subproject commit 4d65a52b8ba0f0ad1843cb5535a485ff372317a3 Subproject commit 003a9e2536ed7d899b570f07d7d6a0ce60c5f155
pybison @ eb1d1da4
Subproject commit 2ae8c15dba13faba38e1a3d1a711cf6d24395a5a Subproject commit eb1d1da4c21cc3f48cabe19485381e3f7e80f279
...@@ -39,8 +39,8 @@ class Parser(BisonParser): ...@@ -39,8 +39,8 @@ class Parser(BisonParser):
# of tokens of the lex script. # of tokens of the lex script.
tokens = ['NUMBER', 'IDENTIFIER', tokens = ['NUMBER', 'IDENTIFIER',
'PLUS', 'MINUS', 'TIMES', 'DIVIDE', 'POW', 'PLUS', 'MINUS', 'TIMES', 'DIVIDE', 'POW',
'LPAREN', 'RPAREN', 'COMMA', 'CONCAT_POW', 'LPAREN', 'RPAREN', 'COMMA', #'CONCAT_POW',
'NEWLINE', 'QUIT', 'RAISE'] 'NEWLINE', 'QUIT', 'RAISE', 'GRAPH']
# ------------------------------ # ------------------------------
# precedences # precedences
...@@ -71,6 +71,64 @@ class Parser(BisonParser): ...@@ -71,6 +71,64 @@ class Parser(BisonParser):
except EOFError: except EOFError:
return '' return ''
def hook_read(self, data):
"""
This hook will be called when the read() method returned. The data
argument points to the data read by the read() method. This hook
function should return the data to be used by the parser.
"""
import re
# TODO: remove this quick preprocesing hack. This hack enabled
# concatenated expressions, since the grammar currently does not
# support those. This workaround will replace:
# - ")(" with ")*(".
# - "a(" with "a*(".
# - ")a" with ")*a".
# - "ab" with "a*b".
# - "4a" with "4*a".
# - "a4" with "a^4".
pattern = ('(?:(\))\s*(\()' # match: )(
+ '|([a-z0-9])\s*(\()' # match: a(
+ '|(\))\s*([a-z0-9])' # match: )a
+ '|([a-z])\s*([a-z]+)' # match: ab
+ '|([0-9])\s*([a-z])' # match: 4a
+ '|([a-z])\s*([0-9]))') # match: a4
def preprocess_data(match):
left, right = filter(None, match.groups())
# Filter words (otherwise they will be preprocessed as well)
if left + right in ['graph', 'raise']:
return left + right
# If all characters on the right are numbers. e.g. "a4", the
# expression implies exponentiation. Make sure ")4" is not
# converted into an exponentiation, because that's multipliciation.
if left != ')' \
and all(map(lambda x: 48 <= ord(x) < 58, list(right))):
return '%s^%s' % (left, right)
# match: ab | abc | abcd (where left = "a")
return '*'.join([left] + list(right))
# Iteratively replace all matches.
while True:
data_after = re.sub(pattern, preprocess_data, data)
if data == data_after:
break
if self.verbose:
print 'hook_read() modified the input data:'
print 'before:', data.replace('\n', '\\n')
print 'after :', data_after.replace('\n', '\\n')
data = data_after
return data
# --------------------------------------------------------------- # ---------------------------------------------------------------
# These methods are the python handlers for the bison targets. # These methods are the python handlers for the bison targets.
# (which get called by the bison code each time the corresponding # (which get called by the bison code each time the corresponding
...@@ -103,14 +161,27 @@ class Parser(BisonParser): ...@@ -103,14 +161,27 @@ class Parser(BisonParser):
""" """
line : NEWLINE line : NEWLINE
| exp NEWLINE | exp NEWLINE
| debug NEWLINE
| RAISE NEWLINE | RAISE NEWLINE
""" """
if option == 1: if option in [1, 2]:
return values[0] return values[0]
if option == 2: if option == 3:
raise RuntimeError('on_line: exception raised') raise RuntimeError('on_line: exception raised')
def on_debug(self, target, option, names, values):
"""
debug : GRAPH exp
"""
if option == 0:
print generate_graph(values[1])
return values[1]
raise BisonSyntaxError('Unsupported option %d in target "%s".'
% (option, target)) # pragma: nocover
def on_exp(self, target, option, names, values): def on_exp(self, target, option, names, values):
""" """
exp : NUMBER exp : NUMBER
...@@ -118,8 +189,8 @@ class Parser(BisonParser): ...@@ -118,8 +189,8 @@ class Parser(BisonParser):
| LPAREN exp RPAREN | LPAREN exp RPAREN
| unary | unary
| binary | binary
| concat
""" """
# | concat
if option == 0: # rule: NUMBER if option == 0: # rule: NUMBER
# TODO: A bit hacky, this achieves long integers and floats. # TODO: A bit hacky, this achieves long integers and floats.
...@@ -132,9 +203,12 @@ class Parser(BisonParser): ...@@ -132,9 +203,12 @@ class Parser(BisonParser):
if option == 2: # rule: LPAREN exp RPAREN if option == 2: # rule: LPAREN exp RPAREN
return values[1] return values[1]
if option in [3, 4, 5]: # rule: unary | binary | concat if option in [3, 4]: # rule: unary | binary
return values[0] return values[0]
#if option in [3, 4, 5]: # rule: unary | binary | concat
# return values[0]
raise BisonSyntaxError('Unsupported option %d in target "%s".' raise BisonSyntaxError('Unsupported option %d in target "%s".'
% (option, target)) # pragma: nocover % (option, target)) # pragma: nocover
...@@ -179,39 +253,39 @@ class Parser(BisonParser): ...@@ -179,39 +253,39 @@ class Parser(BisonParser):
raise BisonSyntaxError('Unsupported option %d in target "%s".' raise BisonSyntaxError('Unsupported option %d in target "%s".'
% (option, target)) # pragma: nocover % (option, target)) # pragma: nocover
def on_concat(self, option, target, names, values): #def on_concat(self, option, target, names, values):
""" # """
concat : exp IDENTIFIER # concat : exp IDENTIFIER %prec TIMES
| exp NUMBER # | exp NUMBER %prec TIMES
| exp LPAREN exp RPAREN # | exp LPAREN exp RPAREN %prec TIMES
| exp CONCAT_POW # | exp CONCAT_POW %prec TIMES
| CONCAT_POW # | CONCAT_POW
""" # """
if option in [0, 1]: # rule: exp IDENTIFIER | exp NUMBER # if option in [0, 1]: # rule: exp IDENTIFIER | exp NUMBER
# example: xy -> x*y # # example: xy -> x*y
# example: (x)4 -> x*4 # # example: (x)4 -> x*4
val = int(values[1]) if option == 1 else values[1] # val = int(values[1]) if option == 1 else values[1]
return Node('*', *(combine('*', values[0]) + [Leaf(val)])) # return Node('*', *(combine('*', values[0]) + [Leaf(val)]))
if option == 2: # rule: exp LPAREN exp RPAREN # if option == 2: # rule: exp LPAREN exp RPAREN
# example: x(y) -> x*(y) # # example: x(y) -> x*(y)
return Node('*', *(combine('*', values[0]) # return Node('*', *(combine('*', values[0])
+ combine('*', values[2]))) # + combine('*', values[2])))
if option == 3: # if option == 3:
# example: xy4 -> x*y^4 # # example: xy4 -> x*y^4
identifier, exponent = list(values[1]) # identifier, exponent = list(values[1])
node = Node('^', Leaf(identifier), Leaf(int(exponent))) # node = Node('^', Leaf(identifier), Leaf(int(exponent)))
return Node('*', values[0], node) # return Node('*', values[0], node)
if option == 4: # if option == 4:
# example: x4 -> x^4 # # example: x4 -> x^4
identifier, exponent = list(values[0]) # identifier, exponent = list(values[0])
return Node('^', Leaf(identifier), Leaf(int(exponent))) # return Node('^', Leaf(identifier), Leaf(int(exponent)))
raise BisonSyntaxError('Unsupported option %d in target "%s".' # raise BisonSyntaxError('Unsupported option %d in target "%s".'
% (option, target)) # pragma: nocover # % (option, target)) # pragma: nocover
# ----------------------------------------- # -----------------------------------------
# raw lex script, verbatim here # raw lex script, verbatim here
...@@ -230,11 +304,11 @@ class Parser(BisonParser): ...@@ -230,11 +304,11 @@ class Parser(BisonParser):
#define YY_INPUT(buf,result,max_size) { \ #define YY_INPUT(buf,result,max_size) { \
(*py_input)(py_parser, buf, &result, max_size); \ (*py_input)(py_parser, buf, &result, max_size); \
} }
/*[a-zA-Z][0-9]+ { returntoken(CONCAT_POW); }*/
%} %}
%% %%
[a-zA-Z][0-9]+ { returntoken(CONCAT_POW); }
[0-9]+ { returntoken(NUMBER); } [0-9]+ { returntoken(NUMBER); }
[a-zA-Z] { returntoken(IDENTIFIER); } [a-zA-Z] { returntoken(IDENTIFIER); }
"(" { returntoken(LPAREN); } "(" { returntoken(LPAREN); }
...@@ -247,6 +321,7 @@ class Parser(BisonParser): ...@@ -247,6 +321,7 @@ class Parser(BisonParser):
"," { returntoken(COMMA); } "," { returntoken(COMMA); }
"quit" { printf("lex: got QUIT\n"); yyterminate(); returntoken(QUIT); } "quit" { printf("lex: got QUIT\n"); yyterminate(); returntoken(QUIT); }
"raise" { returntoken(RAISE); } "raise" { returntoken(RAISE); }
"graph" { returntoken(GRAPH); }
[ \t\v\f] {} [ \t\v\f] {}
[\n] {yylineno++; returntoken(NEWLINE); } [\n] {yylineno++; returntoken(NEWLINE); }
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment