<commit_before>package whatever

import (
	"encoding/json"
	"fmt"
	"net/url"
	"strconv"
	"strings"
	"time"
)

// Params is a type on top of map[string]interface{}
// that has a few useful getters and other methods.
// This type can help you to work with JSON data that
// can be unmarshaled into it and then extracted with
// the appropriate type (it even has a getter for time.Time).
// Also the Required and Empty methods may be helpful with
// validating data. It is possible to work with nested Params,
// getting an Interface that you can cast yourself to the
// wanted type and adding new values to the Params structure.
type Params map[string]interface{}

// NewFromJSON receives a slice of bytes that should be json
// body and returns Params structure for that json body and an
// error if there was one while decoding the json.
func NewFromJSON(jsonBody []byte) (Params, error) {
	var p Params
	err := json.Unmarshal(jsonBody, &p)
	return p, err
}

// Add adds a new pair(key, value) to the Params structure.
// Returns true if an existing value was overwritten.
func (p Params) Add(key string, value interface{}) bool {
	if fmt.Sprintf("%p", p) == fmt.Sprintf("%p", value) {
		return false
	}

	_, ok := p[key]
	p[key] = value
	return ok
}

// Empty checks if the Params structure is empty.
// Returns false if a there are elements in the structure.
func (p Params) Empty() bool {
	for _ = range p {
		return false
	}
	return true
}

// Remove deletes a key from the structure.
// It won't fail even if called on a missing key.
func (p Params) Remove(key string) {
	delete(p, key)
}

// GetP will return Params structure if the value with
// the specified key is of either map[string]interface{} or Params type.
// Returns empty Params if the key is missing or if the value
// was not one of the desired types.
func (p Params) GetP(key string) Params {
	if val, ok := p[key]; ok {
		if vmap, ok := val.(map[string]interface{}); ok {
			return Params(vmap)
		} else if vp, ok := val.(Params); ok {
			return vp
		}
	}

	return Params{}
}

// GetI will return the same as map[key] would have returned.
// Returns an interface that you can try to cast to other types.
// If there is no value with that key it will return nil.
func (p Params) GetI(key string) interface{} {
	if val, ok := p[key]; ok {
		return val
	}

	return nil
}

// Get returns a string representation of the value with
// the specified key.
// Returns empty string if there is no value with the provided key.
func (p Params) Get(key string) string {
	if val, ok := p[key]; ok {
		return stringify(val)
	}

	return ""
}

// GetString returns a string only if the value with the specified key
// can be casted to string. Will return an empty string otherwise.
func (p Params) GetString(key string) string {
	if val, ok := p[key]; ok {
		if vs, ok := val.(string); ok {
			return vs
		}
	}
	return ""
}

// GetInt parses the value with the provided key to an int.
// If there is an error with the parsing, returns 0.
func (p Params) GetInt(key string) int {
	result, err := strconv.ParseInt(p.Get(key), 0, 0)
	if err == nil {
		return int(result)
	}

	return 0
}

// GetInt8 parses the value with the provided key to an int8.
// If there is an error with the parsing, returns 0.
func (p Params) GetInt8(key string) int8 {
	result, err := strconv.ParseInt(p.Get(key), 0, 8)
	if err == nil {
		return int8(result)
	}

	return 0
}

// GetInt64 parses the value with the provided key to an int64.
// If there is an error with the parsing, returns 0.
func (p Params) GetInt64(key string) int64 {
	result, err := strconv.ParseInt(p.Get(key), 0, 64)
	if err == nil {
		return result
	}

	return 0
}

// GetFloat32 parses the value with the provided key to an float32.
// If there is an error with the parsing, returns 0.
func (p Params) GetFloat32(key string) float32 {
	result, err := strconv.ParseFloat(p.Get(key), 32)
	if err == nil {
		return float32(result)
	}

	return 0
}

// GetFloat64 parses the value with the provided key to an float64.
// If there is an error with the parsing, returns 0.
func (p Params) GetFloat64(key string) float64 {
	result, err := strconv.ParseFloat(p.Get(key), 64)
	if err == nil {
		return result
	}

	return 0
}

// GetFloat parses the value with the provided key to an float32.
// If there is an error with the parsing, returns 0.
// This is actually the same as calling GetFloat32.
func (p Params) GetFloat(key string) float32 {
	return p.GetFloat32(key)
}

// GetTime will return the value with the specified key
// parsed as time.Time structure. The layout is time.RFC3339
// or in other words the JSON format for the Date object in JavaScript.
// The value should look like this:
//     "2015-02-27T21:53:57.582Z"
// Otherwise returns time.Time{}
func (p Params) GetTime(key string) time.Time {
	if result, err := time.Parse(time.RFC3339, p.Get(key)); err == nil {
		return result
	}
	return time.Time{}
}

// GetSlice returns a slice of interface{} if the value
// with the provided key can be casted to a slice.
// Otherwise returns nil.
func (p Params) GetSlice(key string) []interface{} {
	if val, ok := p[key]; ok {
		if val, ok := val.([]interface{}); ok {
			return val
		}
	}

	return nil
}

// GetSliceStrings will return a slice of strings.
// This slice of strings will be a result of casting the value
// corresponding to the provided key to a slice of interface{}
// and then individually casting each element to a string.
// Only those elements that can be casted to a string will be
// present in the final result. Those that cannot be casted to string
// will be silently ignored. If the is not value with that
// key or the value is not a slice, nil will be returned.
func (p Params) GetSliceStrings(key string) []string {
	var result []string
	if val, ok := p[key]; ok {
		if val, ok := val.([]interface{}); ok {
			for _, v := range val {
				if vs, ok := v.(string); ok {
					result = append(result, vs)
				}
			}
		}
		return result
	}

	return nil
}

// GetSliceInts will return a slice of strings.
// This slice of strings will be a result of casting the value
// corresponding to the provided key to a slice of interface{}
// and then individually casting each element to an int.
// Only those elements that can be casted to an int will be
// present in the final result. Those who cannot be casted to int
// will be silently ignored. If the is not value with that
// key or the value is not a slice, nil will be returned.
func (p Params) GetSliceInts(key string) []int {
	var result []int
	if val, ok := p[key]; ok {
		if slice, ok := val.([]interface{}); ok {
			for _, v := range slice {
				if intVal, err := strconv.ParseInt(stringify(v), 0, 0); err == nil {
					result = append(result, int(intVal))
				}
			}
		}
		return result
	}

	return nil
}

// URLValues return the values in the Params structure
// as url.Values that can be then used with packages as
// gorilla`s schema or goji`s params. The schema and params
// packages expects different keys for the nested values. For
// this to be able to generate the expected keys you may need to
// pass the prefix and the suffix of the nested key. If the prefix
// is empty string, then the prefix will be set to dot (".") and
// the suffix will be an empty string. This is the way that gorilla`s
// schema expect the nested keys to be represented. Example:
//     some_key.inner_key.last_key
// This somewhat resembles the mongo notation of nested objects as well.
// To be able to use this with params, you will need to pass an prefix "["
// and suffix "]". The key from the previous example will now be:
//     some_key[inner_key][last_key]
//
// If the Params structure is blank, then an empty url.Values will be returned.
func (p Params) URLValues(prefix, suffix string) url.Values {
	return toURLValues(p, prefix, suffix, false)
}

// Required will return an error if one of the passed keys is missing
// in the Params structure. As far as Required cares - an empty string
// is the same as missing value.
// To validate nested parameters please use the dotted notation:
//     some_key.nested_key.last_key
// Returns an error message of the following type:
//     "the parameter {key} is required"
// If all keys are present will return nil.
func (p Params) Required(keys ...string) error {
	for _, key := range keys {
		if ok := exists(p, key); !ok {
			return fmt.Errorf("the parameter %s is required", key)
		}
	}

	return nil
}

func (p Params) Keys() []string {
	return keys(p)
}

func (p Params) NestedKeys() []string {
	return nestedKeys(p, "", false)
}

func (p Params) Merge(set map[string]interface{}) {
	for k, v := range set {
		p[k] = v
	}
}

func (p Params) Defaults(set map[string]interface{}) {
	for k, v := range set {
		if _, ok := p[k]; !ok {
			p[k] = v
		}
	}
}

func exists(input map[string]interface{}, key string) (ok bool) {
	if input == nil {
		return false
	}

	if index := strings.Index(key, "."); index != -1 {
		var casted bool
		var params map[string]interface{}
		pair := strings.SplitN(key, ".", 2)

		if params, casted = input[pair[0]].(Params); !casted {
			params, casted = input[pair[0]].(map[string]interface{})
		}

		if !casted {
			return false
		}

		ok = exists(params, pair[1])
	} else {
		var v interface{}
		v, ok = input[key]
		if s, isS := v.(string); isS && ok {
			ok = (s != "")
		}
	}

	return ok
}

func stringify(v interface{}) string {
	if vs, ok := v.(string); ok {
		return vs
	}

	return fmt.Sprintf("%v", v)
}

func keys(set Params) (result []string) {
	for k, _ := range set {
		result = append(result, k)
	}
	return
}

func nestedKeys(set Params, parent string, subParse bool) []string {
	var key string
	var result []string
	for k, v := range set {

		if subParse {
			key = fmt.Sprintf("%s.%s", parent, k)
		} else {
			key = k
		}

		result = append(result, key)

		if val, ok := v.(map[string]interface{}); ok {
			subKeys := nestedKeys(Params(val), key, true)
			result = append(result, subKeys...)
		} else if val, ok := v.(Params); ok {
			subKeys := nestedKeys(val, key, true)
			result = append(result, subKeys...)
		}

	}
	return result
}

func toURLValues(set Params, prefix, suffix string, subParse bool) url.Values {
	if prefix == "" {
		prefix = "."
		suffix = ""
	}

	result := url.Values{}
	var subset url.Values
	for key, value := range set {
		var foundSubset bool
		if v, ok := value.(Params); ok {
			subset = toURLValues(v, prefix, suffix, true)
			foundSubset = true
		} else if v, ok := value.(map[string]interface{}); ok {
			subset = toURLValues(Params(v), prefix, suffix, true)
			foundSubset = true
		} else if v, ok := value.([]interface{}); ok {
			for _, el := range v {
				result[key] = append(result[key], stringify(el))
			}
			continue
		}
		if foundSubset {
			for k, v := range subset {
				nestedKey := fmt.Sprintf("%s%s", key, k)
				if subParse {
					nestedKey = fmt.Sprintf("%s%s%s%s", prefix, key, suffix, k)
				}
				result[nestedKey] = append(result[nestedKey], v...)
			}
		} else {
			valueKey := key
			if subParse {
				valueKey = fmt.Sprintf("%s%s%s", prefix, key, suffix)
			}
			result[valueKey] = append(result[valueKey], stringify(value))
		}
	}
	return result
}
<commit_msg>Refactor nestedKeys a little.<commit_after>package whatever

import (
	"encoding/json"
	"fmt"
	"net/url"
	"strconv"
	"strings"
	"time"
)

// Params is a type on top of map[string]interface{}
// that has a few useful getters and other methods.
// This type can help you to work with JSON data that
// can be unmarshaled into it and then extracted with
// the appropriate type (it even has a getter for time.Time).
// Also the Required and Empty methods may be helpful with
// validating data. It is possible to work with nested Params,
// getting an Interface that you can cast yourself to the
// wanted type and adding new values to the Params structure.
type Params map[string]interface{}

// NewFromJSON receives a slice of bytes that should be json
// body and returns Params structure for that json body and an
// error if there was one while decoding the json.
func NewFromJSON(jsonBody []byte) (Params, error) {
	var p Params
	err := json.Unmarshal(jsonBody, &p)
	return p, err
}

// Add adds a new pair(key, value) to the Params structure.
// Returns true if an existing value was overwritten.
func (p Params) Add(key string, value interface{}) bool {
	if fmt.Sprintf("%p", p) == fmt.Sprintf("%p", value) {
		return false
	}

	_, ok := p[key]
	p[key] = value
	return ok
}

// Empty checks if the Params structure is empty.
// Returns false if a there are elements in the structure.
func (p Params) Empty() bool {
	for _ = range p {
		return false
	}
	return true
}

// Remove deletes a key from the structure.
// It won't fail even if called on a missing key.
func (p Params) Remove(key string) {
	delete(p, key)
}

// GetP will return Params structure if the value with
// the specified key is of either map[string]interface{} or Params type.
// Returns empty Params if the key is missing or if the value
// was not one of the desired types.
func (p Params) GetP(key string) Params {
	if val, ok := p[key]; ok {
		if vmap, ok := val.(map[string]interface{}); ok {
			return Params(vmap)
		} else if vp, ok := val.(Params); ok {
			return vp
		}
	}

	return Params{}
}

// GetI will return the same as map[key] would have returned.
// Returns an interface that you can try to cast to other types.
// If there is no value with that key it will return nil.
func (p Params) GetI(key string) interface{} {
	if val, ok := p[key]; ok {
		return val
	}

	return nil
}

// Get returns a string representation of the value with
// the specified key.
// Returns empty string if there is no value with the provided key.
func (p Params) Get(key string) string {
	if val, ok := p[key]; ok {
		return stringify(val)
	}

	return ""
}

// GetString returns a string only if the value with the specified key
// can be casted to string. Will return an empty string otherwise.
func (p Params) GetString(key string) string {
	if val, ok := p[key]; ok {
		if vs, ok := val.(string); ok {
			return vs
		}
	}
	return ""
}

// GetInt parses the value with the provided key to an int.
// If there is an error with the parsing, returns 0.
func (p Params) GetInt(key string) int {
	result, err := strconv.ParseInt(p.Get(key), 0, 0)
	if err == nil {
		return int(result)
	}

	return 0
}

// GetInt8 parses the value with the provided key to an int8.
// If there is an error with the parsing, returns 0.
func (p Params) GetInt8(key string) int8 {
	result, err := strconv.ParseInt(p.Get(key), 0, 8)
	if err == nil {
		return int8(result)
	}

	return 0
}

// GetInt64 parses the value with the provided key to an int64.
// If there is an error with the parsing, returns 0.
func (p Params) GetInt64(key string) int64 {
	result, err := strconv.ParseInt(p.Get(key), 0, 64)
	if err == nil {
		return result
	}

	return 0
}

// GetFloat32 parses the value with the provided key to an float32.
// If there is an error with the parsing, returns 0.
func (p Params) GetFloat32(key string) float32 {
	result, err := strconv.ParseFloat(p.Get(key), 32)
	if err == nil {
		return float32(result)
	}

	return 0
}

// GetFloat64 parses the value with the provided key to an float64.
// If there is an error with the parsing, returns 0.
func (p Params) GetFloat64(key string) float64 {
	result, err := strconv.ParseFloat(p.Get(key), 64)
	if err == nil {
		return result
	}

	return 0
}

// GetFloat parses the value with the provided key to an float32.
// If there is an error with the parsing, returns 0.
// This is actually the same as calling GetFloat32.
func (p Params) GetFloat(key string) float32 {
	return p.GetFloat32(key)
}

// GetTime will return the value with the specified key
// parsed as time.Time structure. The layout is time.RFC3339
// or in other words the JSON format for the Date object in JavaScript.
// The value should look like this:
//     "2015-02-27T21:53:57.582Z"
// Otherwise returns time.Time{}
func (p Params) GetTime(key string) time.Time {
	if result, err := time.Parse(time.RFC3339, p.Get(key)); err == nil {
		return result
	}
	return time.Time{}
}

// GetSlice returns a slice of interface{} if the value
// with the provided key can be casted to a slice.
// Otherwise returns nil.
func (p Params) GetSlice(key string) []interface{} {
	if val, ok := p[key]; ok {
		if val, ok := val.([]interface{}); ok {
			return val
		}
	}

	return nil
}

// GetSliceStrings will return a slice of strings.
// This slice of strings will be a result of casting the value
// corresponding to the provided key to a slice of interface{}
// and then individually casting each element to a string.
// Only those elements that can be casted to a string will be
// present in the final result. Those that cannot be casted to string
// will be silently ignored. If the is not value with that
// key or the value is not a slice, nil will be returned.
func (p Params) GetSliceStrings(key string) []string {
	var result []string
	if val, ok := p[key]; ok {
		if val, ok := val.([]interface{}); ok {
			for _, v := range val {
				if vs, ok := v.(string); ok {
					result = append(result, vs)
				}
			}
		}
		return result
	}

	return nil
}

// GetSliceInts will return a slice of strings.
// This slice of strings will be a result of casting the value
// corresponding to the provided key to a slice of interface{}
// and then individually casting each element to an int.
// Only those elements that can be casted to an int will be
// present in the final result. Those who cannot be casted to int
// will be silently ignored. If the is not value with that
// key or the value is not a slice, nil will be returned.
func (p Params) GetSliceInts(key string) []int {
	var result []int
	if val, ok := p[key]; ok {
		if slice, ok := val.([]interface{}); ok {
			for _, v := range slice {
				if intVal, err := strconv.ParseInt(stringify(v), 0, 0); err == nil {
					result = append(result, int(intVal))
				}
			}
		}
		return result
	}

	return nil
}

// URLValues return the values in the Params structure
// as url.Values that can be then used with packages as
// gorilla`s schema or goji`s params. The schema and params
// packages expects different keys for the nested values. For
// this to be able to generate the expected keys you may need to
// pass the prefix and the suffix of the nested key. If the prefix
// is empty string, then the prefix will be set to dot (".") and
// the suffix will be an empty string. This is the way that gorilla`s
// schema expect the nested keys to be represented. Example:
//     some_key.inner_key.last_key
// This somewhat resembles the mongo notation of nested objects as well.
// To be able to use this with params, you will need to pass an prefix "["
// and suffix "]". The key from the previous example will now be:
//     some_key[inner_key][last_key]
//
// If the Params structure is blank, then an empty url.Values will be returned.
func (p Params) URLValues(prefix, suffix string) url.Values {
	return toURLValues(p, prefix, suffix, false)
}

// Required will return an error if one of the passed keys is missing
// in the Params structure. As far as Required cares - an empty string
// is the same as missing value.
// To validate nested parameters please use the dotted notation:
//     some_key.nested_key.last_key
// Returns an error message of the following type:
//     "the parameter {key} is required"
// If all keys are present will return nil.
func (p Params) Required(keys ...string) error {
	for _, key := range keys {
		if ok := exists(p, key); !ok {
			return fmt.Errorf("the parameter %s is required", key)
		}
	}

	return nil
}

func (p Params) Keys() []string {
	return keys(p)
}

func (p Params) NestedKeys() []string {
	return nestedKeys(p, "", false)
}

func (p Params) Merge(set map[string]interface{}) {
	for k, v := range set {
		p[k] = v
	}
}

func (p Params) Defaults(set map[string]interface{}) {
	for k, v := range set {
		if _, ok := p[k]; !ok {
			p[k] = v
		}
	}
}

func exists(input map[string]interface{}, key string) (ok bool) {
	if input == nil {
		return false
	}

	if index := strings.Index(key, "."); index != -1 {
		var casted bool
		var params map[string]interface{}
		pair := strings.SplitN(key, ".", 2)

		if params, casted = input[pair[0]].(Params); !casted {
			params, casted = input[pair[0]].(map[string]interface{})
		}

		if !casted {
			return false
		}

		ok = exists(params, pair[1])
	} else {
		var v interface{}
		v, ok = input[key]
		if s, isS := v.(string); isS && ok {
			ok = (s != "")
		}
	}

	return ok
}

func stringify(v interface{}) string {
	if vs, ok := v.(string); ok {
		return vs
	}

	return fmt.Sprintf("%v", v)
}

func keys(set Params) (result []string) {
	for k, _ := range set {
		result = append(result, k)
	}
	return
}

func nestedKeys(set map[string]interface{}, parent string, subParse bool) []string {
	var key string
	var result []string
	for k, v := range set {

		if subParse {
			key = fmt.Sprintf("%s.%s", parent, k)
		} else {
			key = k
		}

		result = append(result, key)

		if val, ok := v.(map[string]interface{}); ok {
			subKeys := nestedKeys(val, key, true)
			result = append(result, subKeys...)
		} else if val, ok := v.(Params); ok {
			subKeys := nestedKeys(val, key, true)
			result = append(result, subKeys...)
		}

	}
	return result
}

func toURLValues(set Params, prefix, suffix string, subParse bool) url.Values {
	if prefix == "" {
		prefix = "."
		suffix = ""
	}

	result := url.Values{}
	var subset url.Values
	for key, value := range set {
		var foundSubset bool
		if v, ok := value.(Params); ok {
			subset = toURLValues(v, prefix, suffix, true)
			foundSubset = true
		} else if v, ok := value.(map[string]interface{}); ok {
			subset = toURLValues(Params(v), prefix, suffix, true)
			foundSubset = true
		} else if v, ok := value.([]interface{}); ok {
			for _, el := range v {
				result[key] = append(result[key], stringify(el))
			}
			continue
		}
		if foundSubset {
			for k, v := range subset {
				nestedKey := fmt.Sprintf("%s%s", key, k)
				if subParse {
					nestedKey = fmt.Sprintf("%s%s%s%s", prefix, key, suffix, k)
				}
				result[nestedKey] = append(result[nestedKey], v...)
			}
		} else {
			valueKey := key
			if subParse {
				valueKey = fmt.Sprintf("%s%s%s", prefix, key, suffix)
			}
			result[valueKey] = append(result[valueKey], stringify(value))
		}
	}
	return result
}
<|endoftext|>


<commit_before>package mark

import (
	fmt "github.com/k0kubun/pp"
	"regexp"
	"strings"
	"unicode"
	"unicode/utf8"
)

type Tree struct {
	text  string
	lex   *lexer
	Nodes []Node
	// Parsing only
	token     [3]item // three-token lookahead for parser
	peekCount int
	output    string
	links     map[string]*DefLinkNode
}

// Parse convert the raw text to NodeTree.
func (t *Tree) parse() {
Loop:
	for {
		var n Node
		switch p := t.peek(); p.typ {
		case eof, itemError:
			break Loop
		case itemNewLine:
			n = t.newLine(t.next().pos)
		case itemBr:
			n = t.newBr(t.next().pos)
		case itemHr:
			n = t.newHr(t.next().pos)
		case itemText:
			tmp := t.newParagraph(p.pos)
			tmp.Nodes = t.parseText(t.next().val)
			n = tmp
		case itemDefLink:
			n = t.parseDefLink()
		case itemHeading, itemLHeading:
			n = t.parseHeading()
		case itemCodeBlock, itemGfmCodeBlock:
			n = t.parseCodeBlock()
		case itemList:
			n = t.parseList()
		case itemTable, itemLpTable:
			n = t.parseTable()
		case itemBlockQuote:
			n = t.parseBlockQuote()
		default:
			t.next()
		}
		if n != nil {
			t.append(n)
		}
	}
}

// Render parse nodes to the wanted output
func (t *Tree) render() {
	var last Node
	last = t.newLine(0)
	for _, node := range t.Nodes {
		if last.Type() != NodeNewLine || node.Type() != last.Type() {
			t.output += node.Render()
		}
		last = node
	}
}

// append new node to nodes-list
func (t *Tree) append(n Node) {
	t.Nodes = append(t.Nodes, n)
}

// next returns the next token
func (t *Tree) next() item {
	if t.peekCount > 0 {
		t.peekCount--
	} else {
		t.token[0] = t.lex.nextItem()
	}
	return t.token[t.peekCount]
}

// peek returns but does not consume the next token.
func (t *Tree) peek() item {
	if t.peekCount > 0 {
		return t.token[t.peekCount-1]
	}
	t.peekCount = 1
	t.token[0] = t.lex.nextItem()
	return t.token[0]
}

// backup backs the input stream tp one token
func (t *Tree) backup() {
	t.peekCount++
}

// backup2 backs the input stream up two tokens.
// The zeroth token is already there.
func (t *Tree) backup2(t1 item) {
	t.token[1] = t1
	t.peekCount = 2
}

// parseText scan until itemBr occur.
func (t *Tree) parseText(input string) (nodes []Node) {
	// HACK: if there's more 'itemText' in the way, make it one.
	for {
		tkn := t.next()
		if tkn.typ == itemText {
			input += tkn.val
		} else if tkn.typ == itemNewLine {
			if t.peek().typ != itemText {
				t.backup2(tkn)
				break
			}
			input += tkn.val
		} else {
			t.backup()
			break
		}
	}
	l := lexInline(input)
	for token := range l.items {
		var node Node
		switch token.typ {
		case itemNewLine:
			node = t.newLine(token.pos)
		case itemBr:
			node = t.newBr(token.pos)
		case itemStrong, itemItalic, itemStrike, itemCode:
			node = t.parseEmphasis(token.typ, token.pos, token.val)
		case itemLink, itemAutoLink, itemGfmLink:
			var title, text, href string
			match := span[token.typ].FindStringSubmatch(token.val)
			if token.typ == itemLink {
				text, href, title = match[1], match[2], match[3]
			} else {
				text, href = match[1], match[1]
			}
			node = t.newLink(token.pos, title, href, text)
		case itemImage:
			match := span[token.typ].FindStringSubmatch(token.val)
			node = t.newImage(token.pos, match[3], match[2], match[1])
		case itemText:
			node = t.newText(token.pos, token.val)
		default:
			fmt.Println("Matching not found for this token:", token)
		}
		nodes = append(nodes, node)
	}
	return nodes
}

// Parse inline emphasis
func (t *Tree) parseEmphasis(typ itemType, pos Pos, val string) *EmphasisNode {
	node := t.newEmphasis(pos, typ)
	match := span[typ].FindStringSubmatch(val)
	text := match[len(match)-1]
	if text == "" {
		text = match[1]
	}
	node.Nodes = t.parseText(text)
	return node
}

// parse heading block
func (t *Tree) parseHeading() (node *HeadingNode) {
	token := t.next()
	match := block[token.typ].FindStringSubmatch(token.val)
	if token.typ == itemHeading {
		node = t.newHeading(token.pos, len(match[1]), match[2])
	} else {
		// using equal signs for first-level, and dashes for second-level.
		level := 1
		if match[2] == "-" {
			level = 2
		}
		node = t.newHeading(token.pos, level, match[1])
	}
	return
}

func (t *Tree) parseDefLink() *DefLinkNode {
	token := t.next()
	match := block[itemDefLink].FindStringSubmatch(token.val)
	name := strings.ToLower(match[1])
	// name(lowercase), href, title
	n := t.newDefLink(token.pos, name, match[2], match[3])
	// store in links
	t.links[name] = n
	return n
}

// parse codeBlock
func (t *Tree) parseCodeBlock() *CodeNode {
	var lang, text string
	token := t.next()
	if token.typ == itemGfmCodeBlock {
		match := block[itemGfmCodeBlock].FindStringSubmatch(token.val)
		if text = match[2]; text == "" {
			text = match[4]
		}
		if lang = match[1]; lang == "" {
			lang = match[3]
		}
	} else {
		text = regexp.MustCompile("(?m)^( {4})").ReplaceAllLiteralString(token.val, "")
	}
	return t.newCode(token.pos, lang, text)
}

func (t *Tree) parseBlockQuote() (n *BlockQuoteNode) {
	token := t.next()
	// replacer
	re := regexp.MustCompile(`(?m)^> ?`)
	raw := re.ReplaceAllString(token.val, "")
	// TODO(Ariel): not work right now with defLink(inside the blockQuote)
	tr := &Tree{lex: lex(raw)}
	tr.parse()
	n = t.newBlockQuote(token.pos)
	n.Nodes = tr.Nodes
	return
}

// parse list
func (t *Tree) parseList() *ListNode {
	token := t.next()
	list := t.newList(token.pos, isDigit(token.val))
Loop:
	for {
		switch token = t.peek(); token.typ {
		case itemLooseItem, itemListItem:
			list.append(t.parseListItem())
		default:
			break Loop
		}
	}
	return list
}

// parse listItem
// Add ignore list(e.g: table should parse as a text)
func (t *Tree) parseListItem() *ListItemNode {
	token := t.next()
	item := t.newListItem(token.pos)
	tr := &Tree{lex: lex(strings.TrimSpace(token.val))}
	tr.parse()
	for _, node := range tr.Nodes {
		// wrap with paragraph only when it's loose item
		if n, ok := node.(*ParagraphNode); ok && token.typ == itemListItem {
			item.Nodes = append(item.Nodes, n.Nodes...)
		} else {
			item.append(node)
		}
	}
	return item
}

// parse table
func (t *Tree) parseTable() *TableNode {
	// End of table
	done := t.lex.eot
	token := t.next()
	// ignore the first and last one...
	//lp := token.val == "|"
	table := t.newTable(token.pos)
	// Align	[ None, Left, Right, ... ]
	// Header	[ Cels: [token, token, ... ] ]
	// Data:	[ Row: [Cells: [token, ... ] ] ]
	rows := struct {
		Align  []AlignType
		Header [][]item
		Data   [][][]item
	}{}
	var cell []item
	var row [][]item
	// Collect items
Loop:
	for i := 0; ; {
		switch token := t.next(); token.typ {
		case eof, itemError:
			break Loop
		case itemNewLine:
			// If we done with this table
			if t.peek().pos >= done {
				break Loop
			}
			fallthrough
		case itemPipe:
			// Test if cell non-empty before appending to current row
			if len(cell) > 0 {
				// Header
				if i == 0 {
					rows.Header = append(rows.Header, cell)
					// Alignment
				} else if i == 1 {
					align := cell[0].val
					if len(cell) > 1 {
						for i := 1; i < len(cell); i++ {
							align += cell[i].val
						}
					}
					// Trim spaces
					rows.Align = append(rows.Align, parseAlign(align))
					// Data
				} else {
					row = append(row, cell)
				}
			}
			if token.typ == itemNewLine {
				i++
				// test if there's an elemnts to append to tbody.
				// we want to avoid situations like `appending empty rows`, etc..
				if i > 2 && len(row) > 0 {
					rows.Data = append(rows.Data, row)
					row = [][]item{}
				}
			}
			cell = []item{}
		default:
			cell = append(cell, token)
		}
	}
	// Drain cell/row
	if len(cell) > 0 {
		row = append(row, cell)
	}
	if len(row) > 0 {
		rows.Data = append(rows.Data, row)
	}
	// Tranform to nodes
	// Add an average mechanisem, that ignore empty(or " ") in end of cell
	//	rowLen := len(rows.Align)
	// Table head
	table.append(t.parseCells(Header, rows.Header, rows.Align))
	// Table body
	for _, row := range rows.Data {
		table.append(t.parseCells(Data, row, rows.Align))
	}
	return table
}

// Should return typ []CellNode
func (t *Tree) parseCells(kind int, items [][]item, align []AlignType) *RowNode {
	// TODO(Ariel): Add real position
	row := t.newRow(1)
	for i, item := range items {
		// Cell contain nodes
		var s string
		cell := t.newCell(item[0].pos, kind, align[i])
		for _, tkn := range item {
			s += tkn.val
		}
		// Used before: `^[ |\t]*|[ |\t]*$`
		cell.Nodes = t.parseText(strings.TrimSpace(s))
		row.append(cell)
	}
	return row
}

// get align-string and return the align type of it
// e.g: ":---", "---:", ":---:", "---"
func parseAlign(s string) (typ AlignType) {
	// Trim spaces before
	s = strings.TrimSpace(s)
	sfx, pfx := strings.HasSuffix(s, ":"), strings.HasPrefix(s, ":")
	switch {
	case sfx && pfx:
		typ = Center
	case sfx:
		typ = Right
	case pfx:
		typ = Left
	}
	return
}

// test if given string is digit
func isDigit(s string) bool {
	r, _ := utf8.DecodeRuneInString(s)
	return unicode.IsDigit(r)
}

// test if given token is type block
func isBlock(item itemType) (b bool) {
	switch item {
	case itemHeading, itemLHeading, itemCodeBlock, itemBlockQuote,
		itemList, itemTable, itemGfmCodeBlock, itemHr:
		b = true
	}
	return
}
<commit_msg>feat(parser): parse html node<commit_after>package mark

import (
	fmt "github.com/k0kubun/pp"
	"regexp"
	"strings"
	"unicode"
	"unicode/utf8"
)

type Tree struct {
	text  string
	lex   *lexer
	Nodes []Node
	// Parsing only
	token     [3]item // three-token lookahead for parser
	peekCount int
	output    string
	links     map[string]*DefLinkNode
}

// Parse convert the raw text to NodeTree.
func (t *Tree) parse() {
Loop:
	for {
		var n Node
		switch p := t.peek(); p.typ {
		case eof, itemError:
			break Loop
		case itemNewLine:
			n = t.newLine(t.next().pos)
		case itemBr:
			n = t.newBr(t.next().pos)
		case itemHr:
			n = t.newHr(t.next().pos)
		case itemHTML:
			p = t.next()
			n = t.newHTML(p.pos, p.val)
		case itemText:
			tmp := t.newParagraph(p.pos)
			tmp.Nodes = t.parseText(t.next().val)
			n = tmp
		case itemDefLink:
			n = t.parseDefLink()
		case itemHeading, itemLHeading:
			n = t.parseHeading()
		case itemCodeBlock, itemGfmCodeBlock:
			n = t.parseCodeBlock()
		case itemList:
			n = t.parseList()
		case itemTable, itemLpTable:
			n = t.parseTable()
		case itemBlockQuote:
			n = t.parseBlockQuote()
		default:
			t.next()
		}
		if n != nil {
			t.append(n)
		}
	}
}

// Render parse nodes to the wanted output
func (t *Tree) render() {
	var last Node
	last = t.newLine(0)
	for _, node := range t.Nodes {
		if last.Type() != NodeNewLine || node.Type() != last.Type() {
			t.output += node.Render()
		}
		last = node
	}
}

// append new node to nodes-list
func (t *Tree) append(n Node) {
	t.Nodes = append(t.Nodes, n)
}

// next returns the next token
func (t *Tree) next() item {
	if t.peekCount > 0 {
		t.peekCount--
	} else {
		t.token[0] = t.lex.nextItem()
	}
	return t.token[t.peekCount]
}

// peek returns but does not consume the next token.
func (t *Tree) peek() item {
	if t.peekCount > 0 {
		return t.token[t.peekCount-1]
	}
	t.peekCount = 1
	t.token[0] = t.lex.nextItem()
	return t.token[0]
}

// backup backs the input stream tp one token
func (t *Tree) backup() {
	t.peekCount++
}

// backup2 backs the input stream up two tokens.
// The zeroth token is already there.
func (t *Tree) backup2(t1 item) {
	t.token[1] = t1
	t.peekCount = 2
}

// parseText scan until itemBr occur.
func (t *Tree) parseText(input string) (nodes []Node) {
	// HACK: if there's more 'itemText' in the way, make it one.
	for {
		tkn := t.next()
		if tkn.typ == itemText {
			input += tkn.val
		} else if tkn.typ == itemNewLine {
			if t.peek().typ != itemText {
				t.backup2(tkn)
				break
			}
			input += tkn.val
		} else {
			t.backup()
			break
		}
	}
	l := lexInline(input)
	for token := range l.items {
		var node Node
		switch token.typ {
		case itemNewLine:
			node = t.newLine(token.pos)
		case itemBr:
			node = t.newBr(token.pos)
		case itemStrong, itemItalic, itemStrike, itemCode:
			node = t.parseEmphasis(token.typ, token.pos, token.val)
		case itemLink, itemAutoLink, itemGfmLink:
			var title, text, href string
			match := span[token.typ].FindStringSubmatch(token.val)
			if token.typ == itemLink {
				text, href, title = match[1], match[2], match[3]
			} else {
				text, href = match[1], match[1]
			}
			node = t.newLink(token.pos, title, href, text)
		case itemImage:
			match := span[token.typ].FindStringSubmatch(token.val)
			node = t.newImage(token.pos, match[3], match[2], match[1])
		case itemText:
			node = t.newText(token.pos, token.val)
		default:
			fmt.Println("Matching not found for this token:", token)
		}
		nodes = append(nodes, node)
	}
	return nodes
}

// Parse inline emphasis
func (t *Tree) parseEmphasis(typ itemType, pos Pos, val string) *EmphasisNode {
	node := t.newEmphasis(pos, typ)
	match := span[typ].FindStringSubmatch(val)
	text := match[len(match)-1]
	if text == "" {
		text = match[1]
	}
	node.Nodes = t.parseText(text)
	return node
}

// parse heading block
func (t *Tree) parseHeading() (node *HeadingNode) {
	token := t.next()
	match := block[token.typ].FindStringSubmatch(token.val)
	if token.typ == itemHeading {
		node = t.newHeading(token.pos, len(match[1]), match[2])
	} else {
		// using equal signs for first-level, and dashes for second-level.
		level := 1
		if match[2] == "-" {
			level = 2
		}
		node = t.newHeading(token.pos, level, match[1])
	}
	return
}

func (t *Tree) parseDefLink() *DefLinkNode {
	token := t.next()
	match := block[itemDefLink].FindStringSubmatch(token.val)
	name := strings.ToLower(match[1])
	// name(lowercase), href, title
	n := t.newDefLink(token.pos, name, match[2], match[3])
	// store in links
	t.links[name] = n
	return n
}

// parse codeBlock
func (t *Tree) parseCodeBlock() *CodeNode {
	var lang, text string
	token := t.next()
	if token.typ == itemGfmCodeBlock {
		match := block[itemGfmCodeBlock].FindStringSubmatch(token.val)
		if text = match[2]; text == "" {
			text = match[4]
		}
		if lang = match[1]; lang == "" {
			lang = match[3]
		}
	} else {
		text = regexp.MustCompile("(?m)^( {4})").ReplaceAllLiteralString(token.val, "")
	}
	return t.newCode(token.pos, lang, text)
}

func (t *Tree) parseBlockQuote() (n *BlockQuoteNode) {
	token := t.next()
	// replacer
	re := regexp.MustCompile(`(?m)^> ?`)
	raw := re.ReplaceAllString(token.val, "")
	// TODO(Ariel): not work right now with defLink(inside the blockQuote)
	tr := &Tree{lex: lex(raw)}
	tr.parse()
	n = t.newBlockQuote(token.pos)
	n.Nodes = tr.Nodes
	return
}

// parse list
func (t *Tree) parseList() *ListNode {
	token := t.next()
	list := t.newList(token.pos, isDigit(token.val))
Loop:
	for {
		switch token = t.peek(); token.typ {
		case itemLooseItem, itemListItem:
			list.append(t.parseListItem())
		default:
			break Loop
		}
	}
	return list
}

// parse listItem
// Add ignore list(e.g: table should parse as a text)
func (t *Tree) parseListItem() *ListItemNode {
	token := t.next()
	item := t.newListItem(token.pos)
	tr := &Tree{lex: lex(strings.TrimSpace(token.val))}
	tr.parse()
	for _, node := range tr.Nodes {
		// wrap with paragraph only when it's loose item
		if n, ok := node.(*ParagraphNode); ok && token.typ == itemListItem {
			item.Nodes = append(item.Nodes, n.Nodes...)
		} else {
			item.append(node)
		}
	}
	return item
}

// parse table
func (t *Tree) parseTable() *TableNode {
	// End of table
	done := t.lex.eot
	token := t.next()
	// ignore the first and last one...
	//lp := token.val == "|"
	table := t.newTable(token.pos)
	// Align	[ None, Left, Right, ... ]
	// Header	[ Cels: [token, token, ... ] ]
	// Data:	[ Row: [Cells: [token, ... ] ] ]
	rows := struct {
		Align  []AlignType
		Header [][]item
		Data   [][][]item
	}{}
	var cell []item
	var row [][]item
	// Collect items
Loop:
	for i := 0; ; {
		switch token := t.next(); token.typ {
		case eof, itemError:
			break Loop
		case itemNewLine:
			// If we done with this table
			if t.peek().pos >= done {
				break Loop
			}
			fallthrough
		case itemPipe:
			// Test if cell non-empty before appending to current row
			if len(cell) > 0 {
				// Header
				if i == 0 {
					rows.Header = append(rows.Header, cell)
					// Alignment
				} else if i == 1 {
					align := cell[0].val
					if len(cell) > 1 {
						for i := 1; i < len(cell); i++ {
							align += cell[i].val
						}
					}
					// Trim spaces
					rows.Align = append(rows.Align, parseAlign(align))
					// Data
				} else {
					row = append(row, cell)
				}
			}
			if token.typ == itemNewLine {
				i++
				// test if there's an elemnts to append to tbody.
				// we want to avoid situations like `appending empty rows`, etc..
				if i > 2 && len(row) > 0 {
					rows.Data = append(rows.Data, row)
					row = [][]item{}
				}
			}
			cell = []item{}
		default:
			cell = append(cell, token)
		}
	}
	// Drain cell/row
	if len(cell) > 0 {
		row = append(row, cell)
	}
	if len(row) > 0 {
		rows.Data = append(rows.Data, row)
	}
	// Tranform to nodes
	// Add an average mechanisem, that ignore empty(or " ") in end of cell
	//	rowLen := len(rows.Align)
	// Table head
	table.append(t.parseCells(Header, rows.Header, rows.Align))
	// Table body
	for _, row := range rows.Data {
		table.append(t.parseCells(Data, row, rows.Align))
	}
	return table
}

// Should return typ []CellNode
func (t *Tree) parseCells(kind int, items [][]item, align []AlignType) *RowNode {
	// TODO(Ariel): Add real position
	row := t.newRow(1)
	for i, item := range items {
		// Cell contain nodes
		var s string
		cell := t.newCell(item[0].pos, kind, align[i])
		for _, tkn := range item {
			s += tkn.val
		}
		// Used before: `^[ |\t]*|[ |\t]*$`
		cell.Nodes = t.parseText(strings.TrimSpace(s))
		row.append(cell)
	}
	return row
}

// get align-string and return the align type of it
// e.g: ":---", "---:", ":---:", "---"
func parseAlign(s string) (typ AlignType) {
	// Trim spaces before
	s = strings.TrimSpace(s)
	sfx, pfx := strings.HasSuffix(s, ":"), strings.HasPrefix(s, ":")
	switch {
	case sfx && pfx:
		typ = Center
	case sfx:
		typ = Right
	case pfx:
		typ = Left
	}
	return
}

// test if given string is digit
func isDigit(s string) bool {
	r, _ := utf8.DecodeRuneInString(s)
	return unicode.IsDigit(r)
}

// test if given token is type block
func isBlock(item itemType) (b bool) {
	switch item {
	case itemHeading, itemLHeading, itemCodeBlock, itemBlockQuote,
		itemList, itemTable, itemGfmCodeBlock, itemHr:
		b = true
	}
	return
}
<|endoftext|>


<commit_before>package main
<commit_msg>Removed junk file, see github.com/wmbest2/android<commit_after><|endoftext|>


<commit_before>package main

// PieceKind is the type representing for the pieces on the board
type PieceKind int

const (
	// EMPTY_SQUARE aka Empty
	EMPTY_SQUARE PieceKind = 0 + iota
	// PAWN (♙, ♟)
	PAWN
	// ROOK (♖, ♜)
	ROOK
	// KNIGHT (♘, ♞)
	KNIGHT
	// BISHOP (♗, ♝)
	BISHOP
	// QUEEN (♕, ♛)
	QUEEN
	// KING (♔, ♚)
	KING
)

// Position is the place where a piece stand
type Position struct {
	x int
	y int
}

// IsValid returns if a position is on the board or not
func (p Position) IsValid() bool {
	return p.x >= 0 && p.x < 8 && p.y >= 0 && p.y < 8
}

// Equal returns if two positions are in the same place
func (p Position) Equal(p2 Position) bool {
	return p.x == p2.x && p.y == p2.y
}

// Side can be either white or black
type Side int

const (
	// EMPTY is where neither white nor black piece stand on it
	EMPTY Side = 0 + iota
	// WHITE is the side that start the game second
	WHITE
	// BLACK move after the WHITE side is moved at the beginning
	BLACK
)

// Square represents for one of the 64 squares on the board
type Square interface {
	String() string
	PieceKind() PieceKind
	Side() Side
	Move(b *Board, pos1, pos2 Position) bool
}

// The Piece stand at wherever on the board
type Piece struct {
	pk PieceKind
	sd Side
}

func (p Piece) String() string {
	return "   "
}

// Side returns the side of the piece
func (p Piece) Side() Side {
	return p.sd
}

// Movable is used to attach to the piece that need recording if
// it's been moved or not
type Movable struct {
	moved bool
}

// isMoved check if a square is moved or not
func (m Movable) isMoved() bool {
	return m.moved
}

// Pawn - the weakest
type Pawn struct {
	Piece
	*Movable
}

func (p Pawn) String() string {
	if p.sd == WHITE {
		return " ♙ "
	}
	return " ♟ "
}

// PieceKind of Pawn
func (p Pawn) PieceKind() PieceKind { return PAWN }

// Move like a Pawn
func (p Pawn) Move(b *Board, pos1, pos2 Position) bool {
	s1 := b.squares[pos1.x][pos2.y]

	// Allowed move count
	amc := 1
	if p.moved == false {
		amc = 2
	}

	dir := 1
	if p.Side() == BLACK {
		dir = -1
	}

	for i := 1; i <= amc; i++ {
		dx := dir * i
		if !(Position{pos1.x + dx, pos2.y}).IsValid() {
			break
		}

		if pos2.Equal(Position{pos1.x + dx, pos2.y}) {
			s2 := b.squares[pos2.x][pos2.y]
			if s1.Side() == s2.Side() {
				return false
			}
			b.squares[pos2.x][pos2.y] = b.squares[pos1.x][pos1.y]
			movedPawn := b.squares[pos2.x][pos2.y].(Pawn)
			movedPawn.moved = true
			b.squares[pos1.x][pos1.y] = EmptySquare{}
			return true
		}
	}

	ds := [][]int{{1, 1}, {1, -1}}
	for _, d := range ds {
		x, y := pos1.x+d[0], pos1.y+d[1]
		if !(Position{x, y}).IsValid() {
			break
		}

		if pos2.Equal(Position{x, y}) {
			s2 := b.squares[pos2.x][pos2.y]
			if s1.Side() == s2.Side() {
				return false
			}
			b.squares[pos2.x][pos2.y] = b.squares[pos1.x][pos1.y]
			b.squares[pos1.x][pos1.y] = EmptySquare{}
			return true
		}
	}
	return false
}

// Rook - aka. the Tower
type Rook struct {
	Piece
	*Movable
}

func (r Rook) String() string {
	if r.sd == WHITE {
		return " ♖ "
	}
	return " ♜ "
}

// PieceKind of Rook
func (r Rook) PieceKind() PieceKind { return ROOK }

// Move like a Rook
func (r Rook) Move(b *Board, pos1, pos2 Position) bool {
	dirs := [][]int{{1, 0}, {-1, 0}, {0, 1}, {0, -1}}
	for _, dir := range dirs {
		dx, dy := dir[0], dir[1]
		for i, j := pos1.x+dx, pos1.y+dy; (Position{i, j}.IsValid()); i, j = i+dx, j+dy {
			p2 := b.squares[i][j]
			// blocked by it's own piece
			if r.Side() == p2.Side() {
				break
			}
			if pos2.Equal(Position{i, j}) {
				b.squares[pos2.x][pos2.y] = b.squares[pos1.x][pos1.y]
				b.squares[pos1.x][pos1.y] = EmptySquare{}

				movedRook := b.squares[pos2.x][pos2.y].(Rook)
				movedRook.moved = true
				return true
			}
		}
	}
	return false
}

// Knight - Piece that moves in the weird way
type Knight struct {
	Piece
}

func (k Knight) String() string {
	if k.sd == WHITE {
		return " ♘ "
	}
	return " ♞ "
}

// PieceKind of Knight
func (k Knight) PieceKind() PieceKind { return KNIGHT }

// Move like a Knight
func (k Knight) Move(b *Board, pos1, pos2 Position) bool {
	return false
}

// Bishop - The bishop
type Bishop struct {
	Piece
}

func (b Bishop) String() string {
	if b.sd == WHITE {
		return " ♗ "
	}
	return " ♝ "
}

// PieceKind of Bishop
func (b Bishop) PieceKind() PieceKind { return BISHOP }

// Move like a Bishop
func (b Bishop) Move(bd *Board, pos1, pos2 Position) bool {
	dirs := [][]int{{1, 1}, {1, -1}, {-1, 1}, {-1, -1}}
	for _, dir := range dirs {
		dx, dy := dir[0], dir[1]
		for i, j := pos1.x+dx, pos1.y+dy; (Position{i, j}.IsValid()); i, j = i+dx, j+dy {
			p2 := bd.squares[i][j]
			// blocked by it's own piece
			if b.Side() == p2.Side() {
				break
			}
			if pos2.Equal(Position{i, j}) {
				bd.squares[pos2.x][pos2.y] = bd.squares[pos1.x][pos1.y]
				bd.squares[pos1.x][pos1.y] = EmptySquare{}
				return true
			}
		}
	}
	return false
}

// Queen - The strongest piece on the board
type Queen struct {
	Piece
}

func (q Queen) String() string {
	if q.sd == WHITE {
		return " ♕ "
	}
	return " ♛ "
}

// PieceKind of Queen
func (q Queen) PieceKind() PieceKind { return QUEEN }

// Move like the Queen
func (q Queen) Move(b *Board, pos1, pos2 Position) bool {
	for dx := -1; dx <= 1; dx++ {
		for dy := -1; dy <= 1; dy++ {
			if dx == 0 && dy == 0 {
				break
			}
			for i, j := pos1.x+dx, pos1.y+dy; (Position{i, j}.IsValid()); i, j = i+dx, j+dy {
				p2 := b.squares[i][j]
				// blocked by it's own piece
				if q.Side() == p2.Side() {
					break
				}
				if pos2.Equal(Position{i, j}) {
					b.squares[pos2.x][pos2.y] = b.squares[pos1.x][pos1.y]
					b.squares[pos1.x][pos1.y] = EmptySquare{}
					return true
				}
			}
		}
	}
	return false
}

// King - The piece to protect
type King struct {
	Piece
	*Movable
}

func (k King) String() string {
	if k.sd == WHITE {
		return " ♔ "
	}
	return " ♚ "
}

// PieceKind of KING
func (k King) PieceKind() PieceKind { return KING }

// Move in the King's way
func (k King) Move(b *Board, pos1, pos2 Position) bool {
	s1 := b.squares[pos1.x][pos2.y]

	diff := (pos1.x - pos2.x) * (pos1.x - pos2.x)
	if diff >= -1 && diff <= 1 && !pos1.Equal(pos2) {
		s2 := b.squares[pos2.x][pos2.y]
		if s1.Side() == s2.Side() {
			return false
		}
		b.squares[pos2.x][pos2.y] = b.squares[pos1.x][pos1.y]
		b.squares[pos1.x][pos1.y] = EmptySquare{}
		movedKing := b.squares[pos2.x][pos2.y].(King)
		movedKing.moved = true
		return true
	}
	return false
}

// EmptySquare - aka no piece on the board
type EmptySquare struct {
	Piece
}

// PieceKind of KING
func (es EmptySquare) PieceKind() PieceKind { return EMPTY_SQUARE }

// Move always returns false if the squre is empty
func (es EmptySquare) Move(b *Board, pos1, pos2 Position) bool { return false }
<commit_msg>Oh, forgot about the knights.<commit_after>package main

// PieceKind is the type representing for the pieces on the board
type PieceKind int

const (
	// EMPTY_SQUARE aka Empty
	EMPTY_SQUARE PieceKind = 0 + iota
	// PAWN (♙, ♟)
	PAWN
	// ROOK (♖, ♜)
	ROOK
	// KNIGHT (♘, ♞)
	KNIGHT
	// BISHOP (♗, ♝)
	BISHOP
	// QUEEN (♕, ♛)
	QUEEN
	// KING (♔, ♚)
	KING
)

// Position is the place where a piece stand
type Position struct {
	x int
	y int
}

// IsValid returns if a position is on the board or not
func (p Position) IsValid() bool {
	return p.x >= 0 && p.x < 8 && p.y >= 0 && p.y < 8
}

// Equal returns if two positions are in the same place
func (p Position) Equal(p2 Position) bool {
	return p.x == p2.x && p.y == p2.y
}

// Side can be either white or black
type Side int

const (
	// EMPTY is where neither white nor black piece stand on it
	EMPTY Side = 0 + iota
	// WHITE is the side that start the game second
	WHITE
	// BLACK move after the WHITE side is moved at the beginning
	BLACK
)

// Square represents for one of the 64 squares on the board
type Square interface {
	String() string
	PieceKind() PieceKind
	Side() Side
	Move(b *Board, pos1, pos2 Position) bool
}

// The Piece stand at wherever on the board
type Piece struct {
	pk PieceKind
	sd Side
}

func (p Piece) String() string {
	return "   "
}

// Side returns the side of the piece
func (p Piece) Side() Side {
	return p.sd
}

// Movable is used to attach to the piece that need recording if
// it's been moved or not
type Movable struct {
	moved bool
}

// isMoved check if a square is moved or not
func (m Movable) isMoved() bool {
	return m.moved
}

// Pawn - the weakest
type Pawn struct {
	Piece
	*Movable
}

func (p Pawn) String() string {
	if p.sd == WHITE {
		return " ♙ "
	}
	return " ♟ "
}

// PieceKind of Pawn
func (p Pawn) PieceKind() PieceKind { return PAWN }

// Move like a Pawn
func (p Pawn) Move(b *Board, pos1, pos2 Position) bool {
	s1 := b.squares[pos1.x][pos2.y]

	// Allowed move count
	amc := 1
	if p.moved == false {
		amc = 2
	}

	dir := 1
	if p.Side() == BLACK {
		dir = -1
	}

	for i := 1; i <= amc; i++ {
		dx := dir * i
		if !(Position{pos1.x + dx, pos2.y}).IsValid() {
			break
		}

		if pos2.Equal(Position{pos1.x + dx, pos2.y}) {
			s2 := b.squares[pos2.x][pos2.y]
			if s1.Side() == s2.Side() {
				return false
			}
			b.squares[pos2.x][pos2.y] = b.squares[pos1.x][pos1.y]
			movedPawn := b.squares[pos2.x][pos2.y].(Pawn)
			movedPawn.moved = true
			b.squares[pos1.x][pos1.y] = EmptySquare{}
			return true
		}
	}

	ds := [][]int{{1, 1}, {1, -1}}
	for _, d := range ds {
		x, y := pos1.x+d[0], pos1.y+d[1]
		if !(Position{x, y}).IsValid() {
			break
		}

		if pos2.Equal(Position{x, y}) {
			s2 := b.squares[pos2.x][pos2.y]
			if s1.Side() == s2.Side() {
				return false
			}
			b.squares[pos2.x][pos2.y] = b.squares[pos1.x][pos1.y]
			b.squares[pos1.x][pos1.y] = EmptySquare{}
			return true
		}
	}
	return false
}

// Rook - aka. the Tower
type Rook struct {
	Piece
	*Movable
}

func (r Rook) String() string {
	if r.sd == WHITE {
		return " ♖ "
	}
	return " ♜ "
}

// PieceKind of Rook
func (r Rook) PieceKind() PieceKind { return ROOK }

// Move like a Rook
func (r Rook) Move(b *Board, pos1, pos2 Position) bool {
	dirs := [][]int{{1, 0}, {-1, 0}, {0, 1}, {0, -1}}
	for _, dir := range dirs {
		dx, dy := dir[0], dir[1]
		for i, j := pos1.x+dx, pos1.y+dy; (Position{i, j}.IsValid()); i, j = i+dx, j+dy {
			p2 := b.squares[i][j]
			// blocked by it's own piece
			if r.Side() == p2.Side() {
				break
			}
			if pos2.Equal(Position{i, j}) {
				b.squares[pos2.x][pos2.y] = b.squares[pos1.x][pos1.y]
				b.squares[pos1.x][pos1.y] = EmptySquare{}

				movedRook := b.squares[pos2.x][pos2.y].(Rook)
				movedRook.moved = true
				return true
			}
		}
	}
	return false
}

// Knight - Piece that moves in the weird way
type Knight struct {
	Piece
}

func (k Knight) String() string {
	if k.sd == WHITE {
		return " ♘ "
	}
	return " ♞ "
}

// PieceKind of Knight
func (k Knight) PieceKind() PieceKind { return KNIGHT }

// Move like a Knight
func (k Knight) Move(b *Board, pos1, pos2 Position) bool {
	for i := -1; i <= 1; i++ {
		for j := -1; j <= 1; j++ {
			if i == 0 && j == 0 {
				continue
			}
			for t := 0; t <= 1; t++ {
				dirs := []int{i, j}
				dirs[t] *= 2

				x, y := pos1.x+dirs[0], pos1.y+dirs[1]
				if pos2.Equal(Position{x, y}) {
					p2 := b.squares[pos2.x][pos2.y]

					if p2.Side() == k.Side() {
						return false
					}
					b.squares[pos2.x][pos2.y] = b.squares[pos1.x][pos1.y]
					b.squares[pos1.x][pos1.y] = EmptySquare{}
					return true
				}
			}
		}
	}
	return false
}

// Bishop - The bishop
type Bishop struct {
	Piece
}

func (b Bishop) String() string {
	if b.sd == WHITE {
		return " ♗ "
	}
	return " ♝ "
}

// PieceKind of Bishop
func (b Bishop) PieceKind() PieceKind { return BISHOP }

// Move like a Bishop
func (b Bishop) Move(bd *Board, pos1, pos2 Position) bool {
	dirs := [][]int{{1, 1}, {1, -1}, {-1, 1}, {-1, -1}}
	for _, dir := range dirs {
		dx, dy := dir[0], dir[1]
		for i, j := pos1.x+dx, pos1.y+dy; (Position{i, j}.IsValid()); i, j = i+dx, j+dy {
			p2 := bd.squares[i][j]
			// blocked by it's own piece
			if b.Side() == p2.Side() {
				break
			}
			if pos2.Equal(Position{i, j}) {
				bd.squares[pos2.x][pos2.y] = bd.squares[pos1.x][pos1.y]
				bd.squares[pos1.x][pos1.y] = EmptySquare{}
				return true
			}
		}
	}
	return false
}

// Queen - The strongest piece on the board
type Queen struct {
	Piece
}

func (q Queen) String() string {
	if q.sd == WHITE {
		return " ♕ "
	}
	return " ♛ "
}

// PieceKind of Queen
func (q Queen) PieceKind() PieceKind { return QUEEN }

// Move like the Queen
func (q Queen) Move(b *Board, pos1, pos2 Position) bool {
	for dx := -1; dx <= 1; dx++ {
		for dy := -1; dy <= 1; dy++ {
			if dx == 0 && dy == 0 {
				break
			}
			for i, j := pos1.x+dx, pos1.y+dy; (Position{i, j}.IsValid()); i, j = i+dx, j+dy {
				p2 := b.squares[i][j]
				// blocked by it's own piece
				if q.Side() == p2.Side() {
					break
				}
				if pos2.Equal(Position{i, j}) {
					b.squares[pos2.x][pos2.y] = b.squares[pos1.x][pos1.y]
					b.squares[pos1.x][pos1.y] = EmptySquare{}
					return true
				}
			}
		}
	}
	return false
}

// King - The piece to protect
type King struct {
	Piece
	*Movable
}

func (k King) String() string {
	if k.sd == WHITE {
		return " ♔ "
	}
	return " ♚ "
}

// PieceKind of KING
func (k King) PieceKind() PieceKind { return KING }

// Move in the King's way
func (k King) Move(b *Board, pos1, pos2 Position) bool {
	s1 := b.squares[pos1.x][pos2.y]

	diff := (pos1.x - pos2.x) * (pos1.x - pos2.x)
	if diff >= -1 && diff <= 1 && !pos1.Equal(pos2) {
		s2 := b.squares[pos2.x][pos2.y]
		if s1.Side() == s2.Side() {
			return false
		}
		b.squares[pos2.x][pos2.y] = b.squares[pos1.x][pos1.y]
		b.squares[pos1.x][pos1.y] = EmptySquare{}
		movedKing := b.squares[pos2.x][pos2.y].(King)
		movedKing.moved = true
		return true
	}
	return false
}

// EmptySquare - aka no piece on the board
type EmptySquare struct {
	Piece
}

// PieceKind of KING
func (es EmptySquare) PieceKind() PieceKind { return EMPTY_SQUARE }

// Move always returns false if the squre is empty
func (es EmptySquare) Move(b *Board, pos1, pos2 Position) bool { return false }
<|endoftext|>


<commit_before>package main

import (
	"errors"
	"fmt"
)

type player struct {
	LOS         map[position]bool
	Rays        rayMap
	Pos         position
	HP          int
	MP          int
	Consumables map[consumable]int
	Simellas    int
	Target      position
	Statuses    map[status]int
	Armour      armour
	Weapon      weapon
	Shield      shield
	Aptitudes   map[aptitude]bool
	Rods        map[rod]*rodProps
}

func (p *player) HPMax() int {
	hpmax := 40
	if p.Aptitudes[AptHealthy] {
		hpmax += 10
	}
	return hpmax
}

func (p *player) MPMax() int {
	mpmax := 10
	if p.Aptitudes[AptMagic] {
		mpmax += 5
	}
	return mpmax
}

func (p *player) Accuracy() int {
	acc := 15
	if p.Aptitudes[AptAccurate] {
		acc += 2
	}
	return acc
}

func (p *player) RangedAccuracy() int {
	acc := 15
	if p.Aptitudes[AptAccurate] {
		acc += 10
	}
	return acc
}

func (p *player) Armor() int {
	ar := 0
	switch p.Armour {
	case LeatherArmour:
		ar += 3
	case ChainMail:
		ar += 4
	case PlateArmour:
		ar += 6
	}
	if p.Aptitudes[AptScales] {
		ar += 2
	}
	if p.HasStatus(StatusLignification) {
		ar = 9 + ar/2
	}
	if p.HasStatus(StatusCorrosion) {
		ar -= 2 * p.Statuses[StatusCorrosion]
		if ar < 0 {
			ar = 0
		}
	}
	return ar
}

func (p *player) Attack() int {
	attack := p.Weapon.Attack()
	if p.Aptitudes[AptStrong] {
		attack += attack / 5
	}
	if p.HasStatus(StatusCorrosion) {
		penalty := p.Statuses[StatusCorrosion]
		if penalty > 5 {
			penalty = 5
		}
		attack -= penalty
	}
	return attack
}

func (p *player) Block() int {
	block := p.Shield.Block()
	if p.HasStatus(StatusDisabledShield) {
		block /= 3
	}
	return block
}

func (p *player) Evasion() int {
	ev := 15
	if p.Aptitudes[AptAgile] {
		ev += 3
	}
	if p.HasStatus(StatusAgile) {
		ev += 7
	}
	return ev
}

func (p *player) HasStatus(st status) bool {
	return p.Statuses[st] > 0
}

func (p *player) AptitudeCount() int {
	count := 0
	for _, b := range p.Aptitudes {
		if b {
			count++
		}
	}
	return count
}

func (g *game) AutoToDir(ev event) bool {
	if g.MonsterInLOS() == nil {
		err := g.MovePlayer(g.Player.Pos.To(*g.AutoDir), ev)
		if err != nil {
			g.Print(err.Error())
			g.AutoDir = nil
			return false
		}
		return true
	}
	g.AutoDir = nil
	return false
}

func (g *game) GoToDir(dir direction, ev event) error {
	if g.MonsterInLOS() != nil {
		g.AutoDir = nil
		return errors.New("You cannot travel while there are monsters in view.")
	}
	err := g.MovePlayer(g.Player.Pos.To(dir), ev)
	if err != nil {
		return err
	}
	g.AutoDir = &dir
	return nil
}

func (g *game) MoveToTarget(ev event) bool {
	if g.AutoTarget != nil {
		path := g.PlayerPath(g.Player.Pos, *g.AutoTarget)
		if g.MonsterInLOS() != nil {
			g.AutoTarget = nil
		}
		if len(path) >= 1 {
			var err error
			if len(path) > 1 {
				err = g.MovePlayer(path[len(path)-2], ev)
			} else {
				g.WaitTurn(ev)
			}
			if err != nil {
				g.Print(err.Error())
				g.AutoTarget = nil
				return false
			} else if g.AutoTarget != nil && g.Player.Pos == *g.AutoTarget {
				g.AutoTarget = nil
			}
			return true
		}
	}
	g.AutoTarget = nil
	return false
}

func (g *game) WaitTurn(ev event) {
	// XXX Really wait for 10 ?
	g.ScummingAction(ev)
	ev.Renew(g, 10)
}

func (g *game) ExistsMonster() bool {
	for _, mons := range g.Monsters {
		if mons.Exists() {
			return true
		}
	}
	return false
}

func (g *game) ScummingAction(ev event) {
	if g.Player.HP == g.Player.HPMax() && g.Player.MP == g.Player.MPMax() {
		g.Scumming++
	}
	if g.Scumming == 100 {
		if g.ExistsMonster() {
			g.PrintStyled("You feel a little bored.", logCritic)
			g.StopAuto()
		}
	}
	if g.Scumming > 120 {
		if !g.ExistsMonster() {
			g.Scumming = 0
			return
		}
		g.Player.HP = g.Player.HP / 2
		if RandInt(2) == 0 {
			g.MakeNoise(100, g.Player.Pos)
			neighbors := g.Player.Pos.ValidNeighbors()
			for _, pos := range neighbors {
				if RandInt(3) != 0 {
					g.Dungeon.SetCell(pos, FreeCell)
					g.ui.WallExplosionAnimation(g, pos)
					g.Fog(pos, 1, ev)
				}
			}
			g.PrintfStyled("%s An explosion comes from the ground. You are lignified.", logCritic, g.ExplosionSound())
			g.Player.Statuses[StatusLignification]++
			g.PushEvent(&simpleEvent{ERank: ev.Rank() + 240 + RandInt(10), EAction: LignificationEnd})
		} else {
			delay := 20 + RandInt(5)
			g.Player.Statuses[StatusTele] = 1
			g.PushEvent(&simpleEvent{ERank: ev.Rank() + delay, EAction: Teleportation})
			g.PrintStyled("Something hurt you! You feel unstable.", logCritic)
		}
		g.Scumming = 0
		g.StopAuto()
	}
}

func (g *game) FairAction() {
	g.Scumming -= 10
	if g.Scumming < 0 {
		g.Scumming = 0
	}
}

func (g *game) Rest(ev event) error {
	if g.MonsterInLOS() != nil {
		return fmt.Errorf("You cannot sleep while monsters are in view.")
	}
	if g.Player.HP == g.Player.HPMax() && g.Player.MP == g.Player.MPMax() && !g.Player.HasStatus(StatusExhausted) &&
		!g.Player.HasStatus(StatusConfusion) && !g.Player.HasStatus(StatusLignification) {
		return errors.New("You do not need to rest.")
	}
	g.WaitTurn(ev)
	g.Resting = true
	return nil
}

func (g *game) Equip(ev event) error {
	if eq, ok := g.Equipables[g.Player.Pos]; ok {
		eq.Equip(g)
		ev.Renew(g, 10)
		return nil
	}
	return errors.New("Found nothing to equip here.")
}

func (g *game) Teleportation(ev event) {
	var pos position
	i := 0
	count := 0
	for {
		count++
		if count > 1000 {
			panic("Teleportation")
		}
		pos = g.FreeCell()
		if pos.Distance(g.Player.Pos) < 15 && i < 1000 {
			i++
			continue
		}
		break

	}
	if pos.valid() {
		// should always happen
		opos := g.Player.Pos
		g.Player.Pos = pos
		g.Print("You feel yourself teleported away.")
		g.ui.TeleportAnimation(g, opos, pos, true)
		g.CollectGround()
		g.ComputeLOS()
		g.MakeMonstersAware()
	} else {
		g.Print("Something went wrong with the teleportation.")
	}
}

func (g *game) CollectGround() {
	pos := g.Player.Pos
	if g.Simellas[pos] > 0 {
		g.Player.Simellas += g.Simellas[pos]
		if g.Simellas[pos] == 1 {
			g.Print("You pick up a simella.")
		} else {
			g.Printf("You pick up %d simellas.", g.Simellas[pos])
		}
		g.DijkstraMapRebuild = true
		delete(g.Simellas, pos)
	}
	if c, ok := g.Collectables[pos]; ok && c != nil {
		g.Player.Consumables[c.Consumable] += c.Quantity
		g.DijkstraMapRebuild = true
		delete(g.Collectables, pos)
		if c.Quantity > 1 {
			g.Printf("You take %d %s.", c.Quantity, c.Consumable.Plural())
		} else {
			g.Printf("You take %s.", Indefinite(c.Consumable.String(), false))
		}
	}
	if r, ok := g.Rods[pos]; ok {
		g.Player.Rods[r] = &rodProps{Charge: r.MaxCharge() - 1}
		g.DijkstraMapRebuild = true
		delete(g.Rods, pos)
		g.Printf("You take a %s.", r)
		g.StoryPrintf("You found and took a %s.", r)
	}
	if eq, ok := g.Equipables[pos]; ok {
		g.Printf("You stand over %s.", Indefinite(eq.String(), false))
	} else if g.Stairs[pos] {
		g.Print("You stand over stairs.")
	} else if g.Doors[pos] {
		g.Print("You stand at the door.")
	}
}

func (g *game) MovePlayer(pos position, ev event) error {
	c := g.Dungeon.Cell(pos)
	if !pos.valid() || c.T == WallCell && !g.Player.HasStatus(StatusDig) {
		return errors.New("You cannot move there.")
	}
	if g.Player.HasStatus(StatusConfusion) {
		switch pos.Dir(g.Player.Pos) {
		case E, N, W, S:
		default:
			return errors.New("You cannot use diagonal movements while confused.")
		}
	}
	delay := 10
	mons, _ := g.MonsterAt(pos)
	if !mons.Exists() {
		if g.Player.HasStatus(StatusLignification) {
			return errors.New("You cannot move while lignified")
		}
		if c.T == WallCell {
			g.Dungeon.SetCell(pos, FreeCell)
			g.MakeNoise(18, pos)
			g.Print(g.CrackSound())
			g.Fog(pos, 1, ev)
		}
		g.Player.Pos = pos
		g.CollectGround()
		g.ComputeLOS()
		if !g.Autoexploring {
			g.ScummingAction(ev)
		}
		g.MakeMonstersAware()
		if g.Player.Aptitudes[AptFast] {
			// only fast for movement
			delay -= 2
		}
		if g.Player.HasStatus(StatusSwift) {
			// only fast for movement
			if g.Player.HasStatus(StatusBerserk) {
				delay -= 2
			} else {
				delay -= 3
			}
		}
	} else {
		g.FairAction()
		g.AttackMonster(mons, ev)
	}
	if g.Player.HasStatus(StatusBerserk) {
		delay -= 3
	}
	if g.Player.HasStatus(StatusSlow) {
		delay += 3
	}
	ev.Renew(g, delay)
	return nil
}

func (g *game) HealPlayer(ev event) {
	if g.Player.HP < g.Player.HPMax() {
		g.Player.HP++
	}
	delay := 50
	ev.Renew(g, delay)
}

func (g *game) MPRegen(ev event) {
	if g.Player.MP < g.Player.MPMax() {
		g.Player.MP++
	}
	delay := 100
	ev.Renew(g, delay)
}

func (g *game) Smoke(ev event) {
	dij := &normalPath{game: g}
	nm := Dijkstra(dij, []position{g.Player.Pos}, 2)
	for pos := range nm {
		_, ok := g.Clouds[pos]
		if !ok {
			g.Clouds[pos] = CloudFog
			g.PushEvent(&cloudEvent{ERank: ev.Rank() + 100 + RandInt(100), EAction: CloudEnd, Pos: pos})
		}
	}
	g.Player.Statuses[StatusSwift]++
	g.PushEvent(&simpleEvent{ERank: ev.Rank() + 20 + RandInt(10), EAction: HasteEnd})
	g.ComputeLOS()
	g.Print("You feel an energy burst and smoking coming out from you.")
}

func (g *game) Corrosion(ev event) {
	g.Player.Statuses[StatusCorrosion]++
	g.PushEvent(&simpleEvent{ERank: ev.Rank() + 80 + RandInt(40), EAction: CorrosionEnd})
	g.Print("Your equipment is corroded.")
}

func (g *game) Confusion(ev event) {
	if !g.Player.HasStatus(StatusConfusion) {
		g.Player.Statuses[StatusConfusion]++
		g.PushEvent(&simpleEvent{ERank: ev.Rank() + 100 + RandInt(100), EAction: ConfusionEnd})
		g.Print("You feel confused.")
	}
}
<commit_msg>fix regression crash caused by the dig potion stuff<commit_after>package main

import (
	"errors"
	"fmt"
)

type player struct {
	LOS         map[position]bool
	Rays        rayMap
	Pos         position
	HP          int
	MP          int
	Consumables map[consumable]int
	Simellas    int
	Target      position
	Statuses    map[status]int
	Armour      armour
	Weapon      weapon
	Shield      shield
	Aptitudes   map[aptitude]bool
	Rods        map[rod]*rodProps
}

func (p *player) HPMax() int {
	hpmax := 40
	if p.Aptitudes[AptHealthy] {
		hpmax += 10
	}
	return hpmax
}

func (p *player) MPMax() int {
	mpmax := 10
	if p.Aptitudes[AptMagic] {
		mpmax += 5
	}
	return mpmax
}

func (p *player) Accuracy() int {
	acc := 15
	if p.Aptitudes[AptAccurate] {
		acc += 2
	}
	return acc
}

func (p *player) RangedAccuracy() int {
	acc := 15
	if p.Aptitudes[AptAccurate] {
		acc += 10
	}
	return acc
}

func (p *player) Armor() int {
	ar := 0
	switch p.Armour {
	case LeatherArmour:
		ar += 3
	case ChainMail:
		ar += 4
	case PlateArmour:
		ar += 6
	}
	if p.Aptitudes[AptScales] {
		ar += 2
	}
	if p.HasStatus(StatusLignification) {
		ar = 9 + ar/2
	}
	if p.HasStatus(StatusCorrosion) {
		ar -= 2 * p.Statuses[StatusCorrosion]
		if ar < 0 {
			ar = 0
		}
	}
	return ar
}

func (p *player) Attack() int {
	attack := p.Weapon.Attack()
	if p.Aptitudes[AptStrong] {
		attack += attack / 5
	}
	if p.HasStatus(StatusCorrosion) {
		penalty := p.Statuses[StatusCorrosion]
		if penalty > 5 {
			penalty = 5
		}
		attack -= penalty
	}
	return attack
}

func (p *player) Block() int {
	block := p.Shield.Block()
	if p.HasStatus(StatusDisabledShield) {
		block /= 3
	}
	return block
}

func (p *player) Evasion() int {
	ev := 15
	if p.Aptitudes[AptAgile] {
		ev += 3
	}
	if p.HasStatus(StatusAgile) {
		ev += 7
	}
	return ev
}

func (p *player) HasStatus(st status) bool {
	return p.Statuses[st] > 0
}

func (p *player) AptitudeCount() int {
	count := 0
	for _, b := range p.Aptitudes {
		if b {
			count++
		}
	}
	return count
}

func (g *game) AutoToDir(ev event) bool {
	if g.MonsterInLOS() == nil {
		err := g.MovePlayer(g.Player.Pos.To(*g.AutoDir), ev)
		if err != nil {
			g.Print(err.Error())
			g.AutoDir = nil
			return false
		}
		return true
	}
	g.AutoDir = nil
	return false
}

func (g *game) GoToDir(dir direction, ev event) error {
	if g.MonsterInLOS() != nil {
		g.AutoDir = nil
		return errors.New("You cannot travel while there are monsters in view.")
	}
	err := g.MovePlayer(g.Player.Pos.To(dir), ev)
	if err != nil {
		return err
	}
	g.AutoDir = &dir
	return nil
}

func (g *game) MoveToTarget(ev event) bool {
	if g.AutoTarget != nil {
		path := g.PlayerPath(g.Player.Pos, *g.AutoTarget)
		if g.MonsterInLOS() != nil {
			g.AutoTarget = nil
		}
		if len(path) >= 1 {
			var err error
			if len(path) > 1 {
				err = g.MovePlayer(path[len(path)-2], ev)
			} else {
				g.WaitTurn(ev)
			}
			if err != nil {
				g.Print(err.Error())
				g.AutoTarget = nil
				return false
			} else if g.AutoTarget != nil && g.Player.Pos == *g.AutoTarget {
				g.AutoTarget = nil
			}
			return true
		}
	}
	g.AutoTarget = nil
	return false
}

func (g *game) WaitTurn(ev event) {
	// XXX Really wait for 10 ?
	g.ScummingAction(ev)
	ev.Renew(g, 10)
}

func (g *game) ExistsMonster() bool {
	for _, mons := range g.Monsters {
		if mons.Exists() {
			return true
		}
	}
	return false
}

func (g *game) ScummingAction(ev event) {
	if g.Player.HP == g.Player.HPMax() && g.Player.MP == g.Player.MPMax() {
		g.Scumming++
	}
	if g.Scumming == 100 {
		if g.ExistsMonster() {
			g.PrintStyled("You feel a little bored.", logCritic)
			g.StopAuto()
		}
	}
	if g.Scumming > 120 {
		if !g.ExistsMonster() {
			g.Scumming = 0
			return
		}
		g.Player.HP = g.Player.HP / 2
		if RandInt(2) == 0 {
			g.MakeNoise(100, g.Player.Pos)
			neighbors := g.Player.Pos.ValidNeighbors()
			for _, pos := range neighbors {
				if RandInt(3) != 0 {
					g.Dungeon.SetCell(pos, FreeCell)
					g.ui.WallExplosionAnimation(g, pos)
					g.Fog(pos, 1, ev)
				}
			}
			g.PrintfStyled("%s An explosion comes from the ground. You are lignified.", logCritic, g.ExplosionSound())
			g.Player.Statuses[StatusLignification]++
			g.PushEvent(&simpleEvent{ERank: ev.Rank() + 240 + RandInt(10), EAction: LignificationEnd})
		} else {
			delay := 20 + RandInt(5)
			g.Player.Statuses[StatusTele] = 1
			g.PushEvent(&simpleEvent{ERank: ev.Rank() + delay, EAction: Teleportation})
			g.PrintStyled("Something hurt you! You feel unstable.", logCritic)
		}
		g.Scumming = 0
		g.StopAuto()
	}
}

func (g *game) FairAction() {
	g.Scumming -= 10
	if g.Scumming < 0 {
		g.Scumming = 0
	}
}

func (g *game) Rest(ev event) error {
	if g.MonsterInLOS() != nil {
		return fmt.Errorf("You cannot sleep while monsters are in view.")
	}
	if g.Player.HP == g.Player.HPMax() && g.Player.MP == g.Player.MPMax() && !g.Player.HasStatus(StatusExhausted) &&
		!g.Player.HasStatus(StatusConfusion) && !g.Player.HasStatus(StatusLignification) {
		return errors.New("You do not need to rest.")
	}
	g.WaitTurn(ev)
	g.Resting = true
	return nil
}

func (g *game) Equip(ev event) error {
	if eq, ok := g.Equipables[g.Player.Pos]; ok {
		eq.Equip(g)
		ev.Renew(g, 10)
		return nil
	}
	return errors.New("Found nothing to equip here.")
}

func (g *game) Teleportation(ev event) {
	var pos position
	i := 0
	count := 0
	for {
		count++
		if count > 1000 {
			panic("Teleportation")
		}
		pos = g.FreeCell()
		if pos.Distance(g.Player.Pos) < 15 && i < 1000 {
			i++
			continue
		}
		break

	}
	if pos.valid() {
		// should always happen
		opos := g.Player.Pos
		g.Player.Pos = pos
		g.Print("You feel yourself teleported away.")
		g.ui.TeleportAnimation(g, opos, pos, true)
		g.CollectGround()
		g.ComputeLOS()
		g.MakeMonstersAware()
	} else {
		g.Print("Something went wrong with the teleportation.")
	}
}

func (g *game) CollectGround() {
	pos := g.Player.Pos
	if g.Simellas[pos] > 0 {
		g.Player.Simellas += g.Simellas[pos]
		if g.Simellas[pos] == 1 {
			g.Print("You pick up a simella.")
		} else {
			g.Printf("You pick up %d simellas.", g.Simellas[pos])
		}
		g.DijkstraMapRebuild = true
		delete(g.Simellas, pos)
	}
	if c, ok := g.Collectables[pos]; ok && c != nil {
		g.Player.Consumables[c.Consumable] += c.Quantity
		g.DijkstraMapRebuild = true
		delete(g.Collectables, pos)
		if c.Quantity > 1 {
			g.Printf("You take %d %s.", c.Quantity, c.Consumable.Plural())
		} else {
			g.Printf("You take %s.", Indefinite(c.Consumable.String(), false))
		}
	}
	if r, ok := g.Rods[pos]; ok {
		g.Player.Rods[r] = &rodProps{Charge: r.MaxCharge() - 1}
		g.DijkstraMapRebuild = true
		delete(g.Rods, pos)
		g.Printf("You take a %s.", r)
		g.StoryPrintf("You found and took a %s.", r)
	}
	if eq, ok := g.Equipables[pos]; ok {
		g.Printf("You stand over %s.", Indefinite(eq.String(), false))
	} else if g.Stairs[pos] {
		g.Print("You stand over stairs.")
	} else if g.Doors[pos] {
		g.Print("You stand at the door.")
	}
}

func (g *game) MovePlayer(pos position, ev event) error {
	if !pos.valid() {
		return errors.New("You cannot move there.")
	}
	c := g.Dungeon.Cell(pos)
	if c.T == WallCell && !g.Player.HasStatus(StatusDig) {
		return errors.New("You cannot move into a wall.")
	}
	if g.Player.HasStatus(StatusConfusion) {
		switch pos.Dir(g.Player.Pos) {
		case E, N, W, S:
		default:
			return errors.New("You cannot use diagonal movements while confused.")
		}
	}
	delay := 10
	mons, _ := g.MonsterAt(pos)
	if !mons.Exists() {
		if g.Player.HasStatus(StatusLignification) {
			return errors.New("You cannot move while lignified")
		}
		if c.T == WallCell {
			g.Dungeon.SetCell(pos, FreeCell)
			g.MakeNoise(18, pos)
			g.Print(g.CrackSound())
			g.Fog(pos, 1, ev)
		}
		g.Player.Pos = pos
		g.CollectGround()
		g.ComputeLOS()
		if !g.Autoexploring {
			g.ScummingAction(ev)
		}
		g.MakeMonstersAware()
		if g.Player.Aptitudes[AptFast] {
			// only fast for movement
			delay -= 2
		}
		if g.Player.HasStatus(StatusSwift) {
			// only fast for movement
			if g.Player.HasStatus(StatusBerserk) {
				delay -= 2
			} else {
				delay -= 3
			}
		}
	} else {
		g.FairAction()
		g.AttackMonster(mons, ev)
	}
	if g.Player.HasStatus(StatusBerserk) {
		delay -= 3
	}
	if g.Player.HasStatus(StatusSlow) {
		delay += 3
	}
	ev.Renew(g, delay)
	return nil
}

func (g *game) HealPlayer(ev event) {
	if g.Player.HP < g.Player.HPMax() {
		g.Player.HP++
	}
	delay := 50
	ev.Renew(g, delay)
}

func (g *game) MPRegen(ev event) {
	if g.Player.MP < g.Player.MPMax() {
		g.Player.MP++
	}
	delay := 100
	ev.Renew(g, delay)
}

func (g *game) Smoke(ev event) {
	dij := &normalPath{game: g}
	nm := Dijkstra(dij, []position{g.Player.Pos}, 2)
	for pos := range nm {
		_, ok := g.Clouds[pos]
		if !ok {
			g.Clouds[pos] = CloudFog
			g.PushEvent(&cloudEvent{ERank: ev.Rank() + 100 + RandInt(100), EAction: CloudEnd, Pos: pos})
		}
	}
	g.Player.Statuses[StatusSwift]++
	g.PushEvent(&simpleEvent{ERank: ev.Rank() + 20 + RandInt(10), EAction: HasteEnd})
	g.ComputeLOS()
	g.Print("You feel an energy burst and smoking coming out from you.")
}

func (g *game) Corrosion(ev event) {
	g.Player.Statuses[StatusCorrosion]++
	g.PushEvent(&simpleEvent{ERank: ev.Rank() + 80 + RandInt(40), EAction: CorrosionEnd})
	g.Print("Your equipment is corroded.")
}

func (g *game) Confusion(ev event) {
	if !g.Player.HasStatus(StatusConfusion) {
		g.Player.Statuses[StatusConfusion]++
		g.PushEvent(&simpleEvent{ERank: ev.Rank() + 100 + RandInt(100), EAction: ConfusionEnd})
		g.Print("You feel confused.")
	}
}
<|endoftext|>


<commit_before>package main

import (
	//"bufio"
	"fmt"
	"log"
	"strings"
	"text/scanner"
)

type CmdFunc func(root *CmdNode, c *TelnetClient, line string)

type CmdNode struct {
	Path     string
	Desc     string
	MinLevel int
	Handler  CmdFunc
	Children []*CmdNode
}

func lastToken(path string) string {
	// fixme with tokenizer
	f := strings.Fields(path)
	return f[len(f)-1]
}

/*
func dumpChildren(node *CmdNode) string {
	str := ""
	for _, p := range node.Children {
		str += fmt.Sprintf(",%s", p.Path)
	}
	return str
}
*/

func pushChild(node, child *CmdNode) {
	//log.Printf("pushChild: parent=[%s] child=[%s] before: [%v]", node.Path, child.Path, dumpChildren(node))
	node.Children = append(node.Children, child)
	//log.Printf("pushChild: parent=[%s] child=[%s] after: [%v]", node.Path, child.Path, dumpChildren(node))
}

func cmdInstall(root *CmdNode, path string, min int, cmd CmdFunc, desc string) {
	if err := cmdAdd(root, path, min, cmd, desc); err != nil {
		log.Printf("cmdInstall: error %s", err)
	}
}

func cmdAdd(root *CmdNode, path string, min int, cmd CmdFunc, desc string) error {
	//log.Printf("cmdInstall: [%s]", path)

	labelList := strings.Fields(path)
	size := len(labelList)
	parent := root
	for i, label := range labelList {
		currPath := strings.Join(labelList[:i+1], " ")
		//log.Printf("cmdInstall: %d: curr=[%s] label=[%s]", i, currPath, label)
		child := findChild(parent, label)
		if child != nil {
			// found, search next
			//log.Printf("cmdInstall: found [%s]", currPath)
			parent = child
			continue
		}

		// not found

		//log.Printf("cmdInstall: new [%s]", currPath)

		for ; i < size-1; i++ {
			// intermmediate label
			label = labelList[i]
			currPath = strings.Join(labelList[:i+1], " ")
			//log.Printf("cmdInstall: %d: intermmediate curr=[%s] label=[%s]", i, currPath, label)
			newNode := &CmdNode{Path: currPath, MinLevel: EXEC}
			pushChild(parent, newNode)
			parent = newNode
		}

		// last label
		label = labelList[size-1]
		//log.Printf("cmdInstall: %d: leaf curr=[%s] label=[%s]", i, path, label)
		newNode := &CmdNode{Path: path, Desc: desc, MinLevel: min, Handler: cmd}
		pushChild(parent, newNode)

		return nil
	}

	// command node found

	return fmt.Errorf("[%s] already exists", path)
}

func findChild(node *CmdNode, label string) *CmdNode {

	for _, c := range node.Children {
		last := lastToken(c.Path)
		//log.Printf("findChild: searching [%s] against (%s)[%s] under [%s]", label, last, c.Path, node.Path)
		if label == last {
			//log.Printf("findChild: found [%s] as [%s] under [%s]", label, c.Path, node.Path)
			return c
		}
	}

	//log.Printf("findChild: not found [%s] under [%s]", label, node.Path)

	return nil
}

func matchChildren(children []*CmdNode, label string) []*CmdNode {
	c := []*CmdNode{}

	for _, n := range children {
		last := lastToken(n.Path)
		if strings.HasPrefix(last, label) {
			c = append(c, n)
		}
	}

	return c
}

func cmdFind(root *CmdNode, path string, level int) (*CmdNode, error) {

	var s scanner.Scanner
	s.Error = func(s *scanner.Scanner, msg string) {
		log.Printf("command scan error: %s [%s]", msg, path)
	}
	s.Init(strings.NewReader(path))

	parent := root
	for tok := s.Scan(); tok != scanner.EOF; tok = s.Scan() {
		//log.Printf("cmdFind: token: [%s]", s.TokenText())
		label := s.TokenText()
		children := matchChildren(parent.Children, label)
		size := len(children)
		if size < 1 {
			return nil, fmt.Errorf("cmdFind: not found: [%s] under [%s]", label, parent.Path)
		}
		if size > 1 {
			return nil, fmt.Errorf("cmdFind: ambiguous: [%s] under [%s]", label, parent.Path)
		}
		parent = children[0]
	}

	//log.Printf("cmdFind: found [%s] as [%s]", path, parent.Path)

	return parent, nil
}
<commit_msg>Useless assignments.<commit_after>package main

import (
	//"bufio"
	"fmt"
	"log"
	"strings"
	"text/scanner"
)

type CmdFunc func(root *CmdNode, c *TelnetClient, line string)

type CmdNode struct {
	Path     string
	Desc     string
	MinLevel int
	Handler  CmdFunc
	Children []*CmdNode
}

func lastToken(path string) string {
	// fixme with tokenizer
	f := strings.Fields(path)
	return f[len(f)-1]
}

/*
func dumpChildren(node *CmdNode) string {
	str := ""
	for _, p := range node.Children {
		str += fmt.Sprintf(",%s", p.Path)
	}
	return str
}
*/

func pushChild(node, child *CmdNode) {
	//log.Printf("pushChild: parent=[%s] child=[%s] before: [%v]", node.Path, child.Path, dumpChildren(node))
	node.Children = append(node.Children, child)
	//log.Printf("pushChild: parent=[%s] child=[%s] after: [%v]", node.Path, child.Path, dumpChildren(node))
}

func cmdInstall(root *CmdNode, path string, min int, cmd CmdFunc, desc string) {
	if err := cmdAdd(root, path, min, cmd, desc); err != nil {
		log.Printf("cmdInstall: error %s", err)
	}
}

func cmdAdd(root *CmdNode, path string, min int, cmd CmdFunc, desc string) error {
	//log.Printf("cmdInstall: [%s]", path)

	labelList := strings.Fields(path)
	size := len(labelList)
	parent := root
	for i, label := range labelList {
		currPath := strings.Join(labelList[:i+1], " ")
		//log.Printf("cmdInstall: %d: curr=[%s] label=[%s]", i, currPath, label)
		child := findChild(parent, label)
		if child != nil {
			// found, search next
			//log.Printf("cmdInstall: found [%s]", currPath)
			parent = child
			continue
		}

		// not found

		//log.Printf("cmdInstall: new [%s]", currPath)

		for ; i < size-1; i++ {
			// handle intermmediate label
			label = labelList[i]
			currPath = strings.Join(labelList[:i+1], " ")
			//log.Printf("cmdInstall: %d: intermmediate curr=[%s] label=[%s]", i, currPath, label)
			newNode := &CmdNode{Path: currPath, MinLevel: EXEC}
			pushChild(parent, newNode)
			parent = newNode
		}

		// handle last label
		//label = labelList[size-1]
		//log.Printf("cmdInstall: %d: leaf curr=[%s] label=[%s]", i, path, label)
		newNode := &CmdNode{Path: path, Desc: desc, MinLevel: min, Handler: cmd}
		pushChild(parent, newNode)

		return nil
	}

	// command node found

	return fmt.Errorf("[%s] already exists", path)
}

func findChild(node *CmdNode, label string) *CmdNode {

	for _, c := range node.Children {
		last := lastToken(c.Path)
		//log.Printf("findChild: searching [%s] against (%s)[%s] under [%s]", label, last, c.Path, node.Path)
		if label == last {
			//log.Printf("findChild: found [%s] as [%s] under [%s]", label, c.Path, node.Path)
			return c
		}
	}

	//log.Printf("findChild: not found [%s] under [%s]", label, node.Path)

	return nil
}

func matchChildren(children []*CmdNode, label string) []*CmdNode {
	c := []*CmdNode{}

	for _, n := range children {
		last := lastToken(n.Path)
		if strings.HasPrefix(last, label) {
			c = append(c, n)
		}
	}

	return c
}

func cmdFind(root *CmdNode, path string, level int) (*CmdNode, error) {

	var s scanner.Scanner
	s.Error = func(s *scanner.Scanner, msg string) {
		log.Printf("command scan error: %s [%s]", msg, path)
	}
	s.Init(strings.NewReader(path))

	parent := root
	for tok := s.Scan(); tok != scanner.EOF; tok = s.Scan() {
		//log.Printf("cmdFind: token: [%s]", s.TokenText())
		label := s.TokenText()
		children := matchChildren(parent.Children, label)
		size := len(children)
		if size < 1 {
			return nil, fmt.Errorf("cmdFind: not found: [%s] under [%s]", label, parent.Path)
		}
		if size > 1 {
			return nil, fmt.Errorf("cmdFind: ambiguous: [%s] under [%s]", label, parent.Path)
		}
		parent = children[0]
	}

	//log.Printf("cmdFind: found [%s] as [%s]", path, parent.Path)

	return parent, nil
}
<|endoftext|>


<commit_before>// Licensed to the Apache Software Foundation (ASF) under one or more
// contributor license agreements.  See the NOTICE file distributed with
// this work for additional information regarding copyright ownership.
// The ASF licenses this file to You 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.

package main

import (
	"bufio"
	"bytes"
	"errors"
	"fmt"
	"io/ioutil"
	"log"
	"os"
	"path/filepath"
	"strings"

	"github.com/apache/beam/sdks/go/pkg/beam/util/execx"
)

const (
	pip = "/usr/local/bin/pip"
)

// pipInstallRequirements installs the given requirement, if present.
func pipInstallRequirements(files []string, dir, name string) error {
	for _, file := range files {
		if file == name {
			// We run the install process in two rounds in order to avoid as much
			// as possible PyPI downloads. In the first round the --find-links
			// option will make sure that only things staged in the worker will be
			// used without following their dependencies.
			args := []string{"install", "-r", filepath.Join(dir, name), "--no-index", "--no-deps", "--find-links", dir}
			if err := execx.Execute(pip, args...); err != nil {
				return err
			}
			// The second install round opens up the search for packages on PyPI and
			// also installs dependencies. The key is that if all the packages have
			// been installed in the first round then this command will be a no-op.
			args = []string{"install", "-r", filepath.Join(dir, name), "--find-links", dir}
			return execx.Execute(pip, args...)
		}
	}
	return nil
}

// pipInstallPackage installs the given package, if present.
func pipInstallPackage(files []string, dir, name string, force, optional bool, extras []string) error {
	for _, file := range files {
		if file == name {
			var packageSpec = name
			if extras != nil {
				packageSpec += "[" + strings.Join(extras, ",") + "]"
			}
			if force {
				// We only use force reinstallation for packages specified using the
				// --extra_package flag.  In this case, we always want to use the
				// user-specified package, overwriting any existing package already
				// installed.  At the same time, we want to avoid reinstalling any
				// dependencies.  The "pip install" command doesn't have a clean way to do
				// this, so we do this in two steps.
				//
				// First, we use the three flags "--upgrade --force-reinstall --no-deps"
				// to "pip install" so as to force the package to be reinstalled, while
				// avoiding reinstallation of dependencies.  Note now that if any needed
				// dependencies were not installed, they will still be missing.
				//
				// Next, we run "pip install" on the package without any flags.  Since the
				// installed version will match the package specified, the package itself
				// will not be reinstalled, but its dependencies will now be resolved and
				// installed if necessary.  This achieves our goal outlined above.
				args := []string{"install", "--upgrade", "--force-reinstall", "--no-deps",
					filepath.Join(dir, packageSpec)}
				err := execx.Execute(pip, args...)
				if err != nil {
					return err
				}
				args = []string{"install", filepath.Join(dir, packageSpec)}
				return execx.Execute(pip, args...)
			}

			// Case when we do not perform a forced reinstall.
			args := []string{"install", filepath.Join(dir, packageSpec)}
			return execx.Execute(pip, args...)
		}
	}
	if optional {
		return nil
	}
	return errors.New("package '" + name + "' not found")
}

// installExtraPackages installs all the packages declared in the extra
// packages manifest file.
func installExtraPackages(files []string, extraPackagesFile, dir string) error {
	// First check that extra packages manifest file is present.
	for _, file := range files {
		if file != extraPackagesFile {
			continue
		}

		// Found the manifest. Install extra packages.
		manifest, err := ioutil.ReadFile(filepath.Join(dir, extraPackagesFile))
		if err != nil {
			return fmt.Errorf("failed to read extra packages manifest file: %v", err)
		}

		s := bufio.NewScanner(bytes.NewReader(manifest))
		s.Split(bufio.ScanLines)

		for s.Scan() {
			extraPackage := s.Text()
			log.Printf("Installing extra package: %s", extraPackage)
			if err = pipInstallPackage(files, dir, extraPackage, true, false, nil); err != nil {
				return fmt.Errorf("failed to install extra package %s: %v", extraPackage, err)
			}
		}
		return nil
	}
	return nil
}

func findBeamSdkWhl(files []string, acceptableWhlSpecs []string) string {
	for _, file := range files {
		if strings.HasPrefix(file, "apache_beam") {
			for _, s := range acceptableWhlSpecs {
				if strings.HasSuffix(file, s) {
					log.Printf("Found Apache Beam SDK wheel: %v", file)
					return file
				}
			}
		}
	}
	return ""
}

// InstallSdk installs Beam SDK: First, we try to find a compiled
// wheel distribution of Apache Beam among staged files. If we find it, we
// assume that the pipleine was started with the Beam SDK found in the wheel
// file, and we try to install it. If not successful, we fall back to installing
// SDK from source tarball provided in sdkSrcFile.
func installSdk(files []string, workDir string, sdkSrcFile string, acceptableWhlSpecs []string, required bool) error {
	sdkWhlFile := findBeamSdkWhl(files, acceptableWhlSpecs)
	if sdkWhlFile != "" {
		err := pipInstallPackage(files, workDir, sdkWhlFile, false, false, []string{"gcp"})
		if err == nil {
			return nil
		}
		log.Printf("Could not install Apache Beam SDK from a wheel: %v, proceeding to install SDK from source tarball.", err)
	}
	if !required {
		_, err := os.Stat(sdkSrcFile)
		if os.IsNotExist(err) {
			return nil
		}
	}
	err := pipInstallPackage(files, workDir, sdkSrcFile, false, false, []string{"gcp"})
	return err
}
<commit_msg>[BEAM-5155] Check sdk absolute path before installing<commit_after>// Licensed to the Apache Software Foundation (ASF) under one or more
// contributor license agreements.  See the NOTICE file distributed with
// this work for additional information regarding copyright ownership.
// The ASF licenses this file to You 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.

package main

import (
	"bufio"
	"bytes"
	"errors"
	"fmt"
	"io/ioutil"
	"log"
	"os"
	"path/filepath"
	"strings"

	"github.com/apache/beam/sdks/go/pkg/beam/util/execx"
)

const (
	pip = "/usr/local/bin/pip"
)

// pipInstallRequirements installs the given requirement, if present.
func pipInstallRequirements(files []string, dir, name string) error {
	for _, file := range files {
		if file == name {
			// We run the install process in two rounds in order to avoid as much
			// as possible PyPI downloads. In the first round the --find-links
			// option will make sure that only things staged in the worker will be
			// used without following their dependencies.
			args := []string{"install", "-r", filepath.Join(dir, name), "--no-index", "--no-deps", "--find-links", dir}
			if err := execx.Execute(pip, args...); err != nil {
				return err
			}
			// The second install round opens up the search for packages on PyPI and
			// also installs dependencies. The key is that if all the packages have
			// been installed in the first round then this command will be a no-op.
			args = []string{"install", "-r", filepath.Join(dir, name), "--find-links", dir}
			return execx.Execute(pip, args...)
		}
	}
	return nil
}

// pipInstallPackage installs the given package, if present.
func pipInstallPackage(files []string, dir, name string, force, optional bool, extras []string) error {
	for _, file := range files {
		if file == name {
			var packageSpec = name
			if extras != nil {
				packageSpec += "[" + strings.Join(extras, ",") + "]"
			}
			if force {
				// We only use force reinstallation for packages specified using the
				// --extra_package flag.  In this case, we always want to use the
				// user-specified package, overwriting any existing package already
				// installed.  At the same time, we want to avoid reinstalling any
				// dependencies.  The "pip install" command doesn't have a clean way to do
				// this, so we do this in two steps.
				//
				// First, we use the three flags "--upgrade --force-reinstall --no-deps"
				// to "pip install" so as to force the package to be reinstalled, while
				// avoiding reinstallation of dependencies.  Note now that if any needed
				// dependencies were not installed, they will still be missing.
				//
				// Next, we run "pip install" on the package without any flags.  Since the
				// installed version will match the package specified, the package itself
				// will not be reinstalled, but its dependencies will now be resolved and
				// installed if necessary.  This achieves our goal outlined above.
				args := []string{"install", "--upgrade", "--force-reinstall", "--no-deps",
					filepath.Join(dir, packageSpec)}
				err := execx.Execute(pip, args...)
				if err != nil {
					return err
				}
				args = []string{"install", filepath.Join(dir, packageSpec)}
				return execx.Execute(pip, args...)
			}

			// Case when we do not perform a forced reinstall.
			args := []string{"install", filepath.Join(dir, packageSpec)}
			return execx.Execute(pip, args...)
		}
	}
	if optional {
		return nil
	}
	return errors.New("package '" + name + "' not found")
}

// installExtraPackages installs all the packages declared in the extra
// packages manifest file.
func installExtraPackages(files []string, extraPackagesFile, dir string) error {
	// First check that extra packages manifest file is present.
	for _, file := range files {
		if file != extraPackagesFile {
			continue
		}

		// Found the manifest. Install extra packages.
		manifest, err := ioutil.ReadFile(filepath.Join(dir, extraPackagesFile))
		if err != nil {
			return fmt.Errorf("failed to read extra packages manifest file: %v", err)
		}

		s := bufio.NewScanner(bytes.NewReader(manifest))
		s.Split(bufio.ScanLines)

		for s.Scan() {
			extraPackage := s.Text()
			log.Printf("Installing extra package: %s", extraPackage)
			if err = pipInstallPackage(files, dir, extraPackage, true, false, nil); err != nil {
				return fmt.Errorf("failed to install extra package %s: %v", extraPackage, err)
			}
		}
		return nil
	}
	return nil
}

func findBeamSdkWhl(files []string, acceptableWhlSpecs []string) string {
	for _, file := range files {
		if strings.HasPrefix(file, "apache_beam") {
			for _, s := range acceptableWhlSpecs {
				if strings.HasSuffix(file, s) {
					log.Printf("Found Apache Beam SDK wheel: %v", file)
					return file
				}
			}
		}
	}
	return ""
}

// InstallSdk installs Beam SDK: First, we try to find a compiled
// wheel distribution of Apache Beam among staged files. If we find it, we
// assume that the pipleine was started with the Beam SDK found in the wheel
// file, and we try to install it. If not successful, we fall back to installing
// SDK from source tarball provided in sdkSrcFile.
func installSdk(files []string, workDir string, sdkSrcFile string, acceptableWhlSpecs []string, required bool) error {
	sdkWhlFile := findBeamSdkWhl(files, acceptableWhlSpecs)
	if sdkWhlFile != "" {
		err := pipInstallPackage(files, workDir, sdkWhlFile, false, false, []string{"gcp"})
		if err == nil {
			return nil
		}
		log.Printf("Could not install Apache Beam SDK from a wheel: %v, proceeding to install SDK from source tarball.", err)
	}
	if !required {
		_, err := os.Stat(filepath.Join(workDir, sdkSrcFile))
		if os.IsNotExist(err) {
			return nil
		}
	}
	err := pipInstallPackage(files, workDir, sdkSrcFile, false, false, []string{"gcp"})
	return err
}
<|endoftext|>


<commit_before>// Copyright 2014 The DevMine Authors. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.

package src

// SourceFile holds information about a source file.
type SourceFile struct {
	// The path of the source file, relative to the root of the project.
	Path string `json:"path"`

	// Programming language used.
	ProgLang *Language `json:"language"`

	// List of the imports used by the srouce file.
	Imports []string `json:"imports"`

	// Types definition
	// TODO rename JSON key into type_definition
	TypeDefs []TypeDef `json:"type_defs"`

	// Structures definition
	// TODO rename JSON key into structures
	Structs []StructuredType `json:"structs"`

	// List of constants defined at the file level (e.g. global constants)
	Constants []Constant `json:"constants"`

	// List of variables defined at the file level (e.g. global variables)
	Variables []Variable `json:"variables"`

	// List of functions
	Functions []Function `json:"functions"`

	// List of interfaces
	Interfaces []Interface `json:"interfaces"`

	// List of classes
	Classes []Class `json:"classes"`

	// List of traits
	// See http://en.wikipedia.org/wiki/Trait_%28computer_programming%29
	Traits []Trait `json:"traits"`

	// The total number of lines of code.
	LoC int64 `json:"loc"`
}
<commit_msg>src: add some omitempty annotations<commit_after>// Copyright 2014 The DevMine Authors. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.

package src

// SourceFile holds information about a source file.
type SourceFile struct {
	// The path of the source file, relative to the root of the project.
	Path string `json:"path"`

	// Programming language used.
	ProgLang *Language `json:"language"`

	// List of the imports used by the srouce file.
	Imports []string `json:"imports,omitempty"`

	// Types definition
	// TODO rename JSON key into type_definition
	TypeDefs []TypeDef `json:"type_defs,omitempty"`

	// Structures definition
	// TODO rename JSON key into structures
	Structs []StructuredType `json:"structs,omitempty"`

	// List of constants defined at the file level (e.g. global constants)
	Constants []Constant `json:"constants,omitempty"`

	// List of variables defined at the file level (e.g. global variables)
	Variables []Variable `json:"variables,omitempty"`

	// List of functions
	Functions []Function `json:"functions,omitempty"`

	// List of interfaces
	Interfaces []Interface `json:"interfaces,omitempty"`

	// List of classes
	Classes []Class `json:"classes,omitempty"`

	// List of traits
	// See http://en.wikipedia.org/wiki/Trait_%28computer_programming%29
	Traits []Trait `json:"traits,omitempty"`

	// The total number of lines of code.
	LoC int64 `json:"loc"`
}
<|endoftext|>