Commit 33b0863c authored by Robert Bradshaw's avatar Robert Bradshaw

Added for i from ... ['by' step] syntax.

E.g.

for i from 0 <= i < 10 by 2:
    print i

0
2
4
6
8

Old for-from loops remain exactly the same (using the ++ or --). If step is specified, the increment operator will be += step or -= step, depending on the orientation of the inequalities (as before).

NOTE: 'by' is now a keyword
parent eb232ced
...@@ -3297,6 +3297,7 @@ class ForFromStatNode(StatNode): ...@@ -3297,6 +3297,7 @@ class ForFromStatNode(StatNode):
# relation1 string # relation1 string
# relation2 string # relation2 string
# bound2 ExprNode # bound2 ExprNode
# step ExprNode or None
# body StatNode # body StatNode
# else_clause StatNode or None # else_clause StatNode or None
# #
...@@ -3318,6 +3319,9 @@ class ForFromStatNode(StatNode): ...@@ -3318,6 +3319,9 @@ class ForFromStatNode(StatNode):
self.bound2.analyse_types(env) self.bound2.analyse_types(env)
self.bound1 = self.bound1.coerce_to_integer(env) self.bound1 = self.bound1.coerce_to_integer(env)
self.bound2 = self.bound2.coerce_to_integer(env) self.bound2 = self.bound2.coerce_to_integer(env)
if self.step is not None:
self.step.analyse_types(env)
self.step = self.step.coerce_to_integer(env)
if not (self.bound2.is_name or self.bound2.is_literal): if not (self.bound2.is_name or self.bound2.is_literal):
self.bound2 = self.bound2.coerce_to_temp(env) self.bound2 = self.bound2.coerce_to_temp(env)
target_type = self.target.type target_type = self.target.type
...@@ -3337,6 +3341,8 @@ class ForFromStatNode(StatNode): ...@@ -3337,6 +3341,8 @@ class ForFromStatNode(StatNode):
ExprNodes.CloneNode(c_loopvar_node).coerce_to_pyobject(env) ExprNodes.CloneNode(c_loopvar_node).coerce_to_pyobject(env)
self.bound1.allocate_temps(env) self.bound1.allocate_temps(env)
self.bound2.allocate_temps(env) self.bound2.allocate_temps(env)
if self.step is not None:
self.step.allocate_temps(env)
if self.py_loopvar_node: if self.py_loopvar_node:
self.py_loopvar_node.allocate_temps(env) self.py_loopvar_node.allocate_temps(env)
self.target.allocate_target_temps(env) self.target.allocate_target_temps(env)
...@@ -3350,6 +3356,8 @@ class ForFromStatNode(StatNode): ...@@ -3350,6 +3356,8 @@ class ForFromStatNode(StatNode):
self.else_clause.analyse_expressions(env) self.else_clause.analyse_expressions(env)
self.bound1.release_temp(env) self.bound1.release_temp(env)
self.bound2.release_temp(env) self.bound2.release_temp(env)
if self.step is not None:
self.step.release_temp(env)
#env.recycle_pending_temps() # TEMPORARY #env.recycle_pending_temps() # TEMPORARY
def generate_execution_code(self, code): def generate_execution_code(self, code):
...@@ -3357,12 +3365,15 @@ class ForFromStatNode(StatNode): ...@@ -3357,12 +3365,15 @@ class ForFromStatNode(StatNode):
self.bound1.generate_evaluation_code(code) self.bound1.generate_evaluation_code(code)
self.bound2.generate_evaluation_code(code) self.bound2.generate_evaluation_code(code)
offset, incop = self.relation_table[self.relation1] offset, incop = self.relation_table[self.relation1]
if self.step is not None:
self.step.generate_evaluation_code(code)
incop = "%s=%s" % (incop[0], self.step.result_code)
code.putln( code.putln(
"for (%s = %s%s; %s %s %s; %s%s) {" % ( "for (%s = %s%s; %s %s %s; %s%s) {" % (
self.loopvar_name, self.loopvar_name,
self.bound1.result_code, offset, self.bound1.result_code, offset,
self.loopvar_name, self.relation2, self.bound2.result_code, self.loopvar_name, self.relation2, self.bound2.result_code,
incop, self.loopvar_name)) self.loopvar_name, incop))
if self.py_loopvar_node: if self.py_loopvar_node:
self.py_loopvar_node.generate_evaluation_code(code) self.py_loopvar_node.generate_evaluation_code(code)
self.target.generate_assignment_code(self.py_loopvar_node, code) self.target.generate_assignment_code(self.py_loopvar_node, code)
...@@ -3378,6 +3389,8 @@ class ForFromStatNode(StatNode): ...@@ -3378,6 +3389,8 @@ class ForFromStatNode(StatNode):
code.put_label(break_label) code.put_label(break_label)
self.bound1.generate_disposal_code(code) self.bound1.generate_disposal_code(code)
self.bound2.generate_disposal_code(code) self.bound2.generate_disposal_code(code)
if self.step is not None:
self.step.generate_disposal_code(code)
relation_table = { relation_table = {
# {relop : (initial offset, increment op)} # {relop : (initial offset, increment op)}
......
...@@ -1047,6 +1047,7 @@ def p_for_bounds(s): ...@@ -1047,6 +1047,7 @@ def p_for_bounds(s):
rel2_pos = s.position() rel2_pos = s.position()
rel2 = p_for_from_relation(s) rel2 = p_for_from_relation(s)
bound2 = p_bit_expr(s) bound2 = p_bit_expr(s)
step = p_for_from_step(s)
if not target.is_name: if not target.is_name:
error(target.pos, error(target.pos,
"Target of for-from statement must be a variable name") "Target of for-from statement must be a variable name")
...@@ -1060,7 +1061,8 @@ def p_for_bounds(s): ...@@ -1060,7 +1061,8 @@ def p_for_bounds(s):
'bound1': bound1, 'bound1': bound1,
'relation1': rel1, 'relation1': rel1,
'relation2': rel2, 'relation2': rel2,
'bound2': bound2 } 'bound2': bound2,
'step': step }
def p_for_from_relation(s): def p_for_from_relation(s):
if s.sy in inequality_relations: if s.sy in inequality_relations:
...@@ -1069,6 +1071,14 @@ def p_for_from_relation(s): ...@@ -1069,6 +1071,14 @@ def p_for_from_relation(s):
return op return op
else: else:
s.error("Expected one of '<', '<=', '>' '>='") s.error("Expected one of '<', '<=', '>' '>='")
def p_for_from_step(s):
if s.sy == 'by':
s.next()
step = p_bit_expr(s)
return step
else:
return None
inequality_relations = ('<', '<=', '>', '>=') inequality_relations = ('<', '<=', '>', '>=')
......
...@@ -138,7 +138,7 @@ reserved_words = [ ...@@ -138,7 +138,7 @@ reserved_words = [
"raise", "import", "exec", "try", "except", "finally", "raise", "import", "exec", "try", "except", "finally",
"while", "if", "elif", "else", "for", "in", "assert", "while", "if", "elif", "else", "for", "in", "assert",
"and", "or", "not", "is", "in", "lambda", "from", "and", "or", "not", "is", "in", "lambda", "from",
"NULL", "cimport" "NULL", "cimport", "by"
] ]
class Method: class Method:
......
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