all: implement iterators in for loops (#7867)
							parent
							
								
									82162b8ff8
								
							
						
					
					
						commit
						efb80bdffd
					
				| 
						 | 
					@ -2824,6 +2824,26 @@ fn (mut c Checker) stmt(node ast.Stmt) {
 | 
				
			||||||
				}
 | 
									}
 | 
				
			||||||
			} else {
 | 
								} else {
 | 
				
			||||||
				sym := c.table.get_type_symbol(typ)
 | 
									sym := c.table.get_type_symbol(typ)
 | 
				
			||||||
 | 
									if sym.kind == .struct_ {
 | 
				
			||||||
 | 
										// iterators
 | 
				
			||||||
 | 
										next_fn := sym.find_method('next') or {
 | 
				
			||||||
 | 
											c.error('a struct must have a `next()` method to be an iterator',
 | 
				
			||||||
 | 
												node.cond.position())
 | 
				
			||||||
 | 
											return
 | 
				
			||||||
 | 
										}
 | 
				
			||||||
 | 
										if !next_fn.return_type.has_flag(.optional) {
 | 
				
			||||||
 | 
											c.error('iterator method `next()` must return an optional', node.cond.position())
 | 
				
			||||||
 | 
										}
 | 
				
			||||||
 | 
										// the receiver
 | 
				
			||||||
 | 
										if next_fn.params.len != 1 {
 | 
				
			||||||
 | 
											c.error('iterator method `next()` must have 0 parameters', node.cond.position())
 | 
				
			||||||
 | 
										}
 | 
				
			||||||
 | 
										val_type := next_fn.return_type.clear_flag(.optional)
 | 
				
			||||||
 | 
										node.cond_type = typ
 | 
				
			||||||
 | 
										node.kind = sym.kind
 | 
				
			||||||
 | 
										node.val_type = val_type
 | 
				
			||||||
 | 
										node.scope.update_var_type(node.val_var, val_type)
 | 
				
			||||||
 | 
									} else {
 | 
				
			||||||
					if sym.kind == .map && !(node.key_var.len > 0 && node.val_var.len > 0) {
 | 
										if sym.kind == .map && !(node.key_var.len > 0 && node.val_var.len > 0) {
 | 
				
			||||||
						c.error('declare a key and a value variable when ranging a map: `for key, val in map {`\n' +
 | 
											c.error('declare a key and a value variable when ranging a map: `for key, val in map {`\n' +
 | 
				
			||||||
							'use `_` if you do not need the variable', node.pos)
 | 
												'use `_` if you do not need the variable', node.pos)
 | 
				
			||||||
| 
						 | 
					@ -2851,6 +2871,7 @@ fn (mut c Checker) stmt(node ast.Stmt) {
 | 
				
			||||||
					node.val_type = value_type
 | 
										node.val_type = value_type
 | 
				
			||||||
					node.scope.update_var_type(node.val_var, value_type)
 | 
										node.scope.update_var_type(node.val_var, value_type)
 | 
				
			||||||
				}
 | 
									}
 | 
				
			||||||
 | 
								}
 | 
				
			||||||
			c.check_loop_label(node.label, node.pos)
 | 
								c.check_loop_label(node.label, node.pos)
 | 
				
			||||||
			c.stmts(node.stmts)
 | 
								c.stmts(node.stmts)
 | 
				
			||||||
			c.loop_label = prev_loop_label
 | 
								c.loop_label = prev_loop_label
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -1305,6 +1305,27 @@ fn (mut g Gen) for_in(it ast.ForInStmt) {
 | 
				
			||||||
			g.expr(it.cond)
 | 
								g.expr(it.cond)
 | 
				
			||||||
			g.writeln('.str[$i];')
 | 
								g.writeln('.str[$i];')
 | 
				
			||||||
		}
 | 
							}
 | 
				
			||||||
 | 
						} else if it.kind == .struct_ {
 | 
				
			||||||
 | 
							cond_type_sym := g.table.get_type_symbol(it.cond_type)
 | 
				
			||||||
 | 
							next_fn := cond_type_sym.find_method('next') or {
 | 
				
			||||||
 | 
								verror('`next` method not found')
 | 
				
			||||||
 | 
								return
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
 | 
							ret_typ := next_fn.return_type
 | 
				
			||||||
 | 
							g.writeln('while (1) {')
 | 
				
			||||||
 | 
							t := g.new_tmp_var()
 | 
				
			||||||
 | 
							receiver_styp := g.typ(next_fn.params[0].typ)
 | 
				
			||||||
 | 
							fn_name := receiver_styp.replace_each(['*', '', '.', '__']) + '_next'
 | 
				
			||||||
 | 
							g.write('\t${g.typ(ret_typ)} $t = ${fn_name}(')
 | 
				
			||||||
 | 
							if !it.cond_type.is_ptr() {
 | 
				
			||||||
 | 
								g.write('&')
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
 | 
							g.expr(it.cond)
 | 
				
			||||||
 | 
							g.writeln(');')
 | 
				
			||||||
 | 
							g.writeln('\tif (!${t}.ok) { break; }')
 | 
				
			||||||
 | 
							val := if it.val_var in ['', '_'] { g.new_tmp_var() } else { it.val_var }
 | 
				
			||||||
 | 
							val_styp := g.typ(it.val_type)
 | 
				
			||||||
 | 
							g.writeln('\t$val_styp $val = *($val_styp*)${t}.data;')
 | 
				
			||||||
	} else {
 | 
						} else {
 | 
				
			||||||
		s := g.table.type_to_str(it.cond_type)
 | 
							s := g.table.type_to_str(it.cond_type)
 | 
				
			||||||
		g.error('for in: unhandled symbol `$it.cond` of type `$s`', it.pos)
 | 
							g.error('for in: unhandled symbol `$it.cond` of type `$s`', it.pos)
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -0,0 +1,36 @@
 | 
				
			||||||
 | 
					struct Doubler {
 | 
				
			||||||
 | 
					mut:
 | 
				
			||||||
 | 
						val int
 | 
				
			||||||
 | 
						until int
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					fn (mut it Doubler) next() ?int {
 | 
				
			||||||
 | 
						v := it.val
 | 
				
			||||||
 | 
						if v > it.until {
 | 
				
			||||||
 | 
							return none
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
						it.val *= 2
 | 
				
			||||||
 | 
						return v
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					fn doubler(start int, until int) Doubler {
 | 
				
			||||||
 | 
						return Doubler{start, until}
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					fn test_for_in_iterator() {
 | 
				
			||||||
 | 
						mut d := doubler(5, 30)
 | 
				
			||||||
 | 
						mut vals := []int{}
 | 
				
			||||||
 | 
						for val in d {
 | 
				
			||||||
 | 
							vals << val
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
						assert vals == [5, 10, 20]
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					fn test_for_in_empty_iterator() {
 | 
				
			||||||
 | 
						mut d := doubler(5, 2)
 | 
				
			||||||
 | 
						mut vals := []int{}
 | 
				
			||||||
 | 
						for val in d {
 | 
				
			||||||
 | 
							vals << val
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
						assert vals == []
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
		Loading…
	
		Reference in New Issue