<commit_before>package in_test

import (
	"fmt"
	"io/ioutil"
	"os"
	"os/exec"
	"path"

	"terraform-resource/in"
	"terraform-resource/models"
	"terraform-resource/out"
	"terraform-resource/test/helpers"

	. "github.com/onsi/ginkgo"
	. "github.com/onsi/gomega"
)

var _ = Describe("JSON Plan", func() {

	var (
		backendType   string
		backendConfig map[string]interface{}
		envName       string
		stateFilePath string
		planFilePath  string
		s3ObjectPath  string
		workingDir    string
		inDir         string
		workspacePath string
		awsVerifier   *helpers.AWSVerifier
		accessKey     string
		secretKey     string
		bucket        string
		bucketPath    string
		region        string
	)

	BeforeEach(func() {
		accessKey = os.Getenv("AWS_ACCESS_KEY")
		Expect(accessKey).ToNot(BeEmpty(), "AWS_ACCESS_KEY must be set")
		secretKey = os.Getenv("AWS_SECRET_KEY")
		Expect(secretKey).ToNot(BeEmpty(), "AWS_SECRET_KEY must be set")
		bucket = os.Getenv("AWS_BUCKET")
		Expect(bucket).ToNot(BeEmpty(), "AWS_BUCKET must be set")
		bucketPath = os.Getenv("AWS_BUCKET_SUBFOLDER")
		Expect(bucketPath).ToNot(BeEmpty(), "AWS_BUCKET_SUBFOLDER must be set")

		region = os.Getenv("AWS_REGION") // optional
		if region == "" {
			region = "us-east-1"
		}

		awsVerifier = helpers.NewAWSVerifier(
			accessKey,
			secretKey,
			region,
			"",
		)

		region := os.Getenv("AWS_REGION") // optional
		if region == "" {
			region = "us-east-1"
		}

		workspacePath = helpers.RandomString("out-backend-test")

		envName = helpers.RandomString("out-test")
		stateFilePath = path.Join(workspacePath, envName, "terraform.tfstate")
		planFilePath = path.Join(workspacePath, fmt.Sprintf("%s-plan", envName), "terraform.tfstate")
		s3ObjectPath = path.Join(bucketPath, helpers.RandomString("out-test"))

		backendType = "s3"
		backendConfig = map[string]interface{}{
			"bucket":               bucket,
			"key":                  "terraform.tfstate",
			"access_key":           accessKey,
			"secret_key":           secretKey,
			"region":               region,
			"workspace_key_prefix": workspacePath,
		}

		var err error
		workingDir, err = ioutil.TempDir(os.TempDir(), "terraform-resource-out-test")
		Expect(err).ToNot(HaveOccurred())

		inDir, err = ioutil.TempDir(os.TempDir(), "terraform-resource-in-test")
		Expect(err).ToNot(HaveOccurred())

		// ensure relative paths resolve correctly
		err = os.Chdir(workingDir)
		Expect(err).ToNot(HaveOccurred())

		fixturesDir := path.Join(helpers.ProjectRoot(), "fixtures")
		err = exec.Command("cp", "-r", fixturesDir, workingDir).Run()
		Expect(err).ToNot(HaveOccurred())

		err = helpers.DownloadStatefulPlugin(workingDir)
		Expect(err).ToNot(HaveOccurred())
	})

	AfterEach(func() {
		_ = os.RemoveAll(workingDir)
		_ = os.RemoveAll(inDir)
		awsVerifier.DeleteObjectFromS3(bucket, s3ObjectPath)
		awsVerifier.DeleteObjectFromS3(bucket, planFilePath)
		awsVerifier.DeleteObjectFromS3(bucket, stateFilePath)
	})

	It("plan infrastructure and test it", func() {
		planOutRequest := models.OutRequest{
			Source: models.Source{
				Terraform: models.Terraform{
					BackendType:   backendType,
					BackendConfig: backendConfig,
				},
			},
			Params: models.OutParams{
				EnvName: envName,
				Terraform: models.Terraform{
					Source:   "fixtures/aws/",
					PlanOnly: true,
					Env: map[string]string{
						"HOME": workingDir, // in prod plugin is installed system-wide
					},
					Vars: map[string]interface{}{
						"access_key":     accessKey,
						"secret_key":     secretKey,
						"bucket":         bucket,
						"object_key":     s3ObjectPath,
						"object_content": "terraform-is-neat",
						"region":         region,
					},
				},
			},
		}

		By("running 'out' to create the plan file")

		planrunner := out.Runner{
			SourceDir: workingDir,
			LogWriter: GinkgoWriter,
		}
		planOutput, err := planrunner.Run(planOutRequest)
		Expect(err).ToNot(HaveOccurred())

		By("ensuring that plan file exists")

		awsVerifier.ExpectS3FileToExist(
			bucket,
			planFilePath,
		)
		defer awsVerifier.DeleteObjectFromS3(bucket, planFilePath)

		Expect(planOutput.Version.EnvName).To(Equal(planOutRequest.Params.EnvName))
		Expect(planOutput.Version.PlanOnly).To(Equal("true"), "Expected PlanOnly to be true, but was false")
		Expect(planOutput.Version.Serial).To(BeEmpty())
		Expect(planOutput.Version.PlanChecksum).To(MatchRegexp("[0-9|a-f]+"))

		By("outputs the statefile if `output_planfile` is given")

		inReq := models.InRequest{
			Source: models.Source{
				Terraform: models.Terraform{
					Source: ".",
					Env: map[string]string{
						"HOME": inDir,
					},
					BackendType: "s3",
					BackendConfig: map[string]interface{}{
						"bucket":               bucket,
						"key":                  "terraform.tfstate",
						"access_key":           accessKey,
						"secret_key":           secretKey,
						"region":               region,
						"workspace_key_prefix": workspacePath,
					},
				},
			},
			Version: models.Version{
				EnvName: envName,
				Serial:  "0",
			},
			Params: models.InParams{
				OutputJSONPlanfile: true,
			},
		}

		runner := in.Runner{
			OutputDir: inDir,
		}
		_, err = runner.Run(inReq)
		Expect(err).ToNot(HaveOccurred())

		expectedNamePath := path.Join(inDir, "name")
		Expect(expectedNamePath).To(BeAnExistingFile())

		expectedStatePath := path.Join(inDir, "plan.json")
		Expect(expectedStatePath).To(BeAnExistingFile())

		stateContents, err := ioutil.ReadFile(expectedStatePath)
		Expect(err).To(BeNil())
		Expect(string(stateContents)).To(ContainSubstring("variables"))
		Expect(string(stateContents)).To(ContainSubstring("output_changes"))
		Expect(string(stateContents)).To(ContainSubstring("resource_changes"))
		Expect(string(stateContents)).To(ContainSubstring("\"format_version\":\"0.1\""))
	})
})
<commit_msg>var renaming<commit_after>package in_test

import (
	"fmt"
	"io/ioutil"
	"os"
	"os/exec"
	"path"

	"terraform-resource/in"
	"terraform-resource/models"
	"terraform-resource/out"
	"terraform-resource/test/helpers"

	. "github.com/onsi/ginkgo"
	. "github.com/onsi/gomega"
)

var _ = Describe("JSON Plan", func() {

	var (
		backendType   string
		backendConfig map[string]interface{}
		envName       string
		stateFilePath string
		planFilePath  string
		s3ObjectPath  string
		workingDir    string
		inDir         string
		workspacePath string
		awsVerifier   *helpers.AWSVerifier
		accessKey     string
		secretKey     string
		bucket        string
		bucketPath    string
		region        string
	)

	BeforeEach(func() {
		accessKey = os.Getenv("AWS_ACCESS_KEY")
		Expect(accessKey).ToNot(BeEmpty(), "AWS_ACCESS_KEY must be set")
		secretKey = os.Getenv("AWS_SECRET_KEY")
		Expect(secretKey).ToNot(BeEmpty(), "AWS_SECRET_KEY must be set")
		bucket = os.Getenv("AWS_BUCKET")
		Expect(bucket).ToNot(BeEmpty(), "AWS_BUCKET must be set")
		bucketPath = os.Getenv("AWS_BUCKET_SUBFOLDER")
		Expect(bucketPath).ToNot(BeEmpty(), "AWS_BUCKET_SUBFOLDER must be set")

		region = os.Getenv("AWS_REGION") // optional
		if region == "" {
			region = "us-east-1"
		}

		awsVerifier = helpers.NewAWSVerifier(
			accessKey,
			secretKey,
			region,
			"",
		)

		region := os.Getenv("AWS_REGION") // optional
		if region == "" {
			region = "us-east-1"
		}

		workspacePath = helpers.RandomString("out-backend-test")

		envName = helpers.RandomString("out-test")
		stateFilePath = path.Join(workspacePath, envName, "terraform.tfstate")
		planFilePath = path.Join(workspacePath, fmt.Sprintf("%s-plan", envName), "terraform.tfstate")
		s3ObjectPath = path.Join(bucketPath, helpers.RandomString("out-test"))

		backendType = "s3"
		backendConfig = map[string]interface{}{
			"bucket":               bucket,
			"key":                  "terraform.tfstate",
			"access_key":           accessKey,
			"secret_key":           secretKey,
			"region":               region,
			"workspace_key_prefix": workspacePath,
		}

		var err error
		workingDir, err = ioutil.TempDir(os.TempDir(), "terraform-resource-out-test")
		Expect(err).ToNot(HaveOccurred())

		inDir, err = ioutil.TempDir(os.TempDir(), "terraform-resource-in-test")
		Expect(err).ToNot(HaveOccurred())

		// ensure relative paths resolve correctly
		err = os.Chdir(workingDir)
		Expect(err).ToNot(HaveOccurred())

		fixturesDir := path.Join(helpers.ProjectRoot(), "fixtures")
		err = exec.Command("cp", "-r", fixturesDir, workingDir).Run()
		Expect(err).ToNot(HaveOccurred())

		err = helpers.DownloadStatefulPlugin(workingDir)
		Expect(err).ToNot(HaveOccurred())
	})

	AfterEach(func() {
		_ = os.RemoveAll(workingDir)
		_ = os.RemoveAll(inDir)
		awsVerifier.DeleteObjectFromS3(bucket, s3ObjectPath)
		awsVerifier.DeleteObjectFromS3(bucket, planFilePath)
		awsVerifier.DeleteObjectFromS3(bucket, stateFilePath)
	})

	It("plan infrastructure and test it", func() {
		planOutRequest := models.OutRequest{
			Source: models.Source{
				Terraform: models.Terraform{
					BackendType:   backendType,
					BackendConfig: backendConfig,
				},
			},
			Params: models.OutParams{
				EnvName: envName,
				Terraform: models.Terraform{
					Source:   "fixtures/aws/",
					PlanOnly: true,
					Env: map[string]string{
						"HOME": workingDir, // in prod plugin is installed system-wide
					},
					Vars: map[string]interface{}{
						"access_key":     accessKey,
						"secret_key":     secretKey,
						"bucket":         bucket,
						"object_key":     s3ObjectPath,
						"object_content": "terraform-is-neat",
						"region":         region,
					},
				},
			},
		}

		By("running 'out' to create the plan file")

		planrunner := out.Runner{
			SourceDir: workingDir,
			LogWriter: GinkgoWriter,
		}
		planOutput, err := planrunner.Run(planOutRequest)
		Expect(err).ToNot(HaveOccurred())

		By("ensuring that plan file exists")

		awsVerifier.ExpectS3FileToExist(
			bucket,
			planFilePath,
		)
		defer awsVerifier.DeleteObjectFromS3(bucket, planFilePath)

		Expect(planOutput.Version.EnvName).To(Equal(planOutRequest.Params.EnvName))
		Expect(planOutput.Version.PlanOnly).To(Equal("true"), "Expected PlanOnly to be true, but was false")
		Expect(planOutput.Version.Serial).To(BeEmpty())
		Expect(planOutput.Version.PlanChecksum).To(MatchRegexp("[0-9|a-f]+"))

		By("outputs the statefile if `output_planfile` is given")

		inReq := models.InRequest{
			Source: models.Source{
				Terraform: models.Terraform{
					Source: ".",
					Env: map[string]string{
						"HOME": inDir,
					},
					BackendType: "s3",
					BackendConfig: map[string]interface{}{
						"bucket":               bucket,
						"key":                  "terraform.tfstate",
						"access_key":           accessKey,
						"secret_key":           secretKey,
						"region":               region,
						"workspace_key_prefix": workspacePath,
					},
				},
			},
			Version: models.Version{
				EnvName: envName,
				Serial:  "0",
			},
			Params: models.InParams{
				OutputJSONPlanfile: true,
			},
		}

		runner := in.Runner{
			OutputDir: inDir,
		}
		_, err = runner.Run(inReq)
		Expect(err).ToNot(HaveOccurred())

		expectedNamePath := path.Join(inDir, "name")
		Expect(expectedNamePath).To(BeAnExistingFile())

		expectedPlanPath := path.Join(inDir, "plan.json")
		Expect(expectedPlanPath).To(BeAnExistingFile())

		stateContents, err := ioutil.ReadFile(expectedPlanPath)
		Expect(err).To(BeNil())
		Expect(string(stateContents)).To(ContainSubstring("variables"))
		Expect(string(stateContents)).To(ContainSubstring("output_changes"))
		Expect(string(stateContents)).To(ContainSubstring("resource_changes"))
		Expect(string(stateContents)).To(ContainSubstring("\"format_version\":\"0.1\""))
	})
})
<|endoftext|>


<commit_before>/*
Copyright 2019 The Vitess Authors.

Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at

    http://www.apache.org/licenses/LICENSE-2.0

Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/

package cluster

import (
	"context"
	"fmt"
	"os"
	"strings"
	"testing"
	"time"

	"github.com/stretchr/testify/assert"

	"github.com/stretchr/testify/require"
	tabletpb "vitess.io/vitess/go/vt/proto/topodata"
	tmc "vitess.io/vitess/go/vt/vttablet/grpctmclient"
)

var (
	tmClient = tmc.NewClient()
)

// GetMasterPosition gets the master position of required vttablet
func GetMasterPosition(t *testing.T, vttablet Vttablet, hostname string) (string, string) {
	ctx := context.Background()
	vtablet := getTablet(vttablet.GrpcPort, hostname)
	pos, err := tmClient.MasterPosition(ctx, vtablet)
	require.NoError(t, err)
	gtID := strings.SplitAfter(pos, "/")[1]
	return pos, gtID
}

// VerifyRowsInTablet Verify total number of rows in a tablet
func VerifyRowsInTablet(t *testing.T, vttablet *Vttablet, ksName string, expectedRows int) {
	timeout := time.Now().Add(10 * time.Second)
	for time.Now().Before(timeout) {
		qr, err := vttablet.VttabletProcess.QueryTablet("select * from vt_insert_test", ksName, true)
		assert.Nil(t, err)
		if len(qr.Rows) == expectedRows {
			return
		}
		time.Sleep(300 * time.Millisecond)
	}
	assert.Fail(t, "expected rows not found.")
}

// VerifyLocalMetadata Verify Local Metadata of a tablet
func VerifyLocalMetadata(t *testing.T, tablet *Vttablet, ksName string, shardName string, cell string) {
	qr, err := tablet.VttabletProcess.QueryTablet("select * from _vt.local_metadata", ksName, false)
	assert.Nil(t, err)
	assert.Equal(t, fmt.Sprintf("%v", qr.Rows[0][1]), fmt.Sprintf(`BLOB("%s")`, tablet.Alias))
	assert.Equal(t, fmt.Sprintf("%v", qr.Rows[1][1]), fmt.Sprintf(`BLOB("%s.%s")`, ksName, shardName))
	assert.Equal(t, fmt.Sprintf("%v", qr.Rows[2][1]), fmt.Sprintf(`BLOB("%s")`, cell))
	if tablet.Type == "replica" {
		assert.Equal(t, fmt.Sprintf("%v", qr.Rows[3][1]), `BLOB("neutral")`)
	} else if tablet.Type == "rdonly" {
		assert.Equal(t, fmt.Sprintf("%v", qr.Rows[3][1]), `BLOB("must_not")`)
	}
}

// ListBackups Lists back preset in shard
func (cluster LocalProcessCluster) ListBackups(shardKsName string) ([]string, error) {
	output, err := cluster.VtctlclientProcess.ExecuteCommandWithOutput("ListBackups", shardKsName)
	if err != nil {
		return nil, err
	}
	result := strings.Split(output, "\n")
	var returnResult []string
	for _, str := range result {
		if str != "" {
			returnResult = append(returnResult, str)
		}
	}
	return returnResult, nil
}

// VerifyBackupCount compares the backup count with expected count.
func (cluster LocalProcessCluster) VerifyBackupCount(t *testing.T, shardKsName string, expected int) []string {
	backups, err := cluster.ListBackups(shardKsName)
	assert.Nil(t, err)
	assert.Equalf(t, expected, len(backups), "invalid number of backups")
	return backups
}

// RemoveAllBackups removes all the backup corresponds to list backup.
func (cluster LocalProcessCluster) RemoveAllBackups(t *testing.T, shardKsName string) {
	backups, err := cluster.ListBackups(shardKsName)
	assert.Nil(t, err)
	for _, backup := range backups {
		cluster.VtctlclientProcess.ExecuteCommand("RemoveBackup", shardKsName, backup)
	}
}

// ResetTabletDirectory transitions back to tablet state (i.e. mysql process restarts with cleaned directory and tablet is off)
func ResetTabletDirectory(tablet Vttablet) error {
	tablet.MysqlctlProcess.Stop()
	tablet.VttabletProcess.TearDown()
	os.RemoveAll(tablet.VttabletProcess.Directory)

	return tablet.MysqlctlProcess.Start()
}

func getTablet(tabletGrpcPort int, hostname string) *tabletpb.Tablet {
	portMap := make(map[string]int32)
	portMap["grpc"] = int32(tabletGrpcPort)
	return &tabletpb.Tablet{Hostname: hostname, PortMap: portMap}
}

func filterResultWhenRunsForCoverage(input string) string {
	lines := strings.Split(input, "\n")
	var result string
	for _, line := range lines {
		if strings.Contains(line, "=== RUN") {
			continue
		}
		if strings.Contains(line, "--- PASS:") || strings.Contains(line, "PASS") {
			break
		}
		result = result + line + "\n"
	}
	return result
}
<commit_msg>Fix coverage flag check<commit_after>/*
Copyright 2019 The Vitess Authors.

Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at

    http://www.apache.org/licenses/LICENSE-2.0

Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/

package cluster

import (
	"context"
	"fmt"
	"os"
	"strings"
	"testing"
	"time"

	"github.com/stretchr/testify/assert"

	"github.com/stretchr/testify/require"
	tabletpb "vitess.io/vitess/go/vt/proto/topodata"
	tmc "vitess.io/vitess/go/vt/vttablet/grpctmclient"
)

var (
	tmClient = tmc.NewClient()
)

// GetMasterPosition gets the master position of required vttablet
func GetMasterPosition(t *testing.T, vttablet Vttablet, hostname string) (string, string) {
	ctx := context.Background()
	vtablet := getTablet(vttablet.GrpcPort, hostname)
	pos, err := tmClient.MasterPosition(ctx, vtablet)
	require.NoError(t, err)
	gtID := strings.SplitAfter(pos, "/")[1]
	return pos, gtID
}

// VerifyRowsInTablet Verify total number of rows in a tablet
func VerifyRowsInTablet(t *testing.T, vttablet *Vttablet, ksName string, expectedRows int) {
	timeout := time.Now().Add(10 * time.Second)
	for time.Now().Before(timeout) {
		qr, err := vttablet.VttabletProcess.QueryTablet("select * from vt_insert_test", ksName, true)
		assert.Nil(t, err)
		if len(qr.Rows) == expectedRows {
			return
		}
		time.Sleep(300 * time.Millisecond)
	}
	assert.Fail(t, "expected rows not found.")
}

// VerifyLocalMetadata Verify Local Metadata of a tablet
func VerifyLocalMetadata(t *testing.T, tablet *Vttablet, ksName string, shardName string, cell string) {
	qr, err := tablet.VttabletProcess.QueryTablet("select * from _vt.local_metadata", ksName, false)
	assert.Nil(t, err)
	assert.Equal(t, fmt.Sprintf("%v", qr.Rows[0][1]), fmt.Sprintf(`BLOB("%s")`, tablet.Alias))
	assert.Equal(t, fmt.Sprintf("%v", qr.Rows[1][1]), fmt.Sprintf(`BLOB("%s.%s")`, ksName, shardName))
	assert.Equal(t, fmt.Sprintf("%v", qr.Rows[2][1]), fmt.Sprintf(`BLOB("%s")`, cell))
	if tablet.Type == "replica" {
		assert.Equal(t, fmt.Sprintf("%v", qr.Rows[3][1]), `BLOB("neutral")`)
	} else if tablet.Type == "rdonly" {
		assert.Equal(t, fmt.Sprintf("%v", qr.Rows[3][1]), `BLOB("must_not")`)
	}
}

// ListBackups Lists back preset in shard
func (cluster LocalProcessCluster) ListBackups(shardKsName string) ([]string, error) {
	output, err := cluster.VtctlclientProcess.ExecuteCommandWithOutput("ListBackups", shardKsName)
	if err != nil {
		return nil, err
	}
	result := strings.Split(output, "\n")
	var returnResult []string
	for _, str := range result {
		if str != "" {
			returnResult = append(returnResult, str)
		}
	}
	return returnResult, nil
}

// VerifyBackupCount compares the backup count with expected count.
func (cluster LocalProcessCluster) VerifyBackupCount(t *testing.T, shardKsName string, expected int) []string {
	backups, err := cluster.ListBackups(shardKsName)
	assert.Nil(t, err)
	assert.Equalf(t, expected, len(backups), "invalid number of backups")
	return backups
}

// RemoveAllBackups removes all the backup corresponds to list backup.
func (cluster LocalProcessCluster) RemoveAllBackups(t *testing.T, shardKsName string) {
	backups, err := cluster.ListBackups(shardKsName)
	assert.Nil(t, err)
	for _, backup := range backups {
		cluster.VtctlclientProcess.ExecuteCommand("RemoveBackup", shardKsName, backup)
	}
}

// ResetTabletDirectory transitions back to tablet state (i.e. mysql process restarts with cleaned directory and tablet is off)
func ResetTabletDirectory(tablet Vttablet) error {
	tablet.MysqlctlProcess.Stop()
	tablet.VttabletProcess.TearDown()
	os.RemoveAll(tablet.VttabletProcess.Directory)

	return tablet.MysqlctlProcess.Start()
}

func getTablet(tabletGrpcPort int, hostname string) *tabletpb.Tablet {
	portMap := make(map[string]int32)
	portMap["grpc"] = int32(tabletGrpcPort)
	return &tabletpb.Tablet{Hostname: hostname, PortMap: portMap}
}

func filterResultWhenRunsForCoverage(input string) string {
	if !*isCoverage {
		return input
	}
	lines := strings.Split(input, "\n")
	var result string
	for _, line := range lines {
		if strings.Contains(line, "=== RUN") {
			continue
		}
		if strings.Contains(line, "--- PASS:") || strings.Contains(line, "PASS") {
			break
		}
		result = result + line + "\n"
	}
	return result
}
<|endoftext|>


<commit_before>package proc

import (
	"strings"
)

// Auxiliary Point struct used during parsing.
type Point struct {
	x, y int
}

// Create a new Point struct
func NewPoint(x, y int)*Point {
	return &Point{x, y}
}

// Line struct defines the line x & y coordinates, the starting and ending type and the color.
type Line struct {
	x0, y0 	int
	start 	string
	x1, y1 	int
	end 	string
	color 	string
}

// Line from (x0, y0) to (x1, y1) with the given color at the start and end.
func NewLine(x0, y0 int, start string, x1, y1 int, end string, color string)*Line {
	return &Line{x0, y0, start, x1, y1, end, color}
}

// Text struct that contains the x & y coordinates and color.
type Text struct {
	x0, y0 	int
	text 	string
	color 	string
}

// Text annotation at (x0, y0) with the given color.
func NewText(x0, y0 int, text, color string)*Text {
	return &Text{x0, y0, text, color}
}

// Compounded struct with Line & Text
type Figures struct {
	Line
	Text
}

type Diagram struct {

}

// Parses given ASCII art string into a list of figures.
func (d *Diagram) ParseASCIIArt(str string)[]*Figures {
	var figures []*Figures

	lines := strings.Split(str, "\n")
	height := len(lines)

	// Get diagram widest line.
	width := func(lines []string) int {
		var max int
		for _, line := range lines {
			if len(line) > max {
				max = len(line)
			}
		}
		return max
	}(lines)

	data := make([][]string, height)

	// Convert strings into a mutable matrix of characters.
	for y := 0; y < height; y++ {
		line := lines[y]
		data[y] = make([]string, width)
		for x := 0; x < len(line); x++ {
			data[y][x] = string(line[x])
		}
		for x := len(line); x < width; x++ {
			data[y][x] = " "
		}
	}

	// Get a character from the slice or 0 if we are out of bounds.
	at := func(y, x int) string {
		if 0 <= y && y < height && 0 <= x && x < width {
			return data[y][x]
		}
		return ""
	}

	// Returns true if the character can be part of the line.
	isPartOfLine := func(x, y int) bool {
		c := at(y, x)
		switch {
		case c == "|" || c == "-" || c == "+" || c == "~" || c == "!" :
			return true
		}
		return false
	}

	toColor := func(x, y int) string {
		c := at(y, x)
		switch {
		case c =="~" || c == "!":
			return "#666"
		}
		return ""
	}

	// Returns true if characters is line ending decoration.
	isLineEnding := func(x, y int) bool {
		c := at(y, x)
		switch {
		case c == "*" || c == "<" || c == ">" || c == "^" || c == "v" :
			return true
		}
		return false
	}

	// Finds a character that belongs to unextracted line.
	findLineChar := func()*Point {
		for y := 0; y < height; y++ {
			for x := 0; x < width; x++ {
				if data[y][x] == "|" || data[y][x] == "-" {
					return NewPoint(x, y)
				}
			}
		}
		return nil
	}

	// Converts line's character to the direction of line's growth.
	dir := map[string]*Point{
		"-" : NewPoint(1, 0),
		"|" : NewPoint(0, 1),
	}

	// Erases character that belongs to the extracted line.
	eraseChar := func(x, y, dx, dy int) {
		c := at(y, x)
		switch {
		case c == "|" || c == "-" || c == "*" || c == ">" || c == "<" || c == "^" || c == "v" || c == "~" || c == "~" :
			data[y][x] = " "
			return
		case c == "+":
			dx = 1 - dx
			dy = 1 - dy
			data[y][x] = " "

			c1 := at(y - dy, x - dx)
			switch {
			case c1 == "|" || c1 == "!" || c1 == "+":
				data[y][x] = "|"
				return
			case c1 == "-" || c1 == "~" || c1 == "+":
				data[y][x] = "-"
				return
			}

			c2 := at(y + dy, x + dx)
			switch {
			case c2 == "|" || c2 == "!" || c2 == "+":
				data[y][x] = "|"
				return
			case c2 == "-" || c2 == "~" || c2 == "+":
				data[y][x] = "-"
				return
			}
			return
		}
	}

	// Erase the given extracted line.
	erase := func(line *Line) {
		var dx, dy int
		if line.x0 != line.x1 {
			dx = 1
		} else {
			dx = 0
		}
		if line.y0 != line.y1 {
			dy = 1
		} else {
			dy = 0
		}
		if dx != 0 || dy != 0 {
			x0, y0 := line.x0 + dx, line.y0 + dy
			x1, y1 := line.x1 - dx, line.y1 - dy

			for x0 <= x1 && y0 <= y1 {
				eraseChar(x0, y0, dx, dy)
				x0 += dx
				y0 += dy
			}
			eraseChar(line.x0, line.y0, dx, dy)
			eraseChar(line.x1, line.y1, dx, dy)
		} else {
			eraseChar(line.x0, line.y0, dx, dy)
		}
	}

	// Extract a single line and erase it from the ascii art matrix.
	extractLine := func() bool {
		var color, start, end string

		ch := findLineChar()
		if ch == nil {
			return false
		}

		d := dir[data[ch.y][ch.x]]
		// Find line's start by advancing in the opposite direction.
		x0 := ch.x
		y0 := ch.y
		for isPartOfLine(x0 - d.x, y0 - d.y) {
			x0 -= d.x
			y0 -= d.y
			if color == "" {
				color = toColor(x0, y0)
			}
		}
		if isLineEnding(x0 - d.x, y0 - d.y) {
			// Line has a decorated start. Extract is as well.
			x0 -= d.x
			y0 -= d.y
			if data[y0][x0] == "*" {
				start = "circle"
			} else {
				start = "arrow"
			}
		}
		// Find line's end by advancing forward in the given direction.
		x1 := ch.x
		y1 := ch.y
		for isPartOfLine(x1 + d.x, y1 + d.y) {
			x1 += d.x
			y1 += d.y
			if color == "" {
				color = toColor(x1, y1)
			}
		}
		if isLineEnding(x1 + d.x, y1 + d.y) {
			// Line has a decorated end. Extract it.
			x1 += d.x
			y1 += d.y
			if data[y1][x1] == "*" {
				end = "circle"
			} else {
				end = "arrow"
			}
		}

		// Create line object and erase line from the ascii art matrix.
		line := NewLine(x0, y0, start, x1, y1, end, color)

		figures = append(figures, &Figures{*line, Text{}})
		erase(line)

		// Adjust line start and end to accomodate for arrow endings.
		// Those should not intersect with their targets but should touch them
		// instead. Should be done after erasure to ensure that erase deletes
		// arrowheads.
		if start == "arrow" {
			line.x0 -= d.x
			line.y0 -= d.y
		}

		if end == "arrow" {
			line.x1 += d.x
			line.y1 += d.y
		}
		return true
	}
	// Extract all non space characters that were left after line extraction
	// as text objects.
	extractText := func() {
		for y := 0; y < height; y++ {
			for x := 0; x < width; x++ {
				if data[y][x] != " " {
					start, end := x, x
					for end < width && data[y][end] != " " {
						end++
					}
					getRange := func(start, end int)[]string {
						return data[y][start:end]
					}
					//fmt.Println(getRange(start, end))
					text := strings.Join(getRange(start, end), "")

					// Check if it can be concatenated with a previously found text annotation.
					prev := figures[len(figures)-1]
					if prev.Text.x0 + len(prev.text) + 1 == start {
						// If they touch concatenate them
						prev.text = prev.text + " " + text
					} else {
						color := "#000"
						if string(text[0]) == "\\" && string(text[len(text) - 1]) == "\\" {
							text = text[1: len(text) - 1]
							color = "#666"
						}
						newtext := NewText(x, y, text, color)
						figures = append(figures, &Figures{Line{}, *newtext})
					}
					x = end
				}
			}
		}
	}

	for extractLine() {}
	extractText()

	return figures
}<commit_msg>Remove Proc<commit_after><|endoftext|>


<commit_before>package phpgo

import (
	"crypto/md5"
	"crypto/rand"
	"crypto/sha1"
	"crypto/sha256"
	"fmt"
	r "math/rand"
	"time"

	"golang.org/x/crypto/bcrypt"
)

// StringRandomStr generate randmon string by specify chars
func StringRandomStr(n int, alphabets ...byte) string {
	return string(StringRandomByte(n, alphabets...))
}

// StringRandomByte generate random []byte by specify chars
func StringRandomByte(n int, alphabets ...byte) []byte {
	if len(alphabets) == 0 {
		alphabets = []byte(`0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz`)
	}
	var bytes = make([]byte, n)
	var randBy bool
	if num, err := rand.Read(bytes); num != n || err != nil {
		r.Seed(time.Now().UnixNano())
		randBy = true
	}
	for i, b := range bytes {
		if randBy {
			bytes[i] = alphabets[r.Intn(len(alphabets))]
		} else {
			bytes[i] = alphabets[b%byte(len(alphabets))]
		}
	}
	return bytes
}

// StringPasswordVerify password verfiy
func StringPasswordVerify(password, hash string) bool {
	err := bcrypt.CompareHashAndPassword([]byte(hash), []byte(password))
	return err == nil
}

// StringPasswordHash password hash
func StringPasswordHash(s string) (string, error) {
	// cost 设置12就够了，太大需要的计算时间太长，PHP中默认是10
	bytes, err := bcrypt.GenerateFromPassword([]byte(s), 12)
	return string(bytes), err
}

// StringSha256 sha256
func StringSha256(s string) string {
	return fmt.Sprintf("%x", sha256.Sum256([]byte(s)))
}

// StringSha1 sha1
func StringSha1(s string) string {
	return fmt.Sprintf("%x", sha1.Sum([]byte(s)))
}

// StringMd5 md5
func StringMd5(s string) string {
	return fmt.Sprintf("%x", md5.Sum([]byte(s)))
}
<commit_msg>sha256 hmac<commit_after>package phpgo

import (
	"crypto/hmac"
	"crypto/md5"
	"crypto/rand"
	"crypto/sha1"
	"crypto/sha256"
	"fmt"
	r "math/rand"
	"time"

	"golang.org/x/crypto/bcrypt"
)

// StringCheckHashHmac reports whether messageMAC is a valid HMAC tag for message
// func StringCheckHashHmac(message, messageMAC, key []byte) bool {
// 	mac := hmac.New(sha256.New, key)
// 	mac.Write(message)
// 	expectedMAC := mac.Sum(nil)
// 	return hmac.Equal(messageMAC, expectedMAC)
// }

// StringHashHmacSha1 hash_hmac sha1
func StringHashHmacSha1(message, key []byte) string {
	mac := hmac.New(sha1.New, key)
	mac.Write(message)
	return fmt.Sprintf("%x", mac.Sum(nil))
}

// StringHashHmac hash_hmac sha256
func StringHashHmac(message, key []byte) string {
	mac := hmac.New(sha256.New, key)
	mac.Write(message)
	return fmt.Sprintf("%x", mac.Sum(nil))
}

// StringRandomStr generate randmon string by specify chars
func StringRandomStr(n int, alphabets ...byte) string {
	return string(StringRandomByte(n, alphabets...))
}

// StringRandomByte generate random []byte by specify chars
func StringRandomByte(n int, alphabets ...byte) []byte {
	if len(alphabets) == 0 {
		alphabets = []byte(`0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz`)
	}
	var bytes = make([]byte, n)
	var randBy bool
	if num, err := rand.Read(bytes); num != n || err != nil {
		r.Seed(time.Now().UnixNano())
		randBy = true
	}
	for i, b := range bytes {
		if randBy {
			bytes[i] = alphabets[r.Intn(len(alphabets))]
		} else {
			bytes[i] = alphabets[b%byte(len(alphabets))]
		}
	}
	return bytes
}

// StringPasswordVerify password verfiy
func StringPasswordVerify(password, hash string) bool {
	err := bcrypt.CompareHashAndPassword([]byte(hash), []byte(password))
	return err == nil
}

// StringPasswordHash password hash
func StringPasswordHash(s string) (string, error) {
	// cost 设置12就够了，太大需要的计算时间太长，PHP中默认是10
	bytes, err := bcrypt.GenerateFromPassword([]byte(s), 12)
	return string(bytes), err
}

// StringSha256 sha256
func StringSha256(s string) string {
	return fmt.Sprintf("%x", sha256.Sum256([]byte(s)))
}

// StringSha1 sha1
func StringSha1(s string) string {
	return fmt.Sprintf("%x", sha1.Sum([]byte(s)))
}

// StringMd5 md5
func StringMd5(s string) string {
	return fmt.Sprintf("%x", md5.Sum([]byte(s)))
}
<|endoftext|>


<commit_before>package helpers

func FetchColors()[]string{
	colors := make([]string, 3)
        colors[0] = "#4b0082"
        colors[1] = "#4b0c8f"
        colors[2] = "#6f29b4"
	return colors
}
<commit_msg>logic to generate color scheme from supplied hex string<commit_after>package helpers

import (
	"fmt"
	"strconv"
	"os"
)

var adjustments = [][]int64 {{0,0,0},{0,12,13},{36,45,50}}

func FetchColors()[]string{

	colorString := os.Getenv("HEX_COLOR")

	colors, err := parseColorString(colorString)

	if err != nil {
		fmt.Printf("Cannot parse string %s using default color scheme\n", colorString)

		colors, err = parseColorString("008bb9")
	}else if colors[0] == 0 && colors[1] == 0 && colors[2] == 0 {
		fmt.Printf("Color String set to Black")
	}

	if colors[0] > 219 {
	  colors[0] = 219
	}

	if colors[1] > 210 {
	  colors[1] = 210
	}

	if colors[2] > 205 {
	  colors[2] = 205
	}

	colorHexStrings := createColorScheme(colors)

	return colorHexStrings
}

func createColorScheme(colors []int64)[]string{
	colorHex := make([]string, 3)

	colorHex[0] = createHexColorString(colors, adjustments[0])
	colorHex[1] = createHexColorString(colors, adjustments[1])
	colorHex[2] = createHexColorString(colors, adjustments[2])

	return colorHex
}

func createHexColorString(vals []int64, adjust []int64) string{

	colors := make([]string, 3)

	for index, _ := range colors {
		colors[index] = "0" + fmt.Sprintf("%x", vals[index]+adjust[index])
		colors[index] = colors[index][len(colors[index])-2:len(colors[index])]

	}

	return "#"+colors[0]+colors[1]+colors[2]
}

func parseColorString(hexColor string)([]int64, error){

	if len(hexColor) < 6 {
		hexColor = "000000" + hexColor
		hexColor = hexColor[len(hexColor)-6:len(hexColor)]
	}

	colorRGB := make([]int64, 3)

	var err error

	for i := 0; i < 3; i++{
		colorRGB[i], err = strconv.ParseInt(hexColor[2*i:(2*i)+2], 16, 0)
		if err != nil{
			break
		}
	}

	return colorRGB, err
}

<|endoftext|>


<commit_before>package runtime_test

import (
	"bytes"
	"fmt"
	"internal/testenv"
	"io/ioutil"
	"os"
	"os/exec"
	"path/filepath"
	"regexp"
	"runtime"
	"strconv"
	"testing"
)

func checkGdbEnvironment(t *testing.T) {
	testenv.MustHaveGoBuild(t)
	if runtime.GOOS == "darwin" {
		t.Skip("gdb does not work on darwin")
	}
	if final := os.Getenv("GOROOT_FINAL"); final != "" && runtime.GOROOT() != final {
		t.Skip("gdb test can fail with GOROOT_FINAL pending")
	}
}

func checkGdbVersion(t *testing.T) {
	// Issue 11214 reports various failures with older versions of gdb.
	out, err := exec.Command("gdb", "--version").CombinedOutput()
	if err != nil {
		t.Skipf("skipping: error executing gdb: %v", err)
	}
	re := regexp.MustCompile(`([0-9]+)\.([0-9]+)`)
	matches := re.FindSubmatch(out)
	if len(matches) < 3 {
		t.Skipf("skipping: can't determine gdb version from\n%s\n", out)
	}
	major, err1 := strconv.Atoi(string(matches[1]))
	minor, err2 := strconv.Atoi(string(matches[2]))
	if err1 != nil || err2 != nil {
		t.Skipf("skipping: can't determine gdb version: %v, %v", err1, err2)
	}
	if major < 7 || (major == 7 && minor < 7) {
		t.Skipf("skipping: gdb version %d.%d too old", major, minor)
	}
	t.Logf("gdb version %d.%d", major, minor)
}

func checkGdbPython(t *testing.T) {
	cmd := exec.Command("gdb", "-nx", "-q", "--batch", "-iex", "python import sys; print('go gdb python support')")
	out, err := cmd.CombinedOutput()

	if err != nil {
		t.Skipf("skipping due to issue running gdb: %v", err)
	}
	if string(out) != "go gdb python support\n" {
		t.Skipf("skipping due to lack of python gdb support: %s", out)
	}
}

const helloSource = `
package main
import "fmt"
func main() {
	mapvar := make(map[string]string,5)
	mapvar["abc"] = "def"
	mapvar["ghi"] = "jkl"
	strvar := "abc"
	ptrvar := &strvar
	fmt.Println("hi") // line 10
	_ = ptrvar
}
`

func TestGdbPython(t *testing.T) {
	checkGdbEnvironment(t)
	checkGdbVersion(t)
	checkGdbPython(t)

	dir, err := ioutil.TempDir("", "go-build")
	if err != nil {
		t.Fatalf("failed to create temp directory: %v", err)
	}
	defer os.RemoveAll(dir)

	src := filepath.Join(dir, "main.go")
	err = ioutil.WriteFile(src, []byte(helloSource), 0644)
	if err != nil {
		t.Fatalf("failed to create file: %v", err)
	}

	cmd := exec.Command("go", "build", "-o", "a.exe")
	cmd.Dir = dir
	out, err := testEnv(cmd).CombinedOutput()
	if err != nil {
		t.Fatalf("building source %v\n%s", err, out)
	}

	args := []string{"-nx", "-q", "--batch", "-iex",
		fmt.Sprintf("add-auto-load-safe-path %s/src/runtime", runtime.GOROOT()),
		"-ex", "set startup-with-shell off",
		"-ex", "info auto-load python-scripts",
		"-ex", "set python print-stack full",
		"-ex", "br main.go:10",
		"-ex", "run",
		"-ex", "echo BEGIN info goroutines\n",
		"-ex", "info goroutines",
		"-ex", "echo END\n",
		"-ex", "echo BEGIN print mapvar\n",
		"-ex", "print mapvar",
		"-ex", "echo END\n",
		"-ex", "echo BEGIN print strvar\n",
		"-ex", "print strvar",
		"-ex", "echo END\n"}

	// without framepointer, gdb cannot backtrace our non-standard
	// stack frames on RISC architectures.
	canBackTrace := false
	switch runtime.GOARCH {
	case "amd64", "386", "ppc64", "ppc64le", "arm", "arm64", "mips64", "mips64le", "s390x":
		canBackTrace = true
		args = append(args,
			"-ex", "echo BEGIN goroutine 2 bt\n",
			"-ex", "goroutine 2 bt",
			"-ex", "echo END\n")
	}

	args = append(args, filepath.Join(dir, "a.exe"))
	got, _ := exec.Command("gdb", args...).CombinedOutput()

	firstLine := bytes.SplitN(got, []byte("\n"), 2)[0]
	if string(firstLine) != "Loading Go Runtime support." {
		// This can happen when using all.bash with
		// GOROOT_FINAL set, because the tests are run before
		// the final installation of the files.
		cmd := exec.Command("go", "env", "GOROOT")
		cmd.Env = []string{}
		out, err := cmd.CombinedOutput()
		if err != nil && bytes.Contains(out, []byte("cannot find GOROOT")) {
			t.Skipf("skipping because GOROOT=%s does not exist", runtime.GOROOT())
		}

		_, file, _, _ := runtime.Caller(1)

		t.Logf("package testing source file: %s", file)
		t.Fatalf("failed to load Go runtime support: %s\n%s", firstLine, got)
	}

	// Extract named BEGIN...END blocks from output
	partRe := regexp.MustCompile(`(?ms)^BEGIN ([^\n]*)\n(.*?)\nEND`)
	blocks := map[string]string{}
	for _, subs := range partRe.FindAllSubmatch(got, -1) {
		blocks[string(subs[1])] = string(subs[2])
	}

	infoGoroutinesRe := regexp.MustCompile(`\*\s+\d+\s+running\s+`)
	if bl := blocks["info goroutines"]; !infoGoroutinesRe.MatchString(bl) {
		t.Fatalf("info goroutines failed: %s", bl)
	}

	printMapvarRe := regexp.MustCompile(`\Q = map[string]string = {["abc"] = "def", ["ghi"] = "jkl"}\E$`)
	if bl := blocks["print mapvar"]; !printMapvarRe.MatchString(bl) {
		t.Fatalf("print mapvar failed: %s", bl)
	}

	strVarRe := regexp.MustCompile(`\Q = "abc"\E$`)
	if bl := blocks["print strvar"]; !strVarRe.MatchString(bl) {
		t.Fatalf("print strvar failed: %s", bl)
	}

	btGoroutineRe := regexp.MustCompile(`^#0\s+runtime.+at`)
	if bl := blocks["goroutine 2 bt"]; canBackTrace && !btGoroutineRe.MatchString(bl) {
		t.Fatalf("goroutine 2 bt failed: %s", bl)
	} else if !canBackTrace {
		t.Logf("gdb cannot backtrace for GOARCH=%s, skipped goroutine backtrace test", runtime.GOARCH)
	}
}

const backtraceSource = `
package main

//go:noinline
func aaa() bool { return bbb() }

//go:noinline
func bbb() bool { return ccc() }

//go:noinline
func ccc() bool { return ddd() }

//go:noinline
func ddd() bool { return f() }

//go:noinline
func eee() bool { return true }

var f = eee

func main() {
	_ = aaa()
}
`

// TestGdbBacktrace tests that gdb can unwind the stack correctly
// using only the DWARF debug info.
func TestGdbBacktrace(t *testing.T) {
	checkGdbEnvironment(t)
	checkGdbVersion(t)

	dir, err := ioutil.TempDir("", "go-build")
	if err != nil {
		t.Fatalf("failed to create temp directory: %v", err)
	}
	defer os.RemoveAll(dir)

	// Build the source code.
	src := filepath.Join(dir, "main.go")
	err = ioutil.WriteFile(src, []byte(backtraceSource), 0644)
	if err != nil {
		t.Fatalf("failed to create file: %v", err)
	}
	cmd := exec.Command("go", "build", "-o", "a.exe")
	cmd.Dir = dir
	out, err := testEnv(cmd).CombinedOutput()
	if err != nil {
		t.Fatalf("building source %v\n%s", err, out)
	}

	// Execute gdb commands.
	args := []string{"-nx", "-batch",
		"-ex", "set startup-with-shell off",
		"-ex", "break main.eee",
		"-ex", "run",
		"-ex", "backtrace",
		"-ex", "continue",
		filepath.Join(dir, "a.exe"),
	}
	got, _ := exec.Command("gdb", args...).CombinedOutput()

	// Check that the backtrace matches the source code.
	bt := []string{
		"eee",
		"ddd",
		"ccc",
		"bbb",
		"aaa",
		"main",
	}
	for i, name := range bt {
		s := fmt.Sprintf("#%v.*main\\.%v", i, name)
		re := regexp.MustCompile(s)
		if found := re.Find(got) != nil; !found {
			t.Errorf("could not find '%v' in backtrace", s)
			t.Fatalf("gdb output:\n%v", string(got))
		}
	}
}
<commit_msg>runtime: skip TestGdbBacktrace on netbsd<commit_after>// Copyright 2015 The Go Authors. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.

package runtime_test

import (
	"bytes"
	"fmt"
	"internal/testenv"
	"io/ioutil"
	"os"
	"os/exec"
	"path/filepath"
	"regexp"
	"runtime"
	"strconv"
	"testing"
)

func checkGdbEnvironment(t *testing.T) {
	testenv.MustHaveGoBuild(t)
	if runtime.GOOS == "darwin" {
		t.Skip("gdb does not work on darwin")
	}
	if final := os.Getenv("GOROOT_FINAL"); final != "" && runtime.GOROOT() != final {
		t.Skip("gdb test can fail with GOROOT_FINAL pending")
	}
}

func checkGdbVersion(t *testing.T) {
	// Issue 11214 reports various failures with older versions of gdb.
	out, err := exec.Command("gdb", "--version").CombinedOutput()
	if err != nil {
		t.Skipf("skipping: error executing gdb: %v", err)
	}
	re := regexp.MustCompile(`([0-9]+)\.([0-9]+)`)
	matches := re.FindSubmatch(out)
	if len(matches) < 3 {
		t.Skipf("skipping: can't determine gdb version from\n%s\n", out)
	}
	major, err1 := strconv.Atoi(string(matches[1]))
	minor, err2 := strconv.Atoi(string(matches[2]))
	if err1 != nil || err2 != nil {
		t.Skipf("skipping: can't determine gdb version: %v, %v", err1, err2)
	}
	if major < 7 || (major == 7 && minor < 7) {
		t.Skipf("skipping: gdb version %d.%d too old", major, minor)
	}
	t.Logf("gdb version %d.%d", major, minor)
}

func checkGdbPython(t *testing.T) {
	cmd := exec.Command("gdb", "-nx", "-q", "--batch", "-iex", "python import sys; print('go gdb python support')")
	out, err := cmd.CombinedOutput()

	if err != nil {
		t.Skipf("skipping due to issue running gdb: %v", err)
	}
	if string(out) != "go gdb python support\n" {
		t.Skipf("skipping due to lack of python gdb support: %s", out)
	}
}

const helloSource = `
package main
import "fmt"
func main() {
	mapvar := make(map[string]string,5)
	mapvar["abc"] = "def"
	mapvar["ghi"] = "jkl"
	strvar := "abc"
	ptrvar := &strvar
	fmt.Println("hi") // line 10
	_ = ptrvar
}
`

func TestGdbPython(t *testing.T) {
	checkGdbEnvironment(t)
	checkGdbVersion(t)
	checkGdbPython(t)

	dir, err := ioutil.TempDir("", "go-build")
	if err != nil {
		t.Fatalf("failed to create temp directory: %v", err)
	}
	defer os.RemoveAll(dir)

	src := filepath.Join(dir, "main.go")
	err = ioutil.WriteFile(src, []byte(helloSource), 0644)
	if err != nil {
		t.Fatalf("failed to create file: %v", err)
	}

	cmd := exec.Command("go", "build", "-o", "a.exe")
	cmd.Dir = dir
	out, err := testEnv(cmd).CombinedOutput()
	if err != nil {
		t.Fatalf("building source %v\n%s", err, out)
	}

	args := []string{"-nx", "-q", "--batch", "-iex",
		fmt.Sprintf("add-auto-load-safe-path %s/src/runtime", runtime.GOROOT()),
		"-ex", "set startup-with-shell off",
		"-ex", "info auto-load python-scripts",
		"-ex", "set python print-stack full",
		"-ex", "br main.go:10",
		"-ex", "run",
		"-ex", "echo BEGIN info goroutines\n",
		"-ex", "info goroutines",
		"-ex", "echo END\n",
		"-ex", "echo BEGIN print mapvar\n",
		"-ex", "print mapvar",
		"-ex", "echo END\n",
		"-ex", "echo BEGIN print strvar\n",
		"-ex", "print strvar",
		"-ex", "echo END\n"}

	// without framepointer, gdb cannot backtrace our non-standard
	// stack frames on RISC architectures.
	canBackTrace := false
	switch runtime.GOARCH {
	case "amd64", "386", "ppc64", "ppc64le", "arm", "arm64", "mips64", "mips64le", "s390x":
		canBackTrace = true
		args = append(args,
			"-ex", "echo BEGIN goroutine 2 bt\n",
			"-ex", "goroutine 2 bt",
			"-ex", "echo END\n")
	}

	args = append(args, filepath.Join(dir, "a.exe"))
	got, _ := exec.Command("gdb", args...).CombinedOutput()

	firstLine := bytes.SplitN(got, []byte("\n"), 2)[0]
	if string(firstLine) != "Loading Go Runtime support." {
		// This can happen when using all.bash with
		// GOROOT_FINAL set, because the tests are run before
		// the final installation of the files.
		cmd := exec.Command("go", "env", "GOROOT")
		cmd.Env = []string{}
		out, err := cmd.CombinedOutput()
		if err != nil && bytes.Contains(out, []byte("cannot find GOROOT")) {
			t.Skipf("skipping because GOROOT=%s does not exist", runtime.GOROOT())
		}

		_, file, _, _ := runtime.Caller(1)

		t.Logf("package testing source file: %s", file)
		t.Fatalf("failed to load Go runtime support: %s\n%s", firstLine, got)
	}

	// Extract named BEGIN...END blocks from output
	partRe := regexp.MustCompile(`(?ms)^BEGIN ([^\n]*)\n(.*?)\nEND`)
	blocks := map[string]string{}
	for _, subs := range partRe.FindAllSubmatch(got, -1) {
		blocks[string(subs[1])] = string(subs[2])
	}

	infoGoroutinesRe := regexp.MustCompile(`\*\s+\d+\s+running\s+`)
	if bl := blocks["info goroutines"]; !infoGoroutinesRe.MatchString(bl) {
		t.Fatalf("info goroutines failed: %s", bl)
	}

	printMapvarRe := regexp.MustCompile(`\Q = map[string]string = {["abc"] = "def", ["ghi"] = "jkl"}\E$`)
	if bl := blocks["print mapvar"]; !printMapvarRe.MatchString(bl) {
		t.Fatalf("print mapvar failed: %s", bl)
	}

	strVarRe := regexp.MustCompile(`\Q = "abc"\E$`)
	if bl := blocks["print strvar"]; !strVarRe.MatchString(bl) {
		t.Fatalf("print strvar failed: %s", bl)
	}

	btGoroutineRe := regexp.MustCompile(`^#0\s+runtime.+at`)
	if bl := blocks["goroutine 2 bt"]; canBackTrace && !btGoroutineRe.MatchString(bl) {
		t.Fatalf("goroutine 2 bt failed: %s", bl)
	} else if !canBackTrace {
		t.Logf("gdb cannot backtrace for GOARCH=%s, skipped goroutine backtrace test", runtime.GOARCH)
	}
}

const backtraceSource = `
package main

//go:noinline
func aaa() bool { return bbb() }

//go:noinline
func bbb() bool { return ccc() }

//go:noinline
func ccc() bool { return ddd() }

//go:noinline
func ddd() bool { return f() }

//go:noinline
func eee() bool { return true }

var f = eee

func main() {
	_ = aaa()
}
`

// TestGdbBacktrace tests that gdb can unwind the stack correctly
// using only the DWARF debug info.
func TestGdbBacktrace(t *testing.T) {
	checkGdbEnvironment(t)
	checkGdbVersion(t)

	if runtime.GOOS == "netbsd" {
		testenv.SkipFlaky(t, 15603)
	}

	dir, err := ioutil.TempDir("", "go-build")
	if err != nil {
		t.Fatalf("failed to create temp directory: %v", err)
	}
	defer os.RemoveAll(dir)

	// Build the source code.
	src := filepath.Join(dir, "main.go")
	err = ioutil.WriteFile(src, []byte(backtraceSource), 0644)
	if err != nil {
		t.Fatalf("failed to create file: %v", err)
	}
	cmd := exec.Command("go", "build", "-o", "a.exe")
	cmd.Dir = dir
	out, err := testEnv(cmd).CombinedOutput()
	if err != nil {
		t.Fatalf("building source %v\n%s", err, out)
	}

	// Execute gdb commands.
	args := []string{"-nx", "-batch",
		"-ex", "set startup-with-shell off",
		"-ex", "break main.eee",
		"-ex", "run",
		"-ex", "backtrace",
		"-ex", "continue",
		filepath.Join(dir, "a.exe"),
	}
	got, _ := exec.Command("gdb", args...).CombinedOutput()

	// Check that the backtrace matches the source code.
	bt := []string{
		"eee",
		"ddd",
		"ccc",
		"bbb",
		"aaa",
		"main",
	}
	for i, name := range bt {
		s := fmt.Sprintf("#%v.*main\\.%v", i, name)
		re := regexp.MustCompile(s)
		if found := re.Find(got) != nil; !found {
			t.Errorf("could not find '%v' in backtrace", s)
			t.Fatalf("gdb output:\n%v", string(got))
		}
	}
}
<|endoftext|>


<commit_before>package basictracer

import (
	"bytes"
	"encoding/binary"
	"io"
	"strconv"
	"strings"
	"time"

	opentracing "github.com/opentracing/opentracing-go"
)

type textMapPropagator struct {
	tracer *tracerImpl
}
type binaryPropagator struct {
	tracer *tracerImpl
}
type goHTTPPropagator struct {
	*textMapPropagator
}

const (
	prefixTracerState = "ot-tracer-"
	prefixBaggage     = "ot-baggage-"

	tracerStateFieldCount = 3
	fieldNameTraceID      = prefixTracerState + "traceid"
	fieldNameSpanID       = prefixTracerState + "spanid"
	fieldNameSampled      = prefixTracerState + "sampled"
)

func (p *textMapPropagator) Inject(
	sp opentracing.Span,
	opaqueCarrier interface{},
) error {
	sc, ok := sp.(*spanImpl)
	if !ok {
		return opentracing.ErrInvalidSpan
	}
	carrier, ok := opaqueCarrier.(opentracing.TextMapWriter)
	if !ok {
		return opentracing.ErrInvalidCarrier
	}
	carrier.Set(fieldNameTraceID, strconv.FormatInt(sc.raw.TraceID, 16))
	carrier.Set(fieldNameSpanID, strconv.FormatInt(sc.raw.SpanID, 16))
	carrier.Set(fieldNameSampled, strconv.FormatBool(sc.raw.Sampled))

	sc.Lock()
	for k, v := range sc.raw.Baggage {
		carrier.Set(prefixBaggage+k, v)
	}
	sc.Unlock()
	return nil
}

func (p *textMapPropagator) Join(
	operationName string,
	opaqueCarrier interface{},
) (opentracing.Span, error) {
	carrier, ok := opaqueCarrier.(opentracing.TextMapReader)
	if !ok {
		return nil, opentracing.ErrInvalidCarrier
	}
	requiredFieldCount := 0
	var traceID, propagatedSpanID int64
	var sampled bool
	var err error
	decodedBaggage := make(map[string]string)
	err = carrier.ForeachKey(func(k, v string) error {
		switch strings.ToLower(k) {
		case fieldNameTraceID:
			traceID, err = strconv.ParseInt(v, 16, 64)
			if err != nil {
				return opentracing.ErrTraceCorrupted
			}
		case fieldNameSpanID:
			propagatedSpanID, err = strconv.ParseInt(v, 16, 64)
			if err != nil {
				return opentracing.ErrTraceCorrupted
			}
		case fieldNameSampled:
			sampled, err = strconv.ParseBool(v)
			if err != nil {
				return opentracing.ErrTraceCorrupted
			}
		default:
			lowercaseK := strings.ToLower(k)
			if strings.HasPrefix(lowercaseK, prefixBaggage) {
				decodedBaggage[strings.TrimPrefix(lowercaseK, prefixBaggage)] = v
			}
			// Balance off the requiredFieldCount++ just below...
			requiredFieldCount--
		}
		requiredFieldCount++
		return nil
	})
	if err != nil {
		return nil, err
	}
	if requiredFieldCount < tracerStateFieldCount {
		if requiredFieldCount == 0 {
			return nil, opentracing.ErrTraceNotFound
		}
		return nil, opentracing.ErrTraceCorrupted
	}

	sp := p.tracer.getSpan()
	sp.raw = RawSpan{
		Context: Context{
			TraceID:      traceID,
			SpanID:       randomID(),
			ParentSpanID: propagatedSpanID,
			Sampled:      sampled,
		},
		Baggage: decodedBaggage,
	}

	return p.tracer.startSpanInternal(
		sp,
		operationName,
		time.Now(),
		nil,
	), nil
}

func (p *binaryPropagator) Inject(
	sp opentracing.Span,
	opaqueCarrier interface{},
) error {
	sc, ok := sp.(*spanImpl)
	if !ok {
		return opentracing.ErrInvalidSpan
	}
	carrier, ok := opaqueCarrier.(io.Writer)
	if !ok {
		return opentracing.ErrInvalidCarrier
	}
	var err error
	var sampledByte byte
	if sc.raw.Sampled {
		sampledByte = 1
	}

	// Handle the trace and span ids, and sampled status.
	err = binary.Write(carrier, binary.BigEndian, sc.raw.TraceID)
	if err != nil {
		return err
	}

	err = binary.Write(carrier, binary.BigEndian, sc.raw.SpanID)
	if err != nil {
		return err
	}

	err = binary.Write(carrier, binary.BigEndian, sampledByte)
	if err != nil {
		return err
	}

	// Handle the baggage.
	err = binary.Write(carrier, binary.BigEndian, int32(len(sc.raw.Baggage)))
	if err != nil {
		return err
	}
	for key, val := range sc.raw.Baggage {
		if err = binary.Write(carrier, binary.BigEndian, int32(len(key))); err != nil {
			return err
		}
		if _, err = io.WriteString(carrier, key); err != nil {
			return err
		}

		if err = binary.Write(carrier, binary.BigEndian, int32(len(val))); err != nil {
			return err
		}
		if _, err = io.WriteString(carrier, val); err != nil {
			return err
		}
	}

	return nil
}

func (p *binaryPropagator) Join(
	operationName string,
	opaqueCarrier interface{},
) (opentracing.Span, error) {
	carrier, ok := opaqueCarrier.(io.Reader)
	if !ok {
		return nil, opentracing.ErrInvalidCarrier
	}
	// Handle the trace, span ids, and sampled status.
	var traceID, propagatedSpanID int64
	var sampledByte byte

	if err := binary.Read(carrier, binary.BigEndian, &traceID); err != nil {
		if err == io.EOF {
			return nil, opentracing.ErrTraceNotFound
		}
		return nil, opentracing.ErrTraceCorrupted
	}
	if err := binary.Read(carrier, binary.BigEndian, &propagatedSpanID); err != nil {
		return nil, opentracing.ErrTraceCorrupted
	}
	if err := binary.Read(carrier, binary.BigEndian, &sampledByte); err != nil {
		return nil, opentracing.ErrTraceCorrupted
	}

	// Handle the baggage.
	var numBaggage int32
	if err := binary.Read(carrier, binary.BigEndian, &numBaggage); err != nil {
		return nil, opentracing.ErrTraceCorrupted
	}
	iNumBaggage := int(numBaggage)
	var baggageMap map[string]string
	if iNumBaggage > 0 {
		var buf bytes.Buffer // TODO(tschottdorf): candidate for sync.Pool
		baggageMap = make(map[string]string, iNumBaggage)
		var keyLen, valLen int32
		for i := 0; i < iNumBaggage; i++ {
			if err := binary.Read(carrier, binary.BigEndian, &keyLen); err != nil {
				return nil, opentracing.ErrTraceCorrupted
			}
			buf.Grow(int(keyLen))
			if n, err := io.CopyN(&buf, carrier, int64(keyLen)); err != nil || int32(n) != keyLen {
				return nil, opentracing.ErrTraceCorrupted
			}
			key := buf.String()
			buf.Reset()

			if err := binary.Read(carrier, binary.BigEndian, &valLen); err != nil {
				return nil, opentracing.ErrTraceCorrupted
			}
			if n, err := io.CopyN(&buf, carrier, int64(valLen)); err != nil || int32(n) != valLen {
				return nil, opentracing.ErrTraceCorrupted
			}
			baggageMap[key] = buf.String()
			buf.Reset()
		}
	}

	sp := p.tracer.getSpan()
	sp.raw = RawSpan{
		Context: Context{
			TraceID:      traceID,
			SpanID:       randomID(),
			ParentSpanID: propagatedSpanID,
			Sampled:      sampledByte != 0,
		},
	}
	sp.raw.Baggage = baggageMap

	return p.tracer.startSpanInternal(
		sp,
		operationName,
		time.Now(),
		nil,
	), nil
}
<commit_msg>Delete no longer used goHTTPPropagator<commit_after>package basictracer

import (
	"bytes"
	"encoding/binary"
	"io"
	"strconv"
	"strings"
	"time"

	opentracing "github.com/opentracing/opentracing-go"
)

type textMapPropagator struct {
	tracer *tracerImpl
}
type binaryPropagator struct {
	tracer *tracerImpl
}

const (
	prefixTracerState = "ot-tracer-"
	prefixBaggage     = "ot-baggage-"

	tracerStateFieldCount = 3
	fieldNameTraceID      = prefixTracerState + "traceid"
	fieldNameSpanID       = prefixTracerState + "spanid"
	fieldNameSampled      = prefixTracerState + "sampled"
)

func (p *textMapPropagator) Inject(
	sp opentracing.Span,
	opaqueCarrier interface{},
) error {
	sc, ok := sp.(*spanImpl)
	if !ok {
		return opentracing.ErrInvalidSpan
	}
	carrier, ok := opaqueCarrier.(opentracing.TextMapWriter)
	if !ok {
		return opentracing.ErrInvalidCarrier
	}
	carrier.Set(fieldNameTraceID, strconv.FormatInt(sc.raw.TraceID, 16))
	carrier.Set(fieldNameSpanID, strconv.FormatInt(sc.raw.SpanID, 16))
	carrier.Set(fieldNameSampled, strconv.FormatBool(sc.raw.Sampled))

	sc.Lock()
	for k, v := range sc.raw.Baggage {
		carrier.Set(prefixBaggage+k, v)
	}
	sc.Unlock()
	return nil
}

func (p *textMapPropagator) Join(
	operationName string,
	opaqueCarrier interface{},
) (opentracing.Span, error) {
	carrier, ok := opaqueCarrier.(opentracing.TextMapReader)
	if !ok {
		return nil, opentracing.ErrInvalidCarrier
	}
	requiredFieldCount := 0
	var traceID, propagatedSpanID int64
	var sampled bool
	var err error
	decodedBaggage := make(map[string]string)
	err = carrier.ForeachKey(func(k, v string) error {
		switch strings.ToLower(k) {
		case fieldNameTraceID:
			traceID, err = strconv.ParseInt(v, 16, 64)
			if err != nil {
				return opentracing.ErrTraceCorrupted
			}
		case fieldNameSpanID:
			propagatedSpanID, err = strconv.ParseInt(v, 16, 64)
			if err != nil {
				return opentracing.ErrTraceCorrupted
			}
		case fieldNameSampled:
			sampled, err = strconv.ParseBool(v)
			if err != nil {
				return opentracing.ErrTraceCorrupted
			}
		default:
			lowercaseK := strings.ToLower(k)
			if strings.HasPrefix(lowercaseK, prefixBaggage) {
				decodedBaggage[strings.TrimPrefix(lowercaseK, prefixBaggage)] = v
			}
			// Balance off the requiredFieldCount++ just below...
			requiredFieldCount--
		}
		requiredFieldCount++
		return nil
	})
	if err != nil {
		return nil, err
	}
	if requiredFieldCount < tracerStateFieldCount {
		if requiredFieldCount == 0 {
			return nil, opentracing.ErrTraceNotFound
		}
		return nil, opentracing.ErrTraceCorrupted
	}

	sp := p.tracer.getSpan()
	sp.raw = RawSpan{
		Context: Context{
			TraceID:      traceID,
			SpanID:       randomID(),
			ParentSpanID: propagatedSpanID,
			Sampled:      sampled,
		},
		Baggage: decodedBaggage,
	}

	return p.tracer.startSpanInternal(
		sp,
		operationName,
		time.Now(),
		nil,
	), nil
}

func (p *binaryPropagator) Inject(
	sp opentracing.Span,
	opaqueCarrier interface{},
) error {
	sc, ok := sp.(*spanImpl)
	if !ok {
		return opentracing.ErrInvalidSpan
	}
	carrier, ok := opaqueCarrier.(io.Writer)
	if !ok {
		return opentracing.ErrInvalidCarrier
	}
	var err error
	var sampledByte byte
	if sc.raw.Sampled {
		sampledByte = 1
	}

	// Handle the trace and span ids, and sampled status.
	err = binary.Write(carrier, binary.BigEndian, sc.raw.TraceID)
	if err != nil {
		return err
	}

	err = binary.Write(carrier, binary.BigEndian, sc.raw.SpanID)
	if err != nil {
		return err
	}

	err = binary.Write(carrier, binary.BigEndian, sampledByte)
	if err != nil {
		return err
	}

	// Handle the baggage.
	err = binary.Write(carrier, binary.BigEndian, int32(len(sc.raw.Baggage)))
	if err != nil {
		return err
	}
	for key, val := range sc.raw.Baggage {
		if err = binary.Write(carrier, binary.BigEndian, int32(len(key))); err != nil {
			return err
		}
		if _, err = io.WriteString(carrier, key); err != nil {
			return err
		}

		if err = binary.Write(carrier, binary.BigEndian, int32(len(val))); err != nil {
			return err
		}
		if _, err = io.WriteString(carrier, val); err != nil {
			return err
		}
	}

	return nil
}

func (p *binaryPropagator) Join(
	operationName string,
	opaqueCarrier interface{},
) (opentracing.Span, error) {
	carrier, ok := opaqueCarrier.(io.Reader)
	if !ok {
		return nil, opentracing.ErrInvalidCarrier
	}
	// Handle the trace, span ids, and sampled status.
	var traceID, propagatedSpanID int64
	var sampledByte byte

	if err := binary.Read(carrier, binary.BigEndian, &traceID); err != nil {
		if err == io.EOF {
			return nil, opentracing.ErrTraceNotFound
		}
		return nil, opentracing.ErrTraceCorrupted
	}
	if err := binary.Read(carrier, binary.BigEndian, &propagatedSpanID); err != nil {
		return nil, opentracing.ErrTraceCorrupted
	}
	if err := binary.Read(carrier, binary.BigEndian, &sampledByte); err != nil {
		return nil, opentracing.ErrTraceCorrupted
	}

	// Handle the baggage.
	var numBaggage int32
	if err := binary.Read(carrier, binary.BigEndian, &numBaggage); err != nil {
		return nil, opentracing.ErrTraceCorrupted
	}
	iNumBaggage := int(numBaggage)
	var baggageMap map[string]string
	if iNumBaggage > 0 {
		var buf bytes.Buffer // TODO(tschottdorf): candidate for sync.Pool
		baggageMap = make(map[string]string, iNumBaggage)
		var keyLen, valLen int32
		for i := 0; i < iNumBaggage; i++ {
			if err := binary.Read(carrier, binary.BigEndian, &keyLen); err != nil {
				return nil, opentracing.ErrTraceCorrupted
			}
			buf.Grow(int(keyLen))
			if n, err := io.CopyN(&buf, carrier, int64(keyLen)); err != nil || int32(n) != keyLen {
				return nil, opentracing.ErrTraceCorrupted
			}
			key := buf.String()
			buf.Reset()

			if err := binary.Read(carrier, binary.BigEndian, &valLen); err != nil {
				return nil, opentracing.ErrTraceCorrupted
			}
			if n, err := io.CopyN(&buf, carrier, int64(valLen)); err != nil || int32(n) != valLen {
				return nil, opentracing.ErrTraceCorrupted
			}
			baggageMap[key] = buf.String()
			buf.Reset()
		}
	}

	sp := p.tracer.getSpan()
	sp.raw = RawSpan{
		Context: Context{
			TraceID:      traceID,
			SpanID:       randomID(),
			ParentSpanID: propagatedSpanID,
			Sampled:      sampledByte != 0,
		},
	}
	sp.raw.Baggage = baggageMap

	return p.tracer.startSpanInternal(
		sp,
		operationName,
		time.Now(),
		nil,
	), nil
}
<|endoftext|>


<commit_before>package php

import (
	"stephensearles.com/php/ast"
	"stephensearles.com/php/token"
)

var operatorPrecedence = map[token.Token]int{
	token.ArrayLookupOperatorLeft: 19,
	token.UnaryOperator:           18,
	token.BitwiseNotOperator:      18,
	token.CastOperator:            18,
	token.InstanceofOperator:      17,
	token.NegationOperator:        16,
	token.MultOperator:            15,
	token.AdditionOperator:        14,
	token.SubtractionOperator:     14,
	token.ConcatenationOperator:   14,

	token.BitwiseShiftOperator: 13,
	token.ComparisonOperator:   12,
	token.EqualityOperator:     11,

	token.AmpersandOperator:  10,
	token.BitwiseXorOperator: 9,
	token.BitwiseOrOperator:  8,
	token.AndOperator:        7,
	token.OrOperator:         6,
	token.TernaryOperator1:   5,
	token.TernaryOperator2:   5,

	/*
	   PHP's documentation would have this operator be at 4, but it also notes:

	       Although = has a lower precedence than most other operators, PHP will
	       still allow expressions similar to the following: if (!$a = foo()), in
	       which case the return value of foo() is put into $a.

	   Thus, we put it at 17, pending further testing.
	*/
	token.AssignmentOperator: 17,
	token.WrittenAndOperator: 3,
	token.WrittenXorOperator: 2,
	token.WrittenOrOperator:  1,
}

func (p *Parser) parseExpression() (expr ast.Expression) {
	originalParenLev := p.parenLevel

	switch p.current.typ {
	case token.IgnoreErrorOperator:
		expr = p.parseIgnoreError()
	case token.List:
		expr = p.parseList()
	case
		token.UnaryOperator,
		token.NegationOperator,
		token.AmpersandOperator,
		token.CastOperator,
		token.SubtractionOperator,
		token.BitwiseNotOperator:
		op := p.current
		expr = p.parseUnaryExpressionRight(p.parseNextExpression(), op)
	case
		token.Function,
		token.NewOperator,
		token.VariableOperator,
		token.Array,
		token.Identifier,
		token.StringLiteral,
		token.NumberLiteral,
		token.BooleanLiteral,
		token.Null,
		token.Self,
		token.Static,
		token.Parent,
		token.ShellCommand:
		expr = p.parseOperation(originalParenLev, p.parseOperand())
	case token.Include:
		expr = p.parseInclude()
	case token.OpenParen:
		p.parenLevel += 1
		p.next()
		expr = p.parseExpression()
		p.expect(token.CloseParen)
		p.parenLevel -= 1
		expr = p.parseOperation(originalParenLev, expr)
	default:
		p.errorf("Expected expression. Found %s", p.current)
	}
	if p.parenLevel != originalParenLev {
		p.errorf("unbalanced parens: %d prev: %d", p.parenLevel, originalParenLev)
		return
	}
	return
}

func (p *Parser) parseOperation(originalParenLevel int, lhs ast.Expression) (expr ast.Expression) {
	p.next()
	switch operationTypeForToken(p.current.typ) {
	case ignoreErrorOperation:
		return p.parseOperation(originalParenLevel, lhs)
	case unaryOperation:
		expr = p.parseUnaryExpressionLeft(lhs, p.current)
	case assignmentOperation, binaryOperation:
		expr = p.parseBinaryOperation(lhs, p.current, originalParenLevel)
	case ternaryOperation:
		expr = p.parseTernaryOperation(lhs)
	case subexpressionEndOperation:
		if p.parenLevel == originalParenLevel {
			p.backup()
			return lhs
		}
		p.parenLevel -= 1
		expr = lhs
	case subexpressionBeginOperation:
		// Check if we have a paren directly after a literal
		if _, ok := lhs.(*ast.Literal); ok {
			// If we do, we might be in a particular case of a function call with NULL as the function name. Let callers handle this
			p.backup()
			return lhs
		}
		p.parenLevel += 1
		expr = p.parseNextExpression()
	default:
		p.backup()
		return lhs
	}

	return p.parseOperation(originalParenLevel, expr)
}

func (p *Parser) parseAssignmentOperation(lhs, rhs ast.Expression, operator Item) (expr ast.Expression) {
	assignee, ok := lhs.(ast.Assignable)
	if !ok {
		p.errorf("%s is not assignable", lhs)
	}
	expr = ast.AssignmentExpression{
		Assignee: assignee,
		Operator: operator.val,
		Value:    rhs,
	}
	return expr
}

// parseOperand takes the current token and returns it as the simplest
// expression for that token. That means an expression with no operators
// except for the object operator.
func (p *Parser) parseOperand() (expr ast.Expression) {

	// These cases must come first and not repeat
	switch p.current.typ {
	case
		token.UnaryOperator,
		token.NegationOperator,
		token.CastOperator,
		token.SubtractionOperator,
		token.AmpersandOperator,
		token.BitwiseNotOperator:
		op := p.current
		p.next()
		return p.parseUnaryExpressionRight(p.parseOperand(), op)
	case token.Function:
		return p.parseAnonymousFunction()
	case token.NewOperator:
		return p.parseInstantiation()
	}

	switch p.current.typ {
	case token.ShellCommand:
		return &ast.ShellCommand{Command: p.current.val}
	case
		token.StringLiteral,
		token.BooleanLiteral,
		token.NumberLiteral,
		token.Null:
		return p.parseLiteral()
	case token.UnaryOperator:
		expr = newUnaryOperation(p.current, expr)
		p.next()
		return

	case token.Array:
		expr = p.parseArrayDeclaration()
		p.next()
	case token.VariableOperator:
		expr = p.parseVariableOperand()
	case token.ObjectOperator:
		expr = p.parseObjectLookup(expr)
		p.next()
	case token.ArrayLookupOperatorLeft:
		expr = p.parseArrayLookup(expr)
		p.next()
	case token.Identifier:
		expr = p.parseIdentifier()
	case token.Self, token.Static, token.Parent:
		expr = p.parseScopeResolutionFromKeyword()
	default:
		p.backup()
		return
	}

	return p.parseOperandComponent(expr)
}

func (p *Parser) parseOperandComponent(lhs ast.Expression) (expr ast.Expression) {
	expr = lhs
	for {
		switch p.current.typ {
		case token.UnaryOperator:
			expr = newUnaryOperation(p.current, expr)
			return
		case token.ObjectOperator:
			expr = p.parseObjectLookup(expr)
			p.next()
		case token.ArrayLookupOperatorLeft:
			expr = p.parseArrayLookup(expr)
			p.next()
		case token.OpenParen:
			p.backup()
			if p.instantiation {
				return
			}
			expr = p.parseFunctionCall(expr)
			p.next()
		default:
			p.backup()
			return
		}
	}
	return
}

func (p *Parser) parseLiteral() ast.Expression {
	switch p.current.typ {
	case token.StringLiteral:
		return &ast.Literal{Type: ast.String, Value: p.current.val}
	case token.BooleanLiteral:
		return &ast.Literal{Type: ast.Boolean, Value: p.current.val}
	case token.NumberLiteral:
		return &ast.Literal{Type: ast.Float, Value: p.current.val}
	case token.Null:
		if p.peek().typ == token.OpenParen {
			expr := p.parseIdentifier()
			p.backup()
			return expr
		}
		return &ast.Literal{Type: ast.Null, Value: p.current.val}
	}
	p.errorf("Unknown literal type")
	return nil
}

func (p *Parser) parseVariable() ast.Expression {
	p.expectCurrent(token.VariableOperator)
	switch p.next(); {
	case isKeyword(p.current.typ, p.current.val):
		// keywords are all valid variable names
		fallthrough
	case p.current.typ == token.Identifier:
		expr := ast.NewVariable(p.current.val)
		return expr
	case p.current.typ == token.BlockBegin:
		return ast.Variable{Name: p.parseExpression()}
	case p.current.typ == token.VariableOperator:
		return ast.Variable{Name: p.parseVariable()}
	default:
		p.errorf("unexpected variable operand %s", p.current)
		return nil
	}
}

func (p *Parser) parseInclude() ast.Expression {
	inc := ast.Include{Expressions: make([]ast.Expression, 0)}
	for {
		inc.Expressions = append(inc.Expressions, p.parseNextExpression())
		if p.peek().typ != token.Comma {
			break
		}
		p.expect(token.Comma)
	}
	return inc
}

func (p *Parser) parseIgnoreError() ast.Expression {
	p.next()
	return p.parseExpression()
}

func (p *Parser) parseNew(originalParenLev int) ast.Expression {
	expr := p.parseInstantiation()
	expr = p.parseOperation(originalParenLev, expr)
	return expr
}

func (p *Parser) parseIdentifier() (expr ast.Expression) {
	switch p.peek().typ {
	case token.OpenParen:
		// Function calls are okay here because we know they came with
		// a non-dynamic identifier.
		expr = p.parseFunctionCall(ast.Identifier{Value: p.current.val})
		p.next()
	case token.ScopeResolutionOperator:
		classIdent := p.current.val
		p.next() // get onto ::, then we get to the next expr
		p.next()
		expr = ast.NewClassExpression(classIdent, p.parseOperand())
		p.next()
	default:
		expr = ast.ConstantExpression{
			Variable: ast.NewVariable(p.current.val),
		}
		p.next()
	}
	return expr
}

// parseScopeResolutionFromKeyword specifically parses self::, static::, and parent::
func (p *Parser) parseScopeResolutionFromKeyword() ast.Expression {
	if p.peek().typ == token.ScopeResolutionOperator {
		r := p.current.val
		p.expect(token.ScopeResolutionOperator)
		p.next()
		expr := ast.NewClassExpression(r, p.parseOperand())
		p.next()
		return expr
	}
	// TODO Error
	p.next()
	return nil
}

func (p *Parser) parseVariableOperand() ast.Expression {
	expr := p.parseVariable()
	p.next()
	// Array lookup with curly braces is a special case that is only supported by PHP in
	// simple contexts.
	switch p.current.typ {
	case token.BlockBegin:
		expr = p.parseArrayLookup(expr)
		p.next()
	case token.ScopeResolutionOperator:
		expr = &ast.ClassExpression{Receiver: expr, Expression: p.parseNextExpression()}
		p.next()
	case token.OpenParen:
		p.backup()
		expr = p.parseFunctionCall(expr)
		p.next()
	}

	return expr
}
<commit_msg>Fixing ignore error parsing<commit_after>package php

import (
	"stephensearles.com/php/ast"
	"stephensearles.com/php/token"
)

var operatorPrecedence = map[token.Token]int{
	token.ArrayLookupOperatorLeft: 19,
	token.UnaryOperator:           18,
	token.BitwiseNotOperator:      18,
	token.CastOperator:            18,
	token.InstanceofOperator:      17,
	token.NegationOperator:        16,
	token.MultOperator:            15,
	token.AdditionOperator:        14,
	token.SubtractionOperator:     14,
	token.ConcatenationOperator:   14,

	token.BitwiseShiftOperator: 13,
	token.ComparisonOperator:   12,
	token.EqualityOperator:     11,

	token.AmpersandOperator:  10,
	token.BitwiseXorOperator: 9,
	token.BitwiseOrOperator:  8,
	token.AndOperator:        7,
	token.OrOperator:         6,
	token.TernaryOperator1:   5,
	token.TernaryOperator2:   5,

	/*
	   PHP's documentation would have this operator be at 4, but it also notes:

	       Although = has a lower precedence than most other operators, PHP will
	       still allow expressions similar to the following: if (!$a = foo()), in
	       which case the return value of foo() is put into $a.

	   Thus, we put it at 17, pending further testing.
	*/
	token.AssignmentOperator: 17,
	token.WrittenAndOperator: 3,
	token.WrittenXorOperator: 2,
	token.WrittenOrOperator:  1,
}

func (p *Parser) parseExpression() (expr ast.Expression) {
	originalParenLev := p.parenLevel

	switch p.current.typ {
	case token.IgnoreErrorOperator:
		return p.parseNextExpression()
	case token.List:
		expr = p.parseList()
	case
		token.UnaryOperator,
		token.NegationOperator,
		token.AmpersandOperator,
		token.CastOperator,
		token.SubtractionOperator,
		token.BitwiseNotOperator:
		op := p.current
		expr = p.parseUnaryExpressionRight(p.parseNextExpression(), op)
	case
		token.Function,
		token.NewOperator,
		token.VariableOperator,
		token.Array,
		token.Identifier,
		token.StringLiteral,
		token.NumberLiteral,
		token.BooleanLiteral,
		token.Null,
		token.Self,
		token.Static,
		token.Parent,
		token.ShellCommand:
		expr = p.parseOperation(originalParenLev, p.parseOperand())
	case token.Include:
		expr = p.parseInclude()
	case token.OpenParen:
		p.parenLevel += 1
		p.next()
		expr = p.parseExpression()
		p.expect(token.CloseParen)
		p.parenLevel -= 1
		expr = p.parseOperation(originalParenLev, expr)
	default:
		p.errorf("Expected expression. Found %s", p.current)
	}
	if p.parenLevel != originalParenLev {
		p.errorf("unbalanced parens: %d prev: %d", p.parenLevel, originalParenLev)
		return
	}
	return
}

func (p *Parser) parseOperation(originalParenLevel int, lhs ast.Expression) (expr ast.Expression) {
	p.next()
	switch operationTypeForToken(p.current.typ) {
	case ignoreErrorOperation:
		return p.parseOperation(originalParenLevel, lhs)
	case unaryOperation:
		expr = p.parseUnaryExpressionLeft(lhs, p.current)
	case assignmentOperation, binaryOperation:
		expr = p.parseBinaryOperation(lhs, p.current, originalParenLevel)
	case ternaryOperation:
		expr = p.parseTernaryOperation(lhs)
	case subexpressionEndOperation:
		if p.parenLevel == originalParenLevel {
			p.backup()
			return lhs
		}
		p.parenLevel -= 1
		expr = lhs
	case subexpressionBeginOperation:
		// Check if we have a paren directly after a literal
		if _, ok := lhs.(*ast.Literal); ok {
			// If we do, we might be in a particular case of a function call with NULL as the function name. Let callers handle this
			p.backup()
			return lhs
		}
		p.parenLevel += 1
		expr = p.parseNextExpression()
	default:
		p.backup()
		return lhs
	}

	return p.parseOperation(originalParenLevel, expr)
}

func (p *Parser) parseAssignmentOperation(lhs, rhs ast.Expression, operator Item) (expr ast.Expression) {
	assignee, ok := lhs.(ast.Assignable)
	if !ok {
		p.errorf("%s is not assignable", lhs)
	}
	expr = ast.AssignmentExpression{
		Assignee: assignee,
		Operator: operator.val,
		Value:    rhs,
	}
	return expr
}

// parseOperand takes the current token and returns it as the simplest
// expression for that token. That means an expression with no operators
// except for the object operator.
func (p *Parser) parseOperand() (expr ast.Expression) {

	// These cases must come first and not repeat
	switch p.current.typ {
	case token.IgnoreErrorOperator:
		p.next()
		return p.parseOperand()
	case
		token.UnaryOperator,
		token.NegationOperator,
		token.CastOperator,
		token.SubtractionOperator,
		token.AmpersandOperator,
		token.BitwiseNotOperator:
		op := p.current
		p.next()
		return p.parseUnaryExpressionRight(p.parseOperand(), op)
	case token.Function:
		return p.parseAnonymousFunction()
	case token.NewOperator:
		return p.parseInstantiation()
	}

	switch p.current.typ {
	case token.ShellCommand:
		return &ast.ShellCommand{Command: p.current.val}
	case
		token.StringLiteral,
		token.BooleanLiteral,
		token.NumberLiteral,
		token.Null:
		return p.parseLiteral()
	case token.UnaryOperator:
		expr = newUnaryOperation(p.current, expr)
		p.next()
		return

	case token.Array:
		expr = p.parseArrayDeclaration()
		p.next()
	case token.VariableOperator:
		expr = p.parseVariableOperand()
	case token.ObjectOperator:
		expr = p.parseObjectLookup(expr)
		p.next()
	case token.ArrayLookupOperatorLeft:
		expr = p.parseArrayLookup(expr)
		p.next()
	case token.Identifier:
		expr = p.parseIdentifier()
	case token.Self, token.Static, token.Parent:
		expr = p.parseScopeResolutionFromKeyword()
	default:
		p.backup()
		return
	}

	return p.parseOperandComponent(expr)
}

func (p *Parser) parseOperandComponent(lhs ast.Expression) (expr ast.Expression) {
	expr = lhs
	for {
		switch p.current.typ {
		case token.UnaryOperator:
			expr = newUnaryOperation(p.current, expr)
			return
		case token.ObjectOperator:
			expr = p.parseObjectLookup(expr)
			p.next()
		case token.ArrayLookupOperatorLeft:
			expr = p.parseArrayLookup(expr)
			p.next()
		case token.OpenParen:
			p.backup()
			if p.instantiation {
				return
			}
			expr = p.parseFunctionCall(expr)
			p.next()
		default:
			p.backup()
			return
		}
	}
	return
}

func (p *Parser) parseLiteral() ast.Expression {
	switch p.current.typ {
	case token.StringLiteral:
		return &ast.Literal{Type: ast.String, Value: p.current.val}
	case token.BooleanLiteral:
		return &ast.Literal{Type: ast.Boolean, Value: p.current.val}
	case token.NumberLiteral:
		return &ast.Literal{Type: ast.Float, Value: p.current.val}
	case token.Null:
		if p.peek().typ == token.OpenParen {
			expr := p.parseIdentifier()
			p.backup()
			return expr
		}
		return &ast.Literal{Type: ast.Null, Value: p.current.val}
	}
	p.errorf("Unknown literal type")
	return nil
}

func (p *Parser) parseVariable() ast.Expression {
	p.expectCurrent(token.VariableOperator)
	switch p.next(); {
	case isKeyword(p.current.typ, p.current.val):
		// keywords are all valid variable names
		fallthrough
	case p.current.typ == token.Identifier:
		expr := ast.NewVariable(p.current.val)
		return expr
	case p.current.typ == token.BlockBegin:
		return ast.Variable{Name: p.parseExpression()}
	case p.current.typ == token.VariableOperator:
		return ast.Variable{Name: p.parseVariable()}
	default:
		p.errorf("unexpected variable operand %s", p.current)
		return nil
	}
}

func (p *Parser) parseInclude() ast.Expression {
	inc := ast.Include{Expressions: make([]ast.Expression, 0)}
	for {
		inc.Expressions = append(inc.Expressions, p.parseNextExpression())
		if p.peek().typ != token.Comma {
			break
		}
		p.expect(token.Comma)
	}
	return inc
}

func (p *Parser) parseIgnoreError() ast.Expression {
	p.next()
	return p.parseExpression()
}

func (p *Parser) parseNew(originalParenLev int) ast.Expression {
	expr := p.parseInstantiation()
	expr = p.parseOperation(originalParenLev, expr)
	return expr
}

func (p *Parser) parseIdentifier() (expr ast.Expression) {
	switch p.peek().typ {
	case token.OpenParen:
		// Function calls are okay here because we know they came with
		// a non-dynamic identifier.
		expr = p.parseFunctionCall(ast.Identifier{Value: p.current.val})
		p.next()
	case token.ScopeResolutionOperator:
		classIdent := p.current.val
		p.next() // get onto ::, then we get to the next expr
		p.next()
		expr = ast.NewClassExpression(classIdent, p.parseOperand())
		p.next()
	default:
		expr = ast.ConstantExpression{
			Variable: ast.NewVariable(p.current.val),
		}
		p.next()
	}
	return expr
}

// parseScopeResolutionFromKeyword specifically parses self::, static::, and parent::
func (p *Parser) parseScopeResolutionFromKeyword() ast.Expression {
	if p.peek().typ == token.ScopeResolutionOperator {
		r := p.current.val
		p.expect(token.ScopeResolutionOperator)
		p.next()
		expr := ast.NewClassExpression(r, p.parseOperand())
		p.next()
		return expr
	}
	// TODO Error
	p.next()
	return nil
}

func (p *Parser) parseVariableOperand() ast.Expression {
	expr := p.parseVariable()
	p.next()
	// Array lookup with curly braces is a special case that is only supported by PHP in
	// simple contexts.
	switch p.current.typ {
	case token.BlockBegin:
		expr = p.parseArrayLookup(expr)
		p.next()
	case token.ScopeResolutionOperator:
		expr = &ast.ClassExpression{Receiver: expr, Expression: p.parseNextExpression()}
		p.next()
	case token.OpenParen:
		p.backup()
		expr = p.parseFunctionCall(expr)
		p.next()
	}

	return expr
}
<|endoftext|>


<commit_before>package main

import (
	"fmt"
	"github.com/maniksurtani/protodocparser/impl"
	"encoding/json"
	"regexp"
	"bufio"
	"os"
	"strings"
	"gopkg.in/alecthomas/kingpin.v2"
)

// Regexps
var (
	startCommentRE = regexp.MustCompile("^\\s*/\\*")
	endCommentRE = regexp.MustCompile("\\s*\\*/\\s*$")
	rpcRE = regexp.MustCompile("\\s*rpc\\s+")
	serviceRE = regexp.MustCompile("\\s*service\\s+")
	serviceNameRE = regexp.MustCompile("\\s*service\\s+(\\w+)\\s*\\{")
	rpcNameRE = regexp.MustCompile("\\s*rpc\\s+(\\w+)\\s*\\(\\s*([\\w]+)\\s*\\)\\s+returns\\s+\\(\\s*(\\w+)\\s*\\)")
	pkgNameRE = regexp.MustCompile("\\s*package\\s+([\\w.]+)\\s*;")
	apiAnnotationRE = regexp.MustCompile("^\\s*\\*\\s*@API")
)

// Command-line args
var (
	debug = kingpin.Flag("verbose", "Enable verbose mode").Bool()
	// TODO should we allow multiple proto files? Separated by ':' or something?
	protoFile = kingpin.Arg("protofile", "Proto file").Required().String()
	outFile = kingpin.Arg("out", "Output file").Required().String()
)

func main() {
	// TODO: parse command line parameters. Do do so, use the kingpin package. See https://github.com/alecthomas/kingpin#examples
	kingpin.Version("0.1") // Version of protodocparser
	kingpin.Parse() // Parse command-line options

	// Read a proto file from StdIn
	protoContents := readFromStdIn()
	j := parseAsString(protoContents)
	fmt.Println(j)
}

func readFromStdIn() []string {
	s := bufio.NewScanner(os.Stdin)
	txt := make([]string, 0)
	for s.Scan() {
		txt = append(txt, s.Text())
	}
	return txt
}

func addRpcToLastService(services []*impl.Service, commentBlock *impl.CommentBlock, lines []string, currentLine int) {
	rpc := impl.NewRpc()
	rpc.Name, rpc.Request, rpc.Response = rpcName(lines[currentLine])

	// TODO: set Options, Doc and Examples
	// TODO: Doc is the comment block before the first @Example annotation
	// TODO: @Examples exists in the comment block
	// TODO: Options are the protobuf options. This might be harder to figure out since they may be split across multiple lines. :/

	lastService := services[len(services) - 1]
	lastService.Rpcs = append(lastService.Rpcs, rpc)
}

func addServiceToServices(services []*impl.Service, commentBlock *impl.CommentBlock, lines []string, apiAnnotation string, currentLine int) []*impl.Service {
	s := impl.NewService()
	s.Name = serviceName(lines[currentLine])
	if len(apiAnnotation) > 0 {
		s.Api = true
	}

	// TODO: set Api, Design, Doc, Examples, File, Org and Url
	// TODO: to set Api, just check whether the @API annotation exists in the comment block
	// TODO: to set Design and Org, look at the params passed in to @API
	// TODO: if Org isn't set, attempt to "guess" what it might be by looking at the path/package of the proto, and look up in Registry
	// TODO: @Examples exists in the comment block
	// TODO: Get File and Url - TODO, have these passed in as command-line params
	// TODO: Doc is the comment block after @API and before the first @Example annotation

	return append(services, s)
}

func serviceName(line string) string {
	r := serviceNameRE.FindStringSubmatch(line)
	if len(r) > 0 {
		return r[1]
	} else {
		return ""
	}
}

func rpcName(line string) (name, req, rsp string) {
	r := rpcNameRE.FindStringSubmatch(line)
	lenR := len(r)
	if lenR > 0 {
		name = r[1]
	}

	if lenR > 1 {
		req = r[2]
	}

	if lenR > 2 {
		rsp = r[3]
	}

	return
}

func matchPkgName(line string) (string, bool) {
	matches := pkgNameRE.FindStringSubmatch(line)
	if len(matches) > 0 {
		return matches[1], true
	}

	return "", false
}

func parseString(fileAsString string) []*impl.Service {
	return parse(strings.Split(fileAsString, "\n"))
}

func parseAsString(lines []string) string {
	services := parse(lines)
	bytes, err := json.Marshal(services)
	if err != nil {
		panic(fmt.Sprintf("Caught error %v trying to serialize %v into JSON.", err, services))
	}
	return string(bytes)
}

func parse(lines []string) []*impl.Service {
	//fmt.Printf("Got %v lines of txt\n", len(lines))
	// Create an array of services.
	services := make([]*impl.Service, 0)

	var currentBlock *impl.CommentBlock
	pkgName := ""
	matched := false
	apiAnnotation := ""

	for ln, line := range lines {
		if pkgName == "" {
			pkgName, matched = matchPkgName(line)
			if matched {
				continue
			}
		}
		//fmt.Printf("Line %v is [%v]\n", ln, line)
		if startCommentRE.MatchString(line) && currentBlock == nil {
			// Create a new comment block.
			currentBlock = &impl.CommentBlock{}
			currentBlock.Start = ln
			currentBlock.Type = impl.OtherComment
		} else if endCommentRE.MatchString(line) && currentBlock != nil {
			currentBlock.End = ln
		} else if rpcRE.MatchString(line) && currentBlock != nil && currentBlock.End > 0 {
			// Mark block as an RPC type.
			currentBlock.Type = impl.RpcComment
			addRpcToLastService(services, currentBlock, lines, ln)
			currentBlock = nil
		} else if serviceRE.MatchString(line) && currentBlock != nil && currentBlock.End > 0 {
			// Mark block as a Service type.
			currentBlock.Type = impl.ServiceComment
			services = addServiceToServices(services, currentBlock, lines, apiAnnotation, ln)
			currentBlock = nil
			apiAnnotation = ""
		} else if apiAnnotationRE.MatchString(line) && currentBlock != nil {
			apiAnnotation = line
		} else {
			// Todo : remove this entire block
			//fmt.Printf("What?: %s\n", line)
			//fmt.Printf(">>>> apiAnnotationRE.MatchString(line): %v\n\n", apiAnnotationRE.MatchString(line))
			//fmt.Printf(">>>> currentBlock: %s\n\n", currentBlock)
			//if currentBlock != nil {
			//	fmt.Printf(">>>> currentBlock.End: %v\n\n", currentBlock.End)
			//}
			//fmt.Printf(">>>> len(services): %d\n\n", len(services))
		}
	}

	// Add package names to services.
	for _, svc := range services {
		svc.Package = pkgName
	}

	return services
}
<commit_msg>Added more todos<commit_after>package main

import (
	"fmt"
	"github.com/maniksurtani/protodocparser/impl"
	"encoding/json"
	"regexp"
	"bufio"
	"os"
	"strings"
	"gopkg.in/alecthomas/kingpin.v2"
)

// Regexps
var (
	startCommentRE = regexp.MustCompile("^\\s*/\\*")
	endCommentRE = regexp.MustCompile("\\s*\\*/\\s*$")
	rpcRE = regexp.MustCompile("\\s*rpc\\s+")
	serviceRE = regexp.MustCompile("\\s*service\\s+")
	serviceNameRE = regexp.MustCompile("\\s*service\\s+(\\w+)\\s*\\{")
	rpcNameRE = regexp.MustCompile("\\s*rpc\\s+(\\w+)\\s*\\(\\s*([\\w]+)\\s*\\)\\s+returns\\s+\\(\\s*(\\w+)\\s*\\)")
	pkgNameRE = regexp.MustCompile("\\s*package\\s+([\\w.]+)\\s*;")
	apiAnnotationRE = regexp.MustCompile("^\\s*\\*\\s*@API")
)

// Command-line args
var (
	debug = kingpin.Flag("verbose", "Enable verbose mode").Bool()
	// TODO should we allow multiple proto files? Separated by ':' or something?
	protoFile = kingpin.Arg("protofile", "Proto file").Required().String()
	outFile = kingpin.Arg("out", "Output file").Required().String()
)

func main() {
	// TODO: parse command line parameters. Do do so, use the kingpin package. See https://github.com/alecthomas/kingpin#examples
	kingpin.Version("0.1") // Version of protodocparser
	kingpin.Parse() // Parse command-line options

	// Read a proto file from StdIn
	// TODO read from input file(s) rather than StdIn
	protoContents := readFromStdIn()
	j := parseAsString(protoContents)

	// TODO write to output file instead of StdOut
	fmt.Println(j)
}

func readFromStdIn() []string {
	s := bufio.NewScanner(os.Stdin)
	txt := make([]string, 0)
	for s.Scan() {
		txt = append(txt, s.Text())
	}
	return txt
}

func addRpcToLastService(services []*impl.Service, commentBlock *impl.CommentBlock, lines []string, currentLine int) {
	rpc := impl.NewRpc()
	rpc.Name, rpc.Request, rpc.Response = rpcName(lines[currentLine])

	// TODO: set Options, Doc and Examples
	// TODO: Doc is the comment block before the first @Example annotation
	// TODO: @Examples exists in the comment block
	// TODO: Options are the protobuf options. This might be harder to figure out since they may be split across multiple lines. :/

	lastService := services[len(services) - 1]
	lastService.Rpcs = append(lastService.Rpcs, rpc)
}

func addServiceToServices(services []*impl.Service, commentBlock *impl.CommentBlock, lines []string, apiAnnotation string, currentLine int) []*impl.Service {
	s := impl.NewService()
	s.Name = serviceName(lines[currentLine])
	if len(apiAnnotation) > 0 {
		s.Api = true
	}

	// TODO: set Api, Design, Doc, Examples, File, Org and Url
	// TODO: to set Api, just check whether the @API annotation exists in the comment block
	// TODO: to set Design and Org, look at the params passed in to @API
	// TODO: if Org isn't set, attempt to "guess" what it might be by looking at the path/package of the proto, and look up in Registry
	// TODO: @Examples exists in the comment block
	// TODO: Get File and Url - TODO, have these passed in as command-line params
	// TODO: Doc is the comment block after @API and before the first @Example annotation

	return append(services, s)
}

func serviceName(line string) string {
	r := serviceNameRE.FindStringSubmatch(line)
	if len(r) > 0 {
		return r[1]
	} else {
		return ""
	}
}

func rpcName(line string) (name, req, rsp string) {
	r := rpcNameRE.FindStringSubmatch(line)
	lenR := len(r)
	if lenR > 0 {
		name = r[1]
	}

	if lenR > 1 {
		req = r[2]
	}

	if lenR > 2 {
		rsp = r[3]
	}

	return
}

func matchPkgName(line string) (string, bool) {
	matches := pkgNameRE.FindStringSubmatch(line)
	if len(matches) > 0 {
		return matches[1], true
	}

	return "", false
}

func parseString(fileAsString string) []*impl.Service {
	return parse(strings.Split(fileAsString, "\n"))
}

func parseAsString(lines []string) string {
	services := parse(lines)
	bytes, err := json.Marshal(services)
	if err != nil {
		panic(fmt.Sprintf("Caught error %v trying to serialize %v into JSON.", err, services))
	}
	return string(bytes)
}

func parse(lines []string) []*impl.Service {
	//fmt.Printf("Got %v lines of txt\n", len(lines))
	// Create an array of services.
	services := make([]*impl.Service, 0)

	var currentBlock *impl.CommentBlock
	pkgName := ""
	matched := false
	apiAnnotation := ""

	for ln, line := range lines {
		if pkgName == "" {
			pkgName, matched = matchPkgName(line)
			if matched {
				continue
			}
		}
		//fmt.Printf("Line %v is [%v]\n", ln, line)
		if startCommentRE.MatchString(line) && currentBlock == nil {
			// Create a new comment block.
			currentBlock = &impl.CommentBlock{}
			currentBlock.Start = ln
			currentBlock.Type = impl.OtherComment
		} else if endCommentRE.MatchString(line) && currentBlock != nil {
			currentBlock.End = ln
		} else if rpcRE.MatchString(line) && currentBlock != nil && currentBlock.End > 0 {
			// Mark block as an RPC type.
			currentBlock.Type = impl.RpcComment
			addRpcToLastService(services, currentBlock, lines, ln)
			currentBlock = nil
		} else if serviceRE.MatchString(line) && currentBlock != nil && currentBlock.End > 0 {
			// Mark block as a Service type.
			currentBlock.Type = impl.ServiceComment
			services = addServiceToServices(services, currentBlock, lines, apiAnnotation, ln)
			currentBlock = nil
			apiAnnotation = ""
		} else if apiAnnotationRE.MatchString(line) && currentBlock != nil {
			apiAnnotation = line
		} else {
			// Todo : remove this entire block
			//fmt.Printf("What?: %s\n", line)
			//fmt.Printf(">>>> apiAnnotationRE.MatchString(line): %v\n\n", apiAnnotationRE.MatchString(line))
			//fmt.Printf(">>>> currentBlock: %s\n\n", currentBlock)
			//if currentBlock != nil {
			//	fmt.Printf(">>>> currentBlock.End: %v\n\n", currentBlock.End)
			//}
			//fmt.Printf(">>>> len(services): %d\n\n", len(services))
		}
	}

	// Add package names to services.
	for _, svc := range services {
		svc.Package = pkgName
	}

	return services
}
<|endoftext|>


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

package main

import (
	"flag"
	"fmt"
	"github.com/andreaskoch/dotman/actions"
	"github.com/andreaskoch/dotman/ui"
	"os"
	"strings"
)

const (
	VERSION       = "0.1.0"
	REPOSITORYURL = "https://github.com/andreaskoch/dotman"
)

var (
	// the what-if flag
	whatIfFlag            = false
	whatIfFlagName        = "whatif"
	whatIfFlagDescription = "Enable the dry-run mode. Only print out what would happen."

	// module filter argument
	moduleFilterExpressionName        = "filter"
	moduleFilterExpressionDescription = "You can add a module filter expression to the import, list, changes and deploy commands."
)

func init() {
	// define flags
	flag.BoolVar(&whatIfFlag, whatIfFlagName, whatIfFlag, whatIfFlagDescription)

}

func main() {

	// parse the command-line options
	flag.Parse()

	// determine working directory
	workingDirectory, err := os.Getwd()
	if err != nil {
		ui.Fatal("Cannot determine working directory. %s", err)
	}

	// determine the command name and command arguments
	commandName := ""
	commandArguments := make([]string, 0)

	commandLineArguments := getCommandLineArguments()
	if len(commandLineArguments) > 0 {
		commandName = commandLineArguments[0]
		commandArguments = commandLineArguments[1:]
	}

	if command := actions.Get(workingDirectory, commandName); command != nil {

		if whatIfFlag {
			ui.Message("Performing a dry-run. No changes will we applied to the system.")
			command.DryRun(commandArguments)
		} else {
			command.Execute(commandArguments)

		}

		os.Exit(0)
	}

	// print the help if no command was recognized
	usage()
}

func getCommandLineArguments() []string {
	args := make([]string, 0)
	for _, arg := range os.Args[1:] {
		whatIfFlagArg := fmt.Sprintf("-%s", whatIfFlagName)
		if strings.HasPrefix(arg, whatIfFlagArg) {
			continue
		}

		args = append(args, arg)
	}

	return args
}

func getApplicationName() string {
	return os.Args[0]
}

var usage = func() {
	// description
	ui.Message("v%s - Backup and bootstrap your dotfiles and system configuration.", VERSION)
	ui.Message("")

	// usage
	ui.Message("usage: %s [-whatif] <command> [<filter>]", getApplicationName())
	ui.Message("")

	// commands
	ui.Message("Available commands are:")
	for _, action := range actions.GetAll() {
		ui.Message("    %s %s  %s", action.Name(), getActionSpacer(action.Name()), action.Description())
	}

	// flags
	ui.Message("")
	ui.Message("Options:")
	ui.Message("    %s %s  %s", whatIfFlagName, getActionSpacer(whatIfFlagName), whatIfFlagDescription)

	// args
	ui.Message("")
	ui.Message("Arguments:")
	ui.Message("    %s %s  %s", moduleFilterExpressionName, getActionSpacer(moduleFilterExpressionName), moduleFilterExpressionDescription)

	// source code
	ui.Message("")
	ui.Message("Contribute: %s", REPOSITORYURL)
}

func getActionSpacer(action string) string {
	maxLen := 0
	for _, action := range actions.GetAll() {
		if len(action.Name()) > maxLen {
			maxLen = len(action.Name())
		}
	}

	spacer := ""
	for i := 0; i < maxLen-len(action); i++ {
		spacer += " "
	}

	return spacer
}
<commit_msg>Forgot to change the version number for the executable itself *d'oh*.<commit_after>// Copyright 2013 Andreas Koch. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.

package main

import (
	"flag"
	"fmt"
	"github.com/andreaskoch/dotman/actions"
	"github.com/andreaskoch/dotman/ui"
	"os"
	"strings"
)

const (
	VERSION       = "0.1.1"
	REPOSITORYURL = "https://github.com/andreaskoch/dotman"
)

var (
	// the what-if flag
	whatIfFlag            = false
	whatIfFlagName        = "whatif"
	whatIfFlagDescription = "Enable the dry-run mode. Only print out what would happen."

	// module filter argument
	moduleFilterExpressionName        = "filter"
	moduleFilterExpressionDescription = "You can add a module filter expression to the import, list, changes and deploy commands."
)

func init() {
	// define flags
	flag.BoolVar(&whatIfFlag, whatIfFlagName, whatIfFlag, whatIfFlagDescription)

}

func main() {

	// parse the command-line options
	flag.Parse()

	// determine working directory
	workingDirectory, err := os.Getwd()
	if err != nil {
		ui.Fatal("Cannot determine working directory. %s", err)
	}

	// determine the command name and command arguments
	commandName := ""
	commandArguments := make([]string, 0)

	commandLineArguments := getCommandLineArguments()
	if len(commandLineArguments) > 0 {
		commandName = commandLineArguments[0]
		commandArguments = commandLineArguments[1:]
	}

	if command := actions.Get(workingDirectory, commandName); command != nil {

		if whatIfFlag {
			ui.Message("Performing a dry-run. No changes will we applied to the system.")
			command.DryRun(commandArguments)
		} else {
			command.Execute(commandArguments)

		}

		os.Exit(0)
	}

	// print the help if no command was recognized
	usage()
}

func getCommandLineArguments() []string {
	args := make([]string, 0)
	for _, arg := range os.Args[1:] {
		whatIfFlagArg := fmt.Sprintf("-%s", whatIfFlagName)
		if strings.HasPrefix(arg, whatIfFlagArg) {
			continue
		}

		args = append(args, arg)
	}

	return args
}

func getApplicationName() string {
	return os.Args[0]
}

var usage = func() {
	// description
	ui.Message("v%s - Backup and bootstrap your dotfiles and system configuration.", VERSION)
	ui.Message("")

	// usage
	ui.Message("usage: %s [-whatif] <command> [<filter>]", getApplicationName())
	ui.Message("")

	// commands
	ui.Message("Available commands are:")
	for _, action := range actions.GetAll() {
		ui.Message("    %s %s  %s", action.Name(), getActionSpacer(action.Name()), action.Description())
	}

	// flags
	ui.Message("")
	ui.Message("Options:")
	ui.Message("    %s %s  %s", whatIfFlagName, getActionSpacer(whatIfFlagName), whatIfFlagDescription)

	// args
	ui.Message("")
	ui.Message("Arguments:")
	ui.Message("    %s %s  %s", moduleFilterExpressionName, getActionSpacer(moduleFilterExpressionName), moduleFilterExpressionDescription)

	// source code
	ui.Message("")
	ui.Message("Contribute: %s", REPOSITORYURL)
}

func getActionSpacer(action string) string {
	maxLen := 0
	for _, action := range actions.GetAll() {
		if len(action.Name()) > maxLen {
			maxLen = len(action.Name())
		}
	}

	spacer := ""
	for i := 0; i < maxLen-len(action); i++ {
		spacer += " "
	}

	return spacer
}
<|endoftext|>