package soko import ( "errors" "fmt" "io" "opslag.de/schobers/krampus19/gut" ) func ParseLevel(r io.Reader) (Level, error) { var l Level ctx := levelContext{&l} err := gut.ParseLines(r, ctx.parse) if err != nil { return Level{}, err } return l, nil } type levelContext struct { level *Level } func (c *levelContext) parse(p *gut.LineParser) gut.ParseLineFn { if p.EOF() { return nil } switch p.Peek() { case "level:": return c.parseContent case "": p.Next() // skip return c.parse default: return nil } } func (c *levelContext) parseContent(p *gut.LineParser) gut.ParseLineFn { if p.Next() != "level:" { return p.EmitErr(errors.New("expected level start")) } return c.parseRow } func (c *levelContext) parseRow(p *gut.LineParser) gut.ParseLineFn { if p.EOF() { return p.EmitErr(errors.New("unexpected end of file")) } line := p.Next() if line == ":level" { return c.parse } if c.level.Height == 0 { c.level.Width = len(line) / 2 } return c.addRow(p, line) } func (c *levelContext) addRow(p *gut.LineParser, line string) gut.ParseLineFn { var tiles []Tile var entities []EntityType for i := 0; i < len(line); i += 2 { tiles = append(tiles, Tile(line[i])) entities = append(entities, EntityType(line[i+1])) } for i, t := range tiles { if !t.IsValid() { return p.EmitErr(fmt.Errorf("level contains invalid Tile at (%d, %d)", i, c.level.Height)) } } for i, e := range entities { if !e.IsValid() { return p.EmitErr(fmt.Errorf("level contains invalid entity type at (%d, %d)", i, c.level.Height)) } } c.level.Height++ c.level.Tiles = append(c.level.Tiles, tiles...) c.level.Entities = append(c.level.Entities, entities...) return c.parseRow }