<commit_before>// Copyright 2014 Google Inc. All Rights Reserved.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
//     http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.

package sources

import (
	"flag"
	"fmt"
	"path/filepath"
	"strings"
	"sync"
	"time"

	"github.com/GoogleCloudPlatform/heapster/sources/api"
	"github.com/GoogleCloudPlatform/heapster/sources/datasource"
	"github.com/GoogleCloudPlatform/heapster/sources/nodes"
	kube_client "github.com/GoogleCloudPlatform/kubernetes/pkg/client"
	"github.com/GoogleCloudPlatform/kubernetes/pkg/clientauth"
	"github.com/golang/glog"
)

const (
	// Cadvisor port in kubernetes.
	cadvisorPort = 4194

	kubeClientVersion = "v1beta1"
)

var (
	argMaster         = flag.String("kubernetes_master", "", "Kubernetes master IP")
	argMasterInsecure = flag.Bool("kubernetes_insecure", true, "Trust Kubernetes master certificate (if using https)")
	argKubeletPort    = flag.String("kubelet_port", "10250", "Kubelet port")
	// TODO: once known location for client auth is defined upstream, default this to that file
	argClientAuthFile = flag.String("kubernetes_client_auth", "", "Kubernetes client authentication file")
)

type kubeSource struct {
	kubeletPort  string
	pollDuration time.Duration
	nodesApi     nodes.NodesApi
	podsApi      podsApi
	kubeletApi   datasource.Kubelet
	stateLock    sync.RWMutex
	podErrors    map[podInstance]int // guarded by stateLock
	lastQuery    time.Time
	eventsApi    EventsSource
}

type podInstance struct {
	name string
	id   string
	ip   string
}

func (self *kubeSource) recordPodError(pod api.Pod) {
	// Heapster knows about pods before they are up and running on a node.
	// Ignore errors for Pods that are not Running.
	if pod.Status != "Running" {
		return
	}

	self.stateLock.Lock()
	defer self.stateLock.Unlock()

	podInstance := podInstance{name: pod.Name, id: pod.ID, ip: pod.HostPublicIP}
	self.podErrors[podInstance]++
}

func (self *kubeSource) getState() string {
	self.stateLock.RLock()
	defer self.stateLock.RUnlock()

	state := fmt.Sprintf("poll duration: %d\n", self.pollDuration)
	if len(self.podErrors) != 0 {
		state += fmt.Sprintf("\tPod Errors: %+v\n", self.podErrors)
	} else {
		state += "\tNo pod errors\n"
	}
	return state
}

func (self *kubeSource) numStatsToFetch() int {
	numStats := int(self.pollDuration / time.Second)
	if time.Since(self.lastQuery) > self.pollDuration {
		numStats = int(time.Since(self.lastQuery) / time.Second)
	}
	return numStats
}

func (self *kubeSource) getStatsFromKubelet(pod *api.Pod, containerName string) (*api.Container, error) {
	resource := filepath.Join("stats", pod.Namespace, pod.Name, pod.ID, containerName)
	if containerName == "/" {
		resource += "/"
	}

	return self.kubeletApi.GetContainer(datasource.Host{IP: pod.HostInternalIP, Port: self.kubeletPort, Resource: resource}, self.numStatsToFetch())
}

func (self *kubeSource) updateStats(host nodes.Host, info nodes.Info) (api.Container, error) {
	container, err := self.getStatsFromKubelet(&api.Pod{HostInternalIP: info.InternalIP}, "/")
	if err != nil {
		glog.V(1).Infof("Failed to get machine stats from kubelet for node %s", host)
		return api.Container{}, err
	}
	if container == nil {
		// no stats found.
		glog.V(1).Infof("no machine stats from kubelet on node %s", host)
		return api.Container{}, fmt.Errorf("no machine stats from kubelet on node %s", host)
	}
	container.Hostname = string(host)
	return *container, nil
}

func (self *kubeSource) getNodesInfo(nodeList *nodes.NodeList) ([]api.Container, error) {
	var (
		lock sync.Mutex
		wg   sync.WaitGroup
	)
	nodesInfo := []api.Container{}
	for host, info := range nodeList.Items {
		wg.Add(1)
		go func(host nodes.Host, info nodes.Info) {
			defer wg.Done()
			if container, err := self.updateStats(host, info); err == nil {
				lock.Lock()
				defer lock.Unlock()
				nodesInfo = append(nodesInfo, container)
			}
		}(host, info)
	}
	wg.Wait()
	return nodesInfo, nil
}

func (self *kubeSource) getPodInfo(nodeList *nodes.NodeList) ([]api.Pod, error) {
	pods, err := self.podsApi.List(nodeList)
	if err != nil {
		return []api.Pod{}, err
	}
	var (
		wg sync.WaitGroup
	)
	for index := range pods {
		wg.Add(1)
		go func(pod *api.Pod) {
			defer wg.Done()
			for index, container := range pod.Containers {
				rawContainer, err := self.getStatsFromKubelet(pod, container.Name)
				if err != nil {
					// Containers could be in the process of being setup or restarting while the pod is alive.
					glog.Errorf("failed to get stats for container %q in pod %q/%q", container.Name, pod.Namespace, pod.Name)
					self.recordPodError(*pod)
					continue
				}
				glog.V(2).Infof("Fetched stats from kubelet for container %s in pod %s", container.Name, pod.Name)
				pod.Containers[index].Hostname = pod.Hostname
				pod.Containers[index].Spec = rawContainer.Spec
				pod.Containers[index].Stats = rawContainer.Stats
			}
		}(&pods[index])
	}
	wg.Wait()

	return pods, nil
}

func (self *kubeSource) GetInfo() (api.AggregateData, error) {
	kubeNodes, err := self.nodesApi.List()
	if err != nil || len(kubeNodes.Items) == 0 {
		return api.AggregateData{}, err
	}
	podsInfo, err := self.getPodInfo(kubeNodes)
	if err != nil {
		return api.AggregateData{}, err
	}
	nodesInfo, err := self.getNodesInfo(kubeNodes)
	if err != nil {
		return api.AggregateData{}, err
	}
	glog.V(2).Info("Fetched list of nodes from the master")
	events, err := self.eventsApi.GetEvents()
	if err != nil {
		if eventErr, ok := err.(EventError); ok && eventErr.WatchLoopTerminated() {
			glog.Errorf("Event watch loop was terminated due to error. Will restart it. Error: %v", err)
			self.eventsApi.RestartWatchLoop()
		}
		return api.AggregateData{}, err
	} else {
		glog.V(4).Infof("Kube.GetInfo fetched the following events: %v", events)
	}
	glog.V(2).Info("Fetched list of events from the master")
	self.lastQuery = time.Now()

	return api.AggregateData{Pods: podsInfo, Machine: nodesInfo, Events: events}, nil
}

func newKubeSource(pollDuration time.Duration) (*kubeSource, error) {
	if len(*argMaster) == 0 {
		return nil, fmt.Errorf("kubernetes_master flag not specified")
	}

	if !(strings.HasPrefix(*argMaster, "http://") || strings.HasPrefix(*argMaster, "https://")) {
		*argMaster = "http://" + *argMaster
	}

	kubeConfig := &kube_client.Config{
		Host:    *argMaster,
		Version: kubeClientVersion,
	}

	if len(*argClientAuthFile) > 0 {
		if clientAuth, err := clientauth.LoadFromFile(*argClientAuthFile); err != nil {
			return nil, err
		} else {
			clientAuth.MergeWithConfig(*kubeConfig)
		}
	}

	kubeClient := kube_client.NewOrDie(kubeConfig)

	nodesApi, err := nodes.NewKubeNodes(kubeClient)
	if err != nil {
		return nil, err
	}
	glog.Infof("Using Kubernetes client with master %q and version %s\n", *argMaster, kubeClientVersion)
	glog.Infof("Using kubelet port %q", *argKubeletPort)
	eventsSource := NewEventsSource(kubeClient)

	return &kubeSource{
		lastQuery:    time.Now(),
		pollDuration: pollDuration,
		kubeletPort:  *argKubeletPort,
		kubeletApi:   datasource.NewKubelet(),
		nodesApi:     nodesApi,
		podsApi:      newPodsApi(kubeClient),
		podErrors:    make(map[podInstance]int),
		eventsApi:    eventsSource,
	}, nil
}

func (self *kubeSource) DebugInfo() string {
	desc := "Source type: Kube\n"
	desc += fmt.Sprintf("\tClient config: master ip %q, version %s\n", *argMaster, kubeClientVersion)
	desc += fmt.Sprintf("\tUsing kubelet port %q\n", self.kubeletPort)
	desc += self.getState()
	desc += "\n"
	desc += self.nodesApi.DebugInfo() + "\n"
	return desc
}
<commit_msg>Better error checking for clientauth<commit_after>// Copyright 2014 Google Inc. All Rights Reserved.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
//     http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.

package sources

import (
	"flag"
	"fmt"
	"path/filepath"
	"strings"
	"sync"
	"time"

	"github.com/GoogleCloudPlatform/heapster/sources/api"
	"github.com/GoogleCloudPlatform/heapster/sources/datasource"
	"github.com/GoogleCloudPlatform/heapster/sources/nodes"
	kube_client "github.com/GoogleCloudPlatform/kubernetes/pkg/client"
	"github.com/GoogleCloudPlatform/kubernetes/pkg/clientauth"
	"github.com/golang/glog"
)

const (
	// Cadvisor port in kubernetes.
	cadvisorPort = 4194

	kubeClientVersion = "v1beta1"
)

var (
	argMaster         = flag.String("kubernetes_master", "", "Kubernetes master IP")
	argMasterInsecure = flag.Bool("kubernetes_insecure", true, "Trust Kubernetes master certificate (if using https)")
	argKubeletPort    = flag.String("kubelet_port", "10250", "Kubelet port")
	// TODO: once known location for client auth is defined upstream, default this to that file
	argClientAuthFile = flag.String("kubernetes_client_auth", "", "Kubernetes client authentication file")
)

type kubeSource struct {
	kubeletPort  string
	pollDuration time.Duration
	nodesApi     nodes.NodesApi
	podsApi      podsApi
	kubeletApi   datasource.Kubelet
	stateLock    sync.RWMutex
	podErrors    map[podInstance]int // guarded by stateLock
	lastQuery    time.Time
	eventsApi    EventsSource
}

type podInstance struct {
	name string
	id   string
	ip   string
}

func (self *kubeSource) recordPodError(pod api.Pod) {
	// Heapster knows about pods before they are up and running on a node.
	// Ignore errors for Pods that are not Running.
	if pod.Status != "Running" {
		return
	}

	self.stateLock.Lock()
	defer self.stateLock.Unlock()

	podInstance := podInstance{name: pod.Name, id: pod.ID, ip: pod.HostPublicIP}
	self.podErrors[podInstance]++
}

func (self *kubeSource) getState() string {
	self.stateLock.RLock()
	defer self.stateLock.RUnlock()

	state := fmt.Sprintf("poll duration: %d\n", self.pollDuration)
	if len(self.podErrors) != 0 {
		state += fmt.Sprintf("\tPod Errors: %+v\n", self.podErrors)
	} else {
		state += "\tNo pod errors\n"
	}
	return state
}

func (self *kubeSource) numStatsToFetch() int {
	numStats := int(self.pollDuration / time.Second)
	if time.Since(self.lastQuery) > self.pollDuration {
		numStats = int(time.Since(self.lastQuery) / time.Second)
	}
	return numStats
}

func (self *kubeSource) getStatsFromKubelet(pod *api.Pod, containerName string) (*api.Container, error) {
	resource := filepath.Join("stats", pod.Namespace, pod.Name, pod.ID, containerName)
	if containerName == "/" {
		resource += "/"
	}

	return self.kubeletApi.GetContainer(datasource.Host{IP: pod.HostInternalIP, Port: self.kubeletPort, Resource: resource}, self.numStatsToFetch())
}

func (self *kubeSource) updateStats(host nodes.Host, info nodes.Info) (api.Container, error) {
	container, err := self.getStatsFromKubelet(&api.Pod{HostInternalIP: info.InternalIP}, "/")
	if err != nil {
		glog.V(1).Infof("Failed to get machine stats from kubelet for node %s", host)
		return api.Container{}, err
	}
	if container == nil {
		// no stats found.
		glog.V(1).Infof("no machine stats from kubelet on node %s", host)
		return api.Container{}, fmt.Errorf("no machine stats from kubelet on node %s", host)
	}
	container.Hostname = string(host)
	return *container, nil
}

func (self *kubeSource) getNodesInfo(nodeList *nodes.NodeList) ([]api.Container, error) {
	var (
		lock sync.Mutex
		wg   sync.WaitGroup
	)
	nodesInfo := []api.Container{}
	for host, info := range nodeList.Items {
		wg.Add(1)
		go func(host nodes.Host, info nodes.Info) {
			defer wg.Done()
			if container, err := self.updateStats(host, info); err == nil {
				lock.Lock()
				defer lock.Unlock()
				nodesInfo = append(nodesInfo, container)
			}
		}(host, info)
	}
	wg.Wait()
	return nodesInfo, nil
}

func (self *kubeSource) getPodInfo(nodeList *nodes.NodeList) ([]api.Pod, error) {
	pods, err := self.podsApi.List(nodeList)
	if err != nil {
		return []api.Pod{}, err
	}
	var (
		wg sync.WaitGroup
	)
	for index := range pods {
		wg.Add(1)
		go func(pod *api.Pod) {
			defer wg.Done()
			for index, container := range pod.Containers {
				rawContainer, err := self.getStatsFromKubelet(pod, container.Name)
				if err != nil {
					// Containers could be in the process of being setup or restarting while the pod is alive.
					glog.Errorf("failed to get stats for container %q in pod %q/%q", container.Name, pod.Namespace, pod.Name)
					self.recordPodError(*pod)
					continue
				}
				glog.V(2).Infof("Fetched stats from kubelet for container %s in pod %s", container.Name, pod.Name)
				pod.Containers[index].Hostname = pod.Hostname
				pod.Containers[index].Spec = rawContainer.Spec
				pod.Containers[index].Stats = rawContainer.Stats
			}
		}(&pods[index])
	}
	wg.Wait()

	return pods, nil
}

func (self *kubeSource) GetInfo() (api.AggregateData, error) {
	kubeNodes, err := self.nodesApi.List()
	if err != nil || len(kubeNodes.Items) == 0 {
		return api.AggregateData{}, err
	}
	podsInfo, err := self.getPodInfo(kubeNodes)
	if err != nil {
		return api.AggregateData{}, err
	}
	nodesInfo, err := self.getNodesInfo(kubeNodes)
	if err != nil {
		return api.AggregateData{}, err
	}
	glog.V(2).Info("Fetched list of nodes from the master")
	events, err := self.eventsApi.GetEvents()
	if err != nil {
		if eventErr, ok := err.(EventError); ok && eventErr.WatchLoopTerminated() {
			glog.Errorf("Event watch loop was terminated due to error. Will restart it. Error: %v", err)
			self.eventsApi.RestartWatchLoop()
		}
		return api.AggregateData{}, err
	} else {
		glog.V(4).Infof("Kube.GetInfo fetched the following events: %v", events)
	}
	glog.V(2).Info("Fetched list of events from the master")
	self.lastQuery = time.Now()

	return api.AggregateData{Pods: podsInfo, Machine: nodesInfo, Events: events}, nil
}

func newKubeSource(pollDuration time.Duration) (*kubeSource, error) {
	if len(*argMaster) == 0 {
		return nil, fmt.Errorf("kubernetes_master flag not specified")
	}

	if !(strings.HasPrefix(*argMaster, "http://") || strings.HasPrefix(*argMaster, "https://")) {
		*argMaster = "http://" + *argMaster
	}

	kubeConfig := kube_client.Config{
		Host:    *argMaster,
		Version: kubeClientVersion,
	}

	if len(*argClientAuthFile) > 0 {
		clientAuth, err := clientauth.LoadFromFile(*argClientAuthFile)
		if err != nil {
			return nil, err
		}

		kubeConfig, err = clientAuth.MergeWithConfig(kubeConfig)
		if err != nil {
			return nil, err
		}
	}

	kubeClient := kube_client.NewOrDie(&kubeConfig)

	nodesApi, err := nodes.NewKubeNodes(kubeClient)
	if err != nil {
		return nil, err
	}
	glog.Infof("Using Kubernetes client with master %q and version %s\n", *argMaster, kubeClientVersion)
	glog.Infof("Using kubelet port %q", *argKubeletPort)
	eventsSource := NewEventsSource(kubeClient)

	return &kubeSource{
		lastQuery:    time.Now(),
		pollDuration: pollDuration,
		kubeletPort:  *argKubeletPort,
		kubeletApi:   datasource.NewKubelet(),
		nodesApi:     nodesApi,
		podsApi:      newPodsApi(kubeClient),
		podErrors:    make(map[podInstance]int),
		eventsApi:    eventsSource,
	}, nil
}

func (self *kubeSource) DebugInfo() string {
	desc := "Source type: Kube\n"
	desc += fmt.Sprintf("\tClient config: master ip %q, version %s\n", *argMaster, kubeClientVersion)
	desc += fmt.Sprintf("\tUsing kubelet port %q\n", self.kubeletPort)
	desc += self.getState()
	desc += "\n"
	desc += self.nodesApi.DebugInfo() + "\n"
	return desc
}
<|endoftext|>


<commit_before>package evedci

import (
	"stm32/hal/gpio"
	"stm32/hal/spi"
)

// SPI implements eve.DCI using Serial Peripheral Interface.
type SPI struct {
	spi      *spi.Driver
	irqchan  chan struct{}
	pdn, csn gpio.Pin
	started  bool
}

func NewSPI(spidrv *spi.Driver, csn, pdn gpio.Pin) *SPI {
	p := spidrv.P
	p.SetConf(
		spi.Master | spi.MSBF | spi.CPOL0 | spi.CPHA0 |
			p.BR(11e6) | // 11 MHz max. before configure PCLK.
			spi.SoftSS | spi.ISSHigh,
	)
	p.SetWordSize(8)
	p.Enable()
	csn.Set()
	dci := new(SPI)
	dci.spi = spidrv
	dci.csn = csn
	dci.pdn = pdn
	dci.irqchan = make(chan struct{}, 1)
	return dci
}

func (dci *SPI) SetBaudrate(baud int) {
	p := dci.spi.P
	p.SetConf(p.Conf()&^spi.BR256 | p.BR(30e6))
}

func (dci *SPI) SPI() *spi.Driver {
	return dci.spi
}

func (dci *SPI) SetPDN(pdn int) {
	dci.pdn.Store(pdn)
}

func (dci *SPI) IRQ() <-chan struct{} {
	return dci.irqchan
}

func (dci *SPI) Err(clear bool) error {
	return dci.spi.Err(clear)
}

func (dci *SPI) End() {
	dci.started = false
	dci.csn.Set()
}

func (dci *SPI) Read(s []byte) {
	dci.spi.WriteRead(nil, s)
}

func (dci *SPI) Write(s []byte) {
	if !dci.started {
		if len(s) == 0 {
			return
		}
		dci.started = true
		dci.csn.Clear()
	}
	dci.spi.WriteRead(s, nil)
}

func (dci *SPI) ISR() {
	select {
	case dci.irqchan <- struct{}{}:
	default:
	}
}
<commit_msg>stm32/evedci: Fix buf in SetBaudrate.<commit_after>package evedci

import (
	"stm32/hal/gpio"
	"stm32/hal/spi"
)

// SPI implements eve.DCI using Serial Peripheral Interface.
type SPI struct {
	spi      *spi.Driver
	irqchan  chan struct{}
	pdn, csn gpio.Pin
	started  bool
}

func NewSPI(spidrv *spi.Driver, csn, pdn gpio.Pin) *SPI {
	p := spidrv.P
	p.SetConf(
		spi.Master | spi.MSBF | spi.CPOL0 | spi.CPHA0 |
			p.BR(11e6) | // 11 MHz max. before configure PCLK.
			spi.SoftSS | spi.ISSHigh,
	)
	p.SetWordSize(8)
	p.Enable()
	csn.Set()
	dci := new(SPI)
	dci.spi = spidrv
	dci.csn = csn
	dci.pdn = pdn
	dci.irqchan = make(chan struct{}, 1)
	return dci
}

func (dci *SPI) SetBaudrate(baud int) {
	p := dci.spi.P
	p.SetConf(p.Conf()&^spi.BR256 | p.BR(baud))
}

func (dci *SPI) SPI() *spi.Driver {
	return dci.spi
}

func (dci *SPI) SetPDN(pdn int) {
	dci.pdn.Store(pdn)
}

func (dci *SPI) IRQ() <-chan struct{} {
	return dci.irqchan
}

func (dci *SPI) Err(clear bool) error {
	return dci.spi.Err(clear)
}

func (dci *SPI) End() {
	dci.started = false
	dci.csn.Set()
}

func (dci *SPI) Read(s []byte) {
	dci.spi.WriteRead(nil, s)
}

func (dci *SPI) Write(s []byte) {
	if !dci.started {
		if len(s) == 0 {
			return
		}
		dci.started = true
		dci.csn.Clear()
	}
	dci.spi.WriteRead(s, nil)
}

func (dci *SPI) ISR() {
	select {
	case dci.irqchan <- struct{}{}:
	default:
	}
}
<|endoftext|>


<commit_before><commit_msg>rescan: handle err in GetUtxo to prevent shutdown panic<commit_after><|endoftext|>


<commit_before>// Glide is a command line utility that manages Go project dependencies and
// your GOPATH.
//
// Dependencies are managed via a glide.yaml in the root of a project. The yaml
// file lets you specify projects, versions (tags, branches, or references),
// and even alias one location in as other one. Aliasing is useful when supporting
// forks without needing to rewrite the imports in a codebase.
//
// A glide.yaml file looks like:
//
// 		package: github.com/Masterminds/glide
// 		imports:
//			- package: github.com/Masterminds/cookoo
//			  vcs: git
//			  ref: 1.1.0
//			  subpackages: **
//			- package: github.com/kylelemons/go-gypsy
//			  subpackages: yaml
//
// Glide puts dependencies in a _vendor directory. Go utilities require this to
// be in your GOPATH. Glide makes this easy. Use the `glide in` command to enter
// a shell (your default) with the GOPATH set to the projects _vendor directory.
// To leave this shell simply exit it.
//
// If your .bashrc, .zshrc, or other startup shell sets your GOPATH you many need
// to optionally set it using something like:
//
//		if [ "" = "${GOPATH}" ]; then
//		  export GOPATH="/some/dir"
//		fi
//
// For more information use the `glide help` command or see https://github.com/Masterminds/glide
package main

import (
	"github.com/Masterminds/glide/cmd"

	"github.com/Masterminds/cookoo"
	"github.com/Masterminds/cookoo/cli"

	"flag"
	"fmt"
	"os"
)

var version string = "0.1.0"

const Summary = "Manage Go projects with ease."
const Usage = `Manage dependencies, naming, and GOPATH for your Go projects.

Examples:
	$ glide create
	$ glide in
	$ glide install
	$ glide update
	$ glide rebuild

COMMANDS
========

Utilities:

- help: Show this help message (alias of -h)
- status: Print a status report.
- version: Print the version and exit.

Dependency management:

- create: Initialize a new project, creating a template glide.yaml.
- install: Install all packages in the glide.yaml.
- update: Update existing packages (alias: 'up').
- rebuild: Rebuild ('go build') the dependencies.

Project tools:

- in: Glide into a commandline shell preconfigured for your project (with
  GOPATH set).
- into: "glide into /my/project" is the same as running "cd /my/project && glide in"
- gopath: Emits the GOPATH for the current project. Useful for things like
  manually setting GOPATH: GOPATH=$(glide gopath)

Importing:

- godeps: Import Godeps and Godeps-Git files and display the would-be yaml file.

FILES
=====

Each project should have a 'glide.yaml' file in the project directory. Files
look something like this:

	package: github.com/Masterminds/glide
	imports:
		- package: github.com/Masterminds/cookoo
		  vcs: git
		  ref: 1.1.0
		  subpackages: **
  		- package: github.com/kylelemons/go-gypsy
		  subpackages: yaml
`

func main() {
	reg, router, cxt := cookoo.Cookoo()

	routes(reg, cxt)

	if err := router.HandleRequest("@startup", cxt, false); err != nil {
		fmt.Printf("Oops! %s\n", err)
		os.Exit(1)
	}

}

func routes(reg *cookoo.Registry, cxt cookoo.Context) {

	flags := flag.NewFlagSet("global", flag.PanicOnError)
	flags.Bool("h", false, "Print help text.")
	flags.Bool("q", false, "Quiet (no info or debug messages)")
	flags.String("yaml", "glide.yaml", "Set a YAML configuration file.")

	cxt.Put("os.Args", os.Args)

	reg.Route("@startup", "Parse args and send to the right subcommand.").
		Does(cli.ShiftArgs, "_").Using("n").WithDefault(1).
		Does(cli.ParseArgs, "remainingArgs").
		Using("flagset").WithDefault(flags).
		Using("args").From("cxt:os.Args").
		Does(cli.ShowHelp, "help").
		Using("show").From("cxt:h cxt:help").
		Using("summary").WithDefault(Summary).
		Using("usage").WithDefault(Usage).
		Using("flags").WithDefault(flags).
		Does(cmd.BeQuiet, "quiet").
		Using("quiet").From("cxt:q").
		Does(cli.RunSubcommand, "subcommand").
		Using("default").WithDefault("help").
		Using("offset").WithDefault(0).
		Using("args").From("cxt:remainingArgs")

	reg.Route("@ready", "Prepare for glide commands.").
		Does(cmd.ReadyToGlide, "ready").
		Does(cmd.ParseYaml, "cfg").Using("filename").From("cxt:yaml")

	reg.Route("help", "Print help.").
		Does(cli.ShowHelp, "help").
		Using("show").WithDefault(true).
		Using("summary").WithDefault(Summary).
		Using("usage").WithDefault(Usage).
		Using("flags").WithDefault(flags)

	reg.Route("version", "Print the version and exit.").Does(showVersion, "_")

	reg.Route("into", "Creates a new Glide shell.").
		Does(cmd.AlreadyGliding, "isGliding").
		Does(cli.ShiftArgs, "toPath").Using("n").WithDefault(2).
		Does(cmd.Into, "in").Using("into").From("cxt:toPath").
		Using("into").WithDefault("").From("cxt:toPath").
		Includes("@ready")

	reg.Route("in", "Set GOPATH and supporting env vars.").
		Does(cmd.AlreadyGliding, "isGliding").
		Includes("@ready").
		//Does(cli.ShiftArgs, "toPath").Using("n").WithDefault(1).
		Does(cmd.Into, "in").
		Using("into").WithDefault("").From("cxt:toPath").
		Using("conf").From("cxt:cfg")

	reg.Route("gopath", "Return the GOPATH for the present project.").
		Does(cmd.In, "gopath")

	reg.Route("out", "Set GOPATH back to former val.").
		Does(cmd.Out, "gopath")

	reg.Route("install", "Install dependencies.").
		Includes("@ready").
		Does(cmd.Mkdir, "dir").Using("dir").WithDefault("_vendor").
		Does(cmd.LinkPackage, "alias").
		Does(cmd.GetImports, "dependencies").Using("conf").From("cxt:cfg").
		Does(cmd.SetReference, "version").Using("conf").From("cxt:cfg").
		Does(cmd.Rebuild, "rebuild").Using("conf").From("cxt:cfg")

	reg.Route("up", "Update dependencies (alias of 'update')").
		Does(cookoo.ForwardTo, "fwd").Using("route").WithDefault("update")

	reg.Route("update", "Update dependencies.").
		Includes("@ready").
		Does(cmd.CowardMode, "_").
		Does(cmd.UpdateImports, "dependencies").Using("conf").From("cxt:cfg").
		Does(cmd.SetReference, "version").Using("conf").From("cxt:cfg").
		Does(cmd.Rebuild, "rebuild").Using("conf").From("cxt:cfg")

	reg.Route("rebuild", "Rebuild dependencies").
		Includes("@ready").
		Does(cmd.CowardMode, "_").
		Does(cmd.Rebuild, "rebuild").Using("conf").From("cxt:cfg")

	reg.Route("pin", "Print a YAML file with all of the packages pinned to the current version.").
		Includes("@ready").
		Does(cmd.UpdateReferences, "refs").Using("conf").From("cxt:cfg").
		Does(cmd.MergeToYaml, "merged").Using("conf").From("cxt:cfg").
		Does(cmd.WriteYaml, "out").Using("yaml.Node").From("cxt:merged")

	reg.Route("godeps", "Read a Godeps file").
		Includes("@ready").
		Does(cmd.Godeps, "godeps").
		Does(cmd.AddDependencies, "addGodeps").
		Using("dependencies").From("cxt:godeps").
		Using("conf").From("cxt:cfg").
		Does(cmd.GodepsGit, "godepsGit").
		Does(cmd.AddDependencies, "addGodepsGit").
		Using("dependencies").From("cxt:godepsGit").
		Using("conf").From("cxt:cfg").
		// Does(cmd.UpdateReferences, "refs").Using("conf").From("cxt:cfg").
		Does(cmd.MergeToYaml, "merged").Using("conf").From("cxt:cfg").
		Does(cmd.WriteYaml, "out").Using("yaml.Node").From("cxt:merged")

	reg.Route("init", "Initialize Glide (deprecated; use 'create'").
		Does(cookoo.ForwardTo, "fwd").Using("route").WithDefault("create")

	reg.Route("create", "Initialize Glide").
		Does(cmd.InitGlide, "init")

	reg.Route("status", "Status").
		Does(cmd.Status, "status")

	reg.Route("@plugin", "Try to send to a plugin.").
		Includes("@ready").
		Does(cmd.DropToShell, "plugin")
}

func showVersion(c cookoo.Context, p *cookoo.Params) (interface{}, cookoo.Interrupt) {
	fmt.Println(version)
	return version, nil
}
<commit_msg>Updated the version to 0.2.0-dev to begin future work.<commit_after>// Glide is a command line utility that manages Go project dependencies and
// your GOPATH.
//
// Dependencies are managed via a glide.yaml in the root of a project. The yaml
// file lets you specify projects, versions (tags, branches, or references),
// and even alias one location in as other one. Aliasing is useful when supporting
// forks without needing to rewrite the imports in a codebase.
//
// A glide.yaml file looks like:
//
// 		package: github.com/Masterminds/glide
// 		imports:
//			- package: github.com/Masterminds/cookoo
//			  vcs: git
//			  ref: 1.1.0
//			  subpackages: **
//			- package: github.com/kylelemons/go-gypsy
//			  subpackages: yaml
//
// Glide puts dependencies in a _vendor directory. Go utilities require this to
// be in your GOPATH. Glide makes this easy. Use the `glide in` command to enter
// a shell (your default) with the GOPATH set to the projects _vendor directory.
// To leave this shell simply exit it.
//
// If your .bashrc, .zshrc, or other startup shell sets your GOPATH you many need
// to optionally set it using something like:
//
//		if [ "" = "${GOPATH}" ]; then
//		  export GOPATH="/some/dir"
//		fi
//
// For more information use the `glide help` command or see https://github.com/Masterminds/glide
package main

import (
	"github.com/Masterminds/glide/cmd"

	"github.com/Masterminds/cookoo"
	"github.com/Masterminds/cookoo/cli"

	"flag"
	"fmt"
	"os"
)

var version string = "0.2.0-dev"

const Summary = "Manage Go projects with ease."
const Usage = `Manage dependencies, naming, and GOPATH for your Go projects.

Examples:
	$ glide create
	$ glide in
	$ glide install
	$ glide update
	$ glide rebuild

COMMANDS
========

Utilities:

- help: Show this help message (alias of -h)
- status: Print a status report.
- version: Print the version and exit.

Dependency management:

- create: Initialize a new project, creating a template glide.yaml.
- install: Install all packages in the glide.yaml.
- update: Update existing packages (alias: 'up').
- rebuild: Rebuild ('go build') the dependencies.

Project tools:

- in: Glide into a commandline shell preconfigured for your project (with
  GOPATH set).
- into: "glide into /my/project" is the same as running "cd /my/project && glide in"
- gopath: Emits the GOPATH for the current project. Useful for things like
  manually setting GOPATH: GOPATH=$(glide gopath)

Importing:

- godeps: Import Godeps and Godeps-Git files and display the would-be yaml file.

FILES
=====

Each project should have a 'glide.yaml' file in the project directory. Files
look something like this:

	package: github.com/Masterminds/glide
	imports:
		- package: github.com/Masterminds/cookoo
		  vcs: git
		  ref: 1.1.0
		  subpackages: **
  		- package: github.com/kylelemons/go-gypsy
		  subpackages: yaml
`

func main() {
	reg, router, cxt := cookoo.Cookoo()

	routes(reg, cxt)

	if err := router.HandleRequest("@startup", cxt, false); err != nil {
		fmt.Printf("Oops! %s\n", err)
		os.Exit(1)
	}

}

func routes(reg *cookoo.Registry, cxt cookoo.Context) {

	flags := flag.NewFlagSet("global", flag.PanicOnError)
	flags.Bool("h", false, "Print help text.")
	flags.Bool("q", false, "Quiet (no info or debug messages)")
	flags.String("yaml", "glide.yaml", "Set a YAML configuration file.")

	cxt.Put("os.Args", os.Args)

	reg.Route("@startup", "Parse args and send to the right subcommand.").
		Does(cli.ShiftArgs, "_").Using("n").WithDefault(1).
		Does(cli.ParseArgs, "remainingArgs").
		Using("flagset").WithDefault(flags).
		Using("args").From("cxt:os.Args").
		Does(cli.ShowHelp, "help").
		Using("show").From("cxt:h cxt:help").
		Using("summary").WithDefault(Summary).
		Using("usage").WithDefault(Usage).
		Using("flags").WithDefault(flags).
		Does(cmd.BeQuiet, "quiet").
		Using("quiet").From("cxt:q").
		Does(cli.RunSubcommand, "subcommand").
		Using("default").WithDefault("help").
		Using("offset").WithDefault(0).
		Using("args").From("cxt:remainingArgs")

	reg.Route("@ready", "Prepare for glide commands.").
		Does(cmd.ReadyToGlide, "ready").
		Does(cmd.ParseYaml, "cfg").Using("filename").From("cxt:yaml")

	reg.Route("help", "Print help.").
		Does(cli.ShowHelp, "help").
		Using("show").WithDefault(true).
		Using("summary").WithDefault(Summary).
		Using("usage").WithDefault(Usage).
		Using("flags").WithDefault(flags)

	reg.Route("version", "Print the version and exit.").Does(showVersion, "_")

	reg.Route("into", "Creates a new Glide shell.").
		Does(cmd.AlreadyGliding, "isGliding").
		Does(cli.ShiftArgs, "toPath").Using("n").WithDefault(2).
		Does(cmd.Into, "in").Using("into").From("cxt:toPath").
		Using("into").WithDefault("").From("cxt:toPath").
		Includes("@ready")

	reg.Route("in", "Set GOPATH and supporting env vars.").
		Does(cmd.AlreadyGliding, "isGliding").
		Includes("@ready").
		//Does(cli.ShiftArgs, "toPath").Using("n").WithDefault(1).
		Does(cmd.Into, "in").
		Using("into").WithDefault("").From("cxt:toPath").
		Using("conf").From("cxt:cfg")

	reg.Route("gopath", "Return the GOPATH for the present project.").
		Does(cmd.In, "gopath")

	reg.Route("out", "Set GOPATH back to former val.").
		Does(cmd.Out, "gopath")

	reg.Route("install", "Install dependencies.").
		Includes("@ready").
		Does(cmd.Mkdir, "dir").Using("dir").WithDefault("_vendor").
		Does(cmd.LinkPackage, "alias").
		Does(cmd.GetImports, "dependencies").Using("conf").From("cxt:cfg").
		Does(cmd.SetReference, "version").Using("conf").From("cxt:cfg").
		Does(cmd.Rebuild, "rebuild").Using("conf").From("cxt:cfg")

	reg.Route("up", "Update dependencies (alias of 'update')").
		Does(cookoo.ForwardTo, "fwd").Using("route").WithDefault("update")

	reg.Route("update", "Update dependencies.").
		Includes("@ready").
		Does(cmd.CowardMode, "_").
		Does(cmd.UpdateImports, "dependencies").Using("conf").From("cxt:cfg").
		Does(cmd.SetReference, "version").Using("conf").From("cxt:cfg").
		Does(cmd.Rebuild, "rebuild").Using("conf").From("cxt:cfg")

	reg.Route("rebuild", "Rebuild dependencies").
		Includes("@ready").
		Does(cmd.CowardMode, "_").
		Does(cmd.Rebuild, "rebuild").Using("conf").From("cxt:cfg")

	reg.Route("pin", "Print a YAML file with all of the packages pinned to the current version.").
		Includes("@ready").
		Does(cmd.UpdateReferences, "refs").Using("conf").From("cxt:cfg").
		Does(cmd.MergeToYaml, "merged").Using("conf").From("cxt:cfg").
		Does(cmd.WriteYaml, "out").Using("yaml.Node").From("cxt:merged")

	reg.Route("godeps", "Read a Godeps file").
		Includes("@ready").
		Does(cmd.Godeps, "godeps").
		Does(cmd.AddDependencies, "addGodeps").
		Using("dependencies").From("cxt:godeps").
		Using("conf").From("cxt:cfg").
		Does(cmd.GodepsGit, "godepsGit").
		Does(cmd.AddDependencies, "addGodepsGit").
		Using("dependencies").From("cxt:godepsGit").
		Using("conf").From("cxt:cfg").
		// Does(cmd.UpdateReferences, "refs").Using("conf").From("cxt:cfg").
		Does(cmd.MergeToYaml, "merged").Using("conf").From("cxt:cfg").
		Does(cmd.WriteYaml, "out").Using("yaml.Node").From("cxt:merged")

	reg.Route("init", "Initialize Glide (deprecated; use 'create'").
		Does(cookoo.ForwardTo, "fwd").Using("route").WithDefault("create")

	reg.Route("create", "Initialize Glide").
		Does(cmd.InitGlide, "init")

	reg.Route("status", "Status").
		Does(cmd.Status, "status")

	reg.Route("@plugin", "Try to send to a plugin.").
		Includes("@ready").
		Does(cmd.DropToShell, "plugin")
}

func showVersion(c cookoo.Context, p *cookoo.Params) (interface{}, cookoo.Interrupt) {
	fmt.Println(version)
	return version, nil
}
<|endoftext|>


<commit_before>package parser

import (
	"fmt"
	"monkey/ast"
	"monkey/lexer"
	"testing"
)

func checkParseErrors(t *testing.T, p *Parser) {
	errors := p.Errors()
	if len(errors) == 0 {
		return
	}

	t.Errorf("parser had %d errors", len(errors))
	for _, msg := range errors {
		t.Errorf("parser error: %q", msg)
	}

	t.FailNow()
}

func TestLetStatements(t *testing.T) {
	input := `let x = 5;
			  let y = 10;
			  let foobar = 838383;`

	l := lexer.New(input)
	p := New(l)

	program := p.ParseProgram()
	checkParseErrors(t, p)

	if program == nil {
		t.Fatalf("ParseProgram() returned nil")
	}

	if len(program.Statements) != 3 {
		t.Fatalf("program.Statements does not contain 3 statements. got=%d", len(program.Statements))
	}

	tests := []struct {
		expectedIdentifier string
	}{
		{"x"},
		{"y"},
		{"foobar"},
	}

	for i, tt := range tests {
		stmt := program.Statements[i]
		if !testLetStatement(t, stmt, tt.expectedIdentifier) {
			return
		}
	}
}

func testLetStatement(t *testing.T, s ast.Statement, name string) bool {
	if s.TokenLiteral() != "let" {
		t.Errorf("s.TokenLiteral() not 'let'. got=%q", s.TokenLiteral())
		return false
	}

	letStmt, ok := s.(*ast.LetStatement)
	if !ok {
		t.Errorf("s not *ast.LetStatement. got=%T", s)
		return false
	}

	if letStmt.Name.Value != name {
		t.Errorf("letStmt.Name.Value not '%s'. got=%s", name, letStmt.Name.Value)
		return false
	}

	if letStmt.Name.TokenLiteral() != name {
		t.Errorf("s.Name not '%s'. got=%s", name, letStmt.Name)
		return false
	}

	return true
}

func TestReturnStatements(t *testing.T) {
	input := `return 5;
			  return 10;
			  return 993322;`

	l := lexer.New(input)
	p := New(l)

	program := p.ParseProgram()
	checkParseErrors(t, p)

	if len(program.Statements) != 3 {
		t.Fatalf("program.Statements does not contain 3 statements. got=%d", len(program.Statements))
	}

	for _, stmt := range program.Statements {
		returnStmt, ok := stmt.(*ast.ReturnStatement)
		if !ok {
			t.Errorf("stmt not *ast.ReturnStatement. got=%T", stmt)
			continue
		}

		if returnStmt.TokenLiteral() != "return" {
			t.Errorf("returnStmt.TokenLiteral() not 'return'. got=%q", returnStmt.TokenLiteral())
		}
	}
}

func TestIdentifierExpression(t *testing.T) {
	input := "foobar;"

	l := lexer.New(input)
	p := New(l)

	program := p.ParseProgram()
	checkParseErrors(t, p)

	if len(program.Statements) != 1 {
		t.Fatalf("program.Statements does not have enough statements. got=%d", len(program.Statements))
	}

	stmt, ok := program.Statements[0].(*ast.ExpressionStatement)
	if !ok {
		t.Fatalf("program.Statements[0] is not *ast.ExpressionStatement. got=%T", program.Statements[0])
	}

	ident, ok := stmt.Expression.(*ast.Identifier)
	if !ok {
		t.Fatalf("stmt.Expression not *ast.Identifier. got=%T", stmt.Expression)
	}

	if ident.Value != "foobar" {
		t.Errorf("ident.Value not %s. got=%s", "foobar", ident.Value)
	}

	if ident.TokenLiteral() != "foobar" {
		t.Errorf("ident.TokenLiteral() not %s. got=%s", "foobar", ident.TokenLiteral())
	}
}

func TestIntegerLiteralExpression(t *testing.T) {
	input := "5;"

	l := lexer.New(input)
	p := New(l)

	program := p.ParseProgram()
	checkParseErrors(t, p)

	if len(program.Statements) != 1 {
		t.Fatalf("program.Statements does not have enough statements. got=%d", len(program.Statements))
	}

	stmt, ok := program.Statements[0].(*ast.ExpressionStatement)
	if !ok {
		t.Fatalf("program.Statements[0] is not *ast.ExpressionStatement. got=%T", program.Statements[0])
	}

	literal, ok := stmt.Expression.(*ast.IntegerLiteral)
	if !ok {
		t.Fatalf("stmt.Expression not *ast.IntegerLiteral. got=%T", stmt.Expression)
	}

	if literal.Value != 5 {
		t.Errorf("literal.Value not %d. got=%d", 5, literal.Value)
	}

	if literal.TokenLiteral() != "5" {
		t.Errorf("literal.TokenLiteral() not %s. got=%s", "5", literal.TokenLiteral())
	}
}

func TestParsingPrefixExpressions(t *testing.T) {
	prefixTests := []struct {
		input        string
		operator     string
		integerValue int64
	}{
		{"!5;", "!", 5},
		{"-15;", "-", 15},
	}

	for _, tt := range prefixTests {
		l := lexer.New(tt.input)
		p := New(l)

		program := p.ParseProgram()
		checkParseErrors(t, p)

		if len(program.Statements) != 1 {
			t.Fatalf("program.Statements does not contain %d statements. got=%d", 1, len(program.Statements))
		}

		stmt, ok := program.Statements[0].(*ast.ExpressionStatement)
		if !ok {
			t.Fatalf("program.Statements[0] not *ast.ExpressionStatement. got=%T", program.Statements[0])
		}

		exp, ok := stmt.Expression.(*ast.PrefixExpression)
		if !ok {
			t.Fatalf("stmt.Expression is not *ast.PrefixExpression. got=%T", stmt.Expression)
		}

		if exp.Operator != tt.operator {
			t.Fatalf("exp.Operator is not '%s'. got=%s", tt.operator, exp.Operator)
		}

		if !testIntegerLiteral(t, exp.Right, tt.integerValue) {
			return
		}
	}
}

func testIntegerLiteral(t *testing.T, il ast.Expression, value int64) bool {
	integ, ok := il.(*ast.IntegerLiteral)
	if !ok {
		t.Errorf("il not *ast.IntegerLiteral. got=%T", il)
		return false
	}

	if integ.Value != value {
		t.Errorf("integ.Value not %d. got=%d", value, integ.Value)
		return false
	}

	if integ.TokenLiteral() != fmt.Sprintf("%d", value) {
		t.Errorf("integ.TokenLiteral() not %d. got=%s", value, integ.TokenLiteral())
		return false
	}

	return true
}

func TestParsingInfixExpressions(t *testing.T) {
	infixTests := []struct {
		input      string
		leftValue  int64
		operator   string
		rightValue int64
	}{
		{"5 + 5;", 5, "+", 5},
		{"5 - 5;", 5, "-", 5},
		{"5 * 5;", 5, "*", 5},
		{"5 / 5;", 5, "/", 5},
		{"5 > 5;", 5, ">", 5},
		{"5 < 5;", 5, "<", 5},
		{"5 == 5;", 5, "==", 5},
		{"5 != 5;", 5, "!=", 5},
	}

	for _, tt := range infixTests {
		l := lexer.New(tt.input)
		p := New(l)

		program := p.ParseProgram()
		checkParseErrors(t, p)

		if len(program.Statements) != 1 {
			t.Fatalf("program.Statements does not contain %d statements. got=%d", 1, len(program.Statements))
		}

		stmt, ok := program.Statements[0].(*ast.ExpressionStatement)
		if !ok {
			t.Fatalf("program.Statements[0] not *ast.ExpressionStatement. got=%T", program.Statements[0])
		}

		exp, ok := stmt.Expression.(*ast.InfixExpression)
		if !ok {
			t.Fatalf("stmt.Expression is not *ast.InfixExpression. got=%T", stmt.Expression)
		}

		if exp.Operator != tt.operator {
			t.Fatalf("exp.Operator is not '%s'. got=%s", tt.operator, exp.Operator)
		}

		if !testIntegerLiteral(t, exp.Left, tt.leftValue) {
			return
		}
	}
}
<commit_msg>add operator precedence testing<commit_after>package parser

import (
	"fmt"
	"monkey/ast"
	"monkey/lexer"
	"testing"
)

func checkParseErrors(t *testing.T, p *Parser) {
	errors := p.Errors()
	if len(errors) == 0 {
		return
	}

	t.Errorf("parser had %d errors", len(errors))
	for _, msg := range errors {
		t.Errorf("parser error: %q", msg)
	}

	t.FailNow()
}

func TestLetStatements(t *testing.T) {
	input := `let x = 5;
			  let y = 10;
			  let foobar = 838383;`

	l := lexer.New(input)
	p := New(l)

	program := p.ParseProgram()
	checkParseErrors(t, p)

	if program == nil {
		t.Fatalf("ParseProgram() returned nil")
	}

	if len(program.Statements) != 3 {
		t.Fatalf("program.Statements does not contain 3 statements. got=%d", len(program.Statements))
	}

	tests := []struct {
		expectedIdentifier string
	}{
		{"x"},
		{"y"},
		{"foobar"},
	}

	for i, tt := range tests {
		stmt := program.Statements[i]
		if !testLetStatement(t, stmt, tt.expectedIdentifier) {
			return
		}
	}
}

func testLetStatement(t *testing.T, s ast.Statement, name string) bool {
	if s.TokenLiteral() != "let" {
		t.Errorf("s.TokenLiteral() not 'let'. got=%q", s.TokenLiteral())
		return false
	}

	letStmt, ok := s.(*ast.LetStatement)
	if !ok {
		t.Errorf("s not *ast.LetStatement. got=%T", s)
		return false
	}

	if letStmt.Name.Value != name {
		t.Errorf("letStmt.Name.Value not '%s'. got=%s", name, letStmt.Name.Value)
		return false
	}

	if letStmt.Name.TokenLiteral() != name {
		t.Errorf("s.Name not '%s'. got=%s", name, letStmt.Name)
		return false
	}

	return true
}

func TestReturnStatements(t *testing.T) {
	input := `return 5;
			  return 10;
			  return 993322;`

	l := lexer.New(input)
	p := New(l)

	program := p.ParseProgram()
	checkParseErrors(t, p)

	if len(program.Statements) != 3 {
		t.Fatalf("program.Statements does not contain 3 statements. got=%d", len(program.Statements))
	}

	for _, stmt := range program.Statements {
		returnStmt, ok := stmt.(*ast.ReturnStatement)
		if !ok {
			t.Errorf("stmt not *ast.ReturnStatement. got=%T", stmt)
			continue
		}

		if returnStmt.TokenLiteral() != "return" {
			t.Errorf("returnStmt.TokenLiteral() not 'return'. got=%q", returnStmt.TokenLiteral())
		}
	}
}

func TestIdentifierExpression(t *testing.T) {
	input := "foobar;"

	l := lexer.New(input)
	p := New(l)

	program := p.ParseProgram()
	checkParseErrors(t, p)

	if len(program.Statements) != 1 {
		t.Fatalf("program.Statements does not have enough statements. got=%d", len(program.Statements))
	}

	stmt, ok := program.Statements[0].(*ast.ExpressionStatement)
	if !ok {
		t.Fatalf("program.Statements[0] is not *ast.ExpressionStatement. got=%T", program.Statements[0])
	}

	ident, ok := stmt.Expression.(*ast.Identifier)
	if !ok {
		t.Fatalf("stmt.Expression not *ast.Identifier. got=%T", stmt.Expression)
	}

	if ident.Value != "foobar" {
		t.Errorf("ident.Value not %s. got=%s", "foobar", ident.Value)
	}

	if ident.TokenLiteral() != "foobar" {
		t.Errorf("ident.TokenLiteral() not %s. got=%s", "foobar", ident.TokenLiteral())
	}
}

func TestIntegerLiteralExpression(t *testing.T) {
	input := "5;"

	l := lexer.New(input)
	p := New(l)

	program := p.ParseProgram()
	checkParseErrors(t, p)

	if len(program.Statements) != 1 {
		t.Fatalf("program.Statements does not have enough statements. got=%d", len(program.Statements))
	}

	stmt, ok := program.Statements[0].(*ast.ExpressionStatement)
	if !ok {
		t.Fatalf("program.Statements[0] is not *ast.ExpressionStatement. got=%T", program.Statements[0])
	}

	literal, ok := stmt.Expression.(*ast.IntegerLiteral)
	if !ok {
		t.Fatalf("stmt.Expression not *ast.IntegerLiteral. got=%T", stmt.Expression)
	}

	if literal.Value != 5 {
		t.Errorf("literal.Value not %d. got=%d", 5, literal.Value)
	}

	if literal.TokenLiteral() != "5" {
		t.Errorf("literal.TokenLiteral() not %s. got=%s", "5", literal.TokenLiteral())
	}
}

func TestParsingPrefixExpressions(t *testing.T) {
	prefixTests := []struct {
		input        string
		operator     string
		integerValue int64
	}{
		{"!5;", "!", 5},
		{"-15;", "-", 15},
	}

	for _, tt := range prefixTests {
		l := lexer.New(tt.input)
		p := New(l)

		program := p.ParseProgram()
		checkParseErrors(t, p)

		if len(program.Statements) != 1 {
			t.Fatalf("program.Statements does not contain %d statements. got=%d", 1, len(program.Statements))
		}

		stmt, ok := program.Statements[0].(*ast.ExpressionStatement)
		if !ok {
			t.Fatalf("program.Statements[0] not *ast.ExpressionStatement. got=%T", program.Statements[0])
		}

		exp, ok := stmt.Expression.(*ast.PrefixExpression)
		if !ok {
			t.Fatalf("stmt.Expression is not *ast.PrefixExpression. got=%T", stmt.Expression)
		}

		if exp.Operator != tt.operator {
			t.Fatalf("exp.Operator is not '%s'. got=%s", tt.operator, exp.Operator)
		}

		if !testIntegerLiteral(t, exp.Right, tt.integerValue) {
			return
		}
	}
}

func testIntegerLiteral(t *testing.T, il ast.Expression, value int64) bool {
	integ, ok := il.(*ast.IntegerLiteral)
	if !ok {
		t.Errorf("il not *ast.IntegerLiteral. got=%T", il)
		return false
	}

	if integ.Value != value {
		t.Errorf("integ.Value not %d. got=%d", value, integ.Value)
		return false
	}

	if integ.TokenLiteral() != fmt.Sprintf("%d", value) {
		t.Errorf("integ.TokenLiteral() not %d. got=%s", value, integ.TokenLiteral())
		return false
	}

	return true
}

func TestParsingInfixExpressions(t *testing.T) {
	infixTests := []struct {
		input      string
		leftValue  int64
		operator   string
		rightValue int64
	}{
		{"5 + 5;", 5, "+", 5},
		{"5 - 5;", 5, "-", 5},
		{"5 * 5;", 5, "*", 5},
		{"5 / 5;", 5, "/", 5},
		{"5 > 5;", 5, ">", 5},
		{"5 < 5;", 5, "<", 5},
		{"5 == 5;", 5, "==", 5},
		{"5 != 5;", 5, "!=", 5},
	}

	for _, tt := range infixTests {
		l := lexer.New(tt.input)
		p := New(l)

		program := p.ParseProgram()
		checkParseErrors(t, p)

		if len(program.Statements) != 1 {
			t.Fatalf("program.Statements does not contain %d statements. got=%d", 1, len(program.Statements))
		}

		stmt, ok := program.Statements[0].(*ast.ExpressionStatement)
		if !ok {
			t.Fatalf("program.Statements[0] not *ast.ExpressionStatement. got=%T", program.Statements[0])
		}

		exp, ok := stmt.Expression.(*ast.InfixExpression)
		if !ok {
			t.Fatalf("stmt.Expression is not *ast.InfixExpression. got=%T", stmt.Expression)
		}

		if exp.Operator != tt.operator {
			t.Fatalf("exp.Operator is not '%s'. got=%s", tt.operator, exp.Operator)
		}

		if !testIntegerLiteral(t, exp.Left, tt.leftValue) {
			return
		}
	}
}

func TestOperatorPrecedenceParsing(t *testing.T) {
	tests := []struct {
		input    string
		expected string
	}{
		{"-a * b", "((-a) * b)"},
		{"!-a", "(!(-a))"},
		{"a + b + c", "((a + b) + c)"},
		{"a + b - c", "((a + b) - c)"},
		{"a * b * c", "((a * b) * c)"},
		{"3 + 4; -5 * 5", "(3 + 4)((-5) * 5)"},
		{"5 > 4 == 3 < 4", "((5 > 4) == (3 < 4))"},
		{"5 < 4 != 3 > 4", "((5 < 4) != (3 > 4))"},
		{"3 + 4 * 5 == 3 * 1 + 4 * 5", "((3 + (4 * 5)) == ((3 * 1) + (4 * 5)))"},
		{"3 + 4 * 5 == 3 * 1 + 4 * 5", "((3 + (4 * 5)) == ((3 * 1) + (4 * 5)))"},
	}

	for _, tt := range tests {
		l := lexer.New(tt.input)
		p := New(l)

		program := p.ParseProgram()
		checkParseErrors(t, p)

		actual := program.String()
		if actual != tt.expected {
			t.Errorf("expected=%q, got=%q", tt.expected, actual)
		}
	}
}
<|endoftext|>


<commit_before>package main

import (
	"bufio"
	"encoding/json"
	"flag"
	"fmt"
	"io/ioutil"
	"log"
	"net"
	"strconv"
	"strings"
	"sync"
	"sync/atomic"
	"time"

	"github.com/iron-io/golog"
)

type GlockConfig struct {
	Port    int `json:"port"`
	Logging LoggingConfig
}

type LoggingConfig struct {
	To     string `json:"to"`
	Level  string `json:"level"`
	Prefix string `json:"prefix"`
}

type timeoutLock struct {
	mutex sync.Mutex
	id    int64 // unique ID of the current lock. Only allow an unlock if the correct id is passed
}

var locksLock sync.RWMutex
var locks = map[string]*timeoutLock{}

func main() {
	var port int
	var configFile string
	flag.IntVar(&port, "p", 45625, "port")
	flag.StringVar(&configFile, "c", "", "Name of the the file that contains config information")
	flag.Parse()

	var config GlockConfig
	if configFile != "" {
		LoadConfig(configFile, &config)
	}

	if config.Port == 0 {
		config.Port = port
	}

	if config.Logging.Level == "" {
		config.Logging.Level = "info"
	}

	listener, err := net.Listen("tcp", ":"+strconv.Itoa(config.Port))
	if err != nil {
		log.Fatalln("error listening", err)
	}

	golog.SetLogLevel(config.Logging.Level)
	golog.SetLogLocation(config.Logging.To, config.Logging.Prefix)

	golog.Infoln("Glock Server available at port ", config.Port)

	for {
		conn, err := listener.Accept()
		if err != nil {
			golog.Errorln("error accepting", err)
			return
		}
		go handleConn(conn)
	}
}

var (
	unlockedResponse    = []byte("UNLOCKED\n")
	notUnlockedResponse = []byte("NOT_UNLOCKED\n")
	pongResponse        = []byte("PONG\n")

	errBadFormat      = []byte("ERROR bad command format\n")
	errUnknownCommand = []byte("ERROR unknown command\n")
	errLockNotFound   = []byte("ERROR lock not found\n")
)

func handleConn(conn net.Conn) {
	scanner := bufio.NewScanner(conn)
	for scanner.Scan() {
		split := strings.Fields(scanner.Text())

		if split[0] == "PING" {
			conn.Write(pongResponse)
			golog.Debugf("PING PONG")
			continue
		}

		if len(split) < 3 {
			conn.Write(errBadFormat)
			continue
		}

		cmd := split[0]
		key := split[1]
		switch cmd {
		// LOCK <key> <timeout>
		case "LOCK":
			timeout, err := strconv.Atoi(split[2])

			if err != nil {
				conn.Write(errBadFormat)
				golog.Errorln(errBadFormat, ": ", split)
				continue
			}
			locksLock.RLock()
			lock, ok := locks[key]
			locksLock.RUnlock()
			if !ok {
				// lock doesn't exist; create it
				locksLock.Lock()
				lock, ok = locks[key]
				if !ok {
					lock = &timeoutLock{}
					locks[key] = lock
				}
				locksLock.Unlock()
			}

			lock.mutex.Lock()
			id := atomic.AddInt64(&lock.id, 1)
			time.AfterFunc(time.Duration(timeout)*time.Millisecond, func() {
				if atomic.CompareAndSwapInt64(&lock.id, id, id+1) {
					lock.mutex.Unlock()
					golog.Debugf("Timedout: %-12d | Key:  %-15s | Id: %d", timeout, key, id)
				}
			})
			fmt.Fprintf(conn, "LOCKED %v\n", id)

			golog.Debugf("Request:  %-12s | Key:  %-15s | Timeout: %dms", cmd, key, timeout)
			golog.Debugf("Response: %-12s | Key:  %-15s | Id: %d", "LOCKED", key, id)

		// UNLOCK <key> <id>
		case "UNLOCK":
			id, err := strconv.ParseInt(split[2], 10, 64)

			if err != nil {
				golog.Errorln(errBadFormat, ": ", split)
				conn.Write(errBadFormat)
				continue
			}
			locksLock.RLock()
			lock, ok := locks[key]
			locksLock.RUnlock()
			if !ok {
				conn.Write(errLockNotFound)
				golog.Errorln(errLockNotFound, ": ", split)

				golog.Debugf("Request:  %-12s | Key:  %-15s | Id: %d", cmd, key, id)
				golog.Debugf("Response: %-12s | Key:  %-15s", "404", key)
				continue
			}
			if atomic.CompareAndSwapInt64(&lock.id, id, id+1) {
				lock.mutex.Unlock()
				conn.Write(unlockedResponse)

				golog.Debugf("Request:  %-12s | Key:  %-15s | Id: %d", cmd, key, id)
				golog.Debugf("Response: %-12s | Key:  %-15s | Id: %d", "UNLOCKED", key, id)
			} else {
				conn.Write(notUnlockedResponse)

				golog.Debugf("Request:  %-12s | Key:  %-15s | Id: %d", cmd, key, id)
				golog.Debugf("Response: %-12s | Key:  %-15s | Id: %d", "NOT_UNLOCKED", key, id)
			}

		default:
			conn.Write(errUnknownCommand)
			golog.Errorln(errUnknownCommand, ": ", split)
			continue
		}
	}
}

func LoadConfig(configFile string, config interface{}) {
	config_s, err := ioutil.ReadFile(configFile)
	if err != nil {
		log.Fatalln("Couldn't find config at:", configFile)
	}

	err = json.Unmarshal(config_s, &config)
	if err != nil {
		log.Fatalln("Couldn't unmarshal config!", err)
	}
	golog.Infoln("config:", config)
}
<commit_msg>convert errorln to string<commit_after>package main

import (
	"bufio"
	"encoding/json"
	"flag"
	"fmt"
	"io/ioutil"
	"log"
	"net"
	"strconv"
	"strings"
	"sync"
	"sync/atomic"
	"time"

	"github.com/iron-io/golog"
)

type GlockConfig struct {
	Port    int `json:"port"`
	Logging LoggingConfig
}

type LoggingConfig struct {
	To     string `json:"to"`
	Level  string `json:"level"`
	Prefix string `json:"prefix"`
}

type timeoutLock struct {
	mutex sync.Mutex
	id    int64 // unique ID of the current lock. Only allow an unlock if the correct id is passed
}

var locksLock sync.RWMutex
var locks = map[string]*timeoutLock{}

func main() {
	var port int
	var configFile string
	flag.IntVar(&port, "p", 45625, "port")
	flag.StringVar(&configFile, "c", "", "Name of the the file that contains config information")
	flag.Parse()

	var config GlockConfig
	if configFile != "" {
		LoadConfig(configFile, &config)
	}

	if config.Port == 0 {
		config.Port = port
	}

	if config.Logging.Level == "" {
		config.Logging.Level = "info"
	}

	listener, err := net.Listen("tcp", ":"+strconv.Itoa(config.Port))
	if err != nil {
		log.Fatalln("error listening", err)
	}

	golog.SetLogLevel(config.Logging.Level)
	golog.SetLogLocation(config.Logging.To, config.Logging.Prefix)

	golog.Infoln("Glock Server available at port ", config.Port)

	for {
		conn, err := listener.Accept()
		if err != nil {
			golog.Errorln("error accepting", err)
			return
		}
		go handleConn(conn)
	}
}

var (
	unlockedResponse    = []byte("UNLOCKED\n")
	notUnlockedResponse = []byte("NOT_UNLOCKED\n")
	pongResponse        = []byte("PONG\n")

	errBadFormat      = []byte("ERROR bad command format\n")
	errUnknownCommand = []byte("ERROR unknown command\n")
	errLockNotFound   = []byte("ERROR lock not found\n")
)

func handleConn(conn net.Conn) {
	scanner := bufio.NewScanner(conn)
	for scanner.Scan() {
		split := strings.Fields(scanner.Text())

		if split[0] == "PING" {
			conn.Write(pongResponse)
			golog.Debugf("PING PONG")
			continue
		}

		if len(split) < 3 {
			conn.Write(errBadFormat)
			continue
		}

		cmd := split[0]
		key := split[1]
		switch cmd {
		// LOCK <key> <timeout>
		case "LOCK":
			timeout, err := strconv.Atoi(split[2])

			if err != nil {
				conn.Write(errBadFormat)
				golog.Errorln(string(errBadFormat), ": ", split)
				continue
			}
			locksLock.RLock()
			lock, ok := locks[key]
			locksLock.RUnlock()
			if !ok {
				// lock doesn't exist; create it
				locksLock.Lock()
				lock, ok = locks[key]
				if !ok {
					lock = &timeoutLock{}
					locks[key] = lock
				}
				locksLock.Unlock()
			}

			lock.mutex.Lock()
			id := atomic.AddInt64(&lock.id, 1)
			time.AfterFunc(time.Duration(timeout)*time.Millisecond, func() {
				if atomic.CompareAndSwapInt64(&lock.id, id, id+1) {
					lock.mutex.Unlock()
					golog.Debugf("Timedout: %-12d | Key:  %-15s | Id: %d", timeout, key, id)
				}
			})
			fmt.Fprintf(conn, "LOCKED %v\n", id)

			golog.Debugf("Request:  %-12s | Key:  %-15s | Timeout: %dms", cmd, key, timeout)
			golog.Debugf("Response: %-12s | Key:  %-15s | Id: %d", "LOCKED", key, id)

		// UNLOCK <key> <id>
		case "UNLOCK":
			id, err := strconv.ParseInt(split[2], 10, 64)

			if err != nil {
				conn.Write(errBadFormat)
				golog.Errorln(string(errBadFormat), ": ", split)
				continue
			}
			locksLock.RLock()
			lock, ok := locks[key]
			locksLock.RUnlock()
			if !ok {
				conn.Write(errLockNotFound)
				golog.Errorln(string(errLockNotFound), ": ", split)

				golog.Debugf("Request:  %-12s | Key:  %-15s | Id: %d", cmd, key, id)
				golog.Debugf("Response: %-12s | Key:  %-15s", "404", key)
				continue
			}
			if atomic.CompareAndSwapInt64(&lock.id, id, id+1) {
				lock.mutex.Unlock()
				conn.Write(unlockedResponse)

				golog.Debugf("Request:  %-12s | Key:  %-15s | Id: %d", cmd, key, id)
				golog.Debugf("Response: %-12s | Key:  %-15s | Id: %d", "UNLOCKED", key, id)
			} else {
				conn.Write(notUnlockedResponse)

				golog.Debugf("Request:  %-12s | Key:  %-15s | Id: %d", cmd, key, id)
				golog.Debugf("Response: %-12s | Key:  %-15s | Id: %d", "NOT_UNLOCKED", key, id)
			}

		default:
			conn.Write(errUnknownCommand)
			golog.Errorln(string(errUnknownCommand), ": ", split)
			continue
		}
	}
}

func LoadConfig(configFile string, config interface{}) {
	config_s, err := ioutil.ReadFile(configFile)
	if err != nil {
		log.Fatalln("Couldn't find config at:", configFile)
	}

	err = json.Unmarshal(config_s, &config)
	if err != nil {
		log.Fatalln("Couldn't unmarshal config!", err)
	}
	golog.Infoln("config:", config)
}
<|endoftext|>


<commit_before>// Package goawk is an implementation of AWK written in Go.
//
// You can use the command-line "goawk" command or call AWK from your
// Go programs using the "interp" package. The command-line program
// has the same interface as regular awk:
//
//     goawk [-F fs] [-v var=value] [-f progfile | 'prog'] [file ...]
//
// The -F flag specifies the field separator (the default is to split
// on whitespace). The -v flag allows you to set a variable to a
// given value (multiple -v flags allowed). The -f flag allows you to
// read AWK source from a file instead of the 'prog' command-line
// argument. The rest of the arguments are input filenames (default
// is to read from stdin).
//
// To use GoAWK in your Go programs, see README.md or the "interp"
// docs.
//
package main

/*

TODO:
- other interp tests
- performance testing: I/O, allocations, CPU
  + add "go test" benchmarks for various common workloads
  + faster to do switch+case for binary funcs instead of map of funcs?
  + getVar/setVar overhead -- can resolve stuff at compile-time
  + defer in eval/exec -- will this help?

NICE TO HAVE:
- parser: ensure vars aren't used in array context and vice-versa
- interp: flag "unexpected comma-separated expression" at parse time
- fix broken interp tests due to syntax handling

*/

import (
	"bytes"
	"errors"
	"flag"
	"fmt"
	"io/ioutil"
	"os"
	"path/filepath"
	"runtime"
	"runtime/pprof"
	"strings"

	"github.com/benhoyt/goawk/interp"
	"github.com/benhoyt/goawk/lexer"
	"github.com/benhoyt/goawk/parser"
)

func main() {
	progFile := flag.String("f", "", "load AWK source from `progfile`")
	fieldSep := flag.String("F", " ", "field separator")
	var vars varFlags
	flag.Var(&vars, "v", "variable `assignment` (name=value)")

	debug := flag.Bool("d", false, "debug mode (print parsed AST to stderr)")
	cpuprofile := flag.String("cpuprofile", "", "write CPU profile to `file`")
	memprofile := flag.String("memprofile", "", "write memory profile to `file`")

	flag.Parse()
	args := flag.Args()

	var src []byte
	if *progFile != "" {
		var err error
		src, err = ioutil.ReadFile(*progFile)
		if err != nil {
			errorExit("%s", err)
		}
	} else {
		if len(args) < 1 {
			errorExit("usage: goawk [-F fs] [-v var=value] [-f progfile | 'prog'] [file ...]")
		}
		src = []byte(args[0])
		args = args[1:]
	}

	prog, err := parser.ParseProgram(src)
	if err != nil {
		errMsg := fmt.Sprintf("%s", err)
		if err, ok := err.(*parser.ParseError); ok {
			showSourceLine(src, err.Position, len(errMsg))
		}
		errorExit(errMsg)
	}
	if *debug {
		fmt.Fprintln(os.Stderr, prog)
	}

	if *cpuprofile != "" {
		f, err := os.Create(*cpuprofile)
		if err != nil {
			errorExit("could not create CPU profile: %v", err)
		}
		if err := pprof.StartCPUProfile(f); err != nil {
			errorExit("could not start CPU profile: %v", err)
		}
	}

	p := interp.New(nil, nil)
	p.SetVar("FS", *fieldSep)
	for _, v := range vars {
		p.SetVar(v.name, v.value)
	}

	interpArgs := []string{filepath.Base(os.Args[0])}
	interpArgs = append(interpArgs, args...)
	p.SetArgs(interpArgs)
	err = p.Exec(prog, os.Stdin, args)
	if err != nil {
		errorExit("%s", err)
	}

	if *cpuprofile != "" {
		pprof.StopCPUProfile()
	}

	if *memprofile != "" {
		f, err := os.Create(*memprofile)
		if err != nil {
			errorExit("could not create memory profile: %v", err)
		}
		runtime.GC() // get up-to-date statistics
		if err := pprof.WriteHeapProfile(f); err != nil {
			errorExit("could not write memory profile: %v", err)
		}
		f.Close()
	}

	os.Exit(p.ExitStatus())
}

func showSourceLine(src []byte, pos lexer.Position, dividerLen int) {
	divider := strings.Repeat("-", dividerLen)
	if divider != "" {
		fmt.Fprintln(os.Stderr, divider)
	}
	lines := bytes.Split(src, []byte{'\n'})
	srcLine := string(lines[pos.Line-1])
	numTabs := strings.Count(srcLine[:pos.Column-1], "\t")
	fmt.Fprintln(os.Stderr, strings.Replace(srcLine, "\t", "    ", -1))
	fmt.Fprintln(os.Stderr, strings.Repeat(" ", pos.Column-1)+strings.Repeat("   ", numTabs)+"^")
	if divider != "" {
		fmt.Fprintln(os.Stderr, divider)
	}
}

func errorExit(format string, args ...interface{}) {
	fmt.Fprintf(os.Stderr, format+"\n", args...)
	os.Exit(1)
}

type varFlag struct {
	name  string
	value string
}

type varFlags []varFlag

func (v *varFlags) String() string {
	return ""
}

func (v *varFlags) Set(value string) error {
	parts := strings.SplitN(value, "=", 2)
	if len(parts) != 2 {
		return errors.New("must be name=value")
	}
	*v = append(*v, varFlag{parts[0], parts[1]})
	return nil
}
<commit_msg>Allow multiple -f source files<commit_after>// Package goawk is an implementation of AWK written in Go.
//
// You can use the command-line "goawk" command or call AWK from your
// Go programs using the "interp" package. The command-line program
// has the same interface as regular awk:
//
//     goawk [-F fs] [-v var=value] [-f progfile | 'prog'] [file ...]
//
// The -F flag specifies the field separator (the default is to split
// on whitespace). The -v flag allows you to set a variable to a
// given value (multiple -v flags allowed). The -f flag allows you to
// read AWK source from a file instead of the 'prog' command-line
// argument. The rest of the arguments are input filenames (default
// is to read from stdin).
//
// To use GoAWK in your Go programs, see README.md or the "interp"
// docs.
//
package main

/*

TODO:
- other interp tests
- performance testing: I/O, allocations, CPU
  + add "go test" benchmarks for various common workloads
  + faster to do switch+case for binary funcs instead of map of funcs?
  + getVar/setVar overhead -- can resolve stuff at compile-time
  + defer in eval/exec -- will this help?

NICE TO HAVE:
- fix broken interp tests due to syntax handling
- proper CSV support: https://news.ycombinator.com/item?id=17788471
- think about linear time string concat: https://news.ycombinator.com/item?id=17788028
- support for calling Go functions: https://news.ycombinator.com/item?id=17788915
- parser: ensure vars aren't used in array context and vice-versa
- interp: flag "unexpected comma-separated expression" at parse time

*/

import (
	"bytes"
	"flag"
	"fmt"
	"os"
	"path/filepath"
	"runtime"
	"runtime/pprof"
	"strings"

	"github.com/benhoyt/goawk/interp"
	"github.com/benhoyt/goawk/lexer"
	"github.com/benhoyt/goawk/parser"
)

func main() {
	var progFiles multiString
	flag.Var(&progFiles, "f", "load AWK source from `progfile` (multiple allowed)")
	fieldSep := flag.String("F", " ", "field separator")
	var vars multiString
	flag.Var(&vars, "v", "name=value variable `assignment` (multiple allowed)")

	debug := flag.Bool("d", false, "debug mode (print parsed AST to stderr)")
	cpuprofile := flag.String("cpuprofile", "", "write CPU profile to `file`")
	memprofile := flag.String("memprofile", "", "write memory profile to `file`")

	flag.Parse()
	args := flag.Args()

	var src []byte
	if len(progFiles) > 0 {
		buf := &bytes.Buffer{}
		for _, progFile := range progFiles {
			f, err := os.Open(progFile)
			if err != nil {
				errorExit("%s", err)
			}
			_, err = buf.ReadFrom(f)
			if err != nil {
				f.Close()
				errorExit("%s", err)
			}
			f.Close()
			buf.WriteByte('\n')
		}
		src = buf.Bytes()
	} else {
		if len(args) < 1 {
			errorExit("usage: goawk [-F fs] [-v var=value] [-f progfile | 'prog'] [file ...]")
		}
		src = []byte(args[0])
		args = args[1:]
	}

	prog, err := parser.ParseProgram(src)
	if err != nil {
		errMsg := fmt.Sprintf("%s", err)
		if err, ok := err.(*parser.ParseError); ok {
			showSourceLine(src, err.Position, len(errMsg))
		}
		errorExit(errMsg)
	}
	if *debug {
		fmt.Fprintln(os.Stderr, prog)
	}

	if *cpuprofile != "" {
		f, err := os.Create(*cpuprofile)
		if err != nil {
			errorExit("could not create CPU profile: %v", err)
		}
		if err := pprof.StartCPUProfile(f); err != nil {
			errorExit("could not start CPU profile: %v", err)
		}
	}

	p := interp.New(nil, nil)
	p.SetVar("FS", *fieldSep)
	for _, v := range vars {
		parts := strings.SplitN(v, "=", 2)
		if len(parts) != 2 {
			errorExit("-v flag must be in format name=value")
		}
		p.SetVar(parts[0], parts[1])
	}

	interpArgs := []string{filepath.Base(os.Args[0])}
	interpArgs = append(interpArgs, args...)
	p.SetArgs(interpArgs)
	err = p.Exec(prog, os.Stdin, args)
	if err != nil {
		errorExit("%s", err)
	}

	if *cpuprofile != "" {
		pprof.StopCPUProfile()
	}

	if *memprofile != "" {
		f, err := os.Create(*memprofile)
		if err != nil {
			errorExit("could not create memory profile: %v", err)
		}
		runtime.GC() // get up-to-date statistics
		if err := pprof.WriteHeapProfile(f); err != nil {
			errorExit("could not write memory profile: %v", err)
		}
		f.Close()
	}

	os.Exit(p.ExitStatus())
}

func showSourceLine(src []byte, pos lexer.Position, dividerLen int) {
	divider := strings.Repeat("-", dividerLen)
	if divider != "" {
		fmt.Fprintln(os.Stderr, divider)
	}
	lines := bytes.Split(src, []byte{'\n'})
	srcLine := string(lines[pos.Line-1])
	numTabs := strings.Count(srcLine[:pos.Column-1], "\t")
	fmt.Fprintln(os.Stderr, strings.Replace(srcLine, "\t", "    ", -1))
	fmt.Fprintln(os.Stderr, strings.Repeat(" ", pos.Column-1)+strings.Repeat("   ", numTabs)+"^")
	if divider != "" {
		fmt.Fprintln(os.Stderr, divider)
	}
}

func errorExit(format string, args ...interface{}) {
	fmt.Fprintf(os.Stderr, format+"\n", args...)
	os.Exit(1)
}

type multiString []string

func (m *multiString) String() string {
	return fmt.Sprintf("%v", []string(*m))
}

func (m *multiString) Set(value string) error {
	*m = append(*m, value)
	return nil
}
<|endoftext|>


<commit_before>// helps in developing the application by having
// source code change detection, recompiliation,
// and rerunning.
package main

import (
  // "errors"
  //goflag "flag"
  "github.com/howeyc/fsnotify"
  "github.com/jessevdk/go-flags"
  "github.com/kballard/go-shellquote"
  gobuild "go/build"
  "go/parser"
  "go/token"
  "log"
  "os"
  "os/exec"
  "path/filepath"
  "regexp"
  "strconv"
  "strings"
  "sync"
  "time"
)

type Options struct {
  Args    string   `short:"a" long:"args" description:"Arguments to pass to the program"`
  Include []string `short:"i" default:"" long:"include" description:"Regexp of dirs to include for watching. Can be specified multiple times. Default: .*"`
  Exclude []string `short:"e" default:"" long:"exclude" description:"Regexp of dirs to exclude from watching. Can be specified multiple times. Default: ^\.*$"`
  Files   []string `short:"f" default:"" long:"files" description:"Regexp of files that, if changed, will cause a build/run. Can be specified multiple times. Default: ^(.*\.go|.*\.yaml|.*\.conf)$"`
  Watch   []string `short:"w" long:"watch" description:"The directory(s) where to watch and scan for dependencies"`

  include []*regexp.Regexp
  exclude []*regexp.Regexp
  files   []*regexp.Regexp
}

var (
  wpaths = make(map[string]string)
  // curPkg  = ""
  dir     = ""
  program = ""
  opts    = &Options{}
  // flagSet = goflag.NewFlagSet(os.Args[0], goflag.ExitOnError)
  // args    = flagSet.String("args", "", "Arguments to pass to the program")
  // include = flagSet.String("include", "*", "Pattern of dirs to include")
  // exclude = flagSet.String("exclude", "", "Pattern of dirs to exclude")
)

func parseArgs() []string {
  s, err := shellquote.Split(opts.Args)
  if err != nil {
    panic(err)
  }

  return s
}

func build() error {
  log.Printf("Rebuilding %s...\n", program)
  cmd := exec.Command("go", "build")
  cmd.Stdout = os.Stdout
  cmd.Stderr = os.Stderr
  err := cmd.Run()
  if err != nil {
    return err
  }

  return nil
}

func run() (*exec.Cmd, error) {
  log.Printf("Running %s...\n", program)
  cmd := exec.Command(filepath.Join(dir, program), parseArgs()...)
  cmd.Stdout = os.Stdout
  cmd.Stderr = os.Stderr

  err := cmd.Start()
  if err != nil {
    return nil, err
  }

  return cmd, nil
}

func buildAndRun() (*exec.Cmd, error) {
  err := build()
  if err != nil {
    return nil, err
  } else {
    cmd, err := run()
    if err != nil {
      return nil, err
    }
    return cmd, nil
  }

  panic("unreachable")
  return nil, nil
}

func shouldRerun(name string) (ret bool) {
  ret = false

  // um... should be configurable?
  ret = !strings.HasPrefix(filepath.Base(name), ".")
  if !ret {
    return
  }

  for _, r := range opts.files {
    if r.MatchString(name) {
      ret = true
      return
    }
  }

  return false
}

func watcher() {
  log.Println("Running watcher")

  var wg sync.WaitGroup
  watcher, err := fsnotify.NewWatcher()
  if err != nil {
    log.Fatal(err)
  }

  wg.Add(1)
  go func() {
    defer wg.Done()
    cmd, err := buildAndRun()
    if err != nil {
      log.Println(err)
    }

    for {
      select {
      case ev := <-watcher.Event:
        // basic throttling. we only do something when
        // we receive no file system events after
        // a certain time
      LOOP:
        for {
          select {
          case <-watcher.Event:
            continue LOOP
          case <-time.After(300 * time.Millisecond):
            break LOOP
          }
        }

        if ev.IsModify() || ev.IsCreate() {
          if shouldRerun(ev.Name) {
            if cmd != nil && cmd.Process != nil {
              log.Printf("Killing %s...\n", program)
              cmd.Process.Kill()
              cmd.Wait()
            }

            cmd, err = buildAndRun()
            if err != nil {
              log.Println(err)
            }
          }
        }
      case <-watcher.Error:
        //log.Println("error:", err)
      }
    }
  }()

  for _, value := range wpaths {
    err = watcher.Watch(value)
    if err != nil {
      log.Fatal(err)
    }
  }

  wg.Wait()
}

// func usage() {
//   fmt.Printf("Usage of %s:\n", os.Args[0])
//   fmt.Printf("  %s [args] [program]\n\n", os.Args[0])
//   fmt.Printf("Args:\n")
//   usagePrintDefaults()
//   fmt.Println("")
// }

// func usagePrintDefaults() {
//   flagSet.VisitAll(func(flag *goflag.Flag) {
//     format := "  -%s=%s\t%s\n"
//     // if _, ok := flag.Value.(*stringValue); ok {
//     //   // put quotes on the value
//     //   format = "  -%s=%q: %s\n"
//     // }
//     fmt.Printf(format, flag.Name, flag.DefValue, flag.Usage)
//   })
// }

// find where on the fs a package is
func which(pkg string) string {
  for _, top := range strings.Split(os.Getenv("GOPATH"), ":") {
    dir := top + "/src/" + pkg
    _, err := os.Stat(dir)
    if err == nil {
      return dir
    }
    p := err.(*os.PathError)
    if !os.IsNotExist(p.Err) {
      log.Print(err)
    }
  }
  return ""
}

func shouldWatch(path string) bool {
  for _, r := range opts.exclude {
    if r.MatchString(path) {
      log.Println("exclude:", path)
      return false
    }
  }

  for _, r := range opts.include {
    if r.MatchString(path) {
      return true
    }
  }

  return false
}

func getWatchDirs(path string, info os.FileInfo, err error) error {
  if err != nil {
    log.Print(err)
  }

  if info.IsDir() {
    // add all dirs we encounter
    if shouldWatch(path) {
      wpaths[path] = path
    }
  } else if !info.IsDir() && strings.HasSuffix(path, ".go") {
    // parse the go file and add all imports
    err = getWatchDirsFromFile(path)
    if err != nil {
      log.Print(err)
    }
  }
  return nil
}

// finds all the watch directories from the imports of the file
func getWatchDirsFromFile(path string) error {
  fset := token.NewFileSet()
  f, err := parser.ParseFile(fset, path, nil, parser.ParseComments)
  if err != nil {
    return err
  }

  for _, s := range f.Imports {
    path, err := strconv.Unquote(s.Path.Value)
    if err != nil {
      return err // can't happen
    }

    wpath := which(path)

    if wpath != "" {
      if shouldWatch(wpath) {
        wpaths[wpath] = wpath
      }
    }
  }

  return nil
}

// func getCurrentPkg() (string, error) {
//   gopath := os.Getenv("GOPATH")
//   if gopath == "" {
//     return "", errors.New("missing GOPATH")
//   }

//   dot, err := os.Getwd()
//   if err != nil {
//     return "", err
//   }

//   items := strings.Split(gopath, ":")
//   for _, top := range items {
//     top = top + "/src/"
//     if strings.HasPrefix(dot, top) {
//       return dot[len(top):], nil
//     }
//   }

//   return "", errors.New("cwd not found in GOPATH")
// }

func main() {
  var err error

  dir, _ = os.Getwd()

  opts = &Options{}
  args, err := flags.Parse(opts)
  if err != nil {
    os.Exit(1)
  }

  if len(opts.Include) == 0 {
    opts.Include = []string{`.*`}
  }
  if len(opts.Exclude) == 0 {
    opts.Exclude = []string{`^\.*$`}
  }
  if len(opts.Files) == 0 {
    opts.Files = []string{`^(.*\.go|.*\.yaml|.*\.conf)$`}
  }

  // flagSet.Usage = usage
  // flagSet.Parse(os.Args[1:])
  // program = flagSet.Arg(0)

  if len(args) > 0 {
    program = args[0]
  }

  // attempt to find program name
  if program == "" {
    _, spath := filepath.Split(dir)

    exeSuffix := ""
    if gobuild.Default.GOOS == "windows" {
      exeSuffix = ".exe"
    }

    program = spath + exeSuffix
  }

  // curPkg, err = getCurrentPkg()
  // if err != nil {
  //   fmt.Println(err)
  // }

  // build regexps
  for _, r := range opts.Include {
    opts.include = append(opts.include, regexp.MustCompile(r))
  }
  for _, r := range opts.Exclude {
    opts.exclude = append(opts.exclude, regexp.MustCompile(r))
  }
  for _, r := range opts.Files {
    opts.files = append(opts.files, regexp.MustCompile(r))
  }

  for _, d := range append([]string{dir}, opts.Watch...) {
    err = filepath.Walk(d, getWatchDirs)
    if err != nil {
      log.Fatal(err)
    }
  }

  watcher()
}
<commit_msg>Fixed a weird bug with go-flags.<commit_after>// helps in developing the application by having
// source code change detection, recompiliation,
// and rerunning.
package main

import (
  // "errors"
  //goflag "flag"
  "github.com/howeyc/fsnotify"
  "github.com/jessevdk/go-flags"
  "github.com/kballard/go-shellquote"
  gobuild "go/build"
  "go/parser"
  "go/token"
  "log"
  "os"
  "os/exec"
  "path/filepath"
  "regexp"
  "strconv"
  "strings"
  "sync"
  "time"
)

type Options struct {
  Args    string   `short:"a" long:"args" description:"Arguments to pass to the program"`
  Include []string `short:"i" default:"" long:"include" description:"Regexp of dirs to include for watching. Can be specified multiple times. Default: .*"`
  Exclude []string `short:"e" default:"" long:"exclude" description:"Regexp of dirs to exclude from watching. Can be specified multiple times. Default: ^\.*$"`
  Files   []string `short:"f" default:"" long:"files" description:"Regexp of files that, if changed, will cause a build/run. Can be specified multiple times. Default: ^(.*\.go|.*\.yaml|.*\.conf)$"`
  Watch   []string `short:"w" long:"watch" description:"The directory(s) where to watch and scan for dependencies"`

  include []*regexp.Regexp
  exclude []*regexp.Regexp
  files   []*regexp.Regexp
}

var (
  wpaths = make(map[string]string)
  // curPkg  = ""
  dir     = ""
  program = ""
  opts    = &Options{}
  // flagSet = goflag.NewFlagSet(os.Args[0], goflag.ExitOnError)
  // args    = flagSet.String("args", "", "Arguments to pass to the program")
  // include = flagSet.String("include", "*", "Pattern of dirs to include")
  // exclude = flagSet.String("exclude", "", "Pattern of dirs to exclude")
)

func parseArgs() []string {
  s, err := shellquote.Split(opts.Args)
  if err != nil {
    panic(err)
  }

  return s
}

func build() error {
  log.Printf("Rebuilding %s...\n", program)
  cmd := exec.Command("go", "build")
  cmd.Stdout = os.Stdout
  cmd.Stderr = os.Stderr
  err := cmd.Run()
  if err != nil {
    return err
  }

  return nil
}

func run() (*exec.Cmd, error) {
  log.Printf("Running %s...\n", program)
  cmd := exec.Command(filepath.Join(dir, program), parseArgs()...)
  cmd.Stdout = os.Stdout
  cmd.Stderr = os.Stderr

  err := cmd.Start()
  if err != nil {
    return nil, err
  }

  return cmd, nil
}

func buildAndRun() (*exec.Cmd, error) {
  err := build()
  if err != nil {
    return nil, err
  } else {
    cmd, err := run()
    if err != nil {
      return nil, err
    }
    return cmd, nil
  }

  panic("unreachable")
  return nil, nil
}

func shouldRerun(name string) (ret bool) {
  ret = false

  // um... should be configurable?
  ret = !strings.HasPrefix(filepath.Base(name), ".")
  if !ret {
    return
  }

  for _, r := range opts.files {
    if r.MatchString(name) {
      ret = true
      return
    }
  }

  return false
}

func watcher() {
  log.Println("Running watcher")

  var wg sync.WaitGroup
  watcher, err := fsnotify.NewWatcher()
  if err != nil {
    log.Fatal(err)
  }

  wg.Add(1)
  go func() {
    defer wg.Done()
    cmd, err := buildAndRun()
    if err != nil {
      log.Println(err)
    }

    for {
      select {
      case ev := <-watcher.Event:
        // basic throttling. we only do something when
        // we receive no file system events after
        // a certain time
      LOOP:
        for {
          select {
          case <-watcher.Event:
            continue LOOP
          case <-time.After(300 * time.Millisecond):
            break LOOP
          }
        }

        if ev.IsModify() || ev.IsCreate() {
          if shouldRerun(ev.Name) {
            if cmd != nil && cmd.Process != nil {
              log.Printf("Killing %s...\n", program)
              cmd.Process.Kill()
              cmd.Wait()
            }

            cmd, err = buildAndRun()
            if err != nil {
              log.Println(err)
            }
          }
        }
      case <-watcher.Error:
        //log.Println("error:", err)
      }
    }
  }()

  for _, value := range wpaths {
    err = watcher.Watch(value)
    if err != nil {
      log.Fatal(err)
    }
  }

  wg.Wait()
}

// func usage() {
//   fmt.Printf("Usage of %s:\n", os.Args[0])
//   fmt.Printf("  %s [args] [program]\n\n", os.Args[0])
//   fmt.Printf("Args:\n")
//   usagePrintDefaults()
//   fmt.Println("")
// }

// func usagePrintDefaults() {
//   flagSet.VisitAll(func(flag *goflag.Flag) {
//     format := "  -%s=%s\t%s\n"
//     // if _, ok := flag.Value.(*stringValue); ok {
//     //   // put quotes on the value
//     //   format = "  -%s=%q: %s\n"
//     // }
//     fmt.Printf(format, flag.Name, flag.DefValue, flag.Usage)
//   })
// }

// find where on the fs a package is
func which(pkg string) string {
  for _, top := range strings.Split(os.Getenv("GOPATH"), ":") {
    dir := top + "/src/" + pkg
    _, err := os.Stat(dir)
    if err == nil {
      return dir
    }
    p := err.(*os.PathError)
    if !os.IsNotExist(p.Err) {
      log.Print(err)
    }
  }
  return ""
}

func shouldWatch(path string) bool {
  for _, r := range opts.exclude {
    if r.MatchString(path) {
      log.Println("exclude:", path)
      return false
    }
  }

  for _, r := range opts.include {
    if r.MatchString(path) {
      return true
    }
  }

  return false
}

func getWatchDirs(path string, info os.FileInfo, err error) error {
  if err != nil {
    log.Print(err)
  }

  if info.IsDir() {
    // add all dirs we encounter
    if shouldWatch(path) {
      wpaths[path] = path
    }
  } else if !info.IsDir() && strings.HasSuffix(path, ".go") {
    // parse the go file and add all imports
    err = getWatchDirsFromFile(path)
    if err != nil {
      log.Print(err)
    }
  }
  return nil
}

// finds all the watch directories from the imports of the file
func getWatchDirsFromFile(path string) error {
  fset := token.NewFileSet()
  f, err := parser.ParseFile(fset, path, nil, parser.ParseComments)
  if err != nil {
    return err
  }

  for _, s := range f.Imports {
    path, err := strconv.Unquote(s.Path.Value)
    if err != nil {
      return err // can't happen
    }

    wpath := which(path)

    if wpath != "" {
      if shouldWatch(wpath) {
        wpaths[wpath] = wpath
      }
    }
  }

  return nil
}

// func getCurrentPkg() (string, error) {
//   gopath := os.Getenv("GOPATH")
//   if gopath == "" {
//     return "", errors.New("missing GOPATH")
//   }

//   dot, err := os.Getwd()
//   if err != nil {
//     return "", err
//   }

//   items := strings.Split(gopath, ":")
//   for _, top := range items {
//     top = top + "/src/"
//     if strings.HasPrefix(dot, top) {
//       return dot[len(top):], nil
//     }
//   }

//   return "", errors.New("cwd not found in GOPATH")
// }

func main() {
  var err error

  dir, _ = os.Getwd()

  opts = &Options{}
  args, err := flags.Parse(opts)
  if err != nil {
    os.Exit(1)
  }

  if len(opts.Include) == 0 || opts.Include[0] == "" {
    opts.Include = []string{`.*`}
  }
  if len(opts.Exclude) == 0 || opts.Exclude[0] == "" {
    opts.Exclude = []string{`^\.*$`}
  }
  if len(opts.Files) == 0 || opts.Files[0] == "" {
    opts.Files = []string{`^(.*\.go|.*\.yaml|.*\.conf)$`}
  }

  // flagSet.Usage = usage
  // flagSet.Parse(os.Args[1:])
  // program = flagSet.Arg(0)

  if len(args) > 0 {
    program = args[0]
  }

  // attempt to find program name
  if program == "" {
    _, spath := filepath.Split(dir)

    exeSuffix := ""
    if gobuild.Default.GOOS == "windows" {
      exeSuffix = ".exe"
    }

    program = spath + exeSuffix
  }

  // curPkg, err = getCurrentPkg()
  // if err != nil {
  //   fmt.Println(err)
  // }

  // build regexps
  for _, r := range opts.Include {
    opts.include = append(opts.include, regexp.MustCompile(r))
  }
  for _, r := range opts.Exclude {
    opts.exclude = append(opts.exclude, regexp.MustCompile(r))
  }
  for _, r := range opts.Files {
    opts.files = append(opts.files, regexp.MustCompile(r))
  }

  for _, d := range append([]string{dir}, opts.Watch...) {
    err = filepath.Walk(d, getWatchDirs)
    if err != nil {
      log.Fatal(err)
    }
  }

  watcher()
}
<|endoftext|>


<commit_before>package main

import (
	"io/ioutil"
	"os/exec"
)

func GoFmt(dir string) error {
	cmd := exec.Command("gofmt", "-w", dir)
	cmd.Stdout = ioutil.Discard
	cmd.Stderr = ioutil.Discard

	if err := cmd.Run(); err != nil {
		return err
	}

	return nil
}
<commit_msg>Replate gofmt command with go/printer and go/parser<commit_after>package main

import (
	"bytes"
	"go/ast"
	"go/parser"
	"go/printer"
	"go/token"
	"io"
	"io/ioutil"
	"os"
	"path/filepath"
	"strings"
)

func processFile(filename string, in io.Reader, out io.Writer, stdin bool) error {
	if in == nil {
		f, err := os.Open(filename)
		if err != nil {
			return err
		}
		defer f.Close()
		in = f
	}

	src, err := ioutil.ReadAll(in)
	if err != nil {
		return err
	}
	fileSet := token.NewFileSet()
	file, err := parser.ParseFile(fileSet, filename, src, parser.ParseComments)
	if err != nil {
		return err
	}

	ast.SortImports(fileSet, file)

	var buf bytes.Buffer
	tabWidth := 8
	printerMode := printer.UseSpaces | printer.TabIndent
	err = (&printer.Config{Mode: printerMode, Tabwidth: tabWidth}).Fprint(&buf, fileSet, file)
	if err != nil {
		return err
	}

	res := buf.Bytes()
	if !bytes.Equal(src, res) {
		err = ioutil.WriteFile(filename, res, 0)
		if err != nil {
			return err
		}
	}
	return nil
}

func isGofile(f os.FileInfo) bool {
	name := f.Name()
	return !f.IsDir() && !strings.HasPrefix(name, ".") && strings.HasSuffix(name, ".go")
}

func visitFile(path string, f os.FileInfo, err error) error {

	if err == nil && isGofile(f) {
		err = processFile(path, nil, os.Stdout, false)
	}

	if err != nil {
		return err
	}

	return nil
}

func walkDir(path string) {
	filepath.Walk(path, visitFile)
}

func GoFmt(path string) error {
	dir, err := os.Stat(path)
	if err != nil {
		return err
	}

	if dir.IsDir() {
		walkDir(path)
	}
	return nil
}
<|endoftext|>


<commit_before>package golis

import (
	"bytes"
	"encoding/binary"
	"errors"
	"fmt"
	"io"
	"net"
	"os"
)

//系统变量定义

var (
	GolisHandler IoHandler                //事件处理
	Unpacket     func([]byte) interface{} //拆包
	Packet       func(interface{}) []byte //封包
)

//定义session
type Iosession struct {
	conn net.Conn
}

//session写入数据
func (this *Iosession) Write(message *interface{}) {
	//触发消息发送事件
	GolisHandler.MessageSent(this, message)
	data := Packet(message)
	totalLen := len(data)
	this.conn.Write(append(IntToBytes(totalLen), data...))
}

//事件触发接口定义
type IoHandler interface {
	//session打开
	SessionOpened(session *Iosession)
	//session关闭
	SessionClosed(session *Iosession)
	//收到消息时触发
	MessageReceived(session *Iosession, message interface{})
	//消息发送时触发
	MessageSent(session *Iosession, message interface{})
}

//运行golis
//netPro：运行协议参数，tcp/udp
//laddr ：程序监听ip和端口，如127.0.0.1:8080
func Run(netPro, laddr string) {
	Log("初始化系统完成")
	netLis, err := net.Listen(netPro, laddr)
	CheckError(err)
	defer netLis.Close()
	Log("等待客户端连接...")
	for {
		conn, err := netLis.Accept()
		if err != nil {
			continue
		}
		go connectHandle(conn)
	}
}

//处理新连接
func connectHandle(conn net.Conn) {
	//声明一个临时缓冲区，用来存储被截断的数据
	tmpBuffer := make([]byte, 0)
	buffer := make([]byte, 1024)

	//声明一个管道用于接收解包的数据
	readerChannel := make(chan []byte, 16)
	//创建session
	session := Iosession{conn}
	//触发sessionCreated事件
	GolisHandler.SessionOpened(&session)

	go reader(&session, readerChannel)

	flag := true
	for flag {
		n, err := conn.Read(buffer)
		switch err {
		case nil:
			//			tmp, data, err := getReadyData(append(tmpBuffer, buffer[:n]...))
			//			tmpBuffer = tmp
			//			if err != nil {
			//				Log(err.Error())
			//			} else {
			//				readFromData(&session, data)
			//			}
			tmpBuffer = unpack(append(tmpBuffer, buffer[:n]...), readerChannel)
		case io.EOF:
			Log("client is disconnected")
			//session关闭
			GolisHandler.SessionClosed(&session)
			flag = false
			break
		default:
			Log("none")
		}
	}

}

const (
	constDataLength = 4
)

//解包
func unpack(buffer []byte, readerChannel chan []byte) []byte {
	length := len(buffer)

	var i int
	for i = 0; i < length; i = i + 1 {
		if length < i+constDataLength {
			break
		}
		messageLength := BytesToInt(buffer[i : i+constDataLength])
		if length < i+constDataLength+messageLength {
			break
		}
		data := buffer[i+constDataLength : i+constDataLength+messageLength]
		readerChannel <- data
		i += constDataLength + messageLength - 1
	}

	if i == length {
		return make([]byte, 0)
	}
	return buffer[i:]
}

//协议中查看协议头是否满足一个协议报
func getReadyData(buffer []byte) ([]byte, []byte, error) {
	length := len(buffer)
	//	Log("length = ", length)
	if length >= 4 {
		totalLen := BytesToInt(buffer[0:4]) //get totalLen
		if totalLen == 0 {
			return make([]byte, 0), nil, errors.New("msg is null")
		} else if totalLen <= length-4 {
			return buffer[totalLen+4:], buffer[4:totalLen], nil
		}

	}
	return buffer, nil, errors.New("msg is not ready")
}

func reader(session *Iosession, readerChannel chan []byte) {
	for {
		select {
		case data := <-readerChannel:
			readFromData(session, data)
		}
	}
}

//从准备好的数据读取
func readFromData(session *Iosession, data []byte) {
	message := Unpacket(data) //拆包
	//收到消息时到达
	GolisHandler.MessageReceived(session, message)
}

//整形转换成字节
func IntToBytes(n int) []byte {
	x := int32(n)

	bytesBuffer := bytes.NewBuffer([]byte{})
	binary.Write(bytesBuffer, binary.BigEndian, x)
	return bytesBuffer.Bytes()
}

//字节转换成整形
func BytesToInt(b []byte) int {
	bytesBuffer := bytes.NewBuffer(b)

	var x int32
	binary.Read(bytesBuffer, binary.BigEndian, &x)

	return int(x)
}

//整形64转换成字节
func Int64ToBytes(n int64) []byte {
	x := int64(n)

	bytesBuffer := bytes.NewBuffer([]byte{})
	binary.Write(bytesBuffer, binary.BigEndian, x)
	return bytesBuffer.Bytes()
}

//字节转换成整形64
func BytesToInt64(b []byte) int64 {
	bytesBuffer := bytes.NewBuffer(b)

	var x int64
	binary.Read(bytesBuffer, binary.BigEndian, &x)

	return int64(x)
}

//整形64转换成字节
func Int8ToBytes(n int8) []byte {
	x := int8(n)

	bytesBuffer := bytes.NewBuffer([]byte{})
	binary.Write(bytesBuffer, binary.BigEndian, x)
	return bytesBuffer.Bytes()
}

//字节转换成整形8
func BytesToInt8(b []byte) int8 {
	bytesBuffer := bytes.NewBuffer(b)

	var x int8
	binary.Read(bytesBuffer, binary.BigEndian, &x)

	return int8(x)
}

//简单日志输出
func Log(v ...interface{}) {
	fmt.Println(v...)
}

//检查错误并退出程序
func CheckError(err error) {
	if err != nil {
		fmt.Fprintln(os.Stderr, "Fatal error:%s", err.Error())
		os.Exit(1)
	}
}
<commit_msg>*修改传参类型<commit_after>package golis

import (
	"bytes"
	"encoding/binary"
	"errors"
	"fmt"
	"io"
	"net"
	"os"
)

//系统变量定义

var (
	GolisHandler IoHandler                //事件处理
	Unpacket     func([]byte) interface{} //拆包
	Packet       func(interface{}) []byte //封包
)

//定义session
type Iosession struct {
	conn net.Conn
}

//session写入数据
func (this *Iosession) Write(message interface{}) {
	//触发消息发送事件
	GolisHandler.MessageSent(this, message)
	data := Packet(message)
	totalLen := len(data)
	this.conn.Write(append(IntToBytes(totalLen), data...))
}

//事件触发接口定义
type IoHandler interface {
	//session打开
	SessionOpened(session *Iosession)
	//session关闭
	SessionClosed(session *Iosession)
	//收到消息时触发
	MessageReceived(session *Iosession, message interface{})
	//消息发送时触发
	MessageSent(session *Iosession, message interface{})
}

//运行golis
//netPro：运行协议参数，tcp/udp
//laddr ：程序监听ip和端口，如127.0.0.1:8080
func Run(netPro, laddr string) {
	Log("初始化系统完成")
	netLis, err := net.Listen(netPro, laddr)
	CheckError(err)
	defer netLis.Close()
	Log("等待客户端连接...")
	for {
		conn, err := netLis.Accept()
		if err != nil {
			continue
		}
		go connectHandle(conn)
	}
}

//处理新连接
func connectHandle(conn net.Conn) {
	//声明一个临时缓冲区，用来存储被截断的数据
	tmpBuffer := make([]byte, 0)
	buffer := make([]byte, 1024)

	//声明一个管道用于接收解包的数据
	readerChannel := make(chan []byte, 16)
	//创建session
	session := Iosession{conn}
	//触发sessionCreated事件
	GolisHandler.SessionOpened(&session)

	go reader(&session, readerChannel)

	flag := true
	for flag {
		n, err := conn.Read(buffer)
		switch err {
		case nil:
			//			tmp, data, err := getReadyData(append(tmpBuffer, buffer[:n]...))
			//			tmpBuffer = tmp
			//			if err != nil {
			//				Log(err.Error())
			//			} else {
			//				readFromData(&session, data)
			//			}
			tmpBuffer = unpack(append(tmpBuffer, buffer[:n]...), readerChannel)
		case io.EOF:
			Log("client is disconnected")
			//session关闭
			GolisHandler.SessionClosed(&session)
			flag = false
			break
		default:
			Log("none")
		}
	}

}

const (
	constDataLength = 4
)

//解包
func unpack(buffer []byte, readerChannel chan []byte) []byte {
	length := len(buffer)

	var i int
	for i = 0; i < length; i = i + 1 {
		if length < i+constDataLength {
			break
		}
		messageLength := BytesToInt(buffer[i : i+constDataLength])
		if length < i+constDataLength+messageLength {
			break
		}
		data := buffer[i+constDataLength : i+constDataLength+messageLength]
		readerChannel <- data
		i += constDataLength + messageLength - 1
	}

	if i == length {
		return make([]byte, 0)
	}
	return buffer[i:]
}

//协议中查看协议头是否满足一个协议报
func getReadyData(buffer []byte) ([]byte, []byte, error) {
	length := len(buffer)
	//	Log("length = ", length)
	if length >= 4 {
		totalLen := BytesToInt(buffer[0:4]) //get totalLen
		if totalLen == 0 {
			return make([]byte, 0), nil, errors.New("msg is null")
		} else if totalLen <= length-4 {
			return buffer[totalLen+4:], buffer[4:totalLen], nil
		}

	}
	return buffer, nil, errors.New("msg is not ready")
}

func reader(session *Iosession, readerChannel chan []byte) {
	for {
		select {
		case data := <-readerChannel:
			readFromData(session, data)
		}
	}
}

//从准备好的数据读取
func readFromData(session *Iosession, data []byte) {
	message := Unpacket(data) //拆包
	//收到消息时到达
	GolisHandler.MessageReceived(session, message)
}

//整形转换成字节
func IntToBytes(n int) []byte {
	x := int32(n)

	bytesBuffer := bytes.NewBuffer([]byte{})
	binary.Write(bytesBuffer, binary.BigEndian, x)
	return bytesBuffer.Bytes()
}

//字节转换成整形
func BytesToInt(b []byte) int {
	bytesBuffer := bytes.NewBuffer(b)

	var x int32
	binary.Read(bytesBuffer, binary.BigEndian, &x)

	return int(x)
}

//整形64转换成字节
func Int64ToBytes(n int64) []byte {
	x := int64(n)

	bytesBuffer := bytes.NewBuffer([]byte{})
	binary.Write(bytesBuffer, binary.BigEndian, x)
	return bytesBuffer.Bytes()
}

//字节转换成整形64
func BytesToInt64(b []byte) int64 {
	bytesBuffer := bytes.NewBuffer(b)

	var x int64
	binary.Read(bytesBuffer, binary.BigEndian, &x)

	return int64(x)
}

//整形64转换成字节
func Int8ToBytes(n int8) []byte {
	x := int8(n)

	bytesBuffer := bytes.NewBuffer([]byte{})
	binary.Write(bytesBuffer, binary.BigEndian, x)
	return bytesBuffer.Bytes()
}

//字节转换成整形8
func BytesToInt8(b []byte) int8 {
	bytesBuffer := bytes.NewBuffer(b)

	var x int8
	binary.Read(bytesBuffer, binary.BigEndian, &x)

	return int8(x)
}

//简单日志输出
func Log(v ...interface{}) {
	fmt.Println(v...)
}

//检查错误并退出程序
func CheckError(err error) {
	if err != nil {
		fmt.Fprintln(os.Stderr, "Fatal error:%s", err.Error())
		os.Exit(1)
	}
}
<|endoftext|>