<commit_before>package php

import "stephensearles.com/php/ast"

/*

Valid Expression Patterns
Expr [Binary Op] Expr
[Unary Op] Expr
Expr [Unary Op]
Expr [Tertiary Op 1] Expr [Tertiary Op 2] Expr
Identifier
Literal
Function Call

Parentesis always triggers sub-expression

non-associative clone new clone and new
left  [ array()
right ++ -- ~ (int) (float) (string) (array) (object) (bool) @  types and increment/decrement
non-associative instanceof  types
right ! logical
left  * / % arithmetic
left  + - . arithmetic and string
left  << >> bitwise
non-associative < <= > >= comparison
non-associative == != === !== <>  comparison
left  & bitwise and references
left  ^ bitwise
left  | bitwise
left  &&  logical
left  ||  logical
left  ? : ternary
right = += -= *= /= .= %= &= |= ^= <<= >>= => assignment
left  and logical
left  xor logical
left  or  logical
left  , many uses

*/

var operatorPrecedence = map[ItemType]int{
	itemArrayLookupOperatorLeft: 19,
	itemUnaryOperator:           18,
	itemCastOperator:            18,
	itemInstanceofOperator:      17,
	itemNegationOperator:        16,
	itemMultOperator:            15,
	itemAdditionOperator:        14,
	itemSubtractionOperator:     14,
	itemConcatenationOperator:   14,

	itemBitwiseShiftOperator: 13,
	itemComparisonOperator:   12,
	itemEqualityOperator:     11,

	itemAmpersandOperator:  10,
	itemBitwiseXorOperator: 9,
	itemBitwiseOrOperator:  8,
	itemAndOperator:        7,
	itemOrOperator:         6,
	itemTernaryOperator1:   5,
	itemTernaryOperator2:   5,
	itemAssignmentOperator: 4,
	itemWrittenAndOperator: 3,
	itemWrittenXorOperator: 2,
	itemWrittenOrOperator:  1,
}

func (p *parser) parseExpression() (expr ast.Expression) {
	// consume expression
	originalParenLev := p.parenLevel
	switch p.current.typ {
	case itemIgnoreErrorOperator:
		p.next()
		return p.parseExpression()
	case itemNewOperator:
		return &ast.NewExpression{
			Expression: p.parseNextExpression(),
		}
	case itemIdentifier:
		if p.peek().typ == itemAssignmentOperator {
			assignee := p.parseIdentifier().(ast.Assignable)
			p.next()
			return ast.AssignmentExpression{
				Assignee: assignee,
				Operator: p.current.val,
				Value:    p.parseNextExpression(),
			}
		}
		fallthrough
	case itemArray:
		fallthrough
	case itemUnaryOperator, itemNegationOperator, itemAmpersandOperator, itemCastOperator:
		fallthrough
	case itemNonVariableIdentifier, itemStringLiteral, itemNumberLiteral, itemBooleanLiteral, itemInclude, itemNull, itemSelf, itemStatic, itemParent:
		expr = p.parseOperation(originalParenLev, p.expressionize())
	case itemOpenParen:
		p.parenLevel += 1
		p.next()
		expr = p.parseExpression()
		p.expect(itemCloseParen)
		p.parenLevel -= 1
		expr = p.parseOperation(originalParenLev, expr)
	default:
		p.errorf("Expected expression. Found %s", p.current)
		return
	}
	if p.parenLevel != originalParenLev {
		p.errorf("unbalanced parens: %d prev: %d", p.parenLevel, originalParenLev)
		return
	}
	return
}

func (p *parser) parseOperation(originalParenLevel int, lhs ast.Expression) (expr ast.Expression) {
	p.next()
	switch p.current.typ {
	case itemUnaryOperator:
		expr = p.parseUnaryExpressionLeft(lhs, p.current)
	case itemAdditionOperator, itemSubtractionOperator, itemConcatenationOperator, itemComparisonOperator, itemMultOperator, itemAndOperator, itemOrOperator, itemAmpersandOperator, itemBitwiseXorOperator, itemBitwiseOrOperator, itemBitwiseShiftOperator, itemWrittenAndOperator, itemWrittenXorOperator, itemWrittenOrOperator:
		expr = p.parseBinaryOperation(lhs, p.current, originalParenLevel)
	case itemTernaryOperator1:
		expr = p.parseTernaryOperation(lhs)
	case itemCloseParen:
		if p.parenLevel <= originalParenLevel {
			p.backup()
			return lhs
		}
		p.parenLevel -= 1
		return p.parseOperation(originalParenLevel, lhs)
	case itemAssignmentOperator:
		assignee := lhs.(ast.Assignable)
		expr = ast.AssignmentExpression{
			Assignee: assignee,
			Operator: p.current.val,
			Value:    p.parseNextExpression(),
		}
	default:
		p.backup()
		return lhs
	}
	return p.parseOperation(originalParenLevel, expr)
}

func (p *parser) parseBinaryOperation(lhs ast.Expression, operator Item, originalParenLevel int) ast.Expression {
	p.next()
	rhs := p.expressionize()
	for {
		nextOperator := p.peek()
		nextOperatorPrecedence, ok := operatorPrecedence[nextOperator.typ]
		if ok && nextOperatorPrecedence > operatorPrecedence[operator.typ] {
			rhs = p.parseOperation(originalParenLevel, rhs)
		} else {
			break
		}
	}
	return newBinaryOperation(operator, lhs, rhs)
}

func (p *parser) parseTernaryOperation(lhs ast.Expression) ast.Expression {
	truthy := p.parseNextExpression()
	p.expect(itemTernaryOperator2)
	falsy := p.parseNextExpression()
	return &ast.OperatorExpression{
		Operand1: lhs,
		Operand2: truthy,
		Operand3: falsy,
		Type:     truthy.EvaluatesTo() | falsy.EvaluatesTo(),
		Operator: "?:",
	}
}

func (p *parser) parseUnaryExpressionRight(operand ast.Expression, operator Item) ast.Expression {
	return newUnaryOperation(operator, operand)
}

func (p *parser) parseUnaryExpressionLeft(operand ast.Expression, operator Item) ast.Expression {
	return newUnaryOperation(operator, operand)
}

// expressionize takes the current token and returns it as the simplest
// expression for that token. That means an expression with no operators
// except for the object operator.
func (p *parser) expressionize() ast.Expression {
	switch p.current.typ {
	case itemUnaryOperator, itemNegationOperator, itemCastOperator:
		op := p.current
		p.next()
		return p.parseUnaryExpressionRight(p.expressionize(), op)
	case itemArray:
		return p.parseArrayDeclaration()
	case itemIdentifier:
		return p.parseIdentifier()
	case itemStringLiteral, itemBooleanLiteral, itemNumberLiteral, itemNull:
		return p.parseLiteral()
	case itemNonVariableIdentifier, itemSelf, itemStatic, itemParent:
		if p.peek().typ == itemOpenParen {
			var expr ast.Expression
			expr = p.parseFunctionCall()
			for p.peek().typ == itemObjectOperator {
				expr = p.parseObjectLookup(expr)
			}
			return expr
		}
		if p.peek().typ == itemScopeResolutionOperator {
			r := p.current.val
			p.expect(itemScopeResolutionOperator)
			return &ast.ClassExpression{
				Receiver:   r,
				Expression: p.parseNextExpression(),
			}
		}
		return ast.ConstantExpression{
			Identifier: ast.NewIdentifier(p.current.val),
		}
	case itemOpenParen:
		return p.parseExpression()
	case itemInclude:
		return ast.Include{Expression: p.parseNextExpression()}
	}
	// error?
	return nil
}

func (p *parser) parseLiteral() *ast.Literal {
	switch p.current.typ {
	case itemStringLiteral:
		return &ast.Literal{Type: ast.String}
	case itemBooleanLiteral:
		return &ast.Literal{Type: ast.Boolean}
	case itemNumberLiteral:
		return &ast.Literal{Type: ast.Float}
	case itemNull:
		return &ast.Literal{Type: ast.Null}
	}
	p.errorf("Unknown literal type")
	return nil
}

func (p *parser) parseIdentifier() ast.Expression {
	var expr ast.Expression
	expr = ast.NewIdentifier(p.current.val)
	switch pk := p.peek(); pk.typ {
	case itemScopeResolutionOperator:
		r := p.current.val
		p.expect(itemScopeResolutionOperator)
		p.next()
		return &ast.ClassExpression{
			Receiver:   r,
			Expression: p.expressionize(),
		}
	case itemObjectOperator:
		for p.peek().typ == itemObjectOperator {
			expr = p.parseObjectLookup(expr)
		}
	case itemArrayLookupOperatorLeft:
		return p.parseArrayLookup(expr)
	}
	return expr
}

func (p *parser) parseObjectLookup(r ast.Expression) ast.Expression {
	p.expect(itemObjectOperator)
	p.expect(itemNonVariableIdentifier)
	switch pk := p.peek(); pk.typ {
	case itemOpenParen:
		expr := &ast.MethodCallExpression{
			Receiver:               r,
			FunctionCallExpression: p.parseFunctionCall(),
		}
		return expr
	case itemArrayLookupOperatorLeft:
		return p.parseArrayLookup(&ast.PropertyExpression{
			Receiver: r,
			Name:     p.current.val,
		})
	}
	return &ast.PropertyExpression{
		Receiver: r,
		Name:     p.current.val,
	}
}

func (p *parser) parseArrayLookup(e ast.Expression) ast.Expression {
	p.expect(itemArrayLookupOperatorLeft)
	if p.peek().typ == itemArrayLookupOperatorRight {
		p.expect(itemArrayLookupOperatorRight)
		return ast.ArrayAppendExpression{Array: e}
	}
	p.next()
	expr := &ast.ArrayLookupExpression{
		Array: e,
		Index: p.parseExpression(),
	}
	p.expect(itemArrayLookupOperatorRight)
	if p.peek().typ == itemArrayLookupOperatorLeft {
		return p.parseArrayLookup(expr)
	}
	return expr
}

func (p *parser) parseArrayDeclaration() ast.Expression {
	pairs := make([]ast.ArrayPair, 0)
	p.expect(itemOpenParen)
ArrayLoop:
	for {
		var key, val ast.Expression
		switch p.peek().typ {
		case itemCloseParen:
			break ArrayLoop
		default:
			val = p.parseNextExpression()
		}
		switch p.peek().typ {
		case itemComma:
			p.expect(itemComma)
		case itemCloseParen:
			pairs = append(pairs, ast.ArrayPair{Key: key, Value: val})
			break ArrayLoop
		case itemArrayKeyOperator:
			p.expect(itemArrayKeyOperator)
			key = val
			val = p.parseNextExpression()
			if p.peek().typ == itemCloseParen {
				pairs = append(pairs, ast.ArrayPair{Key: key, Value: val})
				break ArrayLoop
			}
			p.expect(itemComma)
		default:
			p.errorf("expected => or ,")
			return nil
		}
		pairs = append(pairs, ast.ArrayPair{Key: key, Value: val})
	}
	p.expect(itemCloseParen)
	return &ast.ArrayExpression{Pairs: pairs}
}
<commit_msg>Allowing instanceof to be a binary expression operator<commit_after>package php

import "stephensearles.com/php/ast"

/*

Valid Expression Patterns
Expr [Binary Op] Expr
[Unary Op] Expr
Expr [Unary Op]
Expr [Tertiary Op 1] Expr [Tertiary Op 2] Expr
Identifier
Literal
Function Call

Parentesis always triggers sub-expression

non-associative clone new clone and new
left  [ array()
right ++ -- ~ (int) (float) (string) (array) (object) (bool) @  types and increment/decrement
non-associative instanceof  types
right ! logical
left  * / % arithmetic
left  + - . arithmetic and string
left  << >> bitwise
non-associative < <= > >= comparison
non-associative == != === !== <>  comparison
left  & bitwise and references
left  ^ bitwise
left  | bitwise
left  &&  logical
left  ||  logical
left  ? : ternary
right = += -= *= /= .= %= &= |= ^= <<= >>= => assignment
left  and logical
left  xor logical
left  or  logical
left  , many uses

*/

var operatorPrecedence = map[ItemType]int{
	itemArrayLookupOperatorLeft: 19,
	itemUnaryOperator:           18,
	itemCastOperator:            18,
	itemInstanceofOperator:      17,
	itemNegationOperator:        16,
	itemMultOperator:            15,
	itemAdditionOperator:        14,
	itemSubtractionOperator:     14,
	itemConcatenationOperator:   14,

	itemBitwiseShiftOperator: 13,
	itemComparisonOperator:   12,
	itemEqualityOperator:     11,

	itemAmpersandOperator:  10,
	itemBitwiseXorOperator: 9,
	itemBitwiseOrOperator:  8,
	itemAndOperator:        7,
	itemOrOperator:         6,
	itemTernaryOperator1:   5,
	itemTernaryOperator2:   5,
	itemAssignmentOperator: 4,
	itemWrittenAndOperator: 3,
	itemWrittenXorOperator: 2,
	itemWrittenOrOperator:  1,
}

func (p *parser) parseExpression() (expr ast.Expression) {
	// consume expression
	originalParenLev := p.parenLevel
	switch p.current.typ {
	case itemIgnoreErrorOperator:
		p.next()
		return p.parseExpression()
	case itemNewOperator:
		return &ast.NewExpression{
			Expression: p.parseNextExpression(),
		}
	case itemIdentifier:
		if p.peek().typ == itemAssignmentOperator {
			assignee := p.parseIdentifier().(ast.Assignable)
			p.next()
			return ast.AssignmentExpression{
				Assignee: assignee,
				Operator: p.current.val,
				Value:    p.parseNextExpression(),
			}
		}
		fallthrough
	case itemArray:
		fallthrough
	case itemUnaryOperator, itemNegationOperator, itemAmpersandOperator, itemCastOperator:
		fallthrough
	case itemNonVariableIdentifier, itemStringLiteral, itemNumberLiteral, itemBooleanLiteral, itemInclude, itemNull, itemSelf, itemStatic, itemParent:
		expr = p.parseOperation(originalParenLev, p.expressionize())
	case itemOpenParen:
		p.parenLevel += 1
		p.next()
		expr = p.parseExpression()
		p.expect(itemCloseParen)
		p.parenLevel -= 1
		expr = p.parseOperation(originalParenLev, expr)
	default:
		p.errorf("Expected expression. Found %s", p.current)
		return
	}
	if p.parenLevel != originalParenLev {
		p.errorf("unbalanced parens: %d prev: %d", p.parenLevel, originalParenLev)
		return
	}
	return
}

func (p *parser) parseOperation(originalParenLevel int, lhs ast.Expression) (expr ast.Expression) {
	p.next()
	switch p.current.typ {
	case itemUnaryOperator:
		expr = p.parseUnaryExpressionLeft(lhs, p.current)
	case itemAdditionOperator, itemSubtractionOperator, itemConcatenationOperator, itemComparisonOperator, itemMultOperator, itemAndOperator, itemOrOperator, itemAmpersandOperator, itemBitwiseXorOperator, itemBitwiseOrOperator, itemBitwiseShiftOperator, itemWrittenAndOperator, itemWrittenXorOperator, itemWrittenOrOperator, itemInstanceofOperator:
		expr = p.parseBinaryOperation(lhs, p.current, originalParenLevel)
	case itemTernaryOperator1:
		expr = p.parseTernaryOperation(lhs)
	case itemCloseParen:
		if p.parenLevel <= originalParenLevel {
			p.backup()
			return lhs
		}
		p.parenLevel -= 1
		return p.parseOperation(originalParenLevel, lhs)
	case itemAssignmentOperator:
		assignee := lhs.(ast.Assignable)
		expr = ast.AssignmentExpression{
			Assignee: assignee,
			Operator: p.current.val,
			Value:    p.parseNextExpression(),
		}
	default:
		p.backup()
		return lhs
	}
	return p.parseOperation(originalParenLevel, expr)
}

func (p *parser) parseBinaryOperation(lhs ast.Expression, operator Item, originalParenLevel int) ast.Expression {
	p.next()
	rhs := p.expressionize()
	for {
		nextOperator := p.peek()
		nextOperatorPrecedence, ok := operatorPrecedence[nextOperator.typ]
		if ok && nextOperatorPrecedence > operatorPrecedence[operator.typ] {
			rhs = p.parseOperation(originalParenLevel, rhs)
		} else {
			break
		}
	}
	return newBinaryOperation(operator, lhs, rhs)
}

func (p *parser) parseTernaryOperation(lhs ast.Expression) ast.Expression {
	truthy := p.parseNextExpression()
	p.expect(itemTernaryOperator2)
	falsy := p.parseNextExpression()
	return &ast.OperatorExpression{
		Operand1: lhs,
		Operand2: truthy,
		Operand3: falsy,
		Type:     truthy.EvaluatesTo() | falsy.EvaluatesTo(),
		Operator: "?:",
	}
}

func (p *parser) parseUnaryExpressionRight(operand ast.Expression, operator Item) ast.Expression {
	return newUnaryOperation(operator, operand)
}

func (p *parser) parseUnaryExpressionLeft(operand ast.Expression, operator Item) ast.Expression {
	return newUnaryOperation(operator, operand)
}

// expressionize takes the current token and returns it as the simplest
// expression for that token. That means an expression with no operators
// except for the object operator.
func (p *parser) expressionize() ast.Expression {
	switch p.current.typ {
	case itemUnaryOperator, itemNegationOperator, itemCastOperator:
		op := p.current
		p.next()
		return p.parseUnaryExpressionRight(p.expressionize(), op)
	case itemArray:
		return p.parseArrayDeclaration()
	case itemIdentifier:
		return p.parseIdentifier()
	case itemStringLiteral, itemBooleanLiteral, itemNumberLiteral, itemNull:
		return p.parseLiteral()
	case itemNonVariableIdentifier, itemSelf, itemStatic, itemParent:
		if p.peek().typ == itemOpenParen {
			var expr ast.Expression
			expr = p.parseFunctionCall()
			for p.peek().typ == itemObjectOperator {
				expr = p.parseObjectLookup(expr)
			}
			return expr
		}
		if p.peek().typ == itemScopeResolutionOperator {
			r := p.current.val
			p.expect(itemScopeResolutionOperator)
			return &ast.ClassExpression{
				Receiver:   r,
				Expression: p.parseNextExpression(),
			}
		}
		return ast.ConstantExpression{
			Identifier: ast.NewIdentifier(p.current.val),
		}
	case itemOpenParen:
		return p.parseExpression()
	case itemInclude:
		return ast.Include{Expression: p.parseNextExpression()}
	}
	// error?
	return nil
}

func (p *parser) parseLiteral() *ast.Literal {
	switch p.current.typ {
	case itemStringLiteral:
		return &ast.Literal{Type: ast.String}
	case itemBooleanLiteral:
		return &ast.Literal{Type: ast.Boolean}
	case itemNumberLiteral:
		return &ast.Literal{Type: ast.Float}
	case itemNull:
		return &ast.Literal{Type: ast.Null}
	}
	p.errorf("Unknown literal type")
	return nil
}

func (p *parser) parseIdentifier() ast.Expression {
	var expr ast.Expression
	expr = ast.NewIdentifier(p.current.val)
	switch pk := p.peek(); pk.typ {
	case itemScopeResolutionOperator:
		r := p.current.val
		p.expect(itemScopeResolutionOperator)
		p.next()
		return &ast.ClassExpression{
			Receiver:   r,
			Expression: p.expressionize(),
		}
	case itemObjectOperator:
		for p.peek().typ == itemObjectOperator {
			expr = p.parseObjectLookup(expr)
		}
	case itemArrayLookupOperatorLeft:
		return p.parseArrayLookup(expr)
	}
	return expr
}

func (p *parser) parseObjectLookup(r ast.Expression) ast.Expression {
	p.expect(itemObjectOperator)
	p.expect(itemNonVariableIdentifier)
	switch pk := p.peek(); pk.typ {
	case itemOpenParen:
		expr := &ast.MethodCallExpression{
			Receiver:               r,
			FunctionCallExpression: p.parseFunctionCall(),
		}
		return expr
	case itemArrayLookupOperatorLeft:
		return p.parseArrayLookup(&ast.PropertyExpression{
			Receiver: r,
			Name:     p.current.val,
		})
	}
	return &ast.PropertyExpression{
		Receiver: r,
		Name:     p.current.val,
	}
}

func (p *parser) parseArrayLookup(e ast.Expression) ast.Expression {
	p.expect(itemArrayLookupOperatorLeft)
	if p.peek().typ == itemArrayLookupOperatorRight {
		p.expect(itemArrayLookupOperatorRight)
		return ast.ArrayAppendExpression{Array: e}
	}
	p.next()
	expr := &ast.ArrayLookupExpression{
		Array: e,
		Index: p.parseExpression(),
	}
	p.expect(itemArrayLookupOperatorRight)
	if p.peek().typ == itemArrayLookupOperatorLeft {
		return p.parseArrayLookup(expr)
	}
	return expr
}

func (p *parser) parseArrayDeclaration() ast.Expression {
	pairs := make([]ast.ArrayPair, 0)
	p.expect(itemOpenParen)
ArrayLoop:
	for {
		var key, val ast.Expression
		switch p.peek().typ {
		case itemCloseParen:
			break ArrayLoop
		default:
			val = p.parseNextExpression()
		}
		switch p.peek().typ {
		case itemComma:
			p.expect(itemComma)
		case itemCloseParen:
			pairs = append(pairs, ast.ArrayPair{Key: key, Value: val})
			break ArrayLoop
		case itemArrayKeyOperator:
			p.expect(itemArrayKeyOperator)
			key = val
			val = p.parseNextExpression()
			if p.peek().typ == itemCloseParen {
				pairs = append(pairs, ast.ArrayPair{Key: key, Value: val})
				break ArrayLoop
			}
			p.expect(itemComma)
		default:
			p.errorf("expected => or ,")
			return nil
		}
		pairs = append(pairs, ast.ArrayPair{Key: key, Value: val})
	}
	p.expect(itemCloseParen)
	return &ast.ArrayExpression{Pairs: pairs}
}
<|endoftext|>


<commit_before>package faker_test

import (
	"bytes"
	"github.com/olekukonko/tablewriter"
	"github.com/raitucarp/faker"
	"strings"
	"testing"
	//"time"
	"strconv"
)

type Table struct {
	data   [][]string
	header []string
	buf    *bytes.Buffer
	table  *tablewriter.Table
}

func (t *Table) SetHeader(header ...string) {
	t.table.SetHeader(header)
}

func (t *Table) AddData(data ...string) {
	t.table.Append(data)
}

func (t *Table) Render() {
	t.table.Render()
}

func (t *Table) String() string {
	return t.buf.String()
}

func NewTable() Table {
	t := Table{}
	t.buf = new(bytes.Buffer)
	t.table = tablewriter.NewWriter(t.buf)

	return t
}

// Testing begin here
func TestAddress(t *testing.T) {
	f := faker.New()
	tbl := NewTable()
	tbl.SetHeader("Field Name", "Data")
	tbl.AddData("ZipCode", f.Address.ZipCode_())
	tbl.AddData("CityPrefix", f.Address.CityPrefix_())
	tbl.AddData("City", f.Address.City_())
	tbl.AddData("CitySuffix", f.Address.CitySuffix_())
	tbl.AddData("StreetName", f.Address.StreetName_())
	tbl.AddData("StreetAddress", f.Address.StreetAddress_())
	tbl.AddData("StreetSuffix", f.Address.StreetSuffix_())
	tbl.AddData("StreetPrefix", f.Address.StreetPrefix_())
	tbl.AddData("SecondaryAddress", f.Address.SecondaryAddress_())
	tbl.AddData("County", f.Address.County_())
	tbl.AddData("Country", f.Address.Country_())
	tbl.AddData("CountryCode", f.Address.CountryCode_())
	tbl.AddData("State", f.Address.State_())
	tbl.AddData("StateAbbr", f.Address.StateAbbr_())
	tbl.AddData("Latitude", f.Address.Latitude_())
	tbl.AddData("Longitude", f.Address.Longitude_())

	/*JSON, err := f.Address.ToJSON()
	checkError(t, err)
	tbl.AddData("JSON", JSON)

	XML, err := f.Address.ToXML()
	checkError(t, err)
	tbl.AddData("XML", XML)*/

	tbl.Render()

	// logging
	t.Log("Fake Address\n", tbl.String())
}

func TestCommerce(t *testing.T) {
	f := faker.New()
	tbl := NewTable()
	tbl.SetHeader("Field Name", "Data")
	tbl.AddData("Color", f.Commerce.Color_())
	tbl.AddData("Department", f.Commerce.Department_())
	tbl.AddData("ProductName", f.Commerce.ProductName_())
	tbl.AddData("Price", f.Commerce.Price_(20000, 3, "$"))
	tbl.AddData("ProductAdjective", f.Commerce.ProductAdjective_())
	tbl.AddData("ProductMaterial", f.Commerce.ProductMaterial_())
	tbl.AddData("Product", f.Commerce.Product_())

	/*JSON, err := f.Commerce.ToJSON()
	checkError(t, err)
	tbl.AddData("JSON", JSON)

	XML, err := f.Commerce.ToXML()
	checkError(t, err)
	tbl.AddData("XML", XML)*/

	tbl.Render()

	// logging
	t.Log("Fake Commerce\n", tbl.String())
}

func TestCompany(t *testing.T) {
	f := faker.New()
	tbl := NewTable()
	tbl.SetHeader("Field Name", "Data")
	tbl.AddData("Suffixes", strings.Join(f.Company.Suffixes_(), ", "))
	tbl.AddData("CompanyName", f.Company.CompanyName_())
	tbl.AddData("CompanySuffix", f.Company.CompanySuffix_())
	tbl.AddData("CatchPhrase", f.Company.CatchPhrase_())
	tbl.AddData("CatchPhraseAdjective", f.Company.CatchPhraseAdjective_())
	tbl.AddData("CatchPhraseDescriptor", f.Company.CatchPhraseAdjective_())
	tbl.AddData("CatchPhraseNoun", f.Company.CatchPhraseNoun_())
	tbl.AddData("Bs", f.Company.BS_())
	tbl.AddData("BsAdjective", f.Company.BsAdjective_())
	tbl.AddData("BsBuzz", f.Company.BsBuzz_())
	tbl.AddData("BsNoun", f.Company.BsNoun_())
	tbl.Render()

	// logging
	t.Log("Fake Company\n", tbl.String())

}

func TestDate(t *testing.T) {
	d := faker.Date{}
	tbl := NewTable()

	tbl.SetHeader("Field Name", "Data")
	tbl.AddData("Past", d.Past_().String())
	tbl.AddData("Future", d.Future_().String())
	tbl.AddData("Between", d.Between_("02/21/2016", "02/22/2016", "01/2/20006").String())
	tbl.AddData("Recent", d.Recent_(1).String())
	tbl.AddData("Month", d.Month_())
	tbl.AddData("Weekday", d.Weekday_())

	tbl.Render()
	// logging
	t.Log("Fake Date\n", tbl.String())
}

func TestFinance(t *testing.T) {
	f := faker.Finance{}

	tbl := NewTable()

	tbl.SetHeader("Field Name", "Data")
	tbl.AddData("Account", f.Account_())
	tbl.AddData("Account Name", f.AccountName_())
	tbl.AddData("Amount", f.Amount_())
	tbl.AddData("Mask", f.Mask_())
	tbl.AddData("TransactionType", f.TransactionType_())
	tbl.AddData("CurrencySymbol", f.CurrencySymbol_())
	tbl.AddData("CurrencyCode", f.CurrencyCode_())
	tbl.AddData("CurrencyName", f.CurrencyName_())

	tbl.Render()
	// logging
	t.Log("Fake Finance\n", tbl.String())
}

func TestHacker(t *testing.T) {
	hacker := faker.Hacker{}

	tbl := NewTable()

	tbl.SetHeader("Field Name", "Data")
	tbl.AddData("Abbreviation", hacker.Abbreviation_())
	tbl.AddData("Adjective", hacker.Adjective_())
	tbl.AddData("Noun", hacker.Noun_())
	tbl.AddData("Verb", hacker.Verb_())
	tbl.AddData("IngVerb", hacker.IngVerb_())
	tbl.AddData("Phrase", hacker.Phrase_())

	tbl.Render()
	// logging
	t.Log("Fake Hacker\n", tbl.String())
}

func TestImage(t *testing.T) {
	image := faker.Image{}

	tbl := NewTable()

	tbl.SetHeader("Field Name", "Data")
	tbl.AddData("Image", image.Image_(1028, 728))
	//tbl.AddData("avatar", image.Image_())
	tbl.AddData("imageUrl", image.ImageURL("abstract"))
	tbl.AddData("abstract", image.Abstract_())
	tbl.AddData("animals", image.Animals_())
	tbl.AddData("business", image.Business_())
	tbl.AddData("cats", image.Cats_())
	tbl.AddData("city", image.City_())
	tbl.AddData("food", image.Food_())
	tbl.AddData("nightlife", image.Nightlife_())
	tbl.AddData("fashion", image.Fashion_())
	tbl.AddData("people", image.People_())
	tbl.AddData("nature", image.Nature_())
	tbl.AddData("sports", image.Sports_())
	tbl.AddData("technics", image.Technics_())
	tbl.AddData("transport", image.Transport_())

	tbl.Render()
	// logging
	t.Log("Fake Image\n", tbl.String())
}

func TestInternet(t *testing.T) {
	internet := faker.Internet{}

	tbl := NewTable()

	tbl.SetHeader("Field Name", "Data")
	tbl.AddData("Avatar", internet.Avatar_())
	tbl.AddData("Email", internet.Email_())
	tbl.AddData("Username", internet.Username_())
	tbl.AddData("Protocol", internet.Protocol_())
	tbl.AddData("URL", internet.URL_())
	tbl.AddData("DomainName", internet.DomainName_())
	tbl.AddData("DomainSuffix", internet.DomainSuffix_())
	tbl.AddData("DomainWord", internet.DomainWord_())
	tbl.AddData("DomainWord", internet.IP_())
	//tbl.AddData("UserAgent", internet.IP_())
	tbl.AddData("Color", internet.Color_())
	tbl.AddData("Mac", internet.Mac_())
	//tbl.AddData("Password", internet.IP_())
	tbl.Render()
	// logging
	t.Log("Fake Internet\n", tbl.String())
}

func TestLorem(t *testing.T) {
	lorem := faker.Lorem{}

	tbl := NewTable()

	tbl.SetHeader("Field Name", "Data")
	tbl.AddData("Word", lorem.Word_())
	tbl.AddData("Words", strings.Join(lorem.Words_(), ", "))
	tbl.AddData("Sentence", lorem.Sentence_())
	tbl.AddData("Sentences", lorem.Sentences_())
	tbl.AddData("Paragraph", lorem.Paragraph_())
	tbl.AddData("Paragraphs", lorem.Paragraphs_())
	tbl.AddData("Text", lorem.Text())
	tbl.Render()

	// logging
	t.Log("Fake Lorem\n", tbl.String())
}

func TestName(t *testing.T) {
	f := faker.New()

	tbl := NewTable()
	tbl.SetHeader("Field Name", "Data")
	tbl.AddData("FirstName", f.Name.FirstName_())
	tbl.AddData("LastName", f.Name.LastName_())
	tbl.AddData("FindName", f.Name.FindName_())
	tbl.AddData("JobTitle", f.Name.JobTitle_())
	tbl.AddData("Prefix", f.Name.Prefix_())
	tbl.AddData("Suffix", f.Name.Suffix_())
	tbl.AddData("Title", f.Name.Title_())
	tbl.AddData("JobDescriptor", f.Name.JobDescriptor_())
	tbl.AddData("JobArea", f.Name.JobArea_())
	tbl.AddData("JobType", f.Name.JobType_())

	/*JSON, err := f.Name.ToJSON()
	checkError(t, err)
	tbl.AddData("JSON", JSON)

	XML, err := f.Name.ToXML()
	checkError(t, err)
	tbl.AddData("XML", XML)*/

	tbl.Render()

	// logging
	t.Log("Fake Name\n", tbl.String())
}

func TestRandom(t *testing.T) {

	tbl := NewTable()
	tbl.SetHeader("Field Name", "Data")
	tbl.AddData("UUID", faker.UUID())
	tbl.AddData("Boolean", strconv.FormatBool(faker.Boolean()))

	tbl.Render()

	// logging
	t.Log("Fake Random\n", tbl.String())
}

func TestPhone(t *testing.T) {
	phone := faker.Phone{}
	tbl := NewTable()
	tbl.SetHeader("Field Name", "Data")
	tbl.AddData("Phone number", phone.Number_())
	tbl.AddData("Phone format", phone.Format_())

	tbl.Render()

	// logging
	t.Log("Fake Phone\n", tbl.String())
}

func checkError(t *testing.T, err error) {
	t.Error(err)
}
<commit_msg>test all card<commit_after>package faker_test

import (
	"bytes"
	"github.com/olekukonko/tablewriter"
	"github.com/raitucarp/faker"
	"strings"
	"testing"
	//"time"
	"strconv"
)

type Table struct {
	data   [][]string
	header []string
	buf    *bytes.Buffer
	table  *tablewriter.Table
}

func (t *Table) SetHeader(header ...string) {
	t.table.SetHeader(header)
}

func (t *Table) AddData(data ...string) {
	t.table.Append(data)
}

func (t *Table) Render() {
	t.table.Render()
}

func (t *Table) String() string {
	return t.buf.String()
}

func NewTable() Table {
	t := Table{}
	t.buf = new(bytes.Buffer)
	t.table = tablewriter.NewWriter(t.buf)

	return t
}

// Testing begin here
func TestAddress(t *testing.T) {
	f := faker.New()
	tbl := NewTable()
	tbl.SetHeader("Field Name", "Data")
	tbl.AddData("ZipCode", f.Address.ZipCode_())
	tbl.AddData("CityPrefix", f.Address.CityPrefix_())
	tbl.AddData("City", f.Address.City_())
	tbl.AddData("CitySuffix", f.Address.CitySuffix_())
	tbl.AddData("StreetName", f.Address.StreetName_())
	tbl.AddData("StreetAddress", f.Address.StreetAddress_())
	tbl.AddData("StreetSuffix", f.Address.StreetSuffix_())
	tbl.AddData("StreetPrefix", f.Address.StreetPrefix_())
	tbl.AddData("SecondaryAddress", f.Address.SecondaryAddress_())
	tbl.AddData("County", f.Address.County_())
	tbl.AddData("Country", f.Address.Country_())
	tbl.AddData("CountryCode", f.Address.CountryCode_())
	tbl.AddData("State", f.Address.State_())
	tbl.AddData("StateAbbr", f.Address.StateAbbr_())
	tbl.AddData("Latitude", f.Address.Latitude_())
	tbl.AddData("Longitude", f.Address.Longitude_())

	/*JSON, err := f.Address.ToJSON()
	checkError(t, err)
	tbl.AddData("JSON", JSON)

	XML, err := f.Address.ToXML()
	checkError(t, err)
	tbl.AddData("XML", XML)*/

	tbl.Render()

	// logging
	t.Log("Fake Address\n", tbl.String())
}

func TestCommerce(t *testing.T) {
	f := faker.New()
	tbl := NewTable()
	tbl.SetHeader("Field Name", "Data")
	tbl.AddData("Color", f.Commerce.Color_())
	tbl.AddData("Department", f.Commerce.Department_())
	tbl.AddData("ProductName", f.Commerce.ProductName_())
	tbl.AddData("Price", f.Commerce.Price_(20000, 3, "$"))
	tbl.AddData("ProductAdjective", f.Commerce.ProductAdjective_())
	tbl.AddData("ProductMaterial", f.Commerce.ProductMaterial_())
	tbl.AddData("Product", f.Commerce.Product_())

	/*JSON, err := f.Commerce.ToJSON()
	checkError(t, err)
	tbl.AddData("JSON", JSON)

	XML, err := f.Commerce.ToXML()
	checkError(t, err)
	tbl.AddData("XML", XML)*/

	tbl.Render()

	// logging
	t.Log("Fake Commerce\n", tbl.String())
}

func TestCompany(t *testing.T) {
	f := faker.New()
	tbl := NewTable()
	tbl.SetHeader("Field Name", "Data")
	tbl.AddData("Suffixes", strings.Join(f.Company.Suffixes_(), ", "))
	tbl.AddData("CompanyName", f.Company.CompanyName_())
	tbl.AddData("CompanySuffix", f.Company.CompanySuffix_())
	tbl.AddData("CatchPhrase", f.Company.CatchPhrase_())
	tbl.AddData("CatchPhraseAdjective", f.Company.CatchPhraseAdjective_())
	tbl.AddData("CatchPhraseDescriptor", f.Company.CatchPhraseAdjective_())
	tbl.AddData("CatchPhraseNoun", f.Company.CatchPhraseNoun_())
	tbl.AddData("Bs", f.Company.BS_())
	tbl.AddData("BsAdjective", f.Company.BsAdjective_())
	tbl.AddData("BsBuzz", f.Company.BsBuzz_())
	tbl.AddData("BsNoun", f.Company.BsNoun_())
	tbl.Render()

	// logging
	t.Log("Fake Company\n", tbl.String())

}

func TestDate(t *testing.T) {
	d := faker.Date{}
	tbl := NewTable()

	tbl.SetHeader("Field Name", "Data")
	tbl.AddData("Past", d.Past_().String())
	tbl.AddData("Future", d.Future_().String())
	tbl.AddData("Between", d.Between_("02/21/2016", "02/22/2016", "01/2/20006").String())
	tbl.AddData("Recent", d.Recent_(1).String())
	tbl.AddData("Month", d.Month_())
	tbl.AddData("Weekday", d.Weekday_())

	tbl.Render()
	// logging
	t.Log("Fake Date\n", tbl.String())
}

func TestFinance(t *testing.T) {
	f := faker.Finance{}

	tbl := NewTable()

	tbl.SetHeader("Field Name", "Data")
	tbl.AddData("Account", strconv.Itoa(f.Account_()))
	tbl.AddData("Account Name", f.AccountName_())
	tbl.AddData("Amount", f.Amount_())
	tbl.AddData("Mask", f.Mask_())
	tbl.AddData("TransactionType", f.TransactionType_())
	tbl.AddData("CurrencySymbol", f.CurrencySymbol_())
	tbl.AddData("CurrencyCode", f.CurrencyCode_())
	tbl.AddData("CurrencyName", f.CurrencyName_())

	tbl.Render()
	// logging
	t.Log("Fake Finance\n", tbl.String())
}

func TestHacker(t *testing.T) {
	hacker := faker.Hacker{}

	tbl := NewTable()

	tbl.SetHeader("Field Name", "Data")
	tbl.AddData("Abbreviation", hacker.Abbreviation_())
	tbl.AddData("Adjective", hacker.Adjective_())
	tbl.AddData("Noun", hacker.Noun_())
	tbl.AddData("Verb", hacker.Verb_())
	tbl.AddData("IngVerb", hacker.IngVerb_())
	tbl.AddData("Phrase", hacker.Phrase_())

	tbl.Render()
	// logging
	t.Log("Fake Hacker\n", tbl.String())
}

func TestImage(t *testing.T) {
	image := faker.Image{}

	tbl := NewTable()

	tbl.SetHeader("Field Name", "Data")
	tbl.AddData("Image", image.Image_(1028, 728))
	//tbl.AddData("avatar", image.Image_())
	//tbl.AddData("imageUrl", image.imageURL("abstract"))
	tbl.AddData("abstract", image.Abstract_())
	tbl.AddData("animals", image.Animals_())
	tbl.AddData("business", image.Business_())
	tbl.AddData("cats", image.Cats_())
	tbl.AddData("city", image.City_())
	tbl.AddData("food", image.Food_())
	tbl.AddData("nightlife", image.Nightlife_())
	tbl.AddData("fashion", image.Fashion_())
	tbl.AddData("people", image.People_())
	tbl.AddData("nature", image.Nature_())
	tbl.AddData("sports", image.Sports_())
	tbl.AddData("technics", image.Technics_())
	tbl.AddData("transport", image.Transport_())

	tbl.Render()
	// logging
	t.Log("Fake Image\n", tbl.String())
}

func TestInternet(t *testing.T) {
	internet := faker.Internet{}

	tbl := NewTable()

	tbl.SetHeader("Field Name", "Data")
	tbl.AddData("Avatar", internet.Avatar_())
	tbl.AddData("Email", internet.Email_())
	tbl.AddData("Username", internet.Username_())
	tbl.AddData("Protocol", internet.Protocol_())
	tbl.AddData("URL", internet.URL_())
	tbl.AddData("DomainName", internet.DomainName_())
	tbl.AddData("DomainSuffix", internet.DomainSuffix_())
	tbl.AddData("DomainWord", internet.DomainWord_())
	tbl.AddData("DomainWord", internet.IP_())
	//tbl.AddData("UserAgent", internet.IP_())
	tbl.AddData("Color", internet.Color_())
	tbl.AddData("Mac", internet.Mac_())
	//tbl.AddData("Password", internet.IP_())
	tbl.Render()
	// logging
	t.Log("Fake Internet\n", tbl.String())
}

func TestLorem(t *testing.T) {
	lorem := faker.Lorem{}

	tbl := NewTable()

	tbl.SetHeader("Field Name", "Data")
	tbl.AddData("Word", lorem.Word_())
	tbl.AddData("Words", strings.Join(lorem.Words_(), ", "))
	tbl.AddData("Sentence", lorem.Sentence_())
	tbl.AddData("Sentences", lorem.Sentences_())
	tbl.AddData("Paragraph", lorem.Paragraph_())
	tbl.AddData("Paragraphs", lorem.Paragraphs_())
	tbl.AddData("Text", lorem.Text())
	tbl.Render()

	// logging
	t.Log("Fake Lorem\n", tbl.String())
}

func TestName(t *testing.T) {
	f := faker.New()

	tbl := NewTable()
	tbl.SetHeader("Field Name", "Data")
	tbl.AddData("FirstName", f.Name.FirstName_())
	tbl.AddData("LastName", f.Name.LastName_())
	tbl.AddData("FindName", f.Name.FindName_())
	tbl.AddData("JobTitle", f.Name.JobTitle_())
	tbl.AddData("Prefix", f.Name.Prefix_())
	tbl.AddData("Suffix", f.Name.Suffix_())
	tbl.AddData("Title", f.Name.Title_())
	tbl.AddData("JobDescriptor", f.Name.JobDescriptor_())
	tbl.AddData("JobArea", f.Name.JobArea_())
	tbl.AddData("JobType", f.Name.JobType_())

	/*JSON, err := f.Name.ToJSON()
	checkError(t, err)
	tbl.AddData("JSON", JSON)

	XML, err := f.Name.ToXML()
	checkError(t, err)
	tbl.AddData("XML", XML)*/

	tbl.Render()

	// logging
	t.Log("Fake Name\n", tbl.String())
}

func TestRandom(t *testing.T) {

	tbl := NewTable()
	tbl.SetHeader("Field Name", "Data")
	tbl.AddData("UUID", faker.UUID())
	tbl.AddData("Boolean", strconv.FormatBool(faker.Boolean()))

	tbl.Render()

	// logging
	t.Log("Fake Random\n", tbl.String())
}

func TestPhone(t *testing.T) {
	phone := faker.Phone{}
	tbl := NewTable()
	tbl.SetHeader("Field Name", "Data")
	tbl.AddData("Phone number", phone.Number_())
	tbl.AddData("Phone format", phone.Format_())

	tbl.Render()

	// logging
	t.Log("Fake Phone\n", tbl.String())
}

func TestCard(t *testing.T) {

	// logging
	t.Log("Fake Card\n")

	card := faker.CreateCard()
	userCard := faker.CreateUserCard()
	contextualCard := faker.CreateContextualCard()
	transactionCard := faker.CreateTransaction()
	t.Log(card.ToJSON())
	t.Log(userCard.ToJSON())
	t.Log(contextualCard.ToJSON())
	t.Log(transactionCard.ToJSON())

}

func checkError(t *testing.T, err error) {
	t.Error(err)
}
<|endoftext|>


<commit_before>package agent

import (
	"bytes"
	"fmt"
	"io"
	"os"
	"path/filepath"
	"reflect"
	"strings"
	"time"

	"github.com/hashicorp/hcl"
	"github.com/hashicorp/nomad/nomad/structs/config"
)

func ParseConfigFile(path string) (*Config, error) {
	// slurp
	var buf bytes.Buffer
	path, err := filepath.Abs(path)
	if err != nil {
		return nil, err
	}

	f, err := os.Open(path)
	if err != nil {
		return nil, err
	}
	defer f.Close()
	if _, err := io.Copy(&buf, f); err != nil {
		return nil, err
	}

	// parse
	c := &Config{
		Client:    &ClientConfig{ServerJoin: &ServerJoin{}},
		ACL:       &ACLConfig{},
		Server:    &ServerConfig{ServerJoin: &ServerJoin{}},
		Consul:    config.DefaultConsulConfig(),
		Autopilot: config.DefaultAutopilotConfig(),
		Telemetry: &Telemetry{},
		Vault:     config.DefaultVaultConfig(),
	}

	err = hcl.Decode(c, buf.String())
	if err != nil {
		return nil, err
	}

	// convert strings to time.Durations
	err = durations([]td{
		{"gc_interval", &c.Client.GCInterval, &c.Client.GCIntervalHCL},
		{"acl.token_ttl", &c.ACL.TokenTTL, &c.ACL.TokenTTLHCL},
		{"acl.policy_ttl", &c.ACL.PolicyTTL, &c.ACL.PolicyTTLHCL},
		{"client.server_join.retry_interval", &c.Client.ServerJoin.RetryInterval, &c.Client.ServerJoin.RetryIntervalHCL},
		{"server.heartbeat_grace", &c.Server.HeartbeatGrace, &c.Server.HeartbeatGraceHCL},
		{"server.min_heartbeat_ttl", &c.Server.MinHeartbeatTTL, &c.Server.MinHeartbeatTTLHCL},
		{"server.retry_interval", &c.Server.RetryInterval, &c.Server.RetryIntervalHCL},
		{"server.server_join.retry_interval", &c.Server.ServerJoin.RetryInterval, &c.Server.ServerJoin.RetryIntervalHCL},
		{"consul.timeout", &c.Consul.Timeout, &c.Consul.TimeoutHCL},
		{"autopilot.server_stabilization_time", &c.Autopilot.ServerStabilizationTime, &c.Autopilot.ServerStabilizationTimeHCL},
		{"autopilot.last_contact_threshold", &c.Autopilot.LastContactThreshold, &c.Autopilot.LastContactThresholdHCL},
		{"telemetry.collection_interval", &c.Telemetry.collectionInterval, &c.Telemetry.CollectionInterval},
	})
	if err != nil {
		return nil, err
	}

	// report unexpected keys
	err = extraKeys(c)
	if err != nil {
		return nil, err
	}

	return c, nil
}

// td holds args for one duration conversion
type td struct {
	path string
	td   *time.Duration
	str  *string
}

// durations parses the duration strings specified in the config files
// into time.Durations
func durations(xs []td) error {
	for _, x := range xs {
		if x.td != nil && x.str != nil && "" != *x.str {
			d, err := time.ParseDuration(*x.str)
			if err != nil {
				return fmt.Errorf("%s can't parse time duration %s", x.path, *x.str)
			}

			*x.td = d
		}
	}

	return nil
}

// removeEqualFold removes the first string that EqualFold matches
func removeEqualFold(xs *[]string, search string) {
	sl := *xs
	for i, x := range sl {
		if strings.EqualFold(x, search) {
			sl = append(sl[:i], sl[i+1:]...)
			if len(sl) == 0 {
				*xs = nil
			} else {
				*xs = sl
			}
			return
		}
	}
}

func extraKeys(c *Config) error {
	// hcl leaves behind extra keys when parsing JSON. These keys
	// are kept on the top level, taken from slices or the keys of
	// structs contained in slices. Clean up before looking for
	// extra keys.
	for range c.HTTPAPIResponseHeaders {
		removeEqualFold(&c.ExtraKeysHCL, "http_api_response_headers")
	}

	for _, p := range c.Plugins {
		removeEqualFold(&c.ExtraKeysHCL, p.Name)
		removeEqualFold(&c.ExtraKeysHCL, "config")
		removeEqualFold(&c.ExtraKeysHCL, "plugin")
	}

	for _, k := range []string{"options", "meta", "chroot_env", "servers", "server_join"} {
		removeEqualFold(&c.ExtraKeysHCL, k)
		removeEqualFold(&c.ExtraKeysHCL, "client")
	}

	// stats is an unused key, continue to silently ignore it
	removeEqualFold(&c.Client.ExtraKeysHCL, "stats")

	for _, k := range []string{"enabled_schedulers", "start_join", "retry_join", "server_join"} {
		removeEqualFold(&c.ExtraKeysHCL, k)
		removeEqualFold(&c.ExtraKeysHCL, "server")
	}

	for _, k := range []string{"datadog_tags"} {
		removeEqualFold(&c.ExtraKeysHCL, k)
		removeEqualFold(&c.ExtraKeysHCL, "telemetry")
	}

	return extraKeysImpl([]string{}, reflect.ValueOf(*c))
}

// extraKeysImpl returns an error if any extraKeys array is not empty
func extraKeysImpl(path []string, val reflect.Value) error {
	stype := val.Type()
	for i := 0; i < stype.NumField(); i++ {
		ftype := stype.Field(i)
		fval := val.Field(i)

		name := ftype.Name
		prop := ""
		tagSplit(ftype, "hcl", &name, &prop)

		if fval.Kind() == reflect.Ptr {
			fval = reflect.Indirect(fval)
		}

		// struct? recurse. add the struct's key to the path
		if fval.Kind() == reflect.Struct {
			err := extraKeysImpl(append([]string{name}, path...), fval)
			if err != nil {
				return err
			}
		}

		if "unusedKeys" == prop {
			if ks, ok := fval.Interface().([]string); ok && len(ks) != 0 {
				return fmt.Errorf("%s unexpected keys %s",
					strings.Join(path, "."),
					strings.Join(ks, ", "))
			}
		}
	}
	return nil
}

// tagSplit reads the named tag from the structfield and splits its values into strings
func tagSplit(field reflect.StructField, tagName string, vars ...*string) {
	tag := strings.Split(field.Tag.Get(tagName), ",")
	end := len(tag) - 1
	for i, s := range vars {
		if i > end {
			return
		}
		*s = tag[i]
	}
}
<commit_msg>config_parse split out defaults from ParseConfig<commit_after>package agent

import (
	"bytes"
	"fmt"
	"io"
	"os"
	"path/filepath"
	"reflect"
	"strings"
	"time"

	"github.com/hashicorp/hcl"
	"github.com/hashicorp/nomad/nomad/structs/config"
)

// ParseConfigDefault returns the configuration base
func ParseConfigDefault() *Config {
	return &Config{
		Consul:    config.DefaultConsulConfig(),
		Autopilot: config.DefaultAutopilotConfig(),
		Vault:     config.DefaultVaultConfig(),
	}
}

func ParseConfigFile(path string) (*Config, error) {
	// slurp
	var buf bytes.Buffer
	path, err := filepath.Abs(path)
	if err != nil {
		return nil, err
	}

	f, err := os.Open(path)
	if err != nil {
		return nil, err
	}
	defer f.Close()
	if _, err := io.Copy(&buf, f); err != nil {
		return nil, err
	}

	// parse
	c := &Config{
		Client:    &ClientConfig{ServerJoin: &ServerJoin{}},
		ACL:       &ACLConfig{},
		Server:    &ServerConfig{ServerJoin: &ServerJoin{}},
		Consul:    &config.ConsulConfig{},
		Autopilot: &config.AutopilotConfig{},
		Telemetry: &Telemetry{},
		Vault:     &config.VaultConfig{},
	}

	err = hcl.Decode(c, buf.String())
	if err != nil {
		return nil, err
	}

	// convert strings to time.Durations
	err = durations([]td{
		{"gc_interval", &c.Client.GCInterval, &c.Client.GCIntervalHCL},
		{"acl.token_ttl", &c.ACL.TokenTTL, &c.ACL.TokenTTLHCL},
		{"acl.policy_ttl", &c.ACL.PolicyTTL, &c.ACL.PolicyTTLHCL},
		{"client.server_join.retry_interval", &c.Client.ServerJoin.RetryInterval, &c.Client.ServerJoin.RetryIntervalHCL},
		{"server.heartbeat_grace", &c.Server.HeartbeatGrace, &c.Server.HeartbeatGraceHCL},
		{"server.min_heartbeat_ttl", &c.Server.MinHeartbeatTTL, &c.Server.MinHeartbeatTTLHCL},
		{"server.retry_interval", &c.Server.RetryInterval, &c.Server.RetryIntervalHCL},
		{"server.server_join.retry_interval", &c.Server.ServerJoin.RetryInterval, &c.Server.ServerJoin.RetryIntervalHCL},
		{"consul.timeout", &c.Consul.Timeout, &c.Consul.TimeoutHCL},
		{"autopilot.server_stabilization_time", &c.Autopilot.ServerStabilizationTime, &c.Autopilot.ServerStabilizationTimeHCL},
		{"autopilot.last_contact_threshold", &c.Autopilot.LastContactThreshold, &c.Autopilot.LastContactThresholdHCL},
		{"telemetry.collection_interval", &c.Telemetry.collectionInterval, &c.Telemetry.CollectionInterval},
	})
	if err != nil {
		return nil, err
	}

	// report unexpected keys
	err = extraKeys(c)
	if err != nil {
		return nil, err
	}

	return c, nil
}

// td holds args for one duration conversion
type td struct {
	path string
	td   *time.Duration
	str  *string
}

// durations parses the duration strings specified in the config files
// into time.Durations
func durations(xs []td) error {
	for _, x := range xs {
		if x.td != nil && x.str != nil && "" != *x.str {
			d, err := time.ParseDuration(*x.str)
			if err != nil {
				return fmt.Errorf("%s can't parse time duration %s", x.path, *x.str)
			}

			*x.td = d
		}
	}

	return nil
}

// removeEqualFold removes the first string that EqualFold matches
func removeEqualFold(xs *[]string, search string) {
	sl := *xs
	for i, x := range sl {
		if strings.EqualFold(x, search) {
			sl = append(sl[:i], sl[i+1:]...)
			if len(sl) == 0 {
				*xs = nil
			} else {
				*xs = sl
			}
			return
		}
	}
}

func extraKeys(c *Config) error {
	// hcl leaves behind extra keys when parsing JSON. These keys
	// are kept on the top level, taken from slices or the keys of
	// structs contained in slices. Clean up before looking for
	// extra keys.
	for range c.HTTPAPIResponseHeaders {
		removeEqualFold(&c.ExtraKeysHCL, "http_api_response_headers")
	}

	for _, p := range c.Plugins {
		removeEqualFold(&c.ExtraKeysHCL, p.Name)
		removeEqualFold(&c.ExtraKeysHCL, "config")
		removeEqualFold(&c.ExtraKeysHCL, "plugin")
	}

	for _, k := range []string{"options", "meta", "chroot_env", "servers", "server_join"} {
		removeEqualFold(&c.ExtraKeysHCL, k)
		removeEqualFold(&c.ExtraKeysHCL, "client")
	}

	// stats is an unused key, continue to silently ignore it
	removeEqualFold(&c.Client.ExtraKeysHCL, "stats")

	for _, k := range []string{"enabled_schedulers", "start_join", "retry_join", "server_join"} {
		removeEqualFold(&c.ExtraKeysHCL, k)
		removeEqualFold(&c.ExtraKeysHCL, "server")
	}

	for _, k := range []string{"datadog_tags"} {
		removeEqualFold(&c.ExtraKeysHCL, k)
		removeEqualFold(&c.ExtraKeysHCL, "telemetry")
	}

	return extraKeysImpl([]string{}, reflect.ValueOf(*c))
}

// extraKeysImpl returns an error if any extraKeys array is not empty
func extraKeysImpl(path []string, val reflect.Value) error {
	stype := val.Type()
	for i := 0; i < stype.NumField(); i++ {
		ftype := stype.Field(i)
		fval := val.Field(i)

		name := ftype.Name
		prop := ""
		tagSplit(ftype, "hcl", &name, &prop)

		if fval.Kind() == reflect.Ptr {
			fval = reflect.Indirect(fval)
		}

		// struct? recurse. add the struct's key to the path
		if fval.Kind() == reflect.Struct {
			err := extraKeysImpl(append([]string{name}, path...), fval)
			if err != nil {
				return err
			}
		}

		if "unusedKeys" == prop {
			if ks, ok := fval.Interface().([]string); ok && len(ks) != 0 {
				return fmt.Errorf("%s unexpected keys %s",
					strings.Join(path, "."),
					strings.Join(ks, ", "))
			}
		}
	}
	return nil
}

// tagSplit reads the named tag from the structfield and splits its values into strings
func tagSplit(field reflect.StructField, tagName string, vars ...*string) {
	tag := strings.Split(field.Tag.Get(tagName), ",")
	end := len(tag) - 1
	for i, s := range vars {
		if i > end {
			return
		}
		*s = tag[i]
	}
}
<|endoftext|>


<commit_before>package build

import (
	"errors"
	"github.com/tueftler/doget/dockerfile"
	"reflect"
	"testing"
)

func assertEqual(expect, actual interface{}, t *testing.T) {
	if !reflect.DeepEqual(expect, actual) {
		t.Errorf("Items not equal:\nexpected %q\nhave     %q\n", expect, actual)
	}
}

func Test_splitArgsRecognizesTransformArgs(t *testing.T) {
	transformArgs, _ := split([]string{"--doget-no-cache=true", "-t", "foo:bar", "--no-cache=true"})
	assertEqual([]string{"-no-cache", "true"}, transformArgs, t)
}

func Test_splitArgsRecognizesTransformArgWithoutValue(t *testing.T) {
	transformArgs, _ := split([]string{"-t", "foo:bar", "--doget-clean", "--no-cache=true"})
	assertEqual([]string{"-clean"}, transformArgs, t)
}

func Test_splitArgsRecognizesDockerBuildArgs(t *testing.T) {
	_, dockerArgs := split([]string{"-t", "foo:bar", "--no-cache=true", "--doget-no-cache=true", "."})
	assertEqual([]string{"-t", "foo:bar", "--no-cache=true", "."}, dockerArgs, t)
}

func Test_transformArgsAreEmptyWhenOnlyDockerArgsPresent(t *testing.T) {
	transformArgs, _ := split([]string{"--no-cache=true"})
	assertEqual([]string{}, transformArgs, t)
}

func Test_dockerArgsAreEmptyWhenOnlyTransformArgsPresent(t *testing.T) {
	_, dockerArgs := split([]string{"--doget-no-cache=true"})
	assertEqual([]string{}, dockerArgs, t)
}

type mock struct {
	executed bool
	err      error
}

func (m *mock) Build(args []string) error {
	m.executed = true
	if nil != m.err {
		return m.err
	}

	return nil
}

func (m *mock) Init(name string) {
	// intentionally empty
}

func (m *mock) Run(parser *dockerfile.Parser, args []string) error {
	m.executed = true
	return m.err
}

func Test_dockerBuildNotExecutedWhenTransformFails(t *testing.T) {
	transform := &mock{executed: false, err: errors.New("an error")}
	docker := &mock{executed: false}
	clean := &mock{executed: false}
	NewCommand("build", transform, clean, docker).Run(dockerfile.NewParser(), []string{})
	assertEqual(false, docker.executed, t)
	assertEqual(false, clean.executed, t)
}

func Test_returnsTransformError(t *testing.T) {
	err := errors.New("an error")
	transform := &mock{executed: false, err: err}
	docker := &mock{executed: false}
	clean := &mock{executed: false}

	assertEqual(err, NewCommand("build", transform, clean, docker).Run(dockerfile.NewParser(), []string{}), t)
}

func Test_cleanNotExecutedWhenDockerBuildFails(t *testing.T) {
	transform := &mock{executed: false}
	docker := &mock{executed: false, err: errors.New("an error")}
	clean := &mock{executed: false}
	NewCommand("build", transform, clean, docker).Run(dockerfile.NewParser(), []string{})
	assertEqual(false, clean.executed, t)
}

func Test_returnsDockerBuildError(t *testing.T) {
	err := errors.New("an error")
	transform := &mock{executed: false}
	docker := &mock{executed: false, err: err}
	clean := &mock{executed: false}

	assertEqual(err, NewCommand("build", transform, clean, docker).Run(dockerfile.NewParser(), []string{}), t)
}

func Test_returnsCleanError(t *testing.T) {
	err := errors.New("an error")
	transform := &mock{executed: false}
	docker := &mock{executed: false}
	clean := &mock{executed: false, err: err}

	assertEqual(err, NewCommand("build", transform, clean, docker).Run(dockerfile.NewParser(), []string{}), t)
}

func Test_executedAllWhenNoneFails(t *testing.T) {
	transform := &mock{executed: false}
	docker := &mock{executed: false}
	clean := &mock{executed: false}
	NewCommand("build", transform, clean, docker).Run(dockerfile.NewParser(), []string{})
	assertEqual(true, transform.executed, t)
	assertEqual(true, docker.executed, t)
	assertEqual(true, clean.executed, t)
}
<commit_msg>Test showing usage<commit_after>package build

import (
	"errors"
	"github.com/tueftler/doget/dockerfile"
	"reflect"
	"testing"
)

func assertEqual(expect, actual interface{}, t *testing.T) {
	if !reflect.DeepEqual(expect, actual) {
		t.Errorf("Items not equal:\nexpected %q\nhave     %q\n", expect, actual)
	}
}

func Test_splitArgsRecognizesTransformArgs(t *testing.T) {
	transformArgs, _ := split([]string{"--doget-no-cache=true", "-t", "foo:bar", "--no-cache=true"})
	assertEqual([]string{"-no-cache", "true"}, transformArgs, t)
}

func Test_splitArgsRecognizesTransformArgWithoutValue(t *testing.T) {
	transformArgs, _ := split([]string{"-t", "foo:bar", "--doget-clean", "--no-cache=true"})
	assertEqual([]string{"-clean"}, transformArgs, t)
}

func Test_splitArgsRecognizesDockerBuildArgs(t *testing.T) {
	_, dockerArgs := split([]string{"-t", "foo:bar", "--no-cache=true", "--doget-no-cache=true", "."})
	assertEqual([]string{"-t", "foo:bar", "--no-cache=true", "."}, dockerArgs, t)
}

func Test_transformArgsAreEmptyWhenOnlyDockerArgsPresent(t *testing.T) {
	transformArgs, _ := split([]string{"--no-cache=true"})
	assertEqual([]string{}, transformArgs, t)
}

func Test_dockerArgsAreEmptyWhenOnlyTransformArgsPresent(t *testing.T) {
	_, dockerArgs := split([]string{"--doget-no-cache=true"})
	assertEqual([]string{}, dockerArgs, t)
}

type mock struct {
	executed bool
	err      error
}

func (m *mock) Build(args []string) error {
	m.executed = true
	if nil != m.err {
		return m.err
	}

	return nil
}

func (m *mock) Help() ([]byte, error) {
	return []byte{}, nil
}

func (m *mock) Init(name string) {
	// intentionally empty
}

func (m *mock) Run(parser *dockerfile.Parser, args []string) error {
	m.executed = true
	return m.err
}

func Test_dockerBuildNotExecutedWhenTransformFails(t *testing.T) {
	transform := &mock{executed: false, err: errors.New("an error")}
	docker := &mock{executed: false}
	clean := &mock{executed: false}
	NewCommand("build", transform, clean, docker).Run(dockerfile.NewParser(), []string{"."})
	assertEqual(false, docker.executed, t)
	assertEqual(false, clean.executed, t)
}

func Test_returnsTransformError(t *testing.T) {
	err := errors.New("an error")
	transform := &mock{executed: false, err: err}
	docker := &mock{executed: false}
	clean := &mock{executed: false}

	assertEqual(err, NewCommand("build", transform, clean, docker).Run(dockerfile.NewParser(), []string{"."}), t)
}

func Test_cleanNotExecutedWhenDockerBuildFails(t *testing.T) {
	transform := &mock{executed: false}
	docker := &mock{executed: false, err: errors.New("an error")}
	clean := &mock{executed: false}
	NewCommand("build", transform, clean, docker).Run(dockerfile.NewParser(), []string{"."})
	assertEqual(false, clean.executed, t)
}

func Test_returnsDockerBuildError(t *testing.T) {
	err := errors.New("an error")
	transform := &mock{executed: false}
	docker := &mock{executed: false, err: err}
	clean := &mock{executed: false}

	assertEqual(err, NewCommand("build", transform, clean, docker).Run(dockerfile.NewParser(), []string{"."}), t)
}

func Test_returnsCleanError(t *testing.T) {
	err := errors.New("an error")
	transform := &mock{executed: false}
	docker := &mock{executed: false}
	clean := &mock{executed: false, err: err}

	assertEqual(err, NewCommand("build", transform, clean, docker).Run(dockerfile.NewParser(), []string{"."}), t)
}

func Test_executedAllWhenNoneFails(t *testing.T) {
	transform := &mock{executed: false}
	docker := &mock{executed: false}
	clean := &mock{executed: false}
	NewCommand("build", transform, clean, docker).Run(dockerfile.NewParser(), []string{"."})
	assertEqual(true, transform.executed, t)
	assertEqual(true, docker.executed, t)
	assertEqual(true, clean.executed, t)
}

func Test_showsUsage(t *testing.T) {
	transform := &mock{executed: false}
	docker := &mock{executed: false}
	clean := &mock{executed: false}

	NewCommand("build", transform, clean, docker).Run(dockerfile.NewParser(), []string{})
	assertEqual(false, transform.executed, t)
	assertEqual(false, docker.executed, t)
	assertEqual(false, clean.executed, t)
}

func Test_showsUsageWhenGivenHelp(t *testing.T) {
	transform := &mock{executed: false}
	docker := &mock{executed: false}
	clean := &mock{executed: false}

	NewCommand("build", transform, clean, docker).Run(dockerfile.NewParser(), []string{"--help"})
	assertEqual(false, transform.executed, t)
	assertEqual(false, docker.executed, t)
	assertEqual(false, clean.executed, t)
}
<|endoftext|>


<commit_before>// Copyright 2015 The Cockroach Authors.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
//     http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
// implied. See the License for the specific language governing
// permissions and limitations under the License. See the AUTHORS file
// for names of contributors.
//
// Author: Peter Mattis (peter@cockroachlabs.com)

package parser

import (
	"fmt"
	"math"
)

type normalizableExpr interface {
	Expr
	normalize(*normalizeVisitor) Expr
}

func (expr *AndExpr) normalize(v *normalizeVisitor) Expr {
	// Use short-circuit evaluation to simplify AND expressions.
	if v.isConst(expr.Left) {
		expr.Left, v.err = expr.Left.Eval(v.ctx)
		if v.err != nil {
			return expr
		}
		if expr.Left != DNull {
			if d, err := getBool(expr.Left.(Datum)); err != nil {
				return DNull
			} else if !d {
				return expr.Left
			}
			return expr.Right
		}
		return expr
	}
	if v.isConst(expr.Right) {
		expr.Right, v.err = expr.Right.Eval(v.ctx)
		if v.err != nil {
			return expr
		}
		if expr.Right != DNull {
			if d, err := getBool(expr.Right.(Datum)); err != nil {
				return DNull
			} else if d {
				return expr.Left
			}
			return expr.Right
		}
		return expr
	}
	return expr
}

func (expr *ComparisonExpr) normalize(v *normalizeVisitor) Expr {
	switch expr.Operator {
	case EQ, GE, GT, LE, LT:
		// We want var nodes (VariableExpr, QualifiedName, etc) to be immediate
		// children of the comparison expression and not second or third
		// children. That is, we want trees that look like:
		//
		//    cmp            cmp
		//   /   \          /   \
		//  a    op        op    a
		//      /  \      /  \
		//     1    2    1    2
		//
		// Not trees that look like:
		//
		//      cmp          cmp        cmp          cmp
		//     /   \        /   \      /   \        /   \
		//    op    2      op    2    1    op      1    op
		//   /  \         /  \            /  \         /  \
		//  a    1       1    a          a    2       2    a
		//
		// We loop attempting to simplify the comparison expression. As a
		// pre-condition, we know there is at least one variable in the expression
		// tree or we would not have entered this code path.
		for {
			if v.isConst(expr.Left) {
				switch expr.Right.(type) {
				case *BinaryExpr, VariableExpr, *QualifiedName, ValArg:
					break
				default:
					return expr
				}
				// The left side is const and the right side is a binary expression or a
				// variable. Flip the comparison op so that the right side is const and
				// the left side is a binary expression or variable.
				// Create a new ComparisonExpr so the function cache isn't reused.
				*expr = ComparisonExpr{
					Operator: invertComparisonOp(expr.Operator),
					Left:     expr.Right,
					Right:    expr.Left,
				}
			} else if !v.isConst(expr.Right) {
				return expr
			}

			left, ok := expr.Left.(*BinaryExpr)
			if !ok {
				return expr
			}

			// The right is const and the left side is a binary expression. Rotate the
			// comparison combining portions that are const.

			switch {
			case v.isConst(left.Right):
				//        cmp          cmp
				//       /   \        /   \
				//    [+-/]   2  ->  a   [-+*]
				//   /     \            /     \
				//  a       1          2       1

				switch left.Operator {
				case Plus, Minus, Div:
					// Clear the function caches; we're about to change stuff.
					expr.fn.fn = nil
					left.fn.fn = nil
					expr.Left = left.Left
					left.Left = expr.Right
					if left.Operator == Plus {
						left.Operator = Minus
					} else if left.Operator == Minus {
						left.Operator = Plus
					} else {
						left.Operator = Mult
					}
					expr.Right, v.err = left.Eval(v.ctx)
					if v.err != nil {
						return nil
					}
					if !isVar(expr.Left) {
						// Continue as long as the left side of the comparison is not a
						// variable.
						continue
					}
				}

			case v.isConst(left.Left):
				//       cmp              cmp
				//      /   \            /   \
				//    [+-]   2  ->     [+-]   a
				//   /    \           /    \
				//  1      a         1      2

				switch left.Operator {
				case Plus, Minus:
					// Clear the function caches; we're about to change stuff.
					expr.fn.fn = nil
					left.fn.fn = nil
					left.Right, expr.Right = expr.Right, left.Right
					if left.Operator == Plus {
						left.Operator = Minus
						left.Left, left.Right = left.Right, left.Left
					} else {
						expr.Operator = invertComparisonOp(expr.Operator)
					}
					expr.Left, v.err = left.Eval(v.ctx)
					if v.err != nil {
						return nil
					}
					expr.Left, expr.Right = expr.Right, expr.Left
					if !isVar(expr.Left) {
						// Continue as long as the left side of the comparison is not a
						// variable.
						continue
					}
				}
			}

			// We've run out of work to do.
			break
		}
	case In, NotIn:
		// If the right tuple in an In or NotIn comparison expression is constant, it can
		// be normalized.
		tuple, ok := expr.Right.(DTuple)
		if ok {
			tuple.Normalize()
			expr.Right = tuple
		}
	}

	return expr
}

func (expr *OrExpr) normalize(v *normalizeVisitor) Expr {
	// Use short-circuit evaluation to simplify OR expressions.
	if v.isConst(expr.Left) {
		expr.Left, v.err = expr.Left.Eval(v.ctx)
		if v.err != nil {
			return expr
		}
		if expr.Left != DNull {
			if d, err := getBool(expr.Left.(Datum)); err != nil {
				return DNull
			} else if d {
				return expr.Left
			}
			return expr.Right
		}
	}
	if v.isConst(expr.Right) {
		expr.Right, v.err = expr.Right.Eval(v.ctx)
		if v.err != nil {
			return expr
		}
		if expr.Right != DNull {
			if d, err := getBool(expr.Right.(Datum)); err != nil {
				return DNull
			} else if d {
				return expr.Right
			}
			return expr.Left
		}
	}
	return expr
}

func (expr *ParenExpr) normalize(v *normalizeVisitor) Expr {
	return expr.Expr
}

func (expr *RangeCond) normalize(v *normalizeVisitor) Expr {
	if expr.Not {
		// "a NOT BETWEEN b AND c" -> "a < b OR a > c"
		return &OrExpr{
			Left: &ComparisonExpr{
				Operator: LT,
				Left:     expr.Left,
				Right:    expr.From,
			},
			Right: &ComparisonExpr{
				Operator: GT,
				Left:     expr.Left,
				Right:    expr.To,
			},
		}
	}

	// "a BETWEEN b AND c" -> "a >= b AND a <= c"
	return &AndExpr{
		Left: &ComparisonExpr{
			Operator: GE,
			Left:     expr.Left,
			Right:    expr.From,
		},
		Right: &ComparisonExpr{
			Operator: LE,
			Left:     expr.Left,
			Right:    expr.To,
		},
	}
}

func (expr *UnaryExpr) normalize(v *normalizeVisitor) Expr {
	// Ugliness: when we see a UnaryMinus, check to see if the expression
	// being negated is math.MinInt64. This IntVal is only possible if we
	// parsed "9223372036854775808" as a signed int and is the only negative
	// IntVal that can be output from the scanner.
	//
	// TODO(pmattis): Seems like this should happen in Eval, yet if we
	// put it there we blow up during normalization when we try to
	// Eval("9223372036854775808") a few lines down from here before
	// doing Eval("- 9223372036854775808"). Perhaps we can move
	// expression evaluation during normalization to the downward
	// traversal. Or do it during the downward traversal for const
	// UnaryExprs.
	if expr.Operator == UnaryMinus {
		if d, ok := expr.Expr.(*IntVal); ok && d.Val == math.MinInt64 {
			return DInt(math.MinInt64)
		}
	}

	return expr
}

func (expr Row) normalize(v *normalizeVisitor) Expr {
	return Tuple(expr)
}

// NormalizeExpr normalizes an expression, simplifying where possible, but
// guaranteeing that the result of evaluating the expression is
// unchanged. Example normalizations:
//
//   (a)                   -> a
//   ROW(a, b, c)          -> (a, b, c)
//   a = 1 + 1             -> a = 2
//   a + 1 = 2             -> a = 1
//   a BETWEEN b AND c     -> (a >= b) AND (a <= c)
//   a NOT BETWEEN b AND c -> (a < b) OR (a > c)
func (ctx EvalContext) NormalizeExpr(expr Expr) (Expr, error) {
	v := normalizeVisitor{ctx: ctx}
	expr = WalkExpr(&v, expr)
	return expr, v.err
}

type normalizeVisitor struct {
	ctx EvalContext
	err error

	isConstVisitor isConstVisitor
}

var _ Visitor = &normalizeVisitor{}

func (v *normalizeVisitor) Visit(expr Expr, pre bool) (Visitor, Expr) {
	if v.err != nil {
		return nil, expr
	}

	// Normalize expressions that know how to normalize themselves.
	if normalizeable, ok := expr.(normalizableExpr); ok {
		expr = normalizeable.normalize(v)
		if v.err != nil {
			return nil, expr
		}
	}

	if pre {
		switch expr.(type) {
		case *CaseExpr, *IfExpr, *NullIfExpr, *CoalesceExpr:
			// Conditional expressions need to be evaluated during the downward
			// traversal in order to avoid evaluating sub-expressions which should
			// not be evaluated due to the case/conditional.
			if v.isConst(expr) {
				expr, v.err = expr.Eval(v.ctx)
				if v.err != nil {
					return nil, expr
				}
			}
		}
	} else {
		// Evaluate all constant expressions.
		if v.isConst(expr) {
			expr, v.err = expr.Eval(v.ctx)
			if v.err != nil {
				return nil, expr
			}
		}
	}

	return v, expr
}

func (v *normalizeVisitor) isConst(expr Expr) bool {
	return v.isConstVisitor.run(expr)
}

func invertComparisonOp(op ComparisonOp) ComparisonOp {
	switch op {
	case EQ:
		return EQ
	case GE:
		return LE
	case GT:
		return LT
	case LE:
		return GE
	case LT:
		return GT
	default:
		panic(fmt.Sprintf("unable to invert: %s", op))
	}
}

type isConstVisitor struct {
	isConst bool
}

var _ Visitor = &isConstVisitor{}

func (v *isConstVisitor) Visit(expr Expr, pre bool) (Visitor, Expr) {
	if pre && v.isConst {
		if isVar(expr) {
			v.isConst = false
			return nil, expr
		}

		switch t := expr.(type) {
		case *Subquery:
			v.isConst = false
			return nil, expr
		case *FuncExpr:
			// typeCheckFuncExpr populates t.fn.impure.
			if _, err := t.TypeCheck(); err != nil || t.fn.impure {
				v.isConst = false
				return nil, expr
			}
		}
	}
	return v, expr
}

func (v *isConstVisitor) run(expr Expr) bool {
	v.isConst = true
	_ = WalkExpr(v, expr)
	return v.isConst
}

func isVar(expr Expr) bool {
	_, ok := expr.(VariableExpr)
	return ok
}

type containsVarsVisitor struct {
	containsVars bool
}

var _ Visitor = &containsVarsVisitor{}

func (v *containsVarsVisitor) Visit(expr Expr, pre bool) (Visitor, Expr) {
	if pre && !v.containsVars {
		if isVar(expr) {
			v.containsVars = true
			return nil, expr
		}
	}
	return v, expr
}

// ContainsVars returns true if the expression contains any variables.
func ContainsVars(expr Expr) bool {
	v := containsVarsVisitor{containsVars: false}
	_ = WalkExpr(&v, expr)
	return v.containsVars
}
<commit_msg>sql/parser: slight refactor for clarity<commit_after>// Copyright 2015 The Cockroach Authors.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
//     http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
// implied. See the License for the specific language governing
// permissions and limitations under the License. See the AUTHORS file
// for names of contributors.
//
// Author: Peter Mattis (peter@cockroachlabs.com)

package parser

import (
	"fmt"
	"math"
)

type normalizableExpr interface {
	Expr
	normalize(*normalizeVisitor) Expr
}

func (expr *AndExpr) normalize(v *normalizeVisitor) Expr {
	// Use short-circuit evaluation to simplify AND expressions.
	if v.isConst(expr.Left) {
		expr.Left, v.err = expr.Left.Eval(v.ctx)
		if v.err != nil {
			return expr
		}
		if expr.Left != DNull {
			if d, err := getBool(expr.Left.(Datum)); err != nil {
				return DNull
			} else if !d {
				return expr.Left
			}
			return expr.Right
		}
		return expr
	}
	if v.isConst(expr.Right) {
		expr.Right, v.err = expr.Right.Eval(v.ctx)
		if v.err != nil {
			return expr
		}
		if expr.Right != DNull {
			if d, err := getBool(expr.Right.(Datum)); err != nil {
				return DNull
			} else if d {
				return expr.Left
			}
			return expr.Right
		}
		return expr
	}
	return expr
}

func (expr *ComparisonExpr) normalize(v *normalizeVisitor) Expr {
	switch expr.Operator {
	case EQ, GE, GT, LE, LT:
		// We want var nodes (VariableExpr, QualifiedName, etc) to be immediate
		// children of the comparison expression and not second or third
		// children. That is, we want trees that look like:
		//
		//    cmp            cmp
		//   /   \          /   \
		//  a    op        op    a
		//      /  \      /  \
		//     1    2    1    2
		//
		// Not trees that look like:
		//
		//      cmp          cmp        cmp          cmp
		//     /   \        /   \      /   \        /   \
		//    op    2      op    2    1    op      1    op
		//   /  \         /  \            /  \         /  \
		//  a    1       1    a          a    2       2    a
		//
		// We loop attempting to simplify the comparison expression. As a
		// pre-condition, we know there is at least one variable in the expression
		// tree or we would not have entered this code path.
		for {
			if v.isConst(expr.Left) {
				switch expr.Right.(type) {
				case *BinaryExpr, VariableExpr, *QualifiedName, ValArg:
					break
				default:
					return expr
				}
				// The left side is const and the right side is a binary expression or a
				// variable. Flip the comparison op so that the right side is const and
				// the left side is a binary expression or variable.
				// Create a new ComparisonExpr so the function cache isn't reused.
				*expr = ComparisonExpr{
					Operator: invertComparisonOp(expr.Operator),
					Left:     expr.Right,
					Right:    expr.Left,
				}
			} else if !v.isConst(expr.Right) {
				return expr
			}

			left, ok := expr.Left.(*BinaryExpr)
			if !ok {
				return expr
			}

			// The right is const and the left side is a binary expression. Rotate the
			// comparison combining portions that are const.

			switch {
			case v.isConst(left.Right):
				//        cmp          cmp
				//       /   \        /   \
				//    [+-/]   2  ->  a   [-+*]
				//   /     \            /     \
				//  a       1          2       1

				rotating := true
				switch left.Operator {
				case Plus:
					left.Operator = Minus
				case Minus:
					left.Operator = Plus
				case Div:
					left.Operator = Mult
				default:
					rotating = false
				}

				if rotating {
					// Clear the function caches since we're rotating.
					expr.fn.fn = nil
					left.fn.fn = nil

					expr.Left = left.Left
					left.Left = expr.Right
					expr.Right, v.err = left.Eval(v.ctx)
					if v.err != nil {
						return nil
					}
					if !isVar(expr.Left) {
						// Continue as long as the left side of the comparison is not a
						// variable.
						continue
					}
				}

			case v.isConst(left.Left):
				//       cmp              cmp
				//      /   \            /   \
				//    [+-]   2  ->     [+-]   a
				//   /    \           /    \
				//  1      a         1      2

				switch left.Operator {
				case Plus, Minus:
					// Clear the function caches; we're about to change stuff.
					expr.fn.fn = nil
					left.fn.fn = nil
					left.Right, expr.Right = expr.Right, left.Right
					if left.Operator == Plus {
						left.Operator = Minus
						left.Left, left.Right = left.Right, left.Left
					} else {
						expr.Operator = invertComparisonOp(expr.Operator)
					}
					expr.Left, v.err = left.Eval(v.ctx)
					if v.err != nil {
						return nil
					}
					expr.Left, expr.Right = expr.Right, expr.Left
					if !isVar(expr.Left) {
						// Continue as long as the left side of the comparison is not a
						// variable.
						continue
					}
				}
			}

			// We've run out of work to do.
			break
		}
	case In, NotIn:
		// If the right tuple in an In or NotIn comparison expression is constant, it can
		// be normalized.
		tuple, ok := expr.Right.(DTuple)
		if ok {
			tuple.Normalize()
			expr.Right = tuple
		}
	}

	return expr
}

func (expr *OrExpr) normalize(v *normalizeVisitor) Expr {
	// Use short-circuit evaluation to simplify OR expressions.
	if v.isConst(expr.Left) {
		expr.Left, v.err = expr.Left.Eval(v.ctx)
		if v.err != nil {
			return expr
		}
		if expr.Left != DNull {
			if d, err := getBool(expr.Left.(Datum)); err != nil {
				return DNull
			} else if d {
				return expr.Left
			}
			return expr.Right
		}
	}
	if v.isConst(expr.Right) {
		expr.Right, v.err = expr.Right.Eval(v.ctx)
		if v.err != nil {
			return expr
		}
		if expr.Right != DNull {
			if d, err := getBool(expr.Right.(Datum)); err != nil {
				return DNull
			} else if d {
				return expr.Right
			}
			return expr.Left
		}
	}
	return expr
}

func (expr *ParenExpr) normalize(v *normalizeVisitor) Expr {
	return expr.Expr
}

func (expr *RangeCond) normalize(v *normalizeVisitor) Expr {
	if expr.Not {
		// "a NOT BETWEEN b AND c" -> "a < b OR a > c"
		return &OrExpr{
			Left: &ComparisonExpr{
				Operator: LT,
				Left:     expr.Left,
				Right:    expr.From,
			},
			Right: &ComparisonExpr{
				Operator: GT,
				Left:     expr.Left,
				Right:    expr.To,
			},
		}
	}

	// "a BETWEEN b AND c" -> "a >= b AND a <= c"
	return &AndExpr{
		Left: &ComparisonExpr{
			Operator: GE,
			Left:     expr.Left,
			Right:    expr.From,
		},
		Right: &ComparisonExpr{
			Operator: LE,
			Left:     expr.Left,
			Right:    expr.To,
		},
	}
}

func (expr *UnaryExpr) normalize(v *normalizeVisitor) Expr {
	// Ugliness: when we see a UnaryMinus, check to see if the expression
	// being negated is math.MinInt64. This IntVal is only possible if we
	// parsed "9223372036854775808" as a signed int and is the only negative
	// IntVal that can be output from the scanner.
	//
	// TODO(pmattis): Seems like this should happen in Eval, yet if we
	// put it there we blow up during normalization when we try to
	// Eval("9223372036854775808") a few lines down from here before
	// doing Eval("- 9223372036854775808"). Perhaps we can move
	// expression evaluation during normalization to the downward
	// traversal. Or do it during the downward traversal for const
	// UnaryExprs.
	if expr.Operator == UnaryMinus {
		if d, ok := expr.Expr.(*IntVal); ok && d.Val == math.MinInt64 {
			return DInt(math.MinInt64)
		}
	}

	return expr
}

func (expr Row) normalize(v *normalizeVisitor) Expr {
	return Tuple(expr)
}

// NormalizeExpr normalizes an expression, simplifying where possible, but
// guaranteeing that the result of evaluating the expression is
// unchanged. Example normalizations:
//
//   (a)                   -> a
//   ROW(a, b, c)          -> (a, b, c)
//   a = 1 + 1             -> a = 2
//   a + 1 = 2             -> a = 1
//   a BETWEEN b AND c     -> (a >= b) AND (a <= c)
//   a NOT BETWEEN b AND c -> (a < b) OR (a > c)
func (ctx EvalContext) NormalizeExpr(expr Expr) (Expr, error) {
	v := normalizeVisitor{ctx: ctx}
	expr = WalkExpr(&v, expr)
	return expr, v.err
}

type normalizeVisitor struct {
	ctx EvalContext
	err error

	isConstVisitor isConstVisitor
}

var _ Visitor = &normalizeVisitor{}

func (v *normalizeVisitor) Visit(expr Expr, pre bool) (Visitor, Expr) {
	if v.err != nil {
		return nil, expr
	}

	// Normalize expressions that know how to normalize themselves.
	if normalizeable, ok := expr.(normalizableExpr); ok {
		expr = normalizeable.normalize(v)
		if v.err != nil {
			return nil, expr
		}
	}

	if pre {
		switch expr.(type) {
		case *CaseExpr, *IfExpr, *NullIfExpr, *CoalesceExpr:
			// Conditional expressions need to be evaluated during the downward
			// traversal in order to avoid evaluating sub-expressions which should
			// not be evaluated due to the case/conditional.
			if v.isConst(expr) {
				expr, v.err = expr.Eval(v.ctx)
				if v.err != nil {
					return nil, expr
				}
			}
		}
	} else {
		// Evaluate all constant expressions.
		if v.isConst(expr) {
			expr, v.err = expr.Eval(v.ctx)
			if v.err != nil {
				return nil, expr
			}
		}
	}

	return v, expr
}

func (v *normalizeVisitor) isConst(expr Expr) bool {
	return v.isConstVisitor.run(expr)
}

func invertComparisonOp(op ComparisonOp) ComparisonOp {
	switch op {
	case EQ:
		return EQ
	case GE:
		return LE
	case GT:
		return LT
	case LE:
		return GE
	case LT:
		return GT
	default:
		panic(fmt.Sprintf("unable to invert: %s", op))
	}
}

type isConstVisitor struct {
	isConst bool
}

var _ Visitor = &isConstVisitor{}

func (v *isConstVisitor) Visit(expr Expr, pre bool) (Visitor, Expr) {
	if pre && v.isConst {
		if isVar(expr) {
			v.isConst = false
			return nil, expr
		}

		switch t := expr.(type) {
		case *Subquery:
			v.isConst = false
			return nil, expr
		case *FuncExpr:
			// typeCheckFuncExpr populates t.fn.impure.
			if _, err := t.TypeCheck(); err != nil || t.fn.impure {
				v.isConst = false
				return nil, expr
			}
		}
	}
	return v, expr
}

func (v *isConstVisitor) run(expr Expr) bool {
	v.isConst = true
	_ = WalkExpr(v, expr)
	return v.isConst
}

func isVar(expr Expr) bool {
	_, ok := expr.(VariableExpr)
	return ok
}

type containsVarsVisitor struct {
	containsVars bool
}

var _ Visitor = &containsVarsVisitor{}

func (v *containsVarsVisitor) Visit(expr Expr, pre bool) (Visitor, Expr) {
	if pre && !v.containsVars {
		if isVar(expr) {
			v.containsVars = true
			return nil, expr
		}
	}
	return v, expr
}

// ContainsVars returns true if the expression contains any variables.
func ContainsVars(expr Expr) bool {
	v := containsVarsVisitor{containsVars: false}
	_ = WalkExpr(&v, expr)
	return v.containsVars
}
<|endoftext|>


<commit_before>package main

/*
#include <stdlib.h>
#include <jni.h>
#include <android/input.h>
#include <GLES2/gl2.h>
#cgo android LDFLAGS: -lGLESv2
*/
import "C"

import (
	"log"
	"fmt"
	"unsafe"
)

type Video struct {
	prog                C.GLuint
	texture             C.GLuint
	width, height       int
	textureUni          int
	pixelBuffer         chan []uint32
}

var gfx Video

const vertShaderSrcDef = `
	attribute vec4 vPosition;
	attribute vec2 vTexCoord;
	varying vec2 texCoord;

	void main() {
		texCoord = vec2(vTexCoord.x, -vTexCoord.y);
		gl_Position = vec4((vPosition.xy * 2.0) - 1.0, vPosition.zw);
	}
`

const fragShaderSrcDef = `
	precision mediump float;
	varying vec2 texCoord;
	uniform sampler2D texture;

	void main() {
		vec4 c = texture2D(texture, texCoord);
		gl_FragColor = vec4(c.a, c.b, c.g, 1.0);
	}
`

func (game *Video) initGL() {
	log.Printf("GL_VERSION: %v GL_RENDERER: %v GL_VENDOR %v\n",
		GetString(C.GL_VERSION), GetString(C.GL_RENDERER), GetString(C.GL_VENDOR))
	log.Printf("GL_EXTENSIONS: %v\n", GetString(C.GL_EXTENSIONS))

	C.glClearColor(0.0, 0.0, 0.0, 1.0)
	C.glEnable(C.GL_CULL_FACE)
	C.glEnable(C.GL_DEPTH_TEST)

	game.prog = createProgram(vertShaderSrcDef, fragShaderSrcDef)
	posAttrib := attribLocation(game.prog, "vPosition")
	texCoordAttr := attribLocation(game.prog, "vTexCoord")
	game.textureUni = uniformLocation(game.prog, "texture")

	game.texture = GenTexture()
	C.glActiveTexture(C.GL_TEXTURE0)
	C.glBindTexture(C.GL_TEXTURE_2D, game.texture)

	C.glTexParameteri(C.GL_TEXTURE_2D, C.GL_TEXTURE_MIN_FILTER, C.GL_NEAREST)
	C.glTexParameteri(C.GL_TEXTURE_2D, C.GL_TEXTURE_MAG_FILTER, C.GL_NEAREST)

	C.glUseProgram(game.prog)
	C.glEnableVertexAttribArray(C.GLuint(posAttrib))
	C.glEnableVertexAttribArray(C.GLuint(texCoordAttr))

	vertVBO := GenBuffer()
	checkGLError()
	C.glBindBuffer(C.GL_ARRAY_BUFFER, vertVBO)
	verts := []float32{-1.0, 1.0, -1.0, -1.0, 1.0, -1.0, 1.0, -1.0, 1.0, 1.0, -1.0, 1.0}
	C.glBufferData(C.GL_ARRAY_BUFFER, C.GLsizeiptr(len(verts)*int(unsafe.Sizeof(verts[0]))), unsafe.Pointer(&verts[0]), C.GL_STATIC_DRAW)

	textCoorBuf := GenBuffer()
	checkGLError()
	C.glBindBuffer(C.GL_ARRAY_BUFFER, textCoorBuf)
	texVerts := []float32{0.0, 1.0, 0.0, 0.0, 1.0, 0.0, 1.0, 0.0, 1.0, 1.0, 0.0, 1.0}
	C.glBufferData(C.GL_ARRAY_BUFFER, C.GLsizeiptr(len(texVerts)*int(unsafe.Sizeof(texVerts[0]))), unsafe.Pointer(&texVerts[0]), C.GL_STATIC_DRAW)

	C.glVertexAttribPointer(C.GLuint(posAttrib), 2, C.GL_FLOAT, C.GL_FALSE, 0, unsafe.Pointer(uintptr(0)))
	C.glVertexAttribPointer(C.GLuint(texCoordAttr), 2, C.GL_FLOAT, C.GL_FALSE, 0, unsafe.Pointer(uintptr(0)))

}

func (game *Video) resize(width, height int) {
	game.width = width
	game.height = height
	C.glViewport(0, 0, C.GLsizei(width), C.GLsizei(height))
}

func (game *Video) drawFrame() {
	C.glClear(C.GL_COLOR_BUFFER_BIT | C.GL_DEPTH_BUFFER_BIT)

	C.glUseProgram(game.prog)

	C.glActiveTexture(C.GL_TEXTURE0)
	C.glBindTexture(C.GL_TEXTURE_2D, game.texture)

	if gfx.pixelBuffer != nil {
		if bmp := <-gfx.pixelBuffer; bmp != nil {
			C.glTexImage2D(C.GL_TEXTURE_2D, 0, C.GL_RGBA, 240, 224, 0, C.GL_RGBA, C.GL_UNSIGNED_BYTE, unsafe.Pointer(&bmp[0]))
		}
	}

	C.glDrawArrays(C.GL_TRIANGLES, 0, 6)
}

func createProgram(vertShaderSrc string, fragShaderSrc string) C.GLuint {
	vertShader := loadShader(C.GL_VERTEX_SHADER, vertShaderSrc)
	fragShader := loadShader(C.GL_FRAGMENT_SHADER, fragShaderSrc)
	prog := C.glCreateProgram()
	if prog == 0 {
		panic("Failed to create shader program")
	}
	C.glAttachShader(prog, vertShader)
	checkGLError()
	C.glAttachShader(prog, fragShader)
	checkGLError()
	C.glLinkProgram(prog)
	var linkStatus C.GLint
	C.glGetProgramiv(prog, C.GL_LINK_STATUS, &linkStatus)
	if linkStatus != C.GL_TRUE {
		log := GetProgramInfoLog(prog)
		panic(fmt.Errorf("Failed to link program: %v", log))
	}
	return prog
}

func loadShader(shaderType C.GLenum, source string) C.GLuint {
	handle := C.glCreateShader(shaderType)
	if handle == 0 {
		panic(fmt.Errorf("Failed to create shader of type %v", shaderType))
	}
	sourceC := C.CString(source)
	defer C.free(unsafe.Pointer(sourceC))
	C.glShaderSource(handle, 1, (**C.GLchar)(unsafe.Pointer(&sourceC)), (*C.GLint)(unsafe.Pointer(nil)))
	C.glCompileShader(handle)
	var compiled C.GLint
	C.glGetShaderiv(handle, C.GL_COMPILE_STATUS, &compiled)
	if compiled != C.GL_TRUE {
		log := GetShaderInfoLog(handle)
		panic(fmt.Errorf("Failed to compile shader: %v, shader: %v", log, source))
	}
	return handle
}

func attribLocation(prog C.GLuint, name string) int {
	nameC := C.CString(name)
	defer C.free(unsafe.Pointer(nameC))
	attrib := int(C.glGetAttribLocation(C.GLuint(prog), (*C.GLchar)(unsafe.Pointer(nameC))))
	checkGLError()
	if attrib == -1 {
		panic(fmt.Errorf("Failed to find attrib position for %v", name))
	}
	return attrib
}

func uniformLocation(prog C.GLuint, name string) int {
	nameC := C.CString(name)
	defer C.free(unsafe.Pointer(nameC))
	attrib := int(C.glGetUniformLocation(C.GLuint(prog), (*C.GLchar)(unsafe.Pointer(nameC))))
	checkGLError()
	if attrib == -1 {
		panic(fmt.Errorf("Failed to find attrib position for %v", name))
	}
	return attrib
}

func GenBuffer() C.GLuint {
	var buf C.GLuint
	C.glGenBuffers(1, &buf)
	return C.GLuint(buf)
}

func GenTexture() C.GLuint {
	var tex C.GLuint
	C.glGenTextures(1, &tex)
	return C.GLuint(tex)
}

func GetShaderInfoLog(shader C.GLuint) string {
	var logLen C.GLint
	C.glGetShaderiv(shader, C.GL_INFO_LOG_LENGTH, &logLen)
	var c C.GLchar
	logLenBytes := int(logLen) * int(unsafe.Sizeof(c))
	log := C.malloc(C.size_t(logLenBytes))
	if log == nil {
		panic("Failed to allocate shader log buffer")
	}
	defer C.free(log)
	C.glGetShaderInfoLog(C.GLuint(shader), C.GLsizei(logLen), (*C.GLsizei)(unsafe.Pointer(nil)), (*C.GLchar)(log))
	return string(C.GoBytes(log, C.int(logLenBytes)))
}

func GetProgramInfoLog(program C.GLuint) string {
	var logLen C.GLint
	C.glGetProgramiv(program, C.GL_INFO_LOG_LENGTH, &logLen)
	var c C.GLchar
	logLenBytes := int(logLen) * int(unsafe.Sizeof(c))
	log := C.malloc(C.size_t(logLenBytes))
	if log == nil {
		panic("Failed to allocate shader log buffer")
	}
	defer C.free(log)
	C.glGetProgramInfoLog(C.GLuint(program), C.GLsizei(logLen), (*C.GLsizei)(unsafe.Pointer(nil)), (*C.GLchar)(log))
	return string(C.GoBytes(log, C.int(logLenBytes)))
}

func GetString(name C.GLenum) string {
	val := C.glGetString(C.GLenum(name))
	return C.GoString((*C.char)(unsafe.Pointer(val)))
}

func checkGLError() {
	if glErr := C.glGetError(); glErr != C.GL_NO_ERROR {
		panic(fmt.Errorf("C.gl error: %v", glErr))
	}
}

// Use JNI_OnLoad to ensure that the go runtime is initialized at a predictable time,
// namely at System.loadLibrary()
//export JNI_OnLoad
func JNI_OnLoad(vm *C.JavaVM, reserved unsafe.Pointer) C.jint {
	return C.JNI_VERSION_1_6
}

//export Java_com_vokal_afergulator_Engine_drawFrame
func Java_com_vokal_afergulator_Engine_drawFrame(env *C.JNIEnv, clazz C.jclass) {
	defer func() {
		if err := recover(); err != nil {
			log.Fatalf("panic: drawFrame: %v\n", err)
		}
	}()
	gfx.drawFrame()
}

//export Java_com_vokal_afergulator_Engine_init
func Java_com_vokal_afergulator_Engine_init(env *C.JNIEnv, clazz C.jclass) {
	defer func() {
		if err := recover(); err != nil {
			log.Fatalf("panic: init: %v\n", err)
		}
	}()
	gfx.initGL()
}

//export Java_com_vokal_afergulator_Engine_resize
func Java_com_vokal_afergulator_Engine_resize(env *C.JNIEnv, clazz C.jclass, width, height C.jint) {
	defer func() {
		if err := recover(); err != nil {
			log.Fatalf("panic: resize: %v\n", err)
		}
	}()
	gfx.resize(int(width), int(height))
}
<commit_msg>Fix screen to proper dimensions<commit_after>package main

/*
#include <stdlib.h>
#include <jni.h>
#include <android/input.h>
#include <GLES2/gl2.h>
#cgo android LDFLAGS: -lGLESv2
*/
import "C"

import (
	"fmt"
	"log"
	"math"
	"unsafe"
)

type Video struct {
	prog          C.GLuint
	texture       C.GLuint
	width, height int
	textureUni    int
	pixelBuffer   chan []uint32
}

var gfx Video

const vertShaderSrcDef = `
	attribute vec4 vPosition;
	attribute vec2 vTexCoord;
	varying vec2 texCoord;

	void main() {
		texCoord = vec2(vTexCoord.x, -vTexCoord.y);
		gl_Position = vec4((vPosition.xy * 2.0) - 1.0, vPosition.zw);
	}
`

const fragShaderSrcDef = `
	precision mediump float;
	varying vec2 texCoord;
	uniform sampler2D texture;

	void main() {
		vec4 c = texture2D(texture, texCoord);
		gl_FragColor = vec4(c.a, c.b, c.g, 1.0);
	}
`

func (game *Video) initGL() {
	log.Printf("GL_VERSION: %v GL_RENDERER: %v GL_VENDOR %v\n",
		GetString(C.GL_VERSION), GetString(C.GL_RENDERER), GetString(C.GL_VENDOR))
	log.Printf("GL_EXTENSIONS: %v\n", GetString(C.GL_EXTENSIONS))

	C.glClearColor(0.0, 0.0, 0.0, 1.0)
	C.glEnable(C.GL_CULL_FACE)
	C.glEnable(C.GL_DEPTH_TEST)

	game.prog = createProgram(vertShaderSrcDef, fragShaderSrcDef)
	posAttrib := attribLocation(game.prog, "vPosition")
	texCoordAttr := attribLocation(game.prog, "vTexCoord")
	game.textureUni = uniformLocation(game.prog, "texture")

	game.texture = GenTexture()
	C.glActiveTexture(C.GL_TEXTURE0)
	C.glBindTexture(C.GL_TEXTURE_2D, game.texture)

	C.glTexParameteri(C.GL_TEXTURE_2D, C.GL_TEXTURE_MIN_FILTER, C.GL_NEAREST)
	C.glTexParameteri(C.GL_TEXTURE_2D, C.GL_TEXTURE_MAG_FILTER, C.GL_NEAREST)

	C.glUseProgram(game.prog)
	C.glEnableVertexAttribArray(C.GLuint(posAttrib))
	C.glEnableVertexAttribArray(C.GLuint(texCoordAttr))

	vertVBO := GenBuffer()
	checkGLError()
	C.glBindBuffer(C.GL_ARRAY_BUFFER, vertVBO)
	verts := []float32{-1.0, 1.0, -1.0, -1.0, 1.0, -1.0, 1.0, -1.0, 1.0, 1.0, -1.0, 1.0}
	C.glBufferData(C.GL_ARRAY_BUFFER, C.GLsizeiptr(len(verts)*int(unsafe.Sizeof(verts[0]))), unsafe.Pointer(&verts[0]), C.GL_STATIC_DRAW)

	textCoorBuf := GenBuffer()
	checkGLError()
	C.glBindBuffer(C.GL_ARRAY_BUFFER, textCoorBuf)
	texVerts := []float32{0.0, 1.0, 0.0, 0.0, 1.0, 0.0, 1.0, 0.0, 1.0, 1.0, 0.0, 1.0}
	C.glBufferData(C.GL_ARRAY_BUFFER, C.GLsizeiptr(len(texVerts)*int(unsafe.Sizeof(texVerts[0]))), unsafe.Pointer(&texVerts[0]), C.GL_STATIC_DRAW)

	C.glVertexAttribPointer(C.GLuint(posAttrib), 2, C.GL_FLOAT, C.GL_FALSE, 0, unsafe.Pointer(uintptr(0)))
	C.glVertexAttribPointer(C.GLuint(texCoordAttr), 2, C.GL_FLOAT, C.GL_FALSE, 0, unsafe.Pointer(uintptr(0)))

}

func (game *Video) resize(width, height int) {
	x_offset := 0
	y_offset := 0

	r := ((float64)(height)) / ((float64)(width))

	if r > 0.9375 { // Height taller than ratio
		h := (int)(math.Floor((float64)(0.9375 * (float64)(width))))
		y_offset = (height - h) / 2
		height = h
	} else if r < 0.9375 { // Width wider
		w := (int)(math.Floor((float64)((256.0 / 240.0) * (float64)(height))))
		x_offset = (width - w) / 2
		width = w
	}

	game.width = width
	game.height = height

	C.glViewport(C.GLint(x_offset), C.GLint(y_offset), C.GLsizei(width), C.GLsizei(height))
}

func (game *Video) drawFrame() {
	C.glClear(C.GL_COLOR_BUFFER_BIT | C.GL_DEPTH_BUFFER_BIT)

	C.glUseProgram(game.prog)

	C.glActiveTexture(C.GL_TEXTURE0)
	C.glBindTexture(C.GL_TEXTURE_2D, game.texture)

	if gfx.pixelBuffer != nil {
		if bmp := <-gfx.pixelBuffer; bmp != nil {
			C.glTexImage2D(C.GL_TEXTURE_2D, 0, C.GL_RGBA, 240, 224, 0, C.GL_RGBA, C.GL_UNSIGNED_BYTE, unsafe.Pointer(&bmp[0]))
		}
	}

	C.glDrawArrays(C.GL_TRIANGLES, 0, 6)
}

func createProgram(vertShaderSrc string, fragShaderSrc string) C.GLuint {
	vertShader := loadShader(C.GL_VERTEX_SHADER, vertShaderSrc)
	fragShader := loadShader(C.GL_FRAGMENT_SHADER, fragShaderSrc)
	prog := C.glCreateProgram()
	if prog == 0 {
		panic("Failed to create shader program")
	}
	C.glAttachShader(prog, vertShader)
	checkGLError()
	C.glAttachShader(prog, fragShader)
	checkGLError()
	C.glLinkProgram(prog)
	var linkStatus C.GLint
	C.glGetProgramiv(prog, C.GL_LINK_STATUS, &linkStatus)
	if linkStatus != C.GL_TRUE {
		log := GetProgramInfoLog(prog)
		panic(fmt.Errorf("Failed to link program: %v", log))
	}
	return prog
}

func loadShader(shaderType C.GLenum, source string) C.GLuint {
	handle := C.glCreateShader(shaderType)
	if handle == 0 {
		panic(fmt.Errorf("Failed to create shader of type %v", shaderType))
	}
	sourceC := C.CString(source)
	defer C.free(unsafe.Pointer(sourceC))
	C.glShaderSource(handle, 1, (**C.GLchar)(unsafe.Pointer(&sourceC)), (*C.GLint)(unsafe.Pointer(nil)))
	C.glCompileShader(handle)
	var compiled C.GLint
	C.glGetShaderiv(handle, C.GL_COMPILE_STATUS, &compiled)
	if compiled != C.GL_TRUE {
		log := GetShaderInfoLog(handle)
		panic(fmt.Errorf("Failed to compile shader: %v, shader: %v", log, source))
	}
	return handle
}

func attribLocation(prog C.GLuint, name string) int {
	nameC := C.CString(name)
	defer C.free(unsafe.Pointer(nameC))
	attrib := int(C.glGetAttribLocation(C.GLuint(prog), (*C.GLchar)(unsafe.Pointer(nameC))))
	checkGLError()
	if attrib == -1 {
		panic(fmt.Errorf("Failed to find attrib position for %v", name))
	}
	return attrib
}

func uniformLocation(prog C.GLuint, name string) int {
	nameC := C.CString(name)
	defer C.free(unsafe.Pointer(nameC))
	attrib := int(C.glGetUniformLocation(C.GLuint(prog), (*C.GLchar)(unsafe.Pointer(nameC))))
	checkGLError()
	if attrib == -1 {
		panic(fmt.Errorf("Failed to find attrib position for %v", name))
	}
	return attrib
}

func GenBuffer() C.GLuint {
	var buf C.GLuint
	C.glGenBuffers(1, &buf)
	return C.GLuint(buf)
}

func GenTexture() C.GLuint {
	var tex C.GLuint
	C.glGenTextures(1, &tex)
	return C.GLuint(tex)
}

func GetShaderInfoLog(shader C.GLuint) string {
	var logLen C.GLint
	C.glGetShaderiv(shader, C.GL_INFO_LOG_LENGTH, &logLen)
	var c C.GLchar
	logLenBytes := int(logLen) * int(unsafe.Sizeof(c))
	log := C.malloc(C.size_t(logLenBytes))
	if log == nil {
		panic("Failed to allocate shader log buffer")
	}
	defer C.free(log)
	C.glGetShaderInfoLog(C.GLuint(shader), C.GLsizei(logLen), (*C.GLsizei)(unsafe.Pointer(nil)), (*C.GLchar)(log))
	return string(C.GoBytes(log, C.int(logLenBytes)))
}

func GetProgramInfoLog(program C.GLuint) string {
	var logLen C.GLint
	C.glGetProgramiv(program, C.GL_INFO_LOG_LENGTH, &logLen)
	var c C.GLchar
	logLenBytes := int(logLen) * int(unsafe.Sizeof(c))
	log := C.malloc(C.size_t(logLenBytes))
	if log == nil {
		panic("Failed to allocate shader log buffer")
	}
	defer C.free(log)
	C.glGetProgramInfoLog(C.GLuint(program), C.GLsizei(logLen), (*C.GLsizei)(unsafe.Pointer(nil)), (*C.GLchar)(log))
	return string(C.GoBytes(log, C.int(logLenBytes)))
}

func GetString(name C.GLenum) string {
	val := C.glGetString(C.GLenum(name))
	return C.GoString((*C.char)(unsafe.Pointer(val)))
}

func checkGLError() {
	if glErr := C.glGetError(); glErr != C.GL_NO_ERROR {
		panic(fmt.Errorf("C.gl error: %v", glErr))
	}
}

// Use JNI_OnLoad to ensure that the go runtime is initialized at a predictable time,
// namely at System.loadLibrary()
//export JNI_OnLoad
func JNI_OnLoad(vm *C.JavaVM, reserved unsafe.Pointer) C.jint {
	return C.JNI_VERSION_1_6
}

//export Java_com_vokal_afergulator_Engine_drawFrame
func Java_com_vokal_afergulator_Engine_drawFrame(env *C.JNIEnv, clazz C.jclass) {
	defer func() {
		if err := recover(); err != nil {
			log.Fatalf("panic: drawFrame: %v\n", err)
		}
	}()
	gfx.drawFrame()
}

//export Java_com_vokal_afergulator_Engine_init
func Java_com_vokal_afergulator_Engine_init(env *C.JNIEnv, clazz C.jclass) {
	defer func() {
		if err := recover(); err != nil {
			log.Fatalf("panic: init: %v\n", err)
		}
	}()
	gfx.initGL()
}

//export Java_com_vokal_afergulator_Engine_resize
func Java_com_vokal_afergulator_Engine_resize(env *C.JNIEnv, clazz C.jclass, width, height C.jint) {
	defer func() {
		if err := recover(); err != nil {
			log.Fatalf("panic: resize: %v\n", err)
		}
	}()
	gfx.resize(int(width), int(height))
}
<|endoftext|>