<commit_before>package command

import (
	"fmt"
	"sync"
	"time"

	"github.com/hashicorp/nomad/api"
	"github.com/hashicorp/nomad/nomad/structs"
	"github.com/mitchellh/cli"
)

const (
	// updateWait is the amount of time to wait between status
	// updates. Because the monitor is poll-based, we use this
	// delay to avoid overwhelming the API server.
	updateWait = time.Second
)

// evalState is used to store the current "state of the world"
// in the context of monitoring an evaluation.
type evalState struct {
	status string
	desc   string
	node   string
	job    string
	allocs map[string]*allocState
	wait   time.Duration
	index  uint64
}

// newEvalState creates and initializes a new monitorState
func newEvalState() *evalState {
	return &evalState{
		status: structs.EvalStatusPending,
		allocs: make(map[string]*allocState),
	}
}

// allocState is used to track the state of an allocation
type allocState struct {
	id          string
	group       string
	node        string
	desired     string
	desiredDesc string
	client      string
	clientDesc  string
	index       uint64

	// full is the allocation struct with full details. This
	// must be queried for explicitly so it is only included
	// if there is important error information inside.
	full *api.Allocation
}

// monitor wraps an evaluation monitor and holds metadata and
// state information.
type monitor struct {
	ui     cli.Ui
	client *api.Client
	state  *evalState

	// length determines the number of characters for identifiers in the ui.
	length int

	sync.Mutex
}

// newMonitor returns a new monitor. The returned monitor will
// write output information to the provided ui. The length parameter determines
// the number of characters for identifiers in the ui.
func newMonitor(ui cli.Ui, client *api.Client, length int) *monitor {
	mon := &monitor{
		ui: &cli.PrefixedUi{
			InfoPrefix:   "==> ",
			OutputPrefix: "    ",
			ErrorPrefix:  "==> ",
			Ui:           ui,
		},
		client: client,
		state:  newEvalState(),
		length: length,
	}
	return mon
}

// update is used to update our monitor with new state. It can be
// called whether the passed information is new or not, and will
// only dump update messages when state changes.
func (m *monitor) update(update *evalState) {
	m.Lock()
	defer m.Unlock()

	existing := m.state

	// Swap in the new state at the end
	defer func() {
		m.state = update
	}()

	// Check if the evaluation was triggered by a node
	if existing.node == "" && update.node != "" {
		m.ui.Output(fmt.Sprintf("Evaluation triggered by node %q",
			limit(update.node, m.length)))
	}

	// Check if the evaluation was triggered by a job
	if existing.job == "" && update.job != "" {
		m.ui.Output(fmt.Sprintf("Evaluation triggered by job %q", update.job))
	}

	// Check the allocations
	for allocID, alloc := range update.allocs {
		if existing, ok := existing.allocs[allocID]; !ok {
			switch {
			case alloc.desired == structs.AllocDesiredStatusFailed:
				// New allocs with desired state failed indicate
				// scheduling failure.
				m.ui.Output(fmt.Sprintf("Scheduling error for group %q (%s)",
					alloc.group, alloc.desiredDesc))

				// Log the client status, if any provided
				if alloc.clientDesc != "" {
					m.ui.Output("Client reported status: " + alloc.clientDesc)
				}

				// Generate a more descriptive error for why the allocation
				// failed and dump it to the screen
				if alloc.full != nil {
					dumpAllocStatus(m.ui, alloc.full, m.length)
				}

			case alloc.index < update.index:
				// New alloc with create index lower than the eval
				// create index indicates modification
				m.ui.Output(fmt.Sprintf(
					"Allocation %q modified: node %q, group %q",
					limit(alloc.id, m.length), limit(alloc.node, m.length), alloc.group))

			case alloc.desired == structs.AllocDesiredStatusRun:
				// New allocation with desired status running
				m.ui.Output(fmt.Sprintf(
					"Allocation %q created: node %q, group %q",
					limit(alloc.id, m.length), limit(alloc.node, m.length), alloc.group))
			}
		} else {
			switch {
			case existing.client != alloc.client:
				// Allocation status has changed
				m.ui.Output(fmt.Sprintf(
					"Allocation %q status changed: %q -> %q (%s)",
					limit(alloc.id, m.length), existing.client, alloc.client, alloc.clientDesc))
			}
		}
	}

	// Check if the status changed. We skip any transitions to pending status.
	if existing.status != "" &&
		update.status != structs.AllocClientStatusPending &&
		existing.status != update.status {
		m.ui.Output(fmt.Sprintf("Evaluation status changed: %q -> %q",
			existing.status, update.status))
	}
}

// monitor is used to start monitoring the given evaluation ID. It
// writes output directly to the monitor's ui, and returns the
// exit code for the command. If allowPrefix is false, monitor will only accept
// exact matching evalIDs.
//
// The return code will be 0 on successful evaluation. If there are
// problems scheduling the job (impossible constraints, resources
// exhausted, etc), then the return code will be 2. For any other
// failures (API connectivity, internal errors, etc), the return code
// will be 1.
func (m *monitor) monitor(evalID string, allowPrefix bool) int {
	// Track if we encounter a scheduling failure. This can only be
	// detected while querying allocations, so we use this bool to
	// carry that status into the return code.
	var schedFailure bool

	// The user may have specified a prefix as eval id. We need to lookup the
	// full id from the database first. Since we do this in a loop we need a
	// variable to keep track if we've already written the header message.
	var headerWritten bool

	// Add the initial pending state
	m.update(newEvalState())

	for {
		// Query the evaluation
		eval, _, err := m.client.Evaluations().Info(evalID, nil)
		if err != nil {
			if !allowPrefix {
				m.ui.Error(fmt.Sprintf("No evaluation with id %q found", evalID))
				return 1
			}
			if len(evalID) == 1 {
				m.ui.Error(fmt.Sprintf("Identifier must contain at least two characters."))
				return 1
			}
			if len(evalID)%2 == 1 {
				// Identifiers must be of even length, so we strip off the last byte
				// to provide a consistent user experience.
				evalID = evalID[:len(evalID)-1]
			}

			evals, _, err := m.client.Evaluations().PrefixList(evalID)
			if err != nil {
				m.ui.Error(fmt.Sprintf("Error reading evaluation: %s", err))
				return 1
			}
			if len(evals) == 0 {
				m.ui.Error(fmt.Sprintf("No evaluation(s) with prefix or id %q found", evalID))
				return 1
			}
			if len(evals) > 1 {
				// Format the evaluations
				out := make([]string, len(evals)+1)
				out[0] = "ID|Priority|Type|Triggered By|Status"
				for i, eval := range evals {
					out[i+1] = fmt.Sprintf("%s|%d|%s|%s|%s",
						limit(eval.ID, m.length),
						eval.Priority,
						eval.Type,
						eval.TriggeredBy,
						eval.Status)
				}
				m.ui.Output(fmt.Sprintf("Prefix matched multiple evaluations\n\n%s", formatList(out)))
				return 0
			}
			// Prefix lookup matched a single evaluation
			eval, _, err = m.client.Evaluations().Info(evals[0].ID, nil)
			if err != nil {
				m.ui.Error(fmt.Sprintf("Error reading evaluation: %s", err))
			}
		}

		if !headerWritten {
			m.ui.Info(fmt.Sprintf("Monitoring evaluation %q", limit(eval.ID, m.length)))
			headerWritten = true
		}

		// Create the new eval state.
		state := newEvalState()
		state.status = eval.Status
		state.desc = eval.StatusDescription
		state.node = eval.NodeID
		state.job = eval.JobID
		state.wait = eval.Wait
		state.index = eval.CreateIndex

		// Query the allocations associated with the evaluation
		allocs, _, err := m.client.Evaluations().Allocations(eval.ID, nil)
		if err != nil {
			m.ui.Error(fmt.Sprintf("Error reading allocations: %s", err))
			return 1
		}

		// Add the allocs to the state
		for _, alloc := range allocs {
			state.allocs[alloc.ID] = &allocState{
				id:          alloc.ID,
				group:       alloc.TaskGroup,
				node:        alloc.NodeID,
				desired:     alloc.DesiredStatus,
				desiredDesc: alloc.DesiredDescription,
				client:      alloc.ClientStatus,
				clientDesc:  alloc.ClientDescription,
				index:       alloc.CreateIndex,
			}

			// If we have a scheduling error, query the full allocation
			// to get the details.
			if alloc.DesiredStatus == structs.AllocDesiredStatusFailed {
				schedFailure = true
				failed, _, err := m.client.Allocations().Info(alloc.ID, nil)
				if err != nil {
					m.ui.Error(fmt.Sprintf("Error querying allocation: %s", err))
					return 1
				}
				state.allocs[alloc.ID].full = failed
			}
		}

		// Update the state
		m.update(state)

		switch eval.Status {
		case structs.EvalStatusComplete, structs.EvalStatusFailed:
			m.ui.Info(fmt.Sprintf("Evaluation %q finished with status %q",
				limit(eval.ID, m.length), eval.Status))
		default:
			// Wait for the next update
			time.Sleep(updateWait)
			continue
		}

		// Monitor the next eval in the chain, if present
		if eval.NextEval != "" {
			m.ui.Info(fmt.Sprintf(
				"Monitoring next evaluation %q in %s",
				eval.NextEval, eval.Wait))

			// Skip some unnecessary polling
			time.Sleep(eval.Wait)

			// Reset the state and monitor the new eval
			m.state = newEvalState()
			return m.monitor(eval.NextEval, allowPrefix)
		}
		break
	}

	// Treat scheduling failures specially using a dedicated exit code.
	// This makes it easier to detect failures from the CLI.
	if schedFailure {
		return 2
	}

	return 0
}

// dumpAllocStatus is a helper to generate a more user-friendly error message
// for scheduling failures, displaying a high level status of why the job
// could not be scheduled out.
func dumpAllocStatus(ui cli.Ui, alloc *api.Allocation, length int) {
	// Print filter stats
	ui.Output(fmt.Sprintf("Allocation %q status %q (%d/%d nodes filtered)",
		limit(alloc.ID, length), alloc.ClientStatus,
		alloc.Metrics.NodesFiltered, alloc.Metrics.NodesEvaluated))

	// Print a helpful message if we have an eligibility problem
	if alloc.Metrics.NodesEvaluated == 0 {
		ui.Output("  * No nodes were eligible for evaluation")
	}

	// Print a helpful message if the user has asked for a DC that has no
	// available nodes.
	for dc, available := range alloc.Metrics.NodesAvailable {
		if available == 0 {
			ui.Output(fmt.Sprintf("  * No nodes are available in datacenter %q", dc))
		}
	}

	// Print filter info
	for class, num := range alloc.Metrics.ClassFiltered {
		ui.Output(fmt.Sprintf("  * Class %q filtered %d nodes", class, num))
	}
	for cs, num := range alloc.Metrics.ConstraintFiltered {
		ui.Output(fmt.Sprintf("  * Constraint %q filtered %d nodes", cs, num))
	}

	// Print exhaustion info
	if ne := alloc.Metrics.NodesExhausted; ne > 0 {
		ui.Output(fmt.Sprintf("  * Resources exhausted on %d nodes", ne))
	}
	for class, num := range alloc.Metrics.ClassExhausted {
		ui.Output(fmt.Sprintf("  * Class %q exhausted on %d nodes", class, num))
	}
	for dim, num := range alloc.Metrics.DimensionExhausted {
		ui.Output(fmt.Sprintf("  * Dimension %q exhausted on %d nodes", dim, num))
	}

	// Print scores
	for name, score := range alloc.Metrics.Scores {
		ui.Output(fmt.Sprintf("  * Score %q = %f", name, score))
	}
}
<commit_msg>Shorted eval id and only print waiting if time > 0<commit_after>package command

import (
	"fmt"
	"sync"
	"time"

	"github.com/hashicorp/nomad/api"
	"github.com/hashicorp/nomad/nomad/structs"
	"github.com/mitchellh/cli"
)

const (
	// updateWait is the amount of time to wait between status
	// updates. Because the monitor is poll-based, we use this
	// delay to avoid overwhelming the API server.
	updateWait = time.Second
)

// evalState is used to store the current "state of the world"
// in the context of monitoring an evaluation.
type evalState struct {
	status string
	desc   string
	node   string
	job    string
	allocs map[string]*allocState
	wait   time.Duration
	index  uint64
}

// newEvalState creates and initializes a new monitorState
func newEvalState() *evalState {
	return &evalState{
		status: structs.EvalStatusPending,
		allocs: make(map[string]*allocState),
	}
}

// allocState is used to track the state of an allocation
type allocState struct {
	id          string
	group       string
	node        string
	desired     string
	desiredDesc string
	client      string
	clientDesc  string
	index       uint64

	// full is the allocation struct with full details. This
	// must be queried for explicitly so it is only included
	// if there is important error information inside.
	full *api.Allocation
}

// monitor wraps an evaluation monitor and holds metadata and
// state information.
type monitor struct {
	ui     cli.Ui
	client *api.Client
	state  *evalState

	// length determines the number of characters for identifiers in the ui.
	length int

	sync.Mutex
}

// newMonitor returns a new monitor. The returned monitor will
// write output information to the provided ui. The length parameter determines
// the number of characters for identifiers in the ui.
func newMonitor(ui cli.Ui, client *api.Client, length int) *monitor {
	mon := &monitor{
		ui: &cli.PrefixedUi{
			InfoPrefix:   "==> ",
			OutputPrefix: "    ",
			ErrorPrefix:  "==> ",
			Ui:           ui,
		},
		client: client,
		state:  newEvalState(),
		length: length,
	}
	return mon
}

// update is used to update our monitor with new state. It can be
// called whether the passed information is new or not, and will
// only dump update messages when state changes.
func (m *monitor) update(update *evalState) {
	m.Lock()
	defer m.Unlock()

	existing := m.state

	// Swap in the new state at the end
	defer func() {
		m.state = update
	}()

	// Check if the evaluation was triggered by a node
	if existing.node == "" && update.node != "" {
		m.ui.Output(fmt.Sprintf("Evaluation triggered by node %q",
			limit(update.node, m.length)))
	}

	// Check if the evaluation was triggered by a job
	if existing.job == "" && update.job != "" {
		m.ui.Output(fmt.Sprintf("Evaluation triggered by job %q", update.job))
	}

	// Check the allocations
	for allocID, alloc := range update.allocs {
		if existing, ok := existing.allocs[allocID]; !ok {
			switch {
			case alloc.desired == structs.AllocDesiredStatusFailed:
				// New allocs with desired state failed indicate
				// scheduling failure.
				m.ui.Output(fmt.Sprintf("Scheduling error for group %q (%s)",
					alloc.group, alloc.desiredDesc))

				// Log the client status, if any provided
				if alloc.clientDesc != "" {
					m.ui.Output("Client reported status: " + alloc.clientDesc)
				}

				// Generate a more descriptive error for why the allocation
				// failed and dump it to the screen
				if alloc.full != nil {
					dumpAllocStatus(m.ui, alloc.full, m.length)
				}

			case alloc.index < update.index:
				// New alloc with create index lower than the eval
				// create index indicates modification
				m.ui.Output(fmt.Sprintf(
					"Allocation %q modified: node %q, group %q",
					limit(alloc.id, m.length), limit(alloc.node, m.length), alloc.group))

			case alloc.desired == structs.AllocDesiredStatusRun:
				// New allocation with desired status running
				m.ui.Output(fmt.Sprintf(
					"Allocation %q created: node %q, group %q",
					limit(alloc.id, m.length), limit(alloc.node, m.length), alloc.group))
			}
		} else {
			switch {
			case existing.client != alloc.client:
				// Allocation status has changed
				m.ui.Output(fmt.Sprintf(
					"Allocation %q status changed: %q -> %q (%s)",
					limit(alloc.id, m.length), existing.client, alloc.client, alloc.clientDesc))
			}
		}
	}

	// Check if the status changed. We skip any transitions to pending status.
	if existing.status != "" &&
		update.status != structs.AllocClientStatusPending &&
		existing.status != update.status {
		m.ui.Output(fmt.Sprintf("Evaluation status changed: %q -> %q",
			existing.status, update.status))
	}
}

// monitor is used to start monitoring the given evaluation ID. It
// writes output directly to the monitor's ui, and returns the
// exit code for the command. If allowPrefix is false, monitor will only accept
// exact matching evalIDs.
//
// The return code will be 0 on successful evaluation. If there are
// problems scheduling the job (impossible constraints, resources
// exhausted, etc), then the return code will be 2. For any other
// failures (API connectivity, internal errors, etc), the return code
// will be 1.
func (m *monitor) monitor(evalID string, allowPrefix bool) int {
	// Track if we encounter a scheduling failure. This can only be
	// detected while querying allocations, so we use this bool to
	// carry that status into the return code.
	var schedFailure bool

	// The user may have specified a prefix as eval id. We need to lookup the
	// full id from the database first. Since we do this in a loop we need a
	// variable to keep track if we've already written the header message.
	var headerWritten bool

	// Add the initial pending state
	m.update(newEvalState())

	for {
		// Query the evaluation
		eval, _, err := m.client.Evaluations().Info(evalID, nil)
		if err != nil {
			if !allowPrefix {
				m.ui.Error(fmt.Sprintf("No evaluation with id %q found", evalID))
				return 1
			}
			if len(evalID) == 1 {
				m.ui.Error(fmt.Sprintf("Identifier must contain at least two characters."))
				return 1
			}
			if len(evalID)%2 == 1 {
				// Identifiers must be of even length, so we strip off the last byte
				// to provide a consistent user experience.
				evalID = evalID[:len(evalID)-1]
			}

			evals, _, err := m.client.Evaluations().PrefixList(evalID)
			if err != nil {
				m.ui.Error(fmt.Sprintf("Error reading evaluation: %s", err))
				return 1
			}
			if len(evals) == 0 {
				m.ui.Error(fmt.Sprintf("No evaluation(s) with prefix or id %q found", evalID))
				return 1
			}
			if len(evals) > 1 {
				// Format the evaluations
				out := make([]string, len(evals)+1)
				out[0] = "ID|Priority|Type|Triggered By|Status"
				for i, eval := range evals {
					out[i+1] = fmt.Sprintf("%s|%d|%s|%s|%s",
						limit(eval.ID, m.length),
						eval.Priority,
						eval.Type,
						eval.TriggeredBy,
						eval.Status)
				}
				m.ui.Output(fmt.Sprintf("Prefix matched multiple evaluations\n\n%s", formatList(out)))
				return 0
			}
			// Prefix lookup matched a single evaluation
			eval, _, err = m.client.Evaluations().Info(evals[0].ID, nil)
			if err != nil {
				m.ui.Error(fmt.Sprintf("Error reading evaluation: %s", err))
			}
		}

		if !headerWritten {
			m.ui.Info(fmt.Sprintf("Monitoring evaluation %q", limit(eval.ID, m.length)))
			headerWritten = true
		}

		// Create the new eval state.
		state := newEvalState()
		state.status = eval.Status
		state.desc = eval.StatusDescription
		state.node = eval.NodeID
		state.job = eval.JobID
		state.wait = eval.Wait
		state.index = eval.CreateIndex

		// Query the allocations associated with the evaluation
		allocs, _, err := m.client.Evaluations().Allocations(eval.ID, nil)
		if err != nil {
			m.ui.Error(fmt.Sprintf("Error reading allocations: %s", err))
			return 1
		}

		// Add the allocs to the state
		for _, alloc := range allocs {
			state.allocs[alloc.ID] = &allocState{
				id:          alloc.ID,
				group:       alloc.TaskGroup,
				node:        alloc.NodeID,
				desired:     alloc.DesiredStatus,
				desiredDesc: alloc.DesiredDescription,
				client:      alloc.ClientStatus,
				clientDesc:  alloc.ClientDescription,
				index:       alloc.CreateIndex,
			}

			// If we have a scheduling error, query the full allocation
			// to get the details.
			if alloc.DesiredStatus == structs.AllocDesiredStatusFailed {
				schedFailure = true
				failed, _, err := m.client.Allocations().Info(alloc.ID, nil)
				if err != nil {
					m.ui.Error(fmt.Sprintf("Error querying allocation: %s", err))
					return 1
				}
				state.allocs[alloc.ID].full = failed
			}
		}

		// Update the state
		m.update(state)

		switch eval.Status {
		case structs.EvalStatusComplete, structs.EvalStatusFailed:
			m.ui.Info(fmt.Sprintf("Evaluation %q finished with status %q",
				limit(eval.ID, m.length), eval.Status))
		default:
			// Wait for the next update
			time.Sleep(updateWait)
			continue
		}

		// Monitor the next eval in the chain, if present
		if eval.NextEval != "" {
			if eval.Wait.Nanoseconds() != 0 {
				m.ui.Info(fmt.Sprintf(
					"Monitoring next evaluation %q in %s",
					limit(eval.NextEval, m.length), eval.Wait))

				// Skip some unnecessary polling
				time.Sleep(eval.Wait)
			}

			// Reset the state and monitor the new eval
			m.state = newEvalState()
			return m.monitor(eval.NextEval, allowPrefix)
		}
		break
	}

	// Treat scheduling failures specially using a dedicated exit code.
	// This makes it easier to detect failures from the CLI.
	if schedFailure {
		return 2
	}

	return 0
}

// dumpAllocStatus is a helper to generate a more user-friendly error message
// for scheduling failures, displaying a high level status of why the job
// could not be scheduled out.
func dumpAllocStatus(ui cli.Ui, alloc *api.Allocation, length int) {
	// Print filter stats
	ui.Output(fmt.Sprintf("Allocation %q status %q (%d/%d nodes filtered)",
		limit(alloc.ID, length), alloc.ClientStatus,
		alloc.Metrics.NodesFiltered, alloc.Metrics.NodesEvaluated))

	// Print a helpful message if we have an eligibility problem
	if alloc.Metrics.NodesEvaluated == 0 {
		ui.Output("  * No nodes were eligible for evaluation")
	}

	// Print a helpful message if the user has asked for a DC that has no
	// available nodes.
	for dc, available := range alloc.Metrics.NodesAvailable {
		if available == 0 {
			ui.Output(fmt.Sprintf("  * No nodes are available in datacenter %q", dc))
		}
	}

	// Print filter info
	for class, num := range alloc.Metrics.ClassFiltered {
		ui.Output(fmt.Sprintf("  * Class %q filtered %d nodes", class, num))
	}
	for cs, num := range alloc.Metrics.ConstraintFiltered {
		ui.Output(fmt.Sprintf("  * Constraint %q filtered %d nodes", cs, num))
	}

	// Print exhaustion info
	if ne := alloc.Metrics.NodesExhausted; ne > 0 {
		ui.Output(fmt.Sprintf("  * Resources exhausted on %d nodes", ne))
	}
	for class, num := range alloc.Metrics.ClassExhausted {
		ui.Output(fmt.Sprintf("  * Class %q exhausted on %d nodes", class, num))
	}
	for dim, num := range alloc.Metrics.DimensionExhausted {
		ui.Output(fmt.Sprintf("  * Dimension %q exhausted on %d nodes", dim, num))
	}

	// Print scores
	for name, score := range alloc.Metrics.Scores {
		ui.Output(fmt.Sprintf("  * Score %q = %f", name, score))
	}
}
<|endoftext|>


<commit_before>// Copyright 2015 Datajin Technologies, Inc. All rights reserved.
// Use of this source code is governed by an Artistic-2
// license that can be found in the LICENSE file.

package commands

import (
	"fmt"
	"github.com/codegangsta/cli"
	"github.com/mktmpio/go-mktmpio"
	"io/ioutil"
	"log"
)

// Config stores the shared mktmpio config used by all the cli commands
var Config = mktmpio.LoadConfig()

var (
	client    *mktmpio.Client
	clientErr error
	logger    = log.New(ioutil.Discard, "", log.LUTC|log.Lshortfile|log.Ldate|log.Ltime)
)

// PopulateConfig populates the shared config used by all the cli commands.
func PopulateConfig(c *cli.Context) error {
	if c.GlobalBool("debug") {
		logger.SetOutput(c.App.Writer)
	}
	if c.GlobalIsSet("token") {
		Config.Token = c.GlobalString("token")
	}
	if c.GlobalIsSet("url") {
		Config.URL = c.GlobalString("url")
	}
	logger.Printf("loaded config: %v", Config)
	client, clientErr = mktmpio.NewClient(Config)
	if clientErr != nil {
		fmt.Fprintf(c.App.Writer, "Error initializing client: %s\n", clientErr)
	} else {
		client.UserAgent = "mktmpio-cli/" + c.App.Version + " (go-mktmpio)"
	}
	logger.Printf("Initialized: %+v", client)
	return clientErr
}
<commit_msg>set debug logger in mktmpio.Client<commit_after>// Copyright 2015 Datajin Technologies, Inc. All rights reserved.
// Use of this source code is governed by an Artistic-2
// license that can be found in the LICENSE file.

package commands

import (
	"fmt"
	"github.com/codegangsta/cli"
	"github.com/mktmpio/go-mktmpio"
	"io/ioutil"
	"log"
)

// Config stores the shared mktmpio config used by all the cli commands
var Config = mktmpio.LoadConfig()

var (
	client    *mktmpio.Client
	clientErr error
	logger    = log.New(ioutil.Discard, "", log.LUTC|log.Lshortfile|log.Ldate|log.Ltime)
)

// PopulateConfig populates the shared config used by all the cli commands.
func PopulateConfig(c *cli.Context) error {
	if c.GlobalBool("debug") {
		logger.SetOutput(c.App.Writer)
	}
	if c.GlobalIsSet("token") {
		Config.Token = c.GlobalString("token")
	}
	if c.GlobalIsSet("url") {
		Config.URL = c.GlobalString("url")
	}
	logger.Printf("loaded config: %v", Config)
	client, clientErr = mktmpio.NewClient(Config)
	if clientErr != nil {
		fmt.Fprintf(c.App.Writer, "Error initializing client: %s\n", clientErr)
	} else {
		client.UserAgent = "mktmpio-cli/" + c.App.Version + " (go-mktmpio)"
		client.SetLogger(logger)
	}
	logger.Printf("Initialized: %+v", client)
	return clientErr
}
<|endoftext|>


<commit_before>package buf

import (
	"io"
	"time"

	"v2ray.com/core/common/errors"
	"v2ray.com/core/common/signal"
)

type dataHandler func(MultiBuffer)

type copyHandler struct {
	onData []dataHandler
}

// SizeCounter is for counting bytes copied by Copy().
type SizeCounter struct {
	Size int64
}

// CopyOption is an option for copying data.
type CopyOption func(*copyHandler)

// UpdateActivity is a CopyOption to update activity on each data copy operation.
func UpdateActivity(timer signal.ActivityUpdater) CopyOption {
	return func(handler *copyHandler) {
		handler.onData = append(handler.onData, func(MultiBuffer) {
			timer.Update()
		})
	}
}

// CountSize is a CopyOption that sums the total size of data copied into the given SizeCounter.
func CountSize(sc *SizeCounter) CopyOption {
	return func(handler *copyHandler) {
		handler.onData = append(handler.onData, func(b MultiBuffer) {
			sc.Size += int64(b.Len())
		})
	}
}

type readError struct {
	error
}

func (e readError) Error() string {
	return e.error.Error()
}

func (e readError) Inner() error {
	return e.error
}

func IsReadError(err error) bool {
	_, ok := err.(readError)
	return ok
}

type writeError struct {
	error
}

func (e writeError) Error() string {
	return e.error.Error()
}

func (e writeError) Inner() error {
	return e.error
}

func IsWriteError(err error) bool {
	_, ok := err.(writeError)
	return ok
}

func copyInternal(reader Reader, writer Writer, handler *copyHandler) error {
	for {
		buffer, err := reader.ReadMultiBuffer()
		if !buffer.IsEmpty() {
			for _, handler := range handler.onData {
				handler(buffer)
			}

			if werr := writer.WriteMultiBuffer(buffer); werr != nil {
				return writeError{werr}
			}
		}

		if err != nil {
			return readError{err}
		}
	}
}

// Copy dumps all payload from reader to writer or stops when an error occurs. It returns nil when EOF.
func Copy(reader Reader, writer Writer, options ...CopyOption) error {
	var handler copyHandler
	for _, option := range options {
		option(&handler)
	}
	err := copyInternal(reader, writer, &handler)
	if err != nil && errors.Cause(err) != io.EOF {
		return err
	}
	return nil
}

var ErrNotTimeoutReader = newError("not a TimeoutReader")

func CopyOnceTimeout(reader Reader, writer Writer, timeout time.Duration) error {
	timeoutReader, ok := reader.(TimeoutReader)
	if !ok {
		return ErrNotTimeoutReader
	}
	mb, err := timeoutReader.ReadMultiBufferTimeout(timeout)
	if err != nil {
		return err
	}
	return writer.WriteMultiBuffer(mb)
}
<commit_msg>comments<commit_after>package buf

import (
	"io"
	"time"

	"v2ray.com/core/common/errors"
	"v2ray.com/core/common/signal"
)

type dataHandler func(MultiBuffer)

type copyHandler struct {
	onData []dataHandler
}

// SizeCounter is for counting bytes copied by Copy().
type SizeCounter struct {
	Size int64
}

// CopyOption is an option for copying data.
type CopyOption func(*copyHandler)

// UpdateActivity is a CopyOption to update activity on each data copy operation.
func UpdateActivity(timer signal.ActivityUpdater) CopyOption {
	return func(handler *copyHandler) {
		handler.onData = append(handler.onData, func(MultiBuffer) {
			timer.Update()
		})
	}
}

// CountSize is a CopyOption that sums the total size of data copied into the given SizeCounter.
func CountSize(sc *SizeCounter) CopyOption {
	return func(handler *copyHandler) {
		handler.onData = append(handler.onData, func(b MultiBuffer) {
			sc.Size += int64(b.Len())
		})
	}
}

type readError struct {
	error
}

func (e readError) Error() string {
	return e.error.Error()
}

func (e readError) Inner() error {
	return e.error
}

// IsReadError returns true if the error in Copy() comes from reading.
func IsReadError(err error) bool {
	_, ok := err.(readError)
	return ok
}

type writeError struct {
	error
}

func (e writeError) Error() string {
	return e.error.Error()
}

func (e writeError) Inner() error {
	return e.error
}

// IsWriteError returns true if the error in Copy() comes from writing.
func IsWriteError(err error) bool {
	_, ok := err.(writeError)
	return ok
}

func copyInternal(reader Reader, writer Writer, handler *copyHandler) error {
	for {
		buffer, err := reader.ReadMultiBuffer()
		if !buffer.IsEmpty() {
			for _, handler := range handler.onData {
				handler(buffer)
			}

			if werr := writer.WriteMultiBuffer(buffer); werr != nil {
				return writeError{werr}
			}
		}

		if err != nil {
			return readError{err}
		}
	}
}

// Copy dumps all payload from reader to writer or stops when an error occurs. It returns nil when EOF.
func Copy(reader Reader, writer Writer, options ...CopyOption) error {
	var handler copyHandler
	for _, option := range options {
		option(&handler)
	}
	err := copyInternal(reader, writer, &handler)
	if err != nil && errors.Cause(err) != io.EOF {
		return err
	}
	return nil
}

var ErrNotTimeoutReader = newError("not a TimeoutReader")

func CopyOnceTimeout(reader Reader, writer Writer, timeout time.Duration) error {
	timeoutReader, ok := reader.(TimeoutReader)
	if !ok {
		return ErrNotTimeoutReader
	}
	mb, err := timeoutReader.ReadMultiBufferTimeout(timeout)
	if err != nil {
		return err
	}
	return writer.WriteMultiBuffer(mb)
}
<|endoftext|>


<commit_before>package api

import (
	"net/http"

	log "github.com/Sirupsen/logrus"
	"github.com/gorilla/mux"
	"github.com/minchao/smsender/smsender"
	"github.com/urfave/negroni"
	"gopkg.in/go-playground/validator.v9"
)

type Message struct {
	Recipient  string `json:"recipient" validate:"required"` // Validate E.164 format
	Body       string `json:"body" validate:"required"`
	Originator string `json:"originator"`
}

type Result struct {
	Message Message `json:"message"`
}

type Server struct {
	addr     string
	sender   *smsender.Sender
	in       chan *smsender.Message
	validate *validator.Validate
}

func NewServer(addr string, sender *smsender.Sender) *Server {
	server := Server{
		addr:   addr,
		sender: sender,
		in:     make(chan *smsender.Message, 1000),
	}
	return &server
}

func (s *Server) Run() {
	go s.sender.Stream(s.in)

	r := mux.NewRouter().StrictSlash(true)
	r.HandleFunc("/", s.Hello).Methods("GET")
	r.HandleFunc("/routes", s.Routes).Methods("GET")
	r.HandleFunc("/send", s.Send).Methods("POST")

	n := negroni.New()
	n.UseFunc(logger)
	n.UseHandler(r)

	log.Infof("Listening for HTTP on %s", s.addr)
	log.Fatal(http.ListenAndServe(s.addr, n))
}

func (s *Server) Hello(w http.ResponseWriter, r *http.Request) {
	render(w, 200, "Hello!")
}

func (s *Server) Routes(w http.ResponseWriter, r *http.Request) {
	render(w, 200, s.sender.GetRoutes())
}

func (s *Server) Send(w http.ResponseWriter, r *http.Request) {
	var msg Message
	err := getInput(r.Body, &msg, newValidate())
	if err != nil {
		render(w, http.StatusBadRequest, formErrorMessage(err))
		return
	}

	s.in <- &smsender.Message{
		Recipient:  msg.Recipient,
		Body:       msg.Body,
		Originator: msg.Originator,
	}

	// TODO result
	render(w, 200, Result{msg})
}
<commit_msg>Remove unused field from api.Server struct<commit_after>package api

import (
	"net/http"

	log "github.com/Sirupsen/logrus"
	"github.com/gorilla/mux"
	"github.com/minchao/smsender/smsender"
	"github.com/urfave/negroni"
)

type Message struct {
	Recipient  string `json:"recipient" validate:"required"` // Validate E.164 format
	Body       string `json:"body" validate:"required"`
	Originator string `json:"originator"`
}

type Result struct {
	Message Message `json:"message"`
}

type Server struct {
	addr   string
	sender *smsender.Sender
	in     chan *smsender.Message
}

func NewServer(addr string, sender *smsender.Sender) *Server {
	server := Server{
		addr:   addr,
		sender: sender,
		in:     make(chan *smsender.Message, 1000),
	}
	return &server
}

func (s *Server) Run() {
	go s.sender.Stream(s.in)

	r := mux.NewRouter().StrictSlash(true)
	r.HandleFunc("/", s.Hello).Methods("GET")
	r.HandleFunc("/routes", s.Routes).Methods("GET")
	r.HandleFunc("/send", s.Send).Methods("POST")

	n := negroni.New()
	n.UseFunc(logger)
	n.UseHandler(r)

	log.Infof("Listening for HTTP on %s", s.addr)
	log.Fatal(http.ListenAndServe(s.addr, n))
}

func (s *Server) Hello(w http.ResponseWriter, r *http.Request) {
	render(w, 200, "Hello!")
}

func (s *Server) Routes(w http.ResponseWriter, r *http.Request) {
	render(w, 200, s.sender.GetRoutes())
}

func (s *Server) Send(w http.ResponseWriter, r *http.Request) {
	var msg Message
	err := getInput(r.Body, &msg, newValidate())
	if err != nil {
		render(w, http.StatusBadRequest, formErrorMessage(err))
		return
	}

	s.in <- &smsender.Message{
		Recipient:  msg.Recipient,
		Body:       msg.Body,
		Originator: msg.Originator,
	}

	// TODO result
	render(w, 200, Result{msg})
}
<|endoftext|>


<commit_before>package compiler

import (
	"fmt"
	"io"
	"strconv"

	"github.com/PuerkitoBio/agora/bytecode"
)

var (
	maj, min      = bytecode.Version()
	disasmComment = fmt.Sprintf("// Generated from the disassembler, v%d.%d", maj, min)
)

// A Disasm translates a bytecode representation into assembly source code.
type Disasm struct {
	w   io.Writer
	err error
}

// ToAsm takes the in-memory bytecode File structure and translates it to
// assembly source code, writing the results to the provided writer. If an
// error is encountered, it is returned, otherwise it returns nil.
func (d *Disasm) ToAsm(f *bytecode.File, w io.Writer) error {
	d.w = w
	d.err = nil
	// 1- Write the standard comment
	d.write(disasmComment, true)
	// 2- Write every function
	for _, fn := range f.Fns {
		d.write("[f]", true)
		d.write(fn.Header.Name, true)
		d.write(fn.Header.StackSz, true)
		d.write(fn.Header.ExpArgs, true)
		d.write(fn.Header.ExpVars, true)
		d.write(fn.Header.LineStart, true)
		d.write(fn.Header.LineEnd, true)

		// 3- Write the function's K section
		d.write("[k]", true)
		for _, k := range fn.Ks {
			d.write(k.Type, false)
			d.write(k.Val, true)
		}
		// 4- Write the function's I section
		d.write("[i]", true)
		for _, i := range fn.Is {
			op, flg, ix := i.Opcode(), i.Flag(), i.Index()
			d.write(op.String(), false)
			d.write(" ", false)
			d.write(flg.String(), false)
			d.write(" ", false)
			d.write(ix, true)
		}
	}
	return d.err
}

// Uncompile reads the bytecode source data from the provided reader, and translates
// it to assembly source code written into the writer. If an error is encountered, it
// is returned, otherwise it returns nil.
func (d *Disasm) Uncompile(r io.Reader, w io.Writer) error {
	f, err := bytecode.NewDecoder(r).Decode()
	if err != nil {
		return err
	}
	return d.ToAsm(f, w)
}

func (d *Disasm) write(i interface{}, newLine bool) {
	if d.err != nil {
		return
	}
	switch v := i.(type) {
	case int64:
		d.write(strconv.FormatInt(v, 10), newLine)
	case uint64:
		d.write(strconv.FormatUint(v, 10), newLine)
	case float64:
		d.write(strconv.FormatFloat(v, 'f', -1, 64), newLine)
	case bytecode.KType:
		d.write(string(v), newLine)
	case string:
		_, d.err = io.WriteString(d.w, v)
		if newLine {
			d.write("\n", false)
		}
	default:
		panic(fmt.Sprintf("unexpected type to write: %T", i))
	}
}
<commit_msg>if func name is empty, save it to asm as <anon><commit_after>package compiler

import (
	"fmt"
	"io"
	"strconv"

	"github.com/PuerkitoBio/agora/bytecode"
)

var (
	maj, min      = bytecode.Version()
	disasmComment = fmt.Sprintf("// Generated from the disassembler, v%d.%d", maj, min)
)

// A Disasm translates a bytecode representation into assembly source code.
type Disasm struct {
	w   io.Writer
	err error
}

// ToAsm takes the in-memory bytecode File structure and translates it to
// assembly source code, writing the results to the provided writer. If an
// error is encountered, it is returned, otherwise it returns nil.
func (d *Disasm) ToAsm(f *bytecode.File, w io.Writer) error {
	d.w = w
	d.err = nil
	// 1- Write the standard comment
	d.write(disasmComment, true)
	// 2- Write every function
	for _, fn := range f.Fns {
		d.write("[f]", true)
		// If the func name is empty, set it to <anon>
		if fn.Header.Name == "" {
			d.write("<anon>", true)
		} else {
			d.write(fn.Header.Name, true)
		}
		d.write(fn.Header.StackSz, true)
		d.write(fn.Header.ExpArgs, true)
		d.write(fn.Header.ExpVars, true)
		d.write(fn.Header.LineStart, true)
		d.write(fn.Header.LineEnd, true)

		// 3- Write the function's K section
		d.write("[k]", true)
		for _, k := range fn.Ks {
			d.write(k.Type, false)
			d.write(k.Val, true)
		}
		// 4- Write the function's I section
		d.write("[i]", true)
		for _, i := range fn.Is {
			op, flg, ix := i.Opcode(), i.Flag(), i.Index()
			d.write(op.String(), false)
			d.write(" ", false)
			d.write(flg.String(), false)
			d.write(" ", false)
			d.write(ix, true)
		}
	}
	return d.err
}

// Uncompile reads the bytecode source data from the provided reader, and translates
// it to assembly source code written into the writer. If an error is encountered, it
// is returned, otherwise it returns nil.
func (d *Disasm) Uncompile(r io.Reader, w io.Writer) error {
	f, err := bytecode.NewDecoder(r).Decode()
	if err != nil {
		return err
	}
	return d.ToAsm(f, w)
}

func (d *Disasm) write(i interface{}, newLine bool) {
	if d.err != nil {
		return
	}
	switch v := i.(type) {
	case int64:
		d.write(strconv.FormatInt(v, 10), newLine)
	case uint64:
		d.write(strconv.FormatUint(v, 10), newLine)
	case float64:
		d.write(strconv.FormatFloat(v, 'f', -1, 64), newLine)
	case bytecode.KType:
		d.write(string(v), newLine)
	case string:
		_, d.err = io.WriteString(d.w, v)
		if newLine {
			d.write("\n", false)
		}
	default:
		panic(fmt.Sprintf("unexpected type to write: %T", i))
	}
}
<|endoftext|>


<commit_before>package matches

type Position struct {
	Y int `json:"y"`
	X int `json:"x"`
}

type ParticipantFrame struct {
	TotalGold           int      `json:"totalGold"`
	TeamScore           int      `json:"teamScore"`
	ParticipantID       int      `json:"participantId"`
	Level               int      `json:"level"`
	CurrentGold         int      `json:"currentGold"`
	MinionsKilled       int      `json:"minionsKilled"`
	DominionScore       int      `json:"dominionScore"`
	Position            Position `json:"position"`
	Xp                  int      `json:"xp"`
	JungleMinionsKilled int      `json:"jungleMinionsKilled"`
}

type Event struct {
	Timestamp               int      `json:"timestamp"`
	Type                    string   `json:"type"`
	CreatorID               int      `json:"creatorId,omitempty"`
	WardType                string   `json:"wardType,omitempty"`
	SkillSlot               int      `json:"skillSlot,omitempty"`
	LevelUpType             string   `json:"levelUpType,omitempty"`
	ParticipantID           int      `json:"participantId,omitempty"`
	ItemID                  int      `json:"itemId,omitempty"`
	KillerID                int      `json:"killerId,omitempty"`
	BuildingType            string   `json:"buildingType,omitempty"`
	TowerType               string   `json:"towerType,omitempty"`
	TeamID                  int      `json:"teamId,omitempty"`
	AssistingParticipantIds []int    `json:"assistingParticipantIds,omitempty"`
	Position                Position `json:"position,omitempty"`
	LaneType                string   `json:"laneType,omitempty"`
	VictimID                int      `json:"victimId,omitempty"`
}

type Frame struct {
	Timestamp         int                         `json:"timestamp"`
	ParticipantFrames map[string]ParticipantFrame `json:"participantFrames"`
	Events            []Event                     `json:"events"`
}

type Timeline struct {
	Frames        []Frame `json:"frames"`
	FrameInterval int     `json:"frameInterval"`
}

func (m Timeline) Filter(by ...string) {
	for i, frame := range m.Frames {
		events := filter(frame.Events, by)
		frame.Events = events
		m.Frames[i] = frame
	}
}
<commit_msg>Return frames on filter<commit_after>package matches

type Position struct {
	Y int `json:"y"`
	X int `json:"x"`
}

type ParticipantFrame struct {
	TotalGold           int      `json:"totalGold"`
	TeamScore           int      `json:"teamScore"`
	ParticipantID       int      `json:"participantId"`
	Level               int      `json:"level"`
	CurrentGold         int      `json:"currentGold"`
	MinionsKilled       int      `json:"minionsKilled"`
	DominionScore       int      `json:"dominionScore"`
	Position            Position `json:"position"`
	Xp                  int      `json:"xp"`
	JungleMinionsKilled int      `json:"jungleMinionsKilled"`
}

type Event struct {
	Timestamp               int      `json:"timestamp"`
	Type                    string   `json:"type"`
	CreatorID               int      `json:"creatorId,omitempty"`
	WardType                string   `json:"wardType,omitempty"`
	SkillSlot               int      `json:"skillSlot,omitempty"`
	LevelUpType             string   `json:"levelUpType,omitempty"`
	ParticipantID           int      `json:"participantId,omitempty"`
	ItemID                  int      `json:"itemId,omitempty"`
	KillerID                int      `json:"killerId,omitempty"`
	BuildingType            string   `json:"buildingType,omitempty"`
	TowerType               string   `json:"towerType,omitempty"`
	TeamID                  int      `json:"teamId,omitempty"`
	AssistingParticipantIds []int    `json:"assistingParticipantIds,omitempty"`
	Position                Position `json:"position,omitempty"`
	LaneType                string   `json:"laneType,omitempty"`
	VictimID                int      `json:"victimId,omitempty"`
}

type Frame struct {
	Timestamp         int                         `json:"timestamp"`
	ParticipantFrames map[string]ParticipantFrame `json:"participantFrames"`
	Events            []Event                     `json:"events"`
}

type Timeline struct {
	Frames        []Frame `json:"frames"`
	FrameInterval int     `json:"frameInterval"`
}

func (m Timeline) Filter(by ...string) (frames []Frame) {
	frames = make([]Frame, len(m.Frames))
	for i, frame := range m.Frames {
		events := filter(frame.Events, by)
		frames[i].Events = events
	}

	return frames
}
<|endoftext|>


<commit_before>package terraform

import (
	"strings"
	"testing"
)

func TestContextImport_basic(t *testing.T) {
	p := testProvider("aws")
	ctx := testContext2(t, &ContextOpts{
		Providers: map[string]ResourceProviderFactory{
			"aws": testProviderFuncFixed(p),
		},
	})

	p.ImportStateReturn = []*InstanceState{
		&InstanceState{
			ID:        "foo",
			Ephemeral: EphemeralState{Type: "aws_instance"},
		},
	}

	state, err := ctx.Import(&ImportOpts{
		Targets: []*ImportTarget{
			&ImportTarget{
				Addr: "aws_instance.foo",
				ID:   "bar",
			},
		},
	})
	if err != nil {
		t.Fatalf("err: %s", err)
	}

	actual := strings.TrimSpace(state.String())
	expected := strings.TrimSpace(testImportStr)
	if actual != expected {
		t.Fatalf("bad: \n%s", actual)
	}
}

func TestContextImport_missingType(t *testing.T) {
	p := testProvider("aws")
	ctx := testContext2(t, &ContextOpts{
		Providers: map[string]ResourceProviderFactory{
			"aws": testProviderFuncFixed(p),
		},
	})

	p.ImportStateReturn = []*InstanceState{
		&InstanceState{
			ID: "foo",
		},
	}

	_, err := ctx.Import(&ImportOpts{
		Targets: []*ImportTarget{
			&ImportTarget{
				Addr: "aws_instance.foo",
				ID:   "bar",
			},
		},
	})
	if err == nil {
		t.Fatal("should error")
	}
}

func TestContextImport_refresh(t *testing.T) {
	p := testProvider("aws")
	ctx := testContext2(t, &ContextOpts{
		Providers: map[string]ResourceProviderFactory{
			"aws": testProviderFuncFixed(p),
		},
	})

	p.ImportStateReturn = []*InstanceState{
		&InstanceState{
			ID:        "foo",
			Ephemeral: EphemeralState{Type: "aws_instance"},
		},
	}

	p.RefreshFn = func(info *InstanceInfo, s *InstanceState) (*InstanceState, error) {
		return &InstanceState{
			ID:         "foo",
			Attributes: map[string]string{"foo": "bar"},
		}, nil
	}

	state, err := ctx.Import(&ImportOpts{
		Targets: []*ImportTarget{
			&ImportTarget{
				Addr: "aws_instance.foo",
				ID:   "bar",
			},
		},
	})
	if err != nil {
		t.Fatalf("err: %s", err)
	}

	actual := strings.TrimSpace(state.String())
	expected := strings.TrimSpace(testImportRefreshStr)
	if actual != expected {
		t.Fatalf("bad: \n%s", actual)
	}
}

func TestContextImport_module(t *testing.T) {
	p := testProvider("aws")
	ctx := testContext2(t, &ContextOpts{
		Providers: map[string]ResourceProviderFactory{
			"aws": testProviderFuncFixed(p),
		},
	})

	p.ImportStateReturn = []*InstanceState{
		&InstanceState{
			ID:        "foo",
			Ephemeral: EphemeralState{Type: "aws_instance"},
		},
	}

	state, err := ctx.Import(&ImportOpts{
		Targets: []*ImportTarget{
			&ImportTarget{
				Addr: "module.foo.aws_instance.foo",
				ID:   "bar",
			},
		},
	})
	if err != nil {
		t.Fatalf("err: %s", err)
	}

	actual := strings.TrimSpace(state.String())
	expected := strings.TrimSpace(testImportModuleStr)
	if actual != expected {
		t.Fatalf("bad: \n%s", actual)
	}
}

func TestContextImport_moduleDepth2(t *testing.T) {
	p := testProvider("aws")
	ctx := testContext2(t, &ContextOpts{
		Providers: map[string]ResourceProviderFactory{
			"aws": testProviderFuncFixed(p),
		},
	})

	p.ImportStateReturn = []*InstanceState{
		&InstanceState{
			ID:        "foo",
			Ephemeral: EphemeralState{Type: "aws_instance"},
		},
	}

	state, err := ctx.Import(&ImportOpts{
		Targets: []*ImportTarget{
			&ImportTarget{
				Addr: "module.a.module.b.aws_instance.foo",
				ID:   "bar",
			},
		},
	})
	if err != nil {
		t.Fatalf("err: %s", err)
	}

	actual := strings.TrimSpace(state.String())
	expected := strings.TrimSpace(testImportModuleDepth2Str)
	if actual != expected {
		t.Fatalf("bad: \n%s", actual)
	}
}

const testImportStr = `
aws_instance.foo:
  ID = foo
  provider = aws
`

const testImportModuleStr = `
<no state>
module.foo:
  aws_instance.foo:
    ID = foo
    provider = aws
`

const testImportModuleDepth2Str = `
<no state>
module.a.b:
  aws_instance.foo:
    ID = foo
    provider = aws
`

const testImportRefreshStr = `
aws_instance.foo:
  ID = foo
  provider = aws
  foo = bar
`
<commit_msg>terraform: verify import with missing type doesn't add to state<commit_after>package terraform

import (
	"strings"
	"testing"
)

func TestContextImport_basic(t *testing.T) {
	p := testProvider("aws")
	ctx := testContext2(t, &ContextOpts{
		Providers: map[string]ResourceProviderFactory{
			"aws": testProviderFuncFixed(p),
		},
	})

	p.ImportStateReturn = []*InstanceState{
		&InstanceState{
			ID:        "foo",
			Ephemeral: EphemeralState{Type: "aws_instance"},
		},
	}

	state, err := ctx.Import(&ImportOpts{
		Targets: []*ImportTarget{
			&ImportTarget{
				Addr: "aws_instance.foo",
				ID:   "bar",
			},
		},
	})
	if err != nil {
		t.Fatalf("err: %s", err)
	}

	actual := strings.TrimSpace(state.String())
	expected := strings.TrimSpace(testImportStr)
	if actual != expected {
		t.Fatalf("bad: \n%s", actual)
	}
}

func TestContextImport_missingType(t *testing.T) {
	p := testProvider("aws")
	ctx := testContext2(t, &ContextOpts{
		Providers: map[string]ResourceProviderFactory{
			"aws": testProviderFuncFixed(p),
		},
	})

	p.ImportStateReturn = []*InstanceState{
		&InstanceState{
			ID: "foo",
		},
	}

	state, err := ctx.Import(&ImportOpts{
		Targets: []*ImportTarget{
			&ImportTarget{
				Addr: "aws_instance.foo",
				ID:   "bar",
			},
		},
	})
	if err == nil {
		t.Fatal("should error")
	}

	actual := strings.TrimSpace(state.String())
	expected := "<nil>"
	if actual != expected {
		t.Fatalf("bad: \n%s", actual)
	}
}

func TestContextImport_refresh(t *testing.T) {
	p := testProvider("aws")
	ctx := testContext2(t, &ContextOpts{
		Providers: map[string]ResourceProviderFactory{
			"aws": testProviderFuncFixed(p),
		},
	})

	p.ImportStateReturn = []*InstanceState{
		&InstanceState{
			ID:        "foo",
			Ephemeral: EphemeralState{Type: "aws_instance"},
		},
	}

	p.RefreshFn = func(info *InstanceInfo, s *InstanceState) (*InstanceState, error) {
		return &InstanceState{
			ID:         "foo",
			Attributes: map[string]string{"foo": "bar"},
		}, nil
	}

	state, err := ctx.Import(&ImportOpts{
		Targets: []*ImportTarget{
			&ImportTarget{
				Addr: "aws_instance.foo",
				ID:   "bar",
			},
		},
	})
	if err != nil {
		t.Fatalf("err: %s", err)
	}

	actual := strings.TrimSpace(state.String())
	expected := strings.TrimSpace(testImportRefreshStr)
	if actual != expected {
		t.Fatalf("bad: \n%s", actual)
	}
}

func TestContextImport_module(t *testing.T) {
	p := testProvider("aws")
	ctx := testContext2(t, &ContextOpts{
		Providers: map[string]ResourceProviderFactory{
			"aws": testProviderFuncFixed(p),
		},
	})

	p.ImportStateReturn = []*InstanceState{
		&InstanceState{
			ID:        "foo",
			Ephemeral: EphemeralState{Type: "aws_instance"},
		},
	}

	state, err := ctx.Import(&ImportOpts{
		Targets: []*ImportTarget{
			&ImportTarget{
				Addr: "module.foo.aws_instance.foo",
				ID:   "bar",
			},
		},
	})
	if err != nil {
		t.Fatalf("err: %s", err)
	}

	actual := strings.TrimSpace(state.String())
	expected := strings.TrimSpace(testImportModuleStr)
	if actual != expected {
		t.Fatalf("bad: \n%s", actual)
	}
}

func TestContextImport_moduleDepth2(t *testing.T) {
	p := testProvider("aws")
	ctx := testContext2(t, &ContextOpts{
		Providers: map[string]ResourceProviderFactory{
			"aws": testProviderFuncFixed(p),
		},
	})

	p.ImportStateReturn = []*InstanceState{
		&InstanceState{
			ID:        "foo",
			Ephemeral: EphemeralState{Type: "aws_instance"},
		},
	}

	state, err := ctx.Import(&ImportOpts{
		Targets: []*ImportTarget{
			&ImportTarget{
				Addr: "module.a.module.b.aws_instance.foo",
				ID:   "bar",
			},
		},
	})
	if err != nil {
		t.Fatalf("err: %s", err)
	}

	actual := strings.TrimSpace(state.String())
	expected := strings.TrimSpace(testImportModuleDepth2Str)
	if actual != expected {
		t.Fatalf("bad: \n%s", actual)
	}
}

const testImportStr = `
aws_instance.foo:
  ID = foo
  provider = aws
`

const testImportModuleStr = `
<no state>
module.foo:
  aws_instance.foo:
    ID = foo
    provider = aws
`

const testImportModuleDepth2Str = `
<no state>
module.a.b:
  aws_instance.foo:
    ID = foo
    provider = aws
`

const testImportRefreshStr = `
aws_instance.foo:
  ID = foo
  provider = aws
  foo = bar
`
<|endoftext|>


<commit_before>//go:build windows
// +build windows

package windows

import (
	"strings"
	"unsafe"

	"github.com/mackerelio/golib/logging"
	"github.com/mackerelio/mackerel-client-go"

	"github.com/mackerelio/mackerel-agent/util/windows"
)

const registryKey = `Software\Microsoft\Windows NT\CurrentVersion`

// KernelGenerator XXX
type KernelGenerator struct {
}

var kernelLogger = logging.GetLogger("spec.kernel")

// Generate XXX
func (g *KernelGenerator) Generate() (interface{}, error) {
	results := make(mackerel.Kernel)

	osname, _, err := windows.RegGetString(
		windows.HKEY_LOCAL_MACHINE, registryKey, `ProductName`)
	if err != nil {
		return nil, err
	}
	edition, _, err := windows.RegGetString(
		windows.HKEY_LOCAL_MACHINE, registryKey, `EditionID`)
	if err != nil {
		return nil, err
	}
	version, _, err := windows.RegGetString(
		windows.HKEY_LOCAL_MACHINE, registryKey, `CurrentVersion`)
	if err != nil {
		return nil, err
	}
	release, errno, err := windows.RegGetString(
		windows.HKEY_LOCAL_MACHINE, registryKey, `CSDVersion`)
	if err != nil && errno != windows.ERROR_FILE_NOT_FOUND { // CSDVersion is nullable
		return nil, err
	}

	if edition != "" && strings.Index(osname, edition) == -1 {
		osname += " (" + edition + ")"
	}

	results["name"] = "Microsoft Windows"
	results["os"] = osname
	results["version"] = version
	results["release"] = release

	var systemInfo windows.SYSTEM_INFO
	windows.GetSystemInfo.Call(uintptr(unsafe.Pointer(&systemInfo)))
	switch systemInfo.ProcessorArchitecture {
	case 0:
		results["machine"] = "x86"
	case 1:
		results["machine"] = "mips"
	case 2:
		results["machine"] = "alpha"
	case 3:
		results["machine"] = "ppc"
	case 4:
		results["machine"] = "shx"
	case 5:
		results["machine"] = "arm"
	case 6:
		results["machine"] = "ia64"
	case 7:
		results["machine"] = "alpha64"
	case 8:
		results["machine"] = "msil"
	case 9:
		results["machine"] = "amd64"
	case 10:
		results["machine"] = "ia32_on_win64"
	}

	return results, nil
}
<commit_msg>fix edition name.<commit_after>//go:build windows
// +build windows

package windows

import (
	"strings"
	"unsafe"

	"github.com/mackerelio/golib/logging"
	"github.com/mackerelio/mackerel-client-go"

	"github.com/mackerelio/mackerel-agent/util/windows"
)

const registryKey = `Software\Microsoft\Windows NT\CurrentVersion`

// KernelGenerator XXX
type KernelGenerator struct {
}

var kernelLogger = logging.GetLogger("spec.kernel")

// Generate XXX
func (g *KernelGenerator) Generate() (interface{}, error) {
	results := make(mackerel.Kernel)

	osname, _, err := windows.RegGetString(
		windows.HKEY_LOCAL_MACHINE, registryKey, `ProductName`)
	if err != nil {
		return nil, err
	}
	edition, _, err := windows.RegGetString(
		windows.HKEY_LOCAL_MACHINE, registryKey, `EditionID`)
	if err != nil {
		return nil, err
	}
	version, _, err := windows.RegGetString(
		windows.HKEY_LOCAL_MACHINE, registryKey, `CurrentVersion`)
	if err != nil {
		return nil, err
	}
	release, errno, err := windows.RegGetString(
		windows.HKEY_LOCAL_MACHINE, registryKey, `CSDVersion`)
	if err != nil && errno != windows.ERROR_FILE_NOT_FOUND { // CSDVersion is nullable
		return nil, err
	}

	if edition != "" && !strings.Contains(osname, edition) {
		osname += " (" + edition + ")"
	}

	results["name"] = "Microsoft Windows"
	results["os"] = osname
	results["version"] = version
	results["release"] = release

	var systemInfo windows.SYSTEM_INFO
	windows.GetSystemInfo.Call(uintptr(unsafe.Pointer(&systemInfo)))
	switch systemInfo.ProcessorArchitecture {
	case 0:
		results["machine"] = "x86"
	case 1:
		results["machine"] = "mips"
	case 2:
		results["machine"] = "alpha"
	case 3:
		results["machine"] = "ppc"
	case 4:
		results["machine"] = "shx"
	case 5:
		results["machine"] = "arm"
	case 6:
		results["machine"] = "ia64"
	case 7:
		results["machine"] = "alpha64"
	case 8:
		results["machine"] = "msil"
	case 9:
		results["machine"] = "amd64"
	case 10:
		results["machine"] = "ia32_on_win64"
	}

	return results, nil
}
<|endoftext|>


<commit_before>// Copyright 2016 CoreOS, Inc.
//
// 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 cmd

import (
	"fmt"
	"sync"
	"time"

	v3 "github.com/coreos/etcd/clientv3"

	"github.com/spf13/cobra"
	"golang.org/x/net/context"
	"gopkg.in/cheggaaa/pb.v1"
)

// watchGetCmd represents the watch command
var watchGetCmd = &cobra.Command{
	Use:   "watch-get",
	Short: "Benchmark watch with get",
	Long:  `Benchmark for serialized key gets with many unsynced watchers`,
	Run:   watchGetFunc,
}

var (
	watchGetTotalStreams int
	watchEvents          int
	firstWatch           sync.Once
)

func init() {
	RootCmd.AddCommand(watchGetCmd)
	watchGetCmd.Flags().IntVar(&watchGetTotalStreams, "watchers", 10000, "Total number of watchers")
	watchGetCmd.Flags().IntVar(&watchEvents, "events", 8, "Number of events per watcher")
}

func watchGetFunc(cmd *cobra.Command, args []string) {
	clients := mustCreateClients(totalClients, totalConns)

	// setup keys for watchers
	watchRev := int64(0)
	for i := 0; i < watchEvents; i++ {
		v := fmt.Sprintf("%d", i)
		resp, err := clients[0].Put(context.TODO(), "watchkey", v)
		if err != nil {
			panic(err)
		}
		if i == 0 {
			watchRev = resp.Header.Revision
		}
	}

	streams := make([]v3.Watcher, watchGetTotalStreams)
	for i := range streams {
		streams[i] = v3.NewWatcher(clients[i%len(clients)])
	}

	// results from trying to do serialized gets with concurrent watchers
	results = make(chan result)

	bar = pb.New(watchGetTotalStreams * watchEvents)
	bar.Format("Bom !")
	bar.Start()

	pdoneC := printReport(results)
	wg.Add(len(streams))
	ctx, cancel := context.WithCancel(context.TODO())
	f := func() {
		doSerializedGet(ctx, clients[0], results)
	}
	for i := range streams {
		go doUnsyncWatch(streams[i], watchRev, f)
	}
	wg.Wait()
	cancel()
	bar.Finish()
	fmt.Printf("Get during watch summary:\n")
	<-pdoneC
}

func doSerializedGet(ctx context.Context, client *v3.Client, results chan result) {
	for {
		st := time.Now()
		_, err := client.Get(ctx, "abc", v3.WithSerializable())
		if ctx.Err() != nil {
			break
		}
		var errStr string
		if err != nil {
			errStr = err.Error()
		}
		res := result{errStr: errStr, duration: time.Since(st), happened: time.Now()}
		results <- res
	}
	close(results)
}

func doUnsyncWatch(stream v3.Watcher, rev int64, f func()) {
	wch := stream.Watch(context.TODO(), "watchkey", v3.WithRev(rev))
	if wch == nil {
		panic("could not open watch channel")
	}
	firstWatch.Do(func() { go f() })
	i := 0
	for i < watchEvents {
		wev := <-wch
		i += len(wev.Events)
		bar.Add(len(wev.Events))
	}
	wg.Done()
}
<commit_msg>benchmark: use separate connection for get in watch-get<commit_after>// Copyright 2016 CoreOS, Inc.
//
// 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 cmd

import (
	"fmt"
	"sync"
	"time"

	v3 "github.com/coreos/etcd/clientv3"

	"github.com/spf13/cobra"
	"golang.org/x/net/context"
	"gopkg.in/cheggaaa/pb.v1"
)

// watchGetCmd represents the watch command
var watchGetCmd = &cobra.Command{
	Use:   "watch-get",
	Short: "Benchmark watch with get",
	Long:  `Benchmark for serialized key gets with many unsynced watchers`,
	Run:   watchGetFunc,
}

var (
	watchGetTotalStreams int
	watchEvents          int
	firstWatch           sync.Once
)

func init() {
	RootCmd.AddCommand(watchGetCmd)
	watchGetCmd.Flags().IntVar(&watchGetTotalStreams, "watchers", 10000, "Total number of watchers")
	watchGetCmd.Flags().IntVar(&watchEvents, "events", 8, "Number of events per watcher")
}

func watchGetFunc(cmd *cobra.Command, args []string) {
	clients := mustCreateClients(totalClients, totalConns)
	getClient := mustCreateClients(1, 1)

	// setup keys for watchers
	watchRev := int64(0)
	for i := 0; i < watchEvents; i++ {
		v := fmt.Sprintf("%d", i)
		resp, err := clients[0].Put(context.TODO(), "watchkey", v)
		if err != nil {
			panic(err)
		}
		if i == 0 {
			watchRev = resp.Header.Revision
		}
	}

	streams := make([]v3.Watcher, watchGetTotalStreams)
	for i := range streams {
		streams[i] = v3.NewWatcher(clients[i%len(clients)])
	}

	// results from trying to do serialized gets with concurrent watchers
	results = make(chan result)

	bar = pb.New(watchGetTotalStreams * watchEvents)
	bar.Format("Bom !")
	bar.Start()

	pdoneC := printReport(results)
	wg.Add(len(streams))
	ctx, cancel := context.WithCancel(context.TODO())
	f := func() {
		doSerializedGet(ctx, getClient[0], results)
	}
	for i := range streams {
		go doUnsyncWatch(streams[i], watchRev, f)
	}
	wg.Wait()
	cancel()
	bar.Finish()
	fmt.Printf("Get during watch summary:\n")
	<-pdoneC
}

func doSerializedGet(ctx context.Context, client *v3.Client, results chan result) {
	for {
		st := time.Now()
		_, err := client.Get(ctx, "abc", v3.WithSerializable())
		if ctx.Err() != nil {
			break
		}
		var errStr string
		if err != nil {
			errStr = err.Error()
		}
		res := result{errStr: errStr, duration: time.Since(st), happened: time.Now()}
		results <- res
	}
	close(results)
}

func doUnsyncWatch(stream v3.Watcher, rev int64, f func()) {
	wch := stream.Watch(context.TODO(), "watchkey", v3.WithRev(rev))
	if wch == nil {
		panic("could not open watch channel")
	}
	firstWatch.Do(func() { go f() })
	i := 0
	for i < watchEvents {
		wev := <-wch
		i += len(wev.Events)
		bar.Add(len(wev.Events))
	}
	wg.Done()
}
<|endoftext|>


<commit_before>package kcp

import (
	"errors"
	"math/rand"
	"net"

	"github.com/v2ray/v2ray-core/common/log"
	v2net "github.com/v2ray/v2ray-core/common/net"
	"github.com/v2ray/v2ray-core/transport/internet"
)

var (
	ErrUnknownDestination = errors.New("Destination IP can't be resolved.")
)

func DialKCP(src v2net.Address, dest v2net.Destination) (internet.Connection, error) {
	udpDest := v2net.UDPDestination(dest.Address(), dest.Port())
	log.Info("Dialling KCP to ", udpDest)
	conn, err := internet.DialToDest(src, udpDest)
	if err != nil {
		return nil, err
	}

	cpip := NewSimpleAuthenticator()
	session := NewConnection(rand.Uint32(), conn, conn.LocalAddr().(*net.UDPAddr), conn.RemoteAddr().(*net.UDPAddr), cpip)
	session.FetchInputFrom(conn)

	return session, nil
}

func init() {
	internet.KCPDialer = DialKCP
}
<commit_msg>remove unused variable<commit_after>package kcp

import (
	"math/rand"
	"net"

	"github.com/v2ray/v2ray-core/common/log"
	v2net "github.com/v2ray/v2ray-core/common/net"
	"github.com/v2ray/v2ray-core/transport/internet"
)

func DialKCP(src v2net.Address, dest v2net.Destination) (internet.Connection, error) {
	udpDest := v2net.UDPDestination(dest.Address(), dest.Port())
	log.Info("Dialling KCP to ", udpDest)
	conn, err := internet.DialToDest(src, udpDest)
	if err != nil {
		return nil, err
	}

	cpip := NewSimpleAuthenticator()
	session := NewConnection(rand.Uint32(), conn, conn.LocalAddr().(*net.UDPAddr), conn.RemoteAddr().(*net.UDPAddr), cpip)
	session.FetchInputFrom(conn)

	return session, nil
}

func init() {
	internet.KCPDialer = DialKCP
}
<|endoftext|>


<commit_before>package cache

import (
	"crypto/sha512"
	"fmt"
	"os"
	"path/filepath"
	"sync"
	"time"

	"github.com/juju/errgo"
	homedir "github.com/mitchellh/go-homedir"
)

const (
	cacheDir = "~/pulcy-cache"
)

var (
	maskAny    = errgo.MaskFunc(errgo.Any)
	cacheMutex sync.Mutex
)

func Clear(key string) error {
	cacheMutex.Lock()
	defer cacheMutex.Unlock()

	dir, err := dir(key)
	if err != nil {
		return maskAny(err)
	}
	if err := os.RemoveAll(dir); err != nil {
		return maskAny(err)
	}
	return nil
}

func ClearAll() error {
	cacheMutex.Lock()
	defer cacheMutex.Unlock()

	dir, err := rootDir()
	if err != nil {
		return maskAny(err)
	}
	if err := os.RemoveAll(dir); err != nil {
		return maskAny(err)
	}
	return nil
}

// Dir returns the cache directory for a given key.
// Returns: path, isValid, error
func Dir(key string, cacheValidHours int) (string, bool, error) {
	cachedir, err := dir(key)
	if err != nil {
		return "", false, maskAny(err)
	}

	// Lock
	cacheMutex.Lock()
	defer cacheMutex.Unlock()

	// Check if cache directory exists
	s, err := os.Stat(cachedir)
	isValid := false
	if err == nil {
		// Package cache directory exists, check age.
		if cacheValidHours > 0 && s.ModTime().Add(time.Hour*time.Duration(cacheValidHours)).Before(time.Now()) {
			// Cache has become invalid
			if err := os.RemoveAll(cachedir); err != nil {
				return "", false, maskAny(err)
			}
		} else {
			// Cache is still valid
			isValid = true
		}
	} else {
		// cache directory not found, create needed
		isValid = false
	}

	// Ensure cache directory exists
	if err := os.MkdirAll(cachedir, 0777); err != nil {
		return "", false, maskAny(err)
	}

	return cachedir, isValid, nil
}

// dir returns the cache directory for a given key.
// Returns: path, error
func dir(key string) (string, error) {
	cachedirRoot, err := rootDir()
	if err != nil {
		return "", maskAny(err)
	}

	// Create hash of key
	hashBytes := sha512.Sum512([]byte(key))
	hash := fmt.Sprintf("%x", hashBytes)
	cachedir := filepath.Join(cachedirRoot, hash)

	return cachedir, nil
}

func rootDir() (string, error) {
	cachedirRoot, err := homedir.Expand(cacheDir)
	if err != nil {
		return "", maskAny(err)
	}

	return cachedirRoot, nil
}
<commit_msg>Moved cachedir<commit_after>package cache

import (
	"crypto/sha512"
	"fmt"
	"os"
	"path/filepath"
	"sync"
	"time"

	"github.com/juju/errgo"
	homedir "github.com/mitchellh/go-homedir"
)

const (
	cacheDir = "~/cache/pulcy"
)

var (
	maskAny    = errgo.MaskFunc(errgo.Any)
	cacheMutex sync.Mutex
)

func Clear(key string) error {
	cacheMutex.Lock()
	defer cacheMutex.Unlock()

	dir, err := dir(key)
	if err != nil {
		return maskAny(err)
	}
	if err := os.RemoveAll(dir); err != nil {
		return maskAny(err)
	}
	return nil
}

func ClearAll() error {
	cacheMutex.Lock()
	defer cacheMutex.Unlock()

	dir, err := rootDir()
	if err != nil {
		return maskAny(err)
	}
	if err := os.RemoveAll(dir); err != nil {
		return maskAny(err)
	}
	return nil
}

// Dir returns the cache directory for a given key.
// Returns: path, isValid, error
func Dir(key string, cacheValidHours int) (string, bool, error) {
	cachedir, err := dir(key)
	if err != nil {
		return "", false, maskAny(err)
	}

	// Lock
	cacheMutex.Lock()
	defer cacheMutex.Unlock()

	// Check if cache directory exists
	s, err := os.Stat(cachedir)
	isValid := false
	if err == nil {
		// Package cache directory exists, check age.
		if cacheValidHours > 0 && s.ModTime().Add(time.Hour*time.Duration(cacheValidHours)).Before(time.Now()) {
			// Cache has become invalid
			if err := os.RemoveAll(cachedir); err != nil {
				return "", false, maskAny(err)
			}
		} else {
			// Cache is still valid
			isValid = true
		}
	} else {
		// cache directory not found, create needed
		isValid = false
	}

	// Ensure cache directory exists
	if err := os.MkdirAll(cachedir, 0777); err != nil {
		return "", false, maskAny(err)
	}

	return cachedir, isValid, nil
}

// dir returns the cache directory for a given key.
// Returns: path, error
func dir(key string) (string, error) {
	cachedirRoot, err := rootDir()
	if err != nil {
		return "", maskAny(err)
	}

	// Create hash of key
	hashBytes := sha512.Sum512([]byte(key))
	hash := fmt.Sprintf("%x", hashBytes)
	cachedir := filepath.Join(cachedirRoot, hash)

	return cachedir, nil
}

func rootDir() (string, error) {
	cachedirRoot, err := homedir.Expand(cacheDir)
	if err != nil {
		return "", maskAny(err)
	}

	return cachedirRoot, nil
}
<|endoftext|>


<commit_before>package main

import (
	"erpel"
	"fmt"
	"os"

	"github.com/jessevdk/go-flags"
)

var opts = &struct {
	Verbose bool   `short:"v" long:"verbose" description:"be verbose"`
	Config  string `short:"c" long:"config" env:"ERPEL_CONFIG" default:"/etc/erpel/erpel.conf" description:"configuration file"`
}{}

// V prints the message when verbose is active.
func V(format string, args ...interface{}) {
	if !opts.Verbose {
		return
	}

	fmt.Printf(format, args...)
}

// E prints an error to stderr.
func E(format string, args ...interface{}) {
	fmt.Fprintf(os.Stderr, format, args...)
}

// Er prints the error err if it is set.
func Er(err error) {
	if err == nil {
		return
	}

	E("error: %v\n", err)
}

// Erx prints the error and exits with the given code, but only if the error is non-nil.
func Erx(err error, exitcode int) {
	if err == nil {
		return
	}

	Er(err)
	os.Exit(exitcode)
}

func main() {
	var parser = flags.NewParser(opts, flags.Default)

	_, err := parser.Parse()
	if e, ok := err.(*flags.Error); ok && e.Type == flags.ErrHelp {
		os.Exit(0)
	}
	Erx(err, 1)

	cfg, err := erpel.LoadConfig(opts.Config)
	if err != nil {
		Erx(err, 2)
	}

	fmt.Printf("cfg: %v\n", cfg)

	rules, err := erpel.LoadAllRules(cfg.RulesDir)
	if err != nil {
		Erx(err, 3)
	}

	fmt.Printf("loaded %v rules\n", len(rules))
}
<commit_msg>Add basic functionality<commit_after>package main

import (
	"bufio"
	"erpel"
	"fmt"
	"os"
	"regexp"
	"strings"

	"github.com/jessevdk/go-flags"
)

var opts = &struct {
	Verbose  bool     `short:"v" long:"verbose" description:"be verbose"`
	Config   string   `short:"c" long:"config" env:"ERPEL_CONFIG" default:"/etc/erpel/erpel.conf" description:"configuration file"`
	Logfiles []string `short:"l" long:"logfile" description:"logfile to process"`
}{}

// V prints the message when verbose is active.
func V(format string, args ...interface{}) {
	if !opts.Verbose {
		return
	}

	fmt.Printf(format, args...)
}

// E prints an error to stderr.
func E(format string, args ...interface{}) {
	fmt.Fprintf(os.Stderr, format, args...)
}

// Er prints the error err if it is set.
func Er(err error) {
	if err == nil {
		return
	}

	E("error: %v\n", err)
}

// Erx prints the error and exits with the given code, but only if the error is non-nil.
func Erx(err error, exitcode int) {
	if err == nil {
		return
	}

	Er(err)
	os.Exit(exitcode)
}

func main() {
	var parser = flags.NewParser(opts, flags.Default)

	_, err := parser.Parse()
	if e, ok := err.(*flags.Error); ok && e.Type == flags.ErrHelp {
		os.Exit(0)
	}
	Erx(err, 1)

	if len(opts.Logfiles) == 0 {
		E("no logfile specified, use --logfile\n")
		os.Exit(1)
	}

	cfg, err := erpel.LoadConfig(opts.Config)
	if err != nil {
		Erx(err, 2)
	}

	V("config loaded from %v\n", opts.Config)

	rules, err := erpel.LoadAllRules(cfg.RulesDir)
	if err != nil {
		Erx(err, 3)
	}

	V("loaded %v rules from %v\n", len(rules), cfg.RulesDir)

	filter := erpel.Filter{
		Rules: rules,
	}

	if cfg.Prefix != "" {
		r, err := regexp.Compile(cfg.Prefix)
		if err != nil {
			Erx(err, 4)
		}

		filter.Prefix = r
	}

	for _, logfile := range opts.Logfiles {
		V("processing %v\n", logfile)

		f, err := os.Open(logfile)
		if err != nil {
			E("error opening logfile %v: %v\n", logfile, err)
			continue
		}

		sc := bufio.NewScanner(f)
		for sc.Scan() {
			line := strings.TrimSpace(sc.Text())

			result := filter.Process([]string{line})

			for _, line := range result {
				fmt.Println(line)
			}
		}

		err = f.Close()
		if err != nil {
			E("error closing logfile %v: %v\n", logfile, err)
			continue
		}
	}
}
<|endoftext|>


<commit_before>package compiler

import (
	"fmt"

	"github.com/nitrogen-lang/nitrogen/src/ast"
	"github.com/nitrogen-lang/nitrogen/src/object"
	"github.com/nitrogen-lang/nitrogen/src/token"
	"github.com/nitrogen-lang/nitrogen/src/vm/opcode"
)

func compileClassLiteral(ccb *codeBlockCompiler, class *ast.ClassLiteral) {
	for _, f := range class.Methods {
		f.FQName = fmt.Sprintf("%s.%s", class.Name, f.Name)
		compileFunction(ccb, f, true, class.Parent != "")
	}

	ccb2 := &codeBlockCompiler{
		constants: newConstantTable(),
		locals:    newStringTable(),
		names:     newStringTable(),
		code:      NewInstSet(),
		filename:  ccb.filename,
	}

	for _, f := range class.Fields {
		compile(ccb2, f)
	}
	compileLoadNull(ccb2)
	ccb2.code.addInst(opcode.Return)

	code := ccb2.code
	props := &CodeBlock{
		Name:         fmt.Sprintf("%s.__init", class.Name),
		Filename:     ccb.filename,
		LocalCount:   len(ccb2.locals.table),
		Code:         code.Assemble(),
		Constants:    ccb2.constants.table,
		Names:        ccb2.names.table,
		Locals:       ccb2.locals.table,
		MaxStackSize: calculateStackSize(code),
		MaxBlockSize: calculateBlockSize(code),
	}

	ccb.code.addInst(opcode.LoadConst, ccb.constants.indexOf(props))

	if class.Parent == "" {
		compileLoadNull(ccb)
	} else {
		compile(ccb, &ast.Identifier{Value: class.Parent})
	}

	ccb.code.addInst(opcode.LoadConst, ccb.constants.indexOf(object.MakeStringObj(class.Name)))
	ccb.code.addInst(opcode.BuildClass, uint16(len(class.Methods)))
}

func compileTryCatch(ccb *codeBlockCompiler, try *ast.TryCatchExpression) {
	_, tryNoNil := try.Try.Statements[len(try.Try.Statements)-1].(*ast.ExpressionStatement)
	_, catchNoNil := try.Catch.Statements[len(try.Catch.Statements)-1].(*ast.ExpressionStatement)

	catchBlkLbl := randomLabel("catch_")
	endTryLbl := randomLabel("endTry_")

	ccb.code.addLabeledArgs(opcode.StartTry, catchBlkLbl)
	compile(ccb, try.Try)
	ccb.code.addLabeledArgs(opcode.JumpAbsolute, endTryLbl)

	ccb.code.addLabel(catchBlkLbl)
	if try.Symbol == nil {
		ccb.code.addInst(opcode.Pop)
	} else {
		ccb.code.addInst(opcode.Define, ccb.locals.indexOf(try.Symbol.Value))
	}

	compile(ccb, try.Catch)
	if try.Symbol != nil {
		ccb.code.addInst(opcode.DeleteFast, ccb.locals.indexOf(try.Symbol.Value))
	}

	if catchNoNil && !tryNoNil {
		ccb.code.addInst(opcode.JumpForward, 3)
	}

	if !tryNoNil || !catchNoNil {
		compileLoadNull(ccb)
	}

	ccb.code.addLabel(endTryLbl)
	ccb.code.addInst(opcode.EndBlock)
}

func compileBlock(ccb *codeBlockCompiler, block *ast.BlockStatement) {
	l := len(block.Statements) - 1
	for i, s := range block.Statements {
		compile(ccb, s)
		if i < l {
			switch s.(type) {
			case *ast.ExpressionStatement:
				ccb.code.addInst(opcode.Pop)
			}
		}
	}
}

func compileFunction(ccb *codeBlockCompiler, fn *ast.FunctionLiteral, inClass, hasParent bool) {
	ccb2 := &codeBlockCompiler{
		constants: newConstantTable(),
		locals:    newStringTable(),
		names:     newStringTable(),
		code:      NewInstSet(),
		filename:  ccb.filename,
	}

	for _, p := range fn.Parameters {
		ccb2.locals.indexOf(p.Value)
	}
	ccb2.locals.indexOf("arguments") // `arguments` holds any remaining arguments from a function call
	if inClass {
		ccb2.locals.indexOf("this")
		if hasParent {
			ccb2.locals.indexOf("parent")
		}
	}

	compile(ccb2, fn.Body)

	if len(fn.Body.Statements) > 0 {
		switch fn.Body.Statements[len(fn.Body.Statements)-1].(type) {
		case *ast.ExpressionStatement:
			break
		case *ast.ReturnStatement:
			break
		default:
			compileLoadNull(ccb2)
		}

		if !ccb2.code.last().Is(opcode.Return) {
			ccb2.code.addInst(opcode.Return)
		}
	} else {
		compileLoadNull(ccb2)
		ccb2.code.addInst(opcode.Return)
	}

	code := ccb2.code
	body := &CodeBlock{
		Name:         fn.FQName,
		Filename:     ccb.filename,
		LocalCount:   len(ccb2.locals.table),
		Code:         code.Assemble(),
		Constants:    ccb2.constants.table,
		Names:        ccb2.names.table,
		Locals:       ccb2.locals.table,
		MaxStackSize: calculateStackSize(code),
		MaxBlockSize: calculateBlockSize(code),
	}

	ccb.code.addInst(opcode.LoadConst, ccb.constants.indexOf(body))

	for _, p := range fn.Parameters {
		ccb.code.addInst(opcode.LoadConst, ccb.constants.indexOf(object.MakeStringObj(p.Value)))
	}
	ccb.code.addInst(opcode.MakeArray, uint16(len(fn.Parameters)))

	ccb.code.addInst(opcode.LoadConst, ccb.constants.indexOf(object.MakeStringObj(fn.Name)))

	ccb.code.addInst(opcode.MakeFunction)
}

func compileIfStatement(ccb *codeBlockCompiler, ifs *ast.IfExpression) {
	if ifs.Alternative == nil {
		compileIfStatementNoElse(ccb, ifs)
		return
	}

	compile(ccb, ifs.Condition)

	_, trueNoNil := ifs.Consequence.Statements[len(ifs.Consequence.Statements)-1].(*ast.ExpressionStatement)
	falseBrnLbl := randomLabel("false_")
	ccb.code.addLabeledArgs(opcode.PopJumpIfFalse, falseBrnLbl)
	compile(ccb, ifs.Consequence)
	if !trueNoNil {
		compileLoadNull(ccb)
	}

	_, falseNoNil := ifs.Alternative.Statements[len(ifs.Alternative.Statements)-1].(*ast.ExpressionStatement)
	afterIfStmt := randomLabel("afterIf_")
	ccb.code.addLabeledArgs(opcode.JumpAbsolute, afterIfStmt)
	ccb.code.addLabel(falseBrnLbl)
	compile(ccb, ifs.Alternative)
	ccb.code.addLabel(afterIfStmt)
	if !falseNoNil {
		compileLoadNull(ccb)
	}
}

func compileIfStatementNoElse(ccb *codeBlockCompiler, ifs *ast.IfExpression) {
	compile(ccb, ifs.Condition)

	_, noNil := ifs.Consequence.Statements[len(ifs.Consequence.Statements)-1].(*ast.ExpressionStatement)
	afterIfStmt := randomLabel("afterIf_")

	ccb.code.addLabeledArgs(opcode.PopJumpIfFalse, afterIfStmt)
	compile(ccb, ifs.Consequence)
	ccb.code.addLabel(afterIfStmt)
	if noNil {
		ccb.code.addInst(opcode.JumpForward, 3)
	}
	compileLoadNull(ccb)
}

func compileLoadNull(ccb *codeBlockCompiler) {
	ccb.code.addInst(opcode.LoadConst, ccb.constants.indexOf(object.NullConst))
}

func compileCompareExpression(ccb *codeBlockCompiler, cmp *ast.CompareExpression) {
	compile(ccb, cmp.Left)

	afterCompareLabel := randomLabel("cmp_")

	if cmp.Token.Type == token.LAnd {
		ccb.code.addLabeledArgs(opcode.JumpIfFalseOrPop, afterCompareLabel)
	} else {
		ccb.code.addLabeledArgs(opcode.JumpIfTrueOrPop, afterCompareLabel)
	}

	compile(ccb, cmp.Right)
	ccb.code.addLabel(afterCompareLabel)
}

func compileLoop(ccb *codeBlockCompiler, loop *ast.LoopStatement) {
	if loop.Init == nil {
		if loop.Condition == nil {
			compileInfiniteLoop(ccb, loop)
		} else {
			compileWhileLoop(ccb, loop)
		}
		return
	}

	endBlockLbl := randomLabel("end_")
	iterBlockLbl := randomLabel("iter_")

	// A loop begins with a PREPARE_BLOCK opcode this creates the first layer environment
	ccb.code.addInst(opcode.OpenScope)
	// Initialization is done in this first layer
	compile(ccb, loop.Init)

	condCCB := &codeBlockCompiler{
		constants: ccb.constants,
		locals:    newStringTableOffset(len(ccb.locals.table)),
		names:     ccb.names,
		code:      NewInstSet(),
		filename:  ccb.filename,
	}

	// Compile the loop's condition check code
	compile(condCCB, loop.Condition)

	// Prepare for main body
	bodyCCB := &codeBlockCompiler{
		constants: ccb.constants,
		locals:    newStringTableOffset(len(ccb.locals.table)),
		names:     ccb.names,
		code:      NewInstSet(),
		filename:  ccb.filename,
	}

	// Compile main body of loop
	compile(bodyCCB, loop.Body)

	// If the body ends in an expression, we need to pop it so the stack is correct
	if _, ok := loop.Body.Statements[len(loop.Body.Statements)-1].(*ast.ExpressionStatement); ok {
		bodyCCB.code.addInst(opcode.Pop)
	}

	// This copies the local variables into the outer compile block for table indexing
	for _, n := range bodyCCB.locals.table[len(ccb.locals.table):] {
		ccb.locals.indexOf(n)
	}

	// Prepare for iteration code
	iterCCB := &codeBlockCompiler{
		constants: ccb.constants,
		locals:    newStringTableOffset(len(ccb.locals.table)),
		names:     ccb.names,
		code:      NewInstSet(),
		filename:  ccb.filename,
	}

	// Compile iteration
	compile(iterCCB, loop.Iter)

	// Again, copy over the locals for indexing
	for _, n := range iterCCB.locals.table[len(ccb.locals.table):] {
		ccb.locals.indexOf(n)
	}

	ccb.code.addLabeledArgs(opcode.StartLoop, endBlockLbl, iterBlockLbl)

	ccb.code.merge(condCCB.code)
	ccb.code.addLabeledArgs(opcode.PopJumpIfFalse, endBlockLbl)
	ccb.code.merge(bodyCCB.code)

	ccb.code.addLabel(iterBlockLbl)
	ccb.code.merge(iterCCB.code)
	ccb.code.addInst(opcode.NextIter)
	ccb.code.addLabel(endBlockLbl)
	ccb.code.addInst(opcode.EndBlock)
	ccb.code.addInst(opcode.CloseScope)
	ccb.code.addInst(opcode.CloseScope)
}

func compileInfiniteLoop(ccb *codeBlockCompiler, loop *ast.LoopStatement) {
	endBlockLbl := randomLabel("end_")
	iterBlockLbl := randomLabel("iter_")

	ccb.code.addLabeledArgs(opcode.StartLoop, endBlockLbl, iterBlockLbl)

	bodyCCB := &codeBlockCompiler{
		constants: ccb.constants,
		locals:    newStringTableOffset(len(ccb.locals.table)),
		names:     ccb.names,
		code:      NewInstSet(),
		filename:  ccb.filename,
	}
	compile(bodyCCB, loop.Body)

	// If the body ends in an expression, we need to pop it so the stack is correct
	if _, ok := loop.Body.Statements[len(loop.Body.Statements)-1].(*ast.ExpressionStatement); ok {
		bodyCCB.code.addInst(opcode.Pop)
	}

	// This copies the local variables into the outer compile block for table indexing
	for _, n := range bodyCCB.locals.table[len(ccb.locals.table):] {
		ccb.locals.indexOf(n)
	}
	ccb.code.merge(bodyCCB.code)

	ccb.code.addLabel(iterBlockLbl)
	ccb.code.addInst(opcode.NextIter)
	ccb.code.addLabel(endBlockLbl)
	ccb.code.addInst(opcode.EndBlock)
	ccb.code.addInst(opcode.CloseScope)
}

func compileWhileLoop(ccb *codeBlockCompiler, loop *ast.LoopStatement) {
	endBlockLbl := randomLabel("end_")
	iterBlockLbl := randomLabel("iter_")

	condCCB := &codeBlockCompiler{
		constants: ccb.constants,
		locals:    newStringTableOffset(len(ccb.locals.table)),
		names:     ccb.names,
		code:      NewInstSet(),
		filename:  ccb.filename,
	}

	// Compile the loop's condition check code
	compile(condCCB, loop.Condition)

	// Prepare for main body
	bodyCCB := &codeBlockCompiler{
		constants: ccb.constants,
		locals:    newStringTableOffset(len(ccb.locals.table)),
		names:     ccb.names,
		code:      NewInstSet(),
		filename:  ccb.filename,
	}

	// Compile main body of loop
	compile(bodyCCB, loop.Body)

	// If the body ends in an expression, we need to pop it so the stack is correct
	if _, ok := loop.Body.Statements[len(loop.Body.Statements)-1].(*ast.ExpressionStatement); ok {
		bodyCCB.code.addInst(opcode.Pop)
	}

	// This copies the local variables into the outer compile block for table indexing
	for _, n := range bodyCCB.locals.table[len(ccb.locals.table):] {
		ccb.locals.indexOf(n)
	}

	ccb.code.addLabeledArgs(opcode.StartLoop, endBlockLbl, iterBlockLbl)

	ccb.code.merge(condCCB.code)
	ccb.code.addLabeledArgs(opcode.PopJumpIfFalse, endBlockLbl)
	ccb.code.merge(bodyCCB.code)

	ccb.code.addLabel(iterBlockLbl)
	ccb.code.addInst(opcode.NextIter)
	ccb.code.addLabel(endBlockLbl)
	ccb.code.addInst(opcode.EndBlock)
	ccb.code.addInst(opcode.CloseScope)
}
<commit_msg>Fixed if statement with no else block<commit_after>package compiler

import (
	"fmt"

	"github.com/nitrogen-lang/nitrogen/src/ast"
	"github.com/nitrogen-lang/nitrogen/src/object"
	"github.com/nitrogen-lang/nitrogen/src/token"
	"github.com/nitrogen-lang/nitrogen/src/vm/opcode"
)

func compileClassLiteral(ccb *codeBlockCompiler, class *ast.ClassLiteral) {
	for _, f := range class.Methods {
		f.FQName = fmt.Sprintf("%s.%s", class.Name, f.Name)
		compileFunction(ccb, f, true, class.Parent != "")
	}

	ccb2 := &codeBlockCompiler{
		constants: newConstantTable(),
		locals:    newStringTable(),
		names:     newStringTable(),
		code:      NewInstSet(),
		filename:  ccb.filename,
	}

	for _, f := range class.Fields {
		compile(ccb2, f)
	}
	compileLoadNull(ccb2)
	ccb2.code.addInst(opcode.Return)

	code := ccb2.code
	props := &CodeBlock{
		Name:         fmt.Sprintf("%s.__init", class.Name),
		Filename:     ccb.filename,
		LocalCount:   len(ccb2.locals.table),
		Code:         code.Assemble(),
		Constants:    ccb2.constants.table,
		Names:        ccb2.names.table,
		Locals:       ccb2.locals.table,
		MaxStackSize: calculateStackSize(code),
		MaxBlockSize: calculateBlockSize(code),
	}

	ccb.code.addInst(opcode.LoadConst, ccb.constants.indexOf(props))

	if class.Parent == "" {
		compileLoadNull(ccb)
	} else {
		compile(ccb, &ast.Identifier{Value: class.Parent})
	}

	ccb.code.addInst(opcode.LoadConst, ccb.constants.indexOf(object.MakeStringObj(class.Name)))
	ccb.code.addInst(opcode.BuildClass, uint16(len(class.Methods)))
}

func compileTryCatch(ccb *codeBlockCompiler, try *ast.TryCatchExpression) {
	_, tryNoNil := try.Try.Statements[len(try.Try.Statements)-1].(*ast.ExpressionStatement)
	_, catchNoNil := try.Catch.Statements[len(try.Catch.Statements)-1].(*ast.ExpressionStatement)

	catchBlkLbl := randomLabel("catch_")
	endTryLbl := randomLabel("endTry_")

	ccb.code.addLabeledArgs(opcode.StartTry, catchBlkLbl)
	compile(ccb, try.Try)
	ccb.code.addLabeledArgs(opcode.JumpAbsolute, endTryLbl)

	ccb.code.addLabel(catchBlkLbl)
	if try.Symbol == nil {
		ccb.code.addInst(opcode.Pop)
	} else {
		ccb.code.addInst(opcode.Define, ccb.locals.indexOf(try.Symbol.Value))
	}

	compile(ccb, try.Catch)
	if try.Symbol != nil {
		ccb.code.addInst(opcode.DeleteFast, ccb.locals.indexOf(try.Symbol.Value))
	}

	if catchNoNil && !tryNoNil {
		ccb.code.addInst(opcode.JumpForward, 3)
	}

	if !tryNoNil || !catchNoNil {
		compileLoadNull(ccb)
	}

	ccb.code.addLabel(endTryLbl)
	ccb.code.addInst(opcode.EndBlock)
}

func compileBlock(ccb *codeBlockCompiler, block *ast.BlockStatement) {
	l := len(block.Statements) - 1
	for i, s := range block.Statements {
		compile(ccb, s)
		if i < l {
			switch s.(type) {
			case *ast.ExpressionStatement:
				ccb.code.addInst(opcode.Pop)
			}
		}
	}
}

func compileFunction(ccb *codeBlockCompiler, fn *ast.FunctionLiteral, inClass, hasParent bool) {
	ccb2 := &codeBlockCompiler{
		constants: newConstantTable(),
		locals:    newStringTable(),
		names:     newStringTable(),
		code:      NewInstSet(),
		filename:  ccb.filename,
	}

	for _, p := range fn.Parameters {
		ccb2.locals.indexOf(p.Value)
	}
	ccb2.locals.indexOf("arguments") // `arguments` holds any remaining arguments from a function call
	if inClass {
		ccb2.locals.indexOf("this")
		if hasParent {
			ccb2.locals.indexOf("parent")
		}
	}

	compile(ccb2, fn.Body)

	if len(fn.Body.Statements) > 0 {
		switch fn.Body.Statements[len(fn.Body.Statements)-1].(type) {
		case *ast.ExpressionStatement:
			break
		case *ast.ReturnStatement:
			break
		default:
			compileLoadNull(ccb2)
		}

		if !ccb2.code.last().Is(opcode.Return) {
			ccb2.code.addInst(opcode.Return)
		}
	} else {
		compileLoadNull(ccb2)
		ccb2.code.addInst(opcode.Return)
	}

	code := ccb2.code
	body := &CodeBlock{
		Name:         fn.FQName,
		Filename:     ccb.filename,
		LocalCount:   len(ccb2.locals.table),
		Code:         code.Assemble(),
		Constants:    ccb2.constants.table,
		Names:        ccb2.names.table,
		Locals:       ccb2.locals.table,
		MaxStackSize: calculateStackSize(code),
		MaxBlockSize: calculateBlockSize(code),
	}

	ccb.code.addInst(opcode.LoadConst, ccb.constants.indexOf(body))

	for _, p := range fn.Parameters {
		ccb.code.addInst(opcode.LoadConst, ccb.constants.indexOf(object.MakeStringObj(p.Value)))
	}
	ccb.code.addInst(opcode.MakeArray, uint16(len(fn.Parameters)))

	ccb.code.addInst(opcode.LoadConst, ccb.constants.indexOf(object.MakeStringObj(fn.Name)))

	ccb.code.addInst(opcode.MakeFunction)
}

func compileIfStatement(ccb *codeBlockCompiler, ifs *ast.IfExpression) {
	if ifs.Alternative == nil {
		compileIfStatementNoElse(ccb, ifs)
		return
	}

	compile(ccb, ifs.Condition)

	_, trueNoNil := ifs.Consequence.Statements[len(ifs.Consequence.Statements)-1].(*ast.ExpressionStatement)
	falseBrnLbl := randomLabel("false_")
	ccb.code.addLabeledArgs(opcode.PopJumpIfFalse, falseBrnLbl)
	compile(ccb, ifs.Consequence)
	if !trueNoNil {
		compileLoadNull(ccb)
	}

	_, falseNoNil := ifs.Alternative.Statements[len(ifs.Alternative.Statements)-1].(*ast.ExpressionStatement)
	afterIfStmt := randomLabel("afterIf_")
	ccb.code.addLabeledArgs(opcode.JumpAbsolute, afterIfStmt)
	ccb.code.addLabel(falseBrnLbl)
	compile(ccb, ifs.Alternative)
	ccb.code.addLabel(afterIfStmt)
	if !falseNoNil {
		compileLoadNull(ccb)
	}
}

func compileIfStatementNoElse(ccb *codeBlockCompiler, ifs *ast.IfExpression) {
	compile(ccb, ifs.Condition)

	_, noNil := ifs.Consequence.Statements[len(ifs.Consequence.Statements)-1].(*ast.ExpressionStatement)
	falseBrnLbl := randomLabel("false_")
	afterIfStmt := randomLabel("afterIf_")

	ccb.code.addLabeledArgs(opcode.PopJumpIfFalse, falseBrnLbl)
	compile(ccb, ifs.Consequence)
	if !noNil {
		compileLoadNull(ccb)
	}

	ccb.code.addLabeledArgs(opcode.JumpAbsolute, afterIfStmt)
	ccb.code.addLabel(falseBrnLbl)
	compileLoadNull(ccb)
	ccb.code.addLabel(afterIfStmt)
}

func compileLoadNull(ccb *codeBlockCompiler) {
	ccb.code.addInst(opcode.LoadConst, ccb.constants.indexOf(object.NullConst))
}

func compileCompareExpression(ccb *codeBlockCompiler, cmp *ast.CompareExpression) {
	compile(ccb, cmp.Left)

	afterCompareLabel := randomLabel("cmp_")

	if cmp.Token.Type == token.LAnd {
		ccb.code.addLabeledArgs(opcode.JumpIfFalseOrPop, afterCompareLabel)
	} else {
		ccb.code.addLabeledArgs(opcode.JumpIfTrueOrPop, afterCompareLabel)
	}

	compile(ccb, cmp.Right)
	ccb.code.addLabel(afterCompareLabel)
}

func compileLoop(ccb *codeBlockCompiler, loop *ast.LoopStatement) {
	if loop.Init == nil {
		if loop.Condition == nil {
			compileInfiniteLoop(ccb, loop)
		} else {
			compileWhileLoop(ccb, loop)
		}
		return
	}

	endBlockLbl := randomLabel("end_")
	iterBlockLbl := randomLabel("iter_")

	// A loop begins with a PREPARE_BLOCK opcode this creates the first layer environment
	ccb.code.addInst(opcode.OpenScope)
	// Initialization is done in this first layer
	compile(ccb, loop.Init)

	condCCB := &codeBlockCompiler{
		constants: ccb.constants,
		locals:    newStringTableOffset(len(ccb.locals.table)),
		names:     ccb.names,
		code:      NewInstSet(),
		filename:  ccb.filename,
	}

	// Compile the loop's condition check code
	compile(condCCB, loop.Condition)

	// Prepare for main body
	bodyCCB := &codeBlockCompiler{
		constants: ccb.constants,
		locals:    newStringTableOffset(len(ccb.locals.table)),
		names:     ccb.names,
		code:      NewInstSet(),
		filename:  ccb.filename,
	}

	// Compile main body of loop
	compile(bodyCCB, loop.Body)

	// If the body ends in an expression, we need to pop it so the stack is correct
	if _, ok := loop.Body.Statements[len(loop.Body.Statements)-1].(*ast.ExpressionStatement); ok {
		bodyCCB.code.addInst(opcode.Pop)
	}

	// This copies the local variables into the outer compile block for table indexing
	for _, n := range bodyCCB.locals.table[len(ccb.locals.table):] {
		ccb.locals.indexOf(n)
	}

	// Prepare for iteration code
	iterCCB := &codeBlockCompiler{
		constants: ccb.constants,
		locals:    newStringTableOffset(len(ccb.locals.table)),
		names:     ccb.names,
		code:      NewInstSet(),
		filename:  ccb.filename,
	}

	// Compile iteration
	compile(iterCCB, loop.Iter)

	// Again, copy over the locals for indexing
	for _, n := range iterCCB.locals.table[len(ccb.locals.table):] {
		ccb.locals.indexOf(n)
	}

	ccb.code.addLabeledArgs(opcode.StartLoop, endBlockLbl, iterBlockLbl)

	ccb.code.merge(condCCB.code)
	ccb.code.addLabeledArgs(opcode.PopJumpIfFalse, endBlockLbl)
	ccb.code.merge(bodyCCB.code)

	ccb.code.addLabel(iterBlockLbl)
	ccb.code.merge(iterCCB.code)
	ccb.code.addInst(opcode.NextIter)
	ccb.code.addLabel(endBlockLbl)
	ccb.code.addInst(opcode.EndBlock)
	ccb.code.addInst(opcode.CloseScope)
	ccb.code.addInst(opcode.CloseScope)
}

func compileInfiniteLoop(ccb *codeBlockCompiler, loop *ast.LoopStatement) {
	endBlockLbl := randomLabel("end_")
	iterBlockLbl := randomLabel("iter_")

	ccb.code.addLabeledArgs(opcode.StartLoop, endBlockLbl, iterBlockLbl)

	bodyCCB := &codeBlockCompiler{
		constants: ccb.constants,
		locals:    newStringTableOffset(len(ccb.locals.table)),
		names:     ccb.names,
		code:      NewInstSet(),
		filename:  ccb.filename,
	}
	compile(bodyCCB, loop.Body)

	// If the body ends in an expression, we need to pop it so the stack is correct
	if _, ok := loop.Body.Statements[len(loop.Body.Statements)-1].(*ast.ExpressionStatement); ok {
		bodyCCB.code.addInst(opcode.Pop)
	}

	// This copies the local variables into the outer compile block for table indexing
	for _, n := range bodyCCB.locals.table[len(ccb.locals.table):] {
		ccb.locals.indexOf(n)
	}
	ccb.code.merge(bodyCCB.code)

	ccb.code.addLabel(iterBlockLbl)
	ccb.code.addInst(opcode.NextIter)
	ccb.code.addLabel(endBlockLbl)
	ccb.code.addInst(opcode.EndBlock)
	ccb.code.addInst(opcode.CloseScope)
}

func compileWhileLoop(ccb *codeBlockCompiler, loop *ast.LoopStatement) {
	endBlockLbl := randomLabel("end_")
	iterBlockLbl := randomLabel("iter_")

	condCCB := &codeBlockCompiler{
		constants: ccb.constants,
		locals:    newStringTableOffset(len(ccb.locals.table)),
		names:     ccb.names,
		code:      NewInstSet(),
		filename:  ccb.filename,
	}

	// Compile the loop's condition check code
	compile(condCCB, loop.Condition)

	// Prepare for main body
	bodyCCB := &codeBlockCompiler{
		constants: ccb.constants,
		locals:    newStringTableOffset(len(ccb.locals.table)),
		names:     ccb.names,
		code:      NewInstSet(),
		filename:  ccb.filename,
	}

	// Compile main body of loop
	compile(bodyCCB, loop.Body)

	// If the body ends in an expression, we need to pop it so the stack is correct
	if _, ok := loop.Body.Statements[len(loop.Body.Statements)-1].(*ast.ExpressionStatement); ok {
		bodyCCB.code.addInst(opcode.Pop)
	}

	// This copies the local variables into the outer compile block for table indexing
	for _, n := range bodyCCB.locals.table[len(ccb.locals.table):] {
		ccb.locals.indexOf(n)
	}

	ccb.code.addLabeledArgs(opcode.StartLoop, endBlockLbl, iterBlockLbl)

	ccb.code.merge(condCCB.code)
	ccb.code.addLabeledArgs(opcode.PopJumpIfFalse, endBlockLbl)
	ccb.code.merge(bodyCCB.code)

	ccb.code.addLabel(iterBlockLbl)
	ccb.code.addInst(opcode.NextIter)
	ccb.code.addLabel(endBlockLbl)
	ccb.code.addInst(opcode.EndBlock)
	ccb.code.addInst(opcode.CloseScope)
}
<|endoftext|>