<commit_before>package main

import (
	"errors"
	"fmt"
	"image/color"
	"os"
	"strconv"
)

// DrawingMode defines the type of each drawing mode
type DrawingMode int

const (
	// DrawLineMode is a draw argument that draws 2D lines onto the Image
	DrawLineMode DrawingMode = iota
	// DrawPolygonMode is a draw argument that draws 3D polygons onto the Image
	DrawPolygonMode
)

// Parser is a script parser
type Parser struct {
	frame  *Image
	em     *Matrix
	tm     *Matrix
	cs     *Stack
	lexer  *Lexer
	backup []Token
}

// NewParser returns a new parser
func NewParser() *Parser {
	cs := NewStack()
	cs.Push(IdentityMatrix(4))
	return &Parser{
		frame:  NewImage(DefaultHeight, DefaultWidth),
		em:     NewMatrix(4, 0),
		tm:     IdentityMatrix(4),
		cs:     cs,
		backup: make([]Token, 0, 10),
	}
}

// ParseFile parses a file for commands and executes them
func (p *Parser) ParseFile(filename string) error {
	f, err := os.Open(filename)
	if err != nil {
		return err
	}
	defer f.Close()

	p.lexer = NewLexer()
	p.lexer.Lex(f)
	for {
		t := p.next()
		switch t.tt {
		case tError:
			return errors.New(t.value)
		case tEOF:
			return nil
		case tIdent:
			err := p.parseIdent(t)
			if err != nil {
				return err
			}
		default:
			return fmt.Errorf("unexpected token %v", t)
		}
	}
}

func (p *Parser) parseIdent(t Token) error {
	var err error
	switch t.value {
	case "line":
		x0 := p.nextFloat()
		y0 := p.nextFloat()
		z0 := p.nextFloat()
		x1 := p.nextFloat()
		y1 := p.nextFloat()
		z1 := p.nextFloat()
		err = p.line(x0, y0, z0, x1, y1, z1)
	case "ident":
		p.tm = IdentityMatrix(4)
	case "scale":
		sx := p.nextFloat()
		sy := p.nextFloat()
		sz := p.nextFloat()
		err = p.scale(sx, sy, sz)
	case "move":
		x := p.nextFloat()
		y := p.nextFloat()
		z := p.nextFloat()
		err = p.move(x, y, z)
	case "rotate":
		axis := p.nextString()
		theta := p.nextFloat()
		err = p.rotate(axis, theta)
	case "save":
		filename := p.nextString()
		err = p.save(filename)
	case "display":
		err = p.display()
	case "circle":
		cx := p.nextFloat()
		cy := p.nextFloat()
		cz := p.nextFloat()
		radius := p.nextFloat()
		err = p.circle(cx, cy, cz, radius)
	case "hermite":
		x0 := p.nextFloat()
		y0 := p.nextFloat()
		x1 := p.nextFloat()
		y1 := p.nextFloat()
		dx0 := p.nextFloat()
		dy0 := p.nextFloat()
		dx1 := p.nextFloat()
		dy1 := p.nextFloat()
		err = p.hermite(x0, y0, x1, y1, dx0, dy0, dx1, dy1)
	case "bezier":
		x0 := p.nextFloat()
		y0 := p.nextFloat()
		x1 := p.nextFloat()
		y1 := p.nextFloat()
		x2 := p.nextFloat()
		y2 := p.nextFloat()
		x3 := p.nextFloat()
		y3 := p.nextFloat()
		err = p.bezier(x0, y0, x1, y1, x2, y2, x3, y3)
	case "box":
		x := p.nextFloat()
		y := p.nextFloat()
		z := p.nextFloat()
		width := p.nextFloat()
		height := p.nextFloat()
		depth := p.nextFloat()
		err = p.box(x, y, z, width, height, depth)
	case "clear":
		p.clear()
	case "sphere":
		cx := p.nextFloat()
		cy := p.nextFloat()
		cz := p.nextFloat()
		radius := p.nextFloat()
		err = p.sphere(cx, cy, cz, radius)
	case "torus":
		cx := p.nextFloat()
		cy := p.nextFloat()
		cz := p.nextFloat()
		r1 := p.nextFloat()
		r2 := p.nextFloat()
		err = p.torus(cx, cy, cz, r1, r2)
	case "push":
		top := p.cs.Peek()
		if top != nil {
			p.cs.Push(top.Copy())
		}
	case "pop":
		p.cs.Pop()
	default:
		err = fmt.Errorf("unrecognized identifier: \"%s\"", t.value)
	}
	return err
}

func (p *Parser) next() Token {
	lenBackup := len(p.backup)
	if lenBackup > 0 {
		token := p.backup[lenBackup-1]
		p.backup = p.backup[:lenBackup-1]
		return token
	}
	token := <-p.lexer.out
	for token.tt == tComment {
		token = <-p.lexer.out
	}
	return token
}

func (p *Parser) nextFloat() float64 {
	if p.requireNext(tInt) != nil && p.requireNext(tFloat) != nil {
		panic(fmt.Errorf("%s expected, got %s", tFloat, p.peek().tt))
	}
	v, _ := strconv.ParseFloat(p.next().value, 64)
	return v
}

func (p *Parser) nextString() string {
	if p.requireNext(tString) != nil {
		panic(fmt.Errorf("%s expected, got %s", tString, p.peek().tt))
	}
	return p.next().value
}

func (p *Parser) unread(token Token) {
	if p.backup == nil {
		p.backup = make([]Token, 0, 10)
	}
	p.backup = append(p.backup, token)
}

func (p *Parser) peek() Token {
	token := p.next()
	p.unread(token)
	return token
}

func (p *Parser) requireNext(tt TokenType) error {
	other := p.peek().tt
	if other != tt {
		return fmt.Errorf("unexpected %v; expected %v", other, tt)
	}
	return nil
}

func (p *Parser) line(x0, y0, z0, x1, y1, z1 float64) error {
	p.em.AddEdge(x0, y0, z0, x1, y1, z1)
	err := p.apply(DrawLineMode)
	return err
}

func (p *Parser) scale(sx, sy, sz float64) error {
	dilation := MakeDilation(sx, sy, sz)

	top := p.cs.Pop()
	top, err := top.Multiply(dilation)
	if err != nil {
		return err
	}
	p.cs.Push(top)
	return nil
}

func (p *Parser) move(x, y, z float64) error {
	translation := MakeTranslation(x, y, z)
	top := p.cs.Pop()
	top, err := top.Multiply(translation)
	if err != nil {
		return err
	}
	p.cs.Push(top)

	return nil
}

func (p *Parser) rotate(axis string, theta float64) error {
	var rotation *Matrix
	switch axis {
	case "x":
		rotation = MakeRotX(theta)
	case "y":
		rotation = MakeRotY(theta)
	case "z":
		rotation = MakeRotZ(theta)
	default:
		return errors.New("axis must be \"x\", \"y\", or \"z\"")
	}

	top := p.cs.Pop()
	top, err := top.Multiply(rotation)
	if err != nil {
		return err
	}
	p.cs.Push(top)

	return nil
}

func (p *Parser) apply(mode DrawingMode) error {
	product, err := p.cs.Peek().Multiply(p.em)
	if err != nil {
		return err
	}
	p.em = product
	p.draw(mode)
	p.clear()

	return nil
}

func (p *Parser) draw(mode DrawingMode) {
	switch mode {
	case DrawLineMode:
		p.frame.DrawLines(p.em, color.White)
	case DrawPolygonMode:
		p.frame.DrawPolygons(p.em, color.White)
	default:
	}
}

func (p *Parser) save(filename string) error {
	err := p.frame.Save(filename)
	return err
}

func (p *Parser) display() error {
	err := p.frame.Display()
	return err
}

func (p *Parser) circle(cx, cy, cz, radius float64) error {
	p.em.AddCircle(cx, cy, cz, radius)
	err := p.apply(DrawLineMode)
	return err
}

func (p *Parser) hermite(x0, y0, x1, y1, dx0, dy0, dx1, dy1 float64) error {
	p.em.AddHermite(x0, y0, x1, y1, dx0, dy0, dx1, dy1)
	err := p.apply(DrawLineMode)

	return err
}

func (p *Parser) bezier(x0, y0, x1, y1, x2, y2, x3, y3 float64) error {
	p.em.AddBezier(x0, y0, x1, y1, x2, y2, x3, y3)
	err := p.apply(DrawLineMode)
	return err
}

func (p *Parser) box(x, y, z, width, height, depth float64) error {
	p.em.AddBox(x, y, z, width, height, depth)
	err := p.apply(DrawPolygonMode)
	return err
}

func (p *Parser) clear() {
	p.em = NewMatrix(4, 0)
}

func (p *Parser) sphere(cx, cy, cz, radius float64) error {
	p.em.AddSphere(cx, cy, cz, radius)
	err := p.apply(DrawPolygonMode)
	return err
}

func (p *Parser) torus(cx, cy, cz, r1, r2 float64) error {
	p.em.AddTorus(cx, cy, cz, r1, r2)
	err := p.apply(DrawPolygonMode)

	return err
}
<commit_msg>Change format for a lexing error<commit_after>package main

import (
	"errors"
	"fmt"
	"image/color"
	"os"
	"strconv"
)

// DrawingMode defines the type of each drawing mode
type DrawingMode int

const (
	// DrawLineMode is a draw argument that draws 2D lines onto the Image
	DrawLineMode DrawingMode = iota
	// DrawPolygonMode is a draw argument that draws 3D polygons onto the Image
	DrawPolygonMode
)

// Parser is a script parser
type Parser struct {
	frame  *Image
	em     *Matrix
	tm     *Matrix
	cs     *Stack
	lexer  *Lexer
	backup []Token
}

// NewParser returns a new parser
func NewParser() *Parser {
	cs := NewStack()
	cs.Push(IdentityMatrix(4))
	return &Parser{
		frame:  NewImage(DefaultHeight, DefaultWidth),
		em:     NewMatrix(4, 0),
		tm:     IdentityMatrix(4),
		cs:     cs,
		backup: make([]Token, 0, 10),
	}
}

// ParseFile parses a file for commands and executes them
func (p *Parser) ParseFile(filename string) error {
	f, err := os.Open(filename)
	if err != nil {
		return err
	}
	defer f.Close()

	p.lexer = NewLexer()
	p.lexer.Lex(f)
	for {
		t := p.next()
		switch t.tt {
		case tError:
			return errors.New(t.value)
		case tEOF:
			return nil
		case tIdent:
			err := p.parseIdent(t)
			if err != nil {
				return err
			}
		default:
			return fmt.Errorf("unexpected token %v", t)
		}
	}
}

func (p *Parser) parseIdent(t Token) error {
	var err error
	switch t.value {
	case "line":
		x0 := p.nextFloat()
		y0 := p.nextFloat()
		z0 := p.nextFloat()
		x1 := p.nextFloat()
		y1 := p.nextFloat()
		z1 := p.nextFloat()
		err = p.line(x0, y0, z0, x1, y1, z1)
	case "ident":
		p.tm = IdentityMatrix(4)
	case "scale":
		sx := p.nextFloat()
		sy := p.nextFloat()
		sz := p.nextFloat()
		err = p.scale(sx, sy, sz)
	case "move":
		x := p.nextFloat()
		y := p.nextFloat()
		z := p.nextFloat()
		err = p.move(x, y, z)
	case "rotate":
		axis := p.nextString()
		theta := p.nextFloat()
		err = p.rotate(axis, theta)
	case "save":
		filename := p.nextString()
		err = p.save(filename)
	case "display":
		err = p.display()
	case "circle":
		cx := p.nextFloat()
		cy := p.nextFloat()
		cz := p.nextFloat()
		radius := p.nextFloat()
		err = p.circle(cx, cy, cz, radius)
	case "hermite":
		x0 := p.nextFloat()
		y0 := p.nextFloat()
		x1 := p.nextFloat()
		y1 := p.nextFloat()
		dx0 := p.nextFloat()
		dy0 := p.nextFloat()
		dx1 := p.nextFloat()
		dy1 := p.nextFloat()
		err = p.hermite(x0, y0, x1, y1, dx0, dy0, dx1, dy1)
	case "bezier":
		x0 := p.nextFloat()
		y0 := p.nextFloat()
		x1 := p.nextFloat()
		y1 := p.nextFloat()
		x2 := p.nextFloat()
		y2 := p.nextFloat()
		x3 := p.nextFloat()
		y3 := p.nextFloat()
		err = p.bezier(x0, y0, x1, y1, x2, y2, x3, y3)
	case "box":
		x := p.nextFloat()
		y := p.nextFloat()
		z := p.nextFloat()
		width := p.nextFloat()
		height := p.nextFloat()
		depth := p.nextFloat()
		err = p.box(x, y, z, width, height, depth)
	case "clear":
		p.clear()
	case "sphere":
		cx := p.nextFloat()
		cy := p.nextFloat()
		cz := p.nextFloat()
		radius := p.nextFloat()
		err = p.sphere(cx, cy, cz, radius)
	case "torus":
		cx := p.nextFloat()
		cy := p.nextFloat()
		cz := p.nextFloat()
		r1 := p.nextFloat()
		r2 := p.nextFloat()
		err = p.torus(cx, cy, cz, r1, r2)
	case "push":
		top := p.cs.Peek()
		if top != nil {
			p.cs.Push(top.Copy())
		}
	case "pop":
		p.cs.Pop()
	default:
		err = fmt.Errorf("unrecognized identifier: \"%s\"", t.value)
	}
	return err
}

func (p *Parser) next() Token {
	lenBackup := len(p.backup)
	if lenBackup > 0 {
		token := p.backup[lenBackup-1]
		p.backup = p.backup[:lenBackup-1]
		return token
	}
	token := <-p.lexer.out
	for token.tt == tComment {
		token = <-p.lexer.out
	}
	return token
}

func (p *Parser) nextFloat() float64 {
	if p.requireNext(tInt) != nil && p.requireNext(tFloat) != nil {
		panic(fmt.Errorf("expected %v, got %v", tFloat, p.peek().tt))
	}
	v, _ := strconv.ParseFloat(p.next().value, 64)
	return v
}

func (p *Parser) nextString() string {
	if p.requireNext(tString) != nil {
		panic(fmt.Errorf("expected %v, got %v", tString, p.peek().tt))
	}
	return p.next().value
}

func (p *Parser) unread(token Token) {
	if p.backup == nil {
		p.backup = make([]Token, 0, 10)
	}
	p.backup = append(p.backup, token)
}

func (p *Parser) peek() Token {
	token := p.next()
	p.unread(token)
	return token
}

func (p *Parser) requireNext(tt TokenType) error {
	other := p.peek().tt
	if other != tt {
		return fmt.Errorf("expected %v, got %v", tt, other)
	}
	return nil
}

func (p *Parser) line(x0, y0, z0, x1, y1, z1 float64) error {
	p.em.AddEdge(x0, y0, z0, x1, y1, z1)
	err := p.apply(DrawLineMode)
	return err
}

func (p *Parser) scale(sx, sy, sz float64) error {
	dilation := MakeDilation(sx, sy, sz)

	top := p.cs.Pop()
	top, err := top.Multiply(dilation)
	if err != nil {
		return err
	}
	p.cs.Push(top)
	return nil
}

func (p *Parser) move(x, y, z float64) error {
	translation := MakeTranslation(x, y, z)
	top := p.cs.Pop()
	top, err := top.Multiply(translation)
	if err != nil {
		return err
	}
	p.cs.Push(top)

	return nil
}

func (p *Parser) rotate(axis string, theta float64) error {
	var rotation *Matrix
	switch axis {
	case "x":
		rotation = MakeRotX(theta)
	case "y":
		rotation = MakeRotY(theta)
	case "z":
		rotation = MakeRotZ(theta)
	default:
		return errors.New("axis must be \"x\", \"y\", or \"z\"")
	}

	top := p.cs.Pop()
	top, err := top.Multiply(rotation)
	if err != nil {
		return err
	}
	p.cs.Push(top)

	return nil
}

func (p *Parser) apply(mode DrawingMode) error {
	product, err := p.cs.Peek().Multiply(p.em)
	if err != nil {
		return err
	}
	p.em = product
	p.draw(mode)
	p.clear()

	return nil
}

func (p *Parser) draw(mode DrawingMode) {
	switch mode {
	case DrawLineMode:
		p.frame.DrawLines(p.em, color.White)
	case DrawPolygonMode:
		p.frame.DrawPolygons(p.em, color.White)
	default:
	}
}

func (p *Parser) save(filename string) error {
	err := p.frame.Save(filename)
	return err
}

func (p *Parser) display() error {
	err := p.frame.Display()
	return err
}

func (p *Parser) circle(cx, cy, cz, radius float64) error {
	p.em.AddCircle(cx, cy, cz, radius)
	err := p.apply(DrawLineMode)
	return err
}

func (p *Parser) hermite(x0, y0, x1, y1, dx0, dy0, dx1, dy1 float64) error {
	p.em.AddHermite(x0, y0, x1, y1, dx0, dy0, dx1, dy1)
	err := p.apply(DrawLineMode)

	return err
}

func (p *Parser) bezier(x0, y0, x1, y1, x2, y2, x3, y3 float64) error {
	p.em.AddBezier(x0, y0, x1, y1, x2, y2, x3, y3)
	err := p.apply(DrawLineMode)
	return err
}

func (p *Parser) box(x, y, z, width, height, depth float64) error {
	p.em.AddBox(x, y, z, width, height, depth)
	err := p.apply(DrawPolygonMode)
	return err
}

func (p *Parser) clear() {
	p.em = NewMatrix(4, 0)
}

func (p *Parser) sphere(cx, cy, cz, radius float64) error {
	p.em.AddSphere(cx, cy, cz, radius)
	err := p.apply(DrawPolygonMode)
	return err
}

func (p *Parser) torus(cx, cy, cz, r1, r2 float64) error {
	p.em.AddTorus(cx, cy, cz, r1, r2)
	err := p.apply(DrawPolygonMode)

	return err
}
<|endoftext|>


<commit_before>package template

import (
	"fmt"
	"io/ioutil"
	"os"
	"strconv"
)

type parser struct {
	l   *lexer
	tok token
	lit []byte
	s   *scope
}

func (p *parser) Error(format string, args ...interface{}) {
	panic(fmt.Sprintf(format, args...))
}

func (p *parser) next() {
	p.tok, p.lit = p.l.scan()
}

func (p *parser) Expect(tok token) string {
	if p.tok != tok {
		p.Error("expected %s, got %s", tokStrings[tok], tokStrings[p.tok])
	}
	lit := p.lit
	p.next()
	return string(lit)
}

func (p *parser) ExpectWord(word string) {
	if p.tok != tokIdent || string(p.lit) != word {
		p.Error("expected ident %s, got token %s, %s", word, tokStrings[p.tok], p.lit)
	}
	p.next()
}

// parse until one of the following tags
func (p *parser) ParseUntil(tags ...string) (string, NodeList) {
	r := make(NodeList, 0, 10)
	for p.tok != tokEof {
		switch p.tok {
		case tokText:
			r = append(r, printLit(p.lit))
			p.next()
		case tokBlockTagStart:
			p.next()
			lit := string(p.lit)
			for _, t := range tags {
				if t == lit {
					p.next()
					return t, r
				}
			}
			r = append(r, p.parseBlockTag())
		case tokVarTagStart:
			r = append(r, p.parseVarTag())
		default:
			p.Error("unexpected token %s", tokStrings[p.tok])
		}
	}
	return "", r
}

func (p *parser) parseBlockTag() Node {
	if tag, ok := tags[p.Expect(tokIdent)]; ok {
		return tag(p)
	}
	p.Error("tag isn't registered")
	return nil
}

func (p *parser) parseVarTag() Node {
	p.Expect(tokVarTagStart)
	e := p.parseExpr()
	p.Expect(tokVarTagEnd)
	return e
}

func (p *parser) parseExpr() valuer {
	v := p.parseVal()
	a := p.parseAttrs()
	f := p.parseFilters()
	if len(a) == 0 && len(f) == 0 {
		return v
	}
	return &expr{v, a, f}
}

func (p *parser) parseVal() valuer {
	var ret valuer
	switch p.tok {
	case tokInt:
		i, err := strconv.Atoi64(string(p.lit))
		if err != nil {
			p.Error("internal int error: %s", err)
		}
		ret = intLit(i)
		p.next()
	case tokFloat:
		f, err := strconv.Atof64(string(p.lit))
		if err != nil {
			p.Error("Internal float error: %s", err)
		}
		ret = floatLit(f)
		p.next()
	case tokString:
		ret = stringLit(p.lit)
		p.next()
	case tokIdent:
		ret = p.parseVar()
	default:
		p.Error("Unexpected token %s", tokStrings[p.tok])
	}
	return ret
}

func (p *parser) parseVar() variable {
	return p.s.Lookup(p.Expect(tokIdent))
}

func (p *parser) parseAttrs() []string {
	var attrs []string
	for p.tok == tokDot {
		p.Expect(tokDot)
		attrs = append(attrs, string(p.lit))
		if p.tok == tokInt {
			p.next()
		} else {
			p.Expect(tokIdent)
		}
	}
	return attrs
}

func (p *parser) parseFilters() []*filter {
	f := make([]*filter, 0, 2)
	for p.tok == tokFilter {
		p.next()
		rf, ok := filters[string(p.lit)]
		if !ok {
			p.Error("filter does not exist")
		}
		p.Expect(tokIdent)
		var val valuer
		args := false
		switch rf.arg {
		case ReqArg:
			args = true
		case OptArg:
			args = p.tok == tokArgument
		case NoArg:
			if p.tok == tokArgument {
				p.Error("filter accepts no arguments")
			}
		}
		if args {
			p.Expect(tokArgument)
			val = p.parseExpr()
		}
		f = append(f, &filter{rf.f, val})
	}
	return f
}

func Parse(s []byte) (*Template, os.Error) {
	t := new(Template)
	l := &lexer{src: s}
	l.init()
	p := &parser{l: l, s: newScope()}

	p.next()
	_, t.nodes = p.ParseUntil()
	t.scope = p.s

	return t, nil
}

func ParseString(s string) (*Template, os.Error) {
	return Parse([]byte(s))
}

func ParseFile(name string) (*Template, os.Error) {
	b, err := ioutil.ReadFile(name)
	if err != nil {
		return nil, err
	}
	return Parse(b)
}
<commit_msg>Don't preallocate filters<commit_after>package template

import (
	"fmt"
	"io/ioutil"
	"os"
	"strconv"
)

type parser struct {
	l   *lexer
	tok token
	lit []byte
	s   *scope
}

func (p *parser) Error(format string, args ...interface{}) {
	panic(fmt.Sprintf(format, args...))
}

func (p *parser) next() {
	p.tok, p.lit = p.l.scan()
}

func (p *parser) Expect(tok token) string {
	if p.tok != tok {
		p.Error("expected %s, got %s", tokStrings[tok], tokStrings[p.tok])
	}
	lit := p.lit
	p.next()
	return string(lit)
}

func (p *parser) ExpectWord(word string) {
	if p.tok != tokIdent || string(p.lit) != word {
		p.Error("expected ident %s, got token %s, %s", word, tokStrings[p.tok], p.lit)
	}
	p.next()
}

// parse until one of the following tags
func (p *parser) ParseUntil(tags ...string) (string, NodeList) {
	r := make(NodeList, 0, 10)
	for p.tok != tokEof {
		switch p.tok {
		case tokText:
			r = append(r, printLit(p.lit))
			p.next()
		case tokBlockTagStart:
			p.next()
			lit := string(p.lit)
			for _, t := range tags {
				if t == lit {
					p.next()
					return t, r
				}
			}
			r = append(r, p.parseBlockTag())
		case tokVarTagStart:
			r = append(r, p.parseVarTag())
		default:
			p.Error("unexpected token %s", tokStrings[p.tok])
		}
	}
	return "", r
}

func (p *parser) parseBlockTag() Node {
	if tag, ok := tags[p.Expect(tokIdent)]; ok {
		return tag(p)
	}
	p.Error("tag isn't registered")
	return nil
}

func (p *parser) parseVarTag() Node {
	p.Expect(tokVarTagStart)
	e := p.parseExpr()
	p.Expect(tokVarTagEnd)
	return e
}

func (p *parser) parseExpr() valuer {
	v := p.parseVal()
	a := p.parseAttrs()
	f := p.parseFilters()
	if len(a) == 0 && len(f) == 0 {
		return v
	}
	return &expr{v, a, f}
}

func (p *parser) parseVal() valuer {
	var ret valuer
	switch p.tok {
	case tokInt:
		i, err := strconv.Atoi64(string(p.lit))
		if err != nil {
			p.Error("internal int error: %s", err)
		}
		ret = intLit(i)
		p.next()
	case tokFloat:
		f, err := strconv.Atof64(string(p.lit))
		if err != nil {
			p.Error("Internal float error: %s", err)
		}
		ret = floatLit(f)
		p.next()
	case tokString:
		ret = stringLit(p.lit)
		p.next()
	case tokIdent:
		ret = p.parseVar()
	default:
		p.Error("Unexpected token %s", tokStrings[p.tok])
	}
	return ret
}

func (p *parser) parseVar() variable {
	return p.s.Lookup(p.Expect(tokIdent))
}

func (p *parser) parseAttrs() []string {
	var attrs []string
	for p.tok == tokDot {
		p.Expect(tokDot)
		attrs = append(attrs, string(p.lit))
		if p.tok == tokInt {
			p.next()
		} else {
			p.Expect(tokIdent)
		}
	}
	return attrs
}

func (p *parser) parseFilters() []*filter {
	var f []*filter
	for p.tok == tokFilter {
		p.next()
		rf, ok := filters[string(p.lit)]
		if !ok {
			p.Error("filter does not exist")
		}
		p.Expect(tokIdent)
		var val valuer
		args := false
		switch rf.arg {
		case ReqArg:
			args = true
		case OptArg:
			args = p.tok == tokArgument
		case NoArg:
			if p.tok == tokArgument {
				p.Error("filter accepts no arguments")
			}
		}
		if args {
			p.Expect(tokArgument)
			val = p.parseExpr()
		}
		f = append(f, &filter{rf.f, val})
	}
	return f
}

func Parse(s []byte) (*Template, os.Error) {
	t := new(Template)
	l := &lexer{src: s}
	l.init()
	p := &parser{l: l, s: newScope()}

	p.next()
	_, t.nodes = p.ParseUntil()
	t.scope = p.s

	return t, nil
}

func ParseString(s string) (*Template, os.Error) {
	return Parse([]byte(s))
}

func ParseFile(name string) (*Template, os.Error) {
	b, err := ioutil.ReadFile(name)
	if err != nil {
		return nil, err
	}
	return Parse(b)
}
<|endoftext|>


<commit_before>package main

import (
	"fmt"
	"unicode/utf8"
)

type Tag struct {
	spaces int
	tag string
}

type Parser struct {
	Scanner Scanner
	output string

	// nested html tags for backtacking
	//tags []string
	// space counts for each indent level for backtacking
	//indents []int

	// heirarchy
	line []Tag

	// for indent calculations, how many spaces should a tab equal
	tabSpace int

	// states as the Parser object figures out how to output the tokens

	// new line has started
	isNewLine bool

	// rest of tokens before TokNewLine is regular text
	isText bool

	// whitespace is significant. This is to distinguish [] syntax, not indent
	isBracket bool

	isIndent bool

	tag Tag
}

func (p *Parser) Output() string {
	var tokens []rune
	var text string

	p.line = []Tag{}
	p.tabSpace = 8
	p.output = ""
	p.isNewLine = true
	p.isText = false
	p.isIndent = true
	p.tag = Tag{spaces:0, tag:""}

	for {
		tokens = p.Scanner.Scan()
		for _, token := range tokens {
			text = p.Scanner.TokenText()
			p.processToken(token, text)
			fmt.Println(TokenString(token))
		}
		if tokens[0] == TokEOF {
			break
		}
	}
	return p.output
}

func (p *Parser) closeLastTag() {
	if len(p.line) > 0 {
		oldTag := p.line[len(p.line) - 1]
		p.line = p.line[0: len(p.line) - 1]
		if r, _ := utf8.DecodeLastRuneInString(p.output); r == ' ' {
			p.output = p.output[0: len(p.output) - 1]
		}
		p.output += "</" + oldTag.tag + ">"
	}
}

func (p *Parser) processToken(tok rune, text string) {
	switch tok {
	case TokWord:
		if p.isNewLine {
			// since there were no indents or dedents, this tag is the new end of the line
			p.closeLastTag()
			p.tag.tag = text
			p.output += "<" + text + ">"
			p.line = append(p.line, p.tag)
			p.isNewLine = false
		} else {
			p.output += text
			p.output += " "
		}
	case TokIndent:
		p.isNewLine = false
	case TokDedent:
		p.isNewLine = false
		// for dedents, pop off the line and close the tag
		p.closeLastTag()
	case TokNewLine:
		p.isNewLine = true
	case TokEOF:
		// close the remaining tags in the line
		for i := len(p.line) - 1; i >= 0; i -= 1 {
			p.output += "</" + p.line[i].tag + ">"
		}
	}
}

<commit_msg>much better parsing algo started, stack based<commit_after>package main

import (
	"fmt"
	"unicode/utf8"
)

type Node struct {
	tag string
	attrs map[string]string
	text string
}

type Parser struct {
	Scanner Scanner
	output string

	// node heirarchy
	stack []*Node

	// states as the Parser object figures out how to output the tokens

	// new line has started
	isNewLine bool

	isIndent bool
	isDedent bool
	dedentCount int

	node *Node
}

func (p *Parser) Output() string {
	var tokens []rune
	var text string

	p.stack = make([]*Node, 0)
	p.output = ""
	p.isNewLine = true
	p.isIndent = false
	p.isDedent = false
	p.dedentCount = 0
	p.node = new(Node)

	for {
		tokens = p.Scanner.Scan()
		for _, token := range tokens {
			text = p.Scanner.TokenText()
			p.processToken(token, text)
			fmt.Println(TokenString(token))
		}
		if tokens[0] == TokEOF {
			break
		}
	}
	return p.output
}

func (p *Parser) closeLastNode() {
	if len(p.stack) > 0 {
		oldNode := p.stack[len(p.stack) - 1]
		p.stack = p.stack[0: len(p.stack) - 1]
		if r, l := utf8.DecodeLastRuneInString(p.output); r == ' ' {
			p.output = p.output[0: len(p.output) - l]
		}
		p.output += "</" + oldNode.tag + ">"
	}
}

func (p *Parser) lastNode() *Node {
	return p.stack[len(p.stack) - 1]
}

func (p *Parser) outputNode(node *Node) {
	p.output += "<" + node.tag + ">"
	p.output += node.text
}

func (p *Parser) processToken(tok rune, text string) {
	switch tok {
	case TokWord:
		if p.node.tag == "" {
			p.node.tag = text
		} else {
			p.node.text += text
		}
	case TokWhitespace:
		p.node.text += text
	case TokIndent:
		p.isIndent = true
		p.isDedent = false
	case TokDedent:
		p.isIndent = false
		p.isDedent = true
		p.dedentCount += 1
	case TokNewLine:
		// up to this point a node has been constructed but not appended to the
		// output

		fmt.Println(p.node)
		if p.isDedent {
			// for dedents, pop off the stack and close the node
			for i := 0; i < p.dedentCount; i += 1 {
				p.closeLastNode()
			}
			// closing one more time because this new node replaces the top node in the
			// stack
			p.closeLastNode()
			p.outputNode(p.node)
			p.stack = append(p.stack, p.node)
		} else if p.isIndent {
			p.outputNode(p.node)
			p.stack = append(p.stack, p.node)
		} else {
			p.closeLastNode()
			p.outputNode(p.node)
			p.stack = append(p.stack, p.node)
		}
		p.node = new(Node)
		p.isIndent = false
		p.isDedent = false
	case TokEOF:
		if p.node.tag != "" {
			p.stack = append(p.stack, p.node)
			p.outputNode(p.node)
		}
		// close the remaining nodes in the stack
		for len(p.stack) > 0 {
			p.closeLastNode()
		}
	}
}

<|endoftext|>


<commit_before>package wellington

import (
	"bytes"
	"errors"
	"fmt"
	"io"
	"log"
	"path/filepath"

	libsass "github.com/wellington/go-libsass"
	// TODO: Remove dot imports
	"github.com/wellington/wellington/lexer"
)

func init() {
	log.SetFlags(log.Lshortfile | log.Ldate | log.Ltime)
}

// Replace holds token values for replacing source input with parsed input.
// DEPRECATED
type Replace struct {
	Start, End int
	Value      []byte
}

// Parser represents a parser engine that returns parsed and imported code
// from the input useful for doing text manipulation before passing to libsass.
type Parser struct {
	Idx, shift           int
	Chop                 []Replace
	Pwd, Input, MainFile string
	SassDir, BuildDir,

	ProjDir string
	Imports    libsass.Imports
	ImageDir   string
	Includes   []string
	Items      []lexer.Item
	Output     []byte
	Line       map[int]string
	LineKeys   []int
	PartialMap *SafePartialMap
}

// NewParser returns a pointer to a Parser object.
func NewParser() *Parser {
	p := &Parser{PartialMap: NewPartialMap()}
	p.Imports.Init()
	return p
}

// Start reads the tokens from the lexer and performs
// conversions and/or substitutions for sprite*() calls.
//
// Start creates a map of all variables and sprites
// (created via sprite-map calls).
// TODO: Remove pkgdir, it can be put on Parser
func (p *Parser) Start(r io.Reader, pkgdir string) ([]byte, error) {

	if r == nil {
		return []byte{}, errors.New("input is empty")
	}

	var in io.Reader
	var err error

	in, err = ToScssReader(r)

	if err != nil {
		return nil, err
	}

	p.Line = make(map[int]string)

	// Setup paths
	if p.MainFile == "" {
		p.MainFile = "stdin"
	}
	if p.BuildDir == "" {
		p.BuildDir = pkgdir
	}
	if p.SassDir == "" {
		p.SassDir = pkgdir
	}
	buf := bytes.NewBuffer(make([]byte, 0, bytes.MinRead))
	if in == nil {
		return []byte{}, fmt.Errorf("input is empty")
	}
	_, err = buf.ReadFrom(in)
	if err != nil {
		return []byte{}, err
	}

	// Send original byte slice
	p.Output = buf.Bytes() //[]byte(p.Input)

	return p.Output, nil
}

// StartParser accepts build arguments
// TODO: Should this be called StartParser or NewParser?
// TODO: Should this function create the partialMap or is this
// the right way to inject one?
func StartParser(ctx *libsass.Context, in io.Reader, out io.Writer, partialMap *SafePartialMap) (*Parser, error) {
	// Run the sprite_sass parser prior to passing to libsass
	parser := NewParser()

	parser.ImageDir = ctx.ImageDir
	parser.Includes = ctx.IncludePaths
	parser.BuildDir = ctx.BuildDir
	parser.MainFile = ctx.MainFile
	parser.Imports = ctx.Imports

	// Save reference to parser in context
	bs, err := parser.Start(in, filepath.Dir(ctx.MainFile))
	if err != nil {
		return parser, err
	}
	out.Write(bs)
	return parser, err
}
<commit_msg>wrong place to set logging flags<commit_after>package wellington

import (
	"bytes"
	"errors"
	"fmt"
	"io"
	"path/filepath"

	libsass "github.com/wellington/go-libsass"
	// TODO: Remove dot imports
	"github.com/wellington/wellington/lexer"
)

func init() {
	//log.SetFlags(log.Lshortfile | log.Ldate | log.Ltime)
}

// Replace holds token values for replacing source input with parsed input.
// DEPRECATED
type Replace struct {
	Start, End int
	Value      []byte
}

// Parser represents a parser engine that returns parsed and imported code
// from the input useful for doing text manipulation before passing to libsass.
type Parser struct {
	Idx, shift           int
	Chop                 []Replace
	Pwd, Input, MainFile string
	SassDir, BuildDir,

	ProjDir string
	Imports    libsass.Imports
	ImageDir   string
	Includes   []string
	Items      []lexer.Item
	Output     []byte
	Line       map[int]string
	LineKeys   []int
	PartialMap *SafePartialMap
}

// NewParser returns a pointer to a Parser object.
func NewParser() *Parser {
	p := &Parser{PartialMap: NewPartialMap()}
	p.Imports.Init()
	return p
}

// Start reads the tokens from the lexer and performs
// conversions and/or substitutions for sprite*() calls.
//
// Start creates a map of all variables and sprites
// (created via sprite-map calls).
// TODO: Remove pkgdir, it can be put on Parser
func (p *Parser) Start(r io.Reader, pkgdir string) ([]byte, error) {

	if r == nil {
		return []byte{}, errors.New("input is empty")
	}

	var in io.Reader
	var err error

	in, err = ToScssReader(r)

	if err != nil {
		return nil, err
	}

	p.Line = make(map[int]string)

	// Setup paths
	if p.MainFile == "" {
		p.MainFile = "stdin"
	}
	if p.BuildDir == "" {
		p.BuildDir = pkgdir
	}
	if p.SassDir == "" {
		p.SassDir = pkgdir
	}
	buf := bytes.NewBuffer(make([]byte, 0, bytes.MinRead))
	if in == nil {
		return []byte{}, fmt.Errorf("input is empty")
	}
	_, err = buf.ReadFrom(in)
	if err != nil {
		return []byte{}, err
	}

	// Send original byte slice
	p.Output = buf.Bytes() //[]byte(p.Input)

	return p.Output, nil
}

// StartParser accepts build arguments
// TODO: Should this be called StartParser or NewParser?
// TODO: Should this function create the partialMap or is this
// the right way to inject one?
func StartParser(ctx *libsass.Context, in io.Reader, out io.Writer, partialMap *SafePartialMap) (*Parser, error) {
	// Run the sprite_sass parser prior to passing to libsass
	parser := NewParser()

	parser.ImageDir = ctx.ImageDir
	parser.Includes = ctx.IncludePaths
	parser.BuildDir = ctx.BuildDir
	parser.MainFile = ctx.MainFile
	parser.Imports = ctx.Imports

	// Save reference to parser in context
	bs, err := parser.Start(in, filepath.Dir(ctx.MainFile))
	if err != nil {
		return parser, err
	}
	out.Write(bs)
	return parser, err
}
<|endoftext|>


<commit_before>package redis

import (
	"errors"
	"fmt"
	"strconv"

	"github.com/vmihailenco/bufio"
)

//------------------------------------------------------------------------------

// Represents Redis nil reply.
var Nil = errors.New("(nil)")

//------------------------------------------------------------------------------

var (
	errReaderTooSmall = errors.New("redis: reader is too small")
	errValNotSet      = errors.New("redis: value is not set")
)

//------------------------------------------------------------------------------

type parserError struct {
	err error
}

func (e *parserError) Error() string {
	return e.err.Error()
}

//------------------------------------------------------------------------------

func appendReq(buf []byte, args []string) []byte {
	buf = append(buf, '*')
	buf = strconv.AppendUint(buf, uint64(len(args)), 10)
	buf = append(buf, '\r', '\n')
	for _, arg := range args {
		buf = append(buf, '$')
		buf = strconv.AppendUint(buf, uint64(len(arg)), 10)
		buf = append(buf, '\r', '\n')
		buf = append(buf, []byte(arg)...)
		buf = append(buf, '\r', '\n')
	}
	return buf
}

//------------------------------------------------------------------------------

type reader interface {
	ReadLine() ([]byte, bool, error)
	Read([]byte) (int, error)
	ReadN(n int) ([]byte, error)
	Buffered() int
}

func readLine(rd reader) ([]byte, error) {
	line, isPrefix, err := rd.ReadLine()
	if err != nil {
		return line, err
	}
	if isPrefix {
		return line, errReaderTooSmall
	}
	return line, nil
}

func readN(rd reader, n int) ([]byte, error) {
	buf, err := rd.ReadN(n)
	if err == bufio.ErrBufferFull {
		newBuf := make([]byte, n)
		r := copy(newBuf, buf)
		buf = newBuf

		for r < n {
			n, err := rd.Read(buf[r:])
			if err != nil {
				return nil, err
			}
			r += n
		}
	} else if err != nil {
		return nil, err
	}
	return buf, nil
}

//------------------------------------------------------------------------------

func ParseReq(rd reader) ([]string, error) {
	line, err := readLine(rd)
	if err != nil {
		return nil, err
	}

	if line[0] != '*' {
		return []string{string(line)}, nil
	}
	numReplies, err := strconv.ParseInt(string(line[1:]), 10, 64)
	if err != nil {
		return nil, err
	}

	args := make([]string, 0, numReplies)
	for i := int64(0); i < numReplies; i++ {
		line, err = readLine(rd)
		if err != nil {
			return nil, err
		}
		if line[0] != '$' {
			return nil, fmt.Errorf("Expected '$', but got %q", line)
		}

		argLen, err := strconv.ParseInt(string(line[1:]), 10, 32)
		if err != nil {
			return nil, err
		}

		arg, err := readN(rd, int(argLen)+2)
		if err != nil {
			return nil, err
		}
		args = append(args, string(arg[:argLen]))
	}
	return args, nil
}

//------------------------------------------------------------------------------

const (
	ifaceSlice = iota
	stringSlice
	boolSlice
)

func parseReply(rd reader) (interface{}, error) {
	return _parseReply(rd, ifaceSlice)
}

func parseStringSliceReply(rd reader) (interface{}, error) {
	return _parseReply(rd, stringSlice)
}

func parseBoolSliceReply(rd reader) (interface{}, error) {
	return _parseReply(rd, boolSlice)
}

func _parseReply(rd reader, multiBulkType int) (interface{}, error) {
	line, err := readLine(rd)
	if err != nil {
		return 0, &parserError{err}
	}

	switch line[0] {
	case '-':
		return nil, errors.New(string(line[1:]))
	case '+':
		return string(line[1:]), nil
	case ':':
		v, err := strconv.ParseInt(string(line[1:]), 10, 64)
		if err != nil {
			return 0, &parserError{err}
		}
		return v, nil
	case '$':
		if len(line) == 3 && line[1] == '-' && line[2] == '1' {
			return "", Nil
		}

		replyLenInt32, err := strconv.ParseInt(string(line[1:]), 10, 32)
		if err != nil {
			return "", &parserError{err}
		}
		replyLen := int(replyLenInt32) + 2

		line, err = readN(rd, replyLen)
		if err != nil {
			return "", &parserError{err}
		}
		return string(line[:len(line)-2]), nil
	case '*':
		if len(line) == 3 && line[1] == '-' && line[2] == '1' {
			return nil, Nil
		}

		numReplies, err := strconv.ParseInt(string(line[1:]), 10, 64)
		if err != nil {
			return nil, &parserError{err}
		}

		switch multiBulkType {
		case stringSlice:
			vals := make([]string, 0, numReplies)
			for i := int64(0); i < numReplies; i++ {
				v, err := parseReply(rd)
				if err != nil {
					return nil, err
				} else {
					vals = append(vals, v.(string))
				}
			}

			return vals, nil
		case boolSlice:
			vals := make([]bool, 0, numReplies)
			for i := int64(0); i < numReplies; i++ {
				v, err := parseReply(rd)
				if err != nil {
					return nil, err
				} else {
					vals = append(vals, v.(int64) == 1)
				}
			}

			return vals, nil
		default:
			vals := make([]interface{}, 0, numReplies)
			for i := int64(0); i < numReplies; i++ {
				v, err := parseReply(rd)
				if err == Nil {
					vals = append(vals, nil)
				} else if err != nil {
					return nil, err
				} else {
					vals = append(vals, v)
				}
			}

			return vals, nil
		}
	default:
		return nil, &parserError{fmt.Errorf("redis: can't parse %q", line)}
	}
	panic("not reachable")
}
<commit_msg>Ignore Read error if we read enough data.<commit_after>package redis

import (
	"errors"
	"fmt"
	"strconv"

	"github.com/vmihailenco/bufio"
)

//------------------------------------------------------------------------------

// Represents Redis nil reply.
var Nil = errors.New("(nil)")

//------------------------------------------------------------------------------

var (
	errReaderTooSmall = errors.New("redis: reader is too small")
	errValNotSet      = errors.New("redis: value is not set")
)

//------------------------------------------------------------------------------

type parserError struct {
	err error
}

func (e *parserError) Error() string {
	return e.err.Error()
}

//------------------------------------------------------------------------------

func appendReq(buf []byte, args []string) []byte {
	buf = append(buf, '*')
	buf = strconv.AppendUint(buf, uint64(len(args)), 10)
	buf = append(buf, '\r', '\n')
	for _, arg := range args {
		buf = append(buf, '$')
		buf = strconv.AppendUint(buf, uint64(len(arg)), 10)
		buf = append(buf, '\r', '\n')
		buf = append(buf, []byte(arg)...)
		buf = append(buf, '\r', '\n')
	}
	return buf
}

//------------------------------------------------------------------------------

type reader interface {
	ReadLine() ([]byte, bool, error)
	Read([]byte) (int, error)
	ReadN(n int) ([]byte, error)
	Buffered() int
}

func readLine(rd reader) ([]byte, error) {
	line, isPrefix, err := rd.ReadLine()
	if err != nil {
		return line, err
	}
	if isPrefix {
		return line, errReaderTooSmall
	}
	return line, nil
}

func readN(rd reader, n int) ([]byte, error) {
	buf, err := rd.ReadN(n)
	if err == bufio.ErrBufferFull {
		newBuf := make([]byte, n)
		r := copy(newBuf, buf)
		buf = newBuf

		for {
			nn, err := rd.Read(buf[r:])
			r += nn
			if r >= n {
				// Ignore error if we read enough.
				break
			}
			if err != nil {
				return nil, err
			}
		}
	} else if err != nil {
		return nil, err
	}
	return buf, nil
}

//------------------------------------------------------------------------------

func ParseReq(rd reader) ([]string, error) {
	line, err := readLine(rd)
	if err != nil {
		return nil, err
	}

	if line[0] != '*' {
		return []string{string(line)}, nil
	}
	numReplies, err := strconv.ParseInt(string(line[1:]), 10, 64)
	if err != nil {
		return nil, err
	}

	args := make([]string, 0, numReplies)
	for i := int64(0); i < numReplies; i++ {
		line, err = readLine(rd)
		if err != nil {
			return nil, err
		}
		if line[0] != '$' {
			return nil, fmt.Errorf("Expected '$', but got %q", line)
		}

		argLen, err := strconv.ParseInt(string(line[1:]), 10, 32)
		if err != nil {
			return nil, err
		}

		arg, err := readN(rd, int(argLen)+2)
		if err != nil {
			return nil, err
		}
		args = append(args, string(arg[:argLen]))
	}
	return args, nil
}

//------------------------------------------------------------------------------

const (
	ifaceSlice = iota
	stringSlice
	boolSlice
)

func parseReply(rd reader) (interface{}, error) {
	return _parseReply(rd, ifaceSlice)
}

func parseStringSliceReply(rd reader) (interface{}, error) {
	return _parseReply(rd, stringSlice)
}

func parseBoolSliceReply(rd reader) (interface{}, error) {
	return _parseReply(rd, boolSlice)
}

func _parseReply(rd reader, multiBulkType int) (interface{}, error) {
	line, err := readLine(rd)
	if err != nil {
		return 0, &parserError{err}
	}

	switch line[0] {
	case '-':
		return nil, errors.New(string(line[1:]))
	case '+':
		return string(line[1:]), nil
	case ':':
		v, err := strconv.ParseInt(string(line[1:]), 10, 64)
		if err != nil {
			return 0, &parserError{err}
		}
		return v, nil
	case '$':
		if len(line) == 3 && line[1] == '-' && line[2] == '1' {
			return "", Nil
		}

		replyLenInt32, err := strconv.ParseInt(string(line[1:]), 10, 32)
		if err != nil {
			return "", &parserError{err}
		}
		replyLen := int(replyLenInt32) + 2

		line, err = readN(rd, replyLen)
		if err != nil {
			return "", &parserError{err}
		}
		return string(line[:len(line)-2]), nil
	case '*':
		if len(line) == 3 && line[1] == '-' && line[2] == '1' {
			return nil, Nil
		}

		numReplies, err := strconv.ParseInt(string(line[1:]), 10, 64)
		if err != nil {
			return nil, &parserError{err}
		}

		switch multiBulkType {
		case stringSlice:
			vals := make([]string, 0, numReplies)
			for i := int64(0); i < numReplies; i++ {
				v, err := parseReply(rd)
				if err != nil {
					return nil, err
				} else {
					vals = append(vals, v.(string))
				}
			}

			return vals, nil
		case boolSlice:
			vals := make([]bool, 0, numReplies)
			for i := int64(0); i < numReplies; i++ {
				v, err := parseReply(rd)
				if err != nil {
					return nil, err
				} else {
					vals = append(vals, v.(int64) == 1)
				}
			}

			return vals, nil
		default:
			vals := make([]interface{}, 0, numReplies)
			for i := int64(0); i < numReplies; i++ {
				v, err := parseReply(rd)
				if err == Nil {
					vals = append(vals, nil)
				} else if err != nil {
					return nil, err
				} else {
					vals = append(vals, v)
				}
			}

			return vals, nil
		}
	default:
		return nil, &parserError{fmt.Errorf("redis: can't parse %q", line)}
	}
	panic("not reachable")
}
<|endoftext|>


<commit_before>package main

import (
	"fmt"
	"strings"
)

// Parser represents a parser.
type Parser struct {
	s   *Scanner
	buf struct {
		tok TokenType // last read token
		lit string    // last read literal
		n   int       // buffer size (max=1)
	}
}

// NewParser returns a new instance of Parser
func NewParser(s *Scanner) *Parser {
	return &Parser{
		s: s,
	}
}

func (p *Parser) scan() (tok TokenType, lit string) {
	// If we have a token on the buffer, then return it.
	if p.buf.n != 0 {
		p.buf.n = 0
		return p.buf.tok, p.buf.lit
	}

	// Otherwise read the next token from the scanner.
	tok, lit = p.s.Scan()

	// Save it to the buffer in case we unscan later.
	p.buf.tok, p.buf.lit = tok, lit

	return
}

func (p *Parser) unscan() {
	//this is trash
	p.buf.n = 1
}

func (p *Parser) scanIgnoreWhitespace() (tok TokenType, lit string) {
	tok, lit = p.scan()
	if tok == TWhitespace {
		tok, lit = p.scan()
	}
	return
}

func tokenArrayContains(t TokenType, types []TokenType) bool {
	for _, i := range types {
		if i == t {
			return true
		}
	}

	return false
}

// Parse parses scanner result
func (p *Parser) Parse() (*Schema, error) {
	sch := &Schema{}

TableLoop:
	for {
		var tok TokenType
		var lit string
		if tok, lit = p.scanIgnoreWhitespace(); !tokenArrayContains(tok, HeadingTokens) {
			return nil, fmt.Errorf("found %q %s, expected %s", lit, tok, HeadingTokens)
		}

		tbl := &CreateTable{}
		if tok == TAtSignHeadingLine {
			tbl.IsPsuedo = true
		}

		if tok, lit = p.scanIgnoreWhitespace(); tok != TString {
			return nil, fmt.Errorf("found %q %s, expected TableName", lit, tok)
		}

		tbl.TableName = lit
		sch.Tables = append(sch.Tables, tbl)

	TblCommentLoop:
		for {
			comtok, comlit := p.scanIgnoreWhitespace()
			if comtok == TColonLine {
				tbl.TableComment = append(tbl.TableComment, strings.TrimSpace(comlit))
			} else {
				// I don't believe this is nessessary anymore
				// tbl.TableComment = strings.TrimSpace(tbl.TableComment)
				p.unscan()
				break TblCommentLoop
			}
		}

		// ColumnLoop:
		for {
			var coltok TokenType
			var collit string

			if coltok, collit = p.scanIgnoreWhitespace(); !tokenArrayContains(coltok, ColumnTokens) {
				if tokenArrayContains(coltok, HeadingTokens) {
					p.unscan()
					continue TableLoop
				}

				return nil, fmt.Errorf("found %q %s, expected %s", collit, coltok, ColumnTokens)
			}

			col := &TableColumn{}

			switch coltok {
			case TExclaimLine:
				col.ColumnReferenceType = ColumnForeignKeyRegister
			case TQuestionLine:
				col.ColumnReferenceType = ColumnForeignKeyReference
			case TDashLine:
				col.ColumnReferenceType = ColumnRegular
			default:
				return nil, fmt.Errorf("unexpected token: %s", coltok)
			}

			var colntok TokenType
			var colnlit string
			if colntok, colnlit = p.scanIgnoreWhitespace(); colntok != TString {
				return nil, fmt.Errorf("found %q %s, expected Column Name", colnlit, colntok)
			}

			col.ColumnName = colnlit

		ModLoop:
			for {
				modtok, _ := p.scanIgnoreWhitespace()
				switch modtok {
				case TAstrisk:
					col.Nullable = true
				case TSigned:
					col.Signed = true
				case TString:
					p.unscan()
					break ModLoop
				default:
					return nil, fmt.Errorf("unexpected token: %s", modtok)
				}
			} //todo limit to one of each

			_, ctype := p.scan()
			ctype, csize, err := strIntSuffixSplit(ctype)
			if err != nil {
				return nil, err
			}

			cctype, err := ColumnTypes.getColumnType(ctype)
			if err != nil {
				return nil, err
			}

			col.ColumnType = cctype
			col.ColumnSize = csize
			tbl.TableColumns = append(tbl.TableColumns, col)

			for {
				tok, lit := p.scanIgnoreWhitespace()

				fmt.Println(tok, lit)

				if tok != TString && tok != TAstrisk {
					fmt.Println("continuing", tok, lit)
					p.unscan()
					break
				}

				// autoIncr := false
				if tok == TAstrisk {
					// autoIncr = true
					if tbl.AutoIncrColumn == nil {
						tbl.AutoIncrColumn = col
					} else {
						return nil, fmt.Errorf("auto increment column already declared")
					}

					tok, lit := p.scanIgnoreWhitespace()
					if tok != TString {
						return nil, fmt.Errorf("found %q %s, expected %s", lit, tok, TString)
					}
				}

				if lit == "pk" {
				} else if lit[0:1] == "k" {
				} else if lit[0:1] == "u" {
				}
			}

		ColCommentLoop:
			for {
				comtok, comlit := p.scanIgnoreWhitespace()
				if comtok == TColonLine {
					col.ColumnComment = append(col.ColumnComment, strings.TrimSpace(comlit))
				} else {
					// This shouldn't be nessessary
					// col.ColumnComment = strings.TrimSpace(col.ColumnComment)
					p.unscan()
					break ColCommentLoop
				}
			}
			// break
		}

		break
	}

	return sch, nil
}
<commit_msg>Handle TEof<commit_after>package main

import (
	"fmt"
	"strings"
)

// Parser represents a parser.
type Parser struct {
	s   *Scanner
	buf struct {
		tok TokenType // last read token
		lit string    // last read literal
		n   int       // buffer size (max=1)
	}
}

// NewParser returns a new instance of Parser
func NewParser(s *Scanner) *Parser {
	return &Parser{
		s: s,
	}
}

func (p *Parser) scan() (tok TokenType, lit string) {
	// If we have a token on the buffer, then return it.
	if p.buf.n != 0 {
		p.buf.n = 0
		return p.buf.tok, p.buf.lit
	}

	// Otherwise read the next token from the scanner.
	tok, lit = p.s.Scan()

	// Save it to the buffer in case we unscan later.
	p.buf.tok, p.buf.lit = tok, lit

	return
}

func (p *Parser) unscan() {
	//this is trash
	p.buf.n = 1
}

func (p *Parser) scanIgnoreWhitespace() (tok TokenType, lit string) {
	tok, lit = p.scan()
	if tok == TWhitespace {
		tok, lit = p.scan()
	}
	return
}

func tokenArrayContains(t TokenType, types []TokenType) bool {
	for _, i := range types {
		if i == t {
			return true
		}
	}

	return false
}

// Parse parses scanner result
func (p *Parser) Parse() (*Schema, error) {
	sch := &Schema{}

TableLoop:
	for {
		var tok TokenType
		var lit string

		tok, lit = p.scanIgnoreWhitespace()
		if tok == TEof {
			break
		}

		if !tokenArrayContains(tok, HeadingTokens) {
			return nil, fmt.Errorf("found %q %s, expected %s", lit, tok, HeadingTokens)
		}

		tbl := &CreateTable{}
		if tok == TAtSignHeadingLine {
			tbl.IsPsuedo = true
		}

		if tok, lit = p.scanIgnoreWhitespace(); tok != TString {
			return nil, fmt.Errorf("found %q %s, expected TableName", lit, tok)
		}

		tbl.TableName = lit
		sch.Tables = append(sch.Tables, tbl)

	TblCommentLoop:
		for {
			comtok, comlit := p.scanIgnoreWhitespace()
			if comtok == TColonLine {
				tbl.TableComment = append(tbl.TableComment, strings.TrimSpace(comlit))
			} else {
				// I don't believe this is nessessary anymore
				// tbl.TableComment = strings.TrimSpace(tbl.TableComment)
				p.unscan()
				break TblCommentLoop
			}
		}

		// ColumnLoop:
		for {
			var coltok TokenType
			var collit string

			if coltok, collit = p.scanIgnoreWhitespace(); !tokenArrayContains(coltok, ColumnTokens) {
				if tokenArrayContains(coltok, HeadingTokens) {
					p.unscan()
					continue TableLoop
				}

				if coltok == TEof {
					p.unscan()
					continue TableLoop
				}

				return nil, fmt.Errorf("found %q %s, expected %s", collit, coltok, ColumnTokens)
			}

			col := &TableColumn{}

			switch coltok {
			case TExclaimLine:
				col.ColumnReferenceType = ColumnForeignKeyRegister
			case TQuestionLine:
				col.ColumnReferenceType = ColumnForeignKeyReference
			case TDashLine:
				col.ColumnReferenceType = ColumnRegular
			default:
				return nil, fmt.Errorf("unexpected token: %s", coltok)
			}

			var colntok TokenType
			var colnlit string
			if colntok, colnlit = p.scanIgnoreWhitespace(); colntok != TString {
				return nil, fmt.Errorf("found %q %s, expected Column Name", colnlit, colntok)
			}

			col.ColumnName = colnlit

		ModLoop:
			for {
				modtok, _ := p.scanIgnoreWhitespace()
				switch modtok {
				case TAstrisk:
					col.Nullable = true
				case TSigned:
					col.Signed = true
				case TString:
					p.unscan()
					break ModLoop
				default:
					return nil, fmt.Errorf("unexpected token: %s", modtok)
				}
			} //todo limit to one of each

			_, ctype := p.scan()
			ctype, csize, err := strIntSuffixSplit(ctype)
			if err != nil {
				return nil, err
			}

			cctype, err := ColumnTypes.getColumnType(ctype)
			if err != nil {
				return nil, err
			}

			col.ColumnType = cctype
			col.ColumnSize = csize
			tbl.TableColumns = append(tbl.TableColumns, col)

			for {
				tok, lit := p.scanIgnoreWhitespace()

				fmt.Println(tok, lit)

				if tok != TString && tok != TAstrisk {
					fmt.Println("continuing", tok, lit)
					p.unscan()
					break
				}

				// autoIncr := false
				if tok == TAstrisk {
					// autoIncr = true
					if tbl.AutoIncrColumn == nil {
						tbl.AutoIncrColumn = col
					} else {
						return nil, fmt.Errorf("auto increment column already declared")
					}

					tok, lit := p.scanIgnoreWhitespace()
					if tok != TString {
						return nil, fmt.Errorf("found %q %s, expected %s", lit, tok, TString)
					}
				}

				if lit == "pk" {
				} else if lit[0:1] == "k" {
				} else if lit[0:1] == "u" {
				}
			}

		ColCommentLoop:
			for {
				comtok, comlit := p.scanIgnoreWhitespace()
				if comtok == TColonLine {
					col.ColumnComment = append(col.ColumnComment, strings.TrimSpace(comlit))
				} else {
					// This shouldn't be nessessary
					// col.ColumnComment = strings.TrimSpace(col.ColumnComment)
					p.unscan()
					break ColCommentLoop
				}
			}
			// break
		}

		// break
	}

	return sch, nil
}
<|endoftext|>


<commit_before>// +build !confonly

package http

import (
	"context"
	"io"
	"net/http"
	"strings"
	"time"

	"v2ray.com/core/common"
	"v2ray.com/core/common/net"
	"v2ray.com/core/common/serial"
	"v2ray.com/core/common/session"
	"v2ray.com/core/common/signal/done"
	"v2ray.com/core/transport/internet"
	"v2ray.com/core/transport/internet/tls"
)

type Listener struct {
	server  *http.Server
	handler internet.ConnHandler
	local   net.Addr
	config  Config
}

func (l *Listener) Addr() net.Addr {
	return l.local
}

func (l *Listener) Close() error {
	return l.server.Close()
}

type flushWriter struct {
	w io.Writer
	d *done.Instance
}

func (fw flushWriter) Write(p []byte) (n int, err error) {
	if fw.d.Done() {
		return 0, io.ErrClosedPipe
	}

	n, err = fw.w.Write(p)
	if f, ok := fw.w.(http.Flusher); ok {
		f.Flush()
	}
	return
}

func (l *Listener) ServeHTTP(writer http.ResponseWriter, request *http.Request) {
	host := request.Host
	if !l.config.isValidHost(host) {
		writer.WriteHeader(404)
		return
	}
	path := l.config.getNormalizedPath()
	if !strings.HasPrefix(request.URL.Path, path) {
		writer.WriteHeader(404)
		return
	}

	writer.Header().Set("Cache-Control", "no-store")
	writer.WriteHeader(200)
	if f, ok := writer.(http.Flusher); ok {
		f.Flush()
	}

	remoteAddr := l.Addr()
	dest, err := net.ParseDestination(request.RemoteAddr)
	if err != nil {
		newError("failed to parse request remote addr: ", request.RemoteAddr).Base(err).WriteToLog()
	} else {
		remoteAddr = &net.TCPAddr{
			IP:   dest.Address.IP(),
			Port: int(dest.Port),
		}
	}

	done := done.New()
	conn := net.NewConnection(
		net.ConnectionOutput(request.Body),
		net.ConnectionInput(flushWriter{w: writer, d: done}),
		net.ConnectionOnClose(common.ChainedClosable{done, request.Body}),
		net.ConnectionLocalAddr(l.Addr()),
		net.ConnectionRemoteAddr(remoteAddr),
	)
	l.handler(conn)
	<-done.Wait()
}

func Listen(ctx context.Context, address net.Address, port net.Port, streamSettings *internet.MemoryStreamConfig, handler internet.ConnHandler) (internet.Listener, error) {
	httpSettings := streamSettings.ProtocolSettings.(*Config)
	listener := &Listener{
		handler: handler,
		local: &net.TCPAddr{
			IP:   address.IP(),
			Port: int(port),
		},
		config: *httpSettings,
	}

	config := tls.ConfigFromStreamSettings(streamSettings)
	if config == nil {
		return nil, newError("TLS must be enabled for http transport.").AtWarning()
	}

	server := &http.Server{
		Addr:              serial.Concat(address, ":", port),
		TLSConfig:         config.GetTLSConfig(tls.WithNextProto("h2")),
		Handler:           listener,
		ReadHeaderTimeout: time.Second * 4,
	}

	listener.server = server
	go func() {
		tcpListener, err := internet.ListenSystem(ctx, &net.TCPAddr{
			IP:   address.IP(),
			Port: int(port),
		}, streamSettings.SocketSettings)
		if err != nil {
			newError("failed to listen on", address, ":", port).Base(err).WriteToLog(session.ExportIDToError(ctx))
			return
		}

		err = server.ServeTLS(tcpListener, "", "")
		if err != nil {
			newError("stoping serving TLS").Base(err).WriteToLog(session.ExportIDToError(ctx))
		}
	}()

	return listener, nil
}

func init() {
	common.Must(internet.RegisterTransportListener(protocolName, Listen))
}
<commit_msg>add server side h2c support<commit_after>// +build !confonly

package http

import (
	"context"
	"io"
	"net/http"
	"strings"
	"time"

	"v2ray.com/core/common"
	"v2ray.com/core/common/net"
	"v2ray.com/core/common/serial"
	"v2ray.com/core/common/session"
	"v2ray.com/core/common/signal/done"
	"v2ray.com/core/transport/internet"
	"v2ray.com/core/transport/internet/tls"

	"golang.org/x/net/http2"
	"golang.org/x/net/http2/h2c"
)

type Listener struct {
	server  *http.Server
	handler internet.ConnHandler
	local   net.Addr
	config  Config
}

func (l *Listener) Addr() net.Addr {
	return l.local
}

func (l *Listener) Close() error {
	return l.server.Close()
}

type flushWriter struct {
	w io.Writer
	d *done.Instance
}

func (fw flushWriter) Write(p []byte) (n int, err error) {
	if fw.d.Done() {
		return 0, io.ErrClosedPipe
	}

	n, err = fw.w.Write(p)
	if f, ok := fw.w.(http.Flusher); ok {
		f.Flush()
	}
	return
}

func (l *Listener) ServeHTTP(writer http.ResponseWriter, request *http.Request) {
	host := request.Host
	if !l.config.isValidHost(host) {
		writer.WriteHeader(404)
		return
	}
	path := l.config.getNormalizedPath()
	if !strings.HasPrefix(request.URL.Path, path) {
		writer.WriteHeader(404)
		return
	}

	writer.Header().Set("Cache-Control", "no-store")
	writer.WriteHeader(200)
	if f, ok := writer.(http.Flusher); ok {
		f.Flush()
	}

	remoteAddr := l.Addr()
	dest, err := net.ParseDestination(request.RemoteAddr)
	if err != nil {
		newError("failed to parse request remote addr: ", request.RemoteAddr).Base(err).WriteToLog()
	} else {
		remoteAddr = &net.TCPAddr{
			IP:   dest.Address.IP(),
			Port: int(dest.Port),
		}
	}

	done := done.New()
	conn := net.NewConnection(
		net.ConnectionOutput(request.Body),
		net.ConnectionInput(flushWriter{w: writer, d: done}),
		net.ConnectionOnClose(common.ChainedClosable{done, request.Body}),
		net.ConnectionLocalAddr(l.Addr()),
		net.ConnectionRemoteAddr(remoteAddr),
	)
	l.handler(conn)
	<-done.Wait()
}

func Listen(ctx context.Context, address net.Address, port net.Port, streamSettings *internet.MemoryStreamConfig, handler internet.ConnHandler) (internet.Listener, error) {
	httpSettings := streamSettings.ProtocolSettings.(*Config)
	listener := &Listener{
		handler: handler,
		local: &net.TCPAddr{
			IP:   address.IP(),
			Port: int(port),
		},
		config: *httpSettings,
	}

	var server *http.Server
	config := tls.ConfigFromStreamSettings(streamSettings)
	if config == nil {
		h2s := &http2.Server{}

		server = &http.Server{
			Addr:              serial.Concat(address, ":", port),
			Handler:           h2c.NewHandler(listener, h2s),
			ReadHeaderTimeout: time.Second * 4,
		}
	} else {
		server = &http.Server{
			Addr:              serial.Concat(address, ":", port),
			TLSConfig:         config.GetTLSConfig(tls.WithNextProto("h2")),
			Handler:           listener,
			ReadHeaderTimeout: time.Second * 4,
		}
	}

	listener.server = server
	go func() {
		tcpListener, err := internet.ListenSystem(ctx, &net.TCPAddr{
			IP:   address.IP(),
			Port: int(port),
		}, streamSettings.SocketSettings)
		if err != nil {
			newError("failed to listen on", address, ":", port).Base(err).WriteToLog(session.ExportIDToError(ctx))
			return
		}
		if config == nil {
			err = server.Serve(tcpListener)
			if err != nil {
				newError("stoping serving H2C").Base(err).WriteToLog(session.ExportIDToError(ctx))
			}
		} else {
			err = server.ServeTLS(tcpListener, "", "")
			if err != nil {
				newError("stoping serving TLS").Base(err).WriteToLog(session.ExportIDToError(ctx))
			}
		}
	}()

	return listener, nil
}

func init() {
	common.Must(internet.RegisterTransportListener(protocolName, Listen))
}
<|endoftext|>


<commit_before>package main

import (
	"io/ioutil"
	"net/http"
	"os"
	"path/filepath"
	"regexp"
)

type Downloader struct {
	OutputDir    string
	RelativePath string
	ChanDown     chan Res
}

type Res struct {
	Data []byte
	Err  error
}

func (d *Downloader) ExtractId(url string) string {

	idReg := regexp.MustCompile(`https://drive.google.com/file/d/(\w+)/.*`)
	id := idReg.FindStringSubmatch(url)

	// didn't find a match
	if len(id) < 2 {
		return ""
	}
	return id[1]
}

func (d *Downloader) CreateRelPath(id string) string {
	return filepath.Join(d.RelativePath, id)
}
func (d *Downloader) CreateDestPath(id string) string {
	return filepath.Join(d.OutputDir, id)
}
func (d *Downloader) CreateTargetUrl(id string) string {
	return "https://googledrive.com/host/" + id
}

type writeFile func(filename string, data []byte, perm os.FileMode) error

func (d *Downloader) Download(id string, url string) Res {
	res, err := http.Get(url)
	if err != nil {
		return Res{make([]byte, 0), err}
	}
	defer res.Body.Close()
	data, err := ioutil.ReadAll(res.Body)
	return Res{data, err}
	// d.ChanDown <- Res{data, err}
	// return data, err
}

func (d *Downloader) Run(assets map[string]Asset) error {
	for k, _ := range assets {
		if id := d.ExtractId(k); id != "" {
			go d.Download(id, d.CreateTargetUrl(id))
		}
	}
	return nil
}

// ioutil.WriteFile

// if err != nil {
// 	d.Assets[k] = Asset{"", err}
// } else {
// 	d.Assets[k] = Asset{d.CreateRelPath(id), nil}
// }
<commit_msg>write to disk go func<commit_after>package main

import (
	"io/ioutil"
	"net/http"
	"os"
	"path/filepath"
	"regexp"
)

type Downloader struct {
	OutputDir    string
	RelativePath string
	ChanDown     chan Res
}

type Res struct {
	Data []byte
	Err  error
}

func (d *Downloader) ExtractId(url string) string {

	idReg := regexp.MustCompile(`https://drive.google.com/file/d/(\w+)/.*`)
	id := idReg.FindStringSubmatch(url)

	// didn't find a match
	if len(id) < 2 {
		return ""
	}
	return id[1]
}

func (d *Downloader) CreateRelPath(id string) string {
	return filepath.Join(d.RelativePath, id)
}
func (d *Downloader) CreateDestPath(id string) string {
	return filepath.Join(d.OutputDir, id)
}
func (d *Downloader) CreateTargetUrl(id string) string {
	return "https://googledrive.com/host/" + id
}

type writeFile func(filename string, data []byte, perm os.FileMode) error

func (d *Downloader) Download(id string) {
	res, err := http.Get(d.CreateTargetUrl(id))
	if err != nil {
		return Res{make([]byte, 0), err}
	}
	defer res.Body.Close()
	data, err := ioutil.ReadAll(res.Body)
	d.ChanDown <- Res{data, err}
}

func (d *Downloader) WriteToDisk(id string) {
	res := <-d.ChanDown
	if res.Err != nil {
		ioutil.WriteFile(d.CreateDestPath(id), res.Data, 0644)
	}
}

func (d *Downloader) Run(assets map[string]Asset) error {
	for k, _ := range assets {
		if id := d.ExtractId(k); id != "" {
			go d.Download(id)
			go d.WriteToDisk(id)
		}
	}
	return nil
}

// ioutil.WriteFile

// if err != nil {
// 	d.Assets[k] = Asset{"", err}
// } else {
// 	d.Assets[k] = Asset{d.CreateRelPath(id), nil}
// }
<|endoftext|>


<commit_before>package main

import (
	"fmt"
	"os"
	"time"
)

func downloader(id int, limiter <-chan time.Time, imgChan <-chan Image) {
	for img := range imgChan {

		os.MkdirAll("downloads/"+img.User, 0755)

		<-limiter
		fmt.Println(img)
		img.Download()

	}
}
<commit_msg>Fixed crash when a downloader tried to save a file in a nonexistent directory.<commit_after>package main

import (
	"fmt"
	"os"
	"time"
)

func downloader(id int, limiter <-chan time.Time, imgChan <-chan Image) {
	for img := range imgChan {

		os.MkdirAll(img.User, 0755)

		<-limiter
		fmt.Println(img)
		img.Download()

	}
}
<|endoftext|>


<commit_before>package forms

import (
	"encoding/json"
	"fmt"

	"github.com/Jeffail/gabs"
	"github.com/bep/gr"
	"github.com/bep/gr/attr"
	"github.com/bep/gr/el"
	"github.com/bep/gr/evt"
	"github.com/murdinc/awsmDashboard/helpers"
)

var (
	elbSchemes = []string{"internal", "internet-facing"}
)

type LoadBalancerClassForm struct {
	*gr.This
}

// Implements the StateInitializer interface
func (l LoadBalancerClassForm) GetInitialState() gr.State {
	return gr.State{"querying": true, "error": "", "success": "", "step": 1,
		"listeners": []interface{}{},
	}
}

// Implements the ComponentWillMount interface
func (l LoadBalancerClassForm) ComponentWillMount() {
	var class map[string]interface{}

	if l.Props().Interface("class") != nil {
		classJson := l.Props().Interface("class").([]byte)
		json.Unmarshal(classJson, &class)
	}

	l.SetState(class)
	l.SetState(gr.State{"querying": true})

	// Get our options for the form
	go func() {
		endpoint := "//localhost:8081/api/classes/" + l.Props().String("apiType") + "/options"
		resp, err := helpers.GetAPI(endpoint)
		if !l.IsMounted() {
			return
		}
		if err != nil {
			l.SetState(gr.State{"querying": false, "error": fmt.Sprintf("Error while querying endpoint: %s", endpoint)})
			return
		}

		l.SetState(gr.State{"classOptionsResp": resp, "querying": false})
	}()
}

func (l LoadBalancerClassForm) Render() gr.Component {

	state := l.State()
	props := l.Props()

	// Form placeholder
	response := el.Div()

	// Print any alerts
	helpers.ErrorElem(state.String("error")).Modify(response)
	helpers.SuccessElem(state.String("success")).Modify(response)

	if state.Int("step") == 1 {
		if state.Bool("querying") {
			gr.Text("Loading...").Modify(response)
		} else {
			l.BuildClassForm(props.String("className"), state.Interface("classOptionsResp")).Modify(response)
		}

	} else if state.Int("step") == 2 {

		if state.Bool("querying") {
			gr.Text("Saving...").Modify(response)
		} else {

			buttons := el.Div(
				gr.CSS("btn-toolbar"),
			)

			// Back
			el.Button(
				evt.Click(l.backButton).PreventDefault(),
				gr.CSS("btn", "btn-secondary"),
				gr.Text("Back"),
			).Modify(buttons)

			// Done
			el.Button(
				evt.Click(l.doneButton).PreventDefault(),
				gr.CSS("btn", "btn-primary"),
				gr.Text("Done"),
			).Modify(buttons)

			buttons.Modify(response)
		}

	}

	return response
}

func (l LoadBalancerClassForm) BuildClassForm(className string, optionsResp interface{}) *gr.Element {

	state := l.State()
	props := l.Props()

	var classOptions map[string][]string
	jsonParsed, _ := gabs.ParseJSON(optionsResp.([]byte))
	classOptionsJson := jsonParsed.S("classOptions").Bytes()
	json.Unmarshal(classOptionsJson, &classOptions)

	classEdit := el.Div(
		el.Header3(gr.Text(className)),
		el.HorizontalRule(),
	)

	classEditForm := el.Form(evt.KeyDown(DisableEnter))

	SelectOne("Scheme", "scheme", elbSchemes, state.Interface("scheme"), l.storeSelect).Modify(classEditForm)
	SelectMultiple("Security Groups", "securityGroups", classOptions["securitygroups"], state.Interface("securityGroups"), l.storeSelect).Modify(classEditForm)
	SelectMultiple("Subnets", "subnets", classOptions["subnets"], state.Interface("subnets"), l.storeSelect).Modify(classEditForm)
	SelectMultiple("Availability Zones", "availabilityZones", classOptions["zones"], state.Interface("availabilityZones"), l.storeSelect).Modify(classEditForm)

	el.Div(
		el.Break(nil),
		el.Header4(
			gr.Text("Listeners"),
			el.Button(
				evt.Click(l.addListener).PreventDefault(),
				gr.CSS("btn", "btn-primary", "btn-sm", "pull-right"),
				gr.Text("New"),
			),
		),
		el.HorizontalRule(nil),
	).Modify(classEditForm)

	listeners := state.Interface("listeners").([]interface{})

	for index, listInf := range listeners {

		listener := listInf.(map[string]interface{})

		// Form placeholder
		listenerForm := el.Div()

		//TextField("Note", "note", listener["note"], l.modifyListener(index, listener)).Modify(listenerForm)

		el.Div(
			gr.CSS("row"), el.Div(gr.CSS("col-sm-6"),
				SelectOne("Protocol", "protocol", []string{"HTTP", "HTTPS", "TCP", "SSL"}, listener["protocol"], l.storeListenerSelect(index, listener)),
			),
			el.Div(gr.CSS("col-sm-6"),
				NumberField("Load Balancer Port", "loadBalancerPort", listener["loadBalancerPort"], l.modifyListener(index, listener)),
			),
		).Modify(listenerForm)

		el.Div(
			gr.CSS("row"), el.Div(gr.CSS("col-sm-6"),
				SelectOne("Instance Protocol", "instanceProtocol", []string{"HTTP", "HTTPS", "TCP", "SSL"}, listener["instanceProtocol"], l.storeListenerSelect(index, listener)),
			),
			el.Div(gr.CSS("col-sm-6"),
				NumberField("Instance Port", "instancePort", listener["instancePort"], l.modifyListener(index, listener)),
			),
		).Modify(listenerForm)

		el.Div(
			gr.CSS("btn-toolbar"),
			el.Button(
				evt.Click(l.removeListener).PreventDefault(),
				gr.CSS("btn", "btn-danger", "btn-sm", "pull-right"),
				gr.Text("Remove"),
				attr.ID(index),
			),
		).Modify(listenerForm)

		el.HorizontalRule(nil).Modify(listenerForm)

		listenerForm.Modify(classEditForm)

	}

	classEditForm.Modify(classEdit)

	buttons := el.Div(
		gr.CSS("btn-toolbar"),
	)

	// Back
	el.Button(
		evt.Click(l.backButton).PreventDefault(),
		gr.CSS("btn", "btn-secondary"),
		gr.Text("Back"),
	).Modify(buttons)

	// Save
	el.Button(
		evt.Click(l.saveButton).PreventDefault(),
		gr.CSS("btn", "btn-primary"),
		gr.Text("Save"),
	).Modify(buttons)

	// Delete
	if props.Interface("hasDelete") != nil && props.Bool("hasDelete") {
		el.Button(
			evt.Click(l.deleteButton).PreventDefault(),
			gr.CSS("btn", "btn-danger", "pull-right"),
			gr.Text("Delete"),
		).Modify(buttons)
	}

	buttons.Modify(classEdit)

	return classEdit

}

func (l LoadBalancerClassForm) backButton(*gr.Event) {
	l.SetState(gr.State{"success": ""})
	l.Props().Call("backButton")
}

func (l LoadBalancerClassForm) doneButton(*gr.Event) {
	l.SetState(gr.State{"success": ""})
	l.Props().Call("hideAllModals")
}

func (l LoadBalancerClassForm) saveButton(*gr.Event) {
	l.SetState(gr.State{"querying": true, "step": 2})

	cfg := make(map[string]interface{})
	for key, _ := range l.State() {
		cfg[key] = l.State().Interface(key)
	}

	go func() {
		endpoint := "//localhost:8081/api/classes/" + l.Props().String("apiType") + "/name/" + l.Props().String("className")

		_, err := helpers.PutAPI(endpoint, cfg)
		if !l.IsMounted() {
			return
		}

		if err != nil {
			l.SetState(gr.State{"querying": false, "error": fmt.Sprintf("Error while querying endpoint: %s", endpoint), "step": 1})
			return
		}

		l.SetState(gr.State{"querying": false, "success": "Class was saved", "error": ""})
	}()

}

func (l LoadBalancerClassForm) deleteButton(*gr.Event) {
	l.SetState(gr.State{"querying": true})

	go func() {
		endpoint := "//localhost:8081/api/classes/" + l.Props().String("apiType") + "/name/" + l.Props().String("className")

		_, err := helpers.DeleteAPI(endpoint)
		if !l.IsMounted() {
			return
		}

		if err != nil {
			l.SetState(gr.State{"querying": false, "error": fmt.Sprintf("Error while querying endpoint: %s", endpoint)})
			return
		}

		l.SetState(gr.State{"querying": false, "success": "Class was deleted", "error": "", "step": 2})
	}()
}

func (l LoadBalancerClassForm) storeValue(event *gr.Event) {
	key := event.Target().Get("name").String()
	inputType := event.Target().Get("type").String()

	switch inputType {

	case "checkbox":
		l.SetState(gr.State{key: event.Target().Get("checked").Bool()})

	case "number":
		l.SetState(gr.State{key: event.TargetValue().Int()})

	default: // text, at least
		l.SetState(gr.State{key: event.TargetValue()})

	}
}

func (l LoadBalancerClassForm) storeSelect(key string, val interface{}) {
	switch value := val.(type) {

	case map[string]interface{}:
		// single
		l.SetState(gr.State{key: value["value"]})

	case []interface{}:
		// multi
		var vals []string
		options := len(value)
		for i := 0; i < options; i++ {
			vals = append(vals, value[i].(map[string]interface{})["value"].(string))
		}
		l.SetState(gr.State{key: vals})

	default:
		l.SetState(gr.State{key: val})

	}
}

func (l LoadBalancerClassForm) modifyListener(index int, listener map[string]interface{}) func(*gr.Event) {
	return func(event *gr.Event) {
		key := event.Target().Get("name").String()
		valueType := event.Target().Get("type").String()

		switch valueType {
		case "text":
			listener[key] = event.TargetValue().String()
		case "number":
			listener[key] = event.TargetValue().Int()
		}
		listeners, ok := l.State().Interface("listeners").([]interface{})
		if ok {
			listeners[index] = listener
			l.SetState(gr.State{"listeners": listeners})
			return
		}
		println("modifyListener failed?")
	}
}

func (l LoadBalancerClassForm) storeListenerSelect(index int, listener map[string]interface{}) func(string, interface{}) {
	return func(key string, val interface{}) {

		switch value := val.(type) {

		case map[string]interface{}:
			// single
			listener[key] = value["value"]

		case []interface{}:
			// multi
			var vals []string
			options := len(value)
			for i := 0; i < options; i++ {
				vals = append(vals, value[i].(map[string]interface{})["value"].(string))
			}
			listener[key] = vals

		default:
			listener[key] = val
		}

		listeners, ok := l.State().Interface("listeners").([]interface{})
		if ok {
			listeners[index] = listener
			l.SetState(gr.State{"listeners": listeners})
			return
		}
	}
}

func (l LoadBalancerClassForm) addListener(*gr.Event) {
	listeners, ok := l.State().Interface("listeners").([]interface{})
	if ok {

		newListener := make(map[string]interface{})
		newListener["protocol"] = "tcp"
		newListener["instanceProtocol"] = "tcp"
		newListener["loadBalancerPort"] = 0
		newListener["instancePort"] = 0

		listeners = append([]interface{}{newListener}, listeners...)

		l.SetState(gr.State{"listeners": listeners})
		return
	}
	println("addListener failed?")
}

func (l LoadBalancerClassForm) removeListener(event *gr.Event) {
	index := event.Target().Get("id").Int()
	listeners, ok := l.State().Interface("listeners").([]interface{})
	if ok {
		listeners = append(listeners[:index], listeners[index+1:]...)
		l.SetState(gr.State{"listeners": listeners})
		return
	}
	println("removeListener failed?")
}
<commit_msg>update to lb class form<commit_after>package forms

import (
	"encoding/json"
	"fmt"

	"github.com/Jeffail/gabs"
	"github.com/bep/gr"
	"github.com/bep/gr/attr"
	"github.com/bep/gr/el"
	"github.com/bep/gr/evt"
	"github.com/murdinc/awsmDashboard/helpers"
)

var (
	elbSchemes = []string{"internal", "internet-facing"}
)

type LoadBalancerClassForm struct {
	*gr.This
}

// Implements the StateInitializer interface
func (l LoadBalancerClassForm) GetInitialState() gr.State {
	return gr.State{"querying": true, "error": "", "success": "", "step": 1,
		"loadBalancerListeners": []interface{}{},
	}
}

// Implements the ComponentWillMount interface
func (l LoadBalancerClassForm) ComponentWillMount() {
	var class map[string]interface{}

	if l.Props().Interface("class") != nil {
		classJson := l.Props().Interface("class").([]byte)
		json.Unmarshal(classJson, &class)
	}

	// health check
	healthCheck := class["loadBalancerHealthCheck"].(map[string]interface{})
	for k, v := range healthCheck {
		class[k] = v
	}

	// attributes
	attributes := class["loadBalancerAttributes"].(map[string]interface{})
	for k, v := range attributes {
		class[k] = v
	}

	l.SetState(class)
	l.SetState(gr.State{"querying": true})

	// Get our options for the form
	go func() {
		endpoint := "//localhost:8081/api/classes/" + l.Props().String("apiType") + "/options"
		resp, err := helpers.GetAPI(endpoint)
		if !l.IsMounted() {
			return
		}
		if err != nil {
			l.SetState(gr.State{"querying": false, "error": fmt.Sprintf("Error while querying endpoint: %s", endpoint)})
			return
		}

		l.SetState(gr.State{"classOptionsResp": resp, "querying": false})
	}()
}

func (l LoadBalancerClassForm) Render() gr.Component {

	state := l.State()
	props := l.Props()

	// Form placeholder
	response := el.Div()

	// Print any alerts
	helpers.ErrorElem(state.String("error")).Modify(response)
	helpers.SuccessElem(state.String("success")).Modify(response)

	if state.Int("step") == 1 {
		if state.Bool("querying") {
			gr.Text("Loading...").Modify(response)
		} else {
			l.BuildClassForm(props.String("className"), state.Interface("classOptionsResp")).Modify(response)
		}

	} else if state.Int("step") == 2 {

		if state.Bool("querying") {
			gr.Text("Saving...").Modify(response)
		} else {

			buttons := el.Div(
				gr.CSS("btn-toolbar"),
			)

			// Back
			el.Button(
				evt.Click(l.backButton).PreventDefault(),
				gr.CSS("btn", "btn-secondary"),
				gr.Text("Back"),
			).Modify(buttons)

			// Done
			el.Button(
				evt.Click(l.doneButton).PreventDefault(),
				gr.CSS("btn", "btn-primary"),
				gr.Text("Done"),
			).Modify(buttons)

			buttons.Modify(response)
		}

	}

	return response
}

func (l LoadBalancerClassForm) BuildClassForm(className string, optionsResp interface{}) *gr.Element {

	state := l.State()
	props := l.Props()

	var classOptions map[string][]string
	jsonParsed, _ := gabs.ParseJSON(optionsResp.([]byte))
	classOptionsJson := jsonParsed.S("classOptions").Bytes()
	json.Unmarshal(classOptionsJson, &classOptions)

	classEdit := el.Div(
		el.Header3(gr.Text(className)),
		el.HorizontalRule(),
	)

	classEditForm := el.Form(evt.KeyDown(DisableEnter))

	SelectOne("Scheme", "scheme", elbSchemes, state.Interface("scheme"), l.storeSelect).Modify(classEditForm)
	SelectOne("Vpc", "vpc", classOptions["vpcs"], state.Interface("vpc"), l.storeSelect).Modify(classEditForm)
	if vpc, ok := state.Interface("vpc").(string); ok && len(vpc) > 0 {
		SelectMultiple("Subnets", "subnets", classOptions["subnets"], state.Interface("subnets"), l.storeSelect).Modify(classEditForm)
		SelectMultiple("Security Groups", "securityGroups", classOptions["securitygroups"], state.Interface("securityGroups"), l.storeSelect).Modify(classEditForm)
	}

	SelectMultiple("Availability Zones", "availabilityZones", classOptions["zones"], state.Interface("availabilityZones"), l.storeSelect).Modify(classEditForm)
	Checkbox("Cross Zone Load Balancing", "crossZoneLoadBalancingEnabled", state.Bool("crossZoneLoadBalancingEnabled"), l.storeValue).Modify(classEditForm)

	NumberField("Idle Timeout", "idleTimeout", state.Int("idleTimeout"), l.storeValue).Modify(classEditForm)

	el.Div(
		el.Break(nil),
		el.Header4(
			gr.Text("Health Check"),
		),
		el.HorizontalRule(nil),
	).Modify(classEditForm)

	TextField("Target", "healthCheckTarget", state.String("healthCheckTarget"), l.storeValue).Modify(classEditForm)
	NumberField("Timeout", "healthCheckTimeout", state.Int("healthCheckTimeout"), l.storeValue).Modify(classEditForm)
	NumberField("Interval", "healthCheckInterval", state.Int("healthCheckInterval"), l.storeValue).Modify(classEditForm)
	NumberField("Unhealthy Threshold", "healthCheckUnhealthyThreshold", state.Int("healthCheckUnhealthyThreshold"), l.storeValue).Modify(classEditForm)
	NumberField("Healthy Threshold", "healthCheckHealthyThreshold", state.Int("healthCheckHealthyThreshold"), l.storeValue).Modify(classEditForm)

	el.Div(
		el.Break(nil),
		el.Header4(
			gr.Text("Connection Draining"),
		),
		el.HorizontalRule(nil),
	).Modify(classEditForm)

	Toggle("Disabled", "Enabled", "connectionDrainingEnabled", state.Bool("connectionDrainingEnabled"), l.storeValue).Modify(classEditForm)
	if state.Bool("connectionDrainingEnabled") {
		NumberField("Draining Timeout", "connectionDrainingTimeout", state.Int("connectionDrainingTimeout"), l.storeValue).Modify(classEditForm)
	}

	el.Div(
		el.Break(nil),
		el.Header4(
			gr.Text("Access Log"),
		),
		el.HorizontalRule(nil),
	).Modify(classEditForm)

	Toggle("Disabled", "Enabled", "accessLogEnabled", state.Bool("accessLogEnabled"), l.storeValue).Modify(classEditForm)
	if state.Bool("accessLogEnabled") {
		NumberField("Emit Interval", "accessLogEmitInterval", state.Int("accessLogEmitInterval"), l.storeValue).Modify(classEditForm)
		TextField("S3 Bucket Name", "accessLogS3BucketName", state.String("accessLogS3BucketName"), l.storeValue).Modify(classEditForm)
		TextField("S3 Bucket Prefix", "accessLogS3BucketPrefix", state.String("accessLogS3BucketPrefix"), l.storeValue).Modify(classEditForm)
	}

	el.Div(
		el.Break(nil),
		el.Header4(
			gr.Text("Listeners"),
			el.Button(
				evt.Click(l.addListener).PreventDefault(),
				gr.CSS("btn", "btn-primary", "btn-sm", "pull-right"),
				gr.Text("New"),
			),
		),
		el.HorizontalRule(nil),
	).Modify(classEditForm)

	listeners := state.Interface("loadBalancerListeners").([]interface{})

	for index, listInf := range listeners {

		listener := listInf.(map[string]interface{})

		// Form placeholder
		listenerForm := el.Div()

		//TextField("Note", "note", listener["note"], l.modifyListener(index, listener)).Modify(listenerForm)

		el.Div(
			gr.CSS("row"), el.Div(gr.CSS("col-sm-6"),
				SelectOne("Protocol", "protocol", []string{"HTTP", "HTTPS", "TCP", "SSL"}, listener["protocol"], l.storeListenerSelect(index, listener)),
			),
			el.Div(gr.CSS("col-sm-6"),
				NumberField("Load Balancer Port", "loadBalancerPort", listener["loadBalancerPort"], l.modifyListener(index, listener)),
			),
		).Modify(listenerForm)

		el.Div(
			gr.CSS("row"), el.Div(gr.CSS("col-sm-6"),
				SelectOne("Instance Protocol", "instanceProtocol", []string{"HTTP", "HTTPS", "TCP", "SSL"}, listener["instanceProtocol"], l.storeListenerSelect(index, listener)),
			),
			el.Div(gr.CSS("col-sm-6"),
				NumberField("Instance Port", "instancePort", listener["instancePort"], l.modifyListener(index, listener)),
			),
		).Modify(listenerForm)

		el.Div(
			gr.CSS("btn-toolbar"),
			el.Button(
				evt.Click(l.removeListener).PreventDefault(),
				gr.CSS("btn", "btn-danger", "btn-sm", "pull-right"),
				gr.Text("Remove"),
				attr.ID(index),
			),
		).Modify(listenerForm)

		el.HorizontalRule(nil).Modify(listenerForm)

		listenerForm.Modify(classEditForm)

	}

	classEditForm.Modify(classEdit)

	buttons := el.Div(
		gr.CSS("btn-toolbar"),
	)

	// Back
	el.Button(
		evt.Click(l.backButton).PreventDefault(),
		gr.CSS("btn", "btn-secondary"),
		gr.Text("Back"),
	).Modify(buttons)

	// Save
	el.Button(
		evt.Click(l.saveButton).PreventDefault(),
		gr.CSS("btn", "btn-primary"),
		gr.Text("Save"),
	).Modify(buttons)

	// Delete
	if props.Interface("hasDelete") != nil && props.Bool("hasDelete") {
		el.Button(
			evt.Click(l.deleteButton).PreventDefault(),
			gr.CSS("btn", "btn-danger", "pull-right"),
			gr.Text("Delete"),
		).Modify(buttons)
	}

	buttons.Modify(classEdit)

	return classEdit

}

func (l LoadBalancerClassForm) backButton(*gr.Event) {
	l.SetState(gr.State{"success": ""})
	l.Props().Call("backButton")
}

func (l LoadBalancerClassForm) doneButton(*gr.Event) {
	l.SetState(gr.State{"success": ""})
	l.Props().Call("hideAllModals")
}

func (l LoadBalancerClassForm) saveButton(*gr.Event) {
	l.SetState(gr.State{"querying": true, "step": 2})

	cfg := make(map[string]interface{})
	for key, _ := range l.State() {
		cfg[key] = l.State().Interface(key)
	}

	loadBalancerHealthCheck := make(map[string]interface{})
	loadBalancerHealthCheck["healthCheckTarget"] = cfg["healthCheckTarget"]
	loadBalancerHealthCheck["healthCheckTimeout"] = cfg["healthCheckTimeout"]
	loadBalancerHealthCheck["healthCheckInterval"] = cfg["healthCheckInterval"]
	loadBalancerHealthCheck["healthCheckUnhealthyThreshold"] = cfg["healthCheckUnhealthyThreshold"]
	loadBalancerHealthCheck["healthCheckHealthyThreshold"] = cfg["healthCheckHealthyThreshold"]
	cfg["loadBalancerHealthCheck"] = loadBalancerHealthCheck

	loadBalancerAttributes := make(map[string]interface{})
	loadBalancerAttributes["connectionDrainingEnabled"] = cfg["connectionDrainingEnabled"]
	loadBalancerAttributes["connectionDrainingTimeout"] = cfg["connectionDrainingTimeout"]
	loadBalancerAttributes["idleTimeout"] = cfg["idleTimeout"]
	loadBalancerAttributes["crossZoneLoadBalancingEnabled"] = cfg["crossZoneLoadBalancingEnabled"]
	loadBalancerAttributes["accessLogEnabled"] = cfg["accessLogEnabled"]
	loadBalancerAttributes["accessLogEmitInterval"] = cfg["accessLogEmitInterval"]
	loadBalancerAttributes["accessLogS3BucketName"] = cfg["accessLogS3BucketName"]
	loadBalancerAttributes["accessLogS3BucketPrefix"] = cfg["accessLogS3BucketPrefix"]
	cfg["loadBalancerAttributes"] = loadBalancerAttributes

	go func() {
		endpoint := "//localhost:8081/api/classes/" + l.Props().String("apiType") + "/name/" + l.Props().String("className")

		_, err := helpers.PutAPI(endpoint, cfg)
		if !l.IsMounted() {
			return
		}

		if err != nil {
			l.SetState(gr.State{"querying": false, "error": fmt.Sprintf("Error while querying endpoint: %s", endpoint), "step": 1})
			return
		}

		l.SetState(gr.State{"querying": false, "success": "Class was saved", "error": ""})
	}()

}

func (l LoadBalancerClassForm) deleteButton(*gr.Event) {
	l.SetState(gr.State{"querying": true})

	go func() {
		endpoint := "//localhost:8081/api/classes/" + l.Props().String("apiType") + "/name/" + l.Props().String("className")

		_, err := helpers.DeleteAPI(endpoint)
		if !l.IsMounted() {
			return
		}

		if err != nil {
			l.SetState(gr.State{"querying": false, "error": fmt.Sprintf("Error while querying endpoint: %s", endpoint)})
			return
		}

		l.SetState(gr.State{"querying": false, "success": "Class was deleted", "error": "", "step": 2})
	}()
}

func (l LoadBalancerClassForm) storeValue(event *gr.Event) {
	key := event.Target().Get("name").String()
	inputType := event.Target().Get("type").String()

	switch inputType {

	case "checkbox":
		l.SetState(gr.State{key: event.Target().Get("checked").Bool()})

	case "number":
		l.SetState(gr.State{key: event.TargetValue().Int()})

	default: // text, at least
		l.SetState(gr.State{key: event.TargetValue()})

	}
}

func (l LoadBalancerClassForm) storeSelect(key string, val interface{}) {
	switch value := val.(type) {

	case map[string]interface{}:
		// single
		l.SetState(gr.State{key: value["value"]})

	case []interface{}:
		// multi
		var vals []string
		options := len(value)
		for i := 0; i < options; i++ {
			vals = append(vals, value[i].(map[string]interface{})["value"].(string))
		}
		l.SetState(gr.State{key: vals})

	default:
		l.SetState(gr.State{key: val})

	}
}

func (l LoadBalancerClassForm) modifyListener(index int, listener map[string]interface{}) func(*gr.Event) {
	return func(event *gr.Event) {
		key := event.Target().Get("name").String()
		valueType := event.Target().Get("type").String()

		switch valueType {
		case "text":
			listener[key] = event.TargetValue().String()
		case "number":
			listener[key] = event.TargetValue().Int()
		}
		listeners, ok := l.State().Interface("loadBalancerListeners").([]interface{})
		if ok {
			listeners[index] = listener
			l.SetState(gr.State{"loadBalancerListeners": listeners})
			return
		}
		println("modifyListener failed?")
	}
}

func (l LoadBalancerClassForm) storeListenerSelect(index int, listener map[string]interface{}) func(string, interface{}) {
	return func(key string, val interface{}) {

		switch value := val.(type) {

		case map[string]interface{}:
			// single
			listener[key] = value["value"]

		case []interface{}:
			// multi
			var vals []string
			options := len(value)
			for i := 0; i < options; i++ {
				vals = append(vals, value[i].(map[string]interface{})["value"].(string))
			}
			listener[key] = vals

		default:
			listener[key] = val
		}

		listeners, ok := l.State().Interface("loadBalancerListeners").([]interface{})
		if ok {
			listeners[index] = listener
			l.SetState(gr.State{"loadBalancerListeners": listeners})
			return
		}
	}
}

func (l LoadBalancerClassForm) addListener(*gr.Event) {
	listeners, ok := l.State().Interface("loadBalancerListeners").([]interface{})
	if ok {

		newListener := make(map[string]interface{})
		newListener["protocol"] = "tcp"
		newListener["instanceProtocol"] = "tcp"
		newListener["loadBalancerPort"] = 0
		newListener["instancePort"] = 0

		listeners = append([]interface{}{newListener}, listeners...)

		l.SetState(gr.State{"loadBalancerListeners": listeners})
		return
	}
	println("addListener failed?")
}

func (l LoadBalancerClassForm) removeListener(event *gr.Event) {
	index := event.Target().Get("id").Int()
	listeners, ok := l.State().Interface("loadBalancerListeners").([]interface{})
	if ok {
		listeners = append(listeners[:index], listeners[index+1:]...)
		l.SetState(gr.State{"loadBalancerListeners": listeners})
		return
	}
	println("removeListener failed?")
}
<|endoftext|>


<commit_before>package migrate

import "github.com/urfave/cli"

var Commands = make([]cli.Command, 0)
<commit_msg>add doc comment to admin/migrate.Commands<commit_after>package migrate

import "github.com/urfave/cli"

// Commands is all the 'migrate blah' admin commands
var Commands = make([]cli.Command, 0)
<|endoftext|>