diff --git a/Makefile.ctrlg b/Makefile.ctrlg
new file mode 100644
index 0000000..e74e430
--- /dev/null
+++ b/Makefile.ctrlg
@@ -0,0 +1,27 @@
+.PHONY: tidy install-deps build test coverage
+
+# Run go mod tidy
+tidy:
+ @echo "Running go mod tidy..."
+ go mod tidy
+
+# Install dependencies
+install-deps: tidy
+ @echo No dependency to install
+
+# Build the project
+build: tidy
+ @echo "Building project..."
+ go test -run=XXX_SHOULD_NEVER_MATCH_XXX ./...
+
+# Run tests
+test: tidy
+ @echo "Running tests..."
+ go test ./... -skip TestMutations -count=1 -v -timeout 60s
+ @echo "Running mutation tests..."
+ go test ./... -run TestMutations -count=1 -v -timeout 300s
+
+# Generate coverage report
+coverage: tidy
+ @echo "Generating coverage report..."
+ go test ./... -cover -timeout 90s
diff --git a/deluge/deluge.go b/deluge/deluge.go
index b9f60a7..c500f74 100644
--- a/deluge/deluge.go
+++ b/deluge/deluge.go
@@ -1,11 +1,12 @@
package deluge
import (
- "github.com/ofux/deluge-dsl/ast"
- "github.com/ofux/deluge-dsl/evaluator"
- "github.com/ofux/deluge-dsl/object"
- log "github.com/sirupsen/logrus"
"time"
+
+ "github.com/ofux/deluge/dsl/ast"
+ "github.com/ofux/deluge/dsl/evaluator"
+ "github.com/ofux/deluge/dsl/object"
+ log "github.com/sirupsen/logrus"
)
type Deluge struct {
@@ -79,7 +80,7 @@ func (d *delugeBuilder) CreateDeluge(node ast.Node, args ...object.Object) objec
}
for scenarioId, v := range conf.Pairs {
- scenarioConf, ok := v.Value.(*object.Hash)
+ scenarioConf, ok := v.(*object.Hash)
if !ok {
log.Fatalf("Expected scenario configuration to be an object at %s\n", ast.PrintLocation(node))
}
@@ -88,7 +89,7 @@ func (d *delugeBuilder) CreateDeluge(node ast.Node, args ...object.Object) objec
if !ok {
log.Fatalf("Expected 'concurrent' value in configuration at %s\n", ast.PrintLocation(node))
}
- concurrentClients, ok := concurrentClientsHashPair.Value.(*object.Integer)
+ concurrentClients, ok := concurrentClientsHashPair.(*object.Integer)
if !ok {
log.Fatalf("Expected 'concurrent' value to be an integer in configuration at %s\n", ast.PrintLocation(node))
}
@@ -97,7 +98,7 @@ func (d *delugeBuilder) CreateDeluge(node ast.Node, args ...object.Object) objec
if !ok {
log.Fatalf("Expected 'delay' value in configuration at %s\n", ast.PrintLocation(node))
}
- delayHashStr, ok := delayHashPair.Value.(*object.String)
+ delayHashStr, ok := delayHashPair.(*object.String)
if !ok {
log.Fatalf("Expected 'concurrent' value to be a duration in configuration at %s\n", ast.PrintLocation(node))
}
diff --git a/deluge/deluge_test.go b/deluge/deluge_test.go
index e5cef79..b656914 100644
--- a/deluge/deluge_test.go
+++ b/deluge/deluge_test.go
@@ -1,9 +1,10 @@
package deluge
import (
- "github.com/ofux/deluge-dsl/lexer"
- "github.com/ofux/deluge-dsl/parser"
"testing"
+
+ "github.com/ofux/deluge/dsl/lexer"
+ "github.com/ofux/deluge/dsl/parser"
)
func BenchmarkNewDeluge(b *testing.B) {
diff --git a/deluge/recorder.go b/deluge/recorder.go
new file mode 100644
index 0000000..bfc4a58
--- /dev/null
+++ b/deluge/recorder.go
@@ -0,0 +1,147 @@
+package deluge
+
+import (
+ "errors"
+ "sync"
+
+ hdr "github.com/ofux/hdrhistogram"
+)
+
+type RecordingState int
+
+const (
+ READY RecordingState = iota
+ RECORDING
+ TERMINATING
+ TERMINATED
+)
+
+// Histogram is an interface for recording and retrieving histogram data.
+// It abstracts the underlying histogram implementation.
+type Histogram interface {
+ RecordValue(value int64) error
+ TotalCount() int64
+}
+
+// HistogramConstructor is a function type that creates new Histogram instances.
+type HistogramConstructor func() Histogram
+
+type Recorder interface {
+ Record(iteration int, id string, value int64)
+ Close()
+ GetRecords() (map[string][]Histogram, error)
+}
+
+type QueuedRecorder struct {
+ recording RecordingState
+ recordsQueue chan Record
+ recordingWaitGroup *sync.WaitGroup
+ processingWaitGroup *sync.WaitGroup
+ histograms map[string][]Histogram
+ newHistogram HistogramConstructor
+}
+
+type Record struct {
+ iteration int
+ id string
+ value int64
+}
+
+func NewRecorder(newHistogram HistogramConstructor) Recorder {
+ recorder := &QueuedRecorder{
+ recording: READY,
+ recordsQueue: make(chan Record, 10),
+ recordingWaitGroup: new(sync.WaitGroup),
+ processingWaitGroup: new(sync.WaitGroup),
+ histograms: make(map[string][]Histogram),
+ newHistogram: newHistogram,
+ }
+ recorder.processRecords()
+ return recorder
+}
+
+// Record records a new value in the underlying appropriate HDRHistogram.
+// This is safe to call this method from different goroutines.
+// Calling this method on a closed Recorder will cause a panic.
+func (r *QueuedRecorder) Record(iteration int, id string, value int64) {
+ r.recordingWaitGroup.Add(1)
+ go func() {
+ defer r.recordingWaitGroup.Done()
+ r.recordsQueue <- Record{
+ iteration: iteration,
+ id: id,
+ value: value,
+ }
+ }()
+}
+
+// Close closes the Recorder, making the results available for read.
+// Trying to record some values on a closed Recorder will cause a panic.
+func (r *QueuedRecorder) Close() {
+ if r.recording == RECORDING {
+ r.recording = TERMINATING
+ // wait for all records to be taken
+ r.recordingWaitGroup.Wait()
+ // ensure listener won't stay blocked
+ close(r.recordsQueue)
+ // wait for the end of recording
+ r.processingWaitGroup.Wait()
+ r.recording = TERMINATED
+ }
+}
+
+func (r *QueuedRecorder) GetRecords() (map[string][]Histogram, error) {
+ if r.recording != TERMINATED {
+ return nil, errors.New("Cannot get records while recording. Did you forget to call the 'Close()' method?")
+ }
+ return r.histograms, nil
+}
+
+func (r *QueuedRecorder) processRecords() {
+ r.recording = RECORDING
+ r.processingWaitGroup.Add(1)
+
+ go func() {
+ defer r.processingWaitGroup.Done()
+
+ for {
+ rec, ok := <-r.recordsQueue
+ if !ok {
+ return
+ }
+
+ histograms, ok := r.histograms[rec.id]
+ if !ok {
+ histograms = make([]Histogram, 0)
+ r.histograms[rec.id] = histograms
+ }
+
+ // TODO: optimize this
+ if len(histograms) <= rec.iteration {
+ diff := rec.iteration + 1 - len(histograms)
+ histograms = append(histograms, r.createHistograms(diff)...)
+ r.histograms[rec.id] = histograms
+ }
+
+ histogram := histograms[rec.iteration]
+ histogram.RecordValue(rec.value)
+ }
+ }()
+
+}
+
+func (r *QueuedRecorder) createHistograms(count int) []Histogram {
+ histograms := make([]Histogram, count)
+ for i := 0; i < count; i++ {
+ histograms[i] = r.newHistogram()
+ }
+ return histograms
+}
+
+// DefaultHistogramConstructor returns a HistogramConstructor that creates
+// HDR histograms with default settings (min=0, max=3600000000, sigfigs=3).
+func DefaultHistogramConstructor() HistogramConstructor {
+ return func() Histogram {
+ return hdr.New(0, 3600000000, 3)
+ }
+}
diff --git a/deluge/scenario.go b/deluge/scenario.go
index f774bb8..a70ca86 100644
--- a/deluge/scenario.go
+++ b/deluge/scenario.go
@@ -1,11 +1,12 @@
package deluge
import (
- "github.com/ofux/deluge-dsl/ast"
- log "github.com/sirupsen/logrus"
"strconv"
"sync"
"time"
+
+ "github.com/ofux/deluge/dsl/ast"
+ log "github.com/sirupsen/logrus"
)
type Scenario struct {
@@ -14,6 +15,7 @@ type Scenario struct {
script ast.Node
duration time.Duration
TotalSimUsersCalls int64
+ recorder Recorder
}
func NewScenario(name string, concurrent int, duration time.Duration, script ast.Node) *Scenario {
@@ -22,10 +24,11 @@ func NewScenario(name string, concurrent int, duration time.Duration, script ast
simUsers: make([]*SimUser, concurrent),
duration: duration,
script: script,
+ recorder: NewRecorder(DefaultHistogramConstructor()),
}
for i := 0; i < concurrent; i++ {
- s.simUsers[i] = NewSimUser(strconv.Itoa(i), s.script)
+ s.simUsers[i] = NewSimUser(strconv.Itoa(i), s.script, s.recorder)
}
return s
@@ -46,6 +49,7 @@ func (sc *Scenario) Run(duration time.Duration) {
ticker := time.NewTicker(sc.duration)
timer := time.NewTimer(duration)
+ i := 0
for {
if time.Now().Sub(start).Nanoseconds() > duration.Nanoseconds() {
log.Debugf("Terminate user simulation %s", su.Name)
@@ -54,7 +58,7 @@ func (sc *Scenario) Run(duration time.Duration) {
log.Debugf("Running user simulation %s", su.Name)
simUserCallCounter <- 1
- su.Run()
+ su.Run(i)
if su.Status == DoneError {
return
@@ -66,10 +70,12 @@ func (sc *Scenario) Run(duration time.Duration) {
return
case <-ticker.C:
}
+ i++
}
}(su)
}
waitg.Wait()
+ sc.recorder.Close()
log.Infof("Scenario executed %d requests in %s", sc.TotalSimUsersCalls, time.Now().Sub(start).String())
}
diff --git a/deluge/simuser.go b/deluge/simuser.go
index 4ff1008..ace8ee9 100644
--- a/deluge/simuser.go
+++ b/deluge/simuser.go
@@ -1,12 +1,14 @@
package deluge
import (
- "github.com/ofux/deluge-dsl/ast"
- "github.com/ofux/deluge-dsl/evaluator"
- "github.com/ofux/deluge-dsl/object"
- log "github.com/sirupsen/logrus"
"net/http"
+ "strconv"
"time"
+
+ "github.com/ofux/deluge/dsl/ast"
+ "github.com/ofux/deluge/dsl/evaluator"
+ "github.com/ofux/deluge/dsl/object"
+ log "github.com/sirupsen/logrus"
)
type SimUserStatus int
@@ -25,15 +27,18 @@ type SimUser struct {
client *http.Client
Status SimUserStatus
SleepDuration time.Duration
+ recorder Recorder
+ iteration int
}
-func NewSimUser(name string, script ast.Node) *SimUser {
+func NewSimUser(name string, script ast.Node, recorder Recorder) *SimUser {
su := &SimUser{
Name: name,
script: script,
evaluator: evaluator.NewEvaluator(),
client: http.DefaultClient,
Status: Virgin,
+ recorder: recorder,
}
su.evaluator.AddBuiltin("http", su.ExecHTTPRequest)
@@ -41,7 +46,8 @@ func NewSimUser(name string, script ast.Node) *SimUser {
return su
}
-func (su *SimUser) Run() {
+func (su *SimUser) Run(iteration int) {
+ su.iteration = iteration
su.Status = InProgress
env := object.NewEnvironment()
evaluated := su.evaluator.Eval(su.script, env)
@@ -62,40 +68,42 @@ func (su *SimUser) ExecHTTPRequest(node ast.Node, args ...object.Object) object.
return oErr
}
- //name := args[0].(*object.String).Value
+ reqName := args[0].(*object.String).Value
reqObj := args[1].(*object.Hash)
jsUrl, ok := reqObj.Get("url")
if !ok {
return evaluator.NewError(node, "invalid HTTP request: missing 'url' field")
}
- url, ok := jsUrl.Value.(*object.String)
+ url, ok := jsUrl.(*object.String)
if !ok {
return evaluator.NewError(node, "invalid HTTP request: 'url' should be a STRING")
}
var method = "GET"
if methodField, ok := reqObj.Get("method"); ok {
- if methodFieldVal, ok := methodField.Value.(*object.String); ok {
+ if methodFieldVal, ok := methodField.(*object.String); ok {
method = methodFieldVal.Value
}
}
req, err := http.NewRequest(method, url.Value, nil)
if err != nil {
- return evaluator.NewError(node, err.Error())
+ return evaluator.NewError(node, "%s", err.Error())
}
log.Debugf("Performing HTTP request: %s %s", req.Method, req.URL.String())
start := time.Now()
- //res, err := su.client.Do(req)
+ res, err := su.client.Do(req)
end := time.Now()
duration := end.Sub(start)
+
if err != nil {
log.Debugf("Request error: %s", err.Error())
- return evaluator.NewError(node, err.Error())
+ return evaluator.NewError(node, "%s", err.Error())
} else {
log.Debugf("Response status: %s in %s", "res.Status", duration.String())
+ su.recorder.Record(su.iteration, reqName+"->"+strconv.Itoa(res.StatusCode), duration.Nanoseconds()/1000)
}
return evaluator.NULL
diff --git a/deluge/simuser_test.go b/deluge/simuser_test.go
index d0c18f0..ed65587 100644
--- a/deluge/simuser_test.go
+++ b/deluge/simuser_test.go
@@ -1,12 +1,16 @@
package deluge
import (
- "github.com/ofux/deluge-dsl/lexer"
- "github.com/ofux/deluge-dsl/parser"
+ "fmt"
+ "net/http"
+ "net/http/httptest"
"testing"
+
+ "github.com/ofux/deluge/dsl/lexer"
+ "github.com/ofux/deluge/dsl/parser"
)
-func NewSimUserTest(t *testing.T, js string) *SimUser {
+func NewSimUserTest(t *testing.T, js string) (*SimUser, Recorder) {
l := lexer.New(js)
p := parser.New(l)
@@ -16,7 +20,8 @@ func NewSimUserTest(t *testing.T, js string) *SimUser {
t.Fatal("Parsing error(s)")
}
- return NewSimUser("1", program)
+ recorder := NewRecorder(DefaultHistogramConstructor())
+ return NewSimUser("1", program, recorder), recorder
}
func checkSimUserStatus(t *testing.T, su *SimUser, status SimUserStatus) {
@@ -25,20 +30,150 @@ func checkSimUserStatus(t *testing.T, su *SimUser, status SimUserStatus) {
}
}
-func TestAssert(t *testing.T) {
+func TestSimUser_Assert(t *testing.T) {
t.Run("Assert true", func(t *testing.T) {
- su := NewSimUserTest(t, `
+ su, _ := NewSimUserTest(t, `
assert(1+1 == 2)
`)
- su.Run()
+ su.Run(0)
checkSimUserStatus(t, su, DoneSuccess)
})
t.Run("Assert false", func(t *testing.T) {
- su := NewSimUserTest(t, `
+ su, _ := NewSimUserTest(t, `
assert(1+1 == 3)
`)
- su.Run()
+ su.Run(0)
checkSimUserStatus(t, su, DoneError)
})
}
+
+func TestSimUser_ExecHTTPRequest(t *testing.T) {
+ t.Run("Simple HTTP GET request", func(t *testing.T) {
+ callCount := 0
+ ts := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
+ callCount++
+ if r.Method != "GET" {
+ t.Errorf("Expected HTTP method to be %s, got %s", "GET", r.Method)
+ }
+ w.Header().Set("Content-Type", "application/json")
+ fmt.Fprintln(w, `{"foo":"bar"}`)
+ }))
+ defer ts.Close()
+
+ url := ts.URL
+ const reqName = "Some request"
+ const recName = reqName + "->200"
+
+ su, rec := NewSimUserTest(t, `
+ http("`+reqName+`", {
+ "url": "`+url+`"
+ });
+ `)
+ su.Run(0)
+ checkSimUserStatus(t, su, DoneSuccess)
+ checkRecords(t, rec, recName, 1)
+
+ if callCount != 1 {
+ t.Errorf("Expected %d call(s), got %d", 1, callCount)
+ }
+ })
+
+ t.Run("Simple HTTP POST request", func(t *testing.T) {
+ callCount := 0
+ ts := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
+ callCount++
+ if r.Method != "POST" {
+ t.Errorf("Expected HTTP method to be %s, got %s", "POST", r.Method)
+ }
+ w.Header().Set("Content-Type", "application/json")
+ fmt.Fprintln(w, `{"foo":"bar"}`)
+ }))
+ defer ts.Close()
+
+ url := ts.URL
+ const reqName = "Some request"
+ const recName = reqName + "->200"
+
+ su, rec := NewSimUserTest(t, `
+ http("`+reqName+`", {
+ "url": "`+url+`",
+ "method": "POST"
+ });
+ `)
+ su.Run(0)
+ checkSimUserStatus(t, su, DoneSuccess)
+ checkRecords(t, rec, recName, 1)
+
+ if callCount != 1 {
+ t.Errorf("Expected %d call(s), got %d", 1, callCount)
+ }
+ })
+
+ t.Run("Bad HTTP arguments", func(t *testing.T) {
+ su, _ := NewSimUserTest(t, `
+ http("foo");
+ `)
+ su.Run(0)
+ checkSimUserStatus(t, su, DoneError)
+ })
+
+ t.Run("Bad HTTP name", func(t *testing.T) {
+ su, _ := NewSimUserTest(t, `
+ http(1, {
+ "url": "http://plop.org",
+ "method": "POST"
+ });
+ `)
+ su.Run(0)
+ checkSimUserStatus(t, su, DoneError)
+ })
+
+ t.Run("No HTTP url", func(t *testing.T) {
+ su, _ := NewSimUserTest(t, `
+ http("foo", {
+ "method": "POST"
+ });
+ `)
+ su.Run(0)
+ checkSimUserStatus(t, su, DoneError)
+ })
+
+ t.Run("Bad HTTP url", func(t *testing.T) {
+ su, _ := NewSimUserTest(t, `
+ http("foo", {
+ "url": 42
+ });
+ `)
+ su.Run(0)
+ checkSimUserStatus(t, su, DoneError)
+ })
+
+ t.Run("Bad HTTP url 2", func(t *testing.T) {
+ su, _ := NewSimUserTest(t, `
+ http("foo", {
+ "url": "foobar"
+ });
+ `)
+ su.Run(0)
+ checkSimUserStatus(t, su, DoneError)
+ })
+}
+
+func checkRecords(t *testing.T, rec Recorder, recName string, recCount int64) {
+ rec.Close()
+ records, err := rec.GetRecords()
+ if err != nil {
+ t.Fatal(err.Error())
+ }
+ record, ok := records[recName]
+ if !ok {
+ t.Fatalf("Expected to have some records for '%s'", recName)
+ }
+ if len(record) != 1 {
+ t.Fatalf("Expected to have %d records for '%s', got %d", 1, recName, len(record))
+ }
+ if record[0].TotalCount() != recCount {
+ t.Errorf("Expected to have totalCount = %d, got %d", recCount, record[0].TotalCount())
+ }
+}
diff --git a/deluge/util.go b/deluge/util.go
index bf16511..6d65f70 100644
--- a/deluge/util.go
+++ b/deluge/util.go
@@ -1,7 +1,7 @@
package deluge
import (
- "github.com/ofux/deluge-dsl/parser"
+ "github.com/ofux/deluge/dsl/parser"
log "github.com/sirupsen/logrus"
)
diff --git a/dsl/ast/ast.go b/dsl/ast/ast.go
new file mode 100644
index 0000000..9483e37
--- /dev/null
+++ b/dsl/ast/ast.go
@@ -0,0 +1,461 @@
+package ast
+
+import (
+ "bytes"
+ "fmt"
+ "github.com/ofux/deluge/dsl/token"
+ "strings"
+)
+
+// The base Node interface
+type Node interface {
+ TokenDetails() token.Token
+ TokenLiteral() string
+ String() string
+}
+
+// All statement nodes implement this
+type Statement interface {
+ Node
+ statementNode()
+}
+
+// All expression nodes implement this
+type Expression interface {
+ Node
+ expressionNode()
+}
+
+func PrintLocation(node Node) string {
+ return fmt.Sprintf("%s (line %d, col %d)", node.TokenLiteral(), node.TokenDetails().Line, node.TokenDetails().Column)
+}
+
+type Program struct {
+ Statements []Statement
+}
+
+func (p *Program) TokenDetails() token.Token {
+ if len(p.Statements) > 0 {
+ return p.Statements[0].TokenDetails()
+ } else {
+ return token.Token{Type: token.EOF, Line: 1, Column: 1, Literal: token.EOF}
+ }
+}
+
+func (p *Program) TokenLiteral() string {
+ if len(p.Statements) > 0 {
+ return p.Statements[0].TokenLiteral()
+ } else {
+ return ""
+ }
+}
+
+func (p *Program) String() string {
+ var out bytes.Buffer
+
+ for _, s := range p.Statements {
+ out.WriteString(s.String())
+ }
+
+ return out.String()
+}
+
+// Statements
+type LetStatement struct {
+ Token token.Token // the token.LET token
+ Name *Identifier
+ Value Expression
+}
+
+func (ls *LetStatement) statementNode() {}
+func (ls *LetStatement) TokenDetails() token.Token { return ls.Token }
+func (ls *LetStatement) TokenLiteral() string { return ls.Token.Literal }
+func (ls *LetStatement) String() string {
+ var out bytes.Buffer
+
+ out.WriteString(ls.TokenLiteral() + " ")
+ out.WriteString(ls.Name.String())
+ out.WriteString(" = ")
+
+ if ls.Value != nil {
+ out.WriteString(ls.Value.String())
+ }
+
+ out.WriteString(";")
+
+ return out.String()
+}
+
+type ReturnStatement struct {
+ Token token.Token // the 'return' token
+ ReturnValue Expression
+}
+
+func (rs *ReturnStatement) statementNode() {}
+func (rs *ReturnStatement) TokenDetails() token.Token { return rs.Token }
+func (rs *ReturnStatement) TokenLiteral() string { return rs.Token.Literal }
+func (rs *ReturnStatement) String() string {
+ var out bytes.Buffer
+
+ out.WriteString(rs.TokenLiteral() + " ")
+
+ if rs.ReturnValue != nil {
+ out.WriteString(rs.ReturnValue.String())
+ }
+
+ out.WriteString(";")
+
+ return out.String()
+}
+
+type ExpressionStatement struct {
+ Token token.Token // the first token of the expression
+ Expression Expression
+}
+
+func (es *ExpressionStatement) statementNode() {}
+func (es *ExpressionStatement) TokenDetails() token.Token { return es.Token }
+func (es *ExpressionStatement) TokenLiteral() string { return es.Token.Literal }
+func (es *ExpressionStatement) String() string {
+ if es.Expression != nil {
+ return es.Expression.String()
+ }
+ return ""
+}
+
+type BlockStatement struct {
+ Token token.Token // the { token
+ Statements []Statement
+}
+
+func (bs *BlockStatement) statementNode() {}
+func (bs *BlockStatement) TokenDetails() token.Token { return bs.Token }
+func (bs *BlockStatement) TokenLiteral() string { return bs.Token.Literal }
+func (bs *BlockStatement) String() string {
+ var out bytes.Buffer
+
+ for _, s := range bs.Statements {
+ out.WriteString(s.String())
+ }
+
+ return out.String()
+}
+
+// Expressions
+type Null struct {
+ Token token.Token // the token.NULL token
+}
+
+func (i *Null) expressionNode() {}
+func (i *Null) TokenDetails() token.Token { return i.Token }
+func (i *Null) TokenLiteral() string { return i.Token.Literal }
+func (i *Null) String() string { return i.Token.Literal }
+
+type Identifier struct {
+ Token token.Token // the token.IDENT token
+ Value string
+}
+
+func (i *Identifier) expressionNode() {}
+func (i *Identifier) TokenDetails() token.Token { return i.Token }
+func (i *Identifier) TokenLiteral() string { return i.Token.Literal }
+func (i *Identifier) String() string { return i.Value }
+
+type Boolean struct {
+ Token token.Token
+ Value bool
+}
+
+func (b *Boolean) expressionNode() {}
+func (b *Boolean) TokenDetails() token.Token { return b.Token }
+func (b *Boolean) TokenLiteral() string { return b.Token.Literal }
+func (b *Boolean) String() string { return b.Token.Literal }
+
+type IntegerLiteral struct {
+ Token token.Token
+ Value int64
+}
+
+func (il *IntegerLiteral) expressionNode() {}
+func (il *IntegerLiteral) TokenDetails() token.Token { return il.Token }
+func (il *IntegerLiteral) TokenLiteral() string { return il.Token.Literal }
+func (il *IntegerLiteral) String() string { return il.Token.Literal }
+
+type FloatLiteral struct {
+ Token token.Token
+ Value float64
+}
+
+func (fl *FloatLiteral) expressionNode() {}
+func (fl *FloatLiteral) TokenDetails() token.Token { return fl.Token }
+func (fl *FloatLiteral) TokenLiteral() string { return fl.Token.Literal }
+func (fl *FloatLiteral) String() string { return fl.Token.Literal }
+
+type PrefixExpression struct {
+ Token token.Token // The prefix token, e.g. !
+ Operator string
+ Right Expression
+}
+
+func (pe *PrefixExpression) expressionNode() {}
+func (pe *PrefixExpression) TokenDetails() token.Token { return pe.Token }
+func (pe *PrefixExpression) TokenLiteral() string { return pe.Token.Literal }
+func (pe *PrefixExpression) String() string {
+ var out bytes.Buffer
+
+ out.WriteString("(")
+ out.WriteString(pe.Operator)
+ out.WriteString(pe.Right.String())
+ out.WriteString(")")
+
+ return out.String()
+}
+
+type InfixExpression struct {
+ Token token.Token // The operator token, e.g. +
+ Left Expression
+ Operator string
+ Right Expression
+}
+
+func (oe *InfixExpression) expressionNode() {}
+func (oe *InfixExpression) TokenDetails() token.Token { return oe.Token }
+func (oe *InfixExpression) TokenLiteral() string { return oe.Token.Literal }
+func (oe *InfixExpression) String() string {
+ var out bytes.Buffer
+
+ out.WriteString("(")
+ out.WriteString(oe.Left.String())
+ out.WriteString(" " + oe.Operator + " ")
+ out.WriteString(oe.Right.String())
+ out.WriteString(")")
+
+ return out.String()
+}
+
+type AssignmentExpression struct {
+ Token token.Token // The operator token, e.g. =
+ Left Expression
+ Operator string
+ Right Expression
+}
+
+func (ae *AssignmentExpression) expressionNode() {}
+func (ae *AssignmentExpression) TokenDetails() token.Token { return ae.Token }
+func (ae *AssignmentExpression) TokenLiteral() string { return ae.Token.Literal }
+func (ae *AssignmentExpression) String() string {
+ var out bytes.Buffer
+
+ out.WriteString("(")
+ out.WriteString(ae.Left.String())
+ out.WriteString(" " + ae.Operator + " ")
+ out.WriteString(ae.Right.String())
+ out.WriteString(")")
+
+ return out.String()
+}
+
+type PostAssignmentExpression struct {
+ Token token.Token // The operator token, e.g. ++
+ Left Expression
+ Operator string
+}
+
+func (pae *PostAssignmentExpression) expressionNode() {}
+func (pae *PostAssignmentExpression) TokenDetails() token.Token { return pae.Token }
+func (pae *PostAssignmentExpression) TokenLiteral() string { return pae.Token.Literal }
+func (pae *PostAssignmentExpression) String() string {
+ var out bytes.Buffer
+
+ out.WriteString("(")
+ out.WriteString(pae.Left.String())
+ out.WriteString(pae.Operator)
+ out.WriteString(")")
+
+ return out.String()
+}
+
+type IfStatement struct {
+ Token token.Token // The 'if' token
+ Condition Expression
+ Consequence *BlockStatement
+ Alternative Statement
+}
+
+func (is *IfStatement) statementNode() {}
+func (is *IfStatement) TokenDetails() token.Token { return is.Token }
+func (is *IfStatement) TokenLiteral() string { return is.Token.Literal }
+func (is *IfStatement) String() string {
+ var out bytes.Buffer
+
+ out.WriteString("if")
+ out.WriteString(is.Condition.String())
+ out.WriteString(" ")
+ out.WriteString(is.Consequence.String())
+
+ if is.Alternative != nil {
+ out.WriteString("else ")
+ switch alternative := is.Alternative.(type) {
+ case *BlockStatement:
+ out.WriteString(alternative.String())
+ case *IfStatement:
+ out.WriteString(alternative.String())
+ }
+ }
+
+ return out.String()
+}
+
+type ForStatement struct {
+ Token token.Token // The 'for' token
+ Initialization Statement
+ Condition Expression
+ Afterthought Statement
+ Loop *BlockStatement
+}
+
+func (fs *ForStatement) statementNode() {}
+func (fs *ForStatement) TokenDetails() token.Token { return fs.Token }
+func (fs *ForStatement) TokenLiteral() string { return fs.Token.Literal }
+func (fs *ForStatement) String() string {
+ var out bytes.Buffer
+
+ out.WriteString("for (")
+ out.WriteString(fs.Initialization.String())
+ out.WriteString("; ")
+ out.WriteString(fs.Condition.String())
+ out.WriteString("; ")
+ out.WriteString(fs.Afterthought.String())
+ out.WriteString(") ")
+ out.WriteString(fs.Loop.String())
+
+ return out.String()
+}
+
+type FunctionLiteral struct {
+ Token token.Token // The 'function' token
+ Parameters []*Identifier
+ Body *BlockStatement
+}
+
+func (fl *FunctionLiteral) expressionNode() {}
+func (fl *FunctionLiteral) TokenDetails() token.Token { return fl.Token }
+func (fl *FunctionLiteral) TokenLiteral() string { return fl.Token.Literal }
+func (fl *FunctionLiteral) String() string {
+ var out bytes.Buffer
+
+ params := []string{}
+ for _, p := range fl.Parameters {
+ params = append(params, p.String())
+ }
+
+ out.WriteString(fl.TokenLiteral())
+ out.WriteString("(")
+ out.WriteString(strings.Join(params, ", "))
+ out.WriteString(") ")
+ out.WriteString(fl.Body.String())
+
+ return out.String()
+}
+
+type CallExpression struct {
+ Token token.Token // The '(' token
+ Function Expression // Identifier or FunctionLiteral
+ Arguments []Expression
+}
+
+func (ce *CallExpression) expressionNode() {}
+func (ce *CallExpression) TokenDetails() token.Token { return ce.Token }
+func (ce *CallExpression) TokenLiteral() string { return ce.Token.Literal }
+func (ce *CallExpression) String() string {
+ var out bytes.Buffer
+
+ args := []string{}
+ for _, a := range ce.Arguments {
+ args = append(args, a.String())
+ }
+
+ out.WriteString(ce.Function.String())
+ out.WriteString("(")
+ out.WriteString(strings.Join(args, ", "))
+ out.WriteString(")")
+
+ return out.String()
+}
+
+type StringLiteral struct {
+ Token token.Token
+ Value string
+}
+
+func (sl *StringLiteral) expressionNode() {}
+func (sl *StringLiteral) TokenDetails() token.Token { return sl.Token }
+func (sl *StringLiteral) TokenLiteral() string { return sl.Token.Literal }
+func (sl *StringLiteral) String() string { return sl.Token.Literal }
+
+type ArrayLiteral struct {
+ Token token.Token // the '[' token
+ Elements []Expression
+}
+
+func (al *ArrayLiteral) expressionNode() {}
+func (al *ArrayLiteral) TokenDetails() token.Token { return al.Token }
+func (al *ArrayLiteral) TokenLiteral() string { return al.Token.Literal }
+func (al *ArrayLiteral) String() string {
+ var out bytes.Buffer
+
+ elements := []string{}
+ for _, el := range al.Elements {
+ elements = append(elements, el.String())
+ }
+
+ out.WriteString("[")
+ out.WriteString(strings.Join(elements, ", "))
+ out.WriteString("]")
+
+ return out.String()
+}
+
+type IndexExpression struct {
+ Token token.Token // The [ token
+ Left Expression
+ Index Expression
+}
+
+func (ie *IndexExpression) expressionNode() {}
+func (ie *IndexExpression) TokenDetails() token.Token { return ie.Token }
+func (ie *IndexExpression) TokenLiteral() string { return ie.Token.Literal }
+func (ie *IndexExpression) String() string {
+ var out bytes.Buffer
+
+ out.WriteString("(")
+ out.WriteString(ie.Left.String())
+ out.WriteString("[")
+ out.WriteString(ie.Index.String())
+ out.WriteString("])")
+
+ return out.String()
+}
+
+type HashLiteral struct {
+ Token token.Token // the '{' token
+ Pairs map[Expression]Expression
+}
+
+func (hl *HashLiteral) expressionNode() {}
+func (hl *HashLiteral) TokenDetails() token.Token { return hl.Token }
+func (hl *HashLiteral) TokenLiteral() string { return hl.Token.Literal }
+func (hl *HashLiteral) String() string {
+ var out bytes.Buffer
+
+ pairs := []string{}
+ for key, value := range hl.Pairs {
+ pairs = append(pairs, key.String()+":"+value.String())
+ }
+
+ out.WriteString("{")
+ out.WriteString(strings.Join(pairs, ", "))
+ out.WriteString("}")
+
+ return out.String()
+}
diff --git a/dsl/ast/ast_test.go b/dsl/ast/ast_test.go
new file mode 100644
index 0000000..1ed0df8
--- /dev/null
+++ b/dsl/ast/ast_test.go
@@ -0,0 +1,28 @@
+package ast
+
+import (
+ "github.com/ofux/deluge/dsl/token"
+ "testing"
+)
+
+func TestString(t *testing.T) {
+ program := &Program{
+ Statements: []Statement{
+ &LetStatement{
+ Token: token.Token{Type: token.LET, Literal: "let"},
+ Name: &Identifier{
+ Token: token.Token{Type: token.IDENT, Literal: "myVar"},
+ Value: "myVar",
+ },
+ Value: &Identifier{
+ Token: token.Token{Type: token.IDENT, Literal: "anotherVar"},
+ Value: "anotherVar",
+ },
+ },
+ },
+ }
+
+ if program.String() != "let myVar = anotherVar;" {
+ t.Errorf("program.String() wrong. got=%q", program.String())
+ }
+}
diff --git a/dsl/evaluator/builtins.go b/dsl/evaluator/builtins.go
new file mode 100644
index 0000000..3b91805
--- /dev/null
+++ b/dsl/evaluator/builtins.go
@@ -0,0 +1,379 @@
+package evaluator
+
+import (
+ "encoding/json"
+ "errors"
+ "fmt"
+ "net/url"
+ "strconv"
+ "strings"
+ "time"
+
+ "github.com/ofux/deluge/dsl/ast"
+ "github.com/ofux/deluge/dsl/object"
+ "github.com/ofux/deluge/dsl/token"
+)
+
+const ANY_TYPE object.ObjectType = "ANY"
+
+func AddGlobalBuiltin(name string, fn object.BuiltinFunction) error {
+ if _, ok := globalBuiltins[name]; ok {
+ return errors.New(fmt.Sprintf("Global built-in function '%s' is already defined", name))
+ }
+ globalBuiltins[name] = &object.Builtin{Fn: fn}
+ return nil
+}
+
+func AssertArgCount(node ast.Node, args []object.Object, count int) *object.Error {
+ if len(args) != count {
+ return NewError(node, "wrong number of arguments. got=%d, want=%d",
+ len(args), count)
+ }
+ return nil
+}
+
+func AssertArgsType(node ast.Node, args []object.Object, types ...object.ObjectType) *object.Error {
+ if len(args) != len(types) {
+ return NewError(node, "wrong number of arguments. got=%d, want=%d",
+ len(args), len(types))
+ }
+ for i, t := range types {
+ if t != ANY_TYPE && args[i].Type() != t {
+ return NewError(node, "wrong type of argument n°%d. got=%s, want=%s",
+ i+1, args[i].Type(), t)
+ }
+ }
+ return nil
+}
+
+var globalBuiltins = map[string]*object.Builtin{
+ "exit": {
+ Fn: func(node ast.Node, args ...object.Object) object.Object {
+ if len(args) > 0 {
+ interrupt(args[0])
+ } else {
+ interrupt(nil)
+ }
+ return nil
+ },
+ },
+ "assert": {
+ Fn: func(node ast.Node, args ...object.Object) object.Object {
+ if oErr := AssertArgsType(node, args, object.BOOLEAN_OBJ); oErr != nil {
+ return oErr
+ }
+
+ if b := args[0].(*object.Boolean); !b.Value {
+ interrupt(&object.Error{
+ Message: "Assertion failed",
+ StackToken: []token.Token{node.TokenDetails()},
+ })
+ }
+ return TRUE
+ },
+ },
+ "pause": {
+ Fn: func(node ast.Node, args ...object.Object) object.Object {
+ if oErr := AssertArgsType(node, args, object.STRING_OBJ); oErr != nil {
+ return oErr
+ }
+
+ dArg := args[0].(*object.String)
+ d, err := time.ParseDuration(dArg.Value)
+ if err != nil {
+ return NewError(node, "%s", err.Error())
+ }
+ time.Sleep(d)
+
+ return NULL
+ },
+ },
+ "len": {
+ Fn: func(node ast.Node, args ...object.Object) object.Object {
+ if oErr := AssertArgCount(node, args, 1); oErr != nil {
+ return oErr
+ }
+
+ switch arg := args[0].(type) {
+ case *object.Array:
+ return &object.Integer{Value: int64(len(arg.Elements))}
+ case *object.String:
+ return &object.Integer{Value: int64(len(arg.Value))}
+ default:
+ return NewError(node, "wrong type of argument. got=%s, want %s or %s",
+ args[0].Type(), object.ARRAY_OBJ, object.STRING_OBJ)
+ }
+ },
+ },
+ "parseInt": {
+ Fn: func(node ast.Node, args ...object.Object) object.Object {
+ if oErr := AssertArgsType(node, args, object.STRING_OBJ); oErr != nil {
+ return oErr
+ }
+
+ dArg := args[0].(*object.String)
+ val, err := strconv.ParseInt(dArg.Value, 10, 64)
+ if err != nil {
+ return NewError(node, "%s", err.Error())
+ }
+ return &object.Integer{Value: val}
+ },
+ },
+ "parseFloat": {
+ Fn: func(node ast.Node, args ...object.Object) object.Object {
+ if oErr := AssertArgsType(node, args, object.STRING_OBJ); oErr != nil {
+ return oErr
+ }
+
+ dArg := args[0].(*object.String)
+ val, err := strconv.ParseFloat(dArg.Value, 64)
+ if err != nil {
+ return NewError(node, "%s", err.Error())
+ }
+ return &object.Float{Value: val}
+ },
+ },
+ "parseBool": {
+ Fn: func(node ast.Node, args ...object.Object) object.Object {
+ if oErr := AssertArgsType(node, args, object.STRING_OBJ); oErr != nil {
+ return oErr
+ }
+
+ dArg := args[0].(*object.String)
+ val, err := strconv.ParseBool(dArg.Value)
+ if err != nil {
+ return NewError(node, "%s", err.Error())
+ }
+ return &object.Boolean{Value: val}
+ },
+ },
+ "parseJson": {
+ Fn: func(node ast.Node, args ...object.Object) object.Object {
+ if oErr := AssertArgsType(node, args, object.STRING_OBJ); oErr != nil {
+ return oErr
+ }
+
+ str := args[0].(*object.String).Value
+ in := make(map[string]interface{})
+ err := json.Unmarshal([]byte(str), &in)
+ if err != nil {
+ return NewError(node, "%s", err.Error())
+ }
+
+ obj, err := object.ToObject(in)
+ if err != nil {
+ return NewError(node, "%s", err.Error())
+ }
+
+ return obj
+ },
+ },
+ "toJson": {
+ Fn: func(node ast.Node, args ...object.Object) object.Object {
+ if oErr := AssertArgsType(node, args, object.HASH_OBJ); oErr != nil {
+ return oErr
+ }
+
+ hash := args[0].(*object.Hash)
+ native, err := object.FromObject(hash)
+ if err != nil {
+ return NewError(node, "%s", err.Error())
+ }
+
+ jsonStr, err := json.Marshal(native)
+ if err != nil {
+ return NewError(node, "%s", err.Error())
+ }
+
+ return &object.String{Value: string(jsonStr)}
+ },
+ },
+ "urlParamsEncode": {
+ Fn: func(node ast.Node, args ...object.Object) object.Object {
+ if oErr := AssertArgsType(node, args, object.HASH_OBJ); oErr != nil {
+ return oErr
+ }
+
+ data := url.Values{}
+ hash := args[0].(*object.Hash)
+ for k, v := range hash.Pairs {
+ vStr, ok := v.(*object.String)
+ if !ok {
+ return NewError(node, "cannot url-encode value of type %s", v.Type())
+ }
+ data.Add(string(k), vStr.Value)
+ }
+
+ return &object.String{Value: data.Encode()}
+ },
+ },
+ "urlParamsDecode": {
+ Fn: func(node ast.Node, args ...object.Object) object.Object {
+ if oErr := AssertArgsType(node, args, object.STRING_OBJ); oErr != nil {
+ return oErr
+ }
+
+ encoded := args[0].(*object.String)
+ parsed, err := url.ParseQuery(encoded.Value)
+ if err != nil {
+ return NewError(node, "%s", err.Error())
+ }
+
+ resHeaders := make(map[object.HashKey]object.Object)
+ for k := range parsed {
+ resHeaders[object.HashKey(k)] = &object.String{Value: parsed.Get(k)}
+ }
+
+ return &object.Hash{
+ Pairs: resHeaders,
+ }
+ },
+ },
+ "first": {
+ Fn: func(node ast.Node, args ...object.Object) object.Object {
+ if oErr := AssertArgsType(node, args, object.ARRAY_OBJ); oErr != nil {
+ return oErr
+ }
+
+ arr := args[0].(*object.Array)
+ if len(arr.Elements) > 0 {
+ return arr.Elements[0]
+ }
+
+ return NULL
+ },
+ },
+ "last": {
+ Fn: func(node ast.Node, args ...object.Object) object.Object {
+ if oErr := AssertArgsType(node, args, object.ARRAY_OBJ); oErr != nil {
+ return oErr
+ }
+
+ arr := args[0].(*object.Array)
+ length := len(arr.Elements)
+ if length > 0 {
+ return arr.Elements[length-1]
+ }
+
+ return NULL
+ },
+ },
+ "arrayIndexOf": {
+ Fn: func(node ast.Node, args ...object.Object) object.Object {
+ if oErr := AssertArgsType(node, args, object.ARRAY_OBJ, ANY_TYPE); oErr != nil {
+ return oErr
+ }
+
+ arr := args[0].(*object.Array)
+ obj := args[1]
+ for i, v := range arr.Elements {
+ if v.Equals(obj) {
+ return &object.Integer{Value: int64(i)}
+ }
+ }
+
+ return &object.Integer{Value: int64(-1)}
+ },
+ },
+ "stringIndexOf": {
+ Fn: func(node ast.Node, args ...object.Object) object.Object {
+ if oErr := AssertArgsType(node, args, object.STRING_OBJ, object.STRING_OBJ); oErr != nil {
+ return oErr
+ }
+
+ str := args[0].(*object.String)
+ searched := args[1].(*object.String)
+
+ idx := strings.Index(str.Value, searched.Value)
+
+ return &object.Integer{Value: int64(idx)}
+ },
+ },
+ "split": {
+ Fn: func(node ast.Node, args ...object.Object) object.Object {
+ if oErr := AssertArgsType(node, args, object.STRING_OBJ, object.STRING_OBJ); oErr != nil {
+ return oErr
+ }
+
+ str := args[0].(*object.String)
+ separator := args[1].(*object.String)
+
+ split := strings.Split(str.Value, separator.Value)
+ elements := make([]object.Object, 0, len(split))
+ for _, v := range split {
+ elements = append(elements, &object.String{Value: v})
+ }
+
+ return &object.Array{Elements: elements}
+ },
+ },
+ "rest": {
+ Fn: func(node ast.Node, args ...object.Object) object.Object {
+ if oErr := AssertArgsType(node, args, object.ARRAY_OBJ); oErr != nil {
+ return oErr
+ }
+
+ arr := args[0].(*object.Array)
+ length := len(arr.Elements)
+ if length > 0 {
+ newElements := make([]object.Object, length-1, length-1)
+ copy(newElements, arr.Elements[1:length])
+ return &object.Array{Elements: newElements}
+ }
+
+ return NULL
+ },
+ },
+ "push": {
+ Fn: func(node ast.Node, args ...object.Object) object.Object {
+ if oErr := AssertArgsType(node, args, object.ARRAY_OBJ, ANY_TYPE); oErr != nil {
+ return oErr
+ }
+
+ arr := args[0].(*object.Array)
+ length := len(arr.Elements)
+
+ newElements := make([]object.Object, length+1, length+1)
+ copy(newElements, arr.Elements)
+ newElements[length] = args[1]
+
+ return &object.Array{Elements: newElements}
+ },
+ },
+ "merge": {
+ Fn: func(node ast.Node, args ...object.Object) object.Object {
+ if oErr := AssertArgsType(node, args, object.HASH_OBJ, object.HASH_OBJ); oErr != nil {
+ return oErr
+ }
+
+ hash1 := args[0].(*object.Hash)
+ hash2 := args[1].(*object.Hash)
+
+ newElements := make(map[object.HashKey]object.Object)
+ for k, v := range hash1.Pairs {
+ newElements[k] = v
+ }
+ for k, v := range hash2.Pairs {
+ newElements[k] = v
+ }
+
+ return &object.Hash{Pairs: newElements}
+ },
+ },
+ "keys": {
+ Fn: func(node ast.Node, args ...object.Object) object.Object {
+ if oErr := AssertArgsType(node, args, object.HASH_OBJ); oErr != nil {
+ return oErr
+ }
+
+ hash := args[0].(*object.Hash)
+
+ keys := make([]object.Object, 0, len(hash.Pairs))
+ for k := range hash.Pairs {
+ keys = append(keys, &object.String{Value: string(k)})
+ }
+
+ return &object.Array{Elements: keys}
+ },
+ },
+}
diff --git a/dsl/evaluator/builtins_test.go b/dsl/evaluator/builtins_test.go
new file mode 100644
index 0000000..c11e0bd
--- /dev/null
+++ b/dsl/evaluator/builtins_test.go
@@ -0,0 +1,940 @@
+package evaluator
+
+import (
+ "testing"
+ "time"
+
+ "github.com/ofux/deluge/dsl/ast"
+ "github.com/ofux/deluge/dsl/lexer"
+ "github.com/ofux/deluge/dsl/object"
+ "github.com/ofux/deluge/dsl/parser"
+ "github.com/stretchr/testify/assert"
+ "github.com/stretchr/testify/require"
+)
+
+func TestAddGlobalBuiltin(t *testing.T) {
+ t.Run("Add a global built-in function", func(t *testing.T) {
+ l := lexer.New("fooTest()")
+ p := parser.New(l)
+ program, ok := p.ParseProgram()
+ if !ok {
+ t.Errorf("Parsing errors: %v", p.Errors())
+ t.FailNow()
+ }
+ env := object.NewEnvironment()
+ ev := NewEvaluator()
+
+ err := AddGlobalBuiltin("fooTest", func(node ast.Node, args ...object.Object) object.Object {
+ return &object.Integer{Value: 42}
+ })
+ assert.NoError(t, err)
+
+ evaluated := ev.Eval(program, env)
+ testIntegerObject(t, evaluated, int64(42))
+
+ // Remove the test function from global built-in to avoid any interaction with other tests
+ delete(globalBuiltins, "fooTest")
+ })
+
+ t.Run("Add a global built-in function that already exists", func(t *testing.T) {
+
+ err := AddGlobalBuiltin("fooTest", func(node ast.Node, args ...object.Object) object.Object {
+ return &object.Integer{Value: 42}
+ })
+ assert.NoError(t, err)
+
+ err = AddGlobalBuiltin("fooTest", func(node ast.Node, args ...object.Object) object.Object {
+ return &object.Integer{Value: 42}
+ })
+ assert.Error(t, err)
+ assert.Equal(t, "Global built-in function 'fooTest' is already defined", err.Error())
+
+ // Remove the test function from global built-in to avoid any interaction with other tests
+ delete(globalBuiltins, "fooTest")
+ })
+}
+
+func TestBuiltinFunctions(t *testing.T) {
+ tests := []struct {
+ input string
+ expected interface{}
+ }{
+ {`len("")`, 0},
+ {`len("four")`, 4},
+ {`len("hello world")`, 11},
+ {`len(1)`, "wrong type of argument. got=INTEGER, want ARRAY or STRING"},
+ {`len("one", "two")`, "wrong number of arguments. got=2, want=1"},
+ {`len([1, 2, 3])`, 3},
+ {`len([])`, 0},
+ {`first([1, 2, 3])`, 1},
+ {`first([])`, nil},
+ {`first(1)`, "wrong type of argument n°1. got=INTEGER, want=ARRAY"},
+ {`first()`, "wrong number of arguments. got=0, want=1"},
+ {`last([1, 2, 3])`, 3},
+ {`last([])`, nil},
+ {`last(1)`, "wrong type of argument n°1. got=INTEGER, want=ARRAY"},
+ {`last()`, "wrong number of arguments. got=0, want=1"},
+ {`arrayIndexOf([1, 2, 3], 2)`, 1},
+ {`arrayIndexOf([], 2)`, -1},
+ {`arrayIndexOf(1, 2)`, "wrong type of argument n°1. got=INTEGER, want=ARRAY"},
+ {`arrayIndexOf()`, "wrong number of arguments. got=0, want=2"},
+ {`stringIndexOf("abcd", "b")`, 1},
+ {`stringIndexOf("abcd", "e")`, -1},
+ {`stringIndexOf(1, 2)`, "wrong type of argument n°1. got=INTEGER, want=STRING"},
+ {`stringIndexOf("1", 2)`, "wrong type of argument n°2. got=INTEGER, want=STRING"},
+ {`stringIndexOf()`, "wrong number of arguments. got=0, want=2"},
+ {`split("abcd", "b")`, []string{"a", "cd"}},
+ {`split("abcd", "e")`, []string{"abcd"}},
+ {`split("", "")`, []string{}},
+ {`split("abcd", "")`, []string{"a", "b", "c", "d"}},
+ {`split(1, 2)`, "wrong type of argument n°1. got=INTEGER, want=STRING"},
+ {`split("1", 2)`, "wrong type of argument n°2. got=INTEGER, want=STRING"},
+ {`split()`, "wrong number of arguments. got=0, want=2"},
+ {`rest([1, 2, 3])`, []int{2, 3}},
+ {`rest([])`, nil},
+ {`rest(1)`, "wrong type of argument n°1. got=INTEGER, want=ARRAY"},
+ {`rest()`, "wrong number of arguments. got=0, want=1"},
+ {`push([], 1)`, []int{1}},
+ {`push(1, 1)`, "wrong type of argument n°1. got=INTEGER, want=ARRAY"},
+ {`push()`, "wrong number of arguments. got=0, want=2"},
+ {`parseInt("12")`, 12},
+ {`parseInt("-12")`, -12},
+ {`parseInt("12.3")`, `strconv.ParseInt: parsing "12.3": invalid syntax`},
+ {`parseInt("a")`, `strconv.ParseInt: parsing "a": invalid syntax`},
+ {`parseInt(12)`, "wrong type of argument n°1. got=INTEGER, want=STRING"},
+ {`parseInt("1", "2")`, "wrong number of arguments. got=2, want=1"},
+ {`parseFloat("12")`, float64(12.0)},
+ {`parseFloat("-12")`, float64(-12.0)},
+ {`parseFloat("12.3")`, float64(12.3)},
+ {`parseFloat("a")`, `strconv.ParseFloat: parsing "a": invalid syntax`},
+ {`parseFloat(12)`, "wrong type of argument n°1. got=INTEGER, want=STRING"},
+ {`parseFloat("1", "2")`, "wrong number of arguments. got=2, want=1"},
+ {`parseBool("true")`, true},
+ {`parseBool("false")`, false},
+ {`parseBool("a")`, `strconv.ParseBool: parsing "a": invalid syntax`},
+ {`parseBool(true)`, "wrong type of argument n°1. got=BOOLEAN, want=STRING"},
+ {`parseBool("true", "false")`, "wrong number of arguments. got=2, want=1"},
+ }
+
+ for _, tt := range tests {
+ evaluated := testEval(t, tt.input)
+
+ switch expected := tt.expected.(type) {
+ case int:
+ testIntegerObject(t, evaluated, int64(expected))
+ case float64:
+ testFloatObject(t, evaluated, expected)
+ case nil:
+ testNullObject(t, evaluated)
+ case string:
+ errObj, ok := evaluated.(*object.Error)
+ if !ok {
+ t.Errorf("object is not Error. got=%T (%+v)",
+ evaluated, evaluated)
+ continue
+ }
+ if errObj.Message != expected {
+ t.Errorf("wrong error message. expected=%q, got=%q",
+ expected, errObj.Message)
+ }
+ case []int:
+ array, ok := evaluated.(*object.Array)
+ if !ok {
+ t.Errorf("obj not Array. got=%T (%+v)", evaluated, evaluated)
+ continue
+ }
+
+ if len(array.Elements) != len(expected) {
+ t.Errorf("wrong num of elements. want=%d, got=%d",
+ len(expected), len(array.Elements))
+ continue
+ }
+
+ for i, expectedElem := range expected {
+ testIntegerObject(t, array.Elements[i], int64(expectedElem))
+ }
+ case []string:
+ array, ok := evaluated.(*object.Array)
+ if !ok {
+ t.Errorf("obj not Array. got=%T (%+v)", evaluated, evaluated)
+ continue
+ }
+
+ if len(array.Elements) != len(expected) {
+ t.Errorf("wrong num of elements. want=%d, got=%d",
+ len(expected), len(array.Elements))
+ continue
+ }
+
+ for i, expectedElem := range expected {
+ testStringObject(t, array.Elements[i], expectedElem)
+ }
+ }
+ }
+}
+
+func TestBuiltinExit(t *testing.T) {
+ t.Run("Exit before end", func(t *testing.T) {
+ input := `
+ let a = 1;
+ a = 2;
+ exit();
+ a = 3;
+ undefinedFunc();
+ `
+
+ evaluated := testEval(t, input)
+ _, ok := evaluated.(*object.Null)
+ if !ok {
+ t.Fatalf("Eval didn't return Integer. got=%T (%+v)", evaluated, evaluated)
+ }
+ })
+
+ t.Run("Exit before end with argument", func(t *testing.T) {
+ input := `
+ let a = 1;
+ a = 2;
+ exit(a);
+ a = 3;
+ `
+
+ evaluated := testEval(t, input)
+ result, ok := evaluated.(*object.Integer)
+ if !ok {
+ t.Fatalf("Eval didn't return Integer. got=%T (%+v)", evaluated, evaluated)
+ }
+
+ if result.Value != 2 {
+ t.Fatalf("Eval didn't return right value. got=%d expected=%d", result, 2)
+ }
+ })
+
+ t.Run("Exit before end in sub-scopes", func(t *testing.T) {
+ input := `
+ let a = 1;
+ a = 2;
+ if (a == 2) {
+ function() {
+ exit();
+ }();
+ }
+ a = 3;
+ undefinedFunc();
+ `
+
+ evaluated := testEval(t, input)
+ _, ok := evaluated.(*object.Null)
+ if !ok {
+ t.Fatalf("Eval didn't return Integer. got=%T (%+v)", evaluated, evaluated)
+ }
+ })
+}
+
+func TestBuiltinAssert(t *testing.T) {
+ t.Run("Assert success", func(t *testing.T) {
+ input := `
+ let a = 1;
+ a = 2;
+ assert(a == 2);
+ a = 3;
+ a;
+ `
+
+ evaluated := testEval(t, input)
+ result, ok := evaluated.(*object.Integer)
+ if !ok {
+ t.Fatalf("Eval didn't return Integer. got=%T (%+v)", evaluated, evaluated)
+ }
+
+ if result.Value != 3 {
+ t.Fatalf("Eval didn't return right value. got=%d expected=%d", result, 3)
+ }
+ })
+
+ t.Run("Assert failure", func(t *testing.T) {
+ input := `
+ let a = 1;
+ a = 2;
+ assert(a == 20);
+ a = 3;
+ undefinedFunc();
+ `
+
+ evaluated := testEval(t, input)
+ result, ok := evaluated.(*object.Error)
+ if !ok {
+ t.Fatalf("Eval didn't return Error. got=%T (%+v)", evaluated, evaluated)
+ }
+
+ if result.Message != "Assertion failed" {
+ t.Fatalf("Bad error message. Expected '%s', got '%s'", "Assertion failed", result.Message)
+ }
+
+ if result.StackToken[0].Line != 4 {
+ t.Fatalf("Wrong line for error. Expected %d, got %d", 4, result.StackToken[0].Line)
+ }
+ })
+
+ t.Run("Assert fail in sub-scopes", func(t *testing.T) {
+ input := `
+ let a = 1;
+ a = 2;
+ if (a == 2) {
+ function() {
+ assert(false);
+ }();
+ }
+ a = 3;
+ undefinedFunc();
+ `
+
+ evaluated := testEval(t, input)
+ result, ok := evaluated.(*object.Error)
+ if !ok {
+ t.Fatalf("Eval didn't return Error. got=%T (%+v)", evaluated, evaluated)
+ }
+
+ if result.Message != "Assertion failed" {
+ t.Fatalf("Bad error message. Expected '%s', got '%s'", "Assertion failed", result.Message)
+ }
+
+ if result.StackToken[0].Line != 6 {
+ t.Fatalf("Wrong line for error. Expected %d, got %d", 6, result.StackToken[0].Line)
+ }
+ })
+
+ t.Run("Assert without argument", func(t *testing.T) {
+ input := `
+ assert();
+ `
+
+ evaluated := testEval(t, input)
+ result, ok := evaluated.(*object.Error)
+ if !ok {
+ t.Fatalf("Eval didn't return Error. got=%T (%+v)", evaluated, evaluated)
+ }
+
+ if result.Message != "wrong number of arguments. got=0, want=1" {
+ t.Fatalf("Bad error message. Expected '%s', got '%s'", "wrong number of arguments. got=0, want=1", result.Message)
+ }
+ })
+
+ t.Run("Assert with bad argument", func(t *testing.T) {
+ input := `
+ assert(3);
+ `
+
+ evaluated := testEval(t, input)
+ result, ok := evaluated.(*object.Error)
+ if !ok {
+ t.Fatalf("Eval didn't return Error. got=%T (%+v)", evaluated, evaluated)
+ }
+
+ if result.Message != "wrong type of argument n°1. got=INTEGER, want=BOOLEAN" {
+ t.Fatalf("Bad error message. Expected '%s', got '%s'", "wrong type of argument n°1. got=INTEGER, want=BOOLEAN", result.Message)
+ }
+ })
+}
+
+func TestBuiltinPause(t *testing.T) {
+ t.Run("Pause 2ms", func(t *testing.T) {
+ input := `
+ let a = 1;
+ pause("31ms");
+ a = 2;
+ a;
+ `
+ start := time.Now()
+ evaluated := testEval(t, input)
+ elaspedTime := time.Now().Sub(start)
+ if elaspedTime.Nanoseconds() < 30000000 {
+ t.Fatalf("Eval didn't last at least 30000000ns.")
+ }
+ result, ok := evaluated.(*object.Integer)
+ if !ok {
+ t.Fatalf("Eval didn't return Integer. got=%T (%+v)", evaluated, evaluated)
+ }
+
+ if result.Value != 2 {
+ t.Fatalf("Eval didn't return right value. got=%d expected=%d", result, 2)
+ }
+ })
+
+ t.Run("Pause with bad type of argument", func(t *testing.T) {
+ input := `
+ pause(2);
+ `
+
+ evaluated := testEval(t, input)
+ result, ok := evaluated.(*object.Error)
+
+ require.True(t, ok)
+ assert.Equal(t, "wrong type of argument n°1. got=INTEGER, want=STRING", result.Message)
+ })
+
+ t.Run("Pause with bad duration", func(t *testing.T) {
+ input := `
+ pause("2");
+ `
+
+ evaluated := testEval(t, input)
+ result, ok := evaluated.(*object.Error)
+
+ require.True(t, ok)
+ assert.Equal(t, "time: missing unit in duration \"2\"", result.Message)
+ })
+
+ t.Run("Pause with no argument", func(t *testing.T) {
+ input := `
+ pause();
+ `
+
+ evaluated := testEval(t, input)
+ result, ok := evaluated.(*object.Error)
+
+ require.True(t, ok)
+ assert.Equal(t, "wrong number of arguments. got=0, want=1", result.Message)
+ })
+}
+
+func TestBuiltinMerge(t *testing.T) {
+ t.Run("Merge 2 simple hashes", func(t *testing.T) {
+ input := `
+ merge({
+ "a": 1
+ }, {
+ "b": 2,
+ "c": 3
+ })
+ `
+ evaluated := testEval(t, input)
+
+ result, ok := evaluated.(*object.Hash)
+ require.True(t, ok)
+
+ assert.Equal(t, map[object.HashKey]object.Object{
+ "a": &object.Integer{1},
+ "b": &object.Integer{2},
+ "c": &object.Integer{3},
+ }, result.Pairs)
+ })
+
+ t.Run("Merge 2 hashes with common keys", func(t *testing.T) {
+ input := `
+ merge({
+ "a": 1
+ }, {
+ "a": 2,
+ "b": 3
+ })
+ `
+ evaluated := testEval(t, input)
+
+ result, ok := evaluated.(*object.Hash)
+ require.True(t, ok)
+
+ assert.Equal(t, map[object.HashKey]object.Object{
+ "a": &object.Integer{2},
+ "b": &object.Integer{3},
+ }, result.Pairs)
+ })
+
+ t.Run("Merge simple hash with an empty hash", func(t *testing.T) {
+ input := `
+ merge({
+ "a": 1
+ }, {
+ })
+ `
+ evaluated := testEval(t, input)
+
+ result, ok := evaluated.(*object.Hash)
+ require.True(t, ok)
+
+ assert.Equal(t, map[object.HashKey]object.Object{
+ "a": &object.Integer{1},
+ }, result.Pairs)
+ })
+
+ t.Run("Merge an empty hash with a simple hash", func(t *testing.T) {
+ input := `
+ merge({
+ }, {
+ "a": 1
+ })
+ `
+ evaluated := testEval(t, input)
+
+ result, ok := evaluated.(*object.Hash)
+ require.True(t, ok)
+
+ assert.Equal(t, map[object.HashKey]object.Object{
+ "a": &object.Integer{1},
+ }, result.Pairs)
+ })
+
+ t.Run("Merge 2 empty hashes", func(t *testing.T) {
+ input := `
+ merge({
+ }, {
+ })
+ `
+ evaluated := testEval(t, input)
+
+ result, ok := evaluated.(*object.Hash)
+ require.True(t, ok)
+
+ assert.Equal(t, map[object.HashKey]object.Object{}, result.Pairs)
+ })
+
+ t.Run("Merge with no argument", func(t *testing.T) {
+ input := `
+ merge();
+ `
+
+ evaluated := testEval(t, input)
+ result, ok := evaluated.(*object.Error)
+
+ require.True(t, ok)
+ assert.Equal(t, "wrong number of arguments. got=0, want=2", result.Message)
+ })
+
+ t.Run("Merge with bad 1st argument", func(t *testing.T) {
+ input := `
+ merge("a", {});
+ `
+
+ evaluated := testEval(t, input)
+ result, ok := evaluated.(*object.Error)
+
+ require.True(t, ok)
+ assert.Equal(t, "wrong type of argument n°1. got=STRING, want=HASH", result.Message)
+ })
+
+ t.Run("Merge with bad 2nd argument", func(t *testing.T) {
+ input := `
+ merge({}, "a");
+ `
+
+ evaluated := testEval(t, input)
+ result, ok := evaluated.(*object.Error)
+
+ require.True(t, ok)
+ assert.Equal(t, "wrong type of argument n°2. got=STRING, want=HASH", result.Message)
+ })
+}
+
+func TestBuiltinKeys(t *testing.T) {
+ t.Run("Keys of a simple hash", func(t *testing.T) {
+ input := `
+ keys({
+ "a": 1,
+ "b": 2
+ })
+ `
+ evaluated := testEval(t, input)
+
+ result, ok := evaluated.(*object.Array)
+ require.True(t, ok)
+
+ assert.Len(t, result.Elements, 2)
+ assert.Contains(t, result.Elements, &object.String{"a"})
+ assert.Contains(t, result.Elements, &object.String{"b"})
+ })
+
+ t.Run("Keys of a hash with integers as keys", func(t *testing.T) {
+ input := `
+ keys({
+ "a": 1,
+ "b": 2,
+ 3: 3
+ })
+ `
+ evaluated := testEval(t, input)
+
+ result, ok := evaluated.(*object.Array)
+ require.True(t, ok)
+
+ assert.Len(t, result.Elements, 3)
+ assert.Contains(t, result.Elements, &object.String{"a"})
+ assert.Contains(t, result.Elements, &object.String{"b"})
+ assert.Contains(t, result.Elements, &object.String{"3"})
+ })
+
+ t.Run("Use the result of Keys", func(t *testing.T) {
+ input := `
+ let h = {
+ "a": 5,
+ "b": 8,
+ 42: 1
+ };
+
+ let k = keys(h);
+ let sum = 0;
+ for (let i=0; i < len(k); i++) {
+ sum += h[k[i]];
+ }
+ sum
+ `
+ evaluated := testEval(t, input)
+
+ result, ok := evaluated.(*object.Integer)
+ require.True(t, ok)
+
+ assert.Equal(t, int64(14), result.Value)
+ })
+
+ t.Run("Keys with no argument", func(t *testing.T) {
+ input := `
+ keys();
+ `
+
+ evaluated := testEval(t, input)
+ result, ok := evaluated.(*object.Error)
+
+ require.True(t, ok)
+ assert.Equal(t, "wrong number of arguments. got=0, want=1", result.Message)
+ })
+
+ t.Run("Keys with bad argument", func(t *testing.T) {
+ input := `
+ keys("a");
+ `
+
+ evaluated := testEval(t, input)
+ result, ok := evaluated.(*object.Error)
+
+ require.True(t, ok)
+ assert.Equal(t, "wrong type of argument n°1. got=STRING, want=HASH", result.Message)
+ })
+}
+
+func TestBuiltinParseJson(t *testing.T) {
+ t.Run("Parse json", func(t *testing.T) {
+ input := `
+parseJson(` + "`" + `{
+ "a": "foo",
+ "b": 42,
+ "c": {
+ "ca": "cfoo",
+ "cb": 43,
+ "cc": [
+ 1,
+ 2
+ ],
+ "cd": {
+ "cda": "bar"
+ }
+ },
+ "d": [
+ "da",
+ 43,
+ [],
+ {},
+ true,
+ 12.3
+ ],
+ "e": 1.2,
+ "f": false
+}` + "`" + `)
+`
+
+ evaluated := testEval(t, input)
+ if err, ok := evaluated.(*object.Error); ok {
+ t.Fatal(err.Message, err.StackToken)
+ }
+
+ result, ok := evaluated.(*object.Hash)
+ require.True(t, ok)
+
+ deepEqual := object.DeepEquals(&object.Hash{
+ Pairs: map[object.HashKey]object.Object{
+ object.HashKey("a"): &object.String{"foo"},
+ object.HashKey("b"): &object.Integer{42},
+ object.HashKey("c"): &object.Hash{
+ Pairs: map[object.HashKey]object.Object{
+ object.HashKey("ca"): &object.String{"cfoo"},
+ object.HashKey("cb"): &object.Integer{43},
+ object.HashKey("cc"): &object.Array{Elements: []object.Object{
+ &object.Integer{1},
+ &object.Integer{2},
+ }},
+ object.HashKey("cd"): &object.Hash{
+ Pairs: map[object.HashKey]object.Object{
+ object.HashKey("cda"): &object.String{"bar"},
+ },
+ },
+ },
+ },
+ object.HashKey("d"): &object.Array{Elements: []object.Object{
+ &object.String{"da"},
+ &object.Integer{43},
+ &object.Array{Elements: []object.Object{}},
+ &object.Hash{Pairs: map[object.HashKey]object.Object{}},
+ &object.Boolean{true},
+ &object.Float{12.3},
+ }},
+ object.HashKey("e"): &object.Float{1.2},
+ object.HashKey("f"): &object.Boolean{false},
+ },
+ }, result)
+
+ assert.True(t, deepEqual)
+ })
+
+ t.Run("Parse json with no argument", func(t *testing.T) {
+ input := `
+ parseJson();
+ `
+
+ evaluated := testEval(t, input)
+ result, ok := evaluated.(*object.Error)
+
+ require.True(t, ok)
+ assert.Equal(t, "wrong number of arguments. got=0, want=1", result.Message)
+ })
+
+ t.Run("Parse json with bad argument", func(t *testing.T) {
+ input := `
+ parseJson({
+ "a":"b"
+ });
+ `
+
+ evaluated := testEval(t, input)
+ result, ok := evaluated.(*object.Error)
+
+ require.True(t, ok)
+ assert.Equal(t, "wrong type of argument n°1. got=HASH, want=STRING", result.Message)
+ })
+
+ t.Run("Parse json with bad json", func(t *testing.T) {
+ input := `
+ parseJson("(!)");
+ `
+
+ evaluated := testEval(t, input)
+ result, ok := evaluated.(*object.Error)
+
+ require.True(t, ok)
+ assert.Equal(t, "invalid character '(' looking for beginning of value", result.Message)
+ })
+}
+
+func TestBuiltinToJson(t *testing.T) {
+ t.Run("To json", func(t *testing.T) {
+ input := `
+toJson({
+ "a": "foo",
+ "b": 42,
+ "c": {
+ "ca": "cfoo",
+ "cb": 43,
+ "cc": [
+ 1,
+ 2
+ ],
+ "cd": {
+ "cda": "bar"
+ }
+ },
+ "d": [
+ "da",
+ 43,
+ [],
+ {},
+ true,
+ 12.3
+ ],
+ "e": 1.2,
+ "f": false
+})
+`
+
+ evaluated := testEval(t, input)
+ if err, ok := evaluated.(*object.Error); ok {
+ t.Fatal(err.Message, err.StackToken)
+ }
+
+ result, ok := evaluated.(*object.String)
+ require.True(t, ok)
+ assert.Equal(t, `{"a":"foo","b":42,"c":{"ca":"cfoo","cb":43,"cc":[1,2],"cd":{"cda":"bar"}},"d":["da",43,[],{},true,12.3],"e":1.2,"f":false}`, result.Value)
+ })
+
+ t.Run("To json with no argument", func(t *testing.T) {
+ input := `
+ toJson();
+ `
+
+ evaluated := testEval(t, input)
+ result, ok := evaluated.(*object.Error)
+
+ require.True(t, ok)
+ assert.Equal(t, "wrong number of arguments. got=0, want=1", result.Message)
+ })
+
+ t.Run("To json with bad argument", func(t *testing.T) {
+ input := `
+ toJson("{}");
+ `
+
+ evaluated := testEval(t, input)
+ result, ok := evaluated.(*object.Error)
+
+ require.True(t, ok)
+ assert.Equal(t, "wrong type of argument n°1. got=STRING, want=HASH", result.Message)
+ })
+
+ t.Run("To json with un-serializable data", func(t *testing.T) {
+ input := `
+ toJson({
+ "a": function(){}
+ });
+ `
+
+ evaluated := testEval(t, input)
+ result, ok := evaluated.(*object.Error)
+
+ require.True(t, ok)
+ assert.Equal(t, "Cannot convert Object of type *object.Function to a native type", result.Message)
+ })
+}
+
+func TestBuiltinFromJsonToJson(t *testing.T) {
+ t.Run("To json", func(t *testing.T) {
+ input := `toJson(parseJson(toJson(parseJson(toJson({"a":"foo","b":42,"c":{"ca":"cfoo","cb":43,"cc":[1,2],"cd":{"cda":"bar"}},"d":["da",43,[],{},true,12.3],"e":1.2,"f":false})))))`
+
+ evaluated := testEval(t, input)
+ if err, ok := evaluated.(*object.Error); ok {
+ t.Fatal(err.Message, err.StackToken)
+ }
+
+ result, ok := evaluated.(*object.String)
+ require.True(t, ok)
+ assert.Equal(t, `{"a":"foo","b":42,"c":{"ca":"cfoo","cb":43,"cc":[1,2],"cd":{"cda":"bar"}},"d":["da",43,[],{},true,12.3],"e":1.2,"f":false}`, result.Value)
+ })
+}
+
+func TestBuiltinUrlEncode(t *testing.T) {
+ t.Run("URL encode", func(t *testing.T) {
+ input := `
+urlParamsEncode({
+ "a": "foo",
+ "b&c": "42",
+})
+`
+
+ evaluated := testEval(t, input)
+ if err, ok := evaluated.(*object.Error); ok {
+ t.Fatal(err.Message, err.StackToken)
+ }
+
+ result, ok := evaluated.(*object.String)
+ require.True(t, ok)
+ assert.Equal(t, `a=foo&b%26c=42`, result.Value)
+ })
+
+ t.Run("URL encode with no argument", func(t *testing.T) {
+ input := `
+ urlParamsEncode();
+ `
+
+ evaluated := testEval(t, input)
+ result, ok := evaluated.(*object.Error)
+
+ require.True(t, ok)
+ assert.Equal(t, "wrong number of arguments. got=0, want=1", result.Message)
+ })
+
+ t.Run("URL encode with bad argument", func(t *testing.T) {
+ input := `
+ urlParamsEncode("{}");
+ `
+
+ evaluated := testEval(t, input)
+ result, ok := evaluated.(*object.Error)
+
+ require.True(t, ok)
+ assert.Equal(t, "wrong type of argument n°1. got=STRING, want=HASH", result.Message)
+ })
+
+ t.Run("URL encode with data that cannot be encoded", func(t *testing.T) {
+ input := `
+ urlParamsEncode({
+ "a": {}
+ });
+ `
+
+ evaluated := testEval(t, input)
+ result, ok := evaluated.(*object.Error)
+
+ require.True(t, ok)
+ assert.Equal(t, "cannot url-encode value of type HASH", result.Message)
+ })
+}
+
+func TestBuiltinUrlDecode(t *testing.T) {
+ t.Run("URL decode", func(t *testing.T) {
+ input := `
+urlParamsDecode("a=foo&b%26c=42")
+`
+
+ evaluated := testEval(t, input)
+ if err, ok := evaluated.(*object.Error); ok {
+ t.Fatal(err.Message, err.StackToken)
+ }
+
+ result, ok := evaluated.(*object.Hash)
+ require.True(t, ok)
+
+ deepEqual := object.DeepEquals(&object.Hash{
+ Pairs: map[object.HashKey]object.Object{
+ object.HashKey("a"): &object.String{"foo"},
+ object.HashKey("b&c"): &object.String{"42"},
+ },
+ }, result)
+
+ assert.True(t, deepEqual)
+ })
+
+ t.Run("URL decode with no argument", func(t *testing.T) {
+ input := `
+ urlParamsDecode();
+ `
+
+ evaluated := testEval(t, input)
+ result, ok := evaluated.(*object.Error)
+
+ require.True(t, ok)
+ assert.Equal(t, "wrong number of arguments. got=0, want=1", result.Message)
+ })
+
+ t.Run("URL decode with bad argument", func(t *testing.T) {
+ input := `
+ urlParamsDecode({});
+ `
+
+ evaluated := testEval(t, input)
+ result, ok := evaluated.(*object.Error)
+
+ require.True(t, ok)
+ assert.Equal(t, "wrong type of argument n°1. got=HASH, want=STRING", result.Message)
+ })
+
+ t.Run("URL decode with data that cannot be decoded", func(t *testing.T) {
+ input := `
+ urlParamsDecode("");
+ `
+
+ evaluated := testEval(t, input)
+
+ result, ok := evaluated.(*object.Hash)
+ require.True(t, ok)
+
+ deepEqual := object.DeepEquals(&object.Hash{
+ Pairs: map[object.HashKey]object.Object{},
+ }, result)
+
+ assert.True(t, deepEqual)
+ })
+}
diff --git a/dsl/evaluator/evaluator.go b/dsl/evaluator/evaluator.go
new file mode 100644
index 0000000..32247a6
--- /dev/null
+++ b/dsl/evaluator/evaluator.go
@@ -0,0 +1,1031 @@
+package evaluator
+
+import (
+ "errors"
+ "fmt"
+ "strconv"
+
+ "github.com/ofux/deluge/dsl/ast"
+ "github.com/ofux/deluge/dsl/object"
+ "github.com/ofux/deluge/dsl/token"
+)
+
+var (
+ NULL = &object.Null{}
+ TRUE = &object.Boolean{Value: true}
+ FALSE = &object.Boolean{Value: false}
+)
+
+type Evaluator struct {
+ builtins map[string]*object.Builtin
+}
+
+type evalInterruption struct {
+ returnedVal object.Object
+}
+
+func NewEvaluator() *Evaluator {
+ ev := &Evaluator{
+ builtins: make(map[string]*object.Builtin),
+ }
+ return ev
+}
+
+func (e *Evaluator) AddBuiltin(name string, fn object.BuiltinFunction) error {
+ if _, ok := e.builtins[name]; ok {
+ return errors.New(fmt.Sprintf("Bult-in function '%s' is already defined", name))
+ }
+ e.builtins[name] = &object.Builtin{Fn: fn}
+ return nil
+}
+
+func (e *Evaluator) Eval(node ast.Node, env *object.Environment) (returnedVal object.Object) {
+ defer func() {
+ if r := recover(); r != nil {
+ if interruption, ok := r.(evalInterruption); ok {
+ returnedVal = interruption.returnedVal
+ } else {
+ panic(r) // Something else happened, repanic!
+ }
+ }
+ }()
+ returnedVal = e.eval(node, env)
+ return
+}
+
+func interrupt(arg object.Object) {
+ var interruption evalInterruption
+ if arg != nil {
+ interruption = evalInterruption{
+ returnedVal: arg,
+ }
+ } else {
+ interruption = evalInterruption{
+ returnedVal: NULL,
+ }
+ }
+ panic(interruption)
+}
+
+func (e *Evaluator) eval(node ast.Node, env *object.Environment) object.Object {
+ switch node := node.(type) {
+
+ // Statements
+ case *ast.Program:
+ return e.evalProgram(node, env)
+
+ case *ast.BlockStatement:
+ env := object.NewEnclosedEnvironment(env)
+ return e.evalBlockStatement(node, env)
+
+ case *ast.ExpressionStatement:
+ return e.eval(node.Expression, env)
+
+ case *ast.ReturnStatement:
+ val := e.eval(node.ReturnValue, env)
+ if IsError(val) {
+ return val
+ }
+ return &object.ReturnValue{Value: val}
+
+ case *ast.LetStatement:
+ val := e.eval(node.Value, env)
+ if IsError(val) {
+ return val
+ }
+ if !env.Add(node.Name.Value, val) {
+ return NewError(node.Name, "variable %s redeclared in this block", node.Name.Value)
+ }
+
+ // Expressions
+ case *ast.Null:
+ return NULL
+
+ case *ast.IntegerLiteral:
+ return &object.Integer{Value: node.Value}
+
+ case *ast.FloatLiteral:
+ return &object.Float{Value: node.Value}
+
+ case *ast.StringLiteral:
+ return &object.String{Value: node.Value}
+
+ case *ast.Boolean:
+ return nativeBoolToBooleanObject(node.Value)
+
+ case *ast.PrefixExpression:
+ right := e.eval(node.Right, env)
+ if IsError(right) {
+ return right
+ }
+ return e.evalPrefixExpression(node, right)
+
+ case *ast.InfixExpression:
+ return e.evalInfixExpression(node, env)
+
+ case *ast.AssignmentExpression:
+ return e.evalAssignmentExpression(node, env)
+
+ case *ast.PostAssignmentExpression:
+ return e.evalPostAssignmentExpression(node, env)
+
+ case *ast.IfStatement:
+ return e.evalIfStatement(node, env)
+
+ case *ast.ForStatement:
+ return e.evalForStatement(node, env)
+
+ case *ast.Identifier:
+ return e.evalIdentifier(node, env)
+
+ case *ast.FunctionLiteral:
+ params := node.Parameters
+ body := node.Body
+ return &object.Function{Parameters: params, Env: env, Body: body}
+
+ case *ast.CallExpression:
+ function := e.eval(node.Function, env)
+ if IsError(function) {
+ return function
+ }
+
+ args := e.evalExpressions(node.Arguments, env)
+ if len(args) == 1 && IsError(args[0]) {
+ return args[0]
+ }
+
+ funcResult := e.applyFunction(node, function, args)
+ if IsError(funcResult) {
+ funcErr := funcResult.(*object.Error)
+ funcErr.AddCallToStack(node)
+ }
+ return funcResult
+
+ case *ast.ArrayLiteral:
+ elements := e.evalExpressions(node.Elements, env)
+ if len(elements) == 1 && IsError(elements[0]) {
+ return elements[0]
+ }
+ return &object.Array{Elements: elements}
+
+ case *ast.IndexExpression:
+ left := e.eval(node.Left, env)
+ if IsError(left) {
+ return left
+ }
+ index := e.eval(node.Index, env)
+ if IsError(index) {
+ return index
+ }
+ return e.evalIndexExpression(node, left, index)
+
+ case *ast.HashLiteral:
+ return e.evalHashLiteral(node, env)
+
+ }
+
+ return nil
+}
+
+func (e *Evaluator) evalProgram(program *ast.Program, env *object.Environment) object.Object {
+ var result object.Object
+
+ for _, statement := range program.Statements {
+ result = e.eval(statement, env)
+
+ switch result := result.(type) {
+ case *object.ReturnValue:
+ return result.Value
+ case *object.Error:
+ return result
+ }
+ }
+
+ return result
+}
+
+func (e *Evaluator) evalBlockStatement(
+ block *ast.BlockStatement,
+ env *object.Environment,
+) object.Object {
+ var result object.Object
+
+ for _, statement := range block.Statements {
+ result = e.eval(statement, env)
+
+ if result != nil {
+ rt := result.Type()
+ if rt == object.RETURN_VALUE_OBJ || rt == object.ERROR_OBJ {
+ return result
+ }
+ }
+ }
+
+ return result
+}
+
+func nativeBoolToBooleanObject(input bool) *object.Boolean {
+ if input {
+ return TRUE
+ }
+ return FALSE
+}
+
+func (e *Evaluator) evalPrefixExpression(prefixExp *ast.PrefixExpression, right object.Object) object.Object {
+ operator := prefixExp.Operator
+ switch operator {
+ case "!":
+ return e.evalBangOperatorExpression(right)
+ case "-":
+ return e.evalMinusPrefixOperatorExpression(prefixExp, right)
+ default:
+ return NewError(prefixExp, "unknown operator: %s%s", operator, right.Type())
+ }
+}
+
+func (e *Evaluator) evalInfixExpression(
+ node *ast.InfixExpression,
+ env *object.Environment,
+) object.Object {
+ operator := node.Operator
+
+ // Eval && and || operators first, because right and left must be evaluated under certain circumstances only
+ if operator == "&&" || operator == "||" {
+ return e.evalBooleanInfixExpression(node, env)
+ }
+
+ // Eval any other operators
+ left := e.eval(node.Left, env)
+ if IsError(left) {
+ return left
+ }
+ right := e.eval(node.Right, env)
+ if IsError(right) {
+ return right
+ }
+
+ switch {
+ case object.IsInteger(left) && object.IsInteger(right):
+ return e.evalIntegerInfixExpression(node, left, right)
+ case object.IsNumeric(left) && object.IsNumeric(right):
+ return e.evalFloatInfixExpression(node, left, right)
+ case left.Type() == object.STRING_OBJ || right.Type() == object.STRING_OBJ:
+ return e.evalStringInfixExpression(node, left, right)
+ case operator == "==":
+ return nativeBoolToBooleanObject(left == right)
+ case operator == "!=":
+ return nativeBoolToBooleanObject(left != right)
+ case left.Type() != right.Type():
+ return NewError(node, "type mismatch: %s %s %s",
+ left.Type(), operator, right.Type())
+ default:
+ return NewError(node, "unknown operator: %s %s %s",
+ left.Type(), operator, right.Type())
+ }
+}
+
+func (e *Evaluator) evalBangOperatorExpression(right object.Object) object.Object {
+ switch right {
+ case TRUE:
+ return FALSE
+ case FALSE:
+ return TRUE
+ case NULL:
+ return TRUE
+ default:
+ return FALSE
+ }
+}
+
+func (e *Evaluator) evalMinusPrefixOperatorExpression(node ast.Node, right object.Object) object.Object {
+ switch right := right.(type) {
+ case *object.Integer:
+ return &object.Integer{Value: -right.Value}
+ case *object.Float:
+ return &object.Float{Value: -right.Value}
+ default:
+ return NewError(node, "unknown operator: -%s", right.Type())
+ }
+}
+
+func (e *Evaluator) evalIntegerInfixExpression(
+ node *ast.InfixExpression,
+ left, right object.Object,
+) object.Object {
+ operator := node.Operator
+ leftVal := left.(*object.Integer).Value
+ rightVal := right.(*object.Integer).Value
+
+ switch operator {
+ case "+":
+ return &object.Integer{Value: leftVal + rightVal}
+ case "-":
+ return &object.Integer{Value: leftVal - rightVal}
+ case "*":
+ return &object.Integer{Value: leftVal * rightVal}
+ case "/":
+ return &object.Integer{Value: leftVal / rightVal}
+ case "%":
+ return &object.Integer{Value: leftVal % rightVal}
+ case "<":
+ return nativeBoolToBooleanObject(leftVal < rightVal)
+ case ">":
+ return nativeBoolToBooleanObject(leftVal > rightVal)
+ case "<=":
+ return nativeBoolToBooleanObject(leftVal <= rightVal)
+ case ">=":
+ return nativeBoolToBooleanObject(leftVal >= rightVal)
+ case "==":
+ return nativeBoolToBooleanObject(leftVal == rightVal)
+ case "!=":
+ return nativeBoolToBooleanObject(leftVal != rightVal)
+ default:
+ return NewError(node, "unknown operator: %s %s %s",
+ left.Type(), operator, right.Type())
+ }
+}
+
+func (e *Evaluator) evalFloatInfixExpression(
+ node *ast.InfixExpression,
+ left, right object.Object,
+) object.Object {
+ operator := node.Operator
+ var leftVal, rightVal float64
+ switch left := left.(type) {
+ case *object.Integer:
+ leftVal = float64(left.Value)
+ case *object.Float:
+ leftVal = left.Value
+ }
+ switch right := right.(type) {
+ case *object.Integer:
+ rightVal = float64(right.Value)
+ case *object.Float:
+ rightVal = right.Value
+ }
+
+ switch operator {
+ case "+":
+ return &object.Float{Value: leftVal + rightVal}
+ case "-":
+ return &object.Float{Value: leftVal - rightVal}
+ case "*":
+ return &object.Float{Value: leftVal * rightVal}
+ case "/":
+ return &object.Float{Value: leftVal / rightVal}
+ case "<":
+ return nativeBoolToBooleanObject(leftVal < rightVal)
+ case ">":
+ return nativeBoolToBooleanObject(leftVal > rightVal)
+ case "<=":
+ return nativeBoolToBooleanObject(leftVal <= rightVal)
+ case ">=":
+ return nativeBoolToBooleanObject(leftVal >= rightVal)
+ case "==":
+ return nativeBoolToBooleanObject(leftVal == rightVal)
+ case "!=":
+ return nativeBoolToBooleanObject(leftVal != rightVal)
+ default:
+ return NewError(node, "unknown operator: %s %s %s",
+ left.Type(), operator, right.Type())
+ }
+}
+
+func (e *Evaluator) evalStringInfixExpression(
+ node *ast.InfixExpression,
+ left, right object.Object,
+) object.Object {
+ operator := node.Operator
+ leftStr, err1 := convertToString(node, left)
+ rightStr, err2 := convertToString(node, right)
+
+ switch operator {
+ case "+":
+ if err1 != nil {
+ return err1
+ }
+ if err2 != nil {
+ return err2
+ }
+ return &object.String{Value: leftStr.Value + rightStr.Value}
+ case "==":
+ if err1 != nil || err2 != nil {
+ return nativeBoolToBooleanObject(false)
+ }
+ return nativeBoolToBooleanObject(leftStr.Value == rightStr.Value)
+ case "!=":
+ if err1 != nil || err2 != nil {
+ return nativeBoolToBooleanObject(false)
+ }
+ return nativeBoolToBooleanObject(leftStr.Value != rightStr.Value)
+ default:
+ return NewError(node, "unknown operator: %s %s %s",
+ left.Type(), operator, right.Type())
+ }
+}
+
+func (e *Evaluator) evalBooleanInfixExpression(
+ node *ast.InfixExpression,
+ env *object.Environment,
+) object.Object {
+ operator := node.Operator
+ switch {
+ case operator == "&&":
+ left := e.eval(node.Left, env)
+ if IsError(left) {
+ return left
+ }
+ if left.Type() != object.BOOLEAN_OBJ {
+ return NewError(node, "unknown operator: %s %s %s",
+ left.Type(), operator, object.BOOLEAN_OBJ)
+ }
+ if !left.(*object.Boolean).Value {
+ return nativeBoolToBooleanObject(false)
+ }
+ // left is true, so let's eval right
+ right := e.eval(node.Right, env)
+ if IsError(right) {
+ return right
+ }
+ if right.Type() != object.BOOLEAN_OBJ {
+ return NewError(node, "unknown operator: %s %s %s",
+ object.BOOLEAN_OBJ, operator, right.Type())
+ }
+ return nativeBoolToBooleanObject(right.(*object.Boolean).Value)
+
+ case operator == "||":
+ left := e.eval(node.Left, env)
+ if IsError(left) {
+ return left
+ }
+ if left.Type() != object.BOOLEAN_OBJ {
+ return NewError(node, "unknown operator: %s %s %s",
+ left.Type(), operator, object.BOOLEAN_OBJ)
+ }
+ if left.(*object.Boolean).Value {
+ return nativeBoolToBooleanObject(true)
+ }
+ // left is false, so let's eval right
+ right := e.eval(node.Right, env)
+ if IsError(right) {
+ return right
+ }
+ if right.Type() != object.BOOLEAN_OBJ {
+ return NewError(node, "unknown operator: %s %s %s",
+ object.BOOLEAN_OBJ, operator, right.Type())
+ }
+ return nativeBoolToBooleanObject(right.(*object.Boolean).Value)
+ }
+
+ panic(errors.New(fmt.Sprintf("evalBooleanInfixExpression has been called with operator %s", operator)))
+}
+
+func (e *Evaluator) evalAssignmentExpression(
+ node *ast.AssignmentExpression,
+ env *object.Environment,
+) object.Object {
+ operator := node.Operator
+ right := e.eval(node.Right, env)
+ if IsError(right) {
+ return right
+ }
+
+ switch assigned := node.Left.(type) {
+ case *ast.Identifier:
+ return evalAssignmentIdentifierExpression(node, env, assigned, right)
+ case *ast.IndexExpression:
+ left := e.eval(assigned.Left, env)
+ if IsError(left) {
+ return left
+ }
+ index := e.eval(assigned.Index, env)
+ if IsError(index) {
+ return index
+ }
+ return evalAssignmentIndexExpression(node, left, index, right)
+ default:
+ return NewError(node, "unknown operator: %s %s %s",
+ node.Left.TokenDetails().Type, operator, right.Type())
+ }
+}
+
+func evalAssignmentIdentifierExpression(
+ node *ast.AssignmentExpression,
+ env *object.Environment,
+ identifier *ast.Identifier,
+ value object.Object,
+) object.Object {
+ return doAssignment(node, value,
+ func() object.Object {
+ v, ok := env.Get(identifier.Value)
+ if !ok {
+ return NewError(node, "identifier not found: %s", identifier.Value)
+ }
+ return v
+ },
+ func(v object.Object) object.Object {
+ if !env.Set(identifier.Value, v) {
+ return NewError(node, "identifier not found: %s", identifier.Value)
+ }
+ return v
+ },
+ )
+}
+
+func evalAssignmentIndexExpression(node *ast.AssignmentExpression, left, index, value object.Object) object.Object {
+ switch {
+ case left.Type() == object.ARRAY_OBJ && index.Type() == object.INTEGER_OBJ:
+ return evalAssignmentArrayIndexExpression(node, left, index, value)
+ case left.Type() == object.HASH_OBJ:
+ return evalAssignmentHashIndexExpression(node, left, index, value)
+ default:
+ return NewError(node, "index operator not supported: %s", left.Type())
+ }
+}
+
+func evalAssignmentArrayIndexExpression(node *ast.AssignmentExpression, array, index, value object.Object) object.Object {
+ arrayObject := array.(*object.Array)
+ idx := index.(*object.Integer).Value
+ max := int64(len(arrayObject.Elements) - 1)
+
+ if idx < 0 || idx > max {
+ return NewError(node, "index %d out of bounds [%d, %d]", idx, 0, max)
+ }
+
+ return doAssignment(node, value,
+ func() object.Object {
+ return arrayObject.Elements[idx]
+ },
+ func(v object.Object) object.Object {
+ arrayObject.Elements[idx] = v
+ return v
+ },
+ )
+}
+
+func evalAssignmentHashIndexExpression(node *ast.AssignmentExpression, hash, index, value object.Object) object.Object {
+ hashObject := hash.(*object.Hash)
+ if hashObject.IsImmutable {
+ return NewError(node, "hash is immutable, you cannot modify it")
+ }
+
+ keyObj, ok := index.(object.Hashable)
+ if !ok {
+ return NewError(node, "unusable as hash key: %s", index.Type())
+ }
+ key := keyObj.HashKey()
+
+ return doAssignment(node, value,
+ func() object.Object {
+ v, ok := hashObject.Pairs[key]
+ if !ok {
+ return NewError(node, "undefined hash key: %s", key)
+ }
+ return v
+ },
+ func(v object.Object) object.Object {
+ hashObject.Pairs[key] = v
+ return v
+ },
+ )
+}
+
+func doAssignment(
+ node *ast.AssignmentExpression,
+ value object.Object,
+ getter func() object.Object,
+ setter func(object.Object) object.Object,
+) object.Object {
+ operator := node.Operator
+ if operator == "=" {
+ return setter(value)
+ }
+
+ oldValue := getter()
+ if IsError(oldValue) {
+ return oldValue
+ }
+
+ switch oldV := oldValue.(type) {
+ case *object.Integer:
+ switch newV := value.(type) {
+ case *object.Integer:
+ switch operator {
+ case "+=":
+ return setter(&object.Integer{Value: oldV.Value + newV.Value})
+ case "-=":
+ return setter(&object.Integer{Value: oldV.Value - newV.Value})
+ case "*=":
+ return setter(&object.Integer{Value: oldV.Value * newV.Value})
+ case "/=":
+ return setter(&object.Integer{Value: oldV.Value / newV.Value})
+ default:
+ return NewError(node, "unknown operator %s %s %s", oldV.Type(), operator, newV.Type())
+ }
+ case *object.Float:
+ switch operator {
+ case "+=":
+ return setter(&object.Float{Value: float64(oldV.Value) + newV.Value})
+ case "-=":
+ return setter(&object.Float{Value: float64(oldV.Value) - newV.Value})
+ case "*=":
+ return setter(&object.Float{Value: float64(oldV.Value) * newV.Value})
+ case "/=":
+ return setter(&object.Float{Value: float64(oldV.Value) / newV.Value})
+ default:
+ return NewError(node, "unknown operator %s %s %s", oldV.Type(), operator, newV.Type())
+ }
+ }
+ case *object.Float:
+ switch newV := value.(type) {
+ case *object.Integer:
+ switch operator {
+ case "+=":
+ return setter(&object.Float{Value: oldV.Value + float64(newV.Value)})
+ case "-=":
+ return setter(&object.Float{Value: oldV.Value - float64(newV.Value)})
+ case "*=":
+ return setter(&object.Float{Value: oldV.Value * float64(newV.Value)})
+ case "/=":
+ return setter(&object.Float{Value: oldV.Value / float64(newV.Value)})
+ default:
+ return NewError(node, "unknown operator %s %s %s", oldV.Type(), operator, newV.Type())
+ }
+ case *object.Float:
+ switch operator {
+ case "+=":
+ return setter(&object.Float{Value: oldV.Value + newV.Value})
+ case "-=":
+ return setter(&object.Float{Value: oldV.Value - newV.Value})
+ case "*=":
+ return setter(&object.Float{Value: oldV.Value * newV.Value})
+ case "/=":
+ return setter(&object.Float{Value: oldV.Value / newV.Value})
+ default:
+ return NewError(node, "unknown operator %s %s %s", oldV.Type(), operator, newV.Type())
+ }
+ }
+ case *object.String:
+ if operator != "+=" {
+ return NewError(node, "unknown operator %s %s %s", oldValue.Type(), operator, value.Type())
+ }
+ switch newV := value.(type) {
+ case *object.String:
+ return setter(&object.String{Value: oldV.Value + newV.Value})
+ case *object.Integer:
+ return setter(&object.String{Value: oldV.Value + strconv.FormatInt(newV.Value, 10)})
+ case *object.Float:
+ return setter(&object.String{Value: oldV.Value + strconv.FormatFloat(newV.Value, 'f', -1, 64)})
+ case *object.Boolean:
+ return setter(&object.String{Value: oldV.Value + strconv.FormatBool(newV.Value)})
+ default:
+ return NewError(node, "unknown operator %s %s %s", oldV.Type(), operator, newV.Type())
+ }
+ }
+ return NewError(node, "unknown operator %s %s %s", oldValue.Type(), operator, value.Type())
+}
+
+func (e *Evaluator) evalPostAssignmentExpression(
+ node *ast.PostAssignmentExpression,
+ env *object.Environment,
+) object.Object {
+ operator := node.Operator
+
+ switch assigned := node.Left.(type) {
+ case *ast.Identifier:
+ return evalPostAssignmentIdentifierExpression(node, env, assigned)
+ case *ast.IndexExpression:
+ left := e.eval(assigned.Left, env)
+ if IsError(left) {
+ return left
+ }
+ index := e.eval(assigned.Index, env)
+ if IsError(index) {
+ return index
+ }
+ return evalPostAssignmentIndexExpression(node, left, index)
+ default:
+ return NewError(node, "unknown operator: %s %s",
+ node.Left.TokenDetails().Type, operator)
+ }
+}
+
+func evalPostAssignmentIdentifierExpression(
+ node *ast.PostAssignmentExpression,
+ env *object.Environment,
+ identifier *ast.Identifier,
+) object.Object {
+ v, ok := env.Get(identifier.Value)
+ if !ok {
+ return NewError(node, "identifier not found: %s", identifier.Value)
+ }
+ return doPostAssignment(node, v)
+}
+
+func evalPostAssignmentIndexExpression(node *ast.PostAssignmentExpression, left, index object.Object) object.Object {
+ switch {
+ case left.Type() == object.ARRAY_OBJ && index.Type() == object.INTEGER_OBJ:
+ return evalPostAssignmentArrayIndexExpression(node, left, index)
+ case left.Type() == object.HASH_OBJ:
+ return evalPostAssignmentHashIndexExpression(node, left, index)
+ default:
+ return NewError(node, "index operator not supported: %s", left.Type())
+ }
+}
+
+func evalPostAssignmentArrayIndexExpression(node *ast.PostAssignmentExpression, array, index object.Object) object.Object {
+ arrayObject := array.(*object.Array)
+ idx := index.(*object.Integer).Value
+ max := int64(len(arrayObject.Elements) - 1)
+
+ if idx < 0 || idx > max {
+ return NewError(node, "index %d out of bounds [%d, %d]", idx, 0, max)
+ }
+
+ return doPostAssignment(node, arrayObject.Elements[idx])
+}
+
+func evalPostAssignmentHashIndexExpression(node *ast.PostAssignmentExpression, hash, index object.Object) object.Object {
+ hashObject := hash.(*object.Hash)
+ if hashObject.IsImmutable {
+ return NewError(node, "hash is immutable, you cannot modify it")
+ }
+
+ keyObj, ok := index.(object.Hashable)
+ if !ok {
+ return NewError(node, "unusable as hash key: %s", index.Type())
+ }
+ key := keyObj.HashKey()
+
+ v, ok := hashObject.Pairs[key]
+ if !ok {
+ return NewError(node, "undefined hash key: %s", key)
+ }
+
+ return doPostAssignment(node, v)
+}
+
+func doPostAssignment(
+ node *ast.PostAssignmentExpression,
+ assigned object.Object,
+) object.Object {
+ vInt, ok := assigned.(*object.Integer)
+ if !ok {
+ return NewError(node, "unknown operator %s %s", assigned.Type(), node.Operator)
+ }
+ oldValue := &object.Integer{Value: vInt.Value}
+ switch node.Operator {
+ case "++":
+ vInt.Value++
+ case "--":
+ vInt.Value--
+ }
+ return oldValue
+}
+
+func (e *Evaluator) evalIfStatement(
+ ie *ast.IfStatement,
+ env *object.Environment,
+) object.Object {
+ condition := e.eval(ie.Condition, env)
+ if IsError(condition) {
+ return condition
+ }
+
+ if isTruthy(condition) {
+ return e.eval(ie.Consequence, env)
+ } else if ie.Alternative != nil {
+ return e.eval(ie.Alternative, env)
+ } else {
+ return NULL
+ }
+}
+
+func (e *Evaluator) evalForStatement(
+ fo *ast.ForStatement,
+ env *object.Environment,
+) object.Object {
+ env = object.NewEnclosedEnvironment(env)
+
+ init := e.eval(fo.Initialization, env)
+ if IsError(init) {
+ return init
+ }
+ condition := e.eval(fo.Condition, env)
+ if IsError(condition) {
+ return condition
+ }
+
+ var loop object.Object = NULL
+
+ for isTruthy(condition) {
+ loop = e.eval(fo.Loop, env)
+ if IsError(loop) {
+ return loop
+ }
+ afterthought := e.eval(fo.Afterthought, env)
+ if IsError(afterthought) {
+ return afterthought
+ }
+ condition = e.eval(fo.Condition, env)
+ if IsError(condition) {
+ return condition
+ }
+ }
+
+ return loop
+}
+
+func (e *Evaluator) evalIdentifier(
+ node *ast.Identifier,
+ env *object.Environment,
+) object.Object {
+ if val, ok := env.Get(node.Value); ok {
+ return val
+ }
+
+ if builtin, ok := e.builtins[node.Value]; ok {
+ return builtin
+ }
+
+ if builtin, ok := globalBuiltins[node.Value]; ok {
+ return builtin
+ }
+
+ return NewError(node, "identifier not found: %s", node.Value)
+}
+
+func (e *Evaluator) evalExpressions(
+ exps []ast.Expression,
+ env *object.Environment,
+) []object.Object {
+ var result []object.Object
+
+ for _, exp := range exps {
+ evaluated := e.eval(exp, env)
+ if IsError(evaluated) {
+ return []object.Object{evaluated}
+ }
+ result = append(result, evaluated)
+ }
+
+ return result
+}
+
+func (e *Evaluator) applyFunction(node ast.Node, fn object.Object, args []object.Object) object.Object {
+ switch fn := fn.(type) {
+
+ case *object.Function:
+ if len(fn.Parameters) != len(args) {
+ return NewError(node, "wrong number of arguments: expected %d, got %d", len(fn.Parameters), len(args))
+ }
+ extendedEnv, err := extendFunctionEnv(fn, args)
+ if err != nil {
+ return err
+ }
+ evaluated := e.eval(fn.Body, extendedEnv)
+ return unwrapReturnValue(evaluated)
+
+ case *object.Builtin:
+ return fn.Fn(node, args...)
+
+ default:
+ return NewError(node, "not a function: %s", fn.Type())
+ }
+}
+
+func (e *Evaluator) evalIndexExpression(node ast.Node, left, index object.Object) object.Object {
+ switch {
+ case left.Type() == object.ARRAY_OBJ && index.Type() == object.INTEGER_OBJ:
+ return evalArrayIndexExpression(node, left, index)
+ case left.Type() == object.HASH_OBJ:
+ return evalHashIndexExpression(node, left, index)
+ default:
+ return NewError(node, "index operator not supported: %s", left.Type())
+ }
+}
+
+func evalArrayIndexExpression(node ast.Node, array, index object.Object) object.Object {
+ arrayObject := array.(*object.Array)
+ idx := index.(*object.Integer).Value
+ max := int64(len(arrayObject.Elements) - 1)
+
+ if idx < 0 || idx > max {
+ return NewError(node, "index %d out of bounds [%d, %d]", idx, 0, max)
+ }
+
+ return arrayObject.Elements[idx]
+}
+
+func evalHashIndexExpression(node ast.Node, hash, index object.Object) object.Object {
+ hashObject := hash.(*object.Hash)
+
+ key, ok := index.(object.Hashable)
+ if !ok {
+ return NewError(node, "unusable as hash key: %s", index.Type())
+ }
+
+ v, ok := hashObject.Pairs[key.HashKey()]
+ if !ok {
+ return NULL
+ }
+
+ return v
+}
+
+func (e *Evaluator) evalHashLiteral(
+ node *ast.HashLiteral,
+ env *object.Environment,
+) object.Object {
+ pairs := make(map[object.HashKey]object.Object)
+
+ for keyNode, valueNode := range node.Pairs {
+ key := e.eval(keyNode, env)
+ if IsError(key) {
+ return key
+ }
+
+ hashKey, ok := key.(object.Hashable)
+ if !ok {
+ return NewError(keyNode, "unusable as hash key: %s", key.Type())
+ }
+
+ value := e.eval(valueNode, env)
+ if IsError(value) {
+ return value
+ }
+
+ hashed := hashKey.HashKey()
+ pairs[hashed] = value
+ }
+
+ return &object.Hash{Pairs: pairs}
+}
+
+func isTruthy(obj object.Object) bool {
+ switch obj {
+ case NULL:
+ return false
+ case TRUE:
+ return true
+ case FALSE:
+ return false
+ default:
+ return true
+ }
+}
+
+func NewError(node ast.Node, format string, a ...interface{}) *object.Error {
+ return &object.Error{
+ Message: fmt.Sprintf(format, a...),
+ StackToken: []token.Token{
+ node.TokenDetails(),
+ },
+ }
+}
+
+func IsError(obj object.Object) bool {
+ return obj != nil && obj.Type() == object.ERROR_OBJ && obj.(*object.Error) != nil
+}
+
+func extendFunctionEnv(
+ fn *object.Function,
+ args []object.Object,
+) (*object.Environment, *object.Error) {
+ env := object.NewEnclosedEnvironment(fn.Env)
+
+ for paramIdx, param := range fn.Parameters {
+ if !env.Add(param.Value, args[paramIdx]) {
+ return nil, NewError(param, "")
+ }
+ }
+
+ return env, nil
+}
+
+func unwrapReturnValue(obj object.Object) object.Object {
+ if returnValue, ok := obj.(*object.ReturnValue); ok {
+ return returnValue.Value
+ }
+
+ return obj
+}
+
+func convertToString(
+ node *ast.InfixExpression,
+ obj object.Object,
+) (*object.String, *object.Error) {
+ switch obj := obj.(type) {
+ case *object.Integer:
+ return &object.String{Value: strconv.FormatInt(obj.Value, 10)}, nil
+ case *object.Float:
+ return &object.String{Value: strconv.FormatFloat(obj.Value, 'f', -1, 64)}, nil
+ case *object.Boolean:
+ return &object.String{Value: strconv.FormatBool(obj.Value)}, nil
+ case *object.String:
+ return obj, nil
+ default:
+ return nil, NewError(node, "cannot convert value of type %s to %s",
+ obj.Type(), object.STRING_OBJ)
+ }
+}
diff --git a/dsl/evaluator/evaluator_errors_test.go b/dsl/evaluator/evaluator_errors_test.go
new file mode 100644
index 0000000..f51247a
--- /dev/null
+++ b/dsl/evaluator/evaluator_errors_test.go
@@ -0,0 +1,897 @@
+package evaluator
+
+import (
+ "github.com/ofux/deluge/dsl/ast"
+ "github.com/ofux/deluge/dsl/object"
+ "github.com/ofux/deluge/dsl/token"
+ "testing"
+)
+
+type expectedError struct {
+ input string
+ expectedMessage string
+}
+
+func TestErrorHandling(t *testing.T) {
+ tests := []expectedError{
+ {
+ "5 + true;",
+ "type mismatch: INTEGER + BOOLEAN",
+ },
+ {
+ "5.0 + true;",
+ "type mismatch: FLOAT + BOOLEAN",
+ },
+ {
+ "5 + true; 5;",
+ "type mismatch: INTEGER + BOOLEAN",
+ },
+ {
+ "-true",
+ "unknown operator: -BOOLEAN",
+ },
+ {
+ "true + false;",
+ "unknown operator: BOOLEAN + BOOLEAN",
+ },
+ {
+ "true + false + true + false;",
+ "unknown operator: BOOLEAN + BOOLEAN",
+ },
+ {
+ "5; true + false; 5",
+ "unknown operator: BOOLEAN + BOOLEAN",
+ },
+ {
+ `"Hello" - "World"`,
+ "unknown operator: STRING - STRING",
+ },
+ {
+ "if (10 > 1) { true + false; }",
+ "unknown operator: BOOLEAN + BOOLEAN",
+ },
+ {
+ "if (false) { a } else if (b) { c }",
+ "identifier not found: b",
+ },
+ {
+ "if (false) { a } else if (true) { c }",
+ "identifier not found: c",
+ },
+ {
+ "if (true) { a } else if (b) { c }",
+ "identifier not found: a",
+ },
+ {
+ `
+if (10 > 1) {
+ if (10 > 1) {
+ return true + false;
+ }
+
+ return 1;
+}
+`,
+ "unknown operator: BOOLEAN + BOOLEAN",
+ },
+ {
+ "foobar",
+ "identifier not found: foobar",
+ },
+ {
+ `{"name": "Monkey"}[function(x) { x }];`,
+ "unusable as hash key: FUNCTION",
+ },
+ {
+ `999[1]`,
+ "index operator not supported: INTEGER",
+ },
+ {
+ `"a" < "b"`,
+ "unknown operator: STRING < STRING",
+ },
+ {
+ `"a" > "b"`,
+ "unknown operator: STRING > STRING",
+ },
+ {
+ `"a" <= "b"`,
+ "unknown operator: STRING <= STRING",
+ },
+ {
+ `"a" >= "b"`,
+ "unknown operator: STRING >= STRING",
+ },
+ {
+ `"foo" || false`,
+ "unknown operator: STRING || BOOLEAN",
+ },
+ {
+ `false || "foo"`,
+ "unknown operator: BOOLEAN || STRING",
+ },
+ {
+ `"foo" && true`,
+ "unknown operator: STRING && BOOLEAN",
+ },
+ {
+ `true && "foo"`,
+ "unknown operator: BOOLEAN && STRING",
+ },
+ {
+ `"foo" || "bar"`,
+ "unknown operator: STRING || BOOLEAN",
+ },
+ {
+ `"foo" && "bar"`,
+ "unknown operator: STRING && BOOLEAN",
+ },
+ {
+ `-"foo";`,
+ "unknown operator: -STRING",
+ },
+ {
+ `-x;`,
+ "identifier not found: x",
+ },
+ {
+ `x();`,
+ "identifier not found: x",
+ },
+ {
+ `let f=function(){}; f(x);`,
+ "identifier not found: x",
+ },
+ {
+ `[1, 2, x]`,
+ "identifier not found: x",
+ },
+ {
+ `x[1]`,
+ "identifier not found: x",
+ },
+ {
+ `[1, 2][x]`,
+ "identifier not found: x",
+ },
+ {
+ `f() && true`,
+ "identifier not found: f",
+ },
+ {
+ `true && f()`,
+ "identifier not found: f",
+ },
+ {
+ `f() || true`,
+ "identifier not found: f",
+ },
+ {
+ `false || f()`,
+ "identifier not found: f",
+ },
+ {
+ `3.0 % 2`,
+ "unknown operator: FLOAT % INTEGER",
+ },
+ {
+ `[1,2,3][-1]`,
+ "index -1 out of bounds [0, 2]",
+ },
+ {
+ `[1,2,3][3]`,
+ "index 3 out of bounds [0, 2]",
+ },
+ }
+
+ for _, tt := range tests {
+ testError(t, tt)
+ }
+}
+
+func TestStringAssignmentErrorHandling(t *testing.T) {
+ tests := []expectedError{
+ {
+ `let a = "a"+null; a`,
+ "cannot convert value of type NULL to STRING",
+ },
+ {
+ `let a = "a"; a += null; a`,
+ "unknown operator STRING += NULL",
+ },
+ }
+
+ for _, tt := range tests {
+ testError(t, tt)
+ }
+}
+
+func TestAssignmentIdentifierErrorHandling(t *testing.T) {
+ tests := []expectedError{
+ {
+ `a = 3`,
+ "identifier not found: a",
+ },
+ {
+ `let a = 3; let a = 4;`,
+ "variable a redeclared in this block",
+ },
+ {
+ `
+ function() {
+ let b = 1;
+ }();
+ b = 2;
+ `,
+ "identifier not found: b",
+ },
+ {
+ `let a = "x"; a--`,
+ "unknown operator STRING --",
+ },
+ {
+ `let a = "x"; a++`,
+ "unknown operator STRING ++",
+ },
+ {
+ `let a = "x"; a -= 2`,
+ "unknown operator STRING -= INTEGER",
+ },
+ {
+ `let a = "x"; a *= 2`,
+ "unknown operator STRING *= INTEGER",
+ },
+ {
+ `let a = "x"; a /= 2`,
+ "unknown operator STRING /= INTEGER",
+ },
+ {
+ `let a = 4; a += "1"`,
+ "unknown operator INTEGER += STRING",
+ },
+ {
+ `let a = 4; a -= "1"`,
+ "unknown operator INTEGER -= STRING",
+ },
+ {
+ `let a = 4; a *= "1"`,
+ "unknown operator INTEGER *= STRING",
+ },
+ {
+ `let a = 4; a /= "1"`,
+ "unknown operator INTEGER /= STRING",
+ },
+ {
+ `let a = b;`,
+ "identifier not found: b",
+ },
+ {
+ `let a = 1; a = b;`,
+ "identifier not found: b",
+ },
+ {
+ `let a = 1; a += x`,
+ "identifier not found: x",
+ },
+ {
+ `x += 1`,
+ "identifier not found: x",
+ },
+ {
+ `let a = "foo"; a += x`,
+ "identifier not found: x",
+ },
+ {
+ `let a = [1, 2]; a += 1`,
+ "unknown operator ARRAY += INTEGER",
+ },
+ {
+ `let a = null; a += 1`,
+ "unknown operator NULL += INTEGER",
+ },
+ {
+ `x++`,
+ "identifier not found: x",
+ },
+ {
+ `1.3++`,
+ "unknown operator: FLOAT ++",
+ },
+ {
+ `2.5--`,
+ "unknown operator: FLOAT --",
+ },
+ }
+
+ for _, tt := range tests {
+ testError(t, tt)
+ }
+}
+
+func TestAssignmentArrayIndexErrorHandling(t *testing.T) {
+ tests := []expectedError{
+ {
+ `let a = ["x"]; a[0]--`,
+ "unknown operator STRING --",
+ },
+ {
+ `let a = ["x"]; a[0]++`,
+ "unknown operator STRING ++",
+ },
+ {
+ `let a = ["x"]; a[0] -= 2`,
+ "unknown operator STRING -= INTEGER",
+ },
+ {
+ `let a = ["x"]; a[0] *= 2`,
+ "unknown operator STRING *= INTEGER",
+ },
+ {
+ `let a = ["x"]; a[0] /= 2`,
+ "unknown operator STRING /= INTEGER",
+ },
+ {
+ `let a = [4]; a[0] += "1"`,
+ "unknown operator INTEGER += STRING",
+ },
+ {
+ `let a = [4]; a[0] -= "1"`,
+ "unknown operator INTEGER -= STRING",
+ },
+ {
+ `let a = [4]; a[0] *= "1"`,
+ "unknown operator INTEGER *= STRING",
+ },
+ {
+ `let a = [4]; a[0] /= "1"`,
+ "unknown operator INTEGER /= STRING",
+ },
+ {
+ `let a = [4]; a[0] = b;`,
+ "identifier not found: b",
+ },
+ {
+ `let a = [4]; a[0] += x`,
+ "identifier not found: x",
+ },
+ {
+ `let a = [[1, 2]]; a[0] += 1`,
+ "unknown operator ARRAY += INTEGER",
+ },
+ {
+ `let a = [null]; a[0] += 1`,
+ "unknown operator NULL += INTEGER",
+ },
+ {
+ `let a = [4]; a[x] = 1`,
+ "identifier not found: x",
+ },
+ {
+ `let a = [4]; a[x]--`,
+ "identifier not found: x",
+ },
+ {
+ `let a = [4]; a[1] = 1`,
+ "index 1 out of bounds [0, 0]",
+ },
+ {
+ `let a = [4]; a[-1] = 1`,
+ "index -1 out of bounds [0, 0]",
+ },
+ {
+ `let a = [4]; a[-1]++`,
+ "index -1 out of bounds [0, 0]",
+ },
+ {
+ `a[0] = 1`,
+ "identifier not found: a",
+ },
+ {
+ `a[0]++`,
+ "identifier not found: a",
+ },
+ {
+ `[1,2] = 4`,
+ "unknown operator: [ = INTEGER",
+ },
+ {
+ `[1,2]++`,
+ "unknown operator: [ ++",
+ },
+ }
+
+ for _, tt := range tests {
+ testError(t, tt)
+ }
+}
+
+func TestAssignmentHashIndexErrorHandling(t *testing.T) {
+ tests := []expectedError{
+ {
+ `let a = {"x":"y"}; a["x"]--`,
+ "unknown operator STRING --",
+ },
+ {
+ `let a = {"x":"y"}; a["x"]++`,
+ "unknown operator STRING ++",
+ },
+ {
+ `let a = {"x":"y"}; a["x"] -= 2`,
+ "unknown operator STRING -= INTEGER",
+ },
+ {
+ `let a = {"x":"y"}; a["x"] *= 2`,
+ "unknown operator STRING *= INTEGER",
+ },
+ {
+ `let a = {"x":"y"}; a["x"] /= 2`,
+ "unknown operator STRING /= INTEGER",
+ },
+ {
+ `let a = {"x":4}; a["x"] += "1"`,
+ "unknown operator INTEGER += STRING",
+ },
+ {
+ `let a = {"x":4}; a["x"] -= "1"`,
+ "unknown operator INTEGER -= STRING",
+ },
+ {
+ `let a = {"x":4}; a["x"] *= "1"`,
+ "unknown operator INTEGER *= STRING",
+ },
+ {
+ `let a = {"x":4}; a["x"] /= "1"`,
+ "unknown operator INTEGER /= STRING",
+ },
+ {
+ `let a = {"x":4}; a["x"] = b;`,
+ "identifier not found: b",
+ },
+ {
+ `let a = {"x":4}; a["x"] += x`,
+ "identifier not found: x",
+ },
+ {
+ `let a = {"x":{}}; a["x"] += 1`,
+ "unknown operator HASH += INTEGER",
+ },
+ {
+ `let a = {"x":null}; a["x"] += 1`,
+ "unknown operator NULL += INTEGER",
+ },
+ {
+ `let a = {"x":4}; a[x] = 1`,
+ "identifier not found: x",
+ },
+ {
+ `let a = {"x":4}; a[x]++`,
+ "identifier not found: x",
+ },
+ {
+ `a["x"] = 1`,
+ "identifier not found: a",
+ },
+ {
+ `a["x"]--`,
+ "identifier not found: a",
+ },
+ {
+ `{"x":4} = 4`,
+ "unknown operator: { = INTEGER",
+ },
+ {
+ `let a = {"x":4}; a[function(){}] = 1`,
+ "unusable as hash key: FUNCTION",
+ },
+ {
+ `let a = {}; a["x"] += 1`,
+ "undefined hash key: x",
+ },
+ {
+ `let a = {"x":4}; a[function(){}]--`,
+ "unusable as hash key: FUNCTION",
+ },
+ {
+ `let a = {}; a["x"]++`,
+ "undefined hash key: x",
+ },
+ {
+ `{} = 4`,
+ "unknown operator: { = INTEGER",
+ },
+ {
+ `{}++`,
+ "unknown operator: { ++",
+ },
+ }
+
+ for _, tt := range tests {
+ testError(t, tt)
+ }
+}
+
+func TestAssignmentOfImmutableErrorHandling(t *testing.T) {
+ tests := []expectedError{
+ {
+ token.ASSIGN,
+ "hash is immutable, you cannot modify it",
+ },
+ {
+ token.ASSIGN_DEC,
+ "hash is immutable, you cannot modify it",
+ },
+ {
+ token.ASSIGN_INC,
+ "hash is immutable, you cannot modify it",
+ },
+ {
+ token.ASSIGN_DIV,
+ "hash is immutable, you cannot modify it",
+ },
+ {
+ token.ASSIGN_MULT,
+ "hash is immutable, you cannot modify it",
+ },
+ }
+
+ for _, tt := range tests {
+ immutableHash := &object.Hash{
+ Pairs: map[object.HashKey]object.Object{
+ object.HashKey("x"): &object.Integer{1},
+ },
+ IsImmutable: true,
+ }
+ index := &object.String{"x"}
+ value := &object.Integer{42}
+ node := &ast.AssignmentExpression{
+ Operator: tt.input,
+ Token: token.Token{
+ Type: token.TokenType(tt.input),
+ Column: 1,
+ Line: 3,
+ Literal: tt.input,
+ },
+ }
+
+ evaluated := evalAssignmentHashIndexExpression(node, immutableHash, index, value)
+
+ errObj, ok := evaluated.(*object.Error)
+ if !ok {
+ t.Errorf("no error object returned. got=%T(%+v)",
+ evaluated, evaluated)
+ return
+ }
+
+ if errObj.Message != tt.expectedMessage {
+ t.Errorf("wrong error message. expected=%q, got=%q",
+ tt.expectedMessage, errObj.Message)
+ }
+ if errObj.StackToken[0].Line != 3 || errObj.StackToken[0].Column != 1 {
+ t.Errorf("wrong stack of tokens %v",
+ errObj.StackToken)
+ }
+ }
+}
+
+func TestReassignmentOfImmutableErrorHandling(t *testing.T) {
+ tests := []expectedError{
+ {
+ token.ASSIGN_INC1,
+ "hash is immutable, you cannot modify it",
+ },
+ {
+ token.ASSIGN_DEC1,
+ "hash is immutable, you cannot modify it",
+ },
+ }
+
+ for _, tt := range tests {
+ immutableHash := &object.Hash{
+ Pairs: map[object.HashKey]object.Object{
+ object.HashKey("x"): &object.Integer{1},
+ },
+ IsImmutable: true,
+ }
+ index := &object.String{"x"}
+ node := &ast.PostAssignmentExpression{
+ Operator: tt.input,
+ Token: token.Token{
+ Type: token.TokenType(tt.input),
+ Column: 1,
+ Line: 3,
+ Literal: tt.input,
+ },
+ }
+
+ evaluated := evalPostAssignmentHashIndexExpression(node, immutableHash, index)
+
+ errObj, ok := evaluated.(*object.Error)
+ if !ok {
+ t.Errorf("no error object returned. got=%T(%+v)",
+ evaluated, evaluated)
+ return
+ }
+
+ if errObj.Message != tt.expectedMessage {
+ t.Errorf("wrong error message. expected=%q, got=%q",
+ tt.expectedMessage, errObj.Message)
+ }
+ if errObj.StackToken[0].Line != 3 || errObj.StackToken[0].Column != 1 {
+ t.Errorf("wrong stack of tokens %v",
+ errObj.StackToken)
+ }
+ }
+}
+
+func TestForLoopsErrorHandling(t *testing.T) {
+ tests := []expectedError{
+ {
+ `for (f(); true; true) {}`,
+ "identifier not found: f",
+ },
+ {
+ `for (let i=0; f(); true) {}`,
+ "identifier not found: f",
+ },
+ {
+ `for (let i=0; true; f()) {}`,
+ "identifier not found: f",
+ },
+ {
+ `
+ let f=function(){ return true; };
+ for (let i=0; f(); i++) {
+ f = null;
+ }`,
+ "not a function: NULL",
+ },
+ {
+ `
+ let f=function(){ return true; };
+ for (let i=0; i < 10; f()) {
+ f = null;
+ }`,
+ "not a function: NULL",
+ },
+ {
+ `
+ for (let i=0; i < 10; i++) {
+ f()
+ }`,
+ "identifier not found: f",
+ },
+ {
+ `{true: 5}[true]`,
+ "unusable as hash key: BOOLEAN",
+ },
+ {
+ `{false: 5}[false]`,
+ "unusable as hash key: BOOLEAN",
+ },
+ }
+
+ for _, tt := range tests {
+ testError(t, tt)
+ }
+}
+
+func TestHashErrorHandling(t *testing.T) {
+ tests := []expectedError{
+ {
+ `{
+ function(){}: "a"
+ }`,
+ "unusable as hash key: FUNCTION",
+ },
+ {
+ `{
+ f(): "a"
+ }`,
+ "identifier not found: f",
+ },
+ {
+ `{
+ "f": f()
+ }`,
+ "identifier not found: f",
+ },
+ }
+
+ for _, tt := range tests {
+ testError(t, tt)
+ }
+}
+
+func TestEnclosingEnvironmentsErrors(t *testing.T) {
+ t.Run("With let only", func(t *testing.T) {
+ testError(t, expectedError{
+ `
+let first = 10;
+let second = 10;
+
+let ourFunction = function(first) {
+ let second = 20;
+ first + second + third;
+};
+
+ourFunction(30);
+`,
+ "identifier not found: third",
+ })
+ })
+
+ t.Run("Function arguments", func(t *testing.T) {
+ testError(t, expectedError{
+ `
+let first = 10;
+let second = 10;
+
+let ourFunction = function(third) {
+};
+
+first + second + third;
+`,
+ "identifier not found: third",
+ })
+ })
+
+ t.Run("If block", func(t *testing.T) {
+ testError(t, expectedError{
+ `
+let first = 10;
+let second = 10;
+
+if (first < 1000) {
+ let second = 20;
+ first + second + third;
+}
+`,
+ "identifier not found: third",
+ })
+ })
+
+ t.Run("If block with inner new variable", func(t *testing.T) {
+ testError(t, expectedError{
+ `
+let first = 10;
+let second = 10;
+
+if (first < 1000) {
+ let third = 20;
+}
+
+first + second + third;
+`,
+ "identifier not found: third",
+ })
+ })
+
+ t.Run("For block", func(t *testing.T) {
+ testError(t, expectedError{
+ `
+let first = 10;
+let second = 10;
+let third = 10;
+
+for (let i=0; i < 20; i++) {
+}
+
+first + second + third + i;
+`,
+ "identifier not found: i",
+ })
+ })
+}
+
+func TestErrorStacktrace(t *testing.T) {
+ tests := []struct {
+ input string
+ expectedStacktrace string
+ }{
+ {
+ `
+if (10 > 1) {
+ if (10 > 1) {
+ return true + false;
+ }
+
+ return 1;
+}
+`,
+ "RUNTIME ERROR: unknown operator: BOOLEAN + BOOLEAN\n\tat + (line 4, col 17)",
+ },
+ {
+ `
+let f = function(x) {
+ return x * 1;
+}
+
+f("str");
+`,
+ "RUNTIME ERROR: unknown operator: STRING * INTEGER\n\tat * (line 3, col 12)\n\tat f (line 6, col 1)",
+ },
+ {
+ `
+let f = function(x, y) {
+ return x + y;
+}
+
+f(42);
+`,
+ "RUNTIME ERROR: wrong number of arguments: expected 2, got 1\n\tat ( (line 6, col 2)\n\tat f (line 6, col 1)",
+ },
+ {
+ `
+let sum = function(a, b) {
+ return a() + b();
+};
+
+let f1 = function() {
+ return 42;
+};
+
+let f2 = function() {
+ return true + false;
+};
+
+sum(f1, f2);
+`,
+ "RUNTIME ERROR: unknown operator: BOOLEAN + BOOLEAN\n\tat + (line 11, col 14)\n\tat b (line 3, col 16)\n\tat sum (line 14, col 1)",
+ },
+ }
+
+ for i, tt := range tests {
+ evaluated := testEval(t, tt.input)
+
+ errObj, ok := evaluated.(*object.Error)
+ if !ok {
+ t.Errorf("no error object returned [%d]. got=%T(%+v)",
+ i, evaluated, evaluated)
+ continue
+ }
+
+ stacktrace := errObj.Inspect()
+ if stacktrace != tt.expectedStacktrace {
+ t.Errorf("wrong stacktrace [%d]. expected=%q, got=%q",
+ i, tt.expectedStacktrace, stacktrace)
+ }
+ }
+}
+
+func TestEvalStringInfixExpression(t *testing.T) {
+ tests := []expectedError{
+ {
+ `["array"] += "array"`,
+ "unknown operator: [ += STRING",
+ },
+ {
+ `{"foo":"bar"} + "foo:bar"`,
+ "cannot convert value of type HASH to STRING",
+ },
+ {
+ `"function" += function(){}`,
+ "unknown operator: STRING += FUNCTION",
+ },
+ }
+
+ for _, tt := range tests {
+ testError(t, tt)
+ }
+}
+
+func testError(t *testing.T, tt expectedError) {
+
+ evaluated := testEval(t, tt.input)
+
+ errObj, ok := evaluated.(*object.Error)
+ if !ok {
+ t.Errorf("no error object returned. got=%T(%+v)",
+ evaluated, evaluated)
+ return
+ }
+
+ if errObj.Message != tt.expectedMessage {
+ t.Errorf("wrong error message. expected=%q, got=%q",
+ tt.expectedMessage, errObj.Message)
+ }
+}
diff --git a/dsl/evaluator/evaluator_profiling_test.go b/dsl/evaluator/evaluator_profiling_test.go
new file mode 100644
index 0000000..da44c1a
--- /dev/null
+++ b/dsl/evaluator/evaluator_profiling_test.go
@@ -0,0 +1,111 @@
+package evaluator
+
+import (
+ "github.com/ofux/deluge/dsl/lexer"
+ "github.com/ofux/deluge/dsl/object"
+ "github.com/ofux/deluge/dsl/parser"
+ "testing"
+)
+
+func BenchmarkEvaluator_Eval_FibRecursive(b *testing.B) {
+
+ script := `
+let fib = function(n) {
+ if (n < 2) {
+ return n;
+ }
+ return fib(n-1) + fib(n-2);
+}
+let r = fib(25);
+r
+`
+
+ l := lexer.New(script)
+ p := parser.New(l)
+ program, ok := p.ParseProgram()
+ if !ok {
+ b.Errorf("Parsing errors: %v", p.Errors())
+ b.FailNow()
+ }
+ ev := NewEvaluator()
+
+ for i := 0; i < b.N; i++ {
+ v := ev.Eval(program, object.NewEnvironment())
+ if v.Type() == object.ERROR_OBJ {
+ b.Fatalf("error: %s", v.(*object.Error).Message)
+ }
+ }
+}
+
+func BenchmarkEvaluator_Eval_FibIter(b *testing.B) {
+
+ script := `
+let fib = function(n) {
+ let x = 0;
+ let y = 1;
+ for (let i=0; i < n; i++) {
+ let aux = x;
+ x = x+y;
+ y = aux;
+ }
+ return x;
+}
+let r = 0;
+for (let i=0; i < 1000; i++) {
+ r = fib(50);
+}
+r
+`
+
+ l := lexer.New(script)
+ p := parser.New(l)
+ program, ok := p.ParseProgram()
+ if !ok {
+ b.Errorf("Parsing errors: %v", p.Errors())
+ b.FailNow()
+ }
+ ev := NewEvaluator()
+
+ for i := 0; i < b.N; i++ {
+ v := ev.Eval(program, object.NewEnvironment())
+ if v.Type() == object.ERROR_OBJ {
+ b.Fatalf("error: %s", v.(*object.Error).Message)
+ }
+ }
+}
+
+func fib(n int) int {
+ if n < 2 {
+ return n
+ }
+ return fib(n-1) + fib(n-2)
+}
+
+func fibIter(n int) int {
+ x, y := 0, 1
+ for i := 0; i < n; i++ {
+ x, y = x+y, x
+ }
+ return x
+}
+
+func BenchmarkEvaluator_Eval_FibRecursive_GoComparison(b *testing.B) {
+ for i := 0; i < b.N; i++ {
+ v := fib(25)
+ if v != 75025 {
+ b.Fatalf("fib(25) is not %d", v)
+ }
+ }
+}
+
+func BenchmarkEvaluator_Eval_FibIter_GoComparison(b *testing.B) {
+ param := 50
+ for i := 0; i < b.N; i++ {
+ for j := 0; j < 1000; j++ {
+ v := fibIter(param)
+ if v != 12586269025 {
+ b.Fatalf("fib(50) is not %d", v)
+ }
+ }
+ }
+}
diff --git a/dsl/evaluator/evaluator_test.go b/dsl/evaluator/evaluator_test.go
new file mode 100644
index 0000000..8bb213d
--- /dev/null
+++ b/dsl/evaluator/evaluator_test.go
@@ -0,0 +1,1077 @@
+package evaluator
+
+import (
+ "github.com/ofux/deluge/dsl/ast"
+ "github.com/ofux/deluge/dsl/lexer"
+ "github.com/ofux/deluge/dsl/object"
+ "github.com/ofux/deluge/dsl/parser"
+ "testing"
+)
+
+func TestEvalIntegerExpression(t *testing.T) {
+ tests := []struct {
+ input string
+ expected int64
+ }{
+ {"5", 5},
+ {"10", 10},
+ {"-5", -5},
+ {"-10", -10},
+ {"5 + 5 + 5 + 5 - 10", 10},
+ {"2 * 2 * 2 * 2 * 2", 32},
+ {"-50 + 100 + -50", 0},
+ {"5 * 2 + 10", 20},
+ {"5 + 2 * 10", 25},
+ {"20 + 2 * -10", 0},
+ {"50 / 2 * 2 + 10", 60},
+ {"2 * (5 + 10)", 30},
+ {"3 * 3 * 3 + 10", 37},
+ {"3 * (3 * 3) + 10", 37},
+ {"(5 + 10 * 2 + 15 / 3) * 2 + -10", 50},
+ {"4 % 2", 0},
+ {"4 % 3", 1},
+ {"16 % 3", 1},
+ }
+
+ for _, tt := range tests {
+ evaluated := testEval(t, tt.input)
+ testIntegerObject(t, evaluated, tt.expected)
+ }
+}
+
+func TestEvalFloatExpression(t *testing.T) {
+ tests := []struct {
+ input string
+ expected float64
+ }{
+ {"5.42", 5.42},
+ {"10.098", 10.098},
+ {"-5.10", -5.1},
+ {"-10.0", -10.0},
+ {"5.0 + 5 + 5 + 5 - 10", 10.0},
+ {"2.0 * 2 * 2 * 2 * 2", 32.0},
+ {"-50.0 + 100.0 + -50.0", 0.0},
+ {"5.0 * 2 + 10", 20.0},
+ {"5.0 + 2 * 10", 25.0},
+ {"20.0 + 2 * -10", 0.0},
+ {"50.0 / 2.0 * 2 + 10", 60.0},
+ {"2.0 * (5 + 10.0)", 30.0},
+ {"3.0 * 3.0 * 3 + 10", 37.0},
+ {"3.0 * (3 * 3) + 10", 37.0},
+ {"(5.0 + 10.0 * 2 + 15 / 3) * 2 + -10", 50.0},
+ }
+
+ for _, tt := range tests {
+ evaluated := testEval(t, tt.input)
+ testFloatObject(t, evaluated, tt.expected)
+ }
+}
+
+func TestEvalStringExpression(t *testing.T) {
+ tests := []struct {
+ input string
+ expected string
+ }{
+ {`"aaa" + "bbb"`, "aaabbb"},
+ {`"aaa" + ""`, "aaa"},
+ {`"" + "bbb"`, "bbb"},
+ {`"" + ""`, ""},
+ {`" " + " x "`, " x "},
+ {`" " + " x " + "yz"`, " x yz"},
+ {`5 + " x " + 2 + " = " + 10`, "5 x 2 = 10"},
+ {`5 + " === " + 2.5*2`, "5 === 5"},
+ {`5.3 + 0.7 + " foo " + 2.54321`, "6 foo 2.54321"},
+ {`"foo" + 5.3 + 0.7`, "foo5.30.7"},
+ }
+
+ for _, tt := range tests {
+ evaluated := testEval(t, tt.input)
+ testStringObject(t, evaluated, tt.expected)
+ }
+}
+
+func TestEvalBooleanExpression(t *testing.T) {
+ t.Run("simple boolean expressions", func(t *testing.T) {
+
+ tests := []struct {
+ input string
+ expected bool
+ }{
+ {"true", true},
+ {"false", false},
+ {"1 < 2", true},
+ {"1 > 2", false},
+ {"1 < 1", false},
+ {"1 > 1", false},
+ {"1 == 1", true},
+ {"1 != 1", false},
+ {"1 == 2", false},
+ {"1 != 2", true},
+ {"1.0 == 1", true},
+ {"1.0 != 1", false},
+ {"1.0 == 2", false},
+ {"1.0 != 2", true},
+ {"1 == 1.0", true},
+ {"1 != 1.0", false},
+ {"1 == 2.0", false},
+ {"1 != 2.0", true},
+ {"1.0 == 1.0", true},
+ {"1.0 != 1.0", false},
+ {"1.0 == 2.0", false},
+ {"1.0 != 2.0", true},
+ {"1.0001 == 1.0001", true},
+ {"1.0001 != 1.0001", false},
+ {"1.0001 == 1.0002", false},
+ {"1.0001 != 1.0002", true},
+ {"true == true", true},
+ {"false == false", true},
+ {"true == false", false},
+ {"true != false", true},
+ {"false != true", true},
+ {`"aaa" == "aaa"`, true},
+ {`"aaa" != "baa"`, true},
+ {`"aaa" != "aaa"`, false},
+ {`"aaa" == "baa"`, false},
+ {`"true" == true`, true},
+ {`"true" != true`, false},
+ {`false != "false"`, false},
+ {`false == "false"`, true},
+ {`"1" == 1`, true},
+ {`"1.1" == 1.1`, true},
+ {`1.1 == "1.1"`, true},
+ {`1 == "1"`, true},
+ {`1+2 == "3"`, true},
+ {`"3" == 1+2`, true},
+ {`1 == "3"`, false},
+ {`"3" == 2`, false},
+ {`"1" != 1`, false},
+ {`"1.1" != 1.1`, false},
+ {`1.1 != "1.1"`, false},
+ {`1 != "1"`, false},
+ {`1+2 != "3"`, false},
+ {`"3" != 1+2`, false},
+ {`1 != "3"`, true},
+ {`"3" != 2`, true},
+ {`"3" == null`, false},
+ {`"1" != null`, false},
+ {`null == "3"`, false},
+ {`null == "1"`, false},
+ {"(1 < 2) == true", true},
+ {"(1 < 2) == false", false},
+ {"(1 > 2) == true", false},
+ {"(1 > 2) == false", true},
+ {"(1 < 1) == true", false},
+ {"(1 < 1) == false", true},
+ {"(1 > 1) == true", false},
+ {"(1 > 1) == false", true},
+ {"(1 <= 2) == true", true},
+ {"(1 <= 2) == false", false},
+ {"(1 >= 2) == true", false},
+ {"(1 >= 2) == false", true},
+ {"(1 <= 1) == true", true},
+ {"(1 <= 1) == false", false},
+ {"(1 >= 1) == true", true},
+ {"(1 >= 1) == false", false},
+ {"(1.0 < 2) == true", true},
+ {"(1.0 < 2.0) == false", false},
+ {"(1 > 2.0) == true", false},
+ {"(1.0 > 2.0) == false", true},
+ {"(1.0 < 1) == true", false},
+ {"(1.0 < 1.0) == false", true},
+ {"(1 > 1.0) == true", false},
+ {"(1.0 > 1) == true", false},
+ {"(1.0 > 1.0) == false", true},
+ {"(1 <= 2.0) == true", true},
+ {"(1.0 <= 2.0) == false", false},
+ {"(1.0 >= 2.0) == true", false},
+ {"(1.0 >= 2) == false", true},
+ {"(1 <= 1.0) == true", true},
+ {"(1.0 <= 1) == false", false},
+ {"(1.0 >= 1.0) == true", true},
+ {"(1.0 >= 1) == false", false},
+ {"false || false", false},
+ {"false || true", true},
+ {"true || true", true},
+ {"false && false", false},
+ {"false && true", false},
+ {"true && true", true},
+ {"false && true || true", true},
+ {"false && true || false", false},
+ {"false && (true || true)", false},
+ }
+
+ for _, tt := range tests {
+ evaluated := testEval(t, tt.input)
+ testBooleanObject(t, evaluated, tt.expected)
+ }
+ })
+ t.Run("AND, OR, code evaluation", func(t *testing.T) {
+ tests := []struct {
+ input string
+ expected int64
+ }{
+ {`
+ let x = 0;
+ let f = function() {
+ x = x + 1;
+ return true;
+ }
+ let b = true || f();
+ x;
+ `, 0},
+ {`
+ let x = 0;
+ let f = function() {
+ x = x + 1;
+ return true;
+ }
+ let b = false || f();
+ x;
+ `, 1},
+ {`
+ let x = 0;
+ let f = function() {
+ x = x + 1;
+ return true;
+ }
+ let b = true && f();
+ x;
+ `, 1},
+ {`
+ let x = 0;
+ let f = function() {
+ x = x + 1;
+ return true;
+ }
+ let b = false && f();
+ x;
+ `, 0},
+ {`
+ let x = 0;
+ let f = function() {
+ x = x + 1;
+ return true;
+ }
+ let b = true && f() || f();
+ x;
+ `, 1},
+ {`
+ let x = 0;
+ let f = function() {
+ x = x + 1;
+ return true;
+ }
+ let b = false && f() || f();
+ x;
+ `, 1},
+ {`
+ let x = 0;
+ let f = function() {
+ x = x + 1;
+ return true;
+ }
+ let b = !(true && f()) || f();
+ x;
+ `, 2},
+ }
+
+ for _, tt := range tests {
+ evaluated := testEval(t, tt.input)
+ testIntegerObject(t, evaluated, tt.expected)
+ }
+ })
+}
+
+func TestBangOperator(t *testing.T) {
+ tests := []struct {
+ input string
+ expected bool
+ }{
+ {"!true", false},
+ {"!false", true},
+ {"!5", false},
+ {"!!true", true},
+ {"!!false", false},
+ {"!!5", true},
+ }
+
+ for _, tt := range tests {
+ evaluated := testEval(t, tt.input)
+ testBooleanObject(t, evaluated, tt.expected)
+ }
+}
+
+func TestNull(t *testing.T) {
+ tests := []struct {
+ input string
+ }{
+ {"null"},
+ {"let a = null; a"},
+ }
+
+ for _, tt := range tests {
+ evaluated := testEval(t, tt.input)
+ testNullObject(t, evaluated)
+ }
+}
+func TestIfElseStatements(t *testing.T) {
+ tests := []struct {
+ input string
+ expected interface{}
+ }{
+ {"if (true) { 10 }", 10},
+ {"if (false) { 10 }", nil},
+ {"if (1) { 10 }", 10},
+ {"if (1 < 2) { 10 }", 10},
+ {"if (1 > 2) { 10 }", nil},
+ {"if (1 > 2) { 10 } else { 20 }", 20},
+ {"if (1 < 2) { 10 } else { 20 }", 10},
+ {"if (1 < 2) { 10 } else if (2 != 2) { 20 } else { 30 }", 10},
+ {"if (2 < 2) { 10 } else if (2 == 2) { 20 } else { 30 }", 20},
+ {"if (2 < 2) { 10 } else if (2 != 2) { 20 } else { 30 }", 30},
+ {"if (2 < 2) { 10 } else if (2 != 2) { 20 } else if (true) { 30 }", 30},
+ {"if (2 < 2) { 10 } else if (2 != 2) { 20 } else if (true) { 30 } else { 40 }", 30},
+ {"if (2 < 2) { 10 } else if (2 != 2) { 20 } else if (false) { 30 } else { 40 }", 40},
+ {"let a = null; if (a == null) { 10 } else { 20 }", 10},
+ {"let a = null; if (a != null) { 10 } else { 20 }", 20},
+ {"let a = null; if (a) { 10 } else { 20 }", 20},
+ {"let a = null; if (!a) { 10 } else { 20 }", 10},
+ }
+
+ for _, tt := range tests {
+ evaluated := testEval(t, tt.input)
+ integer, ok := tt.expected.(int)
+ if ok {
+ testIntegerObject(t, evaluated, int64(integer))
+ } else {
+ testNullObject(t, evaluated)
+ }
+ }
+}
+
+func TestForStatements(t *testing.T) {
+ tests := []struct {
+ input string
+ expected interface{}
+ }{
+ {`let sum = 0;
+ for (let i = 0; i < 10; i += 2) {
+ sum = sum + 1;
+ }
+ sum`, 5},
+ {`let sum = 0;
+ for (let i = 20; i > 0; i--) {
+ sum = sum + i;
+ }
+ sum`, 210},
+ }
+
+ for _, tt := range tests {
+ evaluated := testEval(t, tt.input)
+ integer, ok := tt.expected.(int)
+ if ok {
+ testIntegerObject(t, evaluated, int64(integer))
+ } else {
+ testNullObject(t, evaluated)
+ }
+ }
+}
+
+func TestReturnStatements(t *testing.T) {
+ tests := []struct {
+ input string
+ expected int64
+ }{
+ {"return 10;", 10},
+ {"return 10; 9;", 10},
+ {"return 2 * 5; 9;", 10},
+ {"9; return 2 * 5; 9;", 10},
+ {"if (10 > 1) { return 10; }", 10},
+ {
+ `
+if (10 > 1) {
+ if (10 > 1) {
+ return 10;
+ }
+
+ return 1;
+}
+`,
+ 10,
+ },
+ {
+ `
+let f = function(x) {
+ return x;
+ x + 10;
+};
+f(10);`,
+ 10,
+ },
+ {
+ `
+let f = function(x) {
+ let result = x + 10;
+ return result;
+ return 10;
+};
+f(10);`,
+ 20,
+ },
+ }
+
+ for _, tt := range tests {
+ evaluated := testEval(t, tt.input)
+ testIntegerObject(t, evaluated, tt.expected)
+ }
+}
+
+func TestLetStatements(t *testing.T) {
+ tests := []struct {
+ input string
+ expected int64
+ }{
+ {"let a = 5; a;", 5},
+ {"let a = 5 * 5; a;", 25},
+ {"let a = 5; let b = a; b;", 5},
+ {"let a = 5; let b = a; let c = a + b + 5; c;", 15},
+ }
+
+ for _, tt := range tests {
+ testIntegerObject(t, testEval(t, tt.input), tt.expected)
+ }
+}
+
+func TestAssignmentExpressions(t *testing.T) {
+ t.Run("assign integers to integers", func(t *testing.T) {
+ tests := []struct {
+ input string
+ expected int64
+ }{
+ {"let a = 1; a = 5; a;", 5},
+ {"let a = 1; a = 5 * 5; a;", 25},
+ {"let a = 1; a = 5; let b = a; b;", 5},
+ {"let a = 1; a = 5; let b = a; let c = a; c = c + b + 5; c;", 15},
+
+ {"let a = 1+5; a;", 6},
+ {"let a = 1; a += 5; a;", 6},
+ {"let a = 1; a -= 5; a;", -4},
+ {"let a = 4; a *= 5; a;", 20},
+ {"let a = 4; a /= 2; a;", 2},
+ {"let a = 4; a++; a;", 5},
+ {"let a = 4; a--; a;", 3},
+
+ {"let a = [1, 2]; a[0] = 5; a[0];", 5},
+ {"let a = [1, 2]; a[0] += 5; a[0];", 6},
+ {"let a = [1, 2]; a[0] -= 5; a[0];", -4},
+ {"let a = [4, 2]; a[0] *= 5; a[0];", 20},
+ {"let a = [4, 2]; a[0] /= 2; a[0];", 2},
+ {"let a = [4, 2]; a[0]++; a[0];", 5},
+ {"let a = [4, 2]; a[0]--; a[0];", 3},
+
+ {`let a = {"x":1}; a["x"] = 5; a["x"];`, 5},
+ {`let a = {"x":1}; a["x"] += 5; a["x"];`, 6},
+ {`let a = {"x":1}; a["x"] -= 5; a["x"];`, -4},
+ {`let a = {"x":4}; a["x"] *= 5; a["x"];`, 20},
+ {`let a = {"x":4}; a["x"] /= 2; a["x"];`, 2},
+ {`let a = {"x":4}; a["x"]++; a["x"];`, 5},
+ {`let a = {"x":4}; a["x"]--; a["x"];`, 3},
+ }
+
+ for _, tt := range tests {
+ testIntegerObject(t, testEval(t, tt.input), tt.expected)
+ }
+ })
+
+ t.Run("assign integers to floats", func(t *testing.T) {
+ tests := []struct {
+ input string
+ expected float64
+ }{
+ {"let a = 1.3; a = 5.3; a;", 5.3},
+ {"let a = 1.3; a = 5.3 * 5; a;", 26.5},
+ {"let a = 1.3; a = 5.3; let b = a; b;", 5.3},
+ {"let a = 1.3; a = 5.3; let b = a; let c = a; c = c + b + 5; c;", 15.6},
+
+ {"let a = 1.3+5; a;", 6.3},
+ {"let a = 1.3; a += 5; a;", 6.3},
+ {"let a = 1.3; a -= 5; a;", -3.7},
+ {"let a = 4.3; a *= 5; a;", 21.5},
+ {"let a = 4.3; a /= 2; a;", 2.15},
+
+ {"let a = [1.3, 2]; a[0] = 5.3; a[0];", 5.3},
+ {"let a = [1.3, 2]; a[0] += 5; a[0];", 6.3},
+ {"let a = [1.3, 2]; a[0] -= 5; a[0];", -3.7},
+ {"let a = [4.3, 2]; a[0] *= 5; a[0];", 21.5},
+ {"let a = [4.3, 2]; a[0] /= 2; a[0];", 2.15},
+
+ {`let a = {"x":1.3}; a["x"] = 5.3; a["x"];`, 5.3},
+ {`let a = {"x":1.3}; a["x"] += 5; a["x"];`, 6.3},
+ {`let a = {"x":1.3}; a["x"] -= 5; a["x"];`, -3.7},
+ {`let a = {"x":4.3}; a["x"] *= 5; a["x"];`, 21.5},
+ {`let a = {"x":4.3}; a["x"] /= 2; a["x"];`, 2.15},
+ }
+
+ for _, tt := range tests {
+ testFloatObject(t, testEval(t, tt.input), tt.expected)
+ }
+ })
+
+ t.Run("assign floats to integers", func(t *testing.T) {
+ tests := []struct {
+ input string
+ expected float64
+ }{
+ {"let a = 1; a += 5.3; a;", 6.3},
+ {"let a = 1; a -= 5.3; a;", -4.3},
+ {"let a = 4; a *= 5.3; a;", 21.2},
+ {"let a = 5; a /= 2.5; a;", 2.0},
+
+ {"let a = [1, 2]; a[0] += 5.3; a[0];", 6.3},
+ {"let a = [1, 2]; a[0] -= 5.3; a[0];", -4.3},
+ {"let a = [4, 2]; a[0] *= 5.3; a[0];", 21.2},
+ {"let a = [5, 2]; a[0] /= 2.5; a[0];", 2.0},
+
+ {`let a = {"x":1}; a["x"] += 5.3; a["x"];`, 6.3},
+ {`let a = {"x":1}; a["x"] -= 5.3; a["x"];`, -4.3},
+ {`let a = {"x":4}; a["x"] *= 5.3; a["x"];`, 21.2},
+ {`let a = {"x":5}; a["x"] /= 2.5; a["x"];`, 2.0},
+ }
+
+ for _, tt := range tests {
+ testFloatObject(t, testEval(t, tt.input), tt.expected)
+ }
+ })
+
+ t.Run("assign floats to floats", func(t *testing.T) {
+ tests := []struct {
+ input string
+ expected float64
+ }{
+ {"let a = 1.2; a += 5.3; a;", 6.5},
+ {"let a = 1.2; a -= 5.3; a;", -4.1},
+ {"let a = 4.2; a *= 5.3; a;", 22.26},
+ {"let a = 5.2; a /= 2.6; a;", 2.0},
+
+ {"let a = [1.2, 2]; a[0] += 5.3; a[0];", 6.5},
+ {"let a = [1.2, 2]; a[0] -= 5.3; a[0];", -4.1},
+ {"let a = [4.2, 2]; a[0] *= 5.3; a[0];", 22.26},
+ {"let a = [5.2, 2]; a[0] /= 2.6; a[0];", 2.0},
+
+ {`let a = {"x":1.2}; a["x"] += 5.3; a["x"];`, 6.5},
+ {`let a = {"x":1.2}; a["x"] -= 5.3; a["x"];`, -4.1},
+ {`let a = {"x":4.2}; a["x"] *= 5.3; a["x"];`, 22.26},
+ {`let a = {"x":5.2}; a["x"] /= 2.6; a["x"];`, 2.0},
+ }
+
+ for _, tt := range tests {
+ testFloatObject(t, testEval(t, tt.input), tt.expected)
+ }
+ })
+
+ t.Run("assign strings", func(t *testing.T) {
+ tests := []struct {
+ input string
+ expected string
+ }{
+ {`let a = "x"; a = "A"; a;`, "A"},
+ {`let a = "x"; a = "A" + "B"; a;`, "AB"},
+ {`let a = "x"; a = "A"+1; a;`, "A1"},
+ {`let a = "x"; a = "A"+3.3; a;`, "A3.3"},
+ {`let a = "x"; a = "A"+true; a;`, "Atrue"},
+ {`let a = "x"; a += "A"; a;`, "xA"},
+ {`let a = "x"; a += 1; a;`, "x1"},
+ {`let a = "x"; a += 3.3; a;`, "x3.3"},
+ {`let a = "x"; a += true; a;`, "xtrue"},
+ }
+
+ for _, tt := range tests {
+ testStringObject(t, testEval(t, tt.input), tt.expected)
+ }
+ })
+}
+
+func TestFunctionObject(t *testing.T) {
+ input := "function(x) { x + 2; };"
+
+ evaluated := testEval(t, input)
+ fn, ok := evaluated.(*object.Function)
+ if !ok {
+ t.Fatalf("object is not Function. got=%T (%+v)", evaluated, evaluated)
+ }
+
+ if len(fn.Parameters) != 1 {
+ t.Fatalf("function has wrong parameters. Parameters=%+v",
+ fn.Parameters)
+ }
+
+ if fn.Parameters[0].String() != "x" {
+ t.Fatalf("parameter is not 'x'. got=%q", fn.Parameters[0])
+ }
+
+ expectedBody := "(x + 2)"
+
+ if fn.Body.String() != expectedBody {
+ t.Fatalf("body is not %q. got=%q", expectedBody, fn.Body.String())
+ }
+}
+
+func TestFunctionApplication(t *testing.T) {
+ tests := []struct {
+ input string
+ expected int64
+ }{
+ {"let identity = function(x) { x; }; identity(5);", 5},
+ {"let identity = function(x) { return x; }; identity(5);", 5},
+ {"let double = function(x) { x * 2; }; double(5);", 10},
+ {"let add = function(x, y) { x + y; }; add(5, 5);", 10},
+ {"let add = function(x, y) { x + y; }; add(5 + 5, add(5, 5));", 20},
+ {"function(x) { x; }(5)", 5},
+ }
+
+ for _, tt := range tests {
+ testIntegerObject(t, testEval(t, tt.input), tt.expected)
+ }
+}
+
+func TestEnclosingEnvironments(t *testing.T) {
+ t.Run("With let only", func(t *testing.T) {
+ input := `
+let first = 10;
+let second = 10;
+let third = 10;
+
+let ourFunction = function(first) {
+ let second = 20;
+
+ first + second + third;
+};
+
+ourFunction(20) + first + second;`
+
+ testIntegerObject(t, testEval(t, input), 70)
+ })
+
+ t.Run("With let and assign", func(t *testing.T) {
+ input := `
+let first = 10;
+let second = 10;
+let third = 10;
+
+let ourFunction = function(first) {
+ second = 20;
+
+ first + second + third;
+};
+
+ourFunction(20) + first + second;`
+
+ testIntegerObject(t, testEval(t, input), 80)
+ })
+
+ t.Run("If block", func(t *testing.T) {
+ input := `
+let first = 10;
+let second = 10;
+let third = 10;
+
+if (first == 10) {
+ let second = 20;
+}
+
+first + second + third;
+`
+
+ testIntegerObject(t, testEval(t, input), 30)
+ })
+
+ t.Run("For block", func(t *testing.T) {
+ input := `
+let first = 10;
+let second = 10;
+let third = 10;
+
+for (let i=0; i < 5; i++) {
+ first--;
+ let second = 20;
+}
+
+first + second + third;
+`
+
+ testIntegerObject(t, testEval(t, input), 25)
+ })
+}
+
+func TestClosures(t *testing.T) {
+ input := `
+let newAdder = function(x) {
+ function(y) { x + y };
+};
+
+let addTwo = newAdder(2);
+addTwo(2);`
+
+ testIntegerObject(t, testEval(t, input), 4)
+}
+
+func TestStringLiteral(t *testing.T) {
+ input := `"Hello World!"`
+
+ evaluated := testEval(t, input)
+ str, ok := evaluated.(*object.String)
+ if !ok {
+ t.Fatalf("object is not String. got=%T (%+v)", evaluated, evaluated)
+ }
+
+ if str.Value != "Hello World!" {
+ t.Errorf("String has wrong value. got=%q", str.Value)
+ }
+}
+
+func TestCustomBuiltinFunctions(t *testing.T) {
+ l := lexer.New("yo()")
+ p := parser.New(l)
+ program, ok := p.ParseProgram()
+ if !ok {
+ t.Errorf("Parsing errors: %v", p.Errors())
+ t.FailNow()
+ }
+ env := object.NewEnvironment()
+ ev := NewEvaluator()
+
+ ev.AddBuiltin("yo", func(node ast.Node, args ...object.Object) object.Object {
+ return &object.Integer{Value: 42}
+ })
+
+ evaluated := ev.Eval(program, env)
+ testIntegerObject(t, evaluated, int64(42))
+}
+
+func TestArrayLiterals(t *testing.T) {
+ input := "[1, 2 * 2, 3 + 3]"
+
+ evaluated := testEval(t, input)
+ result, ok := evaluated.(*object.Array)
+ if !ok {
+ t.Fatalf("object is not Array. got=%T (%+v)", evaluated, evaluated)
+ }
+
+ if len(result.Elements) != 3 {
+ t.Fatalf("array has wrong num of elements. got=%d",
+ len(result.Elements))
+ }
+
+ testIntegerObject(t, result.Elements[0], 1)
+ testIntegerObject(t, result.Elements[1], 4)
+ testIntegerObject(t, result.Elements[2], 6)
+}
+
+func TestArrayIndexExpressions(t *testing.T) {
+ tests := []struct {
+ input string
+ expected int
+ }{
+ {
+ "[1, 2, 3][0]",
+ 1,
+ },
+ {
+ "[1, 2, 3][1]",
+ 2,
+ },
+ {
+ "[1, 2, 3][2]",
+ 3,
+ },
+ {
+ "let i = 0; [1][i];",
+ 1,
+ },
+ {
+ "[1, 2, 3][1 + 1];",
+ 3,
+ },
+ {
+ "let myArray = [1, 2, 3]; myArray[2];",
+ 3,
+ },
+ {
+ "let myArray = [1, 2, 3]; myArray[0] + myArray[1] + myArray[2];",
+ 6,
+ },
+ {
+ "let myArray = [1, 2, 3]; let i = myArray[0]; myArray[i]",
+ 2,
+ },
+ }
+
+ for _, tt := range tests {
+ evaluated := testEval(t, tt.input)
+ testIntegerObject(t, evaluated, int64(tt.expected))
+ }
+}
+
+func TestArrayAssignmentExpressions(t *testing.T) {
+ tests := []struct {
+ input string
+ expected int
+ }{
+ {
+ `let h=[1,2]; h[0]=42; h[0]`,
+ 42,
+ },
+ }
+
+ for _, tt := range tests {
+ evaluated := testEval(t, tt.input)
+ testIntegerObject(t, evaluated, int64(tt.expected))
+ }
+}
+
+func TestHashLiterals(t *testing.T) {
+ input := `let two = "two";
+ {
+ "one": 10 - 9,
+ two: 1 + 1,
+ "thr" + "ee": 6 / 2,
+ 4: 4
+ }`
+
+ evaluated := testEval(t, input)
+ result, ok := evaluated.(*object.Hash)
+ if !ok {
+ t.Fatalf("Eval didn't return Hash. got=%T (%+v)", evaluated, evaluated)
+ }
+
+ expected := map[object.HashKey]int64{
+ (&object.String{Value: "one"}).HashKey(): 1,
+ (&object.String{Value: "two"}).HashKey(): 2,
+ (&object.String{Value: "three"}).HashKey(): 3,
+ (&object.Integer{Value: 4}).HashKey(): 4,
+ }
+
+ if len(result.Pairs) != len(expected) {
+ t.Fatalf("Hash has wrong num of pairs. got=%d", len(result.Pairs))
+ }
+
+ for expectedKey, expectedValue := range expected {
+ v, ok := result.Pairs[expectedKey]
+ if !ok {
+ t.Errorf("no pair for given key in Pairs")
+ }
+
+ testIntegerObject(t, v, expectedValue)
+ }
+}
+
+func TestHashIndexExpressions(t *testing.T) {
+ tests := []struct {
+ input string
+ expected interface{}
+ }{
+ {
+ `{"foo": 5}["foo"]`,
+ 5,
+ },
+ {
+ `{"foo": 5}["bar"]`,
+ nil,
+ },
+ {
+ `let key = "foo"; {"foo": 5}[key]`,
+ 5,
+ },
+ {
+ `{}["foo"]`,
+ nil,
+ },
+ {
+ `{5: 5}[5]`,
+ 5,
+ },
+ {
+ `{5: 3}["5"]`,
+ 3,
+ },
+ {
+ `{"5": 3}[5]`,
+ 3,
+ },
+ }
+
+ for _, tt := range tests {
+ evaluated := testEval(t, tt.input)
+ integer, ok := tt.expected.(int)
+ if ok {
+ testIntegerObject(t, evaluated, int64(integer))
+ } else {
+ testNullObject(t, evaluated)
+ }
+ }
+}
+
+func TestHashAssignmentExpressions(t *testing.T) {
+ tests := []struct {
+ input string
+ expected interface{}
+ }{
+ {
+ `let h={}; h["a"]=42; h["a"]`,
+ 42,
+ },
+ {
+ `let h={"a": 1}; h["a"]=42; h["a"]`,
+ 42,
+ },
+ }
+
+ for _, tt := range tests {
+ evaluated := testEval(t, tt.input)
+ integer, ok := tt.expected.(int)
+ if ok {
+ testIntegerObject(t, evaluated, int64(integer))
+ } else {
+ testNullObject(t, evaluated)
+ }
+ }
+}
+
+func TestFullPrograms(t *testing.T) {
+ t.Run("Recursive Fibonacci", func(t *testing.T) {
+ input := `
+ let fib = function(n) {
+ if (n < 2) {
+ return n;
+ }
+ return fib(n-1) + fib(n-2);
+ }
+
+ let f5 = fib(6);
+ f5;
+ `
+
+ evaluated := testEval(t, input)
+ result, ok := evaluated.(*object.Integer)
+ if !ok {
+ t.Fatalf("Eval didn't return Integer. got=%T (%+v)", evaluated, evaluated)
+ }
+
+ if result.Value != 8 {
+ t.Fatalf("Eval didn't return right value. got=%d expected=%d", result, 8)
+ }
+ })
+
+ t.Run("Closure Fibonacci", func(t *testing.T) {
+ input := `
+ // fibonacci is a function that returns
+ // a function that returns an int.
+ let fibonacci = function() {
+ let n = 0;
+ let p = 1;
+ return function() {
+ let aux = n;
+ n = n+p;
+ p = aux;
+ return n;
+ }
+ }
+
+ let fib = fibonacci();
+ for (let i = 0; i < 10; i=i+1) {
+ fib();
+ }
+ `
+
+ evaluated := testEval(t, input)
+ result, ok := evaluated.(*object.Integer)
+ if !ok {
+ t.Fatalf("Eval didn't return Integer. got=%T (%+v)", evaluated, evaluated)
+ }
+
+ if result.Value != 55 {
+ t.Fatalf("Eval didn't return right value. got=%d expected=%d", result, 55)
+ }
+ })
+}
+
+func testEval(t *testing.T, input string) object.Object {
+ l := lexer.New(input)
+ p := parser.New(l)
+ program, ok := p.ParseProgram()
+ if !ok {
+ t.Errorf("Parsing errors: %v", p.Errors())
+ t.FailNow()
+ }
+ env := object.NewEnvironment()
+ ev := NewEvaluator()
+
+ return ev.Eval(program, env)
+}
+
+func testIntegerObject(t *testing.T, obj object.Object, expected int64) bool {
+ result, ok := obj.(*object.Integer)
+ if !ok {
+ t.Errorf("object is not Integer. got=%T (%+v)", obj, obj)
+ return false
+ }
+ if result.Value != expected {
+ t.Errorf("object has wrong value. got=%d, want=%d",
+ result.Value, expected)
+ return false
+ }
+
+ return true
+}
+
+func testFloatObject(t *testing.T, obj object.Object, expected float64) bool {
+ result, ok := obj.(*object.Float)
+ if !ok {
+ t.Errorf("object is not Float. got=%T (%+v)", obj, obj)
+ return false
+ }
+ if result.Value != expected {
+ t.Errorf("object has wrong value. got=%f, want=%f",
+ result.Value, expected)
+ return false
+ }
+
+ return true
+}
+
+func testStringObject(t *testing.T, obj object.Object, expected string) bool {
+ result, ok := obj.(*object.String)
+ if !ok {
+ t.Errorf("object is not String. got=%T (%+v)", obj, obj)
+ return false
+ }
+ if result.Value != expected {
+ t.Errorf("object has wrong value. got=%s, want=%s",
+ result.Value, expected)
+ return false
+ }
+
+ return true
+}
+
+func testBooleanObject(t *testing.T, obj object.Object, expected bool) bool {
+ result, ok := obj.(*object.Boolean)
+ if !ok {
+ t.Errorf("object is not Boolean. got=%T (%+v)", obj, obj)
+ return false
+ }
+ if result.Value != expected {
+ t.Errorf("object has wrong value. got=%t, want=%t",
+ result.Value, expected)
+ return false
+ }
+ return true
+}
+
+func testNullObject(t *testing.T, obj object.Object) bool {
+ if obj != NULL {
+ t.Errorf("object is not NULL. got=%T (%+v)", obj, obj)
+ return false
+ }
+ return true
+}
diff --git a/dsl/lexer/lexer.go b/dsl/lexer/lexer.go
new file mode 100644
index 0000000..fe07f70
--- /dev/null
+++ b/dsl/lexer/lexer.go
@@ -0,0 +1,290 @@
+package lexer
+
+import (
+ "github.com/ofux/deluge/dsl/token"
+ "strconv"
+)
+
+type Lexer struct {
+ input []rune
+ position int // current position in input (points to current char)
+ readPosition int // current reading position in input (after current char)
+ line int // current line in input
+ column int // current column in input
+ ch rune // current char under examination
+}
+
+func New(input string) *Lexer {
+ l := &Lexer{input: []rune(input), line: 1}
+ l.readChar()
+ return l
+}
+
+func (l *Lexer) NextToken() token.Token {
+ var tok token.Token
+
+ l.skipWhitespacesAndComments()
+
+ switch l.ch {
+ case '=':
+ if l.peekChar() == '=' {
+ ch := l.ch
+ l.readChar()
+ tok = token.Token{Type: token.EQ, Literal: string(ch) + string(l.ch)}
+ } else {
+ tok = newToken(token.ASSIGN, l.ch)
+ }
+ case '+':
+ if l.peekChar() == '+' {
+ ch := l.ch
+ l.readChar()
+ tok = token.Token{Type: token.ASSIGN_INC1, Literal: string(ch) + string(l.ch)}
+ } else if l.peekChar() == '=' {
+ ch := l.ch
+ l.readChar()
+ tok = token.Token{Type: token.ASSIGN_INC, Literal: string(ch) + string(l.ch)}
+ } else {
+ tok = newToken(token.PLUS, l.ch)
+ }
+ case '-':
+ if l.peekChar() == '-' {
+ ch := l.ch
+ l.readChar()
+ tok = token.Token{Type: token.ASSIGN_DEC1, Literal: string(ch) + string(l.ch)}
+ } else if l.peekChar() == '=' {
+ ch := l.ch
+ l.readChar()
+ tok = token.Token{Type: token.ASSIGN_DEC, Literal: string(ch) + string(l.ch)}
+ } else {
+ tok = newToken(token.MINUS, l.ch)
+ }
+ case '!':
+ if l.peekChar() == '=' {
+ ch := l.ch
+ l.readChar()
+ tok = token.Token{Type: token.NOT_EQ, Literal: string(ch) + string(l.ch)}
+ } else {
+ tok = newToken(token.BANG, l.ch)
+ }
+ case '/':
+ if l.peekChar() == '=' {
+ ch := l.ch
+ l.readChar()
+ tok = token.Token{Type: token.ASSIGN_DIV, Literal: string(ch) + string(l.ch)}
+ } else {
+ tok = newToken(token.SLASH, l.ch)
+ }
+ case '&':
+ if l.peekChar() == '&' {
+ ch := l.ch
+ l.readChar()
+ tok = token.Token{Type: token.AND, Literal: string(ch) + string(l.ch)}
+ } else {
+ tok = newToken(token.ILLEGAL, l.ch)
+ }
+ case '|':
+ if l.peekChar() == '|' {
+ ch := l.ch
+ l.readChar()
+ tok = token.Token{Type: token.OR, Literal: string(ch) + string(l.ch)}
+ } else {
+ tok = newToken(token.ILLEGAL, l.ch)
+ }
+ case '*':
+ if l.peekChar() == '=' {
+ ch := l.ch
+ l.readChar()
+ tok = token.Token{Type: token.ASSIGN_MULT, Literal: string(ch) + string(l.ch)}
+ } else {
+ tok = newToken(token.ASTERISK, l.ch)
+ }
+ case '%':
+ tok = newToken(token.MODULO, l.ch)
+ case '<':
+ if l.peekChar() == '=' {
+ ch := l.ch
+ l.readChar()
+ tok = token.Token{Type: token.LTE, Literal: string(ch) + string(l.ch)}
+ } else {
+ tok = newToken(token.LT, l.ch)
+ }
+ case '>':
+ if l.peekChar() == '=' {
+ ch := l.ch
+ l.readChar()
+ tok = token.Token{Type: token.GTE, Literal: string(ch) + string(l.ch)}
+ } else {
+ tok = newToken(token.GT, l.ch)
+ }
+ case ';':
+ tok = newToken(token.SEMICOLON, l.ch)
+ case ':':
+ tok = newToken(token.COLON, l.ch)
+ case ',':
+ tok = newToken(token.COMMA, l.ch)
+ case '{':
+ tok = newToken(token.LBRACE, l.ch)
+ case '}':
+ tok = newToken(token.RBRACE, l.ch)
+ case '(':
+ tok = newToken(token.LPAREN, l.ch)
+ case ')':
+ tok = newToken(token.RPAREN, l.ch)
+ case '"':
+ tok.Line = l.line
+ tok.Column = l.column
+ tok.Type = token.STRING
+ tok.Literal = l.readDoubleQuotedString()
+ l.readChar()
+ return tok
+ case '`':
+ tok.Line = l.line
+ tok.Column = l.column
+ tok.Type = token.STRING
+ tok.Literal = l.readBackQuotedString()
+ l.readChar()
+ return tok
+ case '[':
+ tok = newToken(token.LBRACKET, l.ch)
+ case ']':
+ tok = newToken(token.RBRACKET, l.ch)
+ case 0:
+ tok.Literal = ""
+ tok.Type = token.EOF
+ default:
+ if isLetter(l.ch) {
+ tok.Line = l.line
+ tok.Column = l.column
+ tok.Literal = l.readIdentifier()
+ tok.Type = token.LookupIdent(tok.Literal)
+ return tok
+ } else if isDigit(l.ch) {
+ tok.Line = l.line
+ tok.Column = l.column
+ tok.Literal, tok.Type = l.readNumber()
+ return tok
+ } else {
+ tok = newToken(token.ILLEGAL, l.ch)
+ }
+ }
+
+ tok.Line = l.line
+ tok.Column = l.column
+
+ l.readChar()
+ return tok
+}
+
+func (l *Lexer) skipWhitespacesAndComments() {
+ for l.ch == ' ' || l.ch == '\t' || l.ch == '\n' || l.ch == '\r' || l.skipLineComment() || l.skipBlockComment() {
+ l.readChar()
+ }
+}
+
+func (l *Lexer) skipLineComment() bool {
+ if l.ch == '/' && l.peekChar() == '/' {
+ for l.ch != 0 && l.ch != '\n' {
+ l.readChar()
+ }
+ return true
+ }
+ return false
+}
+
+func (l *Lexer) skipBlockComment() bool {
+ if l.ch == '/' && l.peekChar() == '*' {
+ for l.ch != 0 && !(l.ch == '*' && l.peekChar() == '/') {
+ l.readChar()
+ }
+ l.readChar()
+ return true
+ }
+ return false
+}
+
+func (l *Lexer) readChar() {
+ if l.readPosition >= len(l.input) {
+ l.ch = 0
+ } else {
+ l.ch = l.input[l.readPosition]
+ }
+ l.position = l.readPosition
+ l.readPosition += 1
+
+ if l.ch == '\n' {
+ l.line += 1
+ l.column = 0
+ } else {
+ l.column += 1
+ }
+}
+
+func (l *Lexer) peekChar() rune {
+ if l.readPosition >= len(l.input) {
+ return 0
+ } else {
+ return l.input[l.readPosition]
+ }
+}
+
+func (l *Lexer) readIdentifier() string {
+ position := l.position
+ for isLetter(l.ch) || isDigit(l.ch) {
+ l.readChar()
+ }
+ return string(l.input[position:l.position])
+}
+
+func (l *Lexer) readNumber() (string, token.TokenType) {
+ position := l.position
+ var tokType token.TokenType = token.INT
+ for isDigit(l.ch) || (tokType == token.INT && l.ch == '.') {
+ if l.ch == '.' {
+ tokType = token.FLOAT
+ }
+ l.readChar()
+ }
+ return string(l.input[position:l.position]), tokType
+}
+
+func (l *Lexer) readDoubleQuotedString() string {
+ position := l.position + 1
+ for {
+ prevCh := l.ch
+ l.readChar()
+ if (prevCh != '\\' && l.ch == '"') || l.ch == 0 || l.ch == '\n' {
+ break
+ }
+ }
+ str := string(l.input[position:l.position])
+
+ // handles character escaping
+ str, err := strconv.Unquote(`"` + str + `"`)
+ if err != nil {
+ panic(err)
+ }
+ return str
+}
+
+func (l *Lexer) readBackQuotedString() string {
+ position := l.position + 1
+ for {
+ l.readChar()
+ if l.ch == '`' || l.ch == 0 {
+ break
+ }
+ }
+ return string(l.input[position:l.position])
+}
+
+func isLetter(ch rune) bool {
+ return 'a' <= ch && ch <= 'z' || 'A' <= ch && ch <= 'Z' || ch == '_'
+}
+
+func isDigit(ch rune) bool {
+ return '0' <= ch && ch <= '9'
+}
+
+func newToken(tokenType token.TokenType, ch rune) token.Token {
+ return token.Token{Type: tokenType, Literal: string(ch)}
+}
diff --git a/dsl/lexer/lexer_test.go b/dsl/lexer/lexer_test.go
new file mode 100644
index 0000000..929de94
--- /dev/null
+++ b/dsl/lexer/lexer_test.go
@@ -0,0 +1,357 @@
+package lexer
+
+import (
+ "testing"
+
+ "github.com/ofux/deluge/dsl/token"
+)
+
+type tokenExpectation struct {
+ expectedType token.TokenType
+ expectedLiteral string
+ expectedLine int
+ expectedColumn int
+}
+
+func TestNextToken(t *testing.T) {
+ input := `let five = 5;
+let ten = 10;
+
+let add = function(x, y) {
+ x + y;
+};
+
+let result = add(five, ten);
+! - / * 5;
+5 < 10 > 5;
+
+if (5 < 10) {
+ return true;
+} else {
+ return false;
+}
+
+10 == 10;
+10 != 9;
+"foobar"
+"foo bar"
+"some utf8 : 🌧"
+[1, 2];
+{"foo": "bar"}
+{
+ "foo": "bar",
+ "🌨🌨🌨":"⛄"
+}
+
+// line comment
+1
+/* block comment */
+2
+/* multi
+line
+comment
+*/
+3
+4 // 2
+5/* inline comment */6
+7
+// line comment
+/* block comment */// yataa/* yotoo
+/*
+ // comment
+*/
+8
+
+1 <= 2 >= 3
+true && false || true
+i--
+i++
+i += 1
+i -= 1
+i *= 1
+i /= 1
+`
+
+ tests := []tokenExpectation{
+ {token.LET, "let", 1, 1},
+ {token.IDENT, "five", 1, 5},
+ {token.ASSIGN, "=", 1, 10},
+ {token.INT, "5", 1, 12},
+ {token.SEMICOLON, ";", 1, 13},
+ {token.LET, "let", 2, 1},
+ {token.IDENT, "ten", 2, 5},
+ {token.ASSIGN, "=", 2, 9},
+ {token.INT, "10", 2, 11},
+ {token.SEMICOLON, ";", 2, 13},
+ {token.LET, "let", 4, 1},
+ {token.IDENT, "add", 4, 5},
+ {token.ASSIGN, "=", 4, 9},
+ {token.FUNCTION, "function", 4, 11},
+ {token.LPAREN, "(", 4, 19},
+ {token.IDENT, "x", 4, 20},
+ {token.COMMA, ",", 4, 21},
+ {token.IDENT, "y", 4, 23},
+ {token.RPAREN, ")", 4, 24},
+ {token.LBRACE, "{", 4, 26},
+ {token.IDENT, "x", 5, 3},
+ {token.PLUS, "+", 5, 5},
+ {token.IDENT, "y", 5, 7},
+ {token.SEMICOLON, ";", 5, 8},
+ {token.RBRACE, "}", 6, 1},
+ {token.SEMICOLON, ";", 6, 2},
+ {token.LET, "let", 8, 1},
+ {token.IDENT, "result", 8, 5},
+ {token.ASSIGN, "=", 8, 12},
+ {token.IDENT, "add", 8, 14},
+ {token.LPAREN, "(", 8, 17},
+ {token.IDENT, "five", 8, 18},
+ {token.COMMA, ",", 8, 22},
+ {token.IDENT, "ten", 8, 24},
+ {token.RPAREN, ")", 8, 27},
+ {token.SEMICOLON, ";", 8, 28},
+ {token.BANG, "!", 9, 1},
+ {token.MINUS, "-", 9, 3},
+ {token.SLASH, "/", 9, 5},
+ {token.ASTERISK, "*", 9, 7},
+ {token.INT, "5", 9, 9},
+ {token.SEMICOLON, ";", 9, 10},
+ {token.INT, "5", 10, 1},
+ {token.LT, "<", 10, 3},
+ {token.INT, "10", 10, 5},
+ {token.GT, ">", 10, 8},
+ {token.INT, "5", 10, 10},
+ {token.SEMICOLON, ";", 10, 11},
+ {token.IF, "if", 12, 1},
+ {token.LPAREN, "(", 12, 4},
+ {token.INT, "5", 12, 5},
+ {token.LT, "<", 12, 7},
+ {token.INT, "10", 12, 9},
+ {token.RPAREN, ")", 12, 11},
+ {token.LBRACE, "{", 12, 13},
+ {token.RETURN, "return", 13, 2},
+ {token.TRUE, "true", 13, 9},
+ {token.SEMICOLON, ";", 13, 13},
+ {token.RBRACE, "}", 14, 1},
+ {token.ELSE, "else", 14, 3},
+ {token.LBRACE, "{", 14, 8},
+ {token.RETURN, "return", 15, 2},
+ {token.FALSE, "false", 15, 9},
+ {token.SEMICOLON, ";", 15, 14},
+ {token.RBRACE, "}", 16, 1},
+ {token.INT, "10", 18, 1},
+ {token.EQ, "==", 18, 5},
+ {token.INT, "10", 18, 7},
+ {token.SEMICOLON, ";", 18, 9},
+ {token.INT, "10", 19, 1},
+ {token.NOT_EQ, "!=", 19, 5},
+ {token.INT, "9", 19, 7},
+ {token.SEMICOLON, ";", 19, 8},
+ {token.STRING, "foobar", 20, 1},
+ {token.STRING, "foo bar", 21, 1},
+ {token.STRING, "some utf8 : 🌧", 22, 1},
+ {token.LBRACKET, "[", 23, 1},
+ {token.INT, "1", 23, 2},
+ {token.COMMA, ",", 23, 3},
+ {token.INT, "2", 23, 5},
+ {token.RBRACKET, "]", 23, 6},
+ {token.SEMICOLON, ";", 23, 7},
+ {token.LBRACE, "{", 24, 1},
+ {token.STRING, "foo", 24, 2},
+ {token.COLON, ":", 24, 7},
+ {token.STRING, "bar", 24, 9},
+ {token.RBRACE, "}", 24, 14},
+ {token.LBRACE, "{", 25, 1},
+ {token.STRING, "foo", 26, 2},
+ {token.COLON, ":", 26, 7},
+ {token.STRING, "bar", 26, 9},
+ {token.COMMA, ",", 26, 14},
+ {token.STRING, "🌨🌨🌨", 27, 2},
+ {token.COLON, ":", 27, 7},
+ {token.STRING, "⛄", 27, 8},
+ {token.RBRACE, "}", 28, 1},
+ {token.INT, "1", 31, 1},
+ {token.INT, "2", 33, 1},
+ {token.INT, "3", 38, 1},
+ {token.INT, "4", 39, 1},
+ {token.INT, "5", 40, 1},
+ {token.INT, "6", 40, 22},
+ {token.INT, "7", 41, 1},
+ {token.INT, "8", 47, 1},
+ {token.INT, "1", 49, 1},
+ {token.LTE, "<=", 49, 4},
+ {token.INT, "2", 49, 6},
+ {token.GTE, ">=", 49, 9},
+ {token.INT, "3", 49, 11},
+ {token.TRUE, "true", 50, 1},
+ {token.AND, "&&", 50, 7},
+ {token.FALSE, "false", 50, 9},
+ {token.OR, "||", 50, 16},
+ {token.TRUE, "true", 50, 18},
+ {token.IDENT, "i", 51, 1},
+ {token.ASSIGN_DEC1, "--", 51, 3},
+ {token.IDENT, "i", 52, 1},
+ {token.ASSIGN_INC1, "++", 52, 3},
+ {token.IDENT, "i", 53, 1},
+ {token.ASSIGN_INC, "+=", 53, 4},
+ {token.INT, "1", 53, 6},
+ {token.IDENT, "i", 54, 1},
+ {token.ASSIGN_DEC, "-=", 54, 4},
+ {token.INT, "1", 54, 6},
+ {token.IDENT, "i", 55, 1},
+ {token.ASSIGN_MULT, "*=", 55, 4},
+ {token.INT, "1", 55, 6},
+ {token.IDENT, "i", 56, 1},
+ {token.ASSIGN_DIV, "/=", 56, 4},
+ {token.INT, "1", 56, 6},
+ {token.EOF, "", 57, 1},
+ }
+
+ l := New(input)
+
+ for i, tt := range tests {
+ tok := l.NextToken()
+ testToken(t, tok, tt, i)
+ }
+}
+
+func TestReadDoubleQuotedString(t *testing.T) {
+ input := `"aaa\nbbb"
+"aaa\"bbb"
+"aaa
+bbb"
+`
+
+ tests := []tokenExpectation{
+ {token.STRING, "aaa\nbbb", 1, 1},
+ {token.STRING, "aaa\"bbb", 2, 1},
+ {token.STRING, "aaa", 3, 1},
+ {token.IDENT, "bbb", 4, 1},
+ {token.STRING, "", 4, 4},
+ {token.EOF, "", 5, 1},
+ }
+
+ l := New(input)
+
+ for i, tt := range tests {
+ tok := l.NextToken()
+ testToken(t, tok, tt, i)
+ }
+}
+
+func TestReadBackQuotedString(t *testing.T) {
+ input := "`aaa\\nbbb"
+ input += "\n"
+ input += "ccc`"
+
+ tests := []tokenExpectation{
+ {token.STRING, "aaa\\nbbb\nccc", 1, 1},
+ {token.EOF, "", 2, 5},
+ }
+
+ l := New(input)
+
+ for i, tt := range tests {
+ tok := l.NextToken()
+ testToken(t, tok, tt, i)
+ }
+}
+
+func TestForLoop(t *testing.T) {
+ input := `
+for (let i=0; i < 10; i=i+1) {
+}
+`
+
+ tests := []tokenExpectation{
+ {token.FOR, "for", 2, 1},
+ {token.LPAREN, "(", 2, 5},
+ {token.LET, "let", 2, 6},
+ {token.IDENT, "i", 2, 10},
+ {token.ASSIGN, "=", 2, 11},
+ {token.INT, "0", 2, 12},
+ {token.SEMICOLON, ";", 2, 13},
+ {token.IDENT, "i", 2, 15},
+ {token.LT, "<", 2, 17},
+ {token.INT, "10", 2, 19},
+ {token.SEMICOLON, ";", 2, 21},
+ {token.IDENT, "i", 2, 23},
+ {token.ASSIGN, "=", 2, 24},
+ {token.IDENT, "i", 2, 25},
+ {token.PLUS, "+", 2, 26},
+ {token.INT, "1", 2, 27},
+ {token.RPAREN, ")", 2, 28},
+ {token.LBRACE, "{", 2, 30},
+ {token.RBRACE, "}", 3, 1},
+ {token.EOF, "", 4, 1},
+ }
+
+ l := New(input)
+
+ for i, tt := range tests {
+ tok := l.NextToken()
+ testToken(t, tok, tt, i)
+ }
+}
+
+func TestFloats(t *testing.T) {
+ input := `
+33.0
+42.42
+67.6898 29938928.7
+`
+
+ tests := []tokenExpectation{
+ {token.FLOAT, "33.0", 2, 1},
+ {token.FLOAT, "42.42", 3, 1},
+ {token.FLOAT, "67.6898", 4, 1},
+ {token.FLOAT, "29938928.7", 4, 9},
+ }
+
+ l := New(input)
+
+ for i, tt := range tests {
+ tok := l.NextToken()
+ testToken(t, tok, tt, i)
+ }
+}
+
+func TestOperators(t *testing.T) {
+ input := `
+5 % 2
+`
+
+ tests := []tokenExpectation{
+ {token.INT, "5", 2, 1},
+ {token.MODULO, "%", 2, 3},
+ {token.INT, "2", 2, 5},
+ }
+
+ l := New(input)
+
+ for i, tt := range tests {
+ tok := l.NextToken()
+ testToken(t, tok, tt, i)
+ }
+}
+
+func testToken(t *testing.T, tok token.Token, tt tokenExpectation, i int) {
+ if tok.Type != tt.expectedType {
+ t.Fatalf("tests[%d] - tokentype wrong. expected=%q, got=%q",
+ i, tt.expectedType, tok.Type)
+ }
+
+ if tok.Literal != tt.expectedLiteral {
+ t.Fatalf("tests[%d] - literal wrong. expected=%q, got=%q",
+ i, tt.expectedLiteral, tok.Literal)
+ }
+
+ if tok.Line != tt.expectedLine {
+ t.Fatalf("tests[%d] - line wrong. expected=%d, got=%d",
+ i, tt.expectedLine, tok.Line)
+ }
+
+ if tok.Column != tt.expectedColumn {
+ t.Fatalf("tests[%d] - column wrong. expected=%d, got=%d",
+ i, tt.expectedColumn, tok.Column)
+ }
+}
diff --git a/dsl/main.go b/dsl/main.go
new file mode 100644
index 0000000..a4656b2
--- /dev/null
+++ b/dsl/main.go
@@ -0,0 +1,57 @@
+package main
+
+import (
+ "fmt"
+ "github.com/ofux/deluge/dsl/repl"
+ "os"
+ "os/user"
+)
+
+/*
+
+TODO:
+ + add line + column in tokens so we can have better error messages
+ + support UTF8
+ + add stacktraces to runtime errors
+ + support character escaping in double-quoted strings
+ + support back-quoted strings like in Go
+ + 'if' should not be an expression but a statement
+ + support 'else if'
+ + support comments // and / * * /
+ + assign statement =
+ + 'for' loop
+ + operators <= and >=
+ + operators || and &&
+ + statements ++ -- += -= *= /=
+ + floats
+ + operator %
+ + handle scopes (environments) properly
+ + rename 'fn' to 'function'
+ - while loop
+ - async / async "group" / wait / wait "group"
+ - add built-in functions:
+ + exit
+ + assert
+ - http
+ - mqtt
+ - tcp
+ - grpc
+ + push (arrays)
+ - indexOf (arrays)
+ - indexOf (strings)
+ + split (strings)
+ -
+ - check variable declaration / assignment at compile time
+
+*/
+
+func main() {
+ usr, err := user.Current()
+ if err != nil {
+ panic(err)
+ }
+ fmt.Printf("Hello %s! This is the Deluge programming language!\n",
+ usr.Username)
+ fmt.Printf("Feel free to type in commands\n")
+ repl.Start(os.Stdin, os.Stdout)
+}
diff --git a/dsl/object/environment.go b/dsl/object/environment.go
new file mode 100644
index 0000000..d6b5e8d
--- /dev/null
+++ b/dsl/object/environment.go
@@ -0,0 +1,51 @@
+package object
+
+func NewEnclosedEnvironment(outer *Environment) *Environment {
+ env := NewEnvironment()
+ env.outer = outer
+ return env
+}
+
+func NewEnvironment() *Environment {
+ return &Environment{
+ store: make(map[string]Object),
+ outer: nil,
+ }
+}
+
+type Environment struct {
+ store map[string]Object
+ outer *Environment
+}
+
+func (e *Environment) getWithEnv(name string) (*Environment, Object, bool) {
+ env := e
+ obj, ok := e.store[name]
+ if !ok && e.outer != nil {
+ env, obj, ok = e.outer.getWithEnv(name)
+ }
+ return env, obj, ok
+}
+
+func (e *Environment) Get(name string) (Object, bool) {
+ _, obj, ok := e.getWithEnv(name)
+ return obj, ok
+}
+
+func (e *Environment) Add(name string, val Object) bool {
+ _, ok := e.store[name]
+ if ok {
+ return false
+ }
+ e.store[name] = val
+ return true
+}
+
+func (e *Environment) Set(name string, val Object) bool {
+ env, _, ok := e.getWithEnv(name)
+ if !ok {
+ return false
+ }
+ env.store[name] = val
+ return true
+}
diff --git a/dsl/object/environment_test.go b/dsl/object/environment_test.go
new file mode 100644
index 0000000..d20d0ee
--- /dev/null
+++ b/dsl/object/environment_test.go
@@ -0,0 +1,193 @@
+package object
+
+import "testing"
+
+func TestEnvironment_Get(t *testing.T) {
+ t.Run("without outer env", func(t *testing.T) {
+ env := NewEnvironment()
+ env.store["a"] = &Integer{Value: 42}
+ env.store["b"] = &Integer{Value: 73}
+
+ a, ok := env.Get("a")
+ if !ok {
+ t.Error("expected 'a' to be in the environment")
+ }
+ aa := a.(*Integer)
+ if aa.Value != 42 {
+ t.Errorf("expected 'a' to be equal to %d, got %d", 42, aa.Value)
+ }
+
+ b, ok := env.Get("b")
+ if !ok {
+ t.Error("expected 'b' to be in the environment")
+ }
+ bb := b.(*Integer)
+ if bb.Value != 73 {
+ t.Errorf("expected 'b' to be equal to %d, got %d", 73, bb.Value)
+ }
+
+ c, ok := env.Get("c")
+ if ok {
+ t.Error("expected 'c' NOT to be in the environment")
+ }
+ if c != nil {
+ t.Errorf("expected 'c' to be nil, got %v", c)
+ }
+ })
+
+ t.Run("with one outer env", func(t *testing.T) {
+ outer := NewEnvironment()
+ outer.store["a"] = &Integer{Value: 1}
+ env := NewEnclosedEnvironment(outer)
+ env.store["b"] = &Integer{Value: 2}
+
+ a, ok := env.Get("a")
+ if !ok {
+ t.Error("expected 'a' to be in the environment")
+ }
+ aa := a.(*Integer)
+ if aa.Value != 1 {
+ t.Errorf("expected 'a' to be equal to %d, got %d", 1, aa.Value)
+ }
+
+ b, ok := env.Get("b")
+ if !ok {
+ t.Error("expected 'b' to be in the environment")
+ }
+ bb := b.(*Integer)
+ if bb.Value != 2 {
+ t.Errorf("expected 'b' to be equal to %d, got %d", 2, bb.Value)
+ }
+
+ outerB, ok := outer.Get("b")
+ if ok {
+ t.Error("expected 'b' NOT to be in the outer environment")
+ }
+ if outerB != nil {
+ t.Errorf("expected 'b' to be nil, got %v", outerB)
+ }
+ })
+}
+
+func TestEnvironment_Add(t *testing.T) {
+ t.Run("simple add", func(t *testing.T) {
+ env := NewEnvironment()
+
+ if _, ok := env.store["a"]; ok {
+ t.Error("expected 'a' NOT to be in the environment")
+ }
+
+ ok := env.Add("a", &Integer{Value: 1})
+ if !ok {
+ t.Error("expected env.Add to return true")
+ }
+
+ a, ok := env.store["a"]
+ if !ok {
+ t.Error("expected 'a' to be in the environment")
+ }
+ aa := a.(*Integer)
+ if aa.Value != 1 {
+ t.Errorf("expected 'a' to be equal to %d, got %d", 1, aa.Value)
+ }
+ })
+
+ t.Run("add already existing variable", func(t *testing.T) {
+ env := NewEnvironment()
+ env.store["a"] = &Integer{Value: 1}
+
+ ok := env.Add("a", &Integer{Value: 2})
+ if ok {
+ t.Error("expected env.Add to return false")
+ }
+
+ a, ok := env.store["a"]
+ if !ok {
+ t.Error("expected 'a' to be in the environment")
+ }
+ aa := a.(*Integer)
+ if aa.Value != 1 {
+ t.Errorf("expected 'a' to be equal to %d, got %d", 1, aa.Value)
+ }
+ })
+
+ t.Run("add already existing variable in outer", func(t *testing.T) {
+ outer := NewEnvironment()
+ outer.store["a"] = &Integer{Value: 1}
+ env := NewEnclosedEnvironment(outer)
+
+ ok := env.Add("a", &Integer{Value: 2})
+ if !ok {
+ t.Error("expected env.Add to return true")
+ }
+
+ a, ok := env.store["a"]
+ if !ok {
+ t.Error("expected 'a' to be in the environment")
+ }
+ aa := a.(*Integer)
+ if aa.Value != 2 {
+ t.Errorf("expected 'a' to be equal to %d, got %d", 2, aa.Value)
+ }
+ })
+}
+
+func TestEnvironment_Set(t *testing.T) {
+ t.Run("simple set", func(t *testing.T) {
+ env := NewEnvironment()
+ env.store["a"] = &Integer{Value: 1}
+
+ ok := env.Set("a", &Integer{Value: 2})
+ if !ok {
+ t.Error("expected env.Set to return true")
+ }
+
+ a, ok := env.store["a"]
+ if !ok {
+ t.Error("expected 'a' to be in the environment")
+ }
+ aa := a.(*Integer)
+ if aa.Value != 2 {
+ t.Errorf("expected 'a' to be equal to %d, got %d", 2, aa.Value)
+ }
+ })
+
+ t.Run("set not existing variable", func(t *testing.T) {
+ env := NewEnvironment()
+
+ ok := env.Set("a", &Integer{Value: 1})
+ if ok {
+ t.Error("expected env.Set to return false")
+ }
+
+ _, ok = env.store["a"]
+ if ok {
+ t.Error("expected 'a' NOT to be in the environment")
+ }
+ })
+
+ t.Run("set variable in outer", func(t *testing.T) {
+ outer := NewEnvironment()
+ outer.store["a"] = &Integer{Value: 1}
+ env := NewEnclosedEnvironment(outer)
+
+ ok := env.Set("a", &Integer{Value: 2})
+ if !ok {
+ t.Error("expected env.Add to return true")
+ }
+
+ a, ok := env.store["a"]
+ if ok {
+ t.Error("expected 'a' NOT to be in the environment")
+ }
+
+ a, ok = outer.store["a"]
+ if !ok {
+ t.Error("expected 'a' to be in the outer environment")
+ }
+ aa := a.(*Integer)
+ if aa.Value != 2 {
+ t.Errorf("expected 'a' to be equal to %d, got %d", 2, aa.Value)
+ }
+ })
+}
diff --git a/dsl/object/object.go b/dsl/object/object.go
new file mode 100644
index 0000000..b7a16fc
--- /dev/null
+++ b/dsl/object/object.go
@@ -0,0 +1,283 @@
+package object
+
+import (
+ "bytes"
+ "errors"
+ "fmt"
+ "github.com/ofux/deluge/dsl/ast"
+ "github.com/ofux/deluge/dsl/token"
+ "github.com/ofux/floa"
+ "strconv"
+ "strings"
+)
+
+type BuiltinFunction func(node ast.Node, args ...Object) Object
+
+type ObjectType string
+
+const (
+ NULL_OBJ ObjectType = "NULL"
+ ERROR_OBJ = "ERROR"
+ INTEGER_OBJ = "INTEGER"
+ FLOAT_OBJ = "FLOAT"
+ BOOLEAN_OBJ = "BOOLEAN"
+ STRING_OBJ = "STRING"
+ RETURN_VALUE_OBJ = "RETURN_VALUE"
+ FUNCTION_OBJ = "FUNCTION"
+ BUILTIN_OBJ = "BUILTIN"
+ ARRAY_OBJ = "ARRAY"
+ HASH_OBJ = "HASH"
+)
+
+type HashKey string
+
+type Hashable interface {
+ HashKey() HashKey
+}
+
+type Object interface {
+ Type() ObjectType
+ Inspect() string
+ Equals(other Object) bool
+}
+
+type Integer struct {
+ Value int64
+}
+
+func (i *Integer) Type() ObjectType { return INTEGER_OBJ }
+func (i *Integer) Inspect() string { return fmt.Sprintf("%d", i.Value) }
+func (i *Integer) Equals(other Object) bool {
+ typed, ok := other.(*Integer)
+ return ok && typed.Value == i.Value
+}
+func (i *Integer) HashKey() HashKey {
+ return HashKey(strconv.FormatInt(i.Value, 10))
+}
+
+type Float struct {
+ Value float64
+}
+
+func (f *Float) Type() ObjectType { return FLOAT_OBJ }
+func (f *Float) Inspect() string { return fmt.Sprintf("%f", f.Value) }
+func (f *Float) Equals(other Object) bool {
+ typed, ok := other.(*Float)
+ return ok && floa.NearlyEqual(typed.Value, f.Value, 0.0000001)
+}
+
+type Boolean struct {
+ Value bool
+}
+
+func (b *Boolean) Type() ObjectType { return BOOLEAN_OBJ }
+func (b *Boolean) Inspect() string { return fmt.Sprintf("%t", b.Value) }
+func (b *Boolean) Equals(other Object) bool {
+ typed, ok := other.(*Boolean)
+ return ok && typed.Value == b.Value
+}
+
+type Null struct{}
+
+func (n *Null) Type() ObjectType { return NULL_OBJ }
+func (n *Null) Inspect() string { return "null" }
+func (n *Null) Equals(other Object) bool {
+ _, ok := other.(*Null)
+ return ok
+}
+
+type ReturnValue struct {
+ Value Object
+}
+
+func (rv *ReturnValue) Type() ObjectType { return RETURN_VALUE_OBJ }
+func (rv *ReturnValue) Inspect() string { return rv.Value.Inspect() }
+func (rv *ReturnValue) Equals(other Object) bool {
+ otherRV, ok := other.(*ReturnValue)
+ return ok && rv.Value.Equals(otherRV.Value)
+}
+
+type Error struct {
+ Message string `json:"message"`
+ StackToken []token.Token `json:"stacktrace"`
+}
+
+func (e *Error) Type() ObjectType { return ERROR_OBJ }
+func (e *Error) Inspect() string {
+ stacktrace := fmt.Sprintf("RUNTIME ERROR: %s", e.Message)
+ if e.StackToken != nil {
+ for _, tok := range e.StackToken {
+ stacktrace += fmt.Sprintf("\n\tat %s (line %d, col %d)", tok.Literal, tok.Line, tok.Column)
+ }
+ }
+ return stacktrace
+}
+func (e *Error) Equals(other Object) bool {
+ typed, ok := other.(*Error)
+ return ok && typed.Message == e.Message
+}
+func (e *Error) AddCallToStack(call *ast.CallExpression) {
+ e.StackToken = append(e.StackToken, call.Function.TokenDetails())
+}
+
+type Function struct {
+ Parameters []*ast.Identifier
+ Body *ast.BlockStatement
+ Env *Environment
+}
+
+func (f *Function) Type() ObjectType { return FUNCTION_OBJ }
+func (f *Function) Inspect() string {
+ var out bytes.Buffer
+
+ params := []string{}
+ for _, p := range f.Parameters {
+ params = append(params, p.String())
+ }
+
+ out.WriteString("function")
+ out.WriteString("(")
+ out.WriteString(strings.Join(params, ", "))
+ out.WriteString(") {\n")
+ out.WriteString(f.Body.String())
+ out.WriteString("\n}")
+
+ return out.String()
+}
+func (f *Function) Equals(other Object) bool {
+ return f == other
+}
+
+type String struct {
+ Value string
+}
+
+func (s *String) Type() ObjectType { return STRING_OBJ }
+func (s *String) Inspect() string { return s.Value }
+func (s *String) Equals(other Object) bool {
+ typed, ok := other.(*String)
+ return ok && typed.Value == s.Value
+}
+func (s *String) HashKey() HashKey {
+ return HashKey(s.Value)
+}
+
+type Builtin struct {
+ Fn BuiltinFunction
+}
+
+func (b *Builtin) Type() ObjectType { return BUILTIN_OBJ }
+func (b *Builtin) Inspect() string { return "builtin function" }
+func (b *Builtin) Equals(other Object) bool {
+ return b == other
+}
+
+type Array struct {
+ Elements []Object
+}
+
+func (ao *Array) Type() ObjectType { return ARRAY_OBJ }
+func (ao *Array) Inspect() string {
+ var out bytes.Buffer
+
+ elements := []string{}
+ for _, e := range ao.Elements {
+ elements = append(elements, e.Inspect())
+ }
+
+ out.WriteString("[")
+ out.WriteString(strings.Join(elements, ", "))
+ out.WriteString("]")
+
+ return out.String()
+}
+func (ao *Array) Equals(other Object) bool {
+ return ao == other
+}
+
+type Hash struct {
+ Pairs map[HashKey]Object
+ IsImmutable bool
+}
+
+func (h *Hash) Type() ObjectType { return HASH_OBJ }
+func (h *Hash) Inspect() string {
+ var out bytes.Buffer
+
+ pairs := []string{}
+ for k, v := range h.Pairs {
+ pairs = append(pairs, fmt.Sprintf("%s: %s", k, v.Inspect()))
+ }
+
+ if h.IsImmutable {
+ out.WriteString("#")
+ }
+ out.WriteString("{")
+ out.WriteString(strings.Join(pairs, ", "))
+ out.WriteString("}")
+
+ return out.String()
+}
+func (h *Hash) Equals(other Object) bool {
+ return h == other
+}
+func (h *Hash) Get(key string) (Object, bool) {
+ r, ok := h.Pairs[HashKey(key)]
+ return r, ok
+}
+
+// GetAs retrieves the Object for the given key and checks its type.
+// It returns the Object (if any), true if the key was found,
+// and an error if the key was not found or the object was not of expected type.
+func (h *Hash) GetAs(key string, expectedType ObjectType) (Object, bool, error) {
+ v, ok := h.Pairs[HashKey(key)]
+ if !ok {
+ return nil, false, errors.New(fmt.Sprintf("missing '%s' field", key))
+ }
+ if v.Type() != expectedType {
+ return nil, true, errors.New(fmt.Sprintf("'%s' should be of type %s but was %s", key, expectedType, v.Type()))
+ }
+ return v, true, nil
+}
+func (h *Hash) GetAsString(key string) (*String, bool, error) {
+ v, ok, err := h.GetAs(key, STRING_OBJ)
+ if !ok || err != nil {
+ return nil, ok, err
+ }
+ return v.(*String), true, nil
+}
+func (h *Hash) GetAsInt(key string) (*Integer, bool, error) {
+ v, ok, err := h.GetAs(key, INTEGER_OBJ)
+ if !ok || err != nil {
+ return nil, ok, err
+ }
+ return v.(*Integer), true, nil
+}
+func (h *Hash) GetAsFloat(key string) (*Float, bool, error) {
+ v, ok, err := h.GetAs(key, FLOAT_OBJ)
+ if !ok || err != nil {
+ return nil, ok, err
+ }
+ return v.(*Float), true, nil
+}
+func (h *Hash) GetAsBool(key string) (*Boolean, bool, error) {
+ v, ok, err := h.GetAs(key, BOOLEAN_OBJ)
+ if !ok || err != nil {
+ return nil, ok, err
+ }
+ return v.(*Boolean), true, nil
+}
+func (h *Hash) GetAsArray(key string) (*Array, bool, error) {
+ v, ok, err := h.GetAs(key, ARRAY_OBJ)
+ if !ok || err != nil {
+ return nil, ok, err
+ }
+ return v.(*Array), true, nil
+}
+func (h *Hash) GetAsHash(key string) (*Hash, bool, error) {
+ v, ok, err := h.GetAs(key, HASH_OBJ)
+ if !ok || err != nil {
+ return nil, ok, err
+ }
+ return v.(*Hash), true, nil
+}
diff --git a/dsl/object/object_test.go b/dsl/object/object_test.go
new file mode 100644
index 0000000..43d0368
--- /dev/null
+++ b/dsl/object/object_test.go
@@ -0,0 +1,206 @@
+package object
+
+import (
+ "github.com/stretchr/testify/assert"
+ "testing"
+)
+
+func TestStringHashKey(t *testing.T) {
+ hello1 := &String{Value: "Hello World"}
+ hello2 := &String{Value: "Hello World"}
+ diff1 := &String{Value: "My name is johnny"}
+ diff2 := &String{Value: "My name is johnny"}
+
+ if hello1.HashKey() != hello2.HashKey() {
+ t.Errorf("strings with same content have different hash keys")
+ }
+
+ if diff1.HashKey() != diff2.HashKey() {
+ t.Errorf("strings with same content have different hash keys")
+ }
+
+ if hello1.HashKey() == diff1.HashKey() {
+ t.Errorf("strings with different content have same hash keys")
+ }
+}
+
+func TestIntegerHashKey(t *testing.T) {
+ one1 := &Integer{Value: 1}
+ one2 := &Integer{Value: 1}
+ two1 := &Integer{Value: 2}
+ two2 := &Integer{Value: 2}
+
+ if one1.HashKey() != one2.HashKey() {
+ t.Errorf("integers with same content have twoerent hash keys")
+ }
+
+ if two1.HashKey() != two2.HashKey() {
+ t.Errorf("integers with same content have twoerent hash keys")
+ }
+
+ if one1.HashKey() == two1.HashKey() {
+ t.Errorf("integers with twoerent content have same hash keys")
+ }
+}
+
+func TestEquals(t *testing.T) {
+ fn := &Function{}
+ bl := &Builtin{}
+ h := &Hash{}
+ arr := &Array{}
+
+ tests := []struct {
+ input1 Object
+ input2 Object
+ expected bool
+ }{
+ {&Integer{Value: 1}, &Integer{Value: 1}, true},
+ {&Integer{Value: 1}, &Integer{Value: 2}, false},
+ {&Integer{Value: 1}, &String{Value: "1"}, false},
+ {&Integer{Value: 1}, &Float{Value: 1}, false},
+
+ {&Float{Value: 1}, &Float{Value: 1}, true},
+ {&Float{Value: 1}, &Float{Value: 2}, false},
+ {&Float{Value: 1}, &String{Value: "1"}, false},
+ {&Float{Value: 1}, &Integer{Value: 1}, false},
+
+ {&Boolean{Value: true}, &Boolean{Value: true}, true},
+ {&Boolean{Value: true}, &Boolean{Value: false}, false},
+ {&Boolean{Value: true}, &String{Value: "true"}, false},
+
+ {&String{Value: "1"}, &String{Value: "1"}, true},
+ {&String{Value: "1"}, &String{Value: "2"}, false},
+
+ {&Error{Message: "1"}, &Error{Message: "1"}, true},
+ {&Error{Message: "1"}, &Error{Message: "2"}, false},
+ {&Error{Message: "1"}, &String{Value: "1"}, false},
+
+ {&Null{}, &Null{}, true},
+ {&Null{}, &String{Value: "null"}, false},
+ {&Null{}, &Integer{Value: 0}, false},
+ {&Null{}, &Function{}, false},
+ {&Null{}, &Boolean{Value: false}, false},
+ {&Null{}, &Float{Value: 0}, false},
+ {&Null{}, &Hash{}, false},
+ {&Null{}, &Array{}, false},
+
+ {&ReturnValue{Value: &String{Value: "1"}}, &ReturnValue{Value: &String{Value: "1"}}, true},
+ {&ReturnValue{Value: &String{Value: "1"}}, &ReturnValue{Value: &String{Value: "2"}}, false},
+ {&ReturnValue{Value: fn}, &ReturnValue{Value: fn}, true},
+ {&ReturnValue{Value: &Function{}}, &ReturnValue{Value: &Function{}}, false},
+
+ {&Function{}, &Function{}, false},
+ {fn, fn, true},
+
+ {&Builtin{}, &Builtin{}, false},
+ {bl, bl, true},
+
+ {&Hash{}, &Hash{}, false},
+ {h, h, true},
+
+ {&Array{}, &Array{}, false},
+ {arr, arr, true},
+ }
+
+ for _, tt := range tests {
+ assert.Equal(t, tt.expected, tt.input1.Equals(tt.input2))
+ assert.Equal(t, tt.expected, tt.input2.Equals(tt.input1))
+ }
+}
+
+func TestHash_GetAs(t *testing.T) {
+ hash := &Hash{
+ Pairs: map[HashKey]Object{
+ HashKey("a"): &String{"foo"},
+ HashKey("b"): &Integer{42},
+ HashKey("c"): &Hash{Pairs: map[HashKey]Object{}},
+ HashKey("d"): &Array{[]Object{}},
+ HashKey("e"): &Float{1.2},
+ HashKey("f"): &Boolean{true},
+ },
+ }
+
+ va, ok, err := hash.GetAsString("a")
+ assert.NoError(t, err)
+ assert.True(t, ok)
+ assert.Equal(t, "foo", va.Value)
+
+ vb, ok, err := hash.GetAsInt("b")
+ assert.NoError(t, err)
+ assert.True(t, ok)
+ assert.Equal(t, int64(42), vb.Value)
+
+ vc, ok, err := hash.GetAsHash("c")
+ assert.NoError(t, err)
+ assert.True(t, ok)
+ assert.NotNil(t, vc.Pairs)
+
+ vd, ok, err := hash.GetAsArray("d")
+ assert.NoError(t, err)
+ assert.True(t, ok)
+ assert.NotNil(t, vd.Elements)
+
+ ve, ok, err := hash.GetAsFloat("e")
+ assert.NoError(t, err)
+ assert.True(t, ok)
+ assert.Equal(t, float64(1.2), ve.Value)
+
+ vf, ok, err := hash.GetAsBool("f")
+ assert.NoError(t, err)
+ assert.True(t, ok)
+ assert.Equal(t, true, vf.Value)
+
+ // Wrong type
+ _, ok, err = hash.GetAsString("b")
+ assert.Error(t, err)
+ assert.True(t, ok)
+ // Key does not exist
+ _, ok, err = hash.GetAsString("bar")
+ assert.Error(t, err)
+ assert.False(t, ok)
+
+ // Wrong type
+ _, ok, err = hash.GetAsInt("a")
+ assert.Error(t, err)
+ assert.True(t, ok)
+ // Key does not exist
+ _, ok, err = hash.GetAsInt("bar")
+ assert.Error(t, err)
+ assert.False(t, ok)
+
+ // Wrong type
+ _, ok, err = hash.GetAsFloat("a")
+ assert.Error(t, err)
+ assert.True(t, ok)
+ // Key does not exist
+ _, ok, err = hash.GetAsFloat("bar")
+ assert.Error(t, err)
+ assert.False(t, ok)
+
+ // Wrong type
+ _, ok, err = hash.GetAsBool("a")
+ assert.Error(t, err)
+ assert.True(t, ok)
+ // Key does not exist
+ _, ok, err = hash.GetAsBool("bar")
+ assert.Error(t, err)
+ assert.False(t, ok)
+
+ // Wrong type
+ _, ok, err = hash.GetAsArray("a")
+ assert.Error(t, err)
+ assert.True(t, ok)
+ // Key does not exist
+ _, ok, err = hash.GetAsArray("bar")
+ assert.Error(t, err)
+ assert.False(t, ok)
+
+ // Wrong type
+ _, ok, err = hash.GetAsHash("a")
+ assert.Error(t, err)
+ assert.True(t, ok)
+ // Key does not exist
+ _, ok, err = hash.GetAsHash("bar")
+ assert.Error(t, err)
+ assert.False(t, ok)
+}
diff --git a/dsl/object/object_utils.go b/dsl/object/object_utils.go
new file mode 100644
index 0000000..fb69a4e
--- /dev/null
+++ b/dsl/object/object_utils.go
@@ -0,0 +1,154 @@
+package object
+
+import (
+ "errors"
+ "fmt"
+ "math"
+ "reflect"
+)
+
+func IsNumeric(object Object) bool {
+ return object.Type() == INTEGER_OBJ || object.Type() == FLOAT_OBJ
+}
+
+func IsInteger(object Object) bool {
+ return object.Type() == INTEGER_OBJ
+}
+
+func DeepEquals(o1, o2 Object) bool {
+ if o1.Type() != o2.Type() {
+ return false
+ }
+ switch o1.Type() {
+ case HASH_OBJ:
+ o1 := o1.(*Hash)
+ o2 := o2.(*Hash)
+ if len(o1.Pairs) != len(o2.Pairs) {
+ return false
+ }
+ for k, v1 := range o1.Pairs {
+ v2, ok := o2.Pairs[k]
+ if !ok {
+ return false
+ }
+ if !DeepEquals(v1, v2) {
+ return false
+ }
+ }
+ case ARRAY_OBJ:
+ o1 := o1.(*Array)
+ o2 := o2.(*Array)
+ if len(o1.Elements) != len(o2.Elements) {
+ return false
+ }
+ for i, v := range o1.Elements {
+ if !DeepEquals(v, o2.Elements[i]) {
+ return false
+ }
+ }
+ default:
+ return o1.Equals(o2)
+ }
+ return true
+}
+
+func ToObject(in interface{}) (Object, error) {
+ switch in := in.(type) {
+ case string:
+ return &String{Value: in}, nil
+ case int:
+ return &Integer{Value: int64(in)}, nil
+ case int8:
+ return &Integer{Value: int64(in)}, nil
+ case int16:
+ return &Integer{Value: int64(in)}, nil
+ case int32:
+ return &Integer{Value: int64(in)}, nil
+ case int64:
+ return &Integer{Value: in}, nil
+ case uint:
+ return &Integer{Value: int64(in)}, nil
+ case uint8:
+ return &Integer{Value: int64(in)}, nil
+ case uint16:
+ return &Integer{Value: int64(in)}, nil
+ case uint32:
+ return &Integer{Value: int64(in)}, nil
+ case uint64:
+ return &Integer{Value: int64(in)}, nil
+ case float64:
+ if in == math.Trunc(in) && in < math.MaxInt64 {
+ return &Integer{Value: int64(in)}, nil
+ }
+ return &Float{Value: in}, nil
+ case float32:
+ if float64(in) == math.Trunc(float64(in)) && in < math.MaxInt64 {
+ return &Integer{Value: int64(in)}, nil
+ }
+ return &Float{Value: float64(in)}, nil
+ case bool:
+ return &Boolean{Value: in}, nil
+ case []interface{}:
+ elements := make([]Object, 0, len(in))
+ for _, v := range in {
+ obj, err := ToObject(v)
+ if err != nil {
+ return nil, err
+ }
+ elements = append(elements, obj)
+ }
+ return &Array{Elements: elements}, nil
+ case map[string]interface{}:
+ pairs := make(map[HashKey]Object)
+ for k, v := range in {
+ obj, err := ToObject(v)
+ if err != nil {
+ return nil, err
+ }
+ key := &String{Value: k}
+ pairs[key.HashKey()] = obj
+ }
+ return &Hash{Pairs: pairs}, nil
+ default:
+ return nil, errors.New(fmt.Sprintf("Cannot convert value of type %s to Object", reflect.TypeOf(in)))
+ }
+}
+
+func FromObject(in Object) (interface{}, error) {
+ switch in := in.(type) {
+ case *String:
+ return in.Value, nil
+ case *Integer:
+ return in.Value, nil
+ case *Float:
+ return in.Value, nil
+ case *Boolean:
+ return in.Value, nil
+ case *Null:
+ return nil, nil
+ case *ReturnValue:
+ return FromObject(in.Value)
+ case *Hash:
+ pairs := make(map[string]interface{})
+ for k, v := range in.Pairs {
+ val, err := FromObject(v)
+ if err != nil {
+ return nil, err
+ }
+ pairs[string(k)] = val
+ }
+ return pairs, nil
+ case *Array:
+ elements := make([]interface{}, 0, len(in.Elements))
+ for _, v := range in.Elements {
+ val, err := FromObject(v)
+ if err != nil {
+ return nil, err
+ }
+ elements = append(elements, val)
+ }
+ return elements, nil
+ default:
+ return nil, errors.New(fmt.Sprintf("Cannot convert Object of type %s to a native type", reflect.TypeOf(in)))
+ }
+}
diff --git a/dsl/object/object_utils_test.go b/dsl/object/object_utils_test.go
new file mode 100644
index 0000000..04b76b9
--- /dev/null
+++ b/dsl/object/object_utils_test.go
@@ -0,0 +1,424 @@
+package object
+
+import (
+ "github.com/dustin/gojson"
+ "github.com/stretchr/testify/assert"
+ "github.com/stretchr/testify/require"
+ "testing"
+)
+
+func TestDeepEquals(t *testing.T) {
+ t.Run("Deep equality", func(t *testing.T) {
+
+ o1 := &Hash{
+ Pairs: map[HashKey]Object{
+ HashKey("a"): &String{"foo"},
+ HashKey("b"): &Integer{42},
+ HashKey("c"): &Hash{
+ Pairs: map[HashKey]Object{
+ HashKey("ca"): &String{"cfoo"},
+ HashKey("cb"): &Integer{43},
+ HashKey("cc"): &Array{[]Object{
+ &Integer{1},
+ &Integer{2},
+ }},
+ HashKey("cd"): &Hash{
+ Pairs: map[HashKey]Object{
+ HashKey("cda"): &String{"bar"},
+ },
+ },
+ },
+ },
+ HashKey("d"): &Array{[]Object{
+ &String{"da"},
+ &Integer{43},
+ &Array{[]Object{}},
+ &Hash{Pairs: map[HashKey]Object{}},
+ &Boolean{true},
+ &Float{12.3},
+ }},
+ HashKey("e"): &Float{1.2},
+ HashKey("f"): &Boolean{false},
+ },
+ }
+
+ o2 := &Hash{
+ Pairs: map[HashKey]Object{
+ HashKey("a"): &String{"foo"},
+ HashKey("b"): &Integer{42},
+ HashKey("c"): &Hash{
+ Pairs: map[HashKey]Object{
+ HashKey("ca"): &String{"cfoo"},
+ HashKey("cb"): &Integer{43},
+ HashKey("cc"): &Array{[]Object{
+ &Integer{1},
+ &Integer{2},
+ }},
+ HashKey("cd"): &Hash{
+ Pairs: map[HashKey]Object{
+ HashKey("cda"): &String{"bar"},
+ },
+ },
+ },
+ },
+ HashKey("d"): &Array{[]Object{
+ &String{"da"},
+ &Integer{43},
+ &Array{[]Object{}},
+ &Hash{Pairs: map[HashKey]Object{}},
+ &Boolean{true},
+ &Float{12.3},
+ }},
+ HashKey("e"): &Float{1.2},
+ HashKey("f"): &Boolean{false},
+ },
+ }
+
+ assert.True(t, DeepEquals(o1, o2))
+ })
+
+ t.Run("Not equal because of array length", func(t *testing.T) {
+ o1 := &Array{
+ []Object{
+ &String{"da"},
+ &Integer{43},
+ },
+ }
+
+ o2 := &Array{
+ []Object{
+ &String{"da"},
+ &Integer{43},
+ &Integer{44},
+ },
+ }
+
+ assert.False(t, DeepEquals(o1, o2))
+ })
+
+ t.Run("Not equal because of array's value", func(t *testing.T) {
+ o1 := &Array{
+ []Object{
+ &String{"da"},
+ &Integer{43},
+ },
+ }
+
+ o2 := &Array{
+ []Object{
+ &String{"da"},
+ &Integer{44},
+ },
+ }
+
+ assert.False(t, DeepEquals(o1, o2))
+ })
+
+ t.Run("Not equal because of array's value's type", func(t *testing.T) {
+ o1 := &Array{
+ []Object{
+ &String{"1"},
+ },
+ }
+
+ o2 := &Array{
+ []Object{
+ &Integer{1},
+ },
+ }
+
+ assert.False(t, DeepEquals(o1, o2))
+ })
+
+ t.Run("Not equal because of hash length", func(t *testing.T) {
+ o1 := &Hash{
+ Pairs: map[HashKey]Object{
+ HashKey("a"): &String{"foo"},
+ HashKey("b"): &Integer{42},
+ },
+ }
+
+ o2 := &Hash{
+ Pairs: map[HashKey]Object{
+ HashKey("a"): &String{"foo"},
+ HashKey("b"): &Integer{42},
+ HashKey("c"): &Integer{43},
+ },
+ }
+
+ assert.False(t, DeepEquals(o1, o2))
+ })
+
+ t.Run("Not equal because of hash's value", func(t *testing.T) {
+ o1 := &Hash{
+ Pairs: map[HashKey]Object{
+ HashKey("a"): &String{"foo"},
+ HashKey("b"): &Integer{42},
+ },
+ }
+
+ o2 := &Hash{
+ Pairs: map[HashKey]Object{
+ HashKey("a"): &String{"foo"},
+ HashKey("b"): &Integer{43},
+ },
+ }
+
+ assert.False(t, DeepEquals(o1, o2))
+ })
+
+ t.Run("Not equal because of hash's key", func(t *testing.T) {
+ o1 := &Hash{
+ Pairs: map[HashKey]Object{
+ HashKey("a"): &String{"foo"},
+ HashKey("b"): &Integer{42},
+ },
+ }
+
+ o2 := &Hash{
+ Pairs: map[HashKey]Object{
+ HashKey("a"): &String{"foo"},
+ HashKey("c"): &Integer{42},
+ },
+ }
+
+ assert.False(t, DeepEquals(o1, o2))
+ })
+}
+
+func TestToObject(t *testing.T) {
+ t.Run("Test ToObject", func(t *testing.T) {
+ tests := []struct {
+ input interface{}
+ expected Object
+ }{
+ {"string", &String{"string"}},
+ {int(3), &Integer{3}},
+ {int8(3), &Integer{3}},
+ {int16(3), &Integer{3}},
+ {int32(3), &Integer{3}},
+ {int64(3), &Integer{3}},
+ {uint(3), &Integer{3}},
+ {uint8(3), &Integer{3}},
+ {uint16(3), &Integer{3}},
+ {uint32(3), &Integer{3}},
+ {uint64(3), &Integer{3}},
+ {float32(3), &Integer{3}},
+ {float64(3), &Integer{3}},
+ {float32(3.2), &Float{3.2}},
+ {float64(3.2), &Float{3.2}},
+ {float32(3E38), &Float{3E38}},
+ {float64(3E300), &Float{3E300}},
+ }
+
+ for _, tt := range tests {
+ obj, err := ToObject(tt.input)
+ require.NoError(t, err)
+ assert.True(t, obj.Equals(tt.expected))
+ }
+ })
+
+ t.Run("Test ToObject errors", func(t *testing.T) {
+ tests := []struct {
+ input interface{}
+ expected string
+ }{
+ {func() {}, "Cannot convert value of type func() to Object"},
+ {map[string]interface{}{"a": func() {}}, "Cannot convert value of type func() to Object"},
+ {[]interface{}{func() {}}, "Cannot convert value of type func() to Object"},
+ }
+
+ for _, tt := range tests {
+ _, err := ToObject(tt.input)
+ require.Error(t, err)
+ assert.Equal(t, tt.expected, err.Error())
+ }
+ })
+
+ t.Run("Test ToObject from JSON input", func(t *testing.T) {
+
+ input := `
+{
+ "a": "foo",
+ "b": 42,
+ "c": {
+ "ca": "cfoo",
+ "cb": 43,
+ "cc": [
+ 1,
+ 2
+ ],
+ "cd": {
+ "cda": "bar"
+ }
+ },
+ "d": [
+ "da",
+ 43,
+ [],
+ {},
+ true,
+ 12.3
+ ],
+ "e": 1.2,
+ "f": false
+}`
+ in := make(map[string]interface{})
+ err := json.Unmarshal([]byte(input), &in)
+ if err != nil {
+ t.Fatal(err)
+ }
+
+ obj, err := ToObject(in)
+ if err != nil {
+ t.Fatal(err)
+ }
+ deepEqual := DeepEquals(&Hash{
+ Pairs: map[HashKey]Object{
+ HashKey("a"): &String{"foo"},
+ HashKey("b"): &Integer{42},
+ HashKey("c"): &Hash{
+ Pairs: map[HashKey]Object{
+ HashKey("ca"): &String{"cfoo"},
+ HashKey("cb"): &Integer{43},
+ HashKey("cc"): &Array{[]Object{
+ &Integer{1},
+ &Integer{2},
+ }},
+ HashKey("cd"): &Hash{
+ Pairs: map[HashKey]Object{
+ HashKey("cda"): &String{"bar"},
+ },
+ },
+ },
+ },
+ HashKey("d"): &Array{[]Object{
+ &String{"da"},
+ &Integer{43},
+ &Array{[]Object{}},
+ &Hash{Pairs: map[HashKey]Object{}},
+ &Boolean{true},
+ &Float{12.3},
+ }},
+ HashKey("e"): &Float{1.2},
+ HashKey("f"): &Boolean{false},
+ },
+ }, obj)
+
+ assert.True(t, deepEqual)
+ })
+}
+
+func TestFromObject(t *testing.T) {
+ t.Run("From object to native", func(t *testing.T) {
+ tests := []struct {
+ input Object
+ expected interface{}
+ }{
+ {&String{"string"}, "string"},
+ {&Integer{3}, int64(3)},
+ {&Float{3.2}, float64(3.2)},
+ {&Boolean{true}, true},
+ {&Null{}, nil},
+ {&ReturnValue{&Integer{3}}, int64(3)},
+ }
+
+ for _, tt := range tests {
+ native, err := FromObject(tt.input)
+ require.NoError(t, err)
+ assert.Equal(t, tt.expected, native)
+ }
+ })
+
+ t.Run("Test FromObject errors", func(t *testing.T) {
+ tests := []struct {
+ input Object
+ expected string
+ }{
+ {&Function{}, "Cannot convert Object of type *object.Function to a native type"},
+ {&Hash{
+ Pairs: map[HashKey]Object{
+ "a": &Function{},
+ },
+ }, "Cannot convert Object of type *object.Function to a native type"},
+ {&Array{
+ Elements: []Object{
+ &Function{},
+ },
+ }, "Cannot convert Object of type *object.Function to a native type"},
+ }
+
+ for _, tt := range tests {
+ _, err := FromObject(tt.input)
+ require.Error(t, err)
+ assert.Equal(t, tt.expected, err.Error())
+ }
+ })
+
+ t.Run("Test FromObject to JSON output", func(t *testing.T) {
+
+ input := &Hash{
+ Pairs: map[HashKey]Object{
+ HashKey("a"): &String{"foo"},
+ HashKey("b"): &Integer{42},
+ HashKey("c"): &Hash{
+ Pairs: map[HashKey]Object{
+ HashKey("ca"): &String{"cfoo"},
+ HashKey("cb"): &Integer{43},
+ HashKey("cc"): &Array{[]Object{
+ &Integer{1},
+ &Integer{2},
+ }},
+ HashKey("cd"): &Hash{
+ Pairs: map[HashKey]Object{
+ HashKey("cda"): &String{"bar"},
+ },
+ },
+ },
+ },
+ HashKey("d"): &Array{[]Object{
+ &String{"da"},
+ &Integer{43},
+ &Array{[]Object{}},
+ &Hash{Pairs: map[HashKey]Object{}},
+ &Boolean{true},
+ &Float{12.3},
+ }},
+ HashKey("e"): &Float{1.2},
+ HashKey("f"): &Boolean{false},
+ },
+ }
+
+ native, err := FromObject(input)
+ require.NoError(t, err)
+
+ jsonStr, err := json.MarshalIndent(native, "", "\t")
+ require.NoError(t, err)
+
+ assert.Equal(t, string(jsonStr), `{
+ "a": "foo",
+ "b": 42,
+ "c": {
+ "ca": "cfoo",
+ "cb": 43,
+ "cc": [
+ 1,
+ 2
+ ],
+ "cd": {
+ "cda": "bar"
+ }
+ },
+ "d": [
+ "da",
+ 43,
+ [],
+ {},
+ true,
+ 12.3
+ ],
+ "e": 1.2,
+ "f": false
+}`)
+ })
+}
diff --git a/dsl/parser/parser.go b/dsl/parser/parser.go
new file mode 100644
index 0000000..c51cc2c
--- /dev/null
+++ b/dsl/parser/parser.go
@@ -0,0 +1,657 @@
+package parser
+
+import (
+ "fmt"
+ "github.com/ofux/deluge/dsl/ast"
+ "github.com/ofux/deluge/dsl/lexer"
+ "github.com/ofux/deluge/dsl/token"
+ "strconv"
+)
+
+const (
+ _ int = iota
+ LOWEST
+ ASSIGNMENT // = or +=
+ EQUALS // ==
+ LESSGREATER // > or <
+ SUM // +
+ PRODUCT // *
+ BOOL_OR // ||
+ BOOL_AND // &&
+ PREFIX // -X or !X
+ CALL // myFunction(X)
+ POSTFIX // ++ or --
+ INDEX // array[index]
+)
+
+var precedences = map[token.TokenType]int{
+ token.ASSIGN_INC1: POSTFIX,
+ token.ASSIGN_DEC1: POSTFIX,
+ token.ASSIGN: ASSIGNMENT,
+ token.ASSIGN_INC: ASSIGNMENT,
+ token.ASSIGN_DEC: ASSIGNMENT,
+ token.ASSIGN_MULT: ASSIGNMENT,
+ token.ASSIGN_DIV: ASSIGNMENT,
+ token.EQ: EQUALS,
+ token.NOT_EQ: EQUALS,
+ token.LT: LESSGREATER,
+ token.GT: LESSGREATER,
+ token.LTE: LESSGREATER,
+ token.GTE: LESSGREATER,
+ token.PLUS: SUM,
+ token.MINUS: SUM,
+ token.SLASH: PRODUCT,
+ token.ASTERISK: PRODUCT,
+ token.MODULO: PRODUCT,
+ token.OR: BOOL_OR,
+ token.AND: BOOL_AND,
+ token.LPAREN: CALL,
+ token.LBRACKET: INDEX,
+}
+
+type (
+ prefixParseFn func() ast.Expression
+ infixParseFn func(ast.Expression) ast.Expression
+)
+
+type ParseError struct {
+ Message string
+ Line int
+ Column int
+}
+
+func (err ParseError) Error() string {
+ return fmt.Sprintf("%s (line %d, col %d)", err.Message, err.Line, err.Column)
+}
+
+type ParseErrors []ParseError
+
+func (p ParseErrors) Error() string {
+ msg := "Syntax error:\n"
+ for _, err := range p {
+ msg += fmt.Sprintf("\t%s\n", err.Error())
+ }
+ return msg
+}
+
+type Parser struct {
+ l *lexer.Lexer
+ errors []ParseError
+
+ curToken token.Token
+ peekToken token.Token
+
+ prefixParseFns map[token.TokenType]prefixParseFn
+ infixParseFns map[token.TokenType]infixParseFn
+}
+
+func New(l *lexer.Lexer) *Parser {
+ p := &Parser{
+ l: l,
+ errors: []ParseError{},
+ }
+
+ p.prefixParseFns = make(map[token.TokenType]prefixParseFn)
+ p.registerPrefix(token.NULL, p.parseNull)
+ p.registerPrefix(token.IDENT, p.parseIdentifier)
+ p.registerPrefix(token.INT, p.parseIntegerLiteral)
+ p.registerPrefix(token.FLOAT, p.parseFloatLiteral)
+ p.registerPrefix(token.STRING, p.parseStringLiteral)
+ p.registerPrefix(token.BANG, p.parsePrefixExpression)
+ p.registerPrefix(token.MINUS, p.parsePrefixExpression)
+ p.registerPrefix(token.TRUE, p.parseBoolean)
+ p.registerPrefix(token.FALSE, p.parseBoolean)
+ p.registerPrefix(token.LPAREN, p.parseGroupedExpression)
+ p.registerPrefix(token.FUNCTION, p.parseFunctionLiteral)
+ p.registerPrefix(token.LBRACKET, p.parseArrayLiteral)
+ p.registerPrefix(token.LBRACE, p.parseHashLiteral)
+
+ p.infixParseFns = make(map[token.TokenType]infixParseFn)
+ p.registerInfix(token.PLUS, p.parseInfixExpression)
+ p.registerInfix(token.MINUS, p.parseInfixExpression)
+ p.registerInfix(token.SLASH, p.parseInfixExpression)
+ p.registerInfix(token.ASTERISK, p.parseInfixExpression)
+ p.registerInfix(token.MODULO, p.parseInfixExpression)
+ p.registerInfix(token.EQ, p.parseInfixExpression)
+ p.registerInfix(token.NOT_EQ, p.parseInfixExpression)
+ p.registerInfix(token.LT, p.parseInfixExpression)
+ p.registerInfix(token.GT, p.parseInfixExpression)
+ p.registerInfix(token.LTE, p.parseInfixExpression)
+ p.registerInfix(token.GTE, p.parseInfixExpression)
+ p.registerInfix(token.AND, p.parseInfixExpression)
+ p.registerInfix(token.OR, p.parseInfixExpression)
+
+ p.registerInfix(token.ASSIGN, p.parseAssignmentExpression)
+ p.registerInfix(token.ASSIGN_INC, p.parseAssignmentExpression)
+ p.registerInfix(token.ASSIGN_DEC, p.parseAssignmentExpression)
+ p.registerInfix(token.ASSIGN_MULT, p.parseAssignmentExpression)
+ p.registerInfix(token.ASSIGN_DIV, p.parseAssignmentExpression)
+ p.registerInfix(token.ASSIGN_INC1, p.parsePostfixAssignmentExpression)
+ p.registerInfix(token.ASSIGN_DEC1, p.parsePostfixAssignmentExpression)
+
+ p.registerInfix(token.LPAREN, p.parseCallExpression)
+ p.registerInfix(token.LBRACKET, p.parseIndexExpression)
+
+ // Read two tokens, so curToken and peekToken are both set
+ p.nextToken()
+ p.nextToken()
+
+ return p
+}
+
+func (p *Parser) nextToken() {
+ p.curToken = p.peekToken
+ p.peekToken = p.l.NextToken()
+}
+
+func (p *Parser) curTokenIs(t token.TokenType) bool {
+ return p.curToken.Type == t
+}
+
+func (p *Parser) peekTokenIs(t token.TokenType) bool {
+ return p.peekToken.Type == t
+}
+
+func (p *Parser) expectCur(t token.TokenType) bool {
+ if p.curTokenIs(t) {
+ return true
+ } else {
+ p.curError(t)
+ return false
+ }
+}
+
+func (p *Parser) expectPeek(t token.TokenType) bool {
+ if p.peekTokenIs(t) {
+ p.nextToken()
+ return true
+ } else {
+ p.peekError(t)
+ return false
+ }
+}
+
+func (p *Parser) Errors() ParseErrors {
+ return p.errors
+}
+
+func (p *Parser) curError(t token.TokenType) {
+ msg := fmt.Sprintf("expected next token to be %s, got %s instead", t, p.curToken.Type)
+ p.errors = append(p.errors, ParseError{Message: msg, Line: p.curToken.Line, Column: p.curToken.Column})
+}
+
+func (p *Parser) peekError(t token.TokenType) {
+ msg := fmt.Sprintf("expected next token to be %s, got %s instead", t, p.peekToken.Type)
+ p.errors = append(p.errors, ParseError{Message: msg, Line: p.peekToken.Line, Column: p.peekToken.Column})
+}
+
+func (p *Parser) noPrefixParseFnError(t token.TokenType) {
+ msg := fmt.Sprintf("no prefix parse function for %s found", t)
+ p.errors = append(p.errors, ParseError{Message: msg, Line: p.peekToken.Line, Column: p.peekToken.Column})
+}
+
+func (p *Parser) duplicateFuncParamError(tok token.Token, ident *ast.Identifier) {
+ msg := fmt.Sprintf("duplicate function parameter %s", ident.Value)
+ p.errors = append(p.errors, ParseError{Message: msg, Line: tok.Line, Column: tok.Column})
+}
+
+func (p *Parser) ParseProgram() (*ast.Program, bool) {
+ program := &ast.Program{}
+ program.Statements = []ast.Statement{}
+
+ for !p.curTokenIs(token.EOF) {
+ stmt := p.parseStatement()
+ if stmt != nil {
+ program.Statements = append(program.Statements, stmt)
+ }
+ p.nextToken()
+ }
+
+ return program, len(p.errors) == 0
+}
+
+func (p *Parser) parseStatement() ast.Statement {
+ switch p.curToken.Type {
+ case token.LET:
+ return p.parseLetStatement()
+ case token.RETURN:
+ return p.parseReturnStatement()
+ case token.IF:
+ return p.parseIfStatement()
+ case token.FOR:
+ return p.parseForStatement()
+ default:
+ return p.parseExpressionStatement()
+ }
+}
+
+func (p *Parser) parseLetStatement() *ast.LetStatement {
+ stmt := &ast.LetStatement{Token: p.curToken}
+
+ if !p.expectPeek(token.IDENT) {
+ return nil
+ }
+
+ stmt.Name = &ast.Identifier{Token: p.curToken, Value: p.curToken.Literal}
+
+ if !p.expectPeek(token.ASSIGN) {
+ return nil
+ }
+
+ p.nextToken()
+
+ stmt.Value = p.parseExpression(LOWEST)
+
+ if p.peekTokenIs(token.SEMICOLON) {
+ p.nextToken()
+ }
+
+ return stmt
+}
+
+func (p *Parser) parseReturnStatement() *ast.ReturnStatement {
+ stmt := &ast.ReturnStatement{Token: p.curToken}
+
+ p.nextToken()
+
+ stmt.ReturnValue = p.parseExpression(LOWEST)
+
+ if p.peekTokenIs(token.SEMICOLON) {
+ p.nextToken()
+ }
+
+ return stmt
+}
+
+func (p *Parser) parseExpressionStatement() *ast.ExpressionStatement {
+ stmt := &ast.ExpressionStatement{Token: p.curToken}
+
+ stmt.Expression = p.parseExpression(LOWEST)
+
+ if p.peekTokenIs(token.SEMICOLON) {
+ p.nextToken()
+ }
+
+ return stmt
+}
+
+func (p *Parser) parseExpression(precedence int) ast.Expression {
+ prefix := p.prefixParseFns[p.curToken.Type]
+ if prefix == nil {
+ p.noPrefixParseFnError(p.curToken.Type)
+ return nil
+ }
+ leftExp := prefix()
+
+ for !p.peekTokenIs(token.SEMICOLON) && precedence < p.peekPrecedence() {
+ infix := p.infixParseFns[p.peekToken.Type]
+ if infix == nil {
+ return leftExp
+ }
+
+ p.nextToken()
+
+ leftExp = infix(leftExp)
+ }
+
+ return leftExp
+}
+
+func (p *Parser) peekPrecedence() int {
+ if p, ok := precedences[p.peekToken.Type]; ok {
+ return p
+ }
+
+ return LOWEST
+}
+
+func (p *Parser) curPrecedence() int {
+ if p, ok := precedences[p.curToken.Type]; ok {
+ return p
+ }
+
+ return LOWEST
+}
+
+func (p *Parser) parseNull() ast.Expression {
+ return &ast.Null{Token: p.curToken}
+}
+
+func (p *Parser) parseIdentifier() ast.Expression {
+ return &ast.Identifier{Token: p.curToken, Value: p.curToken.Literal}
+}
+
+func (p *Parser) parseIntegerLiteral() ast.Expression {
+ lit := &ast.IntegerLiteral{Token: p.curToken}
+
+ value, err := strconv.ParseInt(p.curToken.Literal, 0, 64)
+ if err != nil {
+ msg := fmt.Sprintf("could not parse %q as integer", p.curToken.Literal)
+ p.errors = append(p.errors, ParseError{Message: msg, Line: p.curToken.Line, Column: p.curToken.Column})
+ return nil
+ }
+
+ lit.Value = value
+
+ return lit
+}
+
+func (p *Parser) parseFloatLiteral() ast.Expression {
+ lit := &ast.FloatLiteral{Token: p.curToken}
+
+ value, err := strconv.ParseFloat(p.curToken.Literal, 64)
+ if err != nil {
+ msg := fmt.Sprintf("could not parse %q as float", p.curToken.Literal)
+ p.errors = append(p.errors, ParseError{Message: msg, Line: p.curToken.Line, Column: p.curToken.Column})
+ return nil
+ }
+
+ lit.Value = value
+
+ return lit
+}
+
+func (p *Parser) parseStringLiteral() ast.Expression {
+ return &ast.StringLiteral{Token: p.curToken, Value: p.curToken.Literal}
+}
+
+func (p *Parser) parsePrefixExpression() ast.Expression {
+ expression := &ast.PrefixExpression{
+ Token: p.curToken,
+ Operator: p.curToken.Literal,
+ }
+
+ p.nextToken()
+
+ expression.Right = p.parseExpression(PREFIX)
+
+ return expression
+}
+
+func (p *Parser) parseInfixExpression(left ast.Expression) ast.Expression {
+ expression := &ast.InfixExpression{
+ Token: p.curToken,
+ Operator: p.curToken.Literal,
+ Left: left,
+ }
+
+ precedence := p.curPrecedence()
+ p.nextToken()
+ expression.Right = p.parseExpression(precedence)
+
+ return expression
+}
+
+func (p *Parser) parsePostfixAssignmentExpression(left ast.Expression) ast.Expression {
+ expression := &ast.PostAssignmentExpression{
+ Token: p.curToken,
+ Operator: p.curToken.Literal,
+ Left: left,
+ }
+
+ return expression
+}
+
+func (p *Parser) parseAssignmentExpression(left ast.Expression) ast.Expression {
+ expression := &ast.AssignmentExpression{
+ Token: p.curToken,
+ Operator: p.curToken.Literal,
+ Left: left,
+ }
+
+ precedence := p.curPrecedence()
+ p.nextToken()
+ expression.Right = p.parseExpression(precedence)
+
+ return expression
+}
+
+func (p *Parser) parseBoolean() ast.Expression {
+ return &ast.Boolean{Token: p.curToken, Value: p.curTokenIs(token.TRUE)}
+}
+
+func (p *Parser) parseGroupedExpression() ast.Expression {
+ p.nextToken()
+
+ exp := p.parseExpression(LOWEST)
+
+ if !p.expectPeek(token.RPAREN) {
+ return nil
+ }
+
+ return exp
+}
+
+func (p *Parser) parseIfStatement() *ast.IfStatement {
+ statement := &ast.IfStatement{Token: p.curToken}
+
+ if !p.expectPeek(token.LPAREN) {
+ return nil
+ }
+
+ p.nextToken()
+ statement.Condition = p.parseExpression(LOWEST)
+
+ if !p.expectPeek(token.RPAREN) {
+ return nil
+ }
+
+ if !p.expectPeek(token.LBRACE) {
+ return nil
+ }
+
+ statement.Consequence = p.parseBlockStatement()
+
+ if p.peekTokenIs(token.ELSE) {
+ p.nextToken()
+
+ if p.peekTokenIs(token.IF) {
+ p.nextToken()
+ statement.Alternative = p.parseIfStatement()
+ } else {
+ if !p.expectPeek(token.LBRACE) {
+ return nil
+ }
+ statement.Alternative = p.parseBlockStatement()
+ }
+ }
+
+ return statement
+}
+
+func (p *Parser) parseForStatement() *ast.ForStatement {
+ statement := &ast.ForStatement{Token: p.curToken}
+
+ if !p.expectPeek(token.LPAREN) {
+ return nil
+ }
+
+ p.nextToken()
+ statement.Initialization = p.parseStatement()
+
+ if !p.expectCur(token.SEMICOLON) {
+ return nil
+ }
+
+ p.nextToken()
+ statement.Condition = p.parseExpression(LOWEST)
+
+ if !p.expectPeek(token.SEMICOLON) {
+ return nil
+ }
+
+ p.nextToken()
+ statement.Afterthought = p.parseStatement()
+
+ if !p.expectPeek(token.RPAREN) {
+ return nil
+ }
+
+ if !p.expectPeek(token.LBRACE) {
+ return nil
+ }
+
+ statement.Loop = p.parseBlockStatement()
+
+ return statement
+}
+
+func (p *Parser) parseBlockStatement() *ast.BlockStatement {
+ block := &ast.BlockStatement{Token: p.curToken}
+ block.Statements = []ast.Statement{}
+
+ p.nextToken()
+
+ for !p.curTokenIs(token.RBRACE) && !p.curTokenIs(token.EOF) {
+ stmt := p.parseStatement()
+ if stmt != nil {
+ block.Statements = append(block.Statements, stmt)
+ }
+ p.nextToken()
+ }
+
+ if !p.expectCur(token.RBRACE) {
+ return nil
+ }
+
+ return block
+}
+
+func (p *Parser) parseFunctionLiteral() ast.Expression {
+ lit := &ast.FunctionLiteral{Token: p.curToken}
+
+ if !p.expectPeek(token.LPAREN) {
+ return nil
+ }
+
+ lit.Parameters = p.parseFunctionParameters()
+
+ if !p.expectPeek(token.LBRACE) {
+ return nil
+ }
+
+ lit.Body = p.parseBlockStatement()
+
+ return lit
+}
+
+func (p *Parser) parseFunctionParameters() []*ast.Identifier {
+ identifiers := []*ast.Identifier{}
+
+ if p.peekTokenIs(token.RPAREN) {
+ p.nextToken()
+ return identifiers
+ }
+
+ p.nextToken()
+
+ ident := &ast.Identifier{Token: p.curToken, Value: p.curToken.Literal}
+ identifiers = append(identifiers, ident)
+
+ for p.peekTokenIs(token.COMMA) {
+ p.nextToken()
+ p.nextToken()
+ ident := &ast.Identifier{Token: p.curToken, Value: p.curToken.Literal}
+ for _, prevIdent := range identifiers {
+ if prevIdent.Value == ident.Value {
+ p.duplicateFuncParamError(p.curToken, ident)
+ return nil
+ }
+ }
+ identifiers = append(identifiers, ident)
+ }
+
+ if !p.expectPeek(token.RPAREN) {
+ return nil
+ }
+
+ return identifiers
+}
+
+func (p *Parser) parseCallExpression(function ast.Expression) ast.Expression {
+ exp := &ast.CallExpression{Token: p.curToken, Function: function}
+ exp.Arguments = p.parseExpressionList(token.RPAREN)
+ return exp
+}
+
+func (p *Parser) parseExpressionList(end token.TokenType) []ast.Expression {
+ list := []ast.Expression{}
+
+ if p.peekTokenIs(end) {
+ p.nextToken()
+ return list
+ }
+
+ p.nextToken()
+ list = append(list, p.parseExpression(LOWEST))
+
+ for p.peekTokenIs(token.COMMA) {
+ p.nextToken()
+ p.nextToken()
+ list = append(list, p.parseExpression(LOWEST))
+ }
+
+ if !p.expectPeek(end) {
+ return nil
+ }
+
+ return list
+}
+
+func (p *Parser) parseArrayLiteral() ast.Expression {
+ array := &ast.ArrayLiteral{Token: p.curToken}
+
+ array.Elements = p.parseExpressionList(token.RBRACKET)
+
+ return array
+}
+
+func (p *Parser) parseIndexExpression(left ast.Expression) ast.Expression {
+ exp := &ast.IndexExpression{Token: p.curToken, Left: left}
+
+ p.nextToken()
+ exp.Index = p.parseExpression(LOWEST)
+
+ if !p.expectPeek(token.RBRACKET) {
+ return nil
+ }
+
+ return exp
+}
+
+func (p *Parser) parseHashLiteral() ast.Expression {
+ hash := &ast.HashLiteral{Token: p.curToken}
+ hash.Pairs = make(map[ast.Expression]ast.Expression)
+
+ for !p.peekTokenIs(token.RBRACE) {
+ p.nextToken()
+ key := p.parseExpression(LOWEST)
+
+ if !p.expectPeek(token.COLON) {
+ return nil
+ }
+
+ p.nextToken()
+ value := p.parseExpression(LOWEST)
+
+ hash.Pairs[key] = value
+
+ if !p.peekTokenIs(token.RBRACE) && !p.expectPeek(token.COMMA) {
+ return nil
+ }
+ }
+
+ if !p.expectPeek(token.RBRACE) {
+ return nil
+ }
+
+ return hash
+}
+
+func (p *Parser) registerPrefix(tokenType token.TokenType, fn prefixParseFn) {
+ p.prefixParseFns[tokenType] = fn
+}
+
+func (p *Parser) registerInfix(tokenType token.TokenType, fn infixParseFn) {
+ p.infixParseFns[tokenType] = fn
+}
diff --git a/dsl/parser/parser_test.go b/dsl/parser/parser_test.go
new file mode 100644
index 0000000..1110dce
--- /dev/null
+++ b/dsl/parser/parser_test.go
@@ -0,0 +1,1664 @@
+package parser
+
+import (
+ "fmt"
+ "github.com/ofux/deluge/dsl/ast"
+ "github.com/ofux/deluge/dsl/lexer"
+ "testing"
+)
+
+func TestParsingErrors(t *testing.T) {
+ tests := []struct {
+ input string
+ expectedValue []ParseError
+ }{
+ {
+ `let;`,
+ []ParseError{
+ {Message: "expected next token to be IDENT, got ; instead", Line: 1, Column: 4},
+ {Message: "no prefix parse function for ; found", Line: 1, Column: 5},
+ },
+ },
+ {
+ `let x=`,
+ []ParseError{
+ {Message: "no prefix parse function for EOF found", Line: 1, Column: 8},
+ },
+ },
+ {
+ `let x#x=2;`,
+ []ParseError{
+ {Message: "expected next token to be =, got ILLEGAL instead", Line: 1, Column: 6},
+ {Message: "no prefix parse function for ILLEGAL found", Line: 1, Column: 7},
+ },
+ },
+ {
+ `let x==2;`,
+ []ParseError{
+ {Message: "expected next token to be =, got == instead", Line: 1, Column: 7},
+ {Message: "no prefix parse function for == found", Line: 1, Column: 8},
+ },
+ },
+ {
+ `function)`,
+ []ParseError{
+ {Message: "expected next token to be (, got ) instead", Line: 1, Column: 9},
+ {Message: "no prefix parse function for ) found", Line: 1, Column: 10},
+ },
+ },
+ {
+ `function() {
+ let x = 1;
+ `,
+ []ParseError{
+ {Message: "expected next token to be }, got EOF instead", Line: 3, Column: 4},
+ },
+ },
+ {
+ `function(x, y, z, y) {}`,
+ []ParseError{
+ {Message: "duplicate function parameter y", Line: 1, Column: 19},
+ {Message: "expected next token to be {, got ) instead", Line: 1, Column: 20},
+ {Message: "no prefix parse function for ) found", Line: 1, Column: 22},
+ },
+ },
+ {
+ `if () {}`,
+ []ParseError{
+ {Message: "no prefix parse function for ) found", Line: 1, Column: 7},
+ {Message: "expected next token to be ), got { instead", Line: 1, Column: 7},
+ },
+ },
+ {
+ `if {}`,
+ []ParseError{
+ {Message: "expected next token to be (, got { instead", Line: 1, Column: 4},
+ },
+ },
+ {
+ `if (true) { } }`,
+ []ParseError{
+ {Message: "no prefix parse function for } found", Line: 1, Column: 16},
+ },
+ },
+ {
+ `let x = if (true) { 1 }`,
+ []ParseError{
+ {Message: "no prefix parse function for IF found", Line: 1, Column: 12},
+ {Message: "expected next token to be :, got } instead", Line: 1, Column: 23},
+ {Message: "no prefix parse function for } found", Line: 1, Column: 24},
+ },
+ },
+ {
+ `if (true) else if { 1 }`,
+ []ParseError{
+ {Message: "expected next token to be {, got ELSE instead", Line: 1, Column: 11},
+ {Message: "no prefix parse function for ELSE found", Line: 1, Column: 16},
+ {Message: "expected next token to be (, got { instead", Line: 1, Column: 19},
+ {Message: "expected next token to be :, got } instead", Line: 1, Column: 23},
+ {Message: "no prefix parse function for } found", Line: 1, Column: 24},
+ },
+ },
+ {
+ `if (false) { 1 } else if { 1 }`,
+ []ParseError{
+ {Message: "expected next token to be (, got { instead", Line: 1, Column: 26},
+ {Message: "expected next token to be :, got } instead", Line: 1, Column: 30},
+ {Message: "no prefix parse function for } found", Line: 1, Column: 31},
+ },
+ },
+ {
+ `((1+2)`,
+ []ParseError{
+ {Message: "expected next token to be ), got EOF instead", Line: 1, Column: 7},
+ },
+ },
+ {
+ `((1+2)}`,
+ []ParseError{
+ {Message: "expected next token to be ), got } instead", Line: 1, Column: 7},
+ {Message: "no prefix parse function for } found", Line: 1, Column: 8},
+ },
+ },
+ {
+ `let a# = 3;`,
+ []ParseError{
+ {Message: "expected next token to be =, got ILLEGAL instead", Line: 1, Column: 6},
+ {Message: "no prefix parse function for ILLEGAL found", Line: 1, Column: 8},
+ {Message: "no prefix parse function for = found", Line: 1, Column: 10},
+ },
+ },
+ {
+ `let 1a = 3;`,
+ []ParseError{
+ {Message: "expected next token to be IDENT, got INT instead", Line: 1, Column: 5},
+ },
+ },
+ {
+ `true | false`,
+ []ParseError{
+ {Message: "no prefix parse function for ILLEGAL found", Line: 1, Column: 8},
+ },
+ },
+ {
+ `true & false`,
+ []ParseError{
+ {Message: "no prefix parse function for ILLEGAL found", Line: 1, Column: 8},
+ },
+ },
+ {
+ `a[]`,
+ []ParseError{
+ {Message: "no prefix parse function for ] found", Line: 1, Column: 4},
+ {Message: "expected next token to be ], got EOF instead", Line: 1, Column: 4},
+ },
+ },
+ {
+ `let a = 3.2.7;`,
+ []ParseError{
+ {Message: "no prefix parse function for ILLEGAL found", Line: 1, Column: 13},
+ },
+ },
+ {
+ `%5;`,
+ []ParseError{
+ {Message: "no prefix parse function for % found", Line: 1, Column: 2},
+ },
+ },
+ }
+
+ for _, tt := range tests {
+ l := lexer.New(tt.input)
+ p := New(l)
+ p.ParseProgram()
+ testParserErrors(t, p, tt.expectedValue)
+ }
+}
+
+func TestLetStatements(t *testing.T) {
+ tests := []struct {
+ input string
+ expectedIdentifier string
+ expectedValue interface{}
+ }{
+ {"let x = 5;", "x", 5},
+ {"let y = true;", "y", true},
+ {"let foobar = y;", "foobar", "y"},
+ {"let a1 = y;", "a1", "y"},
+ {"let _a = _;", "_a", "_"},
+ {"let _a_ = B1_x;", "_a_", "B1_x"},
+ }
+
+ for _, tt := range tests {
+ l := lexer.New(tt.input)
+ p := New(l)
+ program, _ := p.ParseProgram()
+ checkParserErrors(t, p)
+
+ if len(program.Statements) != 1 {
+ t.Fatalf("program.Statements does not contain 1 statements. got=%d",
+ len(program.Statements))
+ }
+
+ stmt := program.Statements[0]
+ if !testLetStatement(t, stmt, tt.expectedIdentifier) {
+ return
+ }
+
+ val := stmt.(*ast.LetStatement).Value
+ if !testLiteralExpression(t, val, tt.expectedValue) {
+ return
+ }
+ }
+}
+
+func TestReturnStatements(t *testing.T) {
+ tests := []struct {
+ input string
+ expectedValue interface{}
+ }{
+ {"return 5;", 5},
+ {"return true;", true},
+ {"return foobar;", "foobar"},
+ }
+
+ for _, tt := range tests {
+ l := lexer.New(tt.input)
+ p := New(l)
+ program, _ := p.ParseProgram()
+ checkParserErrors(t, p)
+
+ if len(program.Statements) != 1 {
+ t.Fatalf("program.Statements does not contain 1 statements. got=%d",
+ len(program.Statements))
+ }
+
+ stmt := program.Statements[0]
+ returnStmt, ok := stmt.(*ast.ReturnStatement)
+ if !ok {
+ t.Fatalf("stmt not *ast.returnStatement. got=%T", stmt)
+ }
+ if returnStmt.TokenLiteral() != "return" {
+ t.Fatalf("returnStmt.TokenLiteral not 'return', got %q",
+ returnStmt.TokenLiteral())
+ }
+ if testLiteralExpression(t, returnStmt.ReturnValue, tt.expectedValue) {
+ return
+ }
+ }
+}
+
+func TestNulExpression(t *testing.T) {
+ input := "null;"
+
+ l := lexer.New(input)
+ p := New(l)
+ program, _ := p.ParseProgram()
+ checkParserErrors(t, p)
+
+ if len(program.Statements) != 1 {
+ t.Fatalf("program has not enough statements. got=%d",
+ len(program.Statements))
+ }
+ stmt, ok := program.Statements[0].(*ast.ExpressionStatement)
+ if !ok {
+ t.Fatalf("program.Statements[0] is not ast.ExpressionStatement. got=%T",
+ program.Statements[0])
+ }
+
+ null, ok := stmt.Expression.(*ast.Null)
+ if !ok {
+ t.Fatalf("exp not *ast.Null. got=%T", stmt.Expression)
+ }
+ if null.TokenLiteral() != "null" {
+ t.Errorf("ident.TokenLiteral not %s. got=%s", "null",
+ null.TokenLiteral())
+ }
+}
+
+func TestIdentifierExpression(t *testing.T) {
+ input := "foobar;"
+
+ l := lexer.New(input)
+ p := New(l)
+ program, _ := p.ParseProgram()
+ checkParserErrors(t, p)
+
+ if len(program.Statements) != 1 {
+ t.Fatalf("program has not enough statements. got=%d",
+ len(program.Statements))
+ }
+ stmt, ok := program.Statements[0].(*ast.ExpressionStatement)
+ if !ok {
+ t.Fatalf("program.Statements[0] is not ast.ExpressionStatement. got=%T",
+ program.Statements[0])
+ }
+
+ ident, ok := stmt.Expression.(*ast.Identifier)
+ if !ok {
+ t.Fatalf("exp not *ast.Identifier. got=%T", stmt.Expression)
+ }
+ if ident.Value != "foobar" {
+ t.Errorf("ident.Value not %s. got=%s", "foobar", ident.Value)
+ }
+ if ident.TokenLiteral() != "foobar" {
+ t.Errorf("ident.TokenLiteral not %s. got=%s", "foobar",
+ ident.TokenLiteral())
+ }
+}
+
+func TestIntegerLiteralExpression(t *testing.T) {
+ input := "5;"
+
+ l := lexer.New(input)
+ p := New(l)
+ program, _ := p.ParseProgram()
+ checkParserErrors(t, p)
+
+ if len(program.Statements) != 1 {
+ t.Fatalf("program has not enough statements. got=%d",
+ len(program.Statements))
+ }
+ stmt, ok := program.Statements[0].(*ast.ExpressionStatement)
+ if !ok {
+ t.Fatalf("program.Statements[0] is not ast.ExpressionStatement. got=%T",
+ program.Statements[0])
+ }
+
+ literal, ok := stmt.Expression.(*ast.IntegerLiteral)
+ if !ok {
+ t.Fatalf("exp not *ast.IntegerLiteral. got=%T", stmt.Expression)
+ }
+ if literal.Value != 5 {
+ t.Errorf("literal.Value not %d. got=%d", 5, literal.Value)
+ }
+ if literal.TokenLiteral() != "5" {
+ t.Errorf("literal.TokenLiteral not %s. got=%s", "5",
+ literal.TokenLiteral())
+ }
+}
+
+func TestFloatLiteralExpression(t *testing.T) {
+ input := "5.32;"
+
+ l := lexer.New(input)
+ p := New(l)
+ program, _ := p.ParseProgram()
+ checkParserErrors(t, p)
+
+ if len(program.Statements) != 1 {
+ t.Fatalf("program has not enough statements. got=%d",
+ len(program.Statements))
+ }
+ stmt, ok := program.Statements[0].(*ast.ExpressionStatement)
+ if !ok {
+ t.Fatalf("program.Statements[0] is not ast.ExpressionStatement. got=%T",
+ program.Statements[0])
+ }
+
+ literal, ok := stmt.Expression.(*ast.FloatLiteral)
+ if !ok {
+ t.Fatalf("exp not *ast.FloatLiteral. got=%T", stmt.Expression)
+ }
+ if literal.Value != 5.32 {
+ t.Errorf("literal.Value not %f. got=%f", 5.32, literal.Value)
+ }
+ if literal.TokenLiteral() != "5.32" {
+ t.Errorf("literal.TokenLiteral not %s. got=%s", "5.32",
+ literal.TokenLiteral())
+ }
+}
+
+func TestParsingPrefixExpressions(t *testing.T) {
+ prefixTests := []struct {
+ input string
+ operator string
+ value interface{}
+ }{
+ {"!5;", "!", 5},
+ {"-15;", "-", 15},
+ {"!foobar;", "!", "foobar"},
+ {"-foobar;", "-", "foobar"},
+ {"!true;", "!", true},
+ {"!false;", "!", false},
+ }
+
+ for _, tt := range prefixTests {
+ l := lexer.New(tt.input)
+ p := New(l)
+ program, _ := p.ParseProgram()
+ checkParserErrors(t, p)
+
+ if len(program.Statements) != 1 {
+ t.Fatalf("program.Statements does not contain %d statements. got=%d\n",
+ 1, len(program.Statements))
+ }
+
+ stmt, ok := program.Statements[0].(*ast.ExpressionStatement)
+ if !ok {
+ t.Fatalf("program.Statements[0] is not ast.ExpressionStatement. got=%T",
+ program.Statements[0])
+ }
+
+ exp, ok := stmt.Expression.(*ast.PrefixExpression)
+ if !ok {
+ t.Fatalf("stmt is not ast.PrefixExpression. got=%T", stmt.Expression)
+ }
+ if exp.Operator != tt.operator {
+ t.Fatalf("exp.Operator is not '%s'. got=%s",
+ tt.operator, exp.Operator)
+ }
+ if !testLiteralExpression(t, exp.Right, tt.value) {
+ return
+ }
+ }
+}
+
+func TestParsingInfixExpressions(t *testing.T) {
+ infixTests := []struct {
+ input string
+ leftValue interface{}
+ operator string
+ rightValue interface{}
+ }{
+ {"5 + 5;", 5, "+", 5},
+ {"5 - 5;", 5, "-", 5},
+ {"5 * 5;", 5, "*", 5},
+ {"5 / 5;", 5, "/", 5},
+ {"5 > 5;", 5, ">", 5},
+ {"5 < 5;", 5, "<", 5},
+ {"5 == 5;", 5, "==", 5},
+ {"5 != 5;", 5, "!=", 5},
+ {"5 % 5;", 5, "%", 5},
+ {"foobar + barfoo;", "foobar", "+", "barfoo"},
+ {"foobar - barfoo;", "foobar", "-", "barfoo"},
+ {"foobar * barfoo;", "foobar", "*", "barfoo"},
+ {"foobar / barfoo;", "foobar", "/", "barfoo"},
+ {"foobar > barfoo;", "foobar", ">", "barfoo"},
+ {"foobar < barfoo;", "foobar", "<", "barfoo"},
+ {"foobar >= barfoo;", "foobar", ">=", "barfoo"},
+ {"foobar <= barfoo;", "foobar", "<=", "barfoo"},
+ {"foobar == barfoo;", "foobar", "==", "barfoo"},
+ {"foobar != barfoo;", "foobar", "!=", "barfoo"},
+ {"true == true", true, "==", true},
+ {"true != false", true, "!=", false},
+ {"false == false", false, "==", false},
+ }
+
+ for _, tt := range infixTests {
+ l := lexer.New(tt.input)
+ p := New(l)
+ program, _ := p.ParseProgram()
+ checkParserErrors(t, p)
+
+ if len(program.Statements) != 1 {
+ t.Fatalf("program.Statements does not contain %d statements. got=%d\n",
+ 1, len(program.Statements))
+ }
+
+ stmt, ok := program.Statements[0].(*ast.ExpressionStatement)
+ if !ok {
+ t.Fatalf("program.Statements[0] is not ast.ExpressionStatement. got=%T",
+ program.Statements[0])
+ }
+
+ if !testInfixExpression(t, stmt.Expression, tt.leftValue,
+ tt.operator, tt.rightValue) {
+ return
+ }
+ }
+}
+
+func TestParsingAssignmentExpressions(t *testing.T) {
+ infixTests := []struct {
+ input string
+ leftValue interface{}
+ operator string
+ rightValue interface{}
+ }{
+ {"foo = 5;", "foo", "=", 5},
+ {"foo += 5;", "foo", "+=", 5},
+ {"foo -= 5;", "foo", "-=", 5},
+ {"foo *= 5;", "foo", "*=", 5},
+ {"foo /= 5;", "foo", "/=", 5},
+ }
+
+ for _, tt := range infixTests {
+ l := lexer.New(tt.input)
+ p := New(l)
+ program, _ := p.ParseProgram()
+ checkParserErrors(t, p)
+
+ if len(program.Statements) != 1 {
+ t.Fatalf("program.Statements does not contain %d statements. got=%d\n",
+ 1, len(program.Statements))
+ }
+
+ stmt, ok := program.Statements[0].(*ast.ExpressionStatement)
+ if !ok {
+ t.Fatalf("program.Statements[0] is not ast.ExpressionStatement. got=%T",
+ program.Statements[0])
+ }
+
+ if !testAssignmentExpression(t, stmt.Expression, tt.leftValue,
+ tt.operator, tt.rightValue) {
+ return
+ }
+ }
+}
+
+func TestParsingPostAssignmentExpressions(t *testing.T) {
+ prefixTests := []struct {
+ input string
+ operator string
+ value interface{}
+ }{
+ {"foo++;", "++", "foo"},
+ {"foo--;", "--", "foo"},
+ }
+
+ for _, tt := range prefixTests {
+ l := lexer.New(tt.input)
+ p := New(l)
+ program, _ := p.ParseProgram()
+ checkParserErrors(t, p)
+
+ if len(program.Statements) != 1 {
+ t.Fatalf("program.Statements does not contain %d statements. got=%d\n",
+ 1, len(program.Statements))
+ }
+
+ stmt, ok := program.Statements[0].(*ast.ExpressionStatement)
+ if !ok {
+ t.Fatalf("program.Statements[0] is not ast.ExpressionStatement. got=%T",
+ program.Statements[0])
+ }
+
+ exp, ok := stmt.Expression.(*ast.PostAssignmentExpression)
+ if !ok {
+ t.Fatalf("stmt is not ast.PostAssignmentExpression. got=%T", stmt.Expression)
+ }
+ if exp.Operator != tt.operator {
+ t.Fatalf("exp.Operator is not '%s'. got=%s",
+ tt.operator, exp.Operator)
+ }
+ if !testLiteralExpression(t, exp.Left, tt.value) {
+ return
+ }
+ }
+}
+
+func TestOperatorPrecedenceParsing(t *testing.T) {
+ tests := []struct {
+ input string
+ expected string
+ }{
+ {
+ "-a * b",
+ "((-a) * b)",
+ },
+ {
+ "!-a",
+ "(!(-a))",
+ },
+ {
+ "a + b + c",
+ "((a + b) + c)",
+ },
+ {
+ "a + b - c",
+ "((a + b) - c)",
+ },
+ {
+ "a * b * c",
+ "((a * b) * c)",
+ },
+ {
+ "a * b / c",
+ "((a * b) / c)",
+ },
+ {
+ "a + b / c",
+ "(a + (b / c))",
+ },
+ {
+ "a + b * c + d / e - f",
+ "(((a + (b * c)) + (d / e)) - f)",
+ },
+ {
+ "3 + 4; -5 * 5",
+ "(3 + 4)((-5) * 5)",
+ },
+ {
+ "5 > 4 == 3 < 4",
+ "((5 > 4) == (3 < 4))",
+ },
+ {
+ "5 < 4 != 3 > 4",
+ "((5 < 4) != (3 > 4))",
+ },
+ {
+ "5 >= 4 == 3 <= 4",
+ "((5 >= 4) == (3 <= 4))",
+ },
+ {
+ "5 <= 4 != 3 >= 4",
+ "((5 <= 4) != (3 >= 4))",
+ },
+ {
+ "3 + 4 * 5 == 3 * 1 + 4 * 5",
+ "((3 + (4 * 5)) == ((3 * 1) + (4 * 5)))",
+ },
+ {
+ "3 + 4 * 5 == 3 * 1 + 4 * 5",
+ "((3 + (4 * 5)) == ((3 * 1) + (4 * 5)))",
+ },
+ {
+ "true",
+ "true",
+ },
+ {
+ "false",
+ "false",
+ },
+ {
+ "3 > 5 == false",
+ "((3 > 5) == false)",
+ },
+ {
+ "3 < 5 == true",
+ "((3 < 5) == true)",
+ },
+ {
+ "1 + (2 + 3) + 4",
+ "((1 + (2 + 3)) + 4)",
+ },
+ {
+ "(5 + 5) * 2",
+ "((5 + 5) * 2)",
+ },
+ {
+ "2 / (5 + 5)",
+ "(2 / (5 + 5))",
+ },
+ {
+ "(5 + 5) * 2 * (5 + 5)",
+ "(((5 + 5) * 2) * (5 + 5))",
+ },
+ {
+ "-(5 + 5)",
+ "(-(5 + 5))",
+ },
+ {
+ "!(true == true)",
+ "(!(true == true))",
+ },
+ {
+ "false && true || false",
+ "((false && true) || false)",
+ },
+ {
+ "false && !true || !false",
+ "((false && (!true)) || (!false))",
+ },
+ {
+ "isIt() || false || true",
+ "((isIt() || false) || true)",
+ },
+ {
+ "isIt() && false && true",
+ "((isIt() && false) && true)",
+ },
+ {
+ "a + add(b * c) + d",
+ "((a + add((b * c))) + d)",
+ },
+ {
+ "add(a, b, 1, 2 * 3, 4 + 5, add(6, 7 * 8))",
+ "add(a, b, 1, (2 * 3), (4 + 5), add(6, (7 * 8)))",
+ },
+ {
+ "add(a + b + c * d / f + g)",
+ "add((((a + b) + ((c * d) / f)) + g))",
+ },
+ {
+ "a * [1, 2, 3, 4][b * c] * d",
+ "((a * ([1, 2, 3, 4][(b * c)])) * d)",
+ },
+ {
+ "add(a * b[2], b[1], 2 * [1, 2][1])",
+ "add((a * (b[2])), (b[1]), (2 * ([1, 2][1])))",
+ },
+ {
+ "[1, 2, 3, 4][b * c] = 3 * d == 30",
+ "(([1, 2, 3, 4][(b * c)]) = ((3 * d) == 30))",
+ },
+ {
+ "(x = 1) == 1",
+ "((x = 1) == 1)",
+ },
+ {
+ "x = y = 3",
+ "((x = y) = 3)",
+ },
+ {
+ "x++",
+ "(x++)",
+ },
+ {
+ "1 + x++",
+ "(1 + (x++))",
+ },
+ {
+ "a[x]++",
+ "((a[x])++)",
+ },
+ {
+ "1 + a[x]++",
+ "(1 + ((a[x])++))",
+ },
+ {
+ "a[x++]",
+ "(a[(x++)])",
+ },
+ {
+ "x--",
+ "(x--)",
+ },
+ {
+ "1 + x--",
+ "(1 + (x--))",
+ },
+ {
+ "a[x]--",
+ "((a[x])--)",
+ },
+ {
+ "1 + a[x]--",
+ "(1 + ((a[x])--))",
+ },
+ {
+ "a[x--]",
+ "(a[(x--)])",
+ },
+ }
+
+ for _, tt := range tests {
+ l := lexer.New(tt.input)
+ p := New(l)
+ program, _ := p.ParseProgram()
+ checkParserErrors(t, p)
+
+ actual := program.String()
+ if actual != tt.expected {
+ t.Errorf("expected=%q, got=%q", tt.expected, actual)
+ }
+ }
+}
+
+func TestBooleanExpression(t *testing.T) {
+ tests := []struct {
+ input string
+ expectedBoolean bool
+ }{
+ {"true;", true},
+ {"false;", false},
+ }
+
+ for _, tt := range tests {
+ l := lexer.New(tt.input)
+ p := New(l)
+ program, _ := p.ParseProgram()
+ checkParserErrors(t, p)
+
+ if len(program.Statements) != 1 {
+ t.Fatalf("program has not enough statements. got=%d",
+ len(program.Statements))
+ }
+
+ stmt, ok := program.Statements[0].(*ast.ExpressionStatement)
+ if !ok {
+ t.Fatalf("program.Statements[0] is not ast.ExpressionStatement. got=%T",
+ program.Statements[0])
+ }
+
+ boolean, ok := stmt.Expression.(*ast.Boolean)
+ if !ok {
+ t.Fatalf("exp not *ast.Boolean. got=%T", stmt.Expression)
+ }
+ if boolean.Value != tt.expectedBoolean {
+ t.Errorf("boolean.Value not %t. got=%t", tt.expectedBoolean,
+ boolean.Value)
+ }
+ }
+}
+
+func TestIfStatement(t *testing.T) {
+ input := `if (x < y) { x }`
+
+ l := lexer.New(input)
+ p := New(l)
+ program, _ := p.ParseProgram()
+ checkParserErrors(t, p)
+
+ if len(program.Statements) != 1 {
+ t.Fatalf("program.Body does not contain %d statements. got=%d\n",
+ 1, len(program.Statements))
+ }
+
+ stmt, ok := program.Statements[0].(*ast.IfStatement)
+ if !ok {
+ t.Fatalf("program.Statements[0] is not ast.IfStatement. got=%T",
+ program.Statements[0])
+ }
+
+ if !testInfixExpression(t, stmt.Condition, "x", "<", "y") {
+ return
+ }
+
+ if len(stmt.Consequence.Statements) != 1 {
+ t.Errorf("consequence is not 1 statements. got=%d\n",
+ len(stmt.Consequence.Statements))
+ }
+
+ consequence, ok := stmt.Consequence.Statements[0].(*ast.ExpressionStatement)
+ if !ok {
+ t.Fatalf("Statements[0] is not ast.ExpressionStatement. got=%T",
+ stmt.Consequence.Statements[0])
+ }
+
+ if !testIdentifier(t, consequence.Expression, "x") {
+ return
+ }
+
+ if stmt.Alternative != nil {
+ t.Errorf("exp.Alternative.Statements was not nil. got=%+v", stmt.Alternative)
+ }
+}
+
+func TestIfElseStatement(t *testing.T) {
+ input := `if (x < y) { x } else { y }`
+
+ l := lexer.New(input)
+ p := New(l)
+ program, _ := p.ParseProgram()
+ checkParserErrors(t, p)
+
+ if len(program.Statements) != 1 {
+ t.Fatalf("program.Body does not contain %d statements. got=%d\n",
+ 1, len(program.Statements))
+ }
+
+ stmt, ok := program.Statements[0].(*ast.IfStatement)
+ if !ok {
+ t.Fatalf("program.Statements[0] is not ast.ExpressionStatement. got=%T",
+ program.Statements[0])
+ }
+
+ if !testInfixExpression(t, stmt.Condition, "x", "<", "y") {
+ return
+ }
+
+ if len(stmt.Consequence.Statements) != 1 {
+ t.Errorf("consequence is not 1 statements. got=%d\n",
+ len(stmt.Consequence.Statements))
+ }
+
+ consequence, ok := stmt.Consequence.Statements[0].(*ast.ExpressionStatement)
+ if !ok {
+ t.Fatalf("Statements[0] is not ast.ExpressionStatement. got=%T",
+ stmt.Consequence.Statements[0])
+ }
+
+ if !testIdentifier(t, consequence.Expression, "x") {
+ return
+ }
+
+ alternativeStmt, ok := stmt.Alternative.(*ast.BlockStatement)
+ if !ok {
+ t.Fatalf("alternative is not ast.BlockStatement. got=%T",
+ stmt.Alternative)
+ }
+
+ if len(alternativeStmt.Statements) != 1 {
+ t.Errorf("exp.Alternative.Statements does not contain 1 statements. got=%d\n",
+ len(alternativeStmt.Statements))
+ }
+
+ alternative, ok := alternativeStmt.Statements[0].(*ast.ExpressionStatement)
+ if !ok {
+ t.Fatalf("Statements[0] is not ast.ExpressionStatement. got=%T",
+ alternativeStmt.Statements[0])
+ }
+
+ if !testIdentifier(t, alternative.Expression, "y") {
+ return
+ }
+}
+
+func TestIfElseIfStatement(t *testing.T) {
+ input := `if (x < y) { x } else if (x == y) { 42 } else { y }`
+
+ l := lexer.New(input)
+ p := New(l)
+ program, _ := p.ParseProgram()
+ checkParserErrors(t, p)
+
+ if len(program.Statements) != 1 {
+ t.Fatalf("program.Body does not contain %d statements. got=%d\n",
+ 1, len(program.Statements))
+ }
+
+ stmt, ok := program.Statements[0].(*ast.IfStatement)
+ if !ok {
+ t.Fatalf("program.Statements[0] is not ast.ExpressionStatement. got=%T",
+ program.Statements[0])
+ }
+
+ if !testInfixExpression(t, stmt.Condition, "x", "<", "y") {
+ return
+ }
+
+ if len(stmt.Consequence.Statements) != 1 {
+ t.Errorf("consequence is not 1 statements. got=%d\n",
+ len(stmt.Consequence.Statements))
+ }
+
+ consequence, ok := stmt.Consequence.Statements[0].(*ast.ExpressionStatement)
+ if !ok {
+ t.Fatalf("Statements[0] is not ast.ExpressionStatement. got=%T",
+ stmt.Consequence.Statements[0])
+ }
+
+ if !testIdentifier(t, consequence.Expression, "x") {
+ return
+ }
+
+ alternativeStmt, ok := stmt.Alternative.(*ast.IfStatement)
+ if !ok {
+ t.Fatalf("alternative is not ast.IfStatement. got=%T",
+ stmt.Alternative)
+ }
+
+ if !testInfixExpression(t, alternativeStmt.Condition, "x", "==", "y") {
+ return
+ }
+
+ if len(alternativeStmt.Consequence.Statements) != 1 {
+ t.Errorf("alternative consequence is not 1 statements. got=%d\n",
+ len(stmt.Consequence.Statements))
+ }
+
+ consequence, ok = alternativeStmt.Consequence.Statements[0].(*ast.ExpressionStatement)
+ if !ok {
+ t.Fatalf("Statements[0] is not ast.ExpressionStatement. got=%T",
+ alternativeStmt.Consequence.Statements[0])
+ }
+
+ if !testIntegerLiteral(t, consequence.Expression, 42) {
+ return
+ }
+
+ alternativeStmt2, ok := alternativeStmt.Alternative.(*ast.BlockStatement)
+ if !ok {
+ t.Fatalf("alternative of alternative is not ast.BlockStatement. got=%T",
+ stmt.Alternative)
+ }
+
+ if len(alternativeStmt2.Statements) != 1 {
+ t.Errorf("exp.Alternative.Statements does not contain 1 statements. got=%d\n",
+ len(alternativeStmt2.Statements))
+ }
+
+ alternative2, ok := alternativeStmt2.Statements[0].(*ast.ExpressionStatement)
+ if !ok {
+ t.Fatalf("Statements[0] is not ast.ExpressionStatement. got=%T",
+ alternativeStmt2.Statements[0])
+ }
+
+ if !testIdentifier(t, alternative2.Expression, "y") {
+ return
+ }
+}
+
+func TestForStatement(t *testing.T) {
+ input := `for (let i=0; i < 5; i++) { x }`
+
+ l := lexer.New(input)
+ p := New(l)
+ program, _ := p.ParseProgram()
+ checkParserErrors(t, p)
+
+ if len(program.Statements) != 1 {
+ t.Fatalf("program.Body does not contain %d statements. got=%d\n",
+ 1, len(program.Statements))
+ }
+
+ stmt, ok := program.Statements[0].(*ast.ForStatement)
+ if !ok {
+ t.Fatalf("program.Statements[0] is not ast.ForStatement. got=%T",
+ program.Statements[0])
+ }
+
+ if !testLetStatement(t, stmt.Initialization, "i") {
+ return
+ }
+ if !testInfixExpression(t, stmt.Condition, "i", "<", 5) {
+ return
+ }
+
+ expStmt, ok := stmt.Afterthought.(*ast.ExpressionStatement)
+ if !ok {
+ t.Fatalf("stmt.Afterthought is not ast.ExpressionStatement. got=%T",
+ stmt.Afterthought)
+ }
+ pae, ok := expStmt.Expression.(*ast.PostAssignmentExpression)
+ if !ok {
+ t.Fatalf("stmt is not ast.PostAssignmentExpression. got=%T", stmt.Afterthought)
+ }
+ if pae.Operator != "++" {
+ t.Fatalf("exp.Operator is not '%s'. got=%s",
+ "++", pae.Operator)
+ }
+ if !testLiteralExpression(t, pae.Left, "i") {
+ return
+ }
+
+ if len(stmt.Loop.Statements) != 1 {
+ t.Errorf("loop is not 1 statements. got=%d\n",
+ len(stmt.Loop.Statements))
+ }
+
+ loop, ok := stmt.Loop.Statements[0].(*ast.ExpressionStatement)
+ if !ok {
+ t.Fatalf("Statements[0] is not ast.ExpressionStatement. got=%T",
+ stmt.Loop.Statements[0])
+ }
+ if !testIdentifier(t, loop.Expression, "x") {
+ return
+ }
+}
+
+func TestFunctionLiteralParsing(t *testing.T) {
+ input := `function(x, y) { x + y; }`
+
+ l := lexer.New(input)
+ p := New(l)
+ program, _ := p.ParseProgram()
+ checkParserErrors(t, p)
+
+ if len(program.Statements) != 1 {
+ t.Fatalf("program.Body does not contain %d statements. got=%d\n",
+ 1, len(program.Statements))
+ }
+
+ stmt, ok := program.Statements[0].(*ast.ExpressionStatement)
+ if !ok {
+ t.Fatalf("program.Statements[0] is not ast.ExpressionStatement. got=%T",
+ program.Statements[0])
+ }
+
+ function, ok := stmt.Expression.(*ast.FunctionLiteral)
+ if !ok {
+ t.Fatalf("stmt.Expression is not ast.FunctionLiteral. got=%T",
+ stmt.Expression)
+ }
+
+ if len(function.Parameters) != 2 {
+ t.Fatalf("function literal parameters wrong. want 2, got=%d\n",
+ len(function.Parameters))
+ }
+
+ testLiteralExpression(t, function.Parameters[0], "x")
+ testLiteralExpression(t, function.Parameters[1], "y")
+
+ if len(function.Body.Statements) != 1 {
+ t.Fatalf("function.Body.Statements has not 1 statements. got=%d\n",
+ len(function.Body.Statements))
+ }
+
+ bodyStmt, ok := function.Body.Statements[0].(*ast.ExpressionStatement)
+ if !ok {
+ t.Fatalf("function body stmt is not ast.ExpressionStatement. got=%T",
+ function.Body.Statements[0])
+ }
+
+ testInfixExpression(t, bodyStmt.Expression, "x", "+", "y")
+}
+
+func TestFunctionParameterParsing(t *testing.T) {
+ tests := []struct {
+ input string
+ expectedParams []string
+ }{
+ {input: "function() {};", expectedParams: []string{}},
+ {input: "function(x) {};", expectedParams: []string{"x"}},
+ {input: "function(x, y, z) {};", expectedParams: []string{"x", "y", "z"}},
+ }
+
+ for _, tt := range tests {
+ l := lexer.New(tt.input)
+ p := New(l)
+ program, _ := p.ParseProgram()
+ checkParserErrors(t, p)
+
+ stmt := program.Statements[0].(*ast.ExpressionStatement)
+ function := stmt.Expression.(*ast.FunctionLiteral)
+
+ if len(function.Parameters) != len(tt.expectedParams) {
+ t.Errorf("length parameters wrong. want %d, got=%d\n",
+ len(tt.expectedParams), len(function.Parameters))
+ }
+
+ for i, ident := range tt.expectedParams {
+ testLiteralExpression(t, function.Parameters[i], ident)
+ }
+ }
+}
+
+func TestCallExpressionParsing(t *testing.T) {
+ input := "add(1, 2 * 3, 4 + 5);"
+
+ l := lexer.New(input)
+ p := New(l)
+ program, _ := p.ParseProgram()
+ checkParserErrors(t, p)
+
+ if len(program.Statements) != 1 {
+ t.Fatalf("program.Statements does not contain %d statements. got=%d\n",
+ 1, len(program.Statements))
+ }
+
+ stmt, ok := program.Statements[0].(*ast.ExpressionStatement)
+ if !ok {
+ t.Fatalf("stmt is not ast.ExpressionStatement. got=%T",
+ program.Statements[0])
+ }
+
+ exp, ok := stmt.Expression.(*ast.CallExpression)
+ if !ok {
+ t.Fatalf("stmt.Expression is not ast.CallExpression. got=%T",
+ stmt.Expression)
+ }
+
+ if !testIdentifier(t, exp.Function, "add") {
+ return
+ }
+
+ if len(exp.Arguments) != 3 {
+ t.Fatalf("wrong length of arguments. got=%d", len(exp.Arguments))
+ }
+
+ testLiteralExpression(t, exp.Arguments[0], 1)
+ testInfixExpression(t, exp.Arguments[1], 2, "*", 3)
+ testInfixExpression(t, exp.Arguments[2], 4, "+", 5)
+}
+
+func TestCallExpressionParameterParsing(t *testing.T) {
+ tests := []struct {
+ input string
+ expectedIdent string
+ expectedArgs []string
+ }{
+ {
+ input: "add();",
+ expectedIdent: "add",
+ expectedArgs: []string{},
+ },
+ {
+ input: "add(1);",
+ expectedIdent: "add",
+ expectedArgs: []string{"1"},
+ },
+ {
+ input: "add(1, 2 * 3, 4 + 5);",
+ expectedIdent: "add",
+ expectedArgs: []string{"1", "(2 * 3)", "(4 + 5)"},
+ },
+ }
+
+ for _, tt := range tests {
+ l := lexer.New(tt.input)
+ p := New(l)
+ program, _ := p.ParseProgram()
+ checkParserErrors(t, p)
+
+ stmt := program.Statements[0].(*ast.ExpressionStatement)
+ exp, ok := stmt.Expression.(*ast.CallExpression)
+ if !ok {
+ t.Fatalf("stmt.Expression is not ast.CallExpression. got=%T",
+ stmt.Expression)
+ }
+
+ if !testIdentifier(t, exp.Function, tt.expectedIdent) {
+ return
+ }
+
+ if len(exp.Arguments) != len(tt.expectedArgs) {
+ t.Fatalf("wrong number of arguments. want=%d, got=%d",
+ len(tt.expectedArgs), len(exp.Arguments))
+ }
+
+ for i, arg := range tt.expectedArgs {
+ if exp.Arguments[i].String() != arg {
+ t.Errorf("argument %d wrong. want=%q, got=%q", i,
+ arg, exp.Arguments[i].String())
+ }
+ }
+ }
+}
+
+func TestStringLiteralExpression(t *testing.T) {
+ input := `"hello world";`
+
+ l := lexer.New(input)
+ p := New(l)
+ program, _ := p.ParseProgram()
+ checkParserErrors(t, p)
+
+ stmt := program.Statements[0].(*ast.ExpressionStatement)
+ literal, ok := stmt.Expression.(*ast.StringLiteral)
+ if !ok {
+ t.Fatalf("exp not *ast.StringLiteral. got=%T", stmt.Expression)
+ }
+
+ if literal.Value != "hello world" {
+ t.Errorf("literal.Value not %q. got=%q", "hello world", literal.Value)
+ }
+}
+
+func TestParsingEmptyArrayLiterals(t *testing.T) {
+ input := "[]"
+
+ l := lexer.New(input)
+ p := New(l)
+ program, _ := p.ParseProgram()
+ checkParserErrors(t, p)
+
+ stmt, ok := program.Statements[0].(*ast.ExpressionStatement)
+ array, ok := stmt.Expression.(*ast.ArrayLiteral)
+ if !ok {
+ t.Fatalf("exp not ast.ArrayLiteral. got=%T", stmt.Expression)
+ }
+
+ if len(array.Elements) != 0 {
+ t.Errorf("len(array.Elements) not 0. got=%d", len(array.Elements))
+ }
+}
+
+func TestParsingArrayLiterals(t *testing.T) {
+ input := "[1, 2 * 2, 3 + 3]"
+
+ l := lexer.New(input)
+ p := New(l)
+ program, _ := p.ParseProgram()
+ checkParserErrors(t, p)
+
+ stmt, ok := program.Statements[0].(*ast.ExpressionStatement)
+ array, ok := stmt.Expression.(*ast.ArrayLiteral)
+ if !ok {
+ t.Fatalf("exp not ast.ArrayLiteral. got=%T", stmt.Expression)
+ }
+
+ if len(array.Elements) != 3 {
+ t.Fatalf("len(array.Elements) not 3. got=%d", len(array.Elements))
+ }
+
+ testIntegerLiteral(t, array.Elements[0], 1)
+ testInfixExpression(t, array.Elements[1], 2, "*", 2)
+ testInfixExpression(t, array.Elements[2], 3, "+", 3)
+}
+
+func TestParsingIndexExpressions(t *testing.T) {
+ input := "myArray[1 + 1]"
+
+ l := lexer.New(input)
+ p := New(l)
+ program, _ := p.ParseProgram()
+ checkParserErrors(t, p)
+
+ stmt, ok := program.Statements[0].(*ast.ExpressionStatement)
+ indexExp, ok := stmt.Expression.(*ast.IndexExpression)
+ if !ok {
+ t.Fatalf("exp not *ast.IndexExpression. got=%T", stmt.Expression)
+ }
+
+ if !testIdentifier(t, indexExp.Left, "myArray") {
+ return
+ }
+
+ if !testInfixExpression(t, indexExp.Index, 1, "+", 1) {
+ return
+ }
+}
+
+func TestParsingEmptyHashLiteral(t *testing.T) {
+ input := "{}"
+
+ l := lexer.New(input)
+ p := New(l)
+ program, _ := p.ParseProgram()
+ checkParserErrors(t, p)
+
+ stmt := program.Statements[0].(*ast.ExpressionStatement)
+ hash, ok := stmt.Expression.(*ast.HashLiteral)
+ if !ok {
+ t.Fatalf("exp is not ast.HashLiteral. got=%T", stmt.Expression)
+ }
+
+ if len(hash.Pairs) != 0 {
+ t.Errorf("hash.Pairs has wrong length. got=%d", len(hash.Pairs))
+ }
+}
+
+func TestParsingHashLiteralsStringKeys(t *testing.T) {
+ input := `{"one": 1, "two": 2, "three": 3}`
+
+ l := lexer.New(input)
+ p := New(l)
+ program, _ := p.ParseProgram()
+ checkParserErrors(t, p)
+
+ stmt := program.Statements[0].(*ast.ExpressionStatement)
+ hash, ok := stmt.Expression.(*ast.HashLiteral)
+ if !ok {
+ t.Fatalf("exp is not ast.HashLiteral. got=%T", stmt.Expression)
+ }
+
+ expected := map[string]int64{
+ "one": 1,
+ "two": 2,
+ "three": 3,
+ }
+
+ if len(hash.Pairs) != len(expected) {
+ t.Errorf("hash.Pairs has wrong length. got=%d", len(hash.Pairs))
+ }
+
+ for key, value := range hash.Pairs {
+ literal, ok := key.(*ast.StringLiteral)
+ if !ok {
+ t.Errorf("key is not ast.StringLiteral. got=%T", key)
+ continue
+ }
+
+ expectedValue := expected[literal.String()]
+ testIntegerLiteral(t, value, expectedValue)
+ }
+}
+
+func TestParsingHashLiteralsBooleanKeys(t *testing.T) {
+ input := `{true: 1, false: 2}`
+
+ l := lexer.New(input)
+ p := New(l)
+ program, _ := p.ParseProgram()
+ checkParserErrors(t, p)
+
+ stmt := program.Statements[0].(*ast.ExpressionStatement)
+ hash, ok := stmt.Expression.(*ast.HashLiteral)
+ if !ok {
+ t.Fatalf("exp is not ast.HashLiteral. got=%T", stmt.Expression)
+ }
+
+ expected := map[string]int64{
+ "true": 1,
+ "false": 2,
+ }
+
+ if len(hash.Pairs) != len(expected) {
+ t.Errorf("hash.Pairs has wrong length. got=%d", len(hash.Pairs))
+ }
+
+ for key, value := range hash.Pairs {
+ boolean, ok := key.(*ast.Boolean)
+ if !ok {
+ t.Errorf("key is not ast.BooleanLiteral. got=%T", key)
+ continue
+ }
+
+ expectedValue := expected[boolean.String()]
+ testIntegerLiteral(t, value, expectedValue)
+ }
+}
+
+func TestParsingHashLiteralsIntegerKeys(t *testing.T) {
+ input := `{1: 1, 2: 2, 3: 3}`
+
+ l := lexer.New(input)
+ p := New(l)
+ program, _ := p.ParseProgram()
+ checkParserErrors(t, p)
+
+ stmt := program.Statements[0].(*ast.ExpressionStatement)
+ hash, ok := stmt.Expression.(*ast.HashLiteral)
+ if !ok {
+ t.Fatalf("exp is not ast.HashLiteral. got=%T", stmt.Expression)
+ }
+
+ expected := map[string]int64{
+ "1": 1,
+ "2": 2,
+ "3": 3,
+ }
+
+ if len(hash.Pairs) != len(expected) {
+ t.Errorf("hash.Pairs has wrong length. got=%d", len(hash.Pairs))
+ }
+
+ for key, value := range hash.Pairs {
+ integer, ok := key.(*ast.IntegerLiteral)
+ if !ok {
+ t.Errorf("key is not ast.IntegerLiteral. got=%T", key)
+ continue
+ }
+
+ expectedValue := expected[integer.String()]
+
+ testIntegerLiteral(t, value, expectedValue)
+ }
+}
+
+func TestParsingHashLiteralsWithExpressions(t *testing.T) {
+ input := `{"one": 0 + 1, "two": 10 - 8, "three": 15 / 5}`
+
+ l := lexer.New(input)
+ p := New(l)
+ program, _ := p.ParseProgram()
+ checkParserErrors(t, p)
+
+ stmt := program.Statements[0].(*ast.ExpressionStatement)
+ hash, ok := stmt.Expression.(*ast.HashLiteral)
+ if !ok {
+ t.Fatalf("exp is not ast.HashLiteral. got=%T", stmt.Expression)
+ }
+
+ if len(hash.Pairs) != 3 {
+ t.Errorf("hash.Pairs has wrong length. got=%d", len(hash.Pairs))
+ }
+
+ tests := map[string]func(ast.Expression){
+ "one": func(e ast.Expression) {
+ testInfixExpression(t, e, 0, "+", 1)
+ },
+ "two": func(e ast.Expression) {
+ testInfixExpression(t, e, 10, "-", 8)
+ },
+ "three": func(e ast.Expression) {
+ testInfixExpression(t, e, 15, "/", 5)
+ },
+ }
+
+ for key, value := range hash.Pairs {
+ literal, ok := key.(*ast.StringLiteral)
+ if !ok {
+ t.Errorf("key is not ast.StringLiteral. got=%T", key)
+ continue
+ }
+
+ testFunc, ok := tests[literal.String()]
+ if !ok {
+ t.Errorf("No test function for key %q found", literal.String())
+ continue
+ }
+
+ testFunc(value)
+ }
+}
+
+func testLetStatement(t *testing.T, s ast.Statement, name string) bool {
+ if s.TokenLiteral() != "let" {
+ t.Errorf("s.TokenLiteral not 'let'. got=%q", s.TokenLiteral())
+ return false
+ }
+
+ letStmt, ok := s.(*ast.LetStatement)
+ if !ok {
+ t.Errorf("s not *ast.LetStatement. got=%T", s)
+ return false
+ }
+
+ if letStmt.Name.Value != name {
+ t.Errorf("letStmt.Name.Value not '%s'. got=%s", name, letStmt.Name.Value)
+ return false
+ }
+
+ if letStmt.Name.TokenLiteral() != name {
+ t.Errorf("s.Name not '%s'. got=%s", name, letStmt.Name)
+ return false
+ }
+
+ return true
+}
+
+func testInfixExpression(t *testing.T, exp ast.Expression, left interface{},
+ operator string, right interface{}) bool {
+
+ opExp, ok := exp.(*ast.InfixExpression)
+ if !ok {
+ t.Errorf("exp is not ast.InfixExpression. got=%T(%s)", exp, exp)
+ return false
+ }
+
+ if !testLiteralExpression(t, opExp.Left, left) {
+ return false
+ }
+
+ if opExp.Operator != operator {
+ t.Errorf("exp.Operator is not '%s'. got=%q", operator, opExp.Operator)
+ return false
+ }
+
+ if !testLiteralExpression(t, opExp.Right, right) {
+ return false
+ }
+
+ return true
+}
+
+func testAssignmentExpression(t *testing.T, exp ast.Expression, left interface{},
+ operator string, right interface{}) bool {
+
+ opExp, ok := exp.(*ast.AssignmentExpression)
+ if !ok {
+ t.Errorf("exp is not ast.AssignmentExpression. got=%T(%s)", exp, exp)
+ return false
+ }
+
+ if !testLiteralExpression(t, opExp.Left, left) {
+ return false
+ }
+
+ if opExp.Operator != operator {
+ t.Errorf("exp.Operator is not '%s'. got=%q", operator, opExp.Operator)
+ return false
+ }
+
+ if !testLiteralExpression(t, opExp.Right, right) {
+ return false
+ }
+
+ return true
+}
+
+func testLiteralExpression(
+ t *testing.T,
+ exp ast.Expression,
+ expected interface{},
+) bool {
+ switch v := expected.(type) {
+ case int:
+ return testIntegerLiteral(t, exp, int64(v))
+ case int64:
+ return testIntegerLiteral(t, exp, v)
+ case string:
+ return testIdentifier(t, exp, v)
+ case bool:
+ return testBooleanLiteral(t, exp, v)
+ }
+ t.Errorf("type of exp not handled. got=%T", exp)
+ return false
+}
+
+func testIntegerLiteral(t *testing.T, il ast.Expression, value int64) bool {
+ integ, ok := il.(*ast.IntegerLiteral)
+ if !ok {
+ t.Errorf("il not *ast.IntegerLiteral. got=%T", il)
+ return false
+ }
+
+ if integ.Value != value {
+ t.Errorf("integ.Value not %d. got=%d", value, integ.Value)
+ return false
+ }
+
+ if integ.TokenLiteral() != fmt.Sprintf("%d", value) {
+ t.Errorf("integ.TokenLiteral not %d. got=%s", value,
+ integ.TokenLiteral())
+ return false
+ }
+
+ return true
+}
+
+func testIdentifier(t *testing.T, exp ast.Expression, value string) bool {
+ ident, ok := exp.(*ast.Identifier)
+ if !ok {
+ t.Errorf("exp not *ast.Identifier. got=%T", exp)
+ return false
+ }
+
+ if ident.Value != value {
+ t.Errorf("ident.Value not %s. got=%s", value, ident.Value)
+ return false
+ }
+
+ if ident.TokenLiteral() != value {
+ t.Errorf("ident.TokenLiteral not %s. got=%s", value,
+ ident.TokenLiteral())
+ return false
+ }
+
+ return true
+}
+
+func testBooleanLiteral(t *testing.T, exp ast.Expression, value bool) bool {
+ bo, ok := exp.(*ast.Boolean)
+ if !ok {
+ t.Errorf("exp not *ast.Boolean. got=%T", exp)
+ return false
+ }
+
+ if bo.Value != value {
+ t.Errorf("bo.Value not %t. got=%t", value, bo.Value)
+ return false
+ }
+
+ if bo.TokenLiteral() != fmt.Sprintf("%t", value) {
+ t.Errorf("bo.TokenLiteral not %t. got=%s",
+ value, bo.TokenLiteral())
+ return false
+ }
+
+ return true
+}
+
+func testParserErrors(t *testing.T, p *Parser, expected []ParseError) {
+ errors := p.Errors()
+ if len(errors) != len(expected) {
+ t.Errorf("expected %d errors but parser has %d errors", len(expected), len(errors))
+ t.FailNow()
+ }
+
+ fail := false
+ for i, err := range errors {
+ if err.Message != expected[i].Message {
+ t.Errorf("parser error[%d]: wrong error message '%s', expected '%s'", i, err.Message, expected[i].Message)
+ fail = true
+ }
+ if err.Line != expected[i].Line {
+ t.Errorf("parser error[%d]: wrong error line %d, expected %d", i, err.Line, expected[i].Line)
+ fail = true
+ }
+ if err.Column != expected[i].Column {
+ t.Errorf("parser error[%d]: wrong error column %d, expected %d", i, err.Column, expected[i].Column)
+ fail = true
+ }
+ }
+ if fail {
+ t.FailNow()
+ }
+}
+
+func checkParserErrors(t *testing.T, p *Parser) {
+ errors := p.Errors()
+ if len(errors) == 0 {
+ return
+ }
+
+ t.Errorf("parser has %d errors", len(errors))
+ for _, err := range errors {
+ t.Errorf("parser error: %q (line %d, col %d)", err.Message, err.Line, err.Column)
+ }
+ t.FailNow()
+}
diff --git a/dsl/repl/repl.go b/dsl/repl/repl.go
new file mode 100644
index 0000000..6e3a261
--- /dev/null
+++ b/dsl/repl/repl.go
@@ -0,0 +1,51 @@
+package repl
+
+import (
+ "bufio"
+ "fmt"
+ "github.com/ofux/deluge/dsl/evaluator"
+ "github.com/ofux/deluge/dsl/lexer"
+ "github.com/ofux/deluge/dsl/object"
+ "github.com/ofux/deluge/dsl/parser"
+ "io"
+)
+
+const PROMPT = ">> "
+
+func Start(in io.Reader, out io.Writer) {
+ scanner := bufio.NewScanner(in)
+ env := object.NewEnvironment()
+
+ for {
+ fmt.Printf(PROMPT)
+ scanned := scanner.Scan()
+ if !scanned {
+ return
+ }
+
+ line := scanner.Text()
+ l := lexer.New(line)
+ p := parser.New(l)
+
+ program, ok := p.ParseProgram()
+ if !ok {
+ printParserErrors(out, p.Errors())
+ continue
+ }
+
+ ev := evaluator.NewEvaluator()
+
+ evaluated := ev.Eval(program, env)
+ if evaluated != nil {
+ io.WriteString(out, evaluated.Inspect())
+ io.WriteString(out, "\n")
+ }
+ }
+}
+
+func printParserErrors(out io.Writer, errors []parser.ParseError) {
+ io.WriteString(out, "Syntax error:\n")
+ for _, err := range errors {
+ io.WriteString(out, fmt.Sprintf("\t%s (line %d, col %d)\n", err.Message, err.Line, err.Column))
+ }
+}
diff --git a/dsl/token/token.go b/dsl/token/token.go
new file mode 100644
index 0000000..633f029
--- /dev/null
+++ b/dsl/token/token.go
@@ -0,0 +1,94 @@
+package token
+
+type TokenType string
+
+const (
+ ILLEGAL = "ILLEGAL"
+ EOF = "EOF"
+
+ // Identifiers + literals
+ IDENT = "IDENT" // add, foobar, x, y, ...
+ INT = "INT" // 1343456
+ FLOAT = "FLOAT" // 1238.873
+ STRING = "STRING" // "foobar"
+
+ ASSIGN = "="
+ ASSIGN_DEC1 = "--"
+ ASSIGN_INC1 = "++"
+ ASSIGN_DEC = "-="
+ ASSIGN_INC = "+="
+ ASSIGN_MULT = "*="
+ ASSIGN_DIV = "/="
+
+ // Operators
+ PLUS = "+"
+ MINUS = "-"
+ BANG = "!"
+ ASTERISK = "*"
+ SLASH = "/"
+ MODULO = "%"
+
+ AND = "&&"
+ OR = "||"
+
+ LT = "<"
+ GT = ">"
+ LTE = "<="
+ GTE = ">="
+
+ EQ = "=="
+ NOT_EQ = "!="
+
+ // Delimiters
+ COMMA = ","
+ SEMICOLON = ";"
+ COLON = ":"
+
+ LPAREN = "("
+ RPAREN = ")"
+ LBRACE = "{"
+ RBRACE = "}"
+ LBRACKET = "["
+ RBRACKET = "]"
+
+ // Keywords
+ FUNCTION = "FUNCTION"
+ LET = "LET"
+ TRUE = "TRUE"
+ FALSE = "FALSE"
+ IF = "IF"
+ ELSE = "ELSE"
+ RETURN = "RETURN"
+ FOR = "FOR"
+ NULL = "NULL"
+)
+
+type Token struct {
+ Type TokenType
+ Line int
+ Column int
+ Literal string
+}
+
+var keywords = map[string]TokenType{
+ "function": FUNCTION,
+ "let": LET,
+ "true": TRUE,
+ "false": FALSE,
+ "if": IF,
+ "else": ELSE,
+ "return": RETURN,
+ "for": FOR,
+ "null": NULL,
+}
+
+func LookupIdent(ident string) TokenType {
+ if tok, ok := keywords[ident]; ok {
+ return tok
+ }
+ return IDENT
+}
+
+func IsAssign(t TokenType) bool {
+ return t == ASSIGN || t == ASSIGN_INC1 || t == ASSIGN_DEC1 || t == ASSIGN_INC || t == ASSIGN_DEC || t == ASSIGN_MULT || t == ASSIGN_DIV
+}
diff --git a/glide.lock b/glide.lock
deleted file mode 100644
index 0d92320..0000000
--- a/glide.lock
+++ /dev/null
@@ -1,10 +0,0 @@
-hash: 0290683c9e29f9b8ba7a104596b3e068c565cae69c14a2cc7203e72bca1b1e77
-updated: 2017-05-05T20:59:58.769770399+02:00
-imports:
-- name: github.com/sirupsen/logrus
- version: ba1b36c82c5e05c4f912a88eab0dcd91a171688f
-- name: golang.org/x/sys
- version: 9ccfe848b9db8435a24c424abbc07a921adf1df5
- subpackages:
- - unix
-testImports: []
diff --git a/glide.yaml b/glide.yaml
deleted file mode 100644
index 2b7b459..0000000
--- a/glide.yaml
+++ /dev/null
@@ -1,4 +0,0 @@
-package: github.com/ofux/deluge
-import:
-- package: github.com/sirupsen/logrus
- version: ~0.11.5
diff --git a/go.mod b/go.mod
new file mode 100644
index 0000000..196776e
--- /dev/null
+++ b/go.mod
@@ -0,0 +1,19 @@
+module github.com/ofux/deluge
+
+go 1.24.6
+
+require (
+ github.com/dustin/gojson v0.0.0-20160307161227-2e71ec9dd5ad
+ github.com/ofux/floa v0.0.0-20170708090307-9b9e96298d3e
+ github.com/ofux/hdrhistogram v0.0.0-20191004163134-d8ebd25fd911
+ github.com/sirupsen/logrus v1.9.3
+ github.com/stretchr/testify v1.11.1
+)
+
+require (
+ github.com/davecgh/go-spew v1.1.1 // indirect
+ github.com/pkg/errors v0.8.1 // indirect
+ github.com/pmezard/go-difflib v1.0.0 // indirect
+ golang.org/x/sys v0.37.0 // indirect
+ gopkg.in/yaml.v3 v3.0.1 // indirect
+)
diff --git a/go.sum b/go.sum
new file mode 100644
index 0000000..e7b5bce
--- /dev/null
+++ b/go.sum
@@ -0,0 +1,27 @@
+github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
+github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c=
+github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
+github.com/dustin/gojson v0.0.0-20160307161227-2e71ec9dd5ad h1:Qk76DOWdOp+GlyDKBAG3Klr9cn7N+LcYc82AZ2S7+cA=
+github.com/dustin/gojson v0.0.0-20160307161227-2e71ec9dd5ad/go.mod h1:mPKfmRa823oBIgl2r20LeMSpTAteW5j7FLkc0vjmzyQ=
+github.com/ofux/floa v0.0.0-20170708090307-9b9e96298d3e h1:CtSYUYTxy4EWaM0WR4ZfwKBm3QmH/OyfN/2rKCiYaqQ=
+github.com/ofux/floa v0.0.0-20170708090307-9b9e96298d3e/go.mod h1:kGd51rnek+P8CVmL94SettpFDoGh/iSWUgFHRGlvbok=
+github.com/ofux/hdrhistogram v0.0.0-20191004163134-d8ebd25fd911 h1:FjR5jz3rnzMEm0yAwLmpebRJ+kDhEbkXD/4ZgoK7KX0=
+github.com/ofux/hdrhistogram v0.0.0-20191004163134-d8ebd25fd911/go.mod h1:88FksYQPkNbBfVlrZdXfC+GpBiBI44pM71MVc+LSsIU=
+github.com/pkg/errors v0.8.1 h1:iURUrRGxPUNPdy5/HRSm+Yj6okJ6UtLINN0Q9M4+h3I=
+github.com/pkg/errors v0.8.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0=
+github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM=
+github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
+github.com/sirupsen/logrus v1.9.3 h1:dueUQJ1C2q9oE3F7wvmSGAaVtTmUizReu6fjN8uqzbQ=
+github.com/sirupsen/logrus v1.9.3/go.mod h1:naHLuLoDiP4jHNo9R0sCBMtWGeIprob74mVsIT4qYEQ=
+github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=
+github.com/stretchr/testify v1.7.0/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg=
+github.com/stretchr/testify v1.11.1 h1:7s2iGBzp5EwR7/aIZr8ao5+dra3wiQyKjjFuvgVKu7U=
+github.com/stretchr/testify v1.11.1/go.mod h1:wZwfW3scLgRK+23gO65QZefKpKQRnfz6sD981Nm4B6U=
+golang.org/x/sys v0.0.0-20220715151400-c0bba94af5f8/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
+golang.org/x/sys v0.37.0 h1:fdNQudmxPjkdUTPnLn5mdQv7Zwvbvpaxqs831goi9kQ=
+golang.org/x/sys v0.37.0/go.mod h1:OgkHotnGiDImocRcuBABYBEXf8A9a87e/uXjp9XT3ks=
+gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405 h1:yhCVgyC4o1eVCa2tZl7eS0r+SDo693bJlVdllGtEeKM=
+gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
+gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
+gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA=
+gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
diff --git a/main.go b/main.go
index 268f62c..f37ca89 100644
--- a/main.go
+++ b/main.go
@@ -1,12 +1,13 @@
package main
import (
- "github.com/ofux/deluge-dsl/lexer"
- "github.com/ofux/deluge-dsl/parser"
- "github.com/ofux/deluge/deluge"
- log "github.com/sirupsen/logrus"
"io/ioutil"
"time"
+
+ "github.com/ofux/deluge/deluge"
+ "github.com/ofux/deluge/dsl/lexer"
+ "github.com/ofux/deluge/dsl/parser"
+ log "github.com/sirupsen/logrus"
)
func main() {
diff --git a/vendor/github.com/sirupsen/logrus/.gitignore b/vendor/github.com/sirupsen/logrus/.gitignore
deleted file mode 100644
index 66be63a..0000000
--- a/vendor/github.com/sirupsen/logrus/.gitignore
+++ /dev/null
@@ -1 +0,0 @@
-logrus
diff --git a/vendor/github.com/sirupsen/logrus/.travis.yml b/vendor/github.com/sirupsen/logrus/.travis.yml
deleted file mode 100644
index 804c569..0000000
--- a/vendor/github.com/sirupsen/logrus/.travis.yml
+++ /dev/null
@@ -1,8 +0,0 @@
-language: go
-go:
- - 1.6
- - 1.7
- - tip
-install:
- - go get -t ./...
-script: GOMAXPROCS=4 GORACE="halt_on_error=1" go test -race -v ./...
diff --git a/vendor/github.com/sirupsen/logrus/CHANGELOG.md b/vendor/github.com/sirupsen/logrus/CHANGELOG.md
deleted file mode 100644
index 747e4d8..0000000
--- a/vendor/github.com/sirupsen/logrus/CHANGELOG.md
+++ /dev/null
@@ -1,94 +0,0 @@
-# 0.11.5
-
-* feature: add writer and writerlevel to entry (#372)
-
-# 0.11.4
-
-* bug: fix undefined variable on solaris (#493)
-
-# 0.11.3
-
-* formatter: configure quoting of empty values (#484)
-* formatter: configure quoting character (default is `"`) (#484)
-* bug: fix not importing io correctly in non-linux environments (#481)
-
-# 0.11.2
-
-* bug: fix windows terminal detection (#476)
-
-# 0.11.1
-
-* bug: fix tty detection with custom out (#471)
-
-# 0.11.0
-
-* performance: Use bufferpool to allocate (#370)
-* terminal: terminal detection for app-engine (#343)
-* feature: exit handler (#375)
-
-# 0.10.0
-
-* feature: Add a test hook (#180)
-* feature: `ParseLevel` is now case-insensitive (#326)
-* feature: `FieldLogger` interface that generalizes `Logger` and `Entry` (#308)
-* performance: avoid re-allocations on `WithFields` (#335)
-
-# 0.9.0
-
-* logrus/text_formatter: don't emit empty msg
-* logrus/hooks/airbrake: move out of main repository
-* logrus/hooks/sentry: move out of main repository
-* logrus/hooks/papertrail: move out of main repository
-* logrus/hooks/bugsnag: move out of main repository
-* logrus/core: run tests with `-race`
-* logrus/core: detect TTY based on `stderr`
-* logrus/core: support `WithError` on logger
-* logrus/core: Solaris support
-
-# 0.8.7
-
-* logrus/core: fix possible race (#216)
-* logrus/doc: small typo fixes and doc improvements
-
-
-# 0.8.6
-
-* hooks/raven: allow passing an initialized client
-
-# 0.8.5
-
-* logrus/core: revert #208
-
-# 0.8.4
-
-* formatter/text: fix data race (#218)
-
-# 0.8.3
-
-* logrus/core: fix entry log level (#208)
-* logrus/core: improve performance of text formatter by 40%
-* logrus/core: expose `LevelHooks` type
-* logrus/core: add support for DragonflyBSD and NetBSD
-* formatter/text: print structs more verbosely
-
-# 0.8.2
-
-* logrus: fix more Fatal family functions
-
-# 0.8.1
-
-* logrus: fix not exiting on `Fatalf` and `Fatalln`
-
-# 0.8.0
-
-* logrus: defaults to stderr instead of stdout
-* hooks/sentry: add special field for `*http.Request`
-* formatter/text: ignore Windows for colors
-
-# 0.7.3
-
-* formatter/\*: allow configuration of timestamp layout
-
-# 0.7.2
-
-* formatter/text: Add configuration option for time format (#158)
diff --git a/vendor/github.com/sirupsen/logrus/LICENSE b/vendor/github.com/sirupsen/logrus/LICENSE
deleted file mode 100644
index f090cb4..0000000
--- a/vendor/github.com/sirupsen/logrus/LICENSE
+++ /dev/null
@@ -1,21 +0,0 @@
-The MIT License (MIT)
-
-Copyright (c) 2014 Simon Eskildsen
-
-Permission is hereby granted, free of charge, to any person obtaining a copy
-of this software and associated documentation files (the "Software"), to deal
-in the Software without restriction, including without limitation the rights
-to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
-copies of the Software, and to permit persons to whom the Software is
-furnished to do so, subject to the following conditions:
-
-The above copyright notice and this permission notice shall be included in
-all copies or substantial portions of the Software.
-
-THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
-IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
-FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
-AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
-LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
-OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
-THE SOFTWARE.
diff --git a/vendor/github.com/sirupsen/logrus/README.md b/vendor/github.com/sirupsen/logrus/README.md
deleted file mode 100644
index 640cf61..0000000
--- a/vendor/github.com/sirupsen/logrus/README.md
+++ /dev/null
@@ -1,476 +0,0 @@
-# Logrus
[](https://travis-ci.org/Sirupsen/logrus) [](https://godoc.org/github.com/Sirupsen/logrus)
-
-**Seeing weird case-sensitive problems?** See [this
-issue](https://github.com/sirupsen/logrus/issues/451#issuecomment-264332021).
-This change has been reverted. I apologize for causing this. I greatly
-underestimated the impact this would have. Logrus strives for stability and
-backwards compatibility and failed to provide that.
-
-Logrus is a structured logger for Go (golang), completely API compatible with
-the standard library logger. [Godoc][godoc]. **Please note the Logrus API is not
-yet stable (pre 1.0). Logrus itself is completely stable and has been used in
-many large deployments. The core API is unlikely to change much but please
-version control your Logrus to make sure you aren't fetching latest `master` on
-every build.**
-
-Nicely color-coded in development (when a TTY is attached, otherwise just
-plain text):
-
-
-
-With `log.SetFormatter(&log.JSONFormatter{})`, for easy parsing by logstash
-or Splunk:
-
-```json
-{"animal":"walrus","level":"info","msg":"A group of walrus emerges from the
-ocean","size":10,"time":"2014-03-10 19:57:38.562264131 -0400 EDT"}
-
-{"level":"warning","msg":"The group's number increased tremendously!",
-"number":122,"omg":true,"time":"2014-03-10 19:57:38.562471297 -0400 EDT"}
-
-{"animal":"walrus","level":"info","msg":"A giant walrus appears!",
-"size":10,"time":"2014-03-10 19:57:38.562500591 -0400 EDT"}
-
-{"animal":"walrus","level":"info","msg":"Tremendously sized cow enters the ocean.",
-"size":9,"time":"2014-03-10 19:57:38.562527896 -0400 EDT"}
-
-{"level":"fatal","msg":"The ice breaks!","number":100,"omg":true,
-"time":"2014-03-10 19:57:38.562543128 -0400 EDT"}
-```
-
-With the default `log.SetFormatter(&log.TextFormatter{})` when a TTY is not
-attached, the output is compatible with the
-[logfmt](http://godoc.org/github.com/kr/logfmt) format:
-
-```text
-time="2015-03-26T01:27:38-04:00" level=debug msg="Started observing beach" animal=walrus number=8
-time="2015-03-26T01:27:38-04:00" level=info msg="A group of walrus emerges from the ocean" animal=walrus size=10
-time="2015-03-26T01:27:38-04:00" level=warning msg="The group's number increased tremendously!" number=122 omg=true
-time="2015-03-26T01:27:38-04:00" level=debug msg="Temperature changes" temperature=-4
-time="2015-03-26T01:27:38-04:00" level=panic msg="It's over 9000!" animal=orca size=9009
-time="2015-03-26T01:27:38-04:00" level=fatal msg="The ice breaks!" err=&{0x2082280c0 map[animal:orca size:9009] 2015-03-26 01:27:38.441574009 -0400 EDT panic It's over 9000!} number=100 omg=true
-exit status 1
-```
-
-#### Example
-
-The simplest way to use Logrus is simply the package-level exported logger:
-
-```go
-package main
-
-import (
- log "github.com/Sirupsen/logrus"
-)
-
-func main() {
- log.WithFields(log.Fields{
- "animal": "walrus",
- }).Info("A walrus appears")
-}
-```
-
-Note that it's completely api-compatible with the stdlib logger, so you can
-replace your `log` imports everywhere with `log "github.com/Sirupsen/logrus"`
-and you'll now have the flexibility of Logrus. You can customize it all you
-want:
-
-```go
-package main
-
-import (
- "os"
- log "github.com/Sirupsen/logrus"
-)
-
-func init() {
- // Log as JSON instead of the default ASCII formatter.
- log.SetFormatter(&log.JSONFormatter{})
-
- // Output to stdout instead of the default stderr
- // Can be any io.Writer, see below for File example
- log.SetOutput(os.Stdout)
-
- // Only log the warning severity or above.
- log.SetLevel(log.WarnLevel)
-}
-
-func main() {
- log.WithFields(log.Fields{
- "animal": "walrus",
- "size": 10,
- }).Info("A group of walrus emerges from the ocean")
-
- log.WithFields(log.Fields{
- "omg": true,
- "number": 122,
- }).Warn("The group's number increased tremendously!")
-
- log.WithFields(log.Fields{
- "omg": true,
- "number": 100,
- }).Fatal("The ice breaks!")
-
- // A common pattern is to re-use fields between logging statements by re-using
- // the logrus.Entry returned from WithFields()
- contextLogger := log.WithFields(log.Fields{
- "common": "this is a common field",
- "other": "I also should be logged always",
- })
-
- contextLogger.Info("I'll be logged with common and other field")
- contextLogger.Info("Me too")
-}
-```
-
-For more advanced usage such as logging to multiple locations from the same
-application, you can also create an instance of the `logrus` Logger:
-
-```go
-package main
-
-import (
- "github.com/Sirupsen/logrus"
-)
-
-// Create a new instance of the logger. You can have any number of instances.
-var log = logrus.New()
-
-func main() {
- // The API for setting attributes is a little different than the package level
- // exported logger. See Godoc.
- log.Out = os.Stdout
-
- // You could set this to any `io.Writer` such as a file
- // file, err := os.OpenFile("logrus.log", os.O_CREATE|os.O_WRONLY, 0666)
- // if err == nil {
- // log.Out = file
- // } else {
- // log.Info("Failed to log to file, using default stderr")
- // }
-
- log.WithFields(logrus.Fields{
- "animal": "walrus",
- "size": 10,
- }).Info("A group of walrus emerges from the ocean")
-}
-```
-
-#### Fields
-
-Logrus encourages careful, structured logging though logging fields instead of
-long, unparseable error messages. For example, instead of: `log.Fatalf("Failed
-to send event %s to topic %s with key %d")`, you should log the much more
-discoverable:
-
-```go
-log.WithFields(log.Fields{
- "event": event,
- "topic": topic,
- "key": key,
-}).Fatal("Failed to send event")
-```
-
-We've found this API forces you to think about logging in a way that produces
-much more useful logging messages. We've been in countless situations where just
-a single added field to a log statement that was already there would've saved us
-hours. The `WithFields` call is optional.
-
-In general, with Logrus using any of the `printf`-family functions should be
-seen as a hint you should add a field, however, you can still use the
-`printf`-family functions with Logrus.
-
-#### Default Fields
-
-Often it's helpful to have fields _always_ attached to log statements in an
-application or parts of one. For example, you may want to always log the
-`request_id` and `user_ip` in the context of a request. Instead of writing
-`log.WithFields(log.Fields{"request_id": request_id, "user_ip": user_ip})` on
-every line, you can create a `logrus.Entry` to pass around instead:
-
-```go
-requestLogger := log.WithFields(log.Fields{"request_id": request_id, user_ip: user_ip})
-requestLogger.Info("something happened on that request") # will log request_id and user_ip
-requestLogger.Warn("something not great happened")
-```
-
-#### Hooks
-
-You can add hooks for logging levels. For example to send errors to an exception
-tracking service on `Error`, `Fatal` and `Panic`, info to StatsD or log to
-multiple places simultaneously, e.g. syslog.
-
-Logrus comes with [built-in hooks](hooks/). Add those, or your custom hook, in
-`init`:
-
-```go
-import (
- log "github.com/Sirupsen/logrus"
- "gopkg.in/gemnasium/logrus-airbrake-hook.v2" // the package is named "aibrake"
- logrus_syslog "github.com/Sirupsen/logrus/hooks/syslog"
- "log/syslog"
-)
-
-func init() {
-
- // Use the Airbrake hook to report errors that have Error severity or above to
- // an exception tracker. You can create custom hooks, see the Hooks section.
- log.AddHook(airbrake.NewHook(123, "xyz", "production"))
-
- hook, err := logrus_syslog.NewSyslogHook("udp", "localhost:514", syslog.LOG_INFO, "")
- if err != nil {
- log.Error("Unable to connect to local syslog daemon")
- } else {
- log.AddHook(hook)
- }
-}
-```
-Note: Syslog hook also support connecting to local syslog (Ex. "/dev/log" or "/var/run/syslog" or "/var/run/log"). For the detail, please check the [syslog hook README](hooks/syslog/README.md).
-
-| Hook | Description |
-| ----- | ----------- |
-| [Airbrake "legacy"](https://github.com/gemnasium/logrus-airbrake-legacy-hook) | Send errors to an exception tracking service compatible with the Airbrake API V2. Uses [`airbrake-go`](https://github.com/tobi/airbrake-go) behind the scenes. |
-| [Airbrake](https://github.com/gemnasium/logrus-airbrake-hook) | Send errors to the Airbrake API V3. Uses the official [`gobrake`](https://github.com/airbrake/gobrake) behind the scenes. |
-| [Amazon Kinesis](https://github.com/evalphobia/logrus_kinesis) | Hook for logging to [Amazon Kinesis](https://aws.amazon.com/kinesis/) |
-| [Amqp-Hook](https://github.com/vladoatanasov/logrus_amqp) | Hook for logging to Amqp broker (Like RabbitMQ) |
-| [Bugsnag](https://github.com/Shopify/logrus-bugsnag/blob/master/bugsnag.go) | Send errors to the Bugsnag exception tracking service. |
-| [DeferPanic](https://github.com/deferpanic/dp-logrus) | Hook for logging to DeferPanic |
-| [ElasticSearch](https://github.com/sohlich/elogrus) | Hook for logging to ElasticSearch|
-| [Fluentd](https://github.com/evalphobia/logrus_fluent) | Hook for logging to fluentd |
-| [Go-Slack](https://github.com/multiplay/go-slack) | Hook for logging to [Slack](https://slack.com) |
-| [Graylog](https://github.com/gemnasium/logrus-graylog-hook) | Hook for logging to [Graylog](http://graylog2.org/) |
-| [Hiprus](https://github.com/nubo/hiprus) | Send errors to a channel in hipchat. |
-| [Honeybadger](https://github.com/agonzalezro/logrus_honeybadger) | Hook for sending exceptions to Honeybadger |
-| [InfluxDB](https://github.com/Abramovic/logrus_influxdb) | Hook for logging to influxdb |
-| [Influxus] (http://github.com/vlad-doru/influxus) | Hook for concurrently logging to [InfluxDB] (http://influxdata.com/) |
-| [Journalhook](https://github.com/wercker/journalhook) | Hook for logging to `systemd-journald` |
-| [KafkaLogrus](https://github.com/goibibo/KafkaLogrus) | Hook for logging to kafka |
-| [LFShook](https://github.com/rifflock/lfshook) | Hook for logging to the local filesystem |
-| [Logentries](https://github.com/jcftang/logentriesrus) | Hook for logging to [Logentries](https://logentries.com/) |
-| [Logentrus](https://github.com/puddingfactory/logentrus) | Hook for logging to [Logentries](https://logentries.com/) |
-| [Logmatic.io](https://github.com/logmatic/logmatic-go) | Hook for logging to [Logmatic.io](http://logmatic.io/) |
-| [Logrusly](https://github.com/sebest/logrusly) | Send logs to [Loggly](https://www.loggly.com/) |
-| [Logstash](https://github.com/bshuster-repo/logrus-logstash-hook) | Hook for logging to [Logstash](https://www.elastic.co/products/logstash) |
-| [Mail](https://github.com/zbindenren/logrus_mail) | Hook for sending exceptions via mail |
-| [Mongodb](https://github.com/weekface/mgorus) | Hook for logging to mongodb |
-| [NATS-Hook](https://github.com/rybit/nats_logrus_hook) | Hook for logging to [NATS](https://nats.io) |
-| [Octokit](https://github.com/dorajistyle/logrus-octokit-hook) | Hook for logging to github via octokit |
-| [Papertrail](https://github.com/polds/logrus-papertrail-hook) | Send errors to the [Papertrail](https://papertrailapp.com) hosted logging service via UDP. |
-| [PostgreSQL](https://github.com/gemnasium/logrus-postgresql-hook) | Send logs to [PostgreSQL](http://postgresql.org) |
-| [Pushover](https://github.com/toorop/logrus_pushover) | Send error via [Pushover](https://pushover.net) |
-| [Raygun](https://github.com/squirkle/logrus-raygun-hook) | Hook for logging to [Raygun.io](http://raygun.io/) |
-| [Redis-Hook](https://github.com/rogierlommers/logrus-redis-hook) | Hook for logging to a ELK stack (through Redis) |
-| [Rollrus](https://github.com/heroku/rollrus) | Hook for sending errors to rollbar |
-| [Scribe](https://github.com/sagar8192/logrus-scribe-hook) | Hook for logging to [Scribe](https://github.com/facebookarchive/scribe)|
-| [Sentry](https://github.com/evalphobia/logrus_sentry) | Send errors to the Sentry error logging and aggregation service. |
-| [Slackrus](https://github.com/johntdyer/slackrus) | Hook for Slack chat. |
-| [Stackdriver](https://github.com/knq/sdhook) | Hook for logging to [Google Stackdriver](https://cloud.google.com/logging/) |
-| [Sumorus](https://github.com/doublefree/sumorus) | Hook for logging to [SumoLogic](https://www.sumologic.com/)|
-| [Syslog](https://github.com/Sirupsen/logrus/blob/master/hooks/syslog/syslog.go) | Send errors to remote syslog server. Uses standard library `log/syslog` behind the scenes. |
-| [TraceView](https://github.com/evalphobia/logrus_appneta) | Hook for logging to [AppNeta TraceView](https://www.appneta.com/products/traceview/) |
-| [Typetalk](https://github.com/dragon3/logrus-typetalk-hook) | Hook for logging to [Typetalk](https://www.typetalk.in/) |
-| [logz.io](https://github.com/ripcurld00d/logrus-logzio-hook) | Hook for logging to [logz.io](https://logz.io), a Log as a Service using Logstash |
-
-#### Level logging
-
-Logrus has six logging levels: Debug, Info, Warning, Error, Fatal and Panic.
-
-```go
-log.Debug("Useful debugging information.")
-log.Info("Something noteworthy happened!")
-log.Warn("You should probably take a look at this.")
-log.Error("Something failed but I'm not quitting.")
-// Calls os.Exit(1) after logging
-log.Fatal("Bye.")
-// Calls panic() after logging
-log.Panic("I'm bailing.")
-```
-
-You can set the logging level on a `Logger`, then it will only log entries with
-that severity or anything above it:
-
-```go
-// Will log anything that is info or above (warn, error, fatal, panic). Default.
-log.SetLevel(log.InfoLevel)
-```
-
-It may be useful to set `log.Level = logrus.DebugLevel` in a debug or verbose
-environment if your application has that.
-
-#### Entries
-
-Besides the fields added with `WithField` or `WithFields` some fields are
-automatically added to all logging events:
-
-1. `time`. The timestamp when the entry was created.
-2. `msg`. The logging message passed to `{Info,Warn,Error,Fatal,Panic}` after
- the `AddFields` call. E.g. `Failed to send event.`
-3. `level`. The logging level. E.g. `info`.
-
-#### Environments
-
-Logrus has no notion of environment.
-
-If you wish for hooks and formatters to only be used in specific environments,
-you should handle that yourself. For example, if your application has a global
-variable `Environment`, which is a string representation of the environment you
-could do:
-
-```go
-import (
- log "github.com/Sirupsen/logrus"
-)
-
-init() {
- // do something here to set environment depending on an environment variable
- // or command-line flag
- if Environment == "production" {
- log.SetFormatter(&log.JSONFormatter{})
- } else {
- // The TextFormatter is default, you don't actually have to do this.
- log.SetFormatter(&log.TextFormatter{})
- }
-}
-```
-
-This configuration is how `logrus` was intended to be used, but JSON in
-production is mostly only useful if you do log aggregation with tools like
-Splunk or Logstash.
-
-#### Formatters
-
-The built-in logging formatters are:
-
-* `logrus.TextFormatter`. Logs the event in colors if stdout is a tty, otherwise
- without colors.
- * *Note:* to force colored output when there is no TTY, set the `ForceColors`
- field to `true`. To force no colored output even if there is a TTY set the
- `DisableColors` field to `true`. For Windows, see
- [github.com/mattn/go-colorable](https://github.com/mattn/go-colorable).
- * All options are listed in the [generated docs](https://godoc.org/github.com/sirupsen/logrus#TextFormatter).
-* `logrus.JSONFormatter`. Logs fields as JSON.
- * All options are listed in the [generated docs](https://godoc.org/github.com/sirupsen/logrus#JSONFormatter).
-
-Third party logging formatters:
-
-* [`logstash`](https://github.com/bshuster-repo/logrus-logstash-hook). Logs fields as [Logstash](http://logstash.net) Events.
-* [`prefixed`](https://github.com/x-cray/logrus-prefixed-formatter). Displays log entry source along with alternative layout.
-* [`zalgo`](https://github.com/aybabtme/logzalgo). Invoking the P͉̫o̳̼̊w̖͈̰͎e̬͔̭͂r͚̼̹̲ ̫͓͉̳͈ō̠͕͖̚f̝͍̠ ͕̲̞͖͑Z̖̫̤̫ͪa͉̬͈̗l͖͎g̳̥o̰̥̅!̣͔̲̻͊̄ ̙̘̦̹̦.
-
-You can define your formatter by implementing the `Formatter` interface,
-requiring a `Format` method. `Format` takes an `*Entry`. `entry.Data` is a
-`Fields` type (`map[string]interface{}`) with all your fields as well as the
-default ones (see Entries section above):
-
-```go
-type MyJSONFormatter struct {
-}
-
-log.SetFormatter(new(MyJSONFormatter))
-
-func (f *MyJSONFormatter) Format(entry *Entry) ([]byte, error) {
- // Note this doesn't include Time, Level and Message which are available on
- // the Entry. Consult `godoc` on information about those fields or read the
- // source of the official loggers.
- serialized, err := json.Marshal(entry.Data)
- if err != nil {
- return nil, fmt.Errorf("Failed to marshal fields to JSON, %v", err)
- }
- return append(serialized, '\n'), nil
-}
-```
-
-#### Logger as an `io.Writer`
-
-Logrus can be transformed into an `io.Writer`. That writer is the end of an `io.Pipe` and it is your responsibility to close it.
-
-```go
-w := logger.Writer()
-defer w.Close()
-
-srv := http.Server{
- // create a stdlib log.Logger that writes to
- // logrus.Logger.
- ErrorLog: log.New(w, "", 0),
-}
-```
-
-Each line written to that writer will be printed the usual way, using formatters
-and hooks. The level for those entries is `info`.
-
-This means that we can override the standard library logger easily:
-
-```go
-logger := logrus.New()
-logger.Formatter = &logrus.JSONFormatter{}
-
-// Use logrus for standard log output
-// Note that `log` here references stdlib's log
-// Not logrus imported under the name `log`.
-log.SetOutput(logger.Writer())
-```
-
-#### Rotation
-
-Log rotation is not provided with Logrus. Log rotation should be done by an
-external program (like `logrotate(8)`) that can compress and delete old log
-entries. It should not be a feature of the application-level logger.
-
-#### Tools
-
-| Tool | Description |
-| ---- | ----------- |
-|[Logrus Mate](https://github.com/gogap/logrus_mate)|Logrus mate is a tool for Logrus to manage loggers, you can initial logger's level, hook and formatter by config file, the logger will generated with different config at different environment.|
-|[Logrus Viper Helper](https://github.com/heirko/go-contrib/tree/master/logrusHelper)|An Helper arround Logrus to wrap with spf13/Viper to load configuration with fangs! And to simplify Logrus configuration use some behavior of [Logrus Mate](https://github.com/gogap/logrus_mate). [sample](https://github.com/heirko/iris-contrib/blob/master/middleware/logrus-logger/example) |
-
-#### Testing
-
-Logrus has a built in facility for asserting the presence of log messages. This is implemented through the `test` hook and provides:
-
-* decorators for existing logger (`test.NewLocal` and `test.NewGlobal`) which basically just add the `test` hook
-* a test logger (`test.NewNullLogger`) that just records log messages (and does not output any):
-
-```go
-logger, hook := NewNullLogger()
-logger.Error("Hello error")
-
-assert.Equal(1, len(hook.Entries))
-assert.Equal(logrus.ErrorLevel, hook.LastEntry().Level)
-assert.Equal("Hello error", hook.LastEntry().Message)
-
-hook.Reset()
-assert.Nil(hook.LastEntry())
-```
-
-#### Fatal handlers
-
-Logrus can register one or more functions that will be called when any `fatal`
-level message is logged. The registered handlers will be executed before
-logrus performs a `os.Exit(1)`. This behavior may be helpful if callers need
-to gracefully shutdown. Unlike a `panic("Something went wrong...")` call which can be intercepted with a deferred `recover` a call to `os.Exit(1)` can not be intercepted.
-
-```
-...
-handler := func() {
- // gracefully shutdown something...
-}
-logrus.RegisterExitHandler(handler)
-...
-```
-
-#### Thread safety
-
-By default Logger is protected by mutex for concurrent writes, this mutex is invoked when calling hooks and writing logs.
-If you are sure such locking is not needed, you can call logger.SetNoLock() to disable the locking.
-
-Situation when locking is not needed includes:
-
-* You have no hooks registered, or hooks calling is already thread-safe.
-
-* Writing to logger.Out is already thread-safe, for example:
-
- 1) logger.Out is protected by locks.
-
- 2) logger.Out is a os.File handler opened with `O_APPEND` flag, and every write is smaller than 4k. (This allow multi-thread/multi-process writing)
-
- (Refer to http://www.notthewizard.com/2014/06/17/are-files-appends-really-atomic/)
diff --git a/vendor/github.com/sirupsen/logrus/alt_exit.go b/vendor/github.com/sirupsen/logrus/alt_exit.go
deleted file mode 100644
index b4c9e84..0000000
--- a/vendor/github.com/sirupsen/logrus/alt_exit.go
+++ /dev/null
@@ -1,64 +0,0 @@
-package logrus
-
-// The following code was sourced and modified from the
-// https://bitbucket.org/tebeka/atexit package governed by the following license:
-//
-// Copyright (c) 2012 Miki Tebeka .
-//
-// Permission is hereby granted, free of charge, to any person obtaining a copy of
-// this software and associated documentation files (the "Software"), to deal in
-// the Software without restriction, including without limitation the rights to
-// use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of
-// the Software, and to permit persons to whom the Software is furnished to do so,
-// subject to the following conditions:
-//
-// The above copyright notice and this permission notice shall be included in all
-// copies or substantial portions of the Software.
-//
-// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
-// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS
-// FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR
-// COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER
-// IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
-// CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
-
-import (
- "fmt"
- "os"
-)
-
-var handlers = []func(){}
-
-func runHandler(handler func()) {
- defer func() {
- if err := recover(); err != nil {
- fmt.Fprintln(os.Stderr, "Error: Logrus exit handler error:", err)
- }
- }()
-
- handler()
-}
-
-func runHandlers() {
- for _, handler := range handlers {
- runHandler(handler)
- }
-}
-
-// Exit runs all the Logrus atexit handlers and then terminates the program using os.Exit(code)
-func Exit(code int) {
- runHandlers()
- os.Exit(code)
-}
-
-// RegisterExitHandler adds a Logrus Exit handler, call logrus.Exit to invoke
-// all handlers. The handlers will also be invoked when any Fatal log entry is
-// made.
-//
-// This method is useful when a caller wishes to use logrus to log a fatal
-// message but also needs to gracefully shutdown. An example usecase could be
-// closing database connections, or sending a alert that the application is
-// closing.
-func RegisterExitHandler(handler func()) {
- handlers = append(handlers, handler)
-}
diff --git a/vendor/github.com/sirupsen/logrus/alt_exit_test.go b/vendor/github.com/sirupsen/logrus/alt_exit_test.go
deleted file mode 100644
index 022b778..0000000
--- a/vendor/github.com/sirupsen/logrus/alt_exit_test.go
+++ /dev/null
@@ -1,74 +0,0 @@
-package logrus
-
-import (
- "io/ioutil"
- "os/exec"
- "testing"
- "time"
-)
-
-func TestRegister(t *testing.T) {
- current := len(handlers)
- RegisterExitHandler(func() {})
- if len(handlers) != current+1 {
- t.Fatalf("can't add handler")
- }
-}
-
-func TestHandler(t *testing.T) {
- gofile := "/tmp/testprog.go"
- if err := ioutil.WriteFile(gofile, testprog, 0666); err != nil {
- t.Fatalf("can't create go file")
- }
-
- outfile := "/tmp/testprog.out"
- arg := time.Now().UTC().String()
- err := exec.Command("go", "run", gofile, outfile, arg).Run()
- if err == nil {
- t.Fatalf("completed normally, should have failed")
- }
-
- data, err := ioutil.ReadFile(outfile)
- if err != nil {
- t.Fatalf("can't read output file %s", outfile)
- }
-
- if string(data) != arg {
- t.Fatalf("bad data")
- }
-}
-
-var testprog = []byte(`
-// Test program for atexit, gets output file and data as arguments and writes
-// data to output file in atexit handler.
-package main
-
-import (
- "github.com/Sirupsen/logrus"
- "flag"
- "fmt"
- "io/ioutil"
-)
-
-var outfile = ""
-var data = ""
-
-func handler() {
- ioutil.WriteFile(outfile, []byte(data), 0666)
-}
-
-func badHandler() {
- n := 0
- fmt.Println(1/n)
-}
-
-func main() {
- flag.Parse()
- outfile = flag.Arg(0)
- data = flag.Arg(1)
-
- logrus.RegisterExitHandler(handler)
- logrus.RegisterExitHandler(badHandler)
- logrus.Fatal("Bye bye")
-}
-`)
diff --git a/vendor/github.com/sirupsen/logrus/doc.go b/vendor/github.com/sirupsen/logrus/doc.go
deleted file mode 100644
index dddd5f8..0000000
--- a/vendor/github.com/sirupsen/logrus/doc.go
+++ /dev/null
@@ -1,26 +0,0 @@
-/*
-Package logrus is a structured logger for Go, completely API compatible with the standard library logger.
-
-
-The simplest way to use Logrus is simply the package-level exported logger:
-
- package main
-
- import (
- log "github.com/Sirupsen/logrus"
- )
-
- func main() {
- log.WithFields(log.Fields{
- "animal": "walrus",
- "number": 1,
- "size": 10,
- }).Info("A walrus appears")
- }
-
-Output:
- time="2015-09-07T08:48:33Z" level=info msg="A walrus appears" animal=walrus number=1 size=10
-
-For a full guide visit https://github.com/Sirupsen/logrus
-*/
-package logrus
diff --git a/vendor/github.com/sirupsen/logrus/entry.go b/vendor/github.com/sirupsen/logrus/entry.go
deleted file mode 100644
index 4edbe7a..0000000
--- a/vendor/github.com/sirupsen/logrus/entry.go
+++ /dev/null
@@ -1,275 +0,0 @@
-package logrus
-
-import (
- "bytes"
- "fmt"
- "os"
- "sync"
- "time"
-)
-
-var bufferPool *sync.Pool
-
-func init() {
- bufferPool = &sync.Pool{
- New: func() interface{} {
- return new(bytes.Buffer)
- },
- }
-}
-
-// Defines the key when adding errors using WithError.
-var ErrorKey = "error"
-
-// An entry is the final or intermediate Logrus logging entry. It contains all
-// the fields passed with WithField{,s}. It's finally logged when Debug, Info,
-// Warn, Error, Fatal or Panic is called on it. These objects can be reused and
-// passed around as much as you wish to avoid field duplication.
-type Entry struct {
- Logger *Logger
-
- // Contains all the fields set by the user.
- Data Fields
-
- // Time at which the log entry was created
- Time time.Time
-
- // Level the log entry was logged at: Debug, Info, Warn, Error, Fatal or Panic
- Level Level
-
- // Message passed to Debug, Info, Warn, Error, Fatal or Panic
- Message string
-
- // When formatter is called in entry.log(), an Buffer may be set to entry
- Buffer *bytes.Buffer
-}
-
-func NewEntry(logger *Logger) *Entry {
- return &Entry{
- Logger: logger,
- // Default is three fields, give a little extra room
- Data: make(Fields, 5),
- }
-}
-
-// Returns the string representation from the reader and ultimately the
-// formatter.
-func (entry *Entry) String() (string, error) {
- serialized, err := entry.Logger.Formatter.Format(entry)
- if err != nil {
- return "", err
- }
- str := string(serialized)
- return str, nil
-}
-
-// Add an error as single field (using the key defined in ErrorKey) to the Entry.
-func (entry *Entry) WithError(err error) *Entry {
- return entry.WithField(ErrorKey, err)
-}
-
-// Add a single field to the Entry.
-func (entry *Entry) WithField(key string, value interface{}) *Entry {
- return entry.WithFields(Fields{key: value})
-}
-
-// Add a map of fields to the Entry.
-func (entry *Entry) WithFields(fields Fields) *Entry {
- data := make(Fields, len(entry.Data)+len(fields))
- for k, v := range entry.Data {
- data[k] = v
- }
- for k, v := range fields {
- data[k] = v
- }
- return &Entry{Logger: entry.Logger, Data: data}
-}
-
-// This function is not declared with a pointer value because otherwise
-// race conditions will occur when using multiple goroutines
-func (entry Entry) log(level Level, msg string) {
- var buffer *bytes.Buffer
- entry.Time = time.Now()
- entry.Level = level
- entry.Message = msg
-
- if err := entry.Logger.Hooks.Fire(level, &entry); err != nil {
- entry.Logger.mu.Lock()
- fmt.Fprintf(os.Stderr, "Failed to fire hook: %v\n", err)
- entry.Logger.mu.Unlock()
- }
- buffer = bufferPool.Get().(*bytes.Buffer)
- buffer.Reset()
- defer bufferPool.Put(buffer)
- entry.Buffer = buffer
- serialized, err := entry.Logger.Formatter.Format(&entry)
- entry.Buffer = nil
- if err != nil {
- entry.Logger.mu.Lock()
- fmt.Fprintf(os.Stderr, "Failed to obtain reader, %v\n", err)
- entry.Logger.mu.Unlock()
- } else {
- entry.Logger.mu.Lock()
- _, err = entry.Logger.Out.Write(serialized)
- if err != nil {
- fmt.Fprintf(os.Stderr, "Failed to write to log, %v\n", err)
- }
- entry.Logger.mu.Unlock()
- }
-
- // To avoid Entry#log() returning a value that only would make sense for
- // panic() to use in Entry#Panic(), we avoid the allocation by checking
- // directly here.
- if level <= PanicLevel {
- panic(&entry)
- }
-}
-
-func (entry *Entry) Debug(args ...interface{}) {
- if entry.Logger.Level >= DebugLevel {
- entry.log(DebugLevel, fmt.Sprint(args...))
- }
-}
-
-func (entry *Entry) Print(args ...interface{}) {
- entry.Info(args...)
-}
-
-func (entry *Entry) Info(args ...interface{}) {
- if entry.Logger.Level >= InfoLevel {
- entry.log(InfoLevel, fmt.Sprint(args...))
- }
-}
-
-func (entry *Entry) Warn(args ...interface{}) {
- if entry.Logger.Level >= WarnLevel {
- entry.log(WarnLevel, fmt.Sprint(args...))
- }
-}
-
-func (entry *Entry) Warning(args ...interface{}) {
- entry.Warn(args...)
-}
-
-func (entry *Entry) Error(args ...interface{}) {
- if entry.Logger.Level >= ErrorLevel {
- entry.log(ErrorLevel, fmt.Sprint(args...))
- }
-}
-
-func (entry *Entry) Fatal(args ...interface{}) {
- if entry.Logger.Level >= FatalLevel {
- entry.log(FatalLevel, fmt.Sprint(args...))
- }
- Exit(1)
-}
-
-func (entry *Entry) Panic(args ...interface{}) {
- if entry.Logger.Level >= PanicLevel {
- entry.log(PanicLevel, fmt.Sprint(args...))
- }
- panic(fmt.Sprint(args...))
-}
-
-// Entry Printf family functions
-
-func (entry *Entry) Debugf(format string, args ...interface{}) {
- if entry.Logger.Level >= DebugLevel {
- entry.Debug(fmt.Sprintf(format, args...))
- }
-}
-
-func (entry *Entry) Infof(format string, args ...interface{}) {
- if entry.Logger.Level >= InfoLevel {
- entry.Info(fmt.Sprintf(format, args...))
- }
-}
-
-func (entry *Entry) Printf(format string, args ...interface{}) {
- entry.Infof(format, args...)
-}
-
-func (entry *Entry) Warnf(format string, args ...interface{}) {
- if entry.Logger.Level >= WarnLevel {
- entry.Warn(fmt.Sprintf(format, args...))
- }
-}
-
-func (entry *Entry) Warningf(format string, args ...interface{}) {
- entry.Warnf(format, args...)
-}
-
-func (entry *Entry) Errorf(format string, args ...interface{}) {
- if entry.Logger.Level >= ErrorLevel {
- entry.Error(fmt.Sprintf(format, args...))
- }
-}
-
-func (entry *Entry) Fatalf(format string, args ...interface{}) {
- if entry.Logger.Level >= FatalLevel {
- entry.Fatal(fmt.Sprintf(format, args...))
- }
- Exit(1)
-}
-
-func (entry *Entry) Panicf(format string, args ...interface{}) {
- if entry.Logger.Level >= PanicLevel {
- entry.Panic(fmt.Sprintf(format, args...))
- }
-}
-
-// Entry Println family functions
-
-func (entry *Entry) Debugln(args ...interface{}) {
- if entry.Logger.Level >= DebugLevel {
- entry.Debug(entry.sprintlnn(args...))
- }
-}
-
-func (entry *Entry) Infoln(args ...interface{}) {
- if entry.Logger.Level >= InfoLevel {
- entry.Info(entry.sprintlnn(args...))
- }
-}
-
-func (entry *Entry) Println(args ...interface{}) {
- entry.Infoln(args...)
-}
-
-func (entry *Entry) Warnln(args ...interface{}) {
- if entry.Logger.Level >= WarnLevel {
- entry.Warn(entry.sprintlnn(args...))
- }
-}
-
-func (entry *Entry) Warningln(args ...interface{}) {
- entry.Warnln(args...)
-}
-
-func (entry *Entry) Errorln(args ...interface{}) {
- if entry.Logger.Level >= ErrorLevel {
- entry.Error(entry.sprintlnn(args...))
- }
-}
-
-func (entry *Entry) Fatalln(args ...interface{}) {
- if entry.Logger.Level >= FatalLevel {
- entry.Fatal(entry.sprintlnn(args...))
- }
- Exit(1)
-}
-
-func (entry *Entry) Panicln(args ...interface{}) {
- if entry.Logger.Level >= PanicLevel {
- entry.Panic(entry.sprintlnn(args...))
- }
-}
-
-// Sprintlnn => Sprint no newline. This is to get the behavior of how
-// fmt.Sprintln where spaces are always added between operands, regardless of
-// their type. Instead of vendoring the Sprintln implementation to spare a
-// string allocation, we do the simplest thing.
-func (entry *Entry) sprintlnn(args ...interface{}) string {
- msg := fmt.Sprintln(args...)
- return msg[:len(msg)-1]
-}
diff --git a/vendor/github.com/sirupsen/logrus/entry_test.go b/vendor/github.com/sirupsen/logrus/entry_test.go
deleted file mode 100644
index 99c3b41..0000000
--- a/vendor/github.com/sirupsen/logrus/entry_test.go
+++ /dev/null
@@ -1,77 +0,0 @@
-package logrus
-
-import (
- "bytes"
- "fmt"
- "testing"
-
- "github.com/stretchr/testify/assert"
-)
-
-func TestEntryWithError(t *testing.T) {
-
- assert := assert.New(t)
-
- defer func() {
- ErrorKey = "error"
- }()
-
- err := fmt.Errorf("kaboom at layer %d", 4711)
-
- assert.Equal(err, WithError(err).Data["error"])
-
- logger := New()
- logger.Out = &bytes.Buffer{}
- entry := NewEntry(logger)
-
- assert.Equal(err, entry.WithError(err).Data["error"])
-
- ErrorKey = "err"
-
- assert.Equal(err, entry.WithError(err).Data["err"])
-
-}
-
-func TestEntryPanicln(t *testing.T) {
- errBoom := fmt.Errorf("boom time")
-
- defer func() {
- p := recover()
- assert.NotNil(t, p)
-
- switch pVal := p.(type) {
- case *Entry:
- assert.Equal(t, "kaboom", pVal.Message)
- assert.Equal(t, errBoom, pVal.Data["err"])
- default:
- t.Fatalf("want type *Entry, got %T: %#v", pVal, pVal)
- }
- }()
-
- logger := New()
- logger.Out = &bytes.Buffer{}
- entry := NewEntry(logger)
- entry.WithField("err", errBoom).Panicln("kaboom")
-}
-
-func TestEntryPanicf(t *testing.T) {
- errBoom := fmt.Errorf("boom again")
-
- defer func() {
- p := recover()
- assert.NotNil(t, p)
-
- switch pVal := p.(type) {
- case *Entry:
- assert.Equal(t, "kaboom true", pVal.Message)
- assert.Equal(t, errBoom, pVal.Data["err"])
- default:
- t.Fatalf("want type *Entry, got %T: %#v", pVal, pVal)
- }
- }()
-
- logger := New()
- logger.Out = &bytes.Buffer{}
- entry := NewEntry(logger)
- entry.WithField("err", errBoom).Panicf("kaboom %v", true)
-}
diff --git a/vendor/github.com/sirupsen/logrus/examples/basic/basic.go b/vendor/github.com/sirupsen/logrus/examples/basic/basic.go
deleted file mode 100644
index ad703fc..0000000
--- a/vendor/github.com/sirupsen/logrus/examples/basic/basic.go
+++ /dev/null
@@ -1,59 +0,0 @@
-package main
-
-import (
- "github.com/Sirupsen/logrus"
- // "os"
-)
-
-var log = logrus.New()
-
-func init() {
- log.Formatter = new(logrus.JSONFormatter)
- log.Formatter = new(logrus.TextFormatter) // default
-
- // file, err := os.OpenFile("logrus.log", os.O_CREATE|os.O_WRONLY, 0666)
- // if err == nil {
- // log.Out = file
- // } else {
- // log.Info("Failed to log to file, using default stderr")
- // }
-
- log.Level = logrus.DebugLevel
-}
-
-func main() {
- defer func() {
- err := recover()
- if err != nil {
- log.WithFields(logrus.Fields{
- "omg": true,
- "err": err,
- "number": 100,
- }).Fatal("The ice breaks!")
- }
- }()
-
- log.WithFields(logrus.Fields{
- "animal": "walrus",
- "number": 8,
- }).Debug("Started observing beach")
-
- log.WithFields(logrus.Fields{
- "animal": "walrus",
- "size": 10,
- }).Info("A group of walrus emerges from the ocean")
-
- log.WithFields(logrus.Fields{
- "omg": true,
- "number": 122,
- }).Warn("The group's number increased tremendously!")
-
- log.WithFields(logrus.Fields{
- "temperature": -4,
- }).Debug("Temperature changes")
-
- log.WithFields(logrus.Fields{
- "animal": "orca",
- "size": 9009,
- }).Panic("It's over 9000!")
-}
diff --git a/vendor/github.com/sirupsen/logrus/examples/hook/hook.go b/vendor/github.com/sirupsen/logrus/examples/hook/hook.go
deleted file mode 100644
index 3187f6d..0000000
--- a/vendor/github.com/sirupsen/logrus/examples/hook/hook.go
+++ /dev/null
@@ -1,30 +0,0 @@
-package main
-
-import (
- "github.com/Sirupsen/logrus"
- "gopkg.in/gemnasium/logrus-airbrake-hook.v2"
-)
-
-var log = logrus.New()
-
-func init() {
- log.Formatter = new(logrus.TextFormatter) // default
- log.Hooks.Add(airbrake.NewHook(123, "xyz", "development"))
-}
-
-func main() {
- log.WithFields(logrus.Fields{
- "animal": "walrus",
- "size": 10,
- }).Info("A group of walrus emerges from the ocean")
-
- log.WithFields(logrus.Fields{
- "omg": true,
- "number": 122,
- }).Warn("The group's number increased tremendously!")
-
- log.WithFields(logrus.Fields{
- "omg": true,
- "number": 100,
- }).Fatal("The ice breaks!")
-}
diff --git a/vendor/github.com/sirupsen/logrus/exported.go b/vendor/github.com/sirupsen/logrus/exported.go
deleted file mode 100644
index 9a0120a..0000000
--- a/vendor/github.com/sirupsen/logrus/exported.go
+++ /dev/null
@@ -1,193 +0,0 @@
-package logrus
-
-import (
- "io"
-)
-
-var (
- // std is the name of the standard logger in stdlib `log`
- std = New()
-)
-
-func StandardLogger() *Logger {
- return std
-}
-
-// SetOutput sets the standard logger output.
-func SetOutput(out io.Writer) {
- std.mu.Lock()
- defer std.mu.Unlock()
- std.Out = out
-}
-
-// SetFormatter sets the standard logger formatter.
-func SetFormatter(formatter Formatter) {
- std.mu.Lock()
- defer std.mu.Unlock()
- std.Formatter = formatter
-}
-
-// SetLevel sets the standard logger level.
-func SetLevel(level Level) {
- std.mu.Lock()
- defer std.mu.Unlock()
- std.Level = level
-}
-
-// GetLevel returns the standard logger level.
-func GetLevel() Level {
- std.mu.Lock()
- defer std.mu.Unlock()
- return std.Level
-}
-
-// AddHook adds a hook to the standard logger hooks.
-func AddHook(hook Hook) {
- std.mu.Lock()
- defer std.mu.Unlock()
- std.Hooks.Add(hook)
-}
-
-// WithError creates an entry from the standard logger and adds an error to it, using the value defined in ErrorKey as key.
-func WithError(err error) *Entry {
- return std.WithField(ErrorKey, err)
-}
-
-// WithField creates an entry from the standard logger and adds a field to
-// it. If you want multiple fields, use `WithFields`.
-//
-// Note that it doesn't log until you call Debug, Print, Info, Warn, Fatal
-// or Panic on the Entry it returns.
-func WithField(key string, value interface{}) *Entry {
- return std.WithField(key, value)
-}
-
-// WithFields creates an entry from the standard logger and adds multiple
-// fields to it. This is simply a helper for `WithField`, invoking it
-// once for each field.
-//
-// Note that it doesn't log until you call Debug, Print, Info, Warn, Fatal
-// or Panic on the Entry it returns.
-func WithFields(fields Fields) *Entry {
- return std.WithFields(fields)
-}
-
-// Debug logs a message at level Debug on the standard logger.
-func Debug(args ...interface{}) {
- std.Debug(args...)
-}
-
-// Print logs a message at level Info on the standard logger.
-func Print(args ...interface{}) {
- std.Print(args...)
-}
-
-// Info logs a message at level Info on the standard logger.
-func Info(args ...interface{}) {
- std.Info(args...)
-}
-
-// Warn logs a message at level Warn on the standard logger.
-func Warn(args ...interface{}) {
- std.Warn(args...)
-}
-
-// Warning logs a message at level Warn on the standard logger.
-func Warning(args ...interface{}) {
- std.Warning(args...)
-}
-
-// Error logs a message at level Error on the standard logger.
-func Error(args ...interface{}) {
- std.Error(args...)
-}
-
-// Panic logs a message at level Panic on the standard logger.
-func Panic(args ...interface{}) {
- std.Panic(args...)
-}
-
-// Fatal logs a message at level Fatal on the standard logger.
-func Fatal(args ...interface{}) {
- std.Fatal(args...)
-}
-
-// Debugf logs a message at level Debug on the standard logger.
-func Debugf(format string, args ...interface{}) {
- std.Debugf(format, args...)
-}
-
-// Printf logs a message at level Info on the standard logger.
-func Printf(format string, args ...interface{}) {
- std.Printf(format, args...)
-}
-
-// Infof logs a message at level Info on the standard logger.
-func Infof(format string, args ...interface{}) {
- std.Infof(format, args...)
-}
-
-// Warnf logs a message at level Warn on the standard logger.
-func Warnf(format string, args ...interface{}) {
- std.Warnf(format, args...)
-}
-
-// Warningf logs a message at level Warn on the standard logger.
-func Warningf(format string, args ...interface{}) {
- std.Warningf(format, args...)
-}
-
-// Errorf logs a message at level Error on the standard logger.
-func Errorf(format string, args ...interface{}) {
- std.Errorf(format, args...)
-}
-
-// Panicf logs a message at level Panic on the standard logger.
-func Panicf(format string, args ...interface{}) {
- std.Panicf(format, args...)
-}
-
-// Fatalf logs a message at level Fatal on the standard logger.
-func Fatalf(format string, args ...interface{}) {
- std.Fatalf(format, args...)
-}
-
-// Debugln logs a message at level Debug on the standard logger.
-func Debugln(args ...interface{}) {
- std.Debugln(args...)
-}
-
-// Println logs a message at level Info on the standard logger.
-func Println(args ...interface{}) {
- std.Println(args...)
-}
-
-// Infoln logs a message at level Info on the standard logger.
-func Infoln(args ...interface{}) {
- std.Infoln(args...)
-}
-
-// Warnln logs a message at level Warn on the standard logger.
-func Warnln(args ...interface{}) {
- std.Warnln(args...)
-}
-
-// Warningln logs a message at level Warn on the standard logger.
-func Warningln(args ...interface{}) {
- std.Warningln(args...)
-}
-
-// Errorln logs a message at level Error on the standard logger.
-func Errorln(args ...interface{}) {
- std.Errorln(args...)
-}
-
-// Panicln logs a message at level Panic on the standard logger.
-func Panicln(args ...interface{}) {
- std.Panicln(args...)
-}
-
-// Fatalln logs a message at level Fatal on the standard logger.
-func Fatalln(args ...interface{}) {
- std.Fatalln(args...)
-}
diff --git a/vendor/github.com/sirupsen/logrus/formatter.go b/vendor/github.com/sirupsen/logrus/formatter.go
deleted file mode 100644
index b5fbe93..0000000
--- a/vendor/github.com/sirupsen/logrus/formatter.go
+++ /dev/null
@@ -1,45 +0,0 @@
-package logrus
-
-import "time"
-
-const DefaultTimestampFormat = time.RFC3339
-
-// The Formatter interface is used to implement a custom Formatter. It takes an
-// `Entry`. It exposes all the fields, including the default ones:
-//
-// * `entry.Data["msg"]`. The message passed from Info, Warn, Error ..
-// * `entry.Data["time"]`. The timestamp.
-// * `entry.Data["level"]. The level the entry was logged at.
-//
-// Any additional fields added with `WithField` or `WithFields` are also in
-// `entry.Data`. Format is expected to return an array of bytes which are then
-// logged to `logger.Out`.
-type Formatter interface {
- Format(*Entry) ([]byte, error)
-}
-
-// This is to not silently overwrite `time`, `msg` and `level` fields when
-// dumping it. If this code wasn't there doing:
-//
-// logrus.WithField("level", 1).Info("hello")
-//
-// Would just silently drop the user provided level. Instead with this code
-// it'll logged as:
-//
-// {"level": "info", "fields.level": 1, "msg": "hello", "time": "..."}
-//
-// It's not exported because it's still using Data in an opinionated way. It's to
-// avoid code duplication between the two default formatters.
-func prefixFieldClashes(data Fields) {
- if t, ok := data["time"]; ok {
- data["fields.time"] = t
- }
-
- if m, ok := data["msg"]; ok {
- data["fields.msg"] = m
- }
-
- if l, ok := data["level"]; ok {
- data["fields.level"] = l
- }
-}
diff --git a/vendor/github.com/sirupsen/logrus/formatter_bench_test.go b/vendor/github.com/sirupsen/logrus/formatter_bench_test.go
deleted file mode 100644
index d948158..0000000
--- a/vendor/github.com/sirupsen/logrus/formatter_bench_test.go
+++ /dev/null
@@ -1,101 +0,0 @@
-package logrus
-
-import (
- "fmt"
- "testing"
- "time"
-)
-
-// smallFields is a small size data set for benchmarking
-var smallFields = Fields{
- "foo": "bar",
- "baz": "qux",
- "one": "two",
- "three": "four",
-}
-
-// largeFields is a large size data set for benchmarking
-var largeFields = Fields{
- "foo": "bar",
- "baz": "qux",
- "one": "two",
- "three": "four",
- "five": "six",
- "seven": "eight",
- "nine": "ten",
- "eleven": "twelve",
- "thirteen": "fourteen",
- "fifteen": "sixteen",
- "seventeen": "eighteen",
- "nineteen": "twenty",
- "a": "b",
- "c": "d",
- "e": "f",
- "g": "h",
- "i": "j",
- "k": "l",
- "m": "n",
- "o": "p",
- "q": "r",
- "s": "t",
- "u": "v",
- "w": "x",
- "y": "z",
- "this": "will",
- "make": "thirty",
- "entries": "yeah",
-}
-
-var errorFields = Fields{
- "foo": fmt.Errorf("bar"),
- "baz": fmt.Errorf("qux"),
-}
-
-func BenchmarkErrorTextFormatter(b *testing.B) {
- doBenchmark(b, &TextFormatter{DisableColors: true}, errorFields)
-}
-
-func BenchmarkSmallTextFormatter(b *testing.B) {
- doBenchmark(b, &TextFormatter{DisableColors: true}, smallFields)
-}
-
-func BenchmarkLargeTextFormatter(b *testing.B) {
- doBenchmark(b, &TextFormatter{DisableColors: true}, largeFields)
-}
-
-func BenchmarkSmallColoredTextFormatter(b *testing.B) {
- doBenchmark(b, &TextFormatter{ForceColors: true}, smallFields)
-}
-
-func BenchmarkLargeColoredTextFormatter(b *testing.B) {
- doBenchmark(b, &TextFormatter{ForceColors: true}, largeFields)
-}
-
-func BenchmarkSmallJSONFormatter(b *testing.B) {
- doBenchmark(b, &JSONFormatter{}, smallFields)
-}
-
-func BenchmarkLargeJSONFormatter(b *testing.B) {
- doBenchmark(b, &JSONFormatter{}, largeFields)
-}
-
-func doBenchmark(b *testing.B, formatter Formatter, fields Fields) {
- logger := New()
-
- entry := &Entry{
- Time: time.Time{},
- Level: InfoLevel,
- Message: "message",
- Data: fields,
- Logger: logger,
- }
- var d []byte
- var err error
- for i := 0; i < b.N; i++ {
- d, err = formatter.Format(entry)
- if err != nil {
- b.Fatal(err)
- }
- b.SetBytes(int64(len(d)))
- }
-}
diff --git a/vendor/github.com/sirupsen/logrus/hook_test.go b/vendor/github.com/sirupsen/logrus/hook_test.go
deleted file mode 100644
index 13f34cb..0000000
--- a/vendor/github.com/sirupsen/logrus/hook_test.go
+++ /dev/null
@@ -1,122 +0,0 @@
-package logrus
-
-import (
- "testing"
-
- "github.com/stretchr/testify/assert"
-)
-
-type TestHook struct {
- Fired bool
-}
-
-func (hook *TestHook) Fire(entry *Entry) error {
- hook.Fired = true
- return nil
-}
-
-func (hook *TestHook) Levels() []Level {
- return []Level{
- DebugLevel,
- InfoLevel,
- WarnLevel,
- ErrorLevel,
- FatalLevel,
- PanicLevel,
- }
-}
-
-func TestHookFires(t *testing.T) {
- hook := new(TestHook)
-
- LogAndAssertJSON(t, func(log *Logger) {
- log.Hooks.Add(hook)
- assert.Equal(t, hook.Fired, false)
-
- log.Print("test")
- }, func(fields Fields) {
- assert.Equal(t, hook.Fired, true)
- })
-}
-
-type ModifyHook struct {
-}
-
-func (hook *ModifyHook) Fire(entry *Entry) error {
- entry.Data["wow"] = "whale"
- return nil
-}
-
-func (hook *ModifyHook) Levels() []Level {
- return []Level{
- DebugLevel,
- InfoLevel,
- WarnLevel,
- ErrorLevel,
- FatalLevel,
- PanicLevel,
- }
-}
-
-func TestHookCanModifyEntry(t *testing.T) {
- hook := new(ModifyHook)
-
- LogAndAssertJSON(t, func(log *Logger) {
- log.Hooks.Add(hook)
- log.WithField("wow", "elephant").Print("test")
- }, func(fields Fields) {
- assert.Equal(t, fields["wow"], "whale")
- })
-}
-
-func TestCanFireMultipleHooks(t *testing.T) {
- hook1 := new(ModifyHook)
- hook2 := new(TestHook)
-
- LogAndAssertJSON(t, func(log *Logger) {
- log.Hooks.Add(hook1)
- log.Hooks.Add(hook2)
-
- log.WithField("wow", "elephant").Print("test")
- }, func(fields Fields) {
- assert.Equal(t, fields["wow"], "whale")
- assert.Equal(t, hook2.Fired, true)
- })
-}
-
-type ErrorHook struct {
- Fired bool
-}
-
-func (hook *ErrorHook) Fire(entry *Entry) error {
- hook.Fired = true
- return nil
-}
-
-func (hook *ErrorHook) Levels() []Level {
- return []Level{
- ErrorLevel,
- }
-}
-
-func TestErrorHookShouldntFireOnInfo(t *testing.T) {
- hook := new(ErrorHook)
-
- LogAndAssertJSON(t, func(log *Logger) {
- log.Hooks.Add(hook)
- log.Info("test")
- }, func(fields Fields) {
- assert.Equal(t, hook.Fired, false)
- })
-}
-
-func TestErrorHookShouldFireOnError(t *testing.T) {
- hook := new(ErrorHook)
-
- LogAndAssertJSON(t, func(log *Logger) {
- log.Hooks.Add(hook)
- log.Error("test")
- }, func(fields Fields) {
- assert.Equal(t, hook.Fired, true)
- })
-}
diff --git a/vendor/github.com/sirupsen/logrus/hooks.go b/vendor/github.com/sirupsen/logrus/hooks.go
deleted file mode 100644
index 3f151cd..0000000
--- a/vendor/github.com/sirupsen/logrus/hooks.go
+++ /dev/null
@@ -1,34 +0,0 @@
-package logrus
-
-// A hook to be fired when logging on the logging levels returned from
-// `Levels()` on your implementation of the interface. Note that this is not
-// fired in a goroutine or a channel with workers, you should handle such
-// functionality yourself if your call is non-blocking and you don't wish for
-// the logging calls for levels returned from `Levels()` to block.
-type Hook interface {
- Levels() []Level
- Fire(*Entry) error
-}
-
-// Internal type for storing the hooks on a logger instance.
-type LevelHooks map[Level][]Hook
-
-// Add a hook to an instance of logger. This is called with
-// `log.Hooks.Add(new(MyHook))` where `MyHook` implements the `Hook` interface.
-func (hooks LevelHooks) Add(hook Hook) {
- for _, level := range hook.Levels() {
- hooks[level] = append(hooks[level], hook)
- }
-}
-
-// Fire all the hooks for the passed level. Used by `entry.log` to fire
-// appropriate hooks for a log entry.
-func (hooks LevelHooks) Fire(level Level, entry *Entry) error {
- for _, hook := range hooks[level] {
- if err := hook.Fire(entry); err != nil {
- return err
- }
- }
-
- return nil
-}
diff --git a/vendor/github.com/sirupsen/logrus/hooks/syslog/README.md b/vendor/github.com/sirupsen/logrus/hooks/syslog/README.md
deleted file mode 100644
index 066704b..0000000
--- a/vendor/github.com/sirupsen/logrus/hooks/syslog/README.md
+++ /dev/null
@@ -1,39 +0,0 @@
-# Syslog Hooks for Logrus
-
-## Usage
-
-```go
-import (
- "log/syslog"
- "github.com/Sirupsen/logrus"
- logrus_syslog "github.com/Sirupsen/logrus/hooks/syslog"
-)
-
-func main() {
- log := logrus.New()
- hook, err := logrus_syslog.NewSyslogHook("udp", "localhost:514", syslog.LOG_INFO, "")
-
- if err == nil {
- log.Hooks.Add(hook)
- }
-}
-```
-
-If you want to connect to local syslog (Ex. "/dev/log" or "/var/run/syslog" or "/var/run/log"). Just assign empty string to the first two parameters of `NewSyslogHook`. It should look like the following.
-
-```go
-import (
- "log/syslog"
- "github.com/Sirupsen/logrus"
- logrus_syslog "github.com/Sirupsen/logrus/hooks/syslog"
-)
-
-func main() {
- log := logrus.New()
- hook, err := logrus_syslog.NewSyslogHook("", "", syslog.LOG_INFO, "")
-
- if err == nil {
- log.Hooks.Add(hook)
- }
-}
-```
\ No newline at end of file
diff --git a/vendor/github.com/sirupsen/logrus/hooks/syslog/syslog.go b/vendor/github.com/sirupsen/logrus/hooks/syslog/syslog.go
deleted file mode 100644
index a36e200..0000000
--- a/vendor/github.com/sirupsen/logrus/hooks/syslog/syslog.go
+++ /dev/null
@@ -1,54 +0,0 @@
-// +build !windows,!nacl,!plan9
-
-package logrus_syslog
-
-import (
- "fmt"
- "github.com/Sirupsen/logrus"
- "log/syslog"
- "os"
-)
-
-// SyslogHook to send logs via syslog.
-type SyslogHook struct {
- Writer *syslog.Writer
- SyslogNetwork string
- SyslogRaddr string
-}
-
-// Creates a hook to be added to an instance of logger. This is called with
-// `hook, err := NewSyslogHook("udp", "localhost:514", syslog.LOG_DEBUG, "")`
-// `if err == nil { log.Hooks.Add(hook) }`
-func NewSyslogHook(network, raddr string, priority syslog.Priority, tag string) (*SyslogHook, error) {
- w, err := syslog.Dial(network, raddr, priority, tag)
- return &SyslogHook{w, network, raddr}, err
-}
-
-func (hook *SyslogHook) Fire(entry *logrus.Entry) error {
- line, err := entry.String()
- if err != nil {
- fmt.Fprintf(os.Stderr, "Unable to read entry, %v", err)
- return err
- }
-
- switch entry.Level {
- case logrus.PanicLevel:
- return hook.Writer.Crit(line)
- case logrus.FatalLevel:
- return hook.Writer.Crit(line)
- case logrus.ErrorLevel:
- return hook.Writer.Err(line)
- case logrus.WarnLevel:
- return hook.Writer.Warning(line)
- case logrus.InfoLevel:
- return hook.Writer.Info(line)
- case logrus.DebugLevel:
- return hook.Writer.Debug(line)
- default:
- return nil
- }
-}
-
-func (hook *SyslogHook) Levels() []logrus.Level {
- return logrus.AllLevels
-}
diff --git a/vendor/github.com/sirupsen/logrus/hooks/syslog/syslog_test.go b/vendor/github.com/sirupsen/logrus/hooks/syslog/syslog_test.go
deleted file mode 100644
index 42762dc..0000000
--- a/vendor/github.com/sirupsen/logrus/hooks/syslog/syslog_test.go
+++ /dev/null
@@ -1,26 +0,0 @@
-package logrus_syslog
-
-import (
- "github.com/Sirupsen/logrus"
- "log/syslog"
- "testing"
-)
-
-func TestLocalhostAddAndPrint(t *testing.T) {
- log := logrus.New()
- hook, err := NewSyslogHook("udp", "localhost:514", syslog.LOG_INFO, "")
-
- if err != nil {
- t.Errorf("Unable to connect to local syslog.")
- }
-
- log.Hooks.Add(hook)
-
- for _, level := range hook.Levels() {
- if len(log.Hooks[level]) != 1 {
- t.Errorf("SyslogHook was not added. The length of log.Hooks[%v]: %v", level, len(log.Hooks[level]))
- }
- }
-
- log.Info("Congratulations!")
-}
diff --git a/vendor/github.com/sirupsen/logrus/hooks/test/test.go b/vendor/github.com/sirupsen/logrus/hooks/test/test.go
deleted file mode 100644
index 0688125..0000000
--- a/vendor/github.com/sirupsen/logrus/hooks/test/test.go
+++ /dev/null
@@ -1,67 +0,0 @@
-package test
-
-import (
- "io/ioutil"
-
- "github.com/Sirupsen/logrus"
-)
-
-// test.Hook is a hook designed for dealing with logs in test scenarios.
-type Hook struct {
- Entries []*logrus.Entry
-}
-
-// Installs a test hook for the global logger.
-func NewGlobal() *Hook {
-
- hook := new(Hook)
- logrus.AddHook(hook)
-
- return hook
-
-}
-
-// Installs a test hook for a given local logger.
-func NewLocal(logger *logrus.Logger) *Hook {
-
- hook := new(Hook)
- logger.Hooks.Add(hook)
-
- return hook
-
-}
-
-// Creates a discarding logger and installs the test hook.
-func NewNullLogger() (*logrus.Logger, *Hook) {
-
- logger := logrus.New()
- logger.Out = ioutil.Discard
-
- return logger, NewLocal(logger)
-
-}
-
-func (t *Hook) Fire(e *logrus.Entry) error {
- t.Entries = append(t.Entries, e)
- return nil
-}
-
-func (t *Hook) Levels() []logrus.Level {
- return logrus.AllLevels
-}
-
-// LastEntry returns the last entry that was logged or nil.
-func (t *Hook) LastEntry() (l *logrus.Entry) {
-
- if i := len(t.Entries) - 1; i < 0 {
- return nil
- } else {
- return t.Entries[i]
- }
-
-}
-
-// Reset removes all Entries from this test hook.
-func (t *Hook) Reset() {
- t.Entries = make([]*logrus.Entry, 0)
-}
diff --git a/vendor/github.com/sirupsen/logrus/hooks/test/test_test.go b/vendor/github.com/sirupsen/logrus/hooks/test/test_test.go
deleted file mode 100644
index d69455b..0000000
--- a/vendor/github.com/sirupsen/logrus/hooks/test/test_test.go
+++ /dev/null
@@ -1,39 +0,0 @@
-package test
-
-import (
- "testing"
-
- "github.com/Sirupsen/logrus"
- "github.com/stretchr/testify/assert"
-)
-
-func TestAllHooks(t *testing.T) {
-
- assert := assert.New(t)
-
- logger, hook := NewNullLogger()
- assert.Nil(hook.LastEntry())
- assert.Equal(0, len(hook.Entries))
-
- logger.Error("Hello error")
- assert.Equal(logrus.ErrorLevel, hook.LastEntry().Level)
- assert.Equal("Hello error", hook.LastEntry().Message)
- assert.Equal(1, len(hook.Entries))
-
- logger.Warn("Hello warning")
- assert.Equal(logrus.WarnLevel, hook.LastEntry().Level)
- assert.Equal("Hello warning", hook.LastEntry().Message)
- assert.Equal(2, len(hook.Entries))
-
- hook.Reset()
- assert.Nil(hook.LastEntry())
- assert.Equal(0, len(hook.Entries))
-
- hook = NewGlobal()
-
- logrus.Error("Hello error")
- assert.Equal(logrus.ErrorLevel, hook.LastEntry().Level)
- assert.Equal("Hello error", hook.LastEntry().Message)
- assert.Equal(1, len(hook.Entries))
-
-}
diff --git a/vendor/github.com/sirupsen/logrus/json_formatter.go b/vendor/github.com/sirupsen/logrus/json_formatter.go
deleted file mode 100644
index 266554e..0000000
--- a/vendor/github.com/sirupsen/logrus/json_formatter.go
+++ /dev/null
@@ -1,74 +0,0 @@
-package logrus
-
-import (
- "encoding/json"
- "fmt"
-)
-
-type fieldKey string
-type FieldMap map[fieldKey]string
-
-const (
- FieldKeyMsg = "msg"
- FieldKeyLevel = "level"
- FieldKeyTime = "time"
-)
-
-func (f FieldMap) resolve(key fieldKey) string {
- if k, ok := f[key]; ok {
- return k
- }
-
- return string(key)
-}
-
-type JSONFormatter struct {
- // TimestampFormat sets the format used for marshaling timestamps.
- TimestampFormat string
-
- // DisableTimestamp allows disabling automatic timestamps in output
- DisableTimestamp bool
-
- // FieldMap allows users to customize the names of keys for various fields.
- // As an example:
- // formatter := &JSONFormatter{
- // FieldMap: FieldMap{
- // FieldKeyTime: "@timestamp",
- // FieldKeyLevel: "@level",
- // FieldKeyLevel: "@message",
- // },
- // }
- FieldMap FieldMap
-}
-
-func (f *JSONFormatter) Format(entry *Entry) ([]byte, error) {
- data := make(Fields, len(entry.Data)+3)
- for k, v := range entry.Data {
- switch v := v.(type) {
- case error:
- // Otherwise errors are ignored by `encoding/json`
- // https://github.com/Sirupsen/logrus/issues/137
- data[k] = v.Error()
- default:
- data[k] = v
- }
- }
- prefixFieldClashes(data)
-
- timestampFormat := f.TimestampFormat
- if timestampFormat == "" {
- timestampFormat = DefaultTimestampFormat
- }
-
- if !f.DisableTimestamp {
- data[f.FieldMap.resolve(FieldKeyTime)] = entry.Time.Format(timestampFormat)
- }
- data[f.FieldMap.resolve(FieldKeyMsg)] = entry.Message
- data[f.FieldMap.resolve(FieldKeyLevel)] = entry.Level.String()
-
- serialized, err := json.Marshal(data)
- if err != nil {
- return nil, fmt.Errorf("Failed to marshal fields to JSON, %v", err)
- }
- return append(serialized, '\n'), nil
-}
diff --git a/vendor/github.com/sirupsen/logrus/json_formatter_test.go b/vendor/github.com/sirupsen/logrus/json_formatter_test.go
deleted file mode 100644
index 51093a7..0000000
--- a/vendor/github.com/sirupsen/logrus/json_formatter_test.go
+++ /dev/null
@@ -1,199 +0,0 @@
-package logrus
-
-import (
- "encoding/json"
- "errors"
- "strings"
- "testing"
-)
-
-func TestErrorNotLost(t *testing.T) {
- formatter := &JSONFormatter{}
-
- b, err := formatter.Format(WithField("error", errors.New("wild walrus")))
- if err != nil {
- t.Fatal("Unable to format entry: ", err)
- }
-
- entry := make(map[string]interface{})
- err = json.Unmarshal(b, &entry)
- if err != nil {
- t.Fatal("Unable to unmarshal formatted entry: ", err)
- }
-
- if entry["error"] != "wild walrus" {
- t.Fatal("Error field not set")
- }
-}
-
-func TestErrorNotLostOnFieldNotNamedError(t *testing.T) {
- formatter := &JSONFormatter{}
-
- b, err := formatter.Format(WithField("omg", errors.New("wild walrus")))
- if err != nil {
- t.Fatal("Unable to format entry: ", err)
- }
-
- entry := make(map[string]interface{})
- err = json.Unmarshal(b, &entry)
- if err != nil {
- t.Fatal("Unable to unmarshal formatted entry: ", err)
- }
-
- if entry["omg"] != "wild walrus" {
- t.Fatal("Error field not set")
- }
-}
-
-func TestFieldClashWithTime(t *testing.T) {
- formatter := &JSONFormatter{}
-
- b, err := formatter.Format(WithField("time", "right now!"))
- if err != nil {
- t.Fatal("Unable to format entry: ", err)
- }
-
- entry := make(map[string]interface{})
- err = json.Unmarshal(b, &entry)
- if err != nil {
- t.Fatal("Unable to unmarshal formatted entry: ", err)
- }
-
- if entry["fields.time"] != "right now!" {
- t.Fatal("fields.time not set to original time field")
- }
-
- if entry["time"] != "0001-01-01T00:00:00Z" {
- t.Fatal("time field not set to current time, was: ", entry["time"])
- }
-}
-
-func TestFieldClashWithMsg(t *testing.T) {
- formatter := &JSONFormatter{}
-
- b, err := formatter.Format(WithField("msg", "something"))
- if err != nil {
- t.Fatal("Unable to format entry: ", err)
- }
-
- entry := make(map[string]interface{})
- err = json.Unmarshal(b, &entry)
- if err != nil {
- t.Fatal("Unable to unmarshal formatted entry: ", err)
- }
-
- if entry["fields.msg"] != "something" {
- t.Fatal("fields.msg not set to original msg field")
- }
-}
-
-func TestFieldClashWithLevel(t *testing.T) {
- formatter := &JSONFormatter{}
-
- b, err := formatter.Format(WithField("level", "something"))
- if err != nil {
- t.Fatal("Unable to format entry: ", err)
- }
-
- entry := make(map[string]interface{})
- err = json.Unmarshal(b, &entry)
- if err != nil {
- t.Fatal("Unable to unmarshal formatted entry: ", err)
- }
-
- if entry["fields.level"] != "something" {
- t.Fatal("fields.level not set to original level field")
- }
-}
-
-func TestJSONEntryEndsWithNewline(t *testing.T) {
- formatter := &JSONFormatter{}
-
- b, err := formatter.Format(WithField("level", "something"))
- if err != nil {
- t.Fatal("Unable to format entry: ", err)
- }
-
- if b[len(b)-1] != '\n' {
- t.Fatal("Expected JSON log entry to end with a newline")
- }
-}
-
-func TestJSONMessageKey(t *testing.T) {
- formatter := &JSONFormatter{
- FieldMap: FieldMap{
- FieldKeyMsg: "message",
- },
- }
-
- b, err := formatter.Format(&Entry{Message: "oh hai"})
- if err != nil {
- t.Fatal("Unable to format entry: ", err)
- }
- s := string(b)
- if !(strings.Contains(s, "message") && strings.Contains(s, "oh hai")) {
- t.Fatal("Expected JSON to format message key")
- }
-}
-
-func TestJSONLevelKey(t *testing.T) {
- formatter := &JSONFormatter{
- FieldMap: FieldMap{
- FieldKeyLevel: "somelevel",
- },
- }
-
- b, err := formatter.Format(WithField("level", "something"))
- if err != nil {
- t.Fatal("Unable to format entry: ", err)
- }
- s := string(b)
- if !strings.Contains(s, "somelevel") {
- t.Fatal("Expected JSON to format level key")
- }
-}
-
-func TestJSONTimeKey(t *testing.T) {
- formatter := &JSONFormatter{
- FieldMap: FieldMap{
- FieldKeyTime: "timeywimey",
- },
- }
-
- b, err := formatter.Format(WithField("level", "something"))
- if err != nil {
- t.Fatal("Unable to format entry: ", err)
- }
- s := string(b)
- if !strings.Contains(s, "timeywimey") {
- t.Fatal("Expected JSON to format time key")
- }
-}
-
-func TestJSONDisableTimestamp(t *testing.T) {
- formatter := &JSONFormatter{
- DisableTimestamp: true,
- }
-
- b, err := formatter.Format(WithField("level", "something"))
- if err != nil {
- t.Fatal("Unable to format entry: ", err)
- }
- s := string(b)
- if strings.Contains(s, FieldKeyTime) {
- t.Error("Did not prevent timestamp", s)
- }
-}
-
-func TestJSONEnableTimestamp(t *testing.T) {
- formatter := &JSONFormatter{}
-
- b, err := formatter.Format(WithField("level", "something"))
- if err != nil {
- t.Fatal("Unable to format entry: ", err)
- }
- s := string(b)
- if !strings.Contains(s, FieldKeyTime) {
- t.Error("Timestamp not present", s)
- }
-}
diff --git a/vendor/github.com/sirupsen/logrus/logger.go b/vendor/github.com/sirupsen/logrus/logger.go
deleted file mode 100644
index b769f3d..0000000
--- a/vendor/github.com/sirupsen/logrus/logger.go
+++ /dev/null
@@ -1,308 +0,0 @@
-package logrus
-
-import (
- "io"
- "os"
- "sync"
-)
-
-type Logger struct {
- // The logs are `io.Copy`'d to this in a mutex. It's common to set this to a
- // file, or leave it default which is `os.Stderr`. You can also set this to
- // something more adventorous, such as logging to Kafka.
- Out io.Writer
- // Hooks for the logger instance. These allow firing events based on logging
- // levels and log entries. For example, to send errors to an error tracking
- // service, log to StatsD or dump the core on fatal errors.
- Hooks LevelHooks
- // All log entries pass through the formatter before logged to Out. The
- // included formatters are `TextFormatter` and `JSONFormatter` for which
- // TextFormatter is the default. In development (when a TTY is attached) it
- // logs with colors, but to a file it wouldn't. You can easily implement your
- // own that implements the `Formatter` interface, see the `README` or included
- // formatters for examples.
- Formatter Formatter
- // The logging level the logger should log at. This is typically (and defaults
- // to) `logrus.Info`, which allows Info(), Warn(), Error() and Fatal() to be
- // logged. `logrus.Debug` is useful in
- Level Level
- // Used to sync writing to the log. Locking is enabled by Default
- mu MutexWrap
- // Reusable empty entry
- entryPool sync.Pool
-}
-
-type MutexWrap struct {
- lock sync.Mutex
- disabled bool
-}
-
-func (mw *MutexWrap) Lock() {
- if !mw.disabled {
- mw.lock.Lock()
- }
-}
-
-func (mw *MutexWrap) Unlock() {
- if !mw.disabled {
- mw.lock.Unlock()
- }
-}
-
-func (mw *MutexWrap) Disable() {
- mw.disabled = true
-}
-
-// Creates a new logger. Configuration should be set by changing `Formatter`,
-// `Out` and `Hooks` directly on the default logger instance. You can also just
-// instantiate your own:
-//
-// var log = &Logger{
-// Out: os.Stderr,
-// Formatter: new(JSONFormatter),
-// Hooks: make(LevelHooks),
-// Level: logrus.DebugLevel,
-// }
-//
-// It's recommended to make this a global instance called `log`.
-func New() *Logger {
- return &Logger{
- Out: os.Stderr,
- Formatter: new(TextFormatter),
- Hooks: make(LevelHooks),
- Level: InfoLevel,
- }
-}
-
-func (logger *Logger) newEntry() *Entry {
- entry, ok := logger.entryPool.Get().(*Entry)
- if ok {
- return entry
- }
- return NewEntry(logger)
-}
-
-func (logger *Logger) releaseEntry(entry *Entry) {
- logger.entryPool.Put(entry)
-}
-
-// Adds a field to the log entry, note that it doesn't log until you call
-// Debug, Print, Info, Warn, Fatal or Panic. It only creates a log entry.
-// If you want multiple fields, use `WithFields`.
-func (logger *Logger) WithField(key string, value interface{}) *Entry {
- entry := logger.newEntry()
- defer logger.releaseEntry(entry)
- return entry.WithField(key, value)
-}
-
-// Adds a struct of fields to the log entry. All it does is call `WithField` for
-// each `Field`.
-func (logger *Logger) WithFields(fields Fields) *Entry {
- entry := logger.newEntry()
- defer logger.releaseEntry(entry)
- return entry.WithFields(fields)
-}
-
-// Add an error as single field to the log entry. All it does is call
-// `WithError` for the given `error`.
-func (logger *Logger) WithError(err error) *Entry {
- entry := logger.newEntry()
- defer logger.releaseEntry(entry)
- return entry.WithError(err)
-}
-
-func (logger *Logger) Debugf(format string, args ...interface{}) {
- if logger.Level >= DebugLevel {
- entry := logger.newEntry()
- entry.Debugf(format, args...)
- logger.releaseEntry(entry)
- }
-}
-
-func (logger *Logger) Infof(format string, args ...interface{}) {
- if logger.Level >= InfoLevel {
- entry := logger.newEntry()
- entry.Infof(format, args...)
- logger.releaseEntry(entry)
- }
-}
-
-func (logger *Logger) Printf(format string, args ...interface{}) {
- entry := logger.newEntry()
- entry.Printf(format, args...)
- logger.releaseEntry(entry)
-}
-
-func (logger *Logger) Warnf(format string, args ...interface{}) {
- if logger.Level >= WarnLevel {
- entry := logger.newEntry()
- entry.Warnf(format, args...)
- logger.releaseEntry(entry)
- }
-}
-
-func (logger *Logger) Warningf(format string, args ...interface{}) {
- if logger.Level >= WarnLevel {
- entry := logger.newEntry()
- entry.Warnf(format, args...)
- logger.releaseEntry(entry)
- }
-}
-
-func (logger *Logger) Errorf(format string, args ...interface{}) {
- if logger.Level >= ErrorLevel {
- entry := logger.newEntry()
- entry.Errorf(format, args...)
- logger.releaseEntry(entry)
- }
-}
-
-func (logger *Logger) Fatalf(format string, args ...interface{}) {
- if logger.Level >= FatalLevel {
- entry := logger.newEntry()
- entry.Fatalf(format, args...)
- logger.releaseEntry(entry)
- }
- Exit(1)
-}
-
-func (logger *Logger) Panicf(format string, args ...interface{}) {
- if logger.Level >= PanicLevel {
- entry := logger.newEntry()
- entry.Panicf(format, args...)
- logger.releaseEntry(entry)
- }
-}
-
-func (logger *Logger) Debug(args ...interface{}) {
- if logger.Level >= DebugLevel {
- entry := logger.newEntry()
- entry.Debug(args...)
- logger.releaseEntry(entry)
- }
-}
-
-func (logger *Logger) Info(args ...interface{}) {
- if logger.Level >= InfoLevel {
- entry := logger.newEntry()
- entry.Info(args...)
- logger.releaseEntry(entry)
- }
-}
-
-func (logger *Logger) Print(args ...interface{}) {
- entry := logger.newEntry()
- entry.Info(args...)
- logger.releaseEntry(entry)
-}
-
-func (logger *Logger) Warn(args ...interface{}) {
- if logger.Level >= WarnLevel {
- entry := logger.newEntry()
- entry.Warn(args...)
- logger.releaseEntry(entry)
- }
-}
-
-func (logger *Logger) Warning(args ...interface{}) {
- if logger.Level >= WarnLevel {
- entry := logger.newEntry()
- entry.Warn(args...)
- logger.releaseEntry(entry)
- }
-}
-
-func (logger *Logger) Error(args ...interface{}) {
- if logger.Level >= ErrorLevel {
- entry := logger.newEntry()
- entry.Error(args...)
- logger.releaseEntry(entry)
- }
-}
-
-func (logger *Logger) Fatal(args ...interface{}) {
- if logger.Level >= FatalLevel {
- entry := logger.newEntry()
- entry.Fatal(args...)
- logger.releaseEntry(entry)
- }
- Exit(1)
-}
-
-func (logger *Logger) Panic(args ...interface{}) {
- if logger.Level >= PanicLevel {
- entry := logger.newEntry()
- entry.Panic(args...)
- logger.releaseEntry(entry)
- }
-}
-
-func (logger *Logger) Debugln(args ...interface{}) {
- if logger.Level >= DebugLevel {
- entry := logger.newEntry()
- entry.Debugln(args...)
- logger.releaseEntry(entry)
- }
-}
-
-func (logger *Logger) Infoln(args ...interface{}) {
- if logger.Level >= InfoLevel {
- entry := logger.newEntry()
- entry.Infoln(args...)
- logger.releaseEntry(entry)
- }
-}
-
-func (logger *Logger) Println(args ...interface{}) {
- entry := logger.newEntry()
- entry.Println(args...)
- logger.releaseEntry(entry)
-}
-
-func (logger *Logger) Warnln(args ...interface{}) {
- if logger.Level >= WarnLevel {
- entry := logger.newEntry()
- entry.Warnln(args...)
- logger.releaseEntry(entry)
- }
-}
-
-func (logger *Logger) Warningln(args ...interface{}) {
- if logger.Level >= WarnLevel {
- entry := logger.newEntry()
- entry.Warnln(args...)
- logger.releaseEntry(entry)
- }
-}
-
-func (logger *Logger) Errorln(args ...interface{}) {
- if logger.Level >= ErrorLevel {
- entry := logger.newEntry()
- entry.Errorln(args...)
- logger.releaseEntry(entry)
- }
-}
-
-func (logger *Logger) Fatalln(args ...interface{}) {
- if logger.Level >= FatalLevel {
- entry := logger.newEntry()
- entry.Fatalln(args...)
- logger.releaseEntry(entry)
- }
- Exit(1)
-}
-
-func (logger *Logger) Panicln(args ...interface{}) {
- if logger.Level >= PanicLevel {
- entry := logger.newEntry()
- entry.Panicln(args...)
- logger.releaseEntry(entry)
- }
-}
-
-//When file is opened with appending mode, it's safe to
-//write concurrently to a file (within 4k message on Linux).
-//In these cases user can choose to disable the lock.
-func (logger *Logger) SetNoLock() {
- logger.mu.Disable()
-}
diff --git a/vendor/github.com/sirupsen/logrus/logger_bench_test.go b/vendor/github.com/sirupsen/logrus/logger_bench_test.go
deleted file mode 100644
index dd23a35..0000000
--- a/vendor/github.com/sirupsen/logrus/logger_bench_test.go
+++ /dev/null
@@ -1,61 +0,0 @@
-package logrus
-
-import (
- "os"
- "testing"
-)
-
-// smallFields is a small size data set for benchmarking
-var loggerFields = Fields{
- "foo": "bar",
- "baz": "qux",
- "one": "two",
- "three": "four",
-}
-
-func BenchmarkDummyLogger(b *testing.B) {
- nullf, err := os.OpenFile("/dev/null", os.O_WRONLY, 0666)
- if err != nil {
- b.Fatalf("%v", err)
- }
- defer nullf.Close()
- doLoggerBenchmark(b, nullf, &TextFormatter{DisableColors: true}, smallFields)
-}
-
-func BenchmarkDummyLoggerNoLock(b *testing.B) {
- nullf, err := os.OpenFile("/dev/null", os.O_WRONLY|os.O_APPEND, 0666)
- if err != nil {
- b.Fatalf("%v", err)
- }
- defer nullf.Close()
- doLoggerBenchmarkNoLock(b, nullf, &TextFormatter{DisableColors: true}, smallFields)
-}
-
-func doLoggerBenchmark(b *testing.B, out *os.File, formatter Formatter, fields Fields) {
- logger := Logger{
- Out: out,
- Level: InfoLevel,
- Formatter: formatter,
- }
- entry := logger.WithFields(fields)
- b.RunParallel(func(pb *testing.PB) {
- for pb.Next() {
- entry.Info("aaa")
- }
- })
-}
-
-func doLoggerBenchmarkNoLock(b *testing.B, out *os.File, formatter Formatter, fields Fields) {
- logger := Logger{
- Out: out,
- Level: InfoLevel,
- Formatter: formatter,
- }
- logger.SetNoLock()
- entry := logger.WithFields(fields)
- b.RunParallel(func(pb *testing.PB) {
- for pb.Next() {
- entry.Info("aaa")
- }
- })
-}
diff --git a/vendor/github.com/sirupsen/logrus/logrus.go b/vendor/github.com/sirupsen/logrus/logrus.go
deleted file mode 100644
index e596691..0000000
--- a/vendor/github.com/sirupsen/logrus/logrus.go
+++ /dev/null
@@ -1,143 +0,0 @@
-package logrus
-
-import (
- "fmt"
- "log"
- "strings"
-)
-
-// Fields type, used to pass to `WithFields`.
-type Fields map[string]interface{}
-
-// Level type
-type Level uint8
-
-// Convert the Level to a string. E.g. PanicLevel becomes "panic".
-func (level Level) String() string {
- switch level {
- case DebugLevel:
- return "debug"
- case InfoLevel:
- return "info"
- case WarnLevel:
- return "warning"
- case ErrorLevel:
- return "error"
- case FatalLevel:
- return "fatal"
- case PanicLevel:
- return "panic"
- }
-
- return "unknown"
-}
-
-// ParseLevel takes a string level and returns the Logrus log level constant.
-func ParseLevel(lvl string) (Level, error) {
- switch strings.ToLower(lvl) {
- case "panic":
- return PanicLevel, nil
- case "fatal":
- return FatalLevel, nil
- case "error":
- return ErrorLevel, nil
- case "warn", "warning":
- return WarnLevel, nil
- case "info":
- return InfoLevel, nil
- case "debug":
- return DebugLevel, nil
- }
-
- var l Level
- return l, fmt.Errorf("not a valid logrus Level: %q", lvl)
-}
-
-// A constant exposing all logging levels
-var AllLevels = []Level{
- PanicLevel,
- FatalLevel,
- ErrorLevel,
- WarnLevel,
- InfoLevel,
- DebugLevel,
-}
-
-// These are the different logging levels. You can set the logging level to log
-// on your instance of logger, obtained with `logrus.New()`.
-const (
- // PanicLevel level, highest level of severity. Logs and then calls panic with the
- // message passed to Debug, Info, ...
- PanicLevel Level = iota
- // FatalLevel level. Logs and then calls `os.Exit(1)`. It will exit even if the
- // logging level is set to Panic.
- FatalLevel
- // ErrorLevel level. Logs. Used for errors that should definitely be noted.
- // Commonly used for hooks to send errors to an error tracking service.
- ErrorLevel
- // WarnLevel level. Non-critical entries that deserve eyes.
- WarnLevel
- // InfoLevel level. General operational entries about what's going on inside the
- // application.
- InfoLevel
- // DebugLevel level. Usually only enabled when debugging. Very verbose logging.
- DebugLevel
-)
-
-// Won't compile if StdLogger can't be realized by a log.Logger
-var (
- _ StdLogger = &log.Logger{}
- _ StdLogger = &Entry{}
- _ StdLogger = &Logger{}
-)
-
-// StdLogger is what your logrus-enabled library should take, that way
-// it'll accept a stdlib logger and a logrus logger. There's no standard
-// interface, this is the closest we get, unfortunately.
-type StdLogger interface {
- Print(...interface{})
- Printf(string, ...interface{})
- Println(...interface{})
-
- Fatal(...interface{})
- Fatalf(string, ...interface{})
- Fatalln(...interface{})
-
- Panic(...interface{})
- Panicf(string, ...interface{})
- Panicln(...interface{})
-}
-
-// The FieldLogger interface generalizes the Entry and Logger types
-type FieldLogger interface {
- WithField(key string, value interface{}) *Entry
- WithFields(fields Fields) *Entry
- WithError(err error) *Entry
-
- Debugf(format string, args ...interface{})
- Infof(format string, args ...interface{})
- Printf(format string, args ...interface{})
- Warnf(format string, args ...interface{})
- Warningf(format string, args ...interface{})
- Errorf(format string, args ...interface{})
- Fatalf(format string, args ...interface{})
- Panicf(format string, args ...interface{})
-
- Debug(args ...interface{})
- Info(args ...interface{})
- Print(args ...interface{})
- Warn(args ...interface{})
- Warning(args ...interface{})
- Error(args ...interface{})
- Fatal(args ...interface{})
- Panic(args ...interface{})
-
- Debugln(args ...interface{})
- Infoln(args ...interface{})
- Println(args ...interface{})
- Warnln(args ...interface{})
- Warningln(args ...interface{})
- Errorln(args ...interface{})
- Fatalln(args ...interface{})
- Panicln(args ...interface{})
-}
diff --git a/vendor/github.com/sirupsen/logrus/logrus_test.go b/vendor/github.com/sirupsen/logrus/logrus_test.go
deleted file mode 100644
index 78cbc28..0000000
--- a/vendor/github.com/sirupsen/logrus/logrus_test.go
+++ /dev/null
@@ -1,386 +0,0 @@
-package logrus
-
-import (
- "bytes"
- "encoding/json"
- "strconv"
- "strings"
- "sync"
- "testing"
-
- "github.com/stretchr/testify/assert"
-)
-
-func LogAndAssertJSON(t *testing.T, log func(*Logger), assertions func(fields Fields)) {
- var buffer bytes.Buffer
- var fields Fields
-
- logger := New()
- logger.Out = &buffer
- logger.Formatter = new(JSONFormatter)
-
- log(logger)
-
- err := json.Unmarshal(buffer.Bytes(), &fields)
- assert.Nil(t, err)
-
- assertions(fields)
-}
-
-func LogAndAssertText(t *testing.T, log func(*Logger), assertions func(fields map[string]string)) {
- var buffer bytes.Buffer
-
- logger := New()
- logger.Out = &buffer
- logger.Formatter = &TextFormatter{
- DisableColors: true,
- }
-
- log(logger)
-
- fields := make(map[string]string)
- for _, kv := range strings.Split(buffer.String(), " ") {
- if !strings.Contains(kv, "=") {
- continue
- }
- kvArr := strings.Split(kv, "=")
- key := strings.TrimSpace(kvArr[0])
- val := kvArr[1]
- if kvArr[1][0] == '"' {
- var err error
- val, err = strconv.Unquote(val)
- assert.NoError(t, err)
- }
- fields[key] = val
- }
- assertions(fields)
-}
-
-func TestPrint(t *testing.T) {
- LogAndAssertJSON(t, func(log *Logger) {
- log.Print("test")
- }, func(fields Fields) {
- assert.Equal(t, fields["msg"], "test")
- assert.Equal(t, fields["level"], "info")
- })
-}
-
-func TestInfo(t *testing.T) {
- LogAndAssertJSON(t, func(log *Logger) {
- log.Info("test")
- }, func(fields Fields) {
- assert.Equal(t, fields["msg"], "test")
- assert.Equal(t, fields["level"], "info")
- })
-}
-
-func TestWarn(t *testing.T) {
- LogAndAssertJSON(t, func(log *Logger) {
- log.Warn("test")
- }, func(fields Fields) {
- assert.Equal(t, fields["msg"], "test")
- assert.Equal(t, fields["level"], "warning")
- })
-}
-
-func TestInfolnShouldAddSpacesBetweenStrings(t *testing.T) {
- LogAndAssertJSON(t, func(log *Logger) {
- log.Infoln("test", "test")
- }, func(fields Fields) {
- assert.Equal(t, fields["msg"], "test test")
- })
-}
-
-func TestInfolnShouldAddSpacesBetweenStringAndNonstring(t *testing.T) {
- LogAndAssertJSON(t, func(log *Logger) {
- log.Infoln("test", 10)
- }, func(fields Fields) {
- assert.Equal(t, fields["msg"], "test 10")
- })
-}
-
-func TestInfolnShouldAddSpacesBetweenTwoNonStrings(t *testing.T) {
- LogAndAssertJSON(t, func(log *Logger) {
- log.Infoln(10, 10)
- }, func(fields Fields) {
- assert.Equal(t, fields["msg"], "10 10")
- })
-}
-
-func TestInfoShouldAddSpacesBetweenTwoNonStrings(t *testing.T) {
- LogAndAssertJSON(t, func(log *Logger) {
- log.Infoln(10, 10)
- }, func(fields Fields) {
- assert.Equal(t, fields["msg"], "10 10")
- })
-}
-
-func TestInfoShouldNotAddSpacesBetweenStringAndNonstring(t *testing.T) {
- LogAndAssertJSON(t, func(log *Logger) {
- log.Info("test", 10)
- }, func(fields Fields) {
- assert.Equal(t, fields["msg"], "test10")
- })
-}
-
-func TestInfoShouldNotAddSpacesBetweenStrings(t *testing.T) {
- LogAndAssertJSON(t, func(log *Logger) {
- log.Info("test", "test")
- }, func(fields Fields) {
- assert.Equal(t, fields["msg"], "testtest")
- })
-}
-
-func TestWithFieldsShouldAllowAssignments(t *testing.T) {
- var buffer bytes.Buffer
- var fields Fields
-
- logger := New()
- logger.Out = &buffer
- logger.Formatter = new(JSONFormatter)
-
- localLog := logger.WithFields(Fields{
- "key1": "value1",
- })
-
- localLog.WithField("key2", "value2").Info("test")
- err := json.Unmarshal(buffer.Bytes(), &fields)
- assert.Nil(t, err)
-
- assert.Equal(t, "value2", fields["key2"])
- assert.Equal(t, "value1", fields["key1"])
-
- buffer = bytes.Buffer{}
- fields = Fields{}
- localLog.Info("test")
- err = json.Unmarshal(buffer.Bytes(), &fields)
- assert.Nil(t, err)
-
- _, ok := fields["key2"]
- assert.Equal(t, false, ok)
- assert.Equal(t, "value1", fields["key1"])
-}
-
-func TestUserSuppliedFieldDoesNotOverwriteDefaults(t *testing.T) {
- LogAndAssertJSON(t, func(log *Logger) {
- log.WithField("msg", "hello").Info("test")
- }, func(fields Fields) {
- assert.Equal(t, fields["msg"], "test")
- })
-}
-
-func TestUserSuppliedMsgFieldHasPrefix(t *testing.T) {
- LogAndAssertJSON(t, func(log *Logger) {
- log.WithField("msg", "hello").Info("test")
- }, func(fields Fields) {
- assert.Equal(t, fields["msg"], "test")
- assert.Equal(t, fields["fields.msg"], "hello")
- })
-}
-
-func TestUserSuppliedTimeFieldHasPrefix(t *testing.T) {
- LogAndAssertJSON(t, func(log *Logger) {
- log.WithField("time", "hello").Info("test")
- }, func(fields Fields) {
- assert.Equal(t, fields["fields.time"], "hello")
- })
-}
-
-func TestUserSuppliedLevelFieldHasPrefix(t *testing.T) {
- LogAndAssertJSON(t, func(log *Logger) {
- log.WithField("level", 1).Info("test")
- }, func(fields Fields) {
- assert.Equal(t, fields["level"], "info")
- assert.Equal(t, fields["fields.level"], 1.0) // JSON has floats only
- })
-}
-
-func TestDefaultFieldsAreNotPrefixed(t *testing.T) {
- LogAndAssertText(t, func(log *Logger) {
- ll := log.WithField("herp", "derp")
- ll.Info("hello")
- ll.Info("bye")
- }, func(fields map[string]string) {
- for _, fieldName := range []string{"fields.level", "fields.time", "fields.msg"} {
- if _, ok := fields[fieldName]; ok {
- t.Fatalf("should not have prefixed %q: %v", fieldName, fields)
- }
- }
- })
-}
-
-func TestDoubleLoggingDoesntPrefixPreviousFields(t *testing.T) {
-
- var buffer bytes.Buffer
- var fields Fields
-
- logger := New()
- logger.Out = &buffer
- logger.Formatter = new(JSONFormatter)
-
- llog := logger.WithField("context", "eating raw fish")
-
- llog.Info("looks delicious")
-
- err := json.Unmarshal(buffer.Bytes(), &fields)
- assert.NoError(t, err, "should have decoded first message")
- assert.Equal(t, len(fields), 4, "should only have msg/time/level/context fields")
- assert.Equal(t, fields["msg"], "looks delicious")
- assert.Equal(t, fields["context"], "eating raw fish")
-
- buffer.Reset()
-
- llog.Warn("omg it is!")
-
- err = json.Unmarshal(buffer.Bytes(), &fields)
- assert.NoError(t, err, "should have decoded second message")
- assert.Equal(t, len(fields), 4, "should only have msg/time/level/context fields")
- assert.Equal(t, fields["msg"], "omg it is!")
- assert.Equal(t, fields["context"], "eating raw fish")
- assert.Nil(t, fields["fields.msg"], "should not have prefixed previous `msg` entry")
-
-}
-
-func TestConvertLevelToString(t *testing.T) {
- assert.Equal(t, "debug", DebugLevel.String())
- assert.Equal(t, "info", InfoLevel.String())
- assert.Equal(t, "warning", WarnLevel.String())
- assert.Equal(t, "error", ErrorLevel.String())
- assert.Equal(t, "fatal", FatalLevel.String())
- assert.Equal(t, "panic", PanicLevel.String())
-}
-
-func TestParseLevel(t *testing.T) {
- l, err := ParseLevel("panic")
- assert.Nil(t, err)
- assert.Equal(t, PanicLevel, l)
-
- l, err = ParseLevel("PANIC")
- assert.Nil(t, err)
- assert.Equal(t, PanicLevel, l)
-
- l, err = ParseLevel("fatal")
- assert.Nil(t, err)
- assert.Equal(t, FatalLevel, l)
-
- l, err = ParseLevel("FATAL")
- assert.Nil(t, err)
- assert.Equal(t, FatalLevel, l)
-
- l, err = ParseLevel("error")
- assert.Nil(t, err)
- assert.Equal(t, ErrorLevel, l)
-
- l, err = ParseLevel("ERROR")
- assert.Nil(t, err)
- assert.Equal(t, ErrorLevel, l)
-
- l, err = ParseLevel("warn")
- assert.Nil(t, err)
- assert.Equal(t, WarnLevel, l)
-
- l, err = ParseLevel("WARN")
- assert.Nil(t, err)
- assert.Equal(t, WarnLevel, l)
-
- l, err = ParseLevel("warning")
- assert.Nil(t, err)
- assert.Equal(t, WarnLevel, l)
-
- l, err = ParseLevel("WARNING")
- assert.Nil(t, err)
- assert.Equal(t, WarnLevel, l)
-
- l, err = ParseLevel("info")
- assert.Nil(t, err)
- assert.Equal(t, InfoLevel, l)
-
- l, err = ParseLevel("INFO")
- assert.Nil(t, err)
- assert.Equal(t, InfoLevel, l)
-
- l, err = ParseLevel("debug")
- assert.Nil(t, err)
- assert.Equal(t, DebugLevel, l)
-
- l, err = ParseLevel("DEBUG")
- assert.Nil(t, err)
- assert.Equal(t, DebugLevel, l)
-
- l, err = ParseLevel("invalid")
- assert.Equal(t, "not a valid logrus Level: \"invalid\"", err.Error())
-}
-
-func TestGetSetLevelRace(t *testing.T) {
- wg := sync.WaitGroup{}
- for i := 0; i < 100; i++ {
- wg.Add(1)
- go func(i int) {
- defer wg.Done()
- if i%2 == 0 {
- SetLevel(InfoLevel)
- } else {
- GetLevel()
- }
- }(i)
-
- }
- wg.Wait()
-}
-
-func TestLoggingRace(t *testing.T) {
- logger := New()
-
- var wg sync.WaitGroup
- wg.Add(100)
-
- for i := 0; i < 100; i++ {
- go func() {
- logger.Info("info")
- wg.Done()
- }()
- }
- wg.Wait()
-}
-
-// Compile test
-func TestLogrusInterface(t *testing.T) {
- var buffer bytes.Buffer
- fn := func(l FieldLogger) {
- b := l.WithField("key", "value")
- b.Debug("Test")
- }
- // test logger
- logger := New()
- logger.Out = &buffer
- fn(logger)
-
- // test Entry
- e := logger.WithField("another", "value")
- fn(e)
-}
-
-// Implements io.Writer using channels for synchronization, so we can wait on
-// the Entry.Writer goroutine to write in a non-racey way. This does assume that
-// there is a single call to Logger.Out for each message.
-type channelWriter chan []byte
-
-func (cw channelWriter) Write(p []byte) (int, error) {
- cw <- p
- return len(p), nil
-}
-
-func TestEntryWriter(t *testing.T) {
- cw := channelWriter(make(chan []byte, 1))
- log := New()
- log.Out = cw
- log.Formatter = new(JSONFormatter)
- log.WithField("foo", "bar").WriterLevel(WarnLevel).Write([]byte("hello\n"))
-
- bs := <-cw
- var fields Fields
- err := json.Unmarshal(bs, &fields)
- assert.Nil(t, err)
- assert.Equal(t, fields["foo"], "bar")
- assert.Equal(t, fields["level"], "warning")
-}
diff --git a/vendor/github.com/sirupsen/logrus/terminal_appengine.go b/vendor/github.com/sirupsen/logrus/terminal_appengine.go
deleted file mode 100644
index e011a86..0000000
--- a/vendor/github.com/sirupsen/logrus/terminal_appengine.go
+++ /dev/null
@@ -1,10 +0,0 @@
-// +build appengine
-
-package logrus
-
-import "io"
-
-// IsTerminal returns true if stderr's file descriptor is a terminal.
-func IsTerminal(f io.Writer) bool {
- return true
-}
diff --git a/vendor/github.com/sirupsen/logrus/terminal_bsd.go b/vendor/github.com/sirupsen/logrus/terminal_bsd.go
deleted file mode 100644
index 5f6be4d..0000000
--- a/vendor/github.com/sirupsen/logrus/terminal_bsd.go
+++ /dev/null
@@ -1,10 +0,0 @@
-// +build darwin freebsd openbsd netbsd dragonfly
-// +build !appengine
-
-package logrus
-
-import "syscall"
-
-const ioctlReadTermios = syscall.TIOCGETA
-
-type Termios syscall.Termios
diff --git a/vendor/github.com/sirupsen/logrus/terminal_linux.go b/vendor/github.com/sirupsen/logrus/terminal_linux.go
deleted file mode 100644
index 308160c..0000000
--- a/vendor/github.com/sirupsen/logrus/terminal_linux.go
+++ /dev/null
@@ -1,14 +0,0 @@
-// Based on ssh/terminal:
-// Copyright 2013 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.
-
-// +build !appengine
-
-package logrus
-
-import "syscall"
-
-const ioctlReadTermios = syscall.TCGETS
-
-type Termios syscall.Termios
diff --git a/vendor/github.com/sirupsen/logrus/terminal_notwindows.go b/vendor/github.com/sirupsen/logrus/terminal_notwindows.go
deleted file mode 100644
index 190297a..0000000
--- a/vendor/github.com/sirupsen/logrus/terminal_notwindows.go
+++ /dev/null
@@ -1,28 +0,0 @@
-// Based on ssh/terminal:
-// Copyright 2011 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.
-
-// +build linux darwin freebsd openbsd netbsd dragonfly
-// +build !appengine
-
-package logrus
-
-import (
- "io"
- "os"
- "syscall"
- "unsafe"
-)
-
-// IsTerminal returns true if stderr's file descriptor is a terminal.
-func IsTerminal(f io.Writer) bool {
- var termios Termios
- switch v := f.(type) {
- case *os.File:
- _, _, err := syscall.Syscall6(syscall.SYS_IOCTL, uintptr(v.Fd()), ioctlReadTermios, uintptr(unsafe.Pointer(&termios)), 0, 0, 0)
- return err == 0
- default:
- return false
- }
-}
diff --git a/vendor/github.com/sirupsen/logrus/terminal_solaris.go b/vendor/github.com/sirupsen/logrus/terminal_solaris.go
deleted file mode 100644
index 3c86b1a..0000000
--- a/vendor/github.com/sirupsen/logrus/terminal_solaris.go
+++ /dev/null
@@ -1,21 +0,0 @@
-// +build solaris,!appengine
-
-package logrus
-
-import (
- "io"
- "os"
-
- "golang.org/x/sys/unix"
-)
-
-// IsTerminal returns true if the given file descriptor is a terminal.
-func IsTerminal(f io.Writer) bool {
- switch v := f.(type) {
- case *os.File:
- _, err := unix.IoctlGetTermios(int(v.Fd()), unix.TCGETA)
- return err == nil
- default:
- return false
- }
-}
diff --git a/vendor/github.com/sirupsen/logrus/terminal_windows.go b/vendor/github.com/sirupsen/logrus/terminal_windows.go
deleted file mode 100644
index 05d2f91..0000000
--- a/vendor/github.com/sirupsen/logrus/terminal_windows.go
+++ /dev/null
@@ -1,33 +0,0 @@
-// Based on ssh/terminal:
-// Copyright 2011 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.
-
-// +build windows,!appengine
-
-package logrus
-
-import (
- "io"
- "os"
- "syscall"
- "unsafe"
-)
-
-var kernel32 = syscall.NewLazyDLL("kernel32.dll")
-
-var (
- procGetConsoleMode = kernel32.NewProc("GetConsoleMode")
-)
-
-// IsTerminal returns true if stderr's file descriptor is a terminal.
-func IsTerminal(f io.Writer) bool {
- switch v := f.(type) {
- case *os.File:
- var st uint32
- r, _, e := syscall.Syscall(procGetConsoleMode.Addr(), 2, uintptr(v.Fd()), uintptr(unsafe.Pointer(&st)), 0)
- return r != 0 && e == 0
- default:
- return false
- }
-}
diff --git a/vendor/github.com/sirupsen/logrus/text_formatter.go b/vendor/github.com/sirupsen/logrus/text_formatter.go
deleted file mode 100644
index ba88854..0000000
--- a/vendor/github.com/sirupsen/logrus/text_formatter.go
+++ /dev/null
@@ -1,189 +0,0 @@
-package logrus
-
-import (
- "bytes"
- "fmt"
- "sort"
- "strings"
- "sync"
- "time"
-)
-
-const (
- nocolor = 0
- red = 31
- green = 32
- yellow = 33
- blue = 34
- gray = 37
-)
-
-var (
- baseTimestamp time.Time
-)
-
-func init() {
- baseTimestamp = time.Now()
-}
-
-type TextFormatter struct {
- // Set to true to bypass checking for a TTY before outputting colors.
- ForceColors bool
-
- // Force disabling colors.
- DisableColors bool
-
- // Disable timestamp logging. useful when output is redirected to logging
- // system that already adds timestamps.
- DisableTimestamp bool
-
- // Enable logging the full timestamp when a TTY is attached instead of just
- // the time passed since beginning of execution.
- FullTimestamp bool
-
- // TimestampFormat to use for display when a full timestamp is printed
- TimestampFormat string
-
- // The fields are sorted by default for a consistent output. For applications
- // that log extremely frequently and don't use the JSON formatter this may not
- // be desired.
- DisableSorting bool
-
- // QuoteEmptyFields will wrap empty fields in quotes if true
- QuoteEmptyFields bool
-
- // QuoteCharacter can be set to the override the default quoting character "
- // with something else. For example: ', or `.
- QuoteCharacter string
-
- // Whether the logger's out is to a terminal
- isTerminal bool
-
- sync.Once
-}
-
-func (f *TextFormatter) init(entry *Entry) {
- if len(f.QuoteCharacter) == 0 {
- f.QuoteCharacter = "\""
- }
- if entry.Logger != nil {
- f.isTerminal = IsTerminal(entry.Logger.Out)
- }
-}
-
-func (f *TextFormatter) Format(entry *Entry) ([]byte, error) {
- var b *bytes.Buffer
- keys := make([]string, 0, len(entry.Data))
- for k := range entry.Data {
- keys = append(keys, k)
- }
-
- if !f.DisableSorting {
- sort.Strings(keys)
- }
- if entry.Buffer != nil {
- b = entry.Buffer
- } else {
- b = &bytes.Buffer{}
- }
-
- prefixFieldClashes(entry.Data)
-
- f.Do(func() { f.init(entry) })
-
- isColored := (f.ForceColors || f.isTerminal) && !f.DisableColors
-
- timestampFormat := f.TimestampFormat
- if timestampFormat == "" {
- timestampFormat = DefaultTimestampFormat
- }
- if isColored {
- f.printColored(b, entry, keys, timestampFormat)
- } else {
- if !f.DisableTimestamp {
- f.appendKeyValue(b, "time", entry.Time.Format(timestampFormat))
- }
- f.appendKeyValue(b, "level", entry.Level.String())
- if entry.Message != "" {
- f.appendKeyValue(b, "msg", entry.Message)
- }
- for _, key := range keys {
- f.appendKeyValue(b, key, entry.Data[key])
- }
- }
-
- b.WriteByte('\n')
- return b.Bytes(), nil
-}
-
-func (f *TextFormatter) printColored(b *bytes.Buffer, entry *Entry, keys []string, timestampFormat string) {
- var levelColor int
- switch entry.Level {
- case DebugLevel:
- levelColor = gray
- case WarnLevel:
- levelColor = yellow
- case ErrorLevel, FatalLevel, PanicLevel:
- levelColor = red
- default:
- levelColor = blue
- }
-
- levelText := strings.ToUpper(entry.Level.String())[0:4]
-
- if f.DisableTimestamp {
- fmt.Fprintf(b, "\x1b[%dm%s\x1b[0m %-44s ", levelColor, levelText, entry.Message)
- } else if !f.FullTimestamp {
- fmt.Fprintf(b, "\x1b[%dm%s\x1b[0m[%04d] %-44s ", levelColor, levelText, int(entry.Time.Sub(baseTimestamp)/time.Second), entry.Message)
- } else {
- fmt.Fprintf(b, "\x1b[%dm%s\x1b[0m[%s] %-44s ", levelColor, levelText, entry.Time.Format(timestampFormat), entry.Message)
- }
- for _, k := range keys {
- v := entry.Data[k]
- fmt.Fprintf(b, " \x1b[%dm%s\x1b[0m=", levelColor, k)
- f.appendValue(b, v)
- }
-}
-
-func (f *TextFormatter) needsQuoting(text string) bool {
- if f.QuoteEmptyFields && len(text) == 0 {
- return true
- }
- for _, ch := range text {
- if !((ch >= 'a' && ch <= 'z') ||
- (ch >= 'A' && ch <= 'Z') ||
- (ch >= '0' && ch <= '9') ||
- ch == '-' || ch == '.') {
- return true
- }
- }
- return false
-}
-
-func (f *TextFormatter) appendKeyValue(b *bytes.Buffer, key string, value interface{}) {
-
- b.WriteString(key)
- b.WriteByte('=')
- f.appendValue(b, value)
- b.WriteByte(' ')
-}
-
-func (f *TextFormatter) appendValue(b *bytes.Buffer, value interface{}) {
- switch value := value.(type) {
- case string:
- if !f.needsQuoting(value) {
- b.WriteString(value)
- } else {
- fmt.Fprintf(b, "%s%v%s", f.QuoteCharacter, value, f.QuoteCharacter)
- }
- case error:
- errmsg := value.Error()
- if !f.needsQuoting(errmsg) {
- b.WriteString(errmsg)
- } else {
- fmt.Fprintf(b, "%s%v%s", f.QuoteCharacter, errmsg, f.QuoteCharacter)
- }
- default:
- fmt.Fprint(b, value)
- }
-}
diff --git a/vendor/github.com/sirupsen/logrus/text_formatter_test.go b/vendor/github.com/sirupsen/logrus/text_formatter_test.go
deleted file mode 100644
index 9793b5f..0000000
--- a/vendor/github.com/sirupsen/logrus/text_formatter_test.go
+++ /dev/null
@@ -1,87 +0,0 @@
-package logrus
-
-import (
- "bytes"
- "errors"
- "strings"
- "testing"
- "time"
-)
-
-func TestQuoting(t *testing.T) {
- tf := &TextFormatter{DisableColors: true}
-
- checkQuoting := func(q bool, value interface{}) {
- b, _ := tf.Format(WithField("test", value))
- idx := bytes.Index(b, ([]byte)("test="))
- cont := bytes.Contains(b[idx+5:], []byte(tf.QuoteCharacter))
- if cont != q {
- if q {
- t.Errorf("quoting expected for: %#v", value)
- } else {
- t.Errorf("quoting not expected for: %#v", value)
- }
- }
- }
-
- checkQuoting(false, "")
- checkQuoting(false, "abcd")
- checkQuoting(false, "v1.0")
- checkQuoting(false, "1234567890")
- checkQuoting(true, "/foobar")
- checkQuoting(true, "x y")
- checkQuoting(true, "x,y")
- checkQuoting(false, errors.New("invalid"))
- checkQuoting(true, errors.New("invalid argument"))
-
- // Test for custom quote character.
- tf.QuoteCharacter = "`"
- checkQuoting(false, "")
- checkQuoting(false, "abcd")
- checkQuoting(true, "/foobar")
- checkQuoting(true, errors.New("invalid argument"))
-
- // Test for multi-character quotes.
- tf.QuoteCharacter = "§~±"
- checkQuoting(false, "abcd")
- checkQuoting(true, errors.New("invalid argument"))
-
- // Test for quoting empty fields.
- tf.QuoteEmptyFields = true
- checkQuoting(true, "")
- checkQuoting(false, "abcd")
- checkQuoting(true, errors.New("invalid argument"))
-}
-
-func TestTimestampFormat(t *testing.T) {
- checkTimeStr := func(format string) {
- customFormatter := &TextFormatter{DisableColors: true, TimestampFormat: format}
- customStr, _ := customFormatter.Format(WithField("test", "test"))
- timeStart := bytes.Index(customStr, ([]byte)("time="))
- timeEnd := bytes.Index(customStr, ([]byte)("level="))
- timeStr := customStr[timeStart+5+len(customFormatter.QuoteCharacter) : timeEnd-1-len(customFormatter.QuoteCharacter)]
- if format == "" {
- format = time.RFC3339
- }
- _, e := time.Parse(format, (string)(timeStr))
- if e != nil {
- t.Errorf("time string \"%s\" did not match provided time format \"%s\": %s", timeStr, format, e)
- }
- }
-
- checkTimeStr("2006-01-02T15:04:05.000000000Z07:00")
- checkTimeStr("Mon Jan _2 15:04:05 2006")
- checkTimeStr("")
-}
-
-func TestDisableTimestampWithColoredOutput(t *testing.T) {
- tf := &TextFormatter{DisableTimestamp: true, ForceColors: true}
-
- b, _ := tf.Format(WithField("test", "test"))
- if strings.Contains(string(b), "[0000]") {
- t.Error("timestamp not expected when DisableTimestamp is true")
- }
-}
-
-// TODO add tests for sorting etc., this requires a parser for the text
-// formatter output.
diff --git a/vendor/github.com/sirupsen/logrus/writer.go b/vendor/github.com/sirupsen/logrus/writer.go
deleted file mode 100644
index 7bdebed..0000000
--- a/vendor/github.com/sirupsen/logrus/writer.go
+++ /dev/null
@@ -1,62 +0,0 @@
-package logrus
-
-import (
- "bufio"
- "io"
- "runtime"
-)
-
-func (logger *Logger) Writer() *io.PipeWriter {
- return logger.WriterLevel(InfoLevel)
-}
-
-func (logger *Logger) WriterLevel(level Level) *io.PipeWriter {
- return NewEntry(logger).WriterLevel(level)
-}
-
-func (entry *Entry) Writer() *io.PipeWriter {
- return entry.WriterLevel(InfoLevel)
-}
-
-func (entry *Entry) WriterLevel(level Level) *io.PipeWriter {
- reader, writer := io.Pipe()
-
- var printFunc func(args ...interface{})
-
- switch level {
- case DebugLevel:
- printFunc = entry.Debug
- case InfoLevel:
- printFunc = entry.Info
- case WarnLevel:
- printFunc = entry.Warn
- case ErrorLevel:
- printFunc = entry.Error
- case FatalLevel:
- printFunc = entry.Fatal
- case PanicLevel:
- printFunc = entry.Panic
- default:
- printFunc = entry.Print
- }
-
- go entry.writerScanner(reader, printFunc)
- runtime.SetFinalizer(writer, writerFinalizer)
-
- return writer
-}
-
-func (entry *Entry) writerScanner(reader *io.PipeReader, printFunc func(args ...interface{})) {
- scanner := bufio.NewScanner(reader)
- for scanner.Scan() {
- printFunc(scanner.Text())
- }
- if err := scanner.Err(); err != nil {
- entry.Errorf("Error while reading from Writer: %s", err)
- }
- reader.Close()
-}
-
-func writerFinalizer(writer *io.PipeWriter) {
- writer.Close()
-}
diff --git a/vendor/golang.org/x/sys/.gitattributes b/vendor/golang.org/x/sys/.gitattributes
deleted file mode 100644
index d2f212e..0000000
--- a/vendor/golang.org/x/sys/.gitattributes
+++ /dev/null
@@ -1,10 +0,0 @@
-# Treat all files in this repo as binary, with no git magic updating
-# line endings. Windows users contributing to Go will need to use a
-# modern version of git and editors capable of LF line endings.
-#
-# We'll prevent accidental CRLF line endings from entering the repo
-# via the git-review gofmt checks.
-#
-# See golang.org/issue/9281
-
-* -text
diff --git a/vendor/golang.org/x/sys/.gitignore b/vendor/golang.org/x/sys/.gitignore
deleted file mode 100644
index 8339fd6..0000000
--- a/vendor/golang.org/x/sys/.gitignore
+++ /dev/null
@@ -1,2 +0,0 @@
-# Add no patterns to .hgignore except for files generated by the build.
-last-change
diff --git a/vendor/golang.org/x/sys/AUTHORS b/vendor/golang.org/x/sys/AUTHORS
deleted file mode 100644
index 15167cd..0000000
--- a/vendor/golang.org/x/sys/AUTHORS
+++ /dev/null
@@ -1,3 +0,0 @@
-# This source code refers to The Go Authors for copyright purposes.
-# The master list of authors is in the main Go distribution,
-# visible at http://tip.golang.org/AUTHORS.
diff --git a/vendor/golang.org/x/sys/CONTRIBUTING.md b/vendor/golang.org/x/sys/CONTRIBUTING.md
deleted file mode 100644
index 88dff59..0000000
--- a/vendor/golang.org/x/sys/CONTRIBUTING.md
+++ /dev/null
@@ -1,31 +0,0 @@
-# Contributing to Go
-
-Go is an open source project.
-
-It is the work of hundreds of contributors. We appreciate your help!
-
-
-## Filing issues
-
-When [filing an issue](https://golang.org/issue/new), make sure to answer these five questions:
-
-1. What version of Go are you using (`go version`)?
-2. What operating system and processor architecture are you using?
-3. What did you do?
-4. What did you expect to see?
-5. What did you see instead?
-
-General questions should go to the [golang-nuts mailing list](https://groups.google.com/group/golang-nuts) instead of the issue tracker.
-The gophers there will answer or ask you to file an issue if you've tripped over a bug.
-
-## Contributing code
-
-Please read the [Contribution Guidelines](https://golang.org/doc/contribute.html)
-before sending patches.
-
-**We do not accept GitHub pull requests**
-(we use [Gerrit](https://code.google.com/p/gerrit/) instead for code review).
-
-Unless otherwise noted, the Go source files are distributed under
-the BSD-style license found in the LICENSE file.
-
diff --git a/vendor/golang.org/x/sys/CONTRIBUTORS b/vendor/golang.org/x/sys/CONTRIBUTORS
deleted file mode 100644
index 1c4577e..0000000
--- a/vendor/golang.org/x/sys/CONTRIBUTORS
+++ /dev/null
@@ -1,3 +0,0 @@
-# This source code was written by the Go contributors.
-# The master list of contributors is in the main Go distribution,
-# visible at http://tip.golang.org/CONTRIBUTORS.
diff --git a/vendor/golang.org/x/sys/LICENSE b/vendor/golang.org/x/sys/LICENSE
deleted file mode 100644
index 6a66aea..0000000
--- a/vendor/golang.org/x/sys/LICENSE
+++ /dev/null
@@ -1,27 +0,0 @@
-Copyright (c) 2009 The Go Authors. All rights reserved.
-
-Redistribution and use in source and binary forms, with or without
-modification, are permitted provided that the following conditions are
-met:
-
- * Redistributions of source code must retain the above copyright
-notice, this list of conditions and the following disclaimer.
- * Redistributions in binary form must reproduce the above
-copyright notice, this list of conditions and the following disclaimer
-in the documentation and/or other materials provided with the
-distribution.
- * Neither the name of Google Inc. nor the names of its
-contributors may be used to endorse or promote products derived from
-this software without specific prior written permission.
-
-THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
-"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
-LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
-A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
-OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
-SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
-LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
-DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
-THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
-(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
-OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
diff --git a/vendor/golang.org/x/sys/PATENTS b/vendor/golang.org/x/sys/PATENTS
deleted file mode 100644
index 7330990..0000000
--- a/vendor/golang.org/x/sys/PATENTS
+++ /dev/null
@@ -1,22 +0,0 @@
-Additional IP Rights Grant (Patents)
-
-"This implementation" means the copyrightable works distributed by
-Google as part of the Go project.
-
-Google hereby grants to You a perpetual, worldwide, non-exclusive,
-no-charge, royalty-free, irrevocable (except as stated in this section)
-patent license to make, have made, use, offer to sell, sell, import,
-transfer and otherwise run, modify and propagate the contents of this
-implementation of Go, where such license applies only to those patent
-claims, both currently owned or controlled by Google and acquired in
-the future, licensable by Google that are necessarily infringed by this
-implementation of Go. This grant does not include claims that would be
-infringed only as a consequence of further modification of this
-implementation. If you or your agent or exclusive licensee institute or
-order or agree to the institution of patent litigation against any
-entity (including a cross-claim or counterclaim in a lawsuit) alleging
-that this implementation of Go or any code incorporated within this
-implementation of Go constitutes direct or contributory patent
-infringement, or inducement of patent infringement, then any patent
-rights granted to you under this License for this implementation of Go
-shall terminate as of the date such litigation is filed.
diff --git a/vendor/golang.org/x/sys/README b/vendor/golang.org/x/sys/README
deleted file mode 100644
index bd422b4..0000000
--- a/vendor/golang.org/x/sys/README
+++ /dev/null
@@ -1,3 +0,0 @@
-This repository holds supplemental Go packages for low-level interactions with the operating system.
-
-To submit changes to this repository, see http://golang.org/doc/contribute.html.
diff --git a/vendor/golang.org/x/sys/codereview.cfg b/vendor/golang.org/x/sys/codereview.cfg
deleted file mode 100644
index 3f8b14b..0000000
--- a/vendor/golang.org/x/sys/codereview.cfg
+++ /dev/null
@@ -1 +0,0 @@
-issuerepo: golang/go
diff --git a/vendor/golang.org/x/sys/plan9/asm.s b/vendor/golang.org/x/sys/plan9/asm.s
deleted file mode 100644
index d4ca868..0000000
--- a/vendor/golang.org/x/sys/plan9/asm.s
+++ /dev/null
@@ -1,8 +0,0 @@
-// Copyright 2014 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.
-
-#include "textflag.h"
-
-TEXT ·use(SB),NOSPLIT,$0
- RET
diff --git a/vendor/golang.org/x/sys/plan9/asm_plan9_386.s b/vendor/golang.org/x/sys/plan9/asm_plan9_386.s
deleted file mode 100644
index bc5cab1..0000000
--- a/vendor/golang.org/x/sys/plan9/asm_plan9_386.s
+++ /dev/null
@@ -1,30 +0,0 @@
-// Copyright 2009 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.
-
-#include "textflag.h"
-
-//
-// System call support for 386, Plan 9
-//
-
-// Just jump to package syscall's implementation for all these functions.
-// The runtime may know about them.
-
-TEXT ·Syscall(SB),NOSPLIT,$0-32
- JMP syscall·Syscall(SB)
-
-TEXT ·Syscall6(SB),NOSPLIT,$0-44
- JMP syscall·Syscall6(SB)
-
-TEXT ·RawSyscall(SB),NOSPLIT,$0-28
- JMP syscall·RawSyscall(SB)
-
-TEXT ·RawSyscall6(SB),NOSPLIT,$0-40
- JMP syscall·RawSyscall6(SB)
-
-TEXT ·seek(SB),NOSPLIT,$0-36
- JMP syscall·seek(SB)
-
-TEXT ·exit(SB),NOSPLIT,$4-4
- JMP syscall·exit(SB)
diff --git a/vendor/golang.org/x/sys/plan9/asm_plan9_amd64.s b/vendor/golang.org/x/sys/plan9/asm_plan9_amd64.s
deleted file mode 100644
index d3448e6..0000000
--- a/vendor/golang.org/x/sys/plan9/asm_plan9_amd64.s
+++ /dev/null
@@ -1,30 +0,0 @@
-// Copyright 2009 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.
-
-#include "textflag.h"
-
-//
-// System call support for amd64, Plan 9
-//
-
-// Just jump to package syscall's implementation for all these functions.
-// The runtime may know about them.
-
-TEXT ·Syscall(SB),NOSPLIT,$0-64
- JMP syscall·Syscall(SB)
-
-TEXT ·Syscall6(SB),NOSPLIT,$0-88
- JMP syscall·Syscall6(SB)
-
-TEXT ·RawSyscall(SB),NOSPLIT,$0-56
- JMP syscall·RawSyscall(SB)
-
-TEXT ·RawSyscall6(SB),NOSPLIT,$0-80
- JMP syscall·RawSyscall6(SB)
-
-TEXT ·seek(SB),NOSPLIT,$0-56
- JMP syscall·seek(SB)
-
-TEXT ·exit(SB),NOSPLIT,$8-8
- JMP syscall·exit(SB)
diff --git a/vendor/golang.org/x/sys/plan9/const_plan9.go b/vendor/golang.org/x/sys/plan9/const_plan9.go
deleted file mode 100644
index b4e85a3..0000000
--- a/vendor/golang.org/x/sys/plan9/const_plan9.go
+++ /dev/null
@@ -1,70 +0,0 @@
-package plan9
-
-// Plan 9 Constants
-
-// Open modes
-const (
- O_RDONLY = 0
- O_WRONLY = 1
- O_RDWR = 2
- O_TRUNC = 16
- O_CLOEXEC = 32
- O_EXCL = 0x1000
-)
-
-// Rfork flags
-const (
- RFNAMEG = 1 << 0
- RFENVG = 1 << 1
- RFFDG = 1 << 2
- RFNOTEG = 1 << 3
- RFPROC = 1 << 4
- RFMEM = 1 << 5
- RFNOWAIT = 1 << 6
- RFCNAMEG = 1 << 10
- RFCENVG = 1 << 11
- RFCFDG = 1 << 12
- RFREND = 1 << 13
- RFNOMNT = 1 << 14
-)
-
-// Qid.Type bits
-const (
- QTDIR = 0x80
- QTAPPEND = 0x40
- QTEXCL = 0x20
- QTMOUNT = 0x10
- QTAUTH = 0x08
- QTTMP = 0x04
- QTFILE = 0x00
-)
-
-// Dir.Mode bits
-const (
- DMDIR = 0x80000000
- DMAPPEND = 0x40000000
- DMEXCL = 0x20000000
- DMMOUNT = 0x10000000
- DMAUTH = 0x08000000
- DMTMP = 0x04000000
- DMREAD = 0x4
- DMWRITE = 0x2
- DMEXEC = 0x1
-)
-
-const (
- STATMAX = 65535
- ERRMAX = 128
- STATFIXLEN = 49
-)
-
-// Mount and bind flags
-const (
- MREPL = 0x0000
- MBEFORE = 0x0001
- MAFTER = 0x0002
- MORDER = 0x0003
- MCREATE = 0x0004
- MCACHE = 0x0010
- MMASK = 0x0017
-)
diff --git a/vendor/golang.org/x/sys/plan9/dir_plan9.go b/vendor/golang.org/x/sys/plan9/dir_plan9.go
deleted file mode 100644
index 0955e0c..0000000
--- a/vendor/golang.org/x/sys/plan9/dir_plan9.go
+++ /dev/null
@@ -1,212 +0,0 @@
-// Copyright 2012 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.
-
-// Plan 9 directory marshalling. See intro(5).
-
-package plan9
-
-import "errors"
-
-var (
- ErrShortStat = errors.New("stat buffer too short")
- ErrBadStat = errors.New("malformed stat buffer")
- ErrBadName = errors.New("bad character in file name")
-)
-
-// A Qid represents a 9P server's unique identification for a file.
-type Qid struct {
- Path uint64 // the file server's unique identification for the file
- Vers uint32 // version number for given Path
- Type uint8 // the type of the file (plan9.QTDIR for example)
-}
-
-// A Dir contains the metadata for a file.
-type Dir struct {
- // system-modified data
- Type uint16 // server type
- Dev uint32 // server subtype
-
- // file data
- Qid Qid // unique id from server
- Mode uint32 // permissions
- Atime uint32 // last read time
- Mtime uint32 // last write time
- Length int64 // file length
- Name string // last element of path
- Uid string // owner name
- Gid string // group name
- Muid string // last modifier name
-}
-
-var nullDir = Dir{
- Type: ^uint16(0),
- Dev: ^uint32(0),
- Qid: Qid{
- Path: ^uint64(0),
- Vers: ^uint32(0),
- Type: ^uint8(0),
- },
- Mode: ^uint32(0),
- Atime: ^uint32(0),
- Mtime: ^uint32(0),
- Length: ^int64(0),
-}
-
-// Null assigns special "don't touch" values to members of d to
-// avoid modifying them during plan9.Wstat.
-func (d *Dir) Null() { *d = nullDir }
-
-// Marshal encodes a 9P stat message corresponding to d into b
-//
-// If there isn't enough space in b for a stat message, ErrShortStat is returned.
-func (d *Dir) Marshal(b []byte) (n int, err error) {
- n = STATFIXLEN + len(d.Name) + len(d.Uid) + len(d.Gid) + len(d.Muid)
- if n > len(b) {
- return n, ErrShortStat
- }
-
- for _, c := range d.Name {
- if c == '/' {
- return n, ErrBadName
- }
- }
-
- b = pbit16(b, uint16(n)-2)
- b = pbit16(b, d.Type)
- b = pbit32(b, d.Dev)
- b = pbit8(b, d.Qid.Type)
- b = pbit32(b, d.Qid.Vers)
- b = pbit64(b, d.Qid.Path)
- b = pbit32(b, d.Mode)
- b = pbit32(b, d.Atime)
- b = pbit32(b, d.Mtime)
- b = pbit64(b, uint64(d.Length))
- b = pstring(b, d.Name)
- b = pstring(b, d.Uid)
- b = pstring(b, d.Gid)
- b = pstring(b, d.Muid)
-
- return n, nil
-}
-
-// UnmarshalDir decodes a single 9P stat message from b and returns the resulting Dir.
-//
-// If b is too small to hold a valid stat message, ErrShortStat is returned.
-//
-// If the stat message itself is invalid, ErrBadStat is returned.
-func UnmarshalDir(b []byte) (*Dir, error) {
- if len(b) < STATFIXLEN {
- return nil, ErrShortStat
- }
- size, buf := gbit16(b)
- if len(b) != int(size)+2 {
- return nil, ErrBadStat
- }
- b = buf
-
- var d Dir
- d.Type, b = gbit16(b)
- d.Dev, b = gbit32(b)
- d.Qid.Type, b = gbit8(b)
- d.Qid.Vers, b = gbit32(b)
- d.Qid.Path, b = gbit64(b)
- d.Mode, b = gbit32(b)
- d.Atime, b = gbit32(b)
- d.Mtime, b = gbit32(b)
-
- n, b := gbit64(b)
- d.Length = int64(n)
-
- var ok bool
- if d.Name, b, ok = gstring(b); !ok {
- return nil, ErrBadStat
- }
- if d.Uid, b, ok = gstring(b); !ok {
- return nil, ErrBadStat
- }
- if d.Gid, b, ok = gstring(b); !ok {
- return nil, ErrBadStat
- }
- if d.Muid, b, ok = gstring(b); !ok {
- return nil, ErrBadStat
- }
-
- return &d, nil
-}
-
-// pbit8 copies the 8-bit number v to b and returns the remaining slice of b.
-func pbit8(b []byte, v uint8) []byte {
- b[0] = byte(v)
- return b[1:]
-}
-
-// pbit16 copies the 16-bit number v to b in little-endian order and returns the remaining slice of b.
-func pbit16(b []byte, v uint16) []byte {
- b[0] = byte(v)
- b[1] = byte(v >> 8)
- return b[2:]
-}
-
-// pbit32 copies the 32-bit number v to b in little-endian order and returns the remaining slice of b.
-func pbit32(b []byte, v uint32) []byte {
- b[0] = byte(v)
- b[1] = byte(v >> 8)
- b[2] = byte(v >> 16)
- b[3] = byte(v >> 24)
- return b[4:]
-}
-
-// pbit64 copies the 64-bit number v to b in little-endian order and returns the remaining slice of b.
-func pbit64(b []byte, v uint64) []byte {
- b[0] = byte(v)
- b[1] = byte(v >> 8)
- b[2] = byte(v >> 16)
- b[3] = byte(v >> 24)
- b[4] = byte(v >> 32)
- b[5] = byte(v >> 40)
- b[6] = byte(v >> 48)
- b[7] = byte(v >> 56)
- return b[8:]
-}
-
-// pstring copies the string s to b, prepending it with a 16-bit length in little-endian order, and
-// returning the remaining slice of b..
-func pstring(b []byte, s string) []byte {
- b = pbit16(b, uint16(len(s)))
- n := copy(b, s)
- return b[n:]
-}
-
-// gbit8 reads an 8-bit number from b and returns it with the remaining slice of b.
-func gbit8(b []byte) (uint8, []byte) {
- return uint8(b[0]), b[1:]
-}
-
-// gbit16 reads a 16-bit number in little-endian order from b and returns it with the remaining slice of b.
-func gbit16(b []byte) (uint16, []byte) {
- return uint16(b[0]) | uint16(b[1])<<8, b[2:]
-}
-
-// gbit32 reads a 32-bit number in little-endian order from b and returns it with the remaining slice of b.
-func gbit32(b []byte) (uint32, []byte) {
- return uint32(b[0]) | uint32(b[1])<<8 | uint32(b[2])<<16 | uint32(b[3])<<24, b[4:]
-}
-
-// gbit64 reads a 64-bit number in little-endian order from b and returns it with the remaining slice of b.
-func gbit64(b []byte) (uint64, []byte) {
- lo := uint32(b[0]) | uint32(b[1])<<8 | uint32(b[2])<<16 | uint32(b[3])<<24
- hi := uint32(b[4]) | uint32(b[5])<<8 | uint32(b[6])<<16 | uint32(b[7])<<24
- return uint64(lo) | uint64(hi)<<32, b[8:]
-}
-
-// gstring reads a string from b, prefixed with a 16-bit length in little-endian order.
-// It returns the string with the remaining slice of b and a boolean. If the length is
-// greater than the number of bytes in b, the boolean will be false.
-func gstring(b []byte) (string, []byte, bool) {
- n, b := gbit16(b)
- if int(n) > len(b) {
- return "", b, false
- }
- return string(b[:n]), b[n:], true
-}
diff --git a/vendor/golang.org/x/sys/plan9/env_plan9.go b/vendor/golang.org/x/sys/plan9/env_plan9.go
deleted file mode 100644
index 25a96e7..0000000
--- a/vendor/golang.org/x/sys/plan9/env_plan9.go
+++ /dev/null
@@ -1,27 +0,0 @@
-// Copyright 2011 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.
-
-// Plan 9 environment variables.
-
-package plan9
-
-import (
- "syscall"
-)
-
-func Getenv(key string) (value string, found bool) {
- return syscall.Getenv(key)
-}
-
-func Setenv(key, value string) error {
- return syscall.Setenv(key, value)
-}
-
-func Clearenv() {
- syscall.Clearenv()
-}
-
-func Environ() []string {
- return syscall.Environ()
-}
diff --git a/vendor/golang.org/x/sys/plan9/env_unset.go b/vendor/golang.org/x/sys/plan9/env_unset.go
deleted file mode 100644
index c37fc26..0000000
--- a/vendor/golang.org/x/sys/plan9/env_unset.go
+++ /dev/null
@@ -1,14 +0,0 @@
-// Copyright 2014 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.
-
-// +build go1.4
-
-package plan9
-
-import "syscall"
-
-func Unsetenv(key string) error {
- // This was added in Go 1.4.
- return syscall.Unsetenv(key)
-}
diff --git a/vendor/golang.org/x/sys/plan9/errors_plan9.go b/vendor/golang.org/x/sys/plan9/errors_plan9.go
deleted file mode 100644
index 110cf6a..0000000
--- a/vendor/golang.org/x/sys/plan9/errors_plan9.go
+++ /dev/null
@@ -1,50 +0,0 @@
-// Copyright 2011 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 plan9
-
-import "syscall"
-
-// Constants
-const (
- // Invented values to support what package os expects.
- O_CREAT = 0x02000
- O_APPEND = 0x00400
- O_NOCTTY = 0x00000
- O_NONBLOCK = 0x00000
- O_SYNC = 0x00000
- O_ASYNC = 0x00000
-
- S_IFMT = 0x1f000
- S_IFIFO = 0x1000
- S_IFCHR = 0x2000
- S_IFDIR = 0x4000
- S_IFBLK = 0x6000
- S_IFREG = 0x8000
- S_IFLNK = 0xa000
- S_IFSOCK = 0xc000
-)
-
-// Errors
-var (
- EINVAL = syscall.NewError("bad arg in system call")
- ENOTDIR = syscall.NewError("not a directory")
- EISDIR = syscall.NewError("file is a directory")
- ENOENT = syscall.NewError("file does not exist")
- EEXIST = syscall.NewError("file already exists")
- EMFILE = syscall.NewError("no free file descriptors")
- EIO = syscall.NewError("i/o error")
- ENAMETOOLONG = syscall.NewError("file name too long")
- EINTR = syscall.NewError("interrupted")
- EPERM = syscall.NewError("permission denied")
- EBUSY = syscall.NewError("no free devices")
- ETIMEDOUT = syscall.NewError("connection timed out")
- EPLAN9 = syscall.NewError("not supported by plan 9")
-
- // The following errors do not correspond to any
- // Plan 9 system messages. Invented to support
- // what package os and others expect.
- EACCES = syscall.NewError("access permission denied")
- EAFNOSUPPORT = syscall.NewError("address family not supported by protocol")
-)
diff --git a/vendor/golang.org/x/sys/plan9/mkall.sh b/vendor/golang.org/x/sys/plan9/mkall.sh
deleted file mode 100755
index 9f73c60..0000000
--- a/vendor/golang.org/x/sys/plan9/mkall.sh
+++ /dev/null
@@ -1,138 +0,0 @@
-#!/usr/bin/env bash
-# Copyright 2009 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.
-
-# The plan9 package provides access to the raw system call
-# interface of the underlying operating system. Porting Go to
-# a new architecture/operating system combination requires
-# some manual effort, though there are tools that automate
-# much of the process. The auto-generated files have names
-# beginning with z.
-#
-# This script runs or (given -n) prints suggested commands to generate z files
-# for the current system. Running those commands is not automatic.
-# This script is documentation more than anything else.
-#
-# * asm_${GOOS}_${GOARCH}.s
-#
-# This hand-written assembly file implements system call dispatch.
-# There are three entry points:
-#
-# func Syscall(trap, a1, a2, a3 uintptr) (r1, r2, err uintptr);
-# func Syscall6(trap, a1, a2, a3, a4, a5, a6 uintptr) (r1, r2, err uintptr);
-# func RawSyscall(trap, a1, a2, a3 uintptr) (r1, r2, err uintptr);
-#
-# The first and second are the standard ones; they differ only in
-# how many arguments can be passed to the kernel.
-# The third is for low-level use by the ForkExec wrapper;
-# unlike the first two, it does not call into the scheduler to
-# let it know that a system call is running.
-#
-# * syscall_${GOOS}.go
-#
-# This hand-written Go file implements system calls that need
-# special handling and lists "//sys" comments giving prototypes
-# for ones that can be auto-generated. Mksyscall reads those
-# comments to generate the stubs.
-#
-# * syscall_${GOOS}_${GOARCH}.go
-#
-# Same as syscall_${GOOS}.go except that it contains code specific
-# to ${GOOS} on one particular architecture.
-#
-# * types_${GOOS}.c
-#
-# This hand-written C file includes standard C headers and then
-# creates typedef or enum names beginning with a dollar sign
-# (use of $ in variable names is a gcc extension). The hardest
-# part about preparing this file is figuring out which headers to
-# include and which symbols need to be #defined to get the
-# actual data structures that pass through to the kernel system calls.
-# Some C libraries present alternate versions for binary compatibility
-# and translate them on the way in and out of system calls, but
-# there is almost always a #define that can get the real ones.
-# See types_darwin.c and types_linux.c for examples.
-#
-# * zerror_${GOOS}_${GOARCH}.go
-#
-# This machine-generated file defines the system's error numbers,
-# error strings, and signal numbers. The generator is "mkerrors.sh".
-# Usually no arguments are needed, but mkerrors.sh will pass its
-# arguments on to godefs.
-#
-# * zsyscall_${GOOS}_${GOARCH}.go
-#
-# Generated by mksyscall.pl; see syscall_${GOOS}.go above.
-#
-# * zsysnum_${GOOS}_${GOARCH}.go
-#
-# Generated by mksysnum_${GOOS}.
-#
-# * ztypes_${GOOS}_${GOARCH}.go
-#
-# Generated by godefs; see types_${GOOS}.c above.
-
-GOOSARCH="${GOOS}_${GOARCH}"
-
-# defaults
-mksyscall="./mksyscall.pl"
-mkerrors="./mkerrors.sh"
-zerrors="zerrors_$GOOSARCH.go"
-mksysctl=""
-zsysctl="zsysctl_$GOOSARCH.go"
-mksysnum=
-mktypes=
-run="sh"
-
-case "$1" in
--syscalls)
- for i in zsyscall*go
- do
- sed 1q $i | sed 's;^// ;;' | sh > _$i && gofmt < _$i > $i
- rm _$i
- done
- exit 0
- ;;
--n)
- run="cat"
- shift
-esac
-
-case "$#" in
-0)
- ;;
-*)
- echo 'usage: mkall.sh [-n]' 1>&2
- exit 2
-esac
-
-case "$GOOSARCH" in
-_* | *_ | _)
- echo 'undefined $GOOS_$GOARCH:' "$GOOSARCH" 1>&2
- exit 1
- ;;
-plan9_386)
- mkerrors=
- mksyscall="./mksyscall.pl -l32 -plan9"
- mksysnum="./mksysnum_plan9.sh /n/sources/plan9/sys/src/libc/9syscall/sys.h"
- mktypes="XXX"
- ;;
-*)
- echo 'unrecognized $GOOS_$GOARCH: ' "$GOOSARCH" 1>&2
- exit 1
- ;;
-esac
-
-(
- if [ -n "$mkerrors" ]; then echo "$mkerrors |gofmt >$zerrors"; fi
- case "$GOOS" in
- plan9)
- syscall_goos="syscall_$GOOS.go"
- if [ -n "$mksyscall" ]; then echo "$mksyscall $syscall_goos syscall_$GOOSARCH.go |gofmt >zsyscall_$GOOSARCH.go"; fi
- ;;
- esac
- if [ -n "$mksysctl" ]; then echo "$mksysctl |gofmt >$zsysctl"; fi
- if [ -n "$mksysnum" ]; then echo "$mksysnum |gofmt >zsysnum_$GOOSARCH.go"; fi
- if [ -n "$mktypes" ]; then echo "$mktypes types_$GOOS.go |gofmt >ztypes_$GOOSARCH.go"; fi
-) | $run
diff --git a/vendor/golang.org/x/sys/plan9/mkerrors.sh b/vendor/golang.org/x/sys/plan9/mkerrors.sh
deleted file mode 100755
index 052c86d..0000000
--- a/vendor/golang.org/x/sys/plan9/mkerrors.sh
+++ /dev/null
@@ -1,246 +0,0 @@
-#!/usr/bin/env bash
-# Copyright 2009 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.
-
-# Generate Go code listing errors and other #defined constant
-# values (ENAMETOOLONG etc.), by asking the preprocessor
-# about the definitions.
-
-unset LANG
-export LC_ALL=C
-export LC_CTYPE=C
-
-CC=${CC:-gcc}
-
-uname=$(uname)
-
-includes='
-#include
-#include
-#include
-#include
-#include
-#include
-#include
-#include
-#include
-#include
-#include
-#include
-#include
-'
-
-ccflags="$@"
-
-# Write go tool cgo -godefs input.
-(
- echo package plan9
- echo
- echo '/*'
- indirect="includes_$(uname)"
- echo "${!indirect} $includes"
- echo '*/'
- echo 'import "C"'
- echo
- echo 'const ('
-
- # The gcc command line prints all the #defines
- # it encounters while processing the input
- echo "${!indirect} $includes" | $CC -x c - -E -dM $ccflags |
- awk '
- $1 != "#define" || $2 ~ /\(/ || $3 == "" {next}
-
- $2 ~ /^E([ABCD]X|[BIS]P|[SD]I|S|FL)$/ {next} # 386 registers
- $2 ~ /^(SIGEV_|SIGSTKSZ|SIGRT(MIN|MAX))/ {next}
- $2 ~ /^(SCM_SRCRT)$/ {next}
- $2 ~ /^(MAP_FAILED)$/ {next}
-
- $2 !~ /^ETH_/ &&
- $2 !~ /^EPROC_/ &&
- $2 !~ /^EQUIV_/ &&
- $2 !~ /^EXPR_/ &&
- $2 ~ /^E[A-Z0-9_]+$/ ||
- $2 ~ /^B[0-9_]+$/ ||
- $2 ~ /^V[A-Z0-9]+$/ ||
- $2 ~ /^CS[A-Z0-9]/ ||
- $2 ~ /^I(SIG|CANON|CRNL|EXTEN|MAXBEL|STRIP|UTF8)$/ ||
- $2 ~ /^IGN/ ||
- $2 ~ /^IX(ON|ANY|OFF)$/ ||
- $2 ~ /^IN(LCR|PCK)$/ ||
- $2 ~ /(^FLU?SH)|(FLU?SH$)/ ||
- $2 ~ /^C(LOCAL|READ)$/ ||
- $2 == "BRKINT" ||
- $2 == "HUPCL" ||
- $2 == "PENDIN" ||
- $2 == "TOSTOP" ||
- $2 ~ /^PAR/ ||
- $2 ~ /^SIG[^_]/ ||
- $2 ~ /^O[CNPFP][A-Z]+[^_][A-Z]+$/ ||
- $2 ~ /^IN_/ ||
- $2 ~ /^LOCK_(SH|EX|NB|UN)$/ ||
- $2 ~ /^(AF|SOCK|SO|SOL|IPPROTO|IP|IPV6|ICMP6|TCP|EVFILT|NOTE|EV|SHUT|PROT|MAP|PACKET|MSG|SCM|MCL|DT|MADV|PR)_/ ||
- $2 == "ICMPV6_FILTER" ||
- $2 == "SOMAXCONN" ||
- $2 == "NAME_MAX" ||
- $2 == "IFNAMSIZ" ||
- $2 ~ /^CTL_(MAXNAME|NET|QUERY)$/ ||
- $2 ~ /^SYSCTL_VERS/ ||
- $2 ~ /^(MS|MNT)_/ ||
- $2 ~ /^TUN(SET|GET|ATTACH|DETACH)/ ||
- $2 ~ /^(O|F|FD|NAME|S|PTRACE|PT)_/ ||
- $2 ~ /^LINUX_REBOOT_CMD_/ ||
- $2 ~ /^LINUX_REBOOT_MAGIC[12]$/ ||
- $2 !~ "NLA_TYPE_MASK" &&
- $2 ~ /^(NETLINK|NLM|NLMSG|NLA|IFA|IFAN|RT|RTCF|RTN|RTPROT|RTNH|ARPHRD|ETH_P)_/ ||
- $2 ~ /^SIOC/ ||
- $2 ~ /^TIOC/ ||
- $2 !~ "RTF_BITS" &&
- $2 ~ /^(IFF|IFT|NET_RT|RTM|RTF|RTV|RTA|RTAX)_/ ||
- $2 ~ /^BIOC/ ||
- $2 ~ /^RUSAGE_(SELF|CHILDREN|THREAD)/ ||
- $2 ~ /^RLIMIT_(AS|CORE|CPU|DATA|FSIZE|NOFILE|STACK)|RLIM_INFINITY/ ||
- $2 ~ /^PRIO_(PROCESS|PGRP|USER)/ ||
- $2 ~ /^CLONE_[A-Z_]+/ ||
- $2 !~ /^(BPF_TIMEVAL)$/ &&
- $2 ~ /^(BPF|DLT)_/ ||
- $2 !~ "WMESGLEN" &&
- $2 ~ /^W[A-Z0-9]+$/ {printf("\t%s = C.%s\n", $2, $2)}
- $2 ~ /^__WCOREFLAG$/ {next}
- $2 ~ /^__W[A-Z0-9]+$/ {printf("\t%s = C.%s\n", substr($2,3), $2)}
-
- {next}
- ' | sort
-
- echo ')'
-) >_const.go
-
-# Pull out the error names for later.
-errors=$(
- echo '#include ' | $CC -x c - -E -dM $ccflags |
- awk '$1=="#define" && $2 ~ /^E[A-Z0-9_]+$/ { print $2 }' |
- sort
-)
-
-# Pull out the signal names for later.
-signals=$(
- echo '#include ' | $CC -x c - -E -dM $ccflags |
- awk '$1=="#define" && $2 ~ /^SIG[A-Z0-9]+$/ { print $2 }' |
- egrep -v '(SIGSTKSIZE|SIGSTKSZ|SIGRT)' |
- sort
-)
-
-# Again, writing regexps to a file.
-echo '#include ' | $CC -x c - -E -dM $ccflags |
- awk '$1=="#define" && $2 ~ /^E[A-Z0-9_]+$/ { print "^\t" $2 "[ \t]*=" }' |
- sort >_error.grep
-echo '#include ' | $CC -x c - -E -dM $ccflags |
- awk '$1=="#define" && $2 ~ /^SIG[A-Z0-9]+$/ { print "^\t" $2 "[ \t]*=" }' |
- egrep -v '(SIGSTKSIZE|SIGSTKSZ|SIGRT)' |
- sort >_signal.grep
-
-echo '// mkerrors.sh' "$@"
-echo '// MACHINE GENERATED BY THE COMMAND ABOVE; DO NOT EDIT'
-echo
-go tool cgo -godefs -- "$@" _const.go >_error.out
-cat _error.out | grep -vf _error.grep | grep -vf _signal.grep
-echo
-echo '// Errors'
-echo 'const ('
-cat _error.out | grep -f _error.grep | sed 's/=\(.*\)/= Errno(\1)/'
-echo ')'
-
-echo
-echo '// Signals'
-echo 'const ('
-cat _error.out | grep -f _signal.grep | sed 's/=\(.*\)/= Signal(\1)/'
-echo ')'
-
-# Run C program to print error and syscall strings.
-(
- echo -E "
-#include
-#include
-#include
-#include
-#include
-#include
-
-#define nelem(x) (sizeof(x)/sizeof((x)[0]))
-
-enum { A = 'A', Z = 'Z', a = 'a', z = 'z' }; // avoid need for single quotes below
-
-int errors[] = {
-"
- for i in $errors
- do
- echo -E ' '$i,
- done
-
- echo -E "
-};
-
-int signals[] = {
-"
- for i in $signals
- do
- echo -E ' '$i,
- done
-
- # Use -E because on some systems bash builtin interprets \n itself.
- echo -E '
-};
-
-static int
-intcmp(const void *a, const void *b)
-{
- return *(int*)a - *(int*)b;
-}
-
-int
-main(void)
-{
- int i, j, e;
- char buf[1024], *p;
-
- printf("\n\n// Error table\n");
- printf("var errors = [...]string {\n");
- qsort(errors, nelem(errors), sizeof errors[0], intcmp);
- for(i=0; i 0 && errors[i-1] == e)
- continue;
- strcpy(buf, strerror(e));
- // lowercase first letter: Bad -> bad, but STREAM -> STREAM.
- if(A <= buf[0] && buf[0] <= Z && a <= buf[1] && buf[1] <= z)
- buf[0] += a - A;
- printf("\t%d: \"%s\",\n", e, buf);
- }
- printf("}\n\n");
-
- printf("\n\n// Signal table\n");
- printf("var signals = [...]string {\n");
- qsort(signals, nelem(signals), sizeof signals[0], intcmp);
- for(i=0; i 0 && signals[i-1] == e)
- continue;
- strcpy(buf, strsignal(e));
- // lowercase first letter: Bad -> bad, but STREAM -> STREAM.
- if(A <= buf[0] && buf[0] <= Z && a <= buf[1] && buf[1] <= z)
- buf[0] += a - A;
- // cut trailing : number.
- p = strrchr(buf, ":"[0]);
- if(p)
- *p = '\0';
- printf("\t%d: \"%s\",\n", e, buf);
- }
- printf("}\n\n");
-
- return 0;
-}
-
-'
-) >_errors.c
-
-$CC $ccflags -o _errors _errors.c && $GORUN ./_errors && rm -f _errors.c _errors _const.go _error.grep _signal.grep _error.out
diff --git a/vendor/golang.org/x/sys/plan9/mksyscall.pl b/vendor/golang.org/x/sys/plan9/mksyscall.pl
deleted file mode 100755
index ce8e1e4..0000000
--- a/vendor/golang.org/x/sys/plan9/mksyscall.pl
+++ /dev/null
@@ -1,319 +0,0 @@
-#!/usr/bin/env perl
-# Copyright 2009 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.
-
-# This program reads a file containing function prototypes
-# (like syscall_plan9.go) and generates system call bodies.
-# The prototypes are marked by lines beginning with "//sys"
-# and read like func declarations if //sys is replaced by func, but:
-# * The parameter lists must give a name for each argument.
-# This includes return parameters.
-# * The parameter lists must give a type for each argument:
-# the (x, y, z int) shorthand is not allowed.
-# * If the return parameter is an error number, it must be named errno.
-
-# A line beginning with //sysnb is like //sys, except that the
-# goroutine will not be suspended during the execution of the system
-# call. This must only be used for system calls which can never
-# block, as otherwise the system call could cause all goroutines to
-# hang.
-
-use strict;
-
-my $cmdline = "mksyscall.pl " . join(' ', @ARGV);
-my $errors = 0;
-my $_32bit = "";
-my $plan9 = 0;
-my $openbsd = 0;
-my $netbsd = 0;
-my $dragonfly = 0;
-my $nacl = 0;
-my $arm = 0; # 64-bit value should use (even, odd)-pair
-
-if($ARGV[0] eq "-b32") {
- $_32bit = "big-endian";
- shift;
-} elsif($ARGV[0] eq "-l32") {
- $_32bit = "little-endian";
- shift;
-}
-if($ARGV[0] eq "-plan9") {
- $plan9 = 1;
- shift;
-}
-if($ARGV[0] eq "-openbsd") {
- $openbsd = 1;
- shift;
-}
-if($ARGV[0] eq "-netbsd") {
- $netbsd = 1;
- shift;
-}
-if($ARGV[0] eq "-dragonfly") {
- $dragonfly = 1;
- shift;
-}
-if($ARGV[0] eq "-nacl") {
- $nacl = 1;
- shift;
-}
-if($ARGV[0] eq "-arm") {
- $arm = 1;
- shift;
-}
-
-if($ARGV[0] =~ /^-/) {
- print STDERR "usage: mksyscall.pl [-b32 | -l32] [file ...]\n";
- exit 1;
-}
-
-sub parseparamlist($) {
- my ($list) = @_;
- $list =~ s/^\s*//;
- $list =~ s/\s*$//;
- if($list eq "") {
- return ();
- }
- return split(/\s*,\s*/, $list);
-}
-
-sub parseparam($) {
- my ($p) = @_;
- if($p !~ /^(\S*) (\S*)$/) {
- print STDERR "$ARGV:$.: malformed parameter: $p\n";
- $errors = 1;
- return ("xx", "int");
- }
- return ($1, $2);
-}
-
-my $text = "";
-while(<>) {
- chomp;
- s/\s+/ /g;
- s/^\s+//;
- s/\s+$//;
- my $nonblock = /^\/\/sysnb /;
- next if !/^\/\/sys / && !$nonblock;
-
- # Line must be of the form
- # func Open(path string, mode int, perm int) (fd int, errno error)
- # Split into name, in params, out params.
- if(!/^\/\/sys(nb)? (\w+)\(([^()]*)\)\s*(?:\(([^()]+)\))?\s*(?:=\s*((?i)SYS_[A-Z0-9_]+))?$/) {
- print STDERR "$ARGV:$.: malformed //sys declaration\n";
- $errors = 1;
- next;
- }
- my ($func, $in, $out, $sysname) = ($2, $3, $4, $5);
-
- # Split argument lists on comma.
- my @in = parseparamlist($in);
- my @out = parseparamlist($out);
-
- # Try in vain to keep people from editing this file.
- # The theory is that they jump into the middle of the file
- # without reading the header.
- $text .= "// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT\n\n";
-
- # Go function header.
- my $out_decl = @out ? sprintf(" (%s)", join(', ', @out)) : "";
- $text .= sprintf "func %s(%s)%s {\n", $func, join(', ', @in), $out_decl;
-
- # Check if err return available
- my $errvar = "";
- foreach my $p (@out) {
- my ($name, $type) = parseparam($p);
- if($type eq "error") {
- $errvar = $name;
- last;
- }
- }
-
- # Prepare arguments to Syscall.
- my @args = ();
- my @uses = ();
- my $n = 0;
- foreach my $p (@in) {
- my ($name, $type) = parseparam($p);
- if($type =~ /^\*/) {
- push @args, "uintptr(unsafe.Pointer($name))";
- } elsif($type eq "string" && $errvar ne "") {
- $text .= "\tvar _p$n *byte\n";
- $text .= "\t_p$n, $errvar = BytePtrFromString($name)\n";
- $text .= "\tif $errvar != nil {\n\t\treturn\n\t}\n";
- push @args, "uintptr(unsafe.Pointer(_p$n))";
- push @uses, "use(unsafe.Pointer(_p$n))";
- $n++;
- } elsif($type eq "string") {
- print STDERR "$ARGV:$.: $func uses string arguments, but has no error return\n";
- $text .= "\tvar _p$n *byte\n";
- $text .= "\t_p$n, _ = BytePtrFromString($name)\n";
- push @args, "uintptr(unsafe.Pointer(_p$n))";
- push @uses, "use(unsafe.Pointer(_p$n))";
- $n++;
- } elsif($type =~ /^\[\](.*)/) {
- # Convert slice into pointer, length.
- # Have to be careful not to take address of &a[0] if len == 0:
- # pass dummy pointer in that case.
- # Used to pass nil, but some OSes or simulators reject write(fd, nil, 0).
- $text .= "\tvar _p$n unsafe.Pointer\n";
- $text .= "\tif len($name) > 0 {\n\t\t_p$n = unsafe.Pointer(\&${name}[0])\n\t}";
- $text .= " else {\n\t\t_p$n = unsafe.Pointer(&_zero)\n\t}";
- $text .= "\n";
- push @args, "uintptr(_p$n)", "uintptr(len($name))";
- $n++;
- } elsif($type eq "int64" && ($openbsd || $netbsd)) {
- push @args, "0";
- if($_32bit eq "big-endian") {
- push @args, "uintptr($name>>32)", "uintptr($name)";
- } elsif($_32bit eq "little-endian") {
- push @args, "uintptr($name)", "uintptr($name>>32)";
- } else {
- push @args, "uintptr($name)";
- }
- } elsif($type eq "int64" && $dragonfly) {
- if ($func !~ /^extp(read|write)/i) {
- push @args, "0";
- }
- if($_32bit eq "big-endian") {
- push @args, "uintptr($name>>32)", "uintptr($name)";
- } elsif($_32bit eq "little-endian") {
- push @args, "uintptr($name)", "uintptr($name>>32)";
- } else {
- push @args, "uintptr($name)";
- }
- } elsif($type eq "int64" && $_32bit ne "") {
- if(@args % 2 && $arm) {
- # arm abi specifies 64-bit argument uses
- # (even, odd) pair
- push @args, "0"
- }
- if($_32bit eq "big-endian") {
- push @args, "uintptr($name>>32)", "uintptr($name)";
- } else {
- push @args, "uintptr($name)", "uintptr($name>>32)";
- }
- } else {
- push @args, "uintptr($name)";
- }
- }
-
- # Determine which form to use; pad args with zeros.
- my $asm = "Syscall";
- if ($nonblock) {
- $asm = "RawSyscall";
- }
- if(@args <= 3) {
- while(@args < 3) {
- push @args, "0";
- }
- } elsif(@args <= 6) {
- $asm .= "6";
- while(@args < 6) {
- push @args, "0";
- }
- } elsif(@args <= 9) {
- $asm .= "9";
- while(@args < 9) {
- push @args, "0";
- }
- } else {
- print STDERR "$ARGV:$.: too many arguments to system call\n";
- }
-
- # System call number.
- if($sysname eq "") {
- $sysname = "SYS_$func";
- $sysname =~ s/([a-z])([A-Z])/${1}_$2/g; # turn FooBar into Foo_Bar
- $sysname =~ y/a-z/A-Z/;
- if($nacl) {
- $sysname =~ y/A-Z/a-z/;
- }
- }
-
- # Actual call.
- my $args = join(', ', @args);
- my $call = "$asm($sysname, $args)";
-
- # Assign return values.
- my $body = "";
- my @ret = ("_", "_", "_");
- my $do_errno = 0;
- for(my $i=0; $i<@out; $i++) {
- my $p = $out[$i];
- my ($name, $type) = parseparam($p);
- my $reg = "";
- if($name eq "err" && !$plan9) {
- $reg = "e1";
- $ret[2] = $reg;
- $do_errno = 1;
- } elsif($name eq "err" && $plan9) {
- $ret[0] = "r0";
- $ret[2] = "e1";
- next;
- } else {
- $reg = sprintf("r%d", $i);
- $ret[$i] = $reg;
- }
- if($type eq "bool") {
- $reg = "$reg != 0";
- }
- if($type eq "int64" && $_32bit ne "") {
- # 64-bit number in r1:r0 or r0:r1.
- if($i+2 > @out) {
- print STDERR "$ARGV:$.: not enough registers for int64 return\n";
- }
- if($_32bit eq "big-endian") {
- $reg = sprintf("int64(r%d)<<32 | int64(r%d)", $i, $i+1);
- } else {
- $reg = sprintf("int64(r%d)<<32 | int64(r%d)", $i+1, $i);
- }
- $ret[$i] = sprintf("r%d", $i);
- $ret[$i+1] = sprintf("r%d", $i+1);
- }
- if($reg ne "e1" || $plan9) {
- $body .= "\t$name = $type($reg)\n";
- }
- }
- if ($ret[0] eq "_" && $ret[1] eq "_" && $ret[2] eq "_") {
- $text .= "\t$call\n";
- } else {
- $text .= "\t$ret[0], $ret[1], $ret[2] := $call\n";
- }
- foreach my $use (@uses) {
- $text .= "\t$use\n";
- }
- $text .= $body;
-
- if ($plan9 && $ret[2] eq "e1") {
- $text .= "\tif int32(r0) == -1 {\n";
- $text .= "\t\terr = e1\n";
- $text .= "\t}\n";
- } elsif ($do_errno) {
- $text .= "\tif e1 != 0 {\n";
- $text .= "\t\terr = e1\n";
- $text .= "\t}\n";
- }
- $text .= "\treturn\n";
- $text .= "}\n\n";
-}
-
-chomp $text;
-chomp $text;
-
-if($errors) {
- exit 1;
-}
-
-print <= 10 {
- buf[i] = byte(val%10 + '0')
- i--
- val /= 10
- }
- buf[i] = byte(val + '0')
- return string(buf[i:])
-}
diff --git a/vendor/golang.org/x/sys/plan9/syscall.go b/vendor/golang.org/x/sys/plan9/syscall.go
deleted file mode 100644
index df6f8c5..0000000
--- a/vendor/golang.org/x/sys/plan9/syscall.go
+++ /dev/null
@@ -1,74 +0,0 @@
-// Copyright 2009 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.
-
-// +build plan9
-
-// Package plan9 contains an interface to the low-level operating system
-// primitives. OS details vary depending on the underlying system, and
-// by default, godoc will display the OS-specific documentation for the current
-// system. If you want godoc to display documentation for another
-// system, set $GOOS and $GOARCH to the desired system. For example, if
-// you want to view documentation for freebsd/arm on linux/amd64, set $GOOS
-// to freebsd and $GOARCH to arm.
-// The primary use of this package is inside other packages that provide a more
-// portable interface to the system, such as "os", "time" and "net". Use
-// those packages rather than this one if you can.
-// For details of the functions and data types in this package consult
-// the manuals for the appropriate operating system.
-// These calls return err == nil to indicate success; otherwise
-// err represents an operating system error describing the failure and
-// holds a value of type syscall.ErrorString.
-package plan9 // import "golang.org/x/sys/plan9"
-
-import "unsafe"
-
-// ByteSliceFromString returns a NUL-terminated slice of bytes
-// containing the text of s. If s contains a NUL byte at any
-// location, it returns (nil, EINVAL).
-func ByteSliceFromString(s string) ([]byte, error) {
- for i := 0; i < len(s); i++ {
- if s[i] == 0 {
- return nil, EINVAL
- }
- }
- a := make([]byte, len(s)+1)
- copy(a, s)
- return a, nil
-}
-
-// BytePtrFromString returns a pointer to a NUL-terminated array of
-// bytes containing the text of s. If s contains a NUL byte at any
-// location, it returns (nil, EINVAL).
-func BytePtrFromString(s string) (*byte, error) {
- a, err := ByteSliceFromString(s)
- if err != nil {
- return nil, err
- }
- return &a[0], nil
-}
-
-// Single-word zero for use when we need a valid pointer to 0 bytes.
-// See mksyscall.pl.
-var _zero uintptr
-
-func (ts *Timespec) Unix() (sec int64, nsec int64) {
- return int64(ts.Sec), int64(ts.Nsec)
-}
-
-func (tv *Timeval) Unix() (sec int64, nsec int64) {
- return int64(tv.Sec), int64(tv.Usec) * 1000
-}
-
-func (ts *Timespec) Nano() int64 {
- return int64(ts.Sec)*1e9 + int64(ts.Nsec)
-}
-
-func (tv *Timeval) Nano() int64 {
- return int64(tv.Sec)*1e9 + int64(tv.Usec)*1000
-}
-
-// use is a no-op, but the compiler cannot see that it is.
-// Calling use(p) ensures that p is kept live until that point.
-//go:noescape
-func use(p unsafe.Pointer)
diff --git a/vendor/golang.org/x/sys/plan9/syscall_plan9.go b/vendor/golang.org/x/sys/plan9/syscall_plan9.go
deleted file mode 100644
index d39d07d..0000000
--- a/vendor/golang.org/x/sys/plan9/syscall_plan9.go
+++ /dev/null
@@ -1,349 +0,0 @@
-// Copyright 2011 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.
-
-// Plan 9 system calls.
-// This file is compiled as ordinary Go code,
-// but it is also input to mksyscall,
-// which parses the //sys lines and generates system call stubs.
-// Note that sometimes we use a lowercase //sys name and
-// wrap it in our own nicer implementation.
-
-package plan9
-
-import (
- "syscall"
- "unsafe"
-)
-
-// A Note is a string describing a process note.
-// It implements the os.Signal interface.
-type Note string
-
-func (n Note) Signal() {}
-
-func (n Note) String() string {
- return string(n)
-}
-
-var (
- Stdin = 0
- Stdout = 1
- Stderr = 2
-)
-
-// For testing: clients can set this flag to force
-// creation of IPv6 sockets to return EAFNOSUPPORT.
-var SocketDisableIPv6 bool
-
-func Syscall(trap, a1, a2, a3 uintptr) (r1, r2 uintptr, err syscall.ErrorString)
-func Syscall6(trap, a1, a2, a3, a4, a5, a6 uintptr) (r1, r2 uintptr, err syscall.ErrorString)
-func RawSyscall(trap, a1, a2, a3 uintptr) (r1, r2, err uintptr)
-func RawSyscall6(trap, a1, a2, a3, a4, a5, a6 uintptr) (r1, r2, err uintptr)
-
-func atoi(b []byte) (n uint) {
- n = 0
- for i := 0; i < len(b); i++ {
- n = n*10 + uint(b[i]-'0')
- }
- return
-}
-
-func cstring(s []byte) string {
- for i := range s {
- if s[i] == 0 {
- return string(s[0:i])
- }
- }
- return string(s)
-}
-
-func errstr() string {
- var buf [ERRMAX]byte
-
- RawSyscall(SYS_ERRSTR, uintptr(unsafe.Pointer(&buf[0])), uintptr(len(buf)), 0)
-
- buf[len(buf)-1] = 0
- return cstring(buf[:])
-}
-
-// Implemented in assembly to import from runtime.
-func exit(code int)
-
-func Exit(code int) { exit(code) }
-
-func readnum(path string) (uint, error) {
- var b [12]byte
-
- fd, e := Open(path, O_RDONLY)
- if e != nil {
- return 0, e
- }
- defer Close(fd)
-
- n, e := Pread(fd, b[:], 0)
-
- if e != nil {
- return 0, e
- }
-
- m := 0
- for ; m < n && b[m] == ' '; m++ {
- }
-
- return atoi(b[m : n-1]), nil
-}
-
-func Getpid() (pid int) {
- n, _ := readnum("#c/pid")
- return int(n)
-}
-
-func Getppid() (ppid int) {
- n, _ := readnum("#c/ppid")
- return int(n)
-}
-
-func Read(fd int, p []byte) (n int, err error) {
- return Pread(fd, p, -1)
-}
-
-func Write(fd int, p []byte) (n int, err error) {
- return Pwrite(fd, p, -1)
-}
-
-var ioSync int64
-
-//sys fd2path(fd int, buf []byte) (err error)
-func Fd2path(fd int) (path string, err error) {
- var buf [512]byte
-
- e := fd2path(fd, buf[:])
- if e != nil {
- return "", e
- }
- return cstring(buf[:]), nil
-}
-
-//sys pipe(p *[2]int32) (err error)
-func Pipe(p []int) (err error) {
- if len(p) != 2 {
- return syscall.ErrorString("bad arg in system call")
- }
- var pp [2]int32
- err = pipe(&pp)
- p[0] = int(pp[0])
- p[1] = int(pp[1])
- return
-}
-
-// Underlying system call writes to newoffset via pointer.
-// Implemented in assembly to avoid allocation.
-func seek(placeholder uintptr, fd int, offset int64, whence int) (newoffset int64, err string)
-
-func Seek(fd int, offset int64, whence int) (newoffset int64, err error) {
- newoffset, e := seek(0, fd, offset, whence)
-
- if newoffset == -1 {
- err = syscall.ErrorString(e)
- }
- return
-}
-
-func Mkdir(path string, mode uint32) (err error) {
- fd, err := Create(path, O_RDONLY, DMDIR|mode)
-
- if fd != -1 {
- Close(fd)
- }
-
- return
-}
-
-type Waitmsg struct {
- Pid int
- Time [3]uint32
- Msg string
-}
-
-func (w Waitmsg) Exited() bool { return true }
-func (w Waitmsg) Signaled() bool { return false }
-
-func (w Waitmsg) ExitStatus() int {
- if len(w.Msg) == 0 {
- // a normal exit returns no message
- return 0
- }
- return 1
-}
-
-//sys await(s []byte) (n int, err error)
-func Await(w *Waitmsg) (err error) {
- var buf [512]byte
- var f [5][]byte
-
- n, err := await(buf[:])
-
- if err != nil || w == nil {
- return
- }
-
- nf := 0
- p := 0
- for i := 0; i < n && nf < len(f)-1; i++ {
- if buf[i] == ' ' {
- f[nf] = buf[p:i]
- p = i + 1
- nf++
- }
- }
- f[nf] = buf[p:]
- nf++
-
- if nf != len(f) {
- return syscall.ErrorString("invalid wait message")
- }
- w.Pid = int(atoi(f[0]))
- w.Time[0] = uint32(atoi(f[1]))
- w.Time[1] = uint32(atoi(f[2]))
- w.Time[2] = uint32(atoi(f[3]))
- w.Msg = cstring(f[4])
- if w.Msg == "''" {
- // await() returns '' for no error
- w.Msg = ""
- }
- return
-}
-
-func Unmount(name, old string) (err error) {
- fixwd()
- oldp, err := BytePtrFromString(old)
- if err != nil {
- return err
- }
- oldptr := uintptr(unsafe.Pointer(oldp))
-
- var r0 uintptr
- var e syscall.ErrorString
-
- // bind(2) man page: If name is zero, everything bound or mounted upon old is unbound or unmounted.
- if name == "" {
- r0, _, e = Syscall(SYS_UNMOUNT, _zero, oldptr, 0)
- } else {
- namep, err := BytePtrFromString(name)
- if err != nil {
- return err
- }
- r0, _, e = Syscall(SYS_UNMOUNT, uintptr(unsafe.Pointer(namep)), oldptr, 0)
- }
-
- if int32(r0) == -1 {
- err = e
- }
- return
-}
-
-func Fchdir(fd int) (err error) {
- path, err := Fd2path(fd)
-
- if err != nil {
- return
- }
-
- return Chdir(path)
-}
-
-type Timespec struct {
- Sec int32
- Nsec int32
-}
-
-type Timeval struct {
- Sec int32
- Usec int32
-}
-
-func NsecToTimeval(nsec int64) (tv Timeval) {
- nsec += 999 // round up to microsecond
- tv.Usec = int32(nsec % 1e9 / 1e3)
- tv.Sec = int32(nsec / 1e9)
- return
-}
-
-func nsec() int64 {
- var scratch int64
-
- r0, _, _ := Syscall(SYS_NSEC, uintptr(unsafe.Pointer(&scratch)), 0, 0)
- // TODO(aram): remove hack after I fix _nsec in the pc64 kernel.
- if r0 == 0 {
- return scratch
- }
- return int64(r0)
-}
-
-func Gettimeofday(tv *Timeval) error {
- nsec := nsec()
- *tv = NsecToTimeval(nsec)
- return nil
-}
-
-func Getpagesize() int { return 0x1000 }
-
-func Getegid() (egid int) { return -1 }
-func Geteuid() (euid int) { return -1 }
-func Getgid() (gid int) { return -1 }
-func Getuid() (uid int) { return -1 }
-
-func Getgroups() (gids []int, err error) {
- return make([]int, 0), nil
-}
-
-//sys open(path string, mode int) (fd int, err error)
-func Open(path string, mode int) (fd int, err error) {
- fixwd()
- return open(path, mode)
-}
-
-//sys create(path string, mode int, perm uint32) (fd int, err error)
-func Create(path string, mode int, perm uint32) (fd int, err error) {
- fixwd()
- return create(path, mode, perm)
-}
-
-//sys remove(path string) (err error)
-func Remove(path string) error {
- fixwd()
- return remove(path)
-}
-
-//sys stat(path string, edir []byte) (n int, err error)
-func Stat(path string, edir []byte) (n int, err error) {
- fixwd()
- return stat(path, edir)
-}
-
-//sys bind(name string, old string, flag int) (err error)
-func Bind(name string, old string, flag int) (err error) {
- fixwd()
- return bind(name, old, flag)
-}
-
-//sys mount(fd int, afd int, old string, flag int, aname string) (err error)
-func Mount(fd int, afd int, old string, flag int, aname string) (err error) {
- fixwd()
- return mount(fd, afd, old, flag, aname)
-}
-
-//sys wstat(path string, edir []byte) (err error)
-func Wstat(path string, edir []byte) (err error) {
- fixwd()
- return wstat(path, edir)
-}
-
-//sys chdir(path string) (err error)
-//sys Dup(oldfd int, newfd int) (fd int, err error)
-//sys Pread(fd int, p []byte, offset int64) (n int, err error)
-//sys Pwrite(fd int, p []byte, offset int64) (n int, err error)
-//sys Close(fd int) (err error)
-//sys Fstat(fd int, edir []byte) (n int, err error)
-//sys Fwstat(fd int, edir []byte) (err error)
diff --git a/vendor/golang.org/x/sys/plan9/syscall_test.go b/vendor/golang.org/x/sys/plan9/syscall_test.go
deleted file mode 100644
index 8f829ba..0000000
--- a/vendor/golang.org/x/sys/plan9/syscall_test.go
+++ /dev/null
@@ -1,33 +0,0 @@
-// Copyright 2013 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.
-
-// +build plan9
-
-package plan9_test
-
-import (
- "testing"
-
- "golang.org/x/sys/plan9"
-)
-
-func testSetGetenv(t *testing.T, key, value string) {
- err := plan9.Setenv(key, value)
- if err != nil {
- t.Fatalf("Setenv failed to set %q: %v", value, err)
- }
- newvalue, found := plan9.Getenv(key)
- if !found {
- t.Fatalf("Getenv failed to find %v variable (want value %q)", key, value)
- }
- if newvalue != value {
- t.Fatalf("Getenv(%v) = %q; want %q", key, newvalue, value)
- }
-}
-
-func TestEnv(t *testing.T) {
- testSetGetenv(t, "TESTENV", "AVALUE")
- // make sure TESTENV gets set to "", not deleted
- testSetGetenv(t, "TESTENV", "")
-}
diff --git a/vendor/golang.org/x/sys/plan9/zsyscall_plan9_386.go b/vendor/golang.org/x/sys/plan9/zsyscall_plan9_386.go
deleted file mode 100644
index b35598a..0000000
--- a/vendor/golang.org/x/sys/plan9/zsyscall_plan9_386.go
+++ /dev/null
@@ -1,292 +0,0 @@
-// mksyscall.pl -l32 -plan9 syscall_plan9.go
-// MACHINE GENERATED BY THE COMMAND ABOVE; DO NOT EDIT
-
-package plan9
-
-import "unsafe"
-
-// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT
-
-func fd2path(fd int, buf []byte) (err error) {
- var _p0 unsafe.Pointer
- if len(buf) > 0 {
- _p0 = unsafe.Pointer(&buf[0])
- } else {
- _p0 = unsafe.Pointer(&_zero)
- }
- r0, _, e1 := Syscall(SYS_FD2PATH, uintptr(fd), uintptr(_p0), uintptr(len(buf)))
- if int32(r0) == -1 {
- err = e1
- }
- return
-}
-
-// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT
-
-func pipe(p *[2]int32) (err error) {
- r0, _, e1 := Syscall(SYS_PIPE, uintptr(unsafe.Pointer(p)), 0, 0)
- if int32(r0) == -1 {
- err = e1
- }
- return
-}
-
-// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT
-
-func await(s []byte) (n int, err error) {
- var _p0 unsafe.Pointer
- if len(s) > 0 {
- _p0 = unsafe.Pointer(&s[0])
- } else {
- _p0 = unsafe.Pointer(&_zero)
- }
- r0, _, e1 := Syscall(SYS_AWAIT, uintptr(_p0), uintptr(len(s)), 0)
- n = int(r0)
- if int32(r0) == -1 {
- err = e1
- }
- return
-}
-
-// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT
-
-func open(path string, mode int) (fd int, err error) {
- var _p0 *byte
- _p0, err = BytePtrFromString(path)
- if err != nil {
- return
- }
- r0, _, e1 := Syscall(SYS_OPEN, uintptr(unsafe.Pointer(_p0)), uintptr(mode), 0)
- use(unsafe.Pointer(_p0))
- fd = int(r0)
- if int32(r0) == -1 {
- err = e1
- }
- return
-}
-
-// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT
-
-func create(path string, mode int, perm uint32) (fd int, err error) {
- var _p0 *byte
- _p0, err = BytePtrFromString(path)
- if err != nil {
- return
- }
- r0, _, e1 := Syscall(SYS_CREATE, uintptr(unsafe.Pointer(_p0)), uintptr(mode), uintptr(perm))
- use(unsafe.Pointer(_p0))
- fd = int(r0)
- if int32(r0) == -1 {
- err = e1
- }
- return
-}
-
-// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT
-
-func remove(path string) (err error) {
- var _p0 *byte
- _p0, err = BytePtrFromString(path)
- if err != nil {
- return
- }
- r0, _, e1 := Syscall(SYS_REMOVE, uintptr(unsafe.Pointer(_p0)), 0, 0)
- use(unsafe.Pointer(_p0))
- if int32(r0) == -1 {
- err = e1
- }
- return
-}
-
-// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT
-
-func stat(path string, edir []byte) (n int, err error) {
- var _p0 *byte
- _p0, err = BytePtrFromString(path)
- if err != nil {
- return
- }
- var _p1 unsafe.Pointer
- if len(edir) > 0 {
- _p1 = unsafe.Pointer(&edir[0])
- } else {
- _p1 = unsafe.Pointer(&_zero)
- }
- r0, _, e1 := Syscall(SYS_STAT, uintptr(unsafe.Pointer(_p0)), uintptr(_p1), uintptr(len(edir)))
- use(unsafe.Pointer(_p0))
- n = int(r0)
- if int32(r0) == -1 {
- err = e1
- }
- return
-}
-
-// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT
-
-func bind(name string, old string, flag int) (err error) {
- var _p0 *byte
- _p0, err = BytePtrFromString(name)
- if err != nil {
- return
- }
- var _p1 *byte
- _p1, err = BytePtrFromString(old)
- if err != nil {
- return
- }
- r0, _, e1 := Syscall(SYS_BIND, uintptr(unsafe.Pointer(_p0)), uintptr(unsafe.Pointer(_p1)), uintptr(flag))
- use(unsafe.Pointer(_p0))
- use(unsafe.Pointer(_p1))
- if int32(r0) == -1 {
- err = e1
- }
- return
-}
-
-// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT
-
-func mount(fd int, afd int, old string, flag int, aname string) (err error) {
- var _p0 *byte
- _p0, err = BytePtrFromString(old)
- if err != nil {
- return
- }
- var _p1 *byte
- _p1, err = BytePtrFromString(aname)
- if err != nil {
- return
- }
- r0, _, e1 := Syscall6(SYS_MOUNT, uintptr(fd), uintptr(afd), uintptr(unsafe.Pointer(_p0)), uintptr(flag), uintptr(unsafe.Pointer(_p1)), 0)
- use(unsafe.Pointer(_p0))
- use(unsafe.Pointer(_p1))
- if int32(r0) == -1 {
- err = e1
- }
- return
-}
-
-// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT
-
-func wstat(path string, edir []byte) (err error) {
- var _p0 *byte
- _p0, err = BytePtrFromString(path)
- if err != nil {
- return
- }
- var _p1 unsafe.Pointer
- if len(edir) > 0 {
- _p1 = unsafe.Pointer(&edir[0])
- } else {
- _p1 = unsafe.Pointer(&_zero)
- }
- r0, _, e1 := Syscall(SYS_WSTAT, uintptr(unsafe.Pointer(_p0)), uintptr(_p1), uintptr(len(edir)))
- use(unsafe.Pointer(_p0))
- if int32(r0) == -1 {
- err = e1
- }
- return
-}
-
-// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT
-
-func chdir(path string) (err error) {
- var _p0 *byte
- _p0, err = BytePtrFromString(path)
- if err != nil {
- return
- }
- r0, _, e1 := Syscall(SYS_CHDIR, uintptr(unsafe.Pointer(_p0)), 0, 0)
- use(unsafe.Pointer(_p0))
- if int32(r0) == -1 {
- err = e1
- }
- return
-}
-
-// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT
-
-func Dup(oldfd int, newfd int) (fd int, err error) {
- r0, _, e1 := Syscall(SYS_DUP, uintptr(oldfd), uintptr(newfd), 0)
- fd = int(r0)
- if int32(r0) == -1 {
- err = e1
- }
- return
-}
-
-// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT
-
-func Pread(fd int, p []byte, offset int64) (n int, err error) {
- var _p0 unsafe.Pointer
- if len(p) > 0 {
- _p0 = unsafe.Pointer(&p[0])
- } else {
- _p0 = unsafe.Pointer(&_zero)
- }
- r0, _, e1 := Syscall6(SYS_PREAD, uintptr(fd), uintptr(_p0), uintptr(len(p)), uintptr(offset), uintptr(offset>>32), 0)
- n = int(r0)
- if int32(r0) == -1 {
- err = e1
- }
- return
-}
-
-// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT
-
-func Pwrite(fd int, p []byte, offset int64) (n int, err error) {
- var _p0 unsafe.Pointer
- if len(p) > 0 {
- _p0 = unsafe.Pointer(&p[0])
- } else {
- _p0 = unsafe.Pointer(&_zero)
- }
- r0, _, e1 := Syscall6(SYS_PWRITE, uintptr(fd), uintptr(_p0), uintptr(len(p)), uintptr(offset), uintptr(offset>>32), 0)
- n = int(r0)
- if int32(r0) == -1 {
- err = e1
- }
- return
-}
-
-// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT
-
-func Close(fd int) (err error) {
- r0, _, e1 := Syscall(SYS_CLOSE, uintptr(fd), 0, 0)
- if int32(r0) == -1 {
- err = e1
- }
- return
-}
-
-// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT
-
-func Fstat(fd int, edir []byte) (n int, err error) {
- var _p0 unsafe.Pointer
- if len(edir) > 0 {
- _p0 = unsafe.Pointer(&edir[0])
- } else {
- _p0 = unsafe.Pointer(&_zero)
- }
- r0, _, e1 := Syscall(SYS_FSTAT, uintptr(fd), uintptr(_p0), uintptr(len(edir)))
- n = int(r0)
- if int32(r0) == -1 {
- err = e1
- }
- return
-}
-
-// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT
-
-func Fwstat(fd int, edir []byte) (err error) {
- var _p0 unsafe.Pointer
- if len(edir) > 0 {
- _p0 = unsafe.Pointer(&edir[0])
- } else {
- _p0 = unsafe.Pointer(&_zero)
- }
- r0, _, e1 := Syscall(SYS_FWSTAT, uintptr(fd), uintptr(_p0), uintptr(len(edir)))
- if int32(r0) == -1 {
- err = e1
- }
- return
-}
diff --git a/vendor/golang.org/x/sys/plan9/zsyscall_plan9_amd64.go b/vendor/golang.org/x/sys/plan9/zsyscall_plan9_amd64.go
deleted file mode 100644
index b35598a..0000000
--- a/vendor/golang.org/x/sys/plan9/zsyscall_plan9_amd64.go
+++ /dev/null
@@ -1,292 +0,0 @@
-// mksyscall.pl -l32 -plan9 syscall_plan9.go
-// MACHINE GENERATED BY THE COMMAND ABOVE; DO NOT EDIT
-
-package plan9
-
-import "unsafe"
-
-// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT
-
-func fd2path(fd int, buf []byte) (err error) {
- var _p0 unsafe.Pointer
- if len(buf) > 0 {
- _p0 = unsafe.Pointer(&buf[0])
- } else {
- _p0 = unsafe.Pointer(&_zero)
- }
- r0, _, e1 := Syscall(SYS_FD2PATH, uintptr(fd), uintptr(_p0), uintptr(len(buf)))
- if int32(r0) == -1 {
- err = e1
- }
- return
-}
-
-// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT
-
-func pipe(p *[2]int32) (err error) {
- r0, _, e1 := Syscall(SYS_PIPE, uintptr(unsafe.Pointer(p)), 0, 0)
- if int32(r0) == -1 {
- err = e1
- }
- return
-}
-
-// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT
-
-func await(s []byte) (n int, err error) {
- var _p0 unsafe.Pointer
- if len(s) > 0 {
- _p0 = unsafe.Pointer(&s[0])
- } else {
- _p0 = unsafe.Pointer(&_zero)
- }
- r0, _, e1 := Syscall(SYS_AWAIT, uintptr(_p0), uintptr(len(s)), 0)
- n = int(r0)
- if int32(r0) == -1 {
- err = e1
- }
- return
-}
-
-// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT
-
-func open(path string, mode int) (fd int, err error) {
- var _p0 *byte
- _p0, err = BytePtrFromString(path)
- if err != nil {
- return
- }
- r0, _, e1 := Syscall(SYS_OPEN, uintptr(unsafe.Pointer(_p0)), uintptr(mode), 0)
- use(unsafe.Pointer(_p0))
- fd = int(r0)
- if int32(r0) == -1 {
- err = e1
- }
- return
-}
-
-// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT
-
-func create(path string, mode int, perm uint32) (fd int, err error) {
- var _p0 *byte
- _p0, err = BytePtrFromString(path)
- if err != nil {
- return
- }
- r0, _, e1 := Syscall(SYS_CREATE, uintptr(unsafe.Pointer(_p0)), uintptr(mode), uintptr(perm))
- use(unsafe.Pointer(_p0))
- fd = int(r0)
- if int32(r0) == -1 {
- err = e1
- }
- return
-}
-
-// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT
-
-func remove(path string) (err error) {
- var _p0 *byte
- _p0, err = BytePtrFromString(path)
- if err != nil {
- return
- }
- r0, _, e1 := Syscall(SYS_REMOVE, uintptr(unsafe.Pointer(_p0)), 0, 0)
- use(unsafe.Pointer(_p0))
- if int32(r0) == -1 {
- err = e1
- }
- return
-}
-
-// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT
-
-func stat(path string, edir []byte) (n int, err error) {
- var _p0 *byte
- _p0, err = BytePtrFromString(path)
- if err != nil {
- return
- }
- var _p1 unsafe.Pointer
- if len(edir) > 0 {
- _p1 = unsafe.Pointer(&edir[0])
- } else {
- _p1 = unsafe.Pointer(&_zero)
- }
- r0, _, e1 := Syscall(SYS_STAT, uintptr(unsafe.Pointer(_p0)), uintptr(_p1), uintptr(len(edir)))
- use(unsafe.Pointer(_p0))
- n = int(r0)
- if int32(r0) == -1 {
- err = e1
- }
- return
-}
-
-// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT
-
-func bind(name string, old string, flag int) (err error) {
- var _p0 *byte
- _p0, err = BytePtrFromString(name)
- if err != nil {
- return
- }
- var _p1 *byte
- _p1, err = BytePtrFromString(old)
- if err != nil {
- return
- }
- r0, _, e1 := Syscall(SYS_BIND, uintptr(unsafe.Pointer(_p0)), uintptr(unsafe.Pointer(_p1)), uintptr(flag))
- use(unsafe.Pointer(_p0))
- use(unsafe.Pointer(_p1))
- if int32(r0) == -1 {
- err = e1
- }
- return
-}
-
-// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT
-
-func mount(fd int, afd int, old string, flag int, aname string) (err error) {
- var _p0 *byte
- _p0, err = BytePtrFromString(old)
- if err != nil {
- return
- }
- var _p1 *byte
- _p1, err = BytePtrFromString(aname)
- if err != nil {
- return
- }
- r0, _, e1 := Syscall6(SYS_MOUNT, uintptr(fd), uintptr(afd), uintptr(unsafe.Pointer(_p0)), uintptr(flag), uintptr(unsafe.Pointer(_p1)), 0)
- use(unsafe.Pointer(_p0))
- use(unsafe.Pointer(_p1))
- if int32(r0) == -1 {
- err = e1
- }
- return
-}
-
-// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT
-
-func wstat(path string, edir []byte) (err error) {
- var _p0 *byte
- _p0, err = BytePtrFromString(path)
- if err != nil {
- return
- }
- var _p1 unsafe.Pointer
- if len(edir) > 0 {
- _p1 = unsafe.Pointer(&edir[0])
- } else {
- _p1 = unsafe.Pointer(&_zero)
- }
- r0, _, e1 := Syscall(SYS_WSTAT, uintptr(unsafe.Pointer(_p0)), uintptr(_p1), uintptr(len(edir)))
- use(unsafe.Pointer(_p0))
- if int32(r0) == -1 {
- err = e1
- }
- return
-}
-
-// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT
-
-func chdir(path string) (err error) {
- var _p0 *byte
- _p0, err = BytePtrFromString(path)
- if err != nil {
- return
- }
- r0, _, e1 := Syscall(SYS_CHDIR, uintptr(unsafe.Pointer(_p0)), 0, 0)
- use(unsafe.Pointer(_p0))
- if int32(r0) == -1 {
- err = e1
- }
- return
-}
-
-// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT
-
-func Dup(oldfd int, newfd int) (fd int, err error) {
- r0, _, e1 := Syscall(SYS_DUP, uintptr(oldfd), uintptr(newfd), 0)
- fd = int(r0)
- if int32(r0) == -1 {
- err = e1
- }
- return
-}
-
-// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT
-
-func Pread(fd int, p []byte, offset int64) (n int, err error) {
- var _p0 unsafe.Pointer
- if len(p) > 0 {
- _p0 = unsafe.Pointer(&p[0])
- } else {
- _p0 = unsafe.Pointer(&_zero)
- }
- r0, _, e1 := Syscall6(SYS_PREAD, uintptr(fd), uintptr(_p0), uintptr(len(p)), uintptr(offset), uintptr(offset>>32), 0)
- n = int(r0)
- if int32(r0) == -1 {
- err = e1
- }
- return
-}
-
-// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT
-
-func Pwrite(fd int, p []byte, offset int64) (n int, err error) {
- var _p0 unsafe.Pointer
- if len(p) > 0 {
- _p0 = unsafe.Pointer(&p[0])
- } else {
- _p0 = unsafe.Pointer(&_zero)
- }
- r0, _, e1 := Syscall6(SYS_PWRITE, uintptr(fd), uintptr(_p0), uintptr(len(p)), uintptr(offset), uintptr(offset>>32), 0)
- n = int(r0)
- if int32(r0) == -1 {
- err = e1
- }
- return
-}
-
-// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT
-
-func Close(fd int) (err error) {
- r0, _, e1 := Syscall(SYS_CLOSE, uintptr(fd), 0, 0)
- if int32(r0) == -1 {
- err = e1
- }
- return
-}
-
-// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT
-
-func Fstat(fd int, edir []byte) (n int, err error) {
- var _p0 unsafe.Pointer
- if len(edir) > 0 {
- _p0 = unsafe.Pointer(&edir[0])
- } else {
- _p0 = unsafe.Pointer(&_zero)
- }
- r0, _, e1 := Syscall(SYS_FSTAT, uintptr(fd), uintptr(_p0), uintptr(len(edir)))
- n = int(r0)
- if int32(r0) == -1 {
- err = e1
- }
- return
-}
-
-// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT
-
-func Fwstat(fd int, edir []byte) (err error) {
- var _p0 unsafe.Pointer
- if len(edir) > 0 {
- _p0 = unsafe.Pointer(&edir[0])
- } else {
- _p0 = unsafe.Pointer(&_zero)
- }
- r0, _, e1 := Syscall(SYS_FWSTAT, uintptr(fd), uintptr(_p0), uintptr(len(edir)))
- if int32(r0) == -1 {
- err = e1
- }
- return
-}
diff --git a/vendor/golang.org/x/sys/plan9/zsysnum_plan9.go b/vendor/golang.org/x/sys/plan9/zsysnum_plan9.go
deleted file mode 100644
index 22e8abd..0000000
--- a/vendor/golang.org/x/sys/plan9/zsysnum_plan9.go
+++ /dev/null
@@ -1,49 +0,0 @@
-// mksysnum_plan9.sh /opt/plan9/sys/src/libc/9syscall/sys.h
-// MACHINE GENERATED BY THE ABOVE COMMAND; DO NOT EDIT
-
-package plan9
-
-const (
- SYS_SYSR1 = 0
- SYS_BIND = 2
- SYS_CHDIR = 3
- SYS_CLOSE = 4
- SYS_DUP = 5
- SYS_ALARM = 6
- SYS_EXEC = 7
- SYS_EXITS = 8
- SYS_FAUTH = 10
- SYS_SEGBRK = 12
- SYS_OPEN = 14
- SYS_OSEEK = 16
- SYS_SLEEP = 17
- SYS_RFORK = 19
- SYS_PIPE = 21
- SYS_CREATE = 22
- SYS_FD2PATH = 23
- SYS_BRK_ = 24
- SYS_REMOVE = 25
- SYS_NOTIFY = 28
- SYS_NOTED = 29
- SYS_SEGATTACH = 30
- SYS_SEGDETACH = 31
- SYS_SEGFREE = 32
- SYS_SEGFLUSH = 33
- SYS_RENDEZVOUS = 34
- SYS_UNMOUNT = 35
- SYS_SEMACQUIRE = 37
- SYS_SEMRELEASE = 38
- SYS_SEEK = 39
- SYS_FVERSION = 40
- SYS_ERRSTR = 41
- SYS_STAT = 42
- SYS_FSTAT = 43
- SYS_WSTAT = 44
- SYS_FWSTAT = 45
- SYS_MOUNT = 46
- SYS_AWAIT = 47
- SYS_PREAD = 50
- SYS_PWRITE = 51
- SYS_TSEMACQUIRE = 52
- SYS_NSEC = 53
-)
diff --git a/vendor/golang.org/x/sys/unix/.gitignore b/vendor/golang.org/x/sys/unix/.gitignore
deleted file mode 100644
index e482715..0000000
--- a/vendor/golang.org/x/sys/unix/.gitignore
+++ /dev/null
@@ -1 +0,0 @@
-_obj/
diff --git a/vendor/golang.org/x/sys/unix/README.md b/vendor/golang.org/x/sys/unix/README.md
deleted file mode 100644
index bc6f603..0000000
--- a/vendor/golang.org/x/sys/unix/README.md
+++ /dev/null
@@ -1,173 +0,0 @@
-# Building `sys/unix`
-
-The sys/unix package provides access to the raw system call interface of the
-underlying operating system. See: https://godoc.org/golang.org/x/sys/unix
-
-Porting Go to a new architecture/OS combination or adding syscalls, types, or
-constants to an existing architecture/OS pair requires some manual effort;
-however, there are tools that automate much of the process.
-
-## Build Systems
-
-There are currently two ways we generate the necessary files. We are currently
-migrating the build system to use containers so the builds are reproducible.
-This is being done on an OS-by-OS basis. Please update this documentation as
-components of the build system change.
-
-### Old Build System (currently for `GOOS != "Linux" || GOARCH == "sparc64"`)
-
-The old build system generates the Go files based on the C header files
-present on your system. This means that files
-for a given GOOS/GOARCH pair must be generated on a system with that OS and
-architecture. This also means that the generated code can differ from system
-to system, based on differences in the header files.
-
-To avoid this, if you are using the old build system, only generate the Go
-files on an installation with unmodified header files. It is also important to
-keep track of which version of the OS the files were generated from (ex.
-Darwin 14 vs Darwin 15). This makes it easier to track the progress of changes
-and have each OS upgrade correspond to a single change.
-
-To build the files for your current OS and architecture, make sure GOOS and
-GOARCH are set correctly and run `mkall.sh`. This will generate the files for
-your specific system. Running `mkall.sh -n` shows the commands that will be run.
-
-Requirements: bash, perl, go
-
-### New Build System (currently for `GOOS == "Linux" && GOARCH != "sparc64"`)
-
-The new build system uses a Docker container to generate the go files directly
-from source checkouts of the kernel and various system libraries. This means
-that on any platform that supports Docker, all the files using the new build
-system can be generated at once, and generated files will not change based on
-what the person running the scripts has installed on their computer.
-
-The OS specific files for the new build system are located in the `${GOOS}`
-directory, and the build is coordinated by the `${GOOS}/mkall.go` program. When
-the kernel or system library updates, modify the Dockerfile at
-`${GOOS}/Dockerfile` to checkout the new release of the source.
-
-To build all the files under the new build system, you must be on an amd64/Linux
-system and have your GOOS and GOARCH set accordingly. Running `mkall.sh` will
-then generate all of the files for all of the GOOS/GOARCH pairs in the new build
-system. Running `mkall.sh -n` shows the commands that will be run.
-
-Requirements: bash, perl, go, docker
-
-## Component files
-
-This section describes the various files used in the code generation process.
-It also contains instructions on how to modify these files to add a new
-architecture/OS or to add additional syscalls, types, or constants. Note that
-if you are using the new build system, the scripts cannot be called normally.
-They must be called from within the docker container.
-
-### asm files
-
-The hand-written assembly file at `asm_${GOOS}_${GOARCH}.s` implements system
-call dispatch. There are three entry points:
-```
- func Syscall(trap, a1, a2, a3 uintptr) (r1, r2, err uintptr)
- func Syscall6(trap, a1, a2, a3, a4, a5, a6 uintptr) (r1, r2, err uintptr)
- func RawSyscall(trap, a1, a2, a3 uintptr) (r1, r2, err uintptr)
-```
-The first and second are the standard ones; they differ only in how many
-arguments can be passed to the kernel. The third is for low-level use by the
-ForkExec wrapper. Unlike the first two, it does not call into the scheduler to
-let it know that a system call is running.
-
-When porting Go to an new architecture/OS, this file must be implemented for
-each GOOS/GOARCH pair.
-
-### mksysnum
-
-Mksysnum is a script located at `${GOOS}/mksysnum.pl` (or `mksysnum_${GOOS}.pl`
-for the old system). This script takes in a list of header files containing the
-syscall number declarations and parses them to produce the corresponding list of
-Go numeric constants. See `zsysnum_${GOOS}_${GOARCH}.go` for the generated
-constants.
-
-Adding new syscall numbers is mostly done by running the build on a sufficiently
-new installation of the target OS (or updating the source checkouts for the
-new build system). However, depending on the OS, you make need to update the
-parsing in mksysnum.
-
-### mksyscall.pl
-
-The `syscall.go`, `syscall_${GOOS}.go`, `syscall_${GOOS}_${GOARCH}.go` are
-hand-written Go files which implement system calls (for unix, the specific OS,
-or the specific OS/Architecture pair respectively) that need special handling
-and list `//sys` comments giving prototypes for ones that can be generated.
-
-The mksyscall.pl script takes the `//sys` and `//sysnb` comments and converts
-them into syscalls. This requires the name of the prototype in the comment to
-match a syscall number in the `zsysnum_${GOOS}_${GOARCH}.go` file. The function
-prototype can be exported (capitalized) or not.
-
-Adding a new syscall often just requires adding a new `//sys` function prototype
-with the desired arguments and a capitalized name so it is exported. However, if
-you want the interface to the syscall to be different, often one will make an
-unexported `//sys` prototype, an then write a custom wrapper in
-`syscall_${GOOS}.go`.
-
-### types files
-
-For each OS, there is a hand-written Go file at `${GOOS}/types.go` (or
-`types_${GOOS}.go` on the old system). This file includes standard C headers and
-creates Go type aliases to the corresponding C types. The file is then fed
-through godef to get the Go compatible definitions. Finally, the generated code
-is fed though mkpost.go to format the code correctly and remove any hidden or
-private identifiers. This cleaned-up code is written to
-`ztypes_${GOOS}_${GOARCH}.go`.
-
-The hardest part about preparing this file is figuring out which headers to
-include and which symbols need to be `#define`d to get the actual data
-structures that pass through to the kernel system calls. Some C libraries
-preset alternate versions for binary compatibility and translate them on the
-way in and out of system calls, but there is almost always a `#define` that can
-get the real ones.
-See `types_darwin.go` and `linux/types.go` for examples.
-
-To add a new type, add in the necessary include statement at the top of the
-file (if it is not already there) and add in a type alias line. Note that if
-your type is significantly different on different architectures, you may need
-some `#if/#elif` macros in your include statements.
-
-### mkerrors.sh
-
-This script is used to generate the system's various constants. This doesn't
-just include the error numbers and error strings, but also the signal numbers
-an a wide variety of miscellaneous constants. The constants come from the list
-of include files in the `includes_${uname}` variable. A regex then picks out
-the desired `#define` statements, and generates the corresponding Go constants.
-The error numbers and strings are generated from `#include `, and the
-signal numbers and strings are generated from `#include `. All of
-these constants are written to `zerrors_${GOOS}_${GOARCH}.go` via a C program,
-`_errors.c`, which prints out all the constants.
-
-To add a constant, add the header that includes it to the appropriate variable.
-Then, edit the regex (if necessary) to match the desired constant. Avoid making
-the regex too broad to avoid matching unintended constants.
-
-
-## Generated files
-
-### `zerror_${GOOS}_${GOARCH}.go`
-
-A file containing all of the system's generated error numbers, error strings,
-signal numbers, and constants. Generated by `mkerrors.sh` (see above).
-
-### `zsyscall_${GOOS}_${GOARCH}.go`
-
-A file containing all the generated syscalls for a specific GOOS and GOARCH.
-Generated by `mksyscall.pl` (see above).
-
-### `zsysnum_${GOOS}_${GOARCH}.go`
-
-A list of numeric constants for all the syscall number of the specific GOOS
-and GOARCH. Generated by mksysnum (see above).
-
-### `ztypes_${GOOS}_${GOARCH}.go`
-
-A file containing Go types for passing into (or returning from) syscalls.
-Generated by godefs and the types file (see above).
diff --git a/vendor/golang.org/x/sys/unix/asm_darwin_386.s b/vendor/golang.org/x/sys/unix/asm_darwin_386.s
deleted file mode 100644
index 8a72783..0000000
--- a/vendor/golang.org/x/sys/unix/asm_darwin_386.s
+++ /dev/null
@@ -1,29 +0,0 @@
-// Copyright 2009 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.
-
-// +build !gccgo
-
-#include "textflag.h"
-
-//
-// System call support for 386, Darwin
-//
-
-// Just jump to package syscall's implementation for all these functions.
-// The runtime may know about them.
-
-TEXT ·Syscall(SB),NOSPLIT,$0-28
- JMP syscall·Syscall(SB)
-
-TEXT ·Syscall6(SB),NOSPLIT,$0-40
- JMP syscall·Syscall6(SB)
-
-TEXT ·Syscall9(SB),NOSPLIT,$0-52
- JMP syscall·Syscall9(SB)
-
-TEXT ·RawSyscall(SB),NOSPLIT,$0-28
- JMP syscall·RawSyscall(SB)
-
-TEXT ·RawSyscall6(SB),NOSPLIT,$0-40
- JMP syscall·RawSyscall6(SB)
diff --git a/vendor/golang.org/x/sys/unix/asm_darwin_amd64.s b/vendor/golang.org/x/sys/unix/asm_darwin_amd64.s
deleted file mode 100644
index 6321421..0000000
--- a/vendor/golang.org/x/sys/unix/asm_darwin_amd64.s
+++ /dev/null
@@ -1,29 +0,0 @@
-// Copyright 2009 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.
-
-// +build !gccgo
-
-#include "textflag.h"
-
-//
-// System call support for AMD64, Darwin
-//
-
-// Just jump to package syscall's implementation for all these functions.
-// The runtime may know about them.
-
-TEXT ·Syscall(SB),NOSPLIT,$0-56
- JMP syscall·Syscall(SB)
-
-TEXT ·Syscall6(SB),NOSPLIT,$0-80
- JMP syscall·Syscall6(SB)
-
-TEXT ·Syscall9(SB),NOSPLIT,$0-104
- JMP syscall·Syscall9(SB)
-
-TEXT ·RawSyscall(SB),NOSPLIT,$0-56
- JMP syscall·RawSyscall(SB)
-
-TEXT ·RawSyscall6(SB),NOSPLIT,$0-80
- JMP syscall·RawSyscall6(SB)
diff --git a/vendor/golang.org/x/sys/unix/asm_darwin_arm.s b/vendor/golang.org/x/sys/unix/asm_darwin_arm.s
deleted file mode 100644
index 333242d..0000000
--- a/vendor/golang.org/x/sys/unix/asm_darwin_arm.s
+++ /dev/null
@@ -1,30 +0,0 @@
-// 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.
-
-// +build !gccgo
-// +build arm,darwin
-
-#include "textflag.h"
-
-//
-// System call support for ARM, Darwin
-//
-
-// Just jump to package syscall's implementation for all these functions.
-// The runtime may know about them.
-
-TEXT ·Syscall(SB),NOSPLIT,$0-28
- B syscall·Syscall(SB)
-
-TEXT ·Syscall6(SB),NOSPLIT,$0-40
- B syscall·Syscall6(SB)
-
-TEXT ·Syscall9(SB),NOSPLIT,$0-52
- B syscall·Syscall9(SB)
-
-TEXT ·RawSyscall(SB),NOSPLIT,$0-28
- B syscall·RawSyscall(SB)
-
-TEXT ·RawSyscall6(SB),NOSPLIT,$0-40
- B syscall·RawSyscall6(SB)
diff --git a/vendor/golang.org/x/sys/unix/asm_darwin_arm64.s b/vendor/golang.org/x/sys/unix/asm_darwin_arm64.s
deleted file mode 100644
index 97e0174..0000000
--- a/vendor/golang.org/x/sys/unix/asm_darwin_arm64.s
+++ /dev/null
@@ -1,30 +0,0 @@
-// 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.
-
-// +build !gccgo
-// +build arm64,darwin
-
-#include "textflag.h"
-
-//
-// System call support for AMD64, Darwin
-//
-
-// Just jump to package syscall's implementation for all these functions.
-// The runtime may know about them.
-
-TEXT ·Syscall(SB),NOSPLIT,$0-56
- B syscall·Syscall(SB)
-
-TEXT ·Syscall6(SB),NOSPLIT,$0-80
- B syscall·Syscall6(SB)
-
-TEXT ·Syscall9(SB),NOSPLIT,$0-104
- B syscall·Syscall9(SB)
-
-TEXT ·RawSyscall(SB),NOSPLIT,$0-56
- B syscall·RawSyscall(SB)
-
-TEXT ·RawSyscall6(SB),NOSPLIT,$0-80
- B syscall·RawSyscall6(SB)
diff --git a/vendor/golang.org/x/sys/unix/asm_dragonfly_amd64.s b/vendor/golang.org/x/sys/unix/asm_dragonfly_amd64.s
deleted file mode 100644
index d5ed672..0000000
--- a/vendor/golang.org/x/sys/unix/asm_dragonfly_amd64.s
+++ /dev/null
@@ -1,29 +0,0 @@
-// Copyright 2009 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.
-
-// +build !gccgo
-
-#include "textflag.h"
-
-//
-// System call support for AMD64, DragonFly
-//
-
-// Just jump to package syscall's implementation for all these functions.
-// The runtime may know about them.
-
-TEXT ·Syscall(SB),NOSPLIT,$0-64
- JMP syscall·Syscall(SB)
-
-TEXT ·Syscall6(SB),NOSPLIT,$0-88
- JMP syscall·Syscall6(SB)
-
-TEXT ·Syscall9(SB),NOSPLIT,$0-112
- JMP syscall·Syscall9(SB)
-
-TEXT ·RawSyscall(SB),NOSPLIT,$0-64
- JMP syscall·RawSyscall(SB)
-
-TEXT ·RawSyscall6(SB),NOSPLIT,$0-88
- JMP syscall·RawSyscall6(SB)
diff --git a/vendor/golang.org/x/sys/unix/asm_freebsd_386.s b/vendor/golang.org/x/sys/unix/asm_freebsd_386.s
deleted file mode 100644
index c9a0a26..0000000
--- a/vendor/golang.org/x/sys/unix/asm_freebsd_386.s
+++ /dev/null
@@ -1,29 +0,0 @@
-// Copyright 2009 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.
-
-// +build !gccgo
-
-#include "textflag.h"
-
-//
-// System call support for 386, FreeBSD
-//
-
-// Just jump to package syscall's implementation for all these functions.
-// The runtime may know about them.
-
-TEXT ·Syscall(SB),NOSPLIT,$0-28
- JMP syscall·Syscall(SB)
-
-TEXT ·Syscall6(SB),NOSPLIT,$0-40
- JMP syscall·Syscall6(SB)
-
-TEXT ·Syscall9(SB),NOSPLIT,$0-52
- JMP syscall·Syscall9(SB)
-
-TEXT ·RawSyscall(SB),NOSPLIT,$0-28
- JMP syscall·RawSyscall(SB)
-
-TEXT ·RawSyscall6(SB),NOSPLIT,$0-40
- JMP syscall·RawSyscall6(SB)
diff --git a/vendor/golang.org/x/sys/unix/asm_freebsd_amd64.s b/vendor/golang.org/x/sys/unix/asm_freebsd_amd64.s
deleted file mode 100644
index 3517247..0000000
--- a/vendor/golang.org/x/sys/unix/asm_freebsd_amd64.s
+++ /dev/null
@@ -1,29 +0,0 @@
-// Copyright 2009 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.
-
-// +build !gccgo
-
-#include "textflag.h"
-
-//
-// System call support for AMD64, FreeBSD
-//
-
-// Just jump to package syscall's implementation for all these functions.
-// The runtime may know about them.
-
-TEXT ·Syscall(SB),NOSPLIT,$0-56
- JMP syscall·Syscall(SB)
-
-TEXT ·Syscall6(SB),NOSPLIT,$0-80
- JMP syscall·Syscall6(SB)
-
-TEXT ·Syscall9(SB),NOSPLIT,$0-104
- JMP syscall·Syscall9(SB)
-
-TEXT ·RawSyscall(SB),NOSPLIT,$0-56
- JMP syscall·RawSyscall(SB)
-
-TEXT ·RawSyscall6(SB),NOSPLIT,$0-80
- JMP syscall·RawSyscall6(SB)
diff --git a/vendor/golang.org/x/sys/unix/asm_freebsd_arm.s b/vendor/golang.org/x/sys/unix/asm_freebsd_arm.s
deleted file mode 100644
index 9227c87..0000000
--- a/vendor/golang.org/x/sys/unix/asm_freebsd_arm.s
+++ /dev/null
@@ -1,29 +0,0 @@
-// Copyright 2012 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.
-
-// +build !gccgo
-
-#include "textflag.h"
-
-//
-// System call support for ARM, FreeBSD
-//
-
-// Just jump to package syscall's implementation for all these functions.
-// The runtime may know about them.
-
-TEXT ·Syscall(SB),NOSPLIT,$0-28
- B syscall·Syscall(SB)
-
-TEXT ·Syscall6(SB),NOSPLIT,$0-40
- B syscall·Syscall6(SB)
-
-TEXT ·Syscall9(SB),NOSPLIT,$0-52
- B syscall·Syscall9(SB)
-
-TEXT ·RawSyscall(SB),NOSPLIT,$0-28
- B syscall·RawSyscall(SB)
-
-TEXT ·RawSyscall6(SB),NOSPLIT,$0-40
- B syscall·RawSyscall6(SB)
diff --git a/vendor/golang.org/x/sys/unix/asm_linux_386.s b/vendor/golang.org/x/sys/unix/asm_linux_386.s
deleted file mode 100644
index 4db2909..0000000
--- a/vendor/golang.org/x/sys/unix/asm_linux_386.s
+++ /dev/null
@@ -1,35 +0,0 @@
-// Copyright 2009 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.
-
-// +build !gccgo
-
-#include "textflag.h"
-
-//
-// System calls for 386, Linux
-//
-
-// Just jump to package syscall's implementation for all these functions.
-// The runtime may know about them.
-
-TEXT ·Syscall(SB),NOSPLIT,$0-28
- JMP syscall·Syscall(SB)
-
-TEXT ·Syscall6(SB),NOSPLIT,$0-40
- JMP syscall·Syscall6(SB)
-
-TEXT ·RawSyscall(SB),NOSPLIT,$0-28
- JMP syscall·RawSyscall(SB)
-
-TEXT ·RawSyscall6(SB),NOSPLIT,$0-40
- JMP syscall·RawSyscall6(SB)
-
-TEXT ·socketcall(SB),NOSPLIT,$0-36
- JMP syscall·socketcall(SB)
-
-TEXT ·rawsocketcall(SB),NOSPLIT,$0-36
- JMP syscall·rawsocketcall(SB)
-
-TEXT ·seek(SB),NOSPLIT,$0-28
- JMP syscall·seek(SB)
diff --git a/vendor/golang.org/x/sys/unix/asm_linux_amd64.s b/vendor/golang.org/x/sys/unix/asm_linux_amd64.s
deleted file mode 100644
index 44e25c6..0000000
--- a/vendor/golang.org/x/sys/unix/asm_linux_amd64.s
+++ /dev/null
@@ -1,29 +0,0 @@
-// Copyright 2009 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.
-
-// +build !gccgo
-
-#include "textflag.h"
-
-//
-// System calls for AMD64, Linux
-//
-
-// Just jump to package syscall's implementation for all these functions.
-// The runtime may know about them.
-
-TEXT ·Syscall(SB),NOSPLIT,$0-56
- JMP syscall·Syscall(SB)
-
-TEXT ·Syscall6(SB),NOSPLIT,$0-80
- JMP syscall·Syscall6(SB)
-
-TEXT ·RawSyscall(SB),NOSPLIT,$0-56
- JMP syscall·RawSyscall(SB)
-
-TEXT ·RawSyscall6(SB),NOSPLIT,$0-80
- JMP syscall·RawSyscall6(SB)
-
-TEXT ·gettimeofday(SB),NOSPLIT,$0-16
- JMP syscall·gettimeofday(SB)
diff --git a/vendor/golang.org/x/sys/unix/asm_linux_arm.s b/vendor/golang.org/x/sys/unix/asm_linux_arm.s
deleted file mode 100644
index cf0b574..0000000
--- a/vendor/golang.org/x/sys/unix/asm_linux_arm.s
+++ /dev/null
@@ -1,29 +0,0 @@
-// Copyright 2009 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.
-
-// +build !gccgo
-
-#include "textflag.h"
-
-//
-// System calls for arm, Linux
-//
-
-// Just jump to package syscall's implementation for all these functions.
-// The runtime may know about them.
-
-TEXT ·Syscall(SB),NOSPLIT,$0-28
- B syscall·Syscall(SB)
-
-TEXT ·Syscall6(SB),NOSPLIT,$0-40
- B syscall·Syscall6(SB)
-
-TEXT ·RawSyscall(SB),NOSPLIT,$0-28
- B syscall·RawSyscall(SB)
-
-TEXT ·RawSyscall6(SB),NOSPLIT,$0-40
- B syscall·RawSyscall6(SB)
-
-TEXT ·seek(SB),NOSPLIT,$0-32
- B syscall·seek(SB)
diff --git a/vendor/golang.org/x/sys/unix/asm_linux_arm64.s b/vendor/golang.org/x/sys/unix/asm_linux_arm64.s
deleted file mode 100644
index 4be9bfe..0000000
--- a/vendor/golang.org/x/sys/unix/asm_linux_arm64.s
+++ /dev/null
@@ -1,24 +0,0 @@
-// 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.
-
-// +build linux
-// +build arm64
-// +build !gccgo
-
-#include "textflag.h"
-
-// Just jump to package syscall's implementation for all these functions.
-// The runtime may know about them.
-
-TEXT ·Syscall(SB),NOSPLIT,$0-56
- B syscall·Syscall(SB)
-
-TEXT ·Syscall6(SB),NOSPLIT,$0-80
- B syscall·Syscall6(SB)
-
-TEXT ·RawSyscall(SB),NOSPLIT,$0-56
- B syscall·RawSyscall(SB)
-
-TEXT ·RawSyscall6(SB),NOSPLIT,$0-80
- B syscall·RawSyscall6(SB)
diff --git a/vendor/golang.org/x/sys/unix/asm_linux_mips64x.s b/vendor/golang.org/x/sys/unix/asm_linux_mips64x.s
deleted file mode 100644
index 724e580..0000000
--- a/vendor/golang.org/x/sys/unix/asm_linux_mips64x.s
+++ /dev/null
@@ -1,28 +0,0 @@
-// 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.
-
-// +build linux
-// +build mips64 mips64le
-// +build !gccgo
-
-#include "textflag.h"
-
-//
-// System calls for mips64, Linux
-//
-
-// Just jump to package syscall's implementation for all these functions.
-// The runtime may know about them.
-
-TEXT ·Syscall(SB),NOSPLIT,$0-56
- JMP syscall·Syscall(SB)
-
-TEXT ·Syscall6(SB),NOSPLIT,$0-80
- JMP syscall·Syscall6(SB)
-
-TEXT ·RawSyscall(SB),NOSPLIT,$0-56
- JMP syscall·RawSyscall(SB)
-
-TEXT ·RawSyscall6(SB),NOSPLIT,$0-80
- JMP syscall·RawSyscall6(SB)
diff --git a/vendor/golang.org/x/sys/unix/asm_linux_mipsx.s b/vendor/golang.org/x/sys/unix/asm_linux_mipsx.s
deleted file mode 100644
index 2ea4257..0000000
--- a/vendor/golang.org/x/sys/unix/asm_linux_mipsx.s
+++ /dev/null
@@ -1,31 +0,0 @@
-// Copyright 2016 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.
-
-// +build linux
-// +build mips mipsle
-// +build !gccgo
-
-#include "textflag.h"
-
-//
-// System calls for mips, Linux
-//
-
-// Just jump to package syscall's implementation for all these functions.
-// The runtime may know about them.
-
-TEXT ·Syscall(SB),NOSPLIT,$0-28
- JMP syscall·Syscall(SB)
-
-TEXT ·Syscall6(SB),NOSPLIT,$0-40
- JMP syscall·Syscall6(SB)
-
-TEXT ·Syscall9(SB),NOSPLIT,$0-52
- JMP syscall·Syscall9(SB)
-
-TEXT ·RawSyscall(SB),NOSPLIT,$0-28
- JMP syscall·RawSyscall(SB)
-
-TEXT ·RawSyscall6(SB),NOSPLIT,$0-40
- JMP syscall·RawSyscall6(SB)
diff --git a/vendor/golang.org/x/sys/unix/asm_linux_ppc64x.s b/vendor/golang.org/x/sys/unix/asm_linux_ppc64x.s
deleted file mode 100644
index 8d231fe..0000000
--- a/vendor/golang.org/x/sys/unix/asm_linux_ppc64x.s
+++ /dev/null
@@ -1,28 +0,0 @@
-// Copyright 2014 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.
-
-// +build linux
-// +build ppc64 ppc64le
-// +build !gccgo
-
-#include "textflag.h"
-
-//
-// System calls for ppc64, Linux
-//
-
-// Just jump to package syscall's implementation for all these functions.
-// The runtime may know about them.
-
-TEXT ·Syscall(SB),NOSPLIT,$0-56
- BR syscall·Syscall(SB)
-
-TEXT ·Syscall6(SB),NOSPLIT,$0-80
- BR syscall·Syscall6(SB)
-
-TEXT ·RawSyscall(SB),NOSPLIT,$0-56
- BR syscall·RawSyscall(SB)
-
-TEXT ·RawSyscall6(SB),NOSPLIT,$0-80
- BR syscall·RawSyscall6(SB)
diff --git a/vendor/golang.org/x/sys/unix/asm_linux_s390x.s b/vendor/golang.org/x/sys/unix/asm_linux_s390x.s
deleted file mode 100644
index 1188985..0000000
--- a/vendor/golang.org/x/sys/unix/asm_linux_s390x.s
+++ /dev/null
@@ -1,28 +0,0 @@
-// Copyright 2016 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.
-
-// +build s390x
-// +build linux
-// +build !gccgo
-
-#include "textflag.h"
-
-//
-// System calls for s390x, Linux
-//
-
-// Just jump to package syscall's implementation for all these functions.
-// The runtime may know about them.
-
-TEXT ·Syscall(SB),NOSPLIT,$0-56
- BR syscall·Syscall(SB)
-
-TEXT ·Syscall6(SB),NOSPLIT,$0-80
- BR syscall·Syscall6(SB)
-
-TEXT ·RawSyscall(SB),NOSPLIT,$0-56
- BR syscall·RawSyscall(SB)
-
-TEXT ·RawSyscall6(SB),NOSPLIT,$0-80
- BR syscall·RawSyscall6(SB)
diff --git a/vendor/golang.org/x/sys/unix/asm_netbsd_386.s b/vendor/golang.org/x/sys/unix/asm_netbsd_386.s
deleted file mode 100644
index 48bdcd7..0000000
--- a/vendor/golang.org/x/sys/unix/asm_netbsd_386.s
+++ /dev/null
@@ -1,29 +0,0 @@
-// Copyright 2009 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.
-
-// +build !gccgo
-
-#include "textflag.h"
-
-//
-// System call support for 386, NetBSD
-//
-
-// Just jump to package syscall's implementation for all these functions.
-// The runtime may know about them.
-
-TEXT ·Syscall(SB),NOSPLIT,$0-28
- JMP syscall·Syscall(SB)
-
-TEXT ·Syscall6(SB),NOSPLIT,$0-40
- JMP syscall·Syscall6(SB)
-
-TEXT ·Syscall9(SB),NOSPLIT,$0-52
- JMP syscall·Syscall9(SB)
-
-TEXT ·RawSyscall(SB),NOSPLIT,$0-28
- JMP syscall·RawSyscall(SB)
-
-TEXT ·RawSyscall6(SB),NOSPLIT,$0-40
- JMP syscall·RawSyscall6(SB)
diff --git a/vendor/golang.org/x/sys/unix/asm_netbsd_amd64.s b/vendor/golang.org/x/sys/unix/asm_netbsd_amd64.s
deleted file mode 100644
index 2ede05c..0000000
--- a/vendor/golang.org/x/sys/unix/asm_netbsd_amd64.s
+++ /dev/null
@@ -1,29 +0,0 @@
-// Copyright 2009 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.
-
-// +build !gccgo
-
-#include "textflag.h"
-
-//
-// System call support for AMD64, NetBSD
-//
-
-// Just jump to package syscall's implementation for all these functions.
-// The runtime may know about them.
-
-TEXT ·Syscall(SB),NOSPLIT,$0-56
- JMP syscall·Syscall(SB)
-
-TEXT ·Syscall6(SB),NOSPLIT,$0-80
- JMP syscall·Syscall6(SB)
-
-TEXT ·Syscall9(SB),NOSPLIT,$0-104
- JMP syscall·Syscall9(SB)
-
-TEXT ·RawSyscall(SB),NOSPLIT,$0-56
- JMP syscall·RawSyscall(SB)
-
-TEXT ·RawSyscall6(SB),NOSPLIT,$0-80
- JMP syscall·RawSyscall6(SB)
diff --git a/vendor/golang.org/x/sys/unix/asm_netbsd_arm.s b/vendor/golang.org/x/sys/unix/asm_netbsd_arm.s
deleted file mode 100644
index e892857..0000000
--- a/vendor/golang.org/x/sys/unix/asm_netbsd_arm.s
+++ /dev/null
@@ -1,29 +0,0 @@
-// Copyright 2013 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.
-
-// +build !gccgo
-
-#include "textflag.h"
-
-//
-// System call support for ARM, NetBSD
-//
-
-// Just jump to package syscall's implementation for all these functions.
-// The runtime may know about them.
-
-TEXT ·Syscall(SB),NOSPLIT,$0-28
- B syscall·Syscall(SB)
-
-TEXT ·Syscall6(SB),NOSPLIT,$0-40
- B syscall·Syscall6(SB)
-
-TEXT ·Syscall9(SB),NOSPLIT,$0-52
- B syscall·Syscall9(SB)
-
-TEXT ·RawSyscall(SB),NOSPLIT,$0-28
- B syscall·RawSyscall(SB)
-
-TEXT ·RawSyscall6(SB),NOSPLIT,$0-40
- B syscall·RawSyscall6(SB)
diff --git a/vendor/golang.org/x/sys/unix/asm_openbsd_386.s b/vendor/golang.org/x/sys/unix/asm_openbsd_386.s
deleted file mode 100644
index 00576f3..0000000
--- a/vendor/golang.org/x/sys/unix/asm_openbsd_386.s
+++ /dev/null
@@ -1,29 +0,0 @@
-// Copyright 2009 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.
-
-// +build !gccgo
-
-#include "textflag.h"
-
-//
-// System call support for 386, OpenBSD
-//
-
-// Just jump to package syscall's implementation for all these functions.
-// The runtime may know about them.
-
-TEXT ·Syscall(SB),NOSPLIT,$0-28
- JMP syscall·Syscall(SB)
-
-TEXT ·Syscall6(SB),NOSPLIT,$0-40
- JMP syscall·Syscall6(SB)
-
-TEXT ·Syscall9(SB),NOSPLIT,$0-52
- JMP syscall·Syscall9(SB)
-
-TEXT ·RawSyscall(SB),NOSPLIT,$0-28
- JMP syscall·RawSyscall(SB)
-
-TEXT ·RawSyscall6(SB),NOSPLIT,$0-40
- JMP syscall·RawSyscall6(SB)
diff --git a/vendor/golang.org/x/sys/unix/asm_openbsd_amd64.s b/vendor/golang.org/x/sys/unix/asm_openbsd_amd64.s
deleted file mode 100644
index 790ef77..0000000
--- a/vendor/golang.org/x/sys/unix/asm_openbsd_amd64.s
+++ /dev/null
@@ -1,29 +0,0 @@
-// Copyright 2009 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.
-
-// +build !gccgo
-
-#include "textflag.h"
-
-//
-// System call support for AMD64, OpenBSD
-//
-
-// Just jump to package syscall's implementation for all these functions.
-// The runtime may know about them.
-
-TEXT ·Syscall(SB),NOSPLIT,$0-56
- JMP syscall·Syscall(SB)
-
-TEXT ·Syscall6(SB),NOSPLIT,$0-80
- JMP syscall·Syscall6(SB)
-
-TEXT ·Syscall9(SB),NOSPLIT,$0-104
- JMP syscall·Syscall9(SB)
-
-TEXT ·RawSyscall(SB),NOSPLIT,$0-56
- JMP syscall·RawSyscall(SB)
-
-TEXT ·RawSyscall6(SB),NOSPLIT,$0-80
- JMP syscall·RawSyscall6(SB)
diff --git a/vendor/golang.org/x/sys/unix/asm_solaris_amd64.s b/vendor/golang.org/x/sys/unix/asm_solaris_amd64.s
deleted file mode 100644
index 43ed17a..0000000
--- a/vendor/golang.org/x/sys/unix/asm_solaris_amd64.s
+++ /dev/null
@@ -1,17 +0,0 @@
-// Copyright 2014 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.
-
-// +build !gccgo
-
-#include "textflag.h"
-
-//
-// System calls for amd64, Solaris are implemented in runtime/syscall_solaris.go
-//
-
-TEXT ·sysvicall6(SB),NOSPLIT,$0-64
- JMP syscall·sysvicall6(SB)
-
-TEXT ·rawSysvicall6(SB),NOSPLIT,$0-64
- JMP syscall·rawSysvicall6(SB)
diff --git a/vendor/golang.org/x/sys/unix/bluetooth_linux.go b/vendor/golang.org/x/sys/unix/bluetooth_linux.go
deleted file mode 100644
index 6e32296..0000000
--- a/vendor/golang.org/x/sys/unix/bluetooth_linux.go
+++ /dev/null
@@ -1,35 +0,0 @@
-// Copyright 2016 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.
-
-// Bluetooth sockets and messages
-
-package unix
-
-// Bluetooth Protocols
-const (
- BTPROTO_L2CAP = 0
- BTPROTO_HCI = 1
- BTPROTO_SCO = 2
- BTPROTO_RFCOMM = 3
- BTPROTO_BNEP = 4
- BTPROTO_CMTP = 5
- BTPROTO_HIDP = 6
- BTPROTO_AVDTP = 7
-)
-
-const (
- HCI_CHANNEL_RAW = 0
- HCI_CHANNEL_USER = 1
- HCI_CHANNEL_MONITOR = 2
- HCI_CHANNEL_CONTROL = 3
-)
-
-// Socketoption Level
-const (
- SOL_BLUETOOTH = 0x112
- SOL_HCI = 0x0
- SOL_L2CAP = 0x6
- SOL_RFCOMM = 0x12
- SOL_SCO = 0x11
-)
diff --git a/vendor/golang.org/x/sys/unix/constants.go b/vendor/golang.org/x/sys/unix/constants.go
deleted file mode 100644
index a96f0eb..0000000
--- a/vendor/golang.org/x/sys/unix/constants.go
+++ /dev/null
@@ -1,13 +0,0 @@
-// 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.
-
-// +build darwin dragonfly freebsd linux netbsd openbsd solaris
-
-package unix
-
-const (
- R_OK = 0x4
- W_OK = 0x2
- X_OK = 0x1
-)
diff --git a/vendor/golang.org/x/sys/unix/creds_test.go b/vendor/golang.org/x/sys/unix/creds_test.go
deleted file mode 100644
index eaae7c3..0000000
--- a/vendor/golang.org/x/sys/unix/creds_test.go
+++ /dev/null
@@ -1,121 +0,0 @@
-// Copyright 2012 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.
-
-// +build linux
-
-package unix_test
-
-import (
- "bytes"
- "net"
- "os"
- "syscall"
- "testing"
-
- "golang.org/x/sys/unix"
-)
-
-// TestSCMCredentials tests the sending and receiving of credentials
-// (PID, UID, GID) in an ancillary message between two UNIX
-// sockets. The SO_PASSCRED socket option is enabled on the sending
-// socket for this to work.
-func TestSCMCredentials(t *testing.T) {
- fds, err := unix.Socketpair(unix.AF_LOCAL, unix.SOCK_STREAM, 0)
- if err != nil {
- t.Fatalf("Socketpair: %v", err)
- }
- defer unix.Close(fds[0])
- defer unix.Close(fds[1])
-
- err = unix.SetsockoptInt(fds[0], unix.SOL_SOCKET, unix.SO_PASSCRED, 1)
- if err != nil {
- t.Fatalf("SetsockoptInt: %v", err)
- }
-
- srvFile := os.NewFile(uintptr(fds[0]), "server")
- defer srvFile.Close()
- srv, err := net.FileConn(srvFile)
- if err != nil {
- t.Errorf("FileConn: %v", err)
- return
- }
- defer srv.Close()
-
- cliFile := os.NewFile(uintptr(fds[1]), "client")
- defer cliFile.Close()
- cli, err := net.FileConn(cliFile)
- if err != nil {
- t.Errorf("FileConn: %v", err)
- return
- }
- defer cli.Close()
-
- var ucred unix.Ucred
- if os.Getuid() != 0 {
- ucred.Pid = int32(os.Getpid())
- ucred.Uid = 0
- ucred.Gid = 0
- oob := unix.UnixCredentials(&ucred)
- _, _, err := cli.(*net.UnixConn).WriteMsgUnix(nil, oob, nil)
- if op, ok := err.(*net.OpError); ok {
- err = op.Err
- }
- if sys, ok := err.(*os.SyscallError); ok {
- err = sys.Err
- }
- if err != syscall.EPERM {
- t.Fatalf("WriteMsgUnix failed with %v, want EPERM", err)
- }
- }
-
- ucred.Pid = int32(os.Getpid())
- ucred.Uid = uint32(os.Getuid())
- ucred.Gid = uint32(os.Getgid())
- oob := unix.UnixCredentials(&ucred)
-
- // this is going to send a dummy byte
- n, oobn, err := cli.(*net.UnixConn).WriteMsgUnix(nil, oob, nil)
- if err != nil {
- t.Fatalf("WriteMsgUnix: %v", err)
- }
- if n != 0 {
- t.Fatalf("WriteMsgUnix n = %d, want 0", n)
- }
- if oobn != len(oob) {
- t.Fatalf("WriteMsgUnix oobn = %d, want %d", oobn, len(oob))
- }
-
- oob2 := make([]byte, 10*len(oob))
- n, oobn2, flags, _, err := srv.(*net.UnixConn).ReadMsgUnix(nil, oob2)
- if err != nil {
- t.Fatalf("ReadMsgUnix: %v", err)
- }
- if flags != 0 {
- t.Fatalf("ReadMsgUnix flags = 0x%x, want 0", flags)
- }
- if n != 1 {
- t.Fatalf("ReadMsgUnix n = %d, want 1 (dummy byte)", n)
- }
- if oobn2 != oobn {
- // without SO_PASSCRED set on the socket, ReadMsgUnix will
- // return zero oob bytes
- t.Fatalf("ReadMsgUnix oobn = %d, want %d", oobn2, oobn)
- }
- oob2 = oob2[:oobn2]
- if !bytes.Equal(oob, oob2) {
- t.Fatal("ReadMsgUnix oob bytes don't match")
- }
-
- scm, err := unix.ParseSocketControlMessage(oob2)
- if err != nil {
- t.Fatalf("ParseSocketControlMessage: %v", err)
- }
- newUcred, err := unix.ParseUnixCredentials(&scm[0])
- if err != nil {
- t.Fatalf("ParseUnixCredentials: %v", err)
- }
- if *newUcred != ucred {
- t.Fatalf("ParseUnixCredentials = %+v, want %+v", newUcred, ucred)
- }
-}
diff --git a/vendor/golang.org/x/sys/unix/dirent.go b/vendor/golang.org/x/sys/unix/dirent.go
deleted file mode 100644
index bd47581..0000000
--- a/vendor/golang.org/x/sys/unix/dirent.go
+++ /dev/null
@@ -1,102 +0,0 @@
-// Copyright 2009 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.
-
-// +build darwin dragonfly freebsd linux nacl netbsd openbsd solaris
-
-package unix
-
-import "unsafe"
-
-// readInt returns the size-bytes unsigned integer in native byte order at offset off.
-func readInt(b []byte, off, size uintptr) (u uint64, ok bool) {
- if len(b) < int(off+size) {
- return 0, false
- }
- if isBigEndian {
- return readIntBE(b[off:], size), true
- }
- return readIntLE(b[off:], size), true
-}
-
-func readIntBE(b []byte, size uintptr) uint64 {
- switch size {
- case 1:
- return uint64(b[0])
- case 2:
- _ = b[1] // bounds check hint to compiler; see golang.org/issue/14808
- return uint64(b[1]) | uint64(b[0])<<8
- case 4:
- _ = b[3] // bounds check hint to compiler; see golang.org/issue/14808
- return uint64(b[3]) | uint64(b[2])<<8 | uint64(b[1])<<16 | uint64(b[0])<<24
- case 8:
- _ = b[7] // bounds check hint to compiler; see golang.org/issue/14808
- return uint64(b[7]) | uint64(b[6])<<8 | uint64(b[5])<<16 | uint64(b[4])<<24 |
- uint64(b[3])<<32 | uint64(b[2])<<40 | uint64(b[1])<<48 | uint64(b[0])<<56
- default:
- panic("syscall: readInt with unsupported size")
- }
-}
-
-func readIntLE(b []byte, size uintptr) uint64 {
- switch size {
- case 1:
- return uint64(b[0])
- case 2:
- _ = b[1] // bounds check hint to compiler; see golang.org/issue/14808
- return uint64(b[0]) | uint64(b[1])<<8
- case 4:
- _ = b[3] // bounds check hint to compiler; see golang.org/issue/14808
- return uint64(b[0]) | uint64(b[1])<<8 | uint64(b[2])<<16 | uint64(b[3])<<24
- case 8:
- _ = b[7] // bounds check hint to compiler; see golang.org/issue/14808
- return uint64(b[0]) | uint64(b[1])<<8 | uint64(b[2])<<16 | uint64(b[3])<<24 |
- uint64(b[4])<<32 | uint64(b[5])<<40 | uint64(b[6])<<48 | uint64(b[7])<<56
- default:
- panic("syscall: readInt with unsupported size")
- }
-}
-
-// ParseDirent parses up to max directory entries in buf,
-// appending the names to names. It returns the number of
-// bytes consumed from buf, the number of entries added
-// to names, and the new names slice.
-func ParseDirent(buf []byte, max int, names []string) (consumed int, count int, newnames []string) {
- origlen := len(buf)
- count = 0
- for max != 0 && len(buf) > 0 {
- reclen, ok := direntReclen(buf)
- if !ok || reclen > uint64(len(buf)) {
- return origlen, count, names
- }
- rec := buf[:reclen]
- buf = buf[reclen:]
- ino, ok := direntIno(rec)
- if !ok {
- break
- }
- if ino == 0 { // File absent in directory.
- continue
- }
- const namoff = uint64(unsafe.Offsetof(Dirent{}.Name))
- namlen, ok := direntNamlen(rec)
- if !ok || namoff+namlen > uint64(len(rec)) {
- break
- }
- name := rec[namoff : namoff+namlen]
- for i, c := range name {
- if c == 0 {
- name = name[:i]
- break
- }
- }
- // Check for useless names before allocating a string.
- if string(name) == "." || string(name) == ".." {
- continue
- }
- max--
- count++
- names = append(names, string(name))
- }
- return origlen - len(buf), count, names
-}
diff --git a/vendor/golang.org/x/sys/unix/endian_big.go b/vendor/golang.org/x/sys/unix/endian_big.go
deleted file mode 100644
index 5e92690..0000000
--- a/vendor/golang.org/x/sys/unix/endian_big.go
+++ /dev/null
@@ -1,9 +0,0 @@
-// Copyright 2016 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.
-//
-// +build ppc64 s390x mips mips64
-
-package unix
-
-const isBigEndian = true
diff --git a/vendor/golang.org/x/sys/unix/endian_little.go b/vendor/golang.org/x/sys/unix/endian_little.go
deleted file mode 100644
index 085df2d..0000000
--- a/vendor/golang.org/x/sys/unix/endian_little.go
+++ /dev/null
@@ -1,9 +0,0 @@
-// Copyright 2016 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.
-//
-// +build 386 amd64 amd64p32 arm arm64 ppc64le mipsle mips64le
-
-package unix
-
-const isBigEndian = false
diff --git a/vendor/golang.org/x/sys/unix/env_unix.go b/vendor/golang.org/x/sys/unix/env_unix.go
deleted file mode 100644
index 45e281a..0000000
--- a/vendor/golang.org/x/sys/unix/env_unix.go
+++ /dev/null
@@ -1,27 +0,0 @@
-// Copyright 2010 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.
-
-// +build darwin dragonfly freebsd linux netbsd openbsd solaris
-
-// Unix environment variables.
-
-package unix
-
-import "syscall"
-
-func Getenv(key string) (value string, found bool) {
- return syscall.Getenv(key)
-}
-
-func Setenv(key, value string) error {
- return syscall.Setenv(key, value)
-}
-
-func Clearenv() {
- syscall.Clearenv()
-}
-
-func Environ() []string {
- return syscall.Environ()
-}
diff --git a/vendor/golang.org/x/sys/unix/env_unset.go b/vendor/golang.org/x/sys/unix/env_unset.go
deleted file mode 100644
index 9222262..0000000
--- a/vendor/golang.org/x/sys/unix/env_unset.go
+++ /dev/null
@@ -1,14 +0,0 @@
-// Copyright 2014 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.
-
-// +build go1.4
-
-package unix
-
-import "syscall"
-
-func Unsetenv(key string) error {
- // This was added in Go 1.4.
- return syscall.Unsetenv(key)
-}
diff --git a/vendor/golang.org/x/sys/unix/export_test.go b/vendor/golang.org/x/sys/unix/export_test.go
deleted file mode 100644
index b4fdd97..0000000
--- a/vendor/golang.org/x/sys/unix/export_test.go
+++ /dev/null
@@ -1,9 +0,0 @@
-// 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.
-
-// +build darwin dragonfly freebsd linux netbsd openbsd solaris
-
-package unix
-
-var Itoa = itoa
diff --git a/vendor/golang.org/x/sys/unix/flock.go b/vendor/golang.org/x/sys/unix/flock.go
deleted file mode 100644
index ce67a59..0000000
--- a/vendor/golang.org/x/sys/unix/flock.go
+++ /dev/null
@@ -1,24 +0,0 @@
-// +build linux darwin freebsd openbsd netbsd dragonfly
-
-// Copyright 2014 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.
-
-// +build darwin dragonfly freebsd linux netbsd openbsd
-
-package unix
-
-import "unsafe"
-
-// fcntl64Syscall is usually SYS_FCNTL, but is overridden on 32-bit Linux
-// systems by flock_linux_32bit.go to be SYS_FCNTL64.
-var fcntl64Syscall uintptr = SYS_FCNTL
-
-// FcntlFlock performs a fcntl syscall for the F_GETLK, F_SETLK or F_SETLKW command.
-func FcntlFlock(fd uintptr, cmd int, lk *Flock_t) error {
- _, _, errno := Syscall(fcntl64Syscall, fd, uintptr(cmd), uintptr(unsafe.Pointer(lk)))
- if errno == 0 {
- return nil
- }
- return errno
-}
diff --git a/vendor/golang.org/x/sys/unix/flock_linux_32bit.go b/vendor/golang.org/x/sys/unix/flock_linux_32bit.go
deleted file mode 100644
index fc0e50e..0000000
--- a/vendor/golang.org/x/sys/unix/flock_linux_32bit.go
+++ /dev/null
@@ -1,13 +0,0 @@
-// +build linux,386 linux,arm linux,mips linux,mipsle
-
-// Copyright 2014 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 unix
-
-func init() {
- // On 32-bit Linux systems, the fcntl syscall that matches Go's
- // Flock_t type is SYS_FCNTL64, not SYS_FCNTL.
- fcntl64Syscall = SYS_FCNTL64
-}
diff --git a/vendor/golang.org/x/sys/unix/gccgo.go b/vendor/golang.org/x/sys/unix/gccgo.go
deleted file mode 100644
index 94c8232..0000000
--- a/vendor/golang.org/x/sys/unix/gccgo.go
+++ /dev/null
@@ -1,46 +0,0 @@
-// 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.
-
-// +build gccgo
-
-package unix
-
-import "syscall"
-
-// We can't use the gc-syntax .s files for gccgo. On the plus side
-// much of the functionality can be written directly in Go.
-
-//extern gccgoRealSyscall
-func realSyscall(trap, a1, a2, a3, a4, a5, a6, a7, a8, a9 uintptr) (r, errno uintptr)
-
-func Syscall(trap, a1, a2, a3 uintptr) (r1, r2 uintptr, err syscall.Errno) {
- syscall.Entersyscall()
- r, errno := realSyscall(trap, a1, a2, a3, 0, 0, 0, 0, 0, 0)
- syscall.Exitsyscall()
- return r, 0, syscall.Errno(errno)
-}
-
-func Syscall6(trap, a1, a2, a3, a4, a5, a6 uintptr) (r1, r2 uintptr, err syscall.Errno) {
- syscall.Entersyscall()
- r, errno := realSyscall(trap, a1, a2, a3, a4, a5, a6, 0, 0, 0)
- syscall.Exitsyscall()
- return r, 0, syscall.Errno(errno)
-}
-
-func Syscall9(trap, a1, a2, a3, a4, a5, a6, a7, a8, a9 uintptr) (r1, r2 uintptr, err syscall.Errno) {
- syscall.Entersyscall()
- r, errno := realSyscall(trap, a1, a2, a3, a4, a5, a6, a7, a8, a9)
- syscall.Exitsyscall()
- return r, 0, syscall.Errno(errno)
-}
-
-func RawSyscall(trap, a1, a2, a3 uintptr) (r1, r2 uintptr, err syscall.Errno) {
- r, errno := realSyscall(trap, a1, a2, a3, 0, 0, 0, 0, 0, 0)
- return r, 0, syscall.Errno(errno)
-}
-
-func RawSyscall6(trap, a1, a2, a3, a4, a5, a6 uintptr) (r1, r2 uintptr, err syscall.Errno) {
- r, errno := realSyscall(trap, a1, a2, a3, a4, a5, a6, 0, 0, 0)
- return r, 0, syscall.Errno(errno)
-}
diff --git a/vendor/golang.org/x/sys/unix/gccgo_c.c b/vendor/golang.org/x/sys/unix/gccgo_c.c
deleted file mode 100644
index 07f6be0..0000000
--- a/vendor/golang.org/x/sys/unix/gccgo_c.c
+++ /dev/null
@@ -1,41 +0,0 @@
-// 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.
-
-// +build gccgo
-
-#include
-#include
-#include
-
-#define _STRINGIFY2_(x) #x
-#define _STRINGIFY_(x) _STRINGIFY2_(x)
-#define GOSYM_PREFIX _STRINGIFY_(__USER_LABEL_PREFIX__)
-
-// Call syscall from C code because the gccgo support for calling from
-// Go to C does not support varargs functions.
-
-struct ret {
- uintptr_t r;
- uintptr_t err;
-};
-
-struct ret
-gccgoRealSyscall(uintptr_t trap, uintptr_t a1, uintptr_t a2, uintptr_t a3, uintptr_t a4, uintptr_t a5, uintptr_t a6, uintptr_t a7, uintptr_t a8, uintptr_t a9)
-{
- struct ret r;
-
- errno = 0;
- r.r = syscall(trap, a1, a2, a3, a4, a5, a6, a7, a8, a9);
- r.err = errno;
- return r;
-}
-
-// Define the use function in C so that it is not inlined.
-
-extern void use(void *) __asm__ (GOSYM_PREFIX GOPKGPATH ".use") __attribute__((noinline));
-
-void
-use(void *p __attribute__ ((unused)))
-{
-}
diff --git a/vendor/golang.org/x/sys/unix/gccgo_linux_amd64.go b/vendor/golang.org/x/sys/unix/gccgo_linux_amd64.go
deleted file mode 100644
index bffe1a7..0000000
--- a/vendor/golang.org/x/sys/unix/gccgo_linux_amd64.go
+++ /dev/null
@@ -1,20 +0,0 @@
-// 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.
-
-// +build gccgo,linux,amd64
-
-package unix
-
-import "syscall"
-
-//extern gettimeofday
-func realGettimeofday(*Timeval, *byte) int32
-
-func gettimeofday(tv *Timeval) (err syscall.Errno) {
- r := realGettimeofday(tv, nil)
- if r < 0 {
- return syscall.GetErrno()
- }
- return 0
-}
diff --git a/vendor/golang.org/x/sys/unix/gccgo_linux_sparc64.go b/vendor/golang.org/x/sys/unix/gccgo_linux_sparc64.go
deleted file mode 100644
index 5633269..0000000
--- a/vendor/golang.org/x/sys/unix/gccgo_linux_sparc64.go
+++ /dev/null
@@ -1,20 +0,0 @@
-// Copyright 2016 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.
-
-// +build gccgo,linux,sparc64
-
-package unix
-
-import "syscall"
-
-//extern sysconf
-func realSysconf(name int) int64
-
-func sysconf(name int) (n int64, err syscall.Errno) {
- r := realSysconf(name)
- if r < 0 {
- return 0, syscall.GetErrno()
- }
- return r, 0
-}
diff --git a/vendor/golang.org/x/sys/unix/linux/Dockerfile b/vendor/golang.org/x/sys/unix/linux/Dockerfile
deleted file mode 100644
index 4397143..0000000
--- a/vendor/golang.org/x/sys/unix/linux/Dockerfile
+++ /dev/null
@@ -1,48 +0,0 @@
-FROM ubuntu:16.04
-
-# Dependencies to get the git sources and go binaries
-RUN apt-get update && apt-get install -y \
- curl \
- git \
- && rm -rf /var/lib/apt/lists/*
-
-# Get the git sources. If not cached, this takes O(5 minutes).
-WORKDIR /git
-RUN git config --global advice.detachedHead false
-# Linux Kernel: Released 19 Feb 2017
-RUN git clone --branch v4.10 --depth 1 https://kernel.googlesource.com/pub/scm/linux/kernel/git/torvalds/linux
-# GNU C library: Released 05 Feb 2017 (we should try to get a secure way to clone this)
-RUN git clone --branch glibc-2.25 --depth 1 git://sourceware.org/git/glibc.git
-
-# Get Go 1.8 (https://github.com/docker-library/golang/blob/master/1.8/Dockerfile)
-ENV GOLANG_VERSION 1.8
-ENV GOLANG_DOWNLOAD_URL https://golang.org/dl/go$GOLANG_VERSION.linux-amd64.tar.gz
-ENV GOLANG_DOWNLOAD_SHA256 53ab94104ee3923e228a2cb2116e5e462ad3ebaeea06ff04463479d7f12d27ca
-
-RUN curl -fsSL "$GOLANG_DOWNLOAD_URL" -o golang.tar.gz \
- && echo "$GOLANG_DOWNLOAD_SHA256 golang.tar.gz" | sha256sum -c - \
- && tar -C /usr/local -xzf golang.tar.gz \
- && rm golang.tar.gz
-
-ENV PATH /usr/local/go/bin:$PATH
-
-# Linux and Glibc build dependencies
-RUN apt-get update && apt-get install -y \
- gawk make python \
- gcc gcc-multilib \
- gettext texinfo \
- && rm -rf /var/lib/apt/lists/*
-# Emulator and cross compilers
-RUN apt-get update && apt-get install -y \
- qemu \
- gcc-aarch64-linux-gnu gcc-arm-linux-gnueabi \
- gcc-mips-linux-gnu gcc-mips64-linux-gnuabi64 \
- gcc-mips64el-linux-gnuabi64 gcc-mipsel-linux-gnu \
- gcc-powerpc64-linux-gnu gcc-powerpc64le-linux-gnu \
- gcc-s390x-linux-gnu gcc-sparc64-linux-gnu \
- && rm -rf /var/lib/apt/lists/*
-
-# Let the scripts know they are in the docker environment
-ENV GOLANG_SYS_BUILD docker
-WORKDIR /build
-ENTRYPOINT ["go", "run", "linux/mkall.go", "/git/linux", "/git/glibc"]
diff --git a/vendor/golang.org/x/sys/unix/linux/mkall.go b/vendor/golang.org/x/sys/unix/linux/mkall.go
deleted file mode 100644
index 429754f..0000000
--- a/vendor/golang.org/x/sys/unix/linux/mkall.go
+++ /dev/null
@@ -1,379 +0,0 @@
-// Copyright 2017 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.
-
-// linux/mkall.go - Generates all Linux zsysnum, zsyscall, zerror, and ztype
-// files for all 11 linux architectures supported by the go compiler. See
-// README.md for more information about the build system.
-
-// To run it you must have a git checkout of the Linux kernel and glibc. Once
-// the appropriate sources are ready, the program is run as:
-// go run linux/mkall.go
-
-// +build ignore
-
-package main
-
-import (
- "fmt"
- "os"
- "os/exec"
- "path/filepath"
- "runtime"
- "strings"
-)
-
-// These will be paths to the appropriate source directories.
-var LinuxDir string
-var GlibcDir string
-
-const TempDir = "/tmp"
-const IncludeDir = TempDir + "/include" // To hold our C headers
-const BuildDir = TempDir + "/build" // To hold intermediate build files
-
-const GOOS = "linux" // Only for Linux targets
-const BuildArch = "amd64" // Must be built on this architecture
-const MinKernel = "2.6.23" // https://golang.org/doc/install#requirements
-
-type target struct {
- GoArch string // Architecture name according to Go
- LinuxArch string // Architecture name according to the Linux Kernel
- GNUArch string // Architecture name according to GNU tools (https://wiki.debian.org/Multiarch/Tuples)
- BigEndian bool // Default Little Endian
- SignedChar bool // Is -fsigned-char needed (default no)
- Bits int
-}
-
-// List of the 11 Linux targets supported by the go compiler. sparc64 is not
-// currently supported, though a port is in progress.
-var targets = []target{
- {
- GoArch: "386",
- LinuxArch: "x86",
- GNUArch: "i686-linux-gnu", // Note "i686" not "i386"
- Bits: 32,
- },
- {
- GoArch: "amd64",
- LinuxArch: "x86",
- GNUArch: "x86_64-linux-gnu",
- Bits: 64,
- },
- {
- GoArch: "arm64",
- LinuxArch: "arm64",
- GNUArch: "aarch64-linux-gnu",
- SignedChar: true,
- Bits: 64,
- },
- {
- GoArch: "arm",
- LinuxArch: "arm",
- GNUArch: "arm-linux-gnueabi",
- Bits: 32,
- },
- {
- GoArch: "mips",
- LinuxArch: "mips",
- GNUArch: "mips-linux-gnu",
- BigEndian: true,
- Bits: 32,
- },
- {
- GoArch: "mipsle",
- LinuxArch: "mips",
- GNUArch: "mipsel-linux-gnu",
- Bits: 32,
- },
- {
- GoArch: "mips64",
- LinuxArch: "mips",
- GNUArch: "mips64-linux-gnuabi64",
- BigEndian: true,
- Bits: 64,
- },
- {
- GoArch: "mips64le",
- LinuxArch: "mips",
- GNUArch: "mips64el-linux-gnuabi64",
- Bits: 64,
- },
- {
- GoArch: "ppc64",
- LinuxArch: "powerpc",
- GNUArch: "powerpc64-linux-gnu",
- BigEndian: true,
- Bits: 64,
- },
- {
- GoArch: "ppc64le",
- LinuxArch: "powerpc",
- GNUArch: "powerpc64le-linux-gnu",
- Bits: 64,
- },
- {
- GoArch: "s390x",
- LinuxArch: "s390",
- GNUArch: "s390x-linux-gnu",
- BigEndian: true,
- SignedChar: true,
- Bits: 64,
- },
- // {
- // GoArch: "sparc64",
- // LinuxArch: "sparc",
- // GNUArch: "sparc64-linux-gnu",
- // BigEndian: true,
- // Bits: 64,
- // },
-}
-
-func main() {
- if runtime.GOOS != GOOS || runtime.GOARCH != BuildArch {
- fmt.Printf("Build system has GOOS_GOARCH = %s_%s, need %s_%s\n",
- runtime.GOOS, runtime.GOARCH, GOOS, BuildArch)
- return
- }
-
- // Check that we are using the new build system if we should
- if os.Getenv("GOLANG_SYS_BUILD") != "docker" {
- fmt.Println("In the new build system, mkall.go should not be called directly.")
- fmt.Println("See README.md")
- return
- }
-
- // Parse the command line options
- if len(os.Args) != 3 {
- fmt.Println("USAGE: go run linux/mkall.go ")
- return
- }
- LinuxDir = os.Args[1]
- GlibcDir = os.Args[2]
-
- for _, t := range targets {
- fmt.Printf("----- GENERATING: %s -----\n", t.GoArch)
- if err := t.generateFiles(); err != nil {
- fmt.Printf("%v\n***** FAILURE: %s *****\n\n", err, t.GoArch)
- } else {
- fmt.Printf("----- SUCCESS: %s -----\n\n", t.GoArch)
- }
- }
-}
-
-// Makes an exec.Cmd with Stderr attached to os.Stderr
-func makeCommand(name string, args ...string) *exec.Cmd {
- cmd := exec.Command(name, args...)
- cmd.Stderr = os.Stderr
- return cmd
-}
-
-// Runs the command, pipes output to a formatter, pipes that to an output file.
-func (t *target) commandFormatOutput(formatter string, outputFile string,
- name string, args ...string) (err error) {
- mainCmd := makeCommand(name, args...)
-
- fmtCmd := makeCommand(formatter)
- if formatter == "mkpost" {
- fmtCmd = makeCommand("go", "run", "mkpost.go")
- // Set GOARCH_TARGET so mkpost knows what GOARCH is..
- fmtCmd.Env = append(os.Environ(), "GOARCH_TARGET="+t.GoArch)
- // Set GOARCH to host arch for mkpost, so it can run natively.
- for i, s := range fmtCmd.Env {
- if strings.HasPrefix(s, "GOARCH=") {
- fmtCmd.Env[i] = "GOARCH=" + BuildArch
- }
- }
- }
-
- // mainCmd | fmtCmd > outputFile
- if fmtCmd.Stdin, err = mainCmd.StdoutPipe(); err != nil {
- return
- }
- if fmtCmd.Stdout, err = os.Create(outputFile); err != nil {
- return
- }
-
- // Make sure the formatter eventually closes
- if err = fmtCmd.Start(); err != nil {
- return
- }
- defer func() {
- fmtErr := fmtCmd.Wait()
- if err == nil {
- err = fmtErr
- }
- }()
-
- return mainCmd.Run()
-}
-
-// Generates all the files for a Linux target
-func (t *target) generateFiles() error {
- // Setup environment variables
- os.Setenv("GOOS", GOOS)
- os.Setenv("GOARCH", t.GoArch)
-
- // Get appropriate compiler and emulator (unless on x86)
- if t.LinuxArch != "x86" {
- // Check/Setup cross compiler
- compiler := t.GNUArch + "-gcc"
- if _, err := exec.LookPath(compiler); err != nil {
- return err
- }
- os.Setenv("CC", compiler)
-
- // Check/Setup emulator (usually first component of GNUArch)
- qemuArchName := t.GNUArch[:strings.Index(t.GNUArch, "-")]
- if t.LinuxArch == "powerpc" {
- qemuArchName = t.GoArch
- }
- os.Setenv("GORUN", "qemu-"+qemuArchName)
- } else {
- os.Setenv("CC", "gcc")
- }
-
- // Make the include directory and fill it with headers
- if err := os.MkdirAll(IncludeDir, os.ModePerm); err != nil {
- return err
- }
- defer os.RemoveAll(IncludeDir)
- if err := t.makeHeaders(); err != nil {
- return fmt.Errorf("could not make header files: %v", err)
- }
- fmt.Println("header files generated")
-
- // Make each of the four files
- if err := t.makeZSysnumFile(); err != nil {
- return fmt.Errorf("could not make zsysnum file: %v", err)
- }
- fmt.Println("zsysnum file generated")
-
- if err := t.makeZSyscallFile(); err != nil {
- return fmt.Errorf("could not make zsyscall file: %v", err)
- }
- fmt.Println("zsyscall file generated")
-
- if err := t.makeZTypesFile(); err != nil {
- return fmt.Errorf("could not make ztypes file: %v", err)
- }
- fmt.Println("ztypes file generated")
-
- if err := t.makeZErrorsFile(); err != nil {
- return fmt.Errorf("could not make zerrors file: %v", err)
- }
- fmt.Println("zerrors file generated")
-
- return nil
-}
-
-// Create the Linux and glibc headers in the include directory.
-func (t *target) makeHeaders() error {
- // Make the Linux headers we need for this architecture
- linuxMake := makeCommand("make", "headers_install", "ARCH="+t.LinuxArch, "INSTALL_HDR_PATH="+TempDir)
- linuxMake.Dir = LinuxDir
- if err := linuxMake.Run(); err != nil {
- return err
- }
-
- // A Temporary build directory for glibc
- if err := os.MkdirAll(BuildDir, os.ModePerm); err != nil {
- return err
- }
- defer os.RemoveAll(BuildDir)
-
- // Make the glibc headers we need for this architecture
- confScript := filepath.Join(GlibcDir, "configure")
- glibcConf := makeCommand(confScript, "--prefix="+TempDir, "--host="+t.GNUArch, "--enable-kernel="+MinKernel)
- glibcConf.Dir = BuildDir
- if err := glibcConf.Run(); err != nil {
- return err
- }
- glibcMake := makeCommand("make", "install-headers")
- glibcMake.Dir = BuildDir
- if err := glibcMake.Run(); err != nil {
- return err
- }
- // We only need an empty stubs file
- stubsFile := filepath.Join(IncludeDir, "gnu/stubs.h")
- if file, err := os.Create(stubsFile); err != nil {
- return err
- } else {
- file.Close()
- }
-
- return nil
-}
-
-// makes the zsysnum_linux_$GOARCH.go file
-func (t *target) makeZSysnumFile() error {
- zsysnumFile := fmt.Sprintf("zsysnum_linux_%s.go", t.GoArch)
- unistdFile := filepath.Join(IncludeDir, "asm/unistd.h")
-
- args := append(t.cFlags(), unistdFile)
- return t.commandFormatOutput("gofmt", zsysnumFile, "linux/mksysnum.pl", args...)
-}
-
-// makes the zsyscall_linux_$GOARCH.go file
-func (t *target) makeZSyscallFile() error {
- zsyscallFile := fmt.Sprintf("zsyscall_linux_%s.go", t.GoArch)
- // Find the correct architecture syscall file (might end with x.go)
- archSyscallFile := fmt.Sprintf("syscall_linux_%s.go", t.GoArch)
- if _, err := os.Stat(archSyscallFile); os.IsNotExist(err) {
- shortArch := strings.TrimSuffix(t.GoArch, "le")
- archSyscallFile = fmt.Sprintf("syscall_linux_%sx.go", shortArch)
- }
-
- args := append(t.mksyscallFlags(), "-tags", "linux,"+t.GoArch,
- "syscall_linux.go", archSyscallFile)
- return t.commandFormatOutput("gofmt", zsyscallFile, "./mksyscall.pl", args...)
-}
-
-// makes the zerrors_linux_$GOARCH.go file
-func (t *target) makeZErrorsFile() error {
- zerrorsFile := fmt.Sprintf("zerrors_linux_%s.go", t.GoArch)
-
- return t.commandFormatOutput("gofmt", zerrorsFile, "./mkerrors.sh", t.cFlags()...)
-}
-
-// makes the ztypes_linux_$GOARCH.go file
-func (t *target) makeZTypesFile() error {
- ztypesFile := fmt.Sprintf("ztypes_linux_%s.go", t.GoArch)
-
- args := []string{"tool", "cgo", "-godefs", "--"}
- args = append(args, t.cFlags()...)
- args = append(args, "linux/types.go")
- return t.commandFormatOutput("mkpost", ztypesFile, "go", args...)
-}
-
-// Flags that should be given to gcc and cgo for this target
-func (t *target) cFlags() []string {
- // Compile statically to avoid cross-architecture dynamic linking.
- flags := []string{"-Wall", "-Werror", "-static", "-I" + IncludeDir}
-
- // Architecture-specific flags
- if t.SignedChar {
- flags = append(flags, "-fsigned-char")
- }
- if t.LinuxArch == "x86" {
- flags = append(flags, fmt.Sprintf("-m%d", t.Bits))
- }
-
- return flags
-}
-
-// Flags that should be given to mksyscall for this target
-func (t *target) mksyscallFlags() (flags []string) {
- if t.Bits == 32 {
- if t.BigEndian {
- flags = append(flags, "-b32")
- } else {
- flags = append(flags, "-l32")
- }
- }
-
- // This flag menas a 64-bit value should use (even, odd)-pair.
- if t.GoArch == "arm" || (t.LinuxArch == "mips" && t.Bits == 32) {
- flags = append(flags, "-arm")
- }
- return
-}
diff --git a/vendor/golang.org/x/sys/unix/linux/mksysnum.pl b/vendor/golang.org/x/sys/unix/linux/mksysnum.pl
deleted file mode 100755
index 63fd800..0000000
--- a/vendor/golang.org/x/sys/unix/linux/mksysnum.pl
+++ /dev/null
@@ -1,85 +0,0 @@
-#!/usr/bin/env perl
-# Copyright 2009 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.
-
-use strict;
-
-if($ENV{'GOARCH'} eq "" || $ENV{'GOOS'} eq "") {
- print STDERR "GOARCH or GOOS not defined in environment\n";
- exit 1;
-}
-
-# Check that we are using the new build system if we should
-if($ENV{'GOLANG_SYS_BUILD'} ne "docker") {
- print STDERR "In the new build system, mksysnum should not be called directly.\n";
- print STDERR "See README.md\n";
- exit 1;
-}
-
-my $command = "$0 ". join(' ', @ARGV);
-
-print < 999){
- # ignore deprecated syscalls that are no longer implemented
- # https://git.kernel.org/cgit/linux/kernel/git/torvalds/linux.git/tree/include/uapi/asm-generic/unistd.h?id=refs/heads/master#n716
- return;
- }
- $name =~ y/a-z/A-Z/;
- $num = $num + $offset;
- print " SYS_$name = $num;\n";
-}
-
-my $prev;
-open(CC, "$ENV{'CC'} -E -dD @ARGV |") || die "can't run $ENV{'CC'}";
-while(){
- if(/^#define __NR_Linux\s+([0-9]+)/){
- # mips/mips64: extract offset
- $offset = $1;
- }
- elsif(/^#define __NR(\w*)_SYSCALL_BASE\s+([0-9]+)/){
- # arm: extract offset
- $offset = $1;
- }
- elsif(/^#define __NR_syscalls\s+/) {
- # ignore redefinitions of __NR_syscalls
- }
- elsif(/^#define __NR_(\w*)Linux_syscalls\s+/) {
- # mips/mips64: ignore definitions about the number of syscalls
- }
- elsif(/^#define __NR_(\w+)\s+([0-9]+)/){
- $prev = $2;
- fmt($1, $2);
- }
- elsif(/^#define __NR3264_(\w+)\s+([0-9]+)/){
- $prev = $2;
- fmt($1, $2);
- }
- elsif(/^#define __NR_(\w+)\s+\(\w+\+\s*([0-9]+)\)/){
- fmt($1, $prev+$2)
- }
- elsif(/^#define __NR_(\w+)\s+\(__NR_Linux \+ ([0-9]+)/){
- fmt($1, $2);
- }
- elsif(/^#define __NR_(\w+)\s+\(__NR_SYSCALL_BASE \+ ([0-9]+)/){
- fmt($1, $2);
- }
-}
-
-print <
-#include
-#include
-#include
-#include
-#include
-#include
-#include
-#include
-#include
-#include
-#include
-#include
-#include
-#include
-#include
-#include
-#include
-#include
-#include
-#include
-#include
-#include
-#include
-#include
-#include
-#include
-#include
-#include
-#include
-#include
-#include
-#include
-#include
-#include
-#include
-#include
-#include
-#include
-
-// On mips64, the glibc stat and kernel stat do not agree
-#if (defined(__mips__) && _MIPS_SIM == _MIPS_SIM_ABI64)
-
-// Use the stat defined by the kernel with a few modifications. These are:
-// * The time fields (like st_atime and st_atimensec) use the timespec
-// struct (like st_atim) for consitancy with the glibc fields.
-// * The padding fields get different names to not break compatibility.
-// * st_blocks is signed, again for compatibility.
-struct stat {
- unsigned int st_dev;
- unsigned int st_pad1[3]; // Reserved for st_dev expansion
-
- unsigned long st_ino;
-
- mode_t st_mode;
- __u32 st_nlink;
-
- uid_t st_uid;
- gid_t st_gid;
-
- unsigned int st_rdev;
- unsigned int st_pad2[3]; // Reserved for st_rdev expansion
-
- off_t st_size;
-
- // These are declared as speperate fields in the kernel. Here we use
- // the timespec struct for consistancy with the other stat structs.
- struct timespec st_atim;
- struct timespec st_mtim;
- struct timespec st_ctim;
-
- unsigned int st_blksize;
- unsigned int st_pad4;
-
- long st_blocks;
-};
-
-// These are needed because we do not include fcntl.h or sys/types.h
-#include
-#include
-
-#else
-
-// Use the stat defined by glibc
-#include
-#include
-
-#endif
-
-// Certain constants and structs are missing from the fs/crypto UAPI
-#define FS_MAX_KEY_SIZE 64
-struct fscrypt_key {
- __u32 mode;
- __u8 raw[FS_MAX_KEY_SIZE];
- __u32 size;
-};
-
-#ifdef TCSETS2
-// On systems that have "struct termios2" use this as type Termios.
-typedef struct termios2 termios_t;
-#else
-typedef struct termios termios_t;
-#endif
-
-enum {
- sizeofPtr = sizeof(void*),
-};
-
-union sockaddr_all {
- struct sockaddr s1; // this one gets used for fields
- struct sockaddr_in s2; // these pad it out
- struct sockaddr_in6 s3;
- struct sockaddr_un s4;
- struct sockaddr_ll s5;
- struct sockaddr_nl s6;
-};
-
-struct sockaddr_any {
- struct sockaddr addr;
- char pad[sizeof(union sockaddr_all) - sizeof(struct sockaddr)];
-};
-
-// copied from /usr/include/bluetooth/hci.h
-struct sockaddr_hci {
- sa_family_t hci_family;
- unsigned short hci_dev;
- unsigned short hci_channel;
-};;
-
-// copied from /usr/include/linux/un.h
-struct my_sockaddr_un {
- sa_family_t sun_family;
-#if defined(__ARM_EABI__) || defined(__powerpc64__)
- // on ARM char is by default unsigned
- signed char sun_path[108];
-#else
- char sun_path[108];
-#endif
-};
-
-#ifdef __ARM_EABI__
-typedef struct user_regs PtraceRegs;
-#elif defined(__aarch64__)
-typedef struct user_pt_regs PtraceRegs;
-#elif defined(__powerpc64__)
-typedef struct pt_regs PtraceRegs;
-#elif defined(__mips__)
-typedef struct user PtraceRegs;
-#elif defined(__s390x__)
-typedef struct _user_regs_struct PtraceRegs;
-#elif defined(__sparc__)
-#include
-typedef struct pt_regs PtraceRegs;
-#else
-typedef struct user_regs_struct PtraceRegs;
-#endif
-
-#if defined(__s390x__)
-typedef struct _user_psw_struct ptracePsw;
-typedef struct _user_fpregs_struct ptraceFpregs;
-typedef struct _user_per_struct ptracePer;
-#else
-typedef struct {} ptracePsw;
-typedef struct {} ptraceFpregs;
-typedef struct {} ptracePer;
-#endif
-
-// The real epoll_event is a union, and godefs doesn't handle it well.
-struct my_epoll_event {
- uint32_t events;
-#if defined(__ARM_EABI__) || defined(__aarch64__) || (defined(__mips__) && _MIPS_SIM == _ABIO32)
- // padding is not specified in linux/eventpoll.h but added to conform to the
- // alignment requirements of EABI
- int32_t padFd;
-#elif defined(__powerpc64__) || defined(__s390x__) || defined(__sparc__)
- int32_t _padFd;
-#endif
- int32_t fd;
- int32_t pad;
-};
-
-*/
-import "C"
-
-// Machine characteristics; for internal use.
-
-const (
- sizeofPtr = C.sizeofPtr
- sizeofShort = C.sizeof_short
- sizeofInt = C.sizeof_int
- sizeofLong = C.sizeof_long
- sizeofLongLong = C.sizeof_longlong
- PathMax = C.PATH_MAX
-)
-
-// Basic types
-
-type (
- _C_short C.short
- _C_int C.int
- _C_long C.long
- _C_long_long C.longlong
-)
-
-// Time
-
-type Timespec C.struct_timespec
-
-type Timeval C.struct_timeval
-
-type Timex C.struct_timex
-
-type Time_t C.time_t
-
-type Tms C.struct_tms
-
-type Utimbuf C.struct_utimbuf
-
-// Processes
-
-type Rusage C.struct_rusage
-
-type Rlimit C.struct_rlimit
-
-type _Gid_t C.gid_t
-
-// Files
-
-type Stat_t C.struct_stat
-
-type Statfs_t C.struct_statfs
-
-type Dirent C.struct_dirent
-
-type Fsid C.fsid_t
-
-type Flock_t C.struct_flock
-
-// Filesystem Encryption
-
-type FscryptPolicy C.struct_fscrypt_policy
-
-type FscryptKey C.struct_fscrypt_key
-
-// Advice to Fadvise
-
-const (
- FADV_NORMAL = C.POSIX_FADV_NORMAL
- FADV_RANDOM = C.POSIX_FADV_RANDOM
- FADV_SEQUENTIAL = C.POSIX_FADV_SEQUENTIAL
- FADV_WILLNEED = C.POSIX_FADV_WILLNEED
- FADV_DONTNEED = C.POSIX_FADV_DONTNEED
- FADV_NOREUSE = C.POSIX_FADV_NOREUSE
-)
-
-// Sockets
-
-type RawSockaddrInet4 C.struct_sockaddr_in
-
-type RawSockaddrInet6 C.struct_sockaddr_in6
-
-type RawSockaddrUnix C.struct_my_sockaddr_un
-
-type RawSockaddrLinklayer C.struct_sockaddr_ll
-
-type RawSockaddrNetlink C.struct_sockaddr_nl
-
-type RawSockaddrHCI C.struct_sockaddr_hci
-
-type RawSockaddrCAN C.struct_sockaddr_can
-
-type RawSockaddrALG C.struct_sockaddr_alg
-
-type RawSockaddrVM C.struct_sockaddr_vm
-
-type RawSockaddr C.struct_sockaddr
-
-type RawSockaddrAny C.struct_sockaddr_any
-
-type _Socklen C.socklen_t
-
-type Linger C.struct_linger
-
-type Iovec C.struct_iovec
-
-type IPMreq C.struct_ip_mreq
-
-type IPMreqn C.struct_ip_mreqn
-
-type IPv6Mreq C.struct_ipv6_mreq
-
-type Msghdr C.struct_msghdr
-
-type Cmsghdr C.struct_cmsghdr
-
-type Inet4Pktinfo C.struct_in_pktinfo
-
-type Inet6Pktinfo C.struct_in6_pktinfo
-
-type IPv6MTUInfo C.struct_ip6_mtuinfo
-
-type ICMPv6Filter C.struct_icmp6_filter
-
-type Ucred C.struct_ucred
-
-type TCPInfo C.struct_tcp_info
-
-const (
- SizeofSockaddrInet4 = C.sizeof_struct_sockaddr_in
- SizeofSockaddrInet6 = C.sizeof_struct_sockaddr_in6
- SizeofSockaddrAny = C.sizeof_struct_sockaddr_any
- SizeofSockaddrUnix = C.sizeof_struct_sockaddr_un
- SizeofSockaddrLinklayer = C.sizeof_struct_sockaddr_ll
- SizeofSockaddrNetlink = C.sizeof_struct_sockaddr_nl
- SizeofSockaddrHCI = C.sizeof_struct_sockaddr_hci
- SizeofSockaddrCAN = C.sizeof_struct_sockaddr_can
- SizeofSockaddrALG = C.sizeof_struct_sockaddr_alg
- SizeofSockaddrVM = C.sizeof_struct_sockaddr_vm
- SizeofLinger = C.sizeof_struct_linger
- SizeofIPMreq = C.sizeof_struct_ip_mreq
- SizeofIPMreqn = C.sizeof_struct_ip_mreqn
- SizeofIPv6Mreq = C.sizeof_struct_ipv6_mreq
- SizeofMsghdr = C.sizeof_struct_msghdr
- SizeofCmsghdr = C.sizeof_struct_cmsghdr
- SizeofInet4Pktinfo = C.sizeof_struct_in_pktinfo
- SizeofInet6Pktinfo = C.sizeof_struct_in6_pktinfo
- SizeofIPv6MTUInfo = C.sizeof_struct_ip6_mtuinfo
- SizeofICMPv6Filter = C.sizeof_struct_icmp6_filter
- SizeofUcred = C.sizeof_struct_ucred
- SizeofTCPInfo = C.sizeof_struct_tcp_info
-)
-
-// Netlink routing and interface messages
-
-const (
- IFA_UNSPEC = C.IFA_UNSPEC
- IFA_ADDRESS = C.IFA_ADDRESS
- IFA_LOCAL = C.IFA_LOCAL
- IFA_LABEL = C.IFA_LABEL
- IFA_BROADCAST = C.IFA_BROADCAST
- IFA_ANYCAST = C.IFA_ANYCAST
- IFA_CACHEINFO = C.IFA_CACHEINFO
- IFA_MULTICAST = C.IFA_MULTICAST
- IFLA_UNSPEC = C.IFLA_UNSPEC
- IFLA_ADDRESS = C.IFLA_ADDRESS
- IFLA_BROADCAST = C.IFLA_BROADCAST
- IFLA_IFNAME = C.IFLA_IFNAME
- IFLA_MTU = C.IFLA_MTU
- IFLA_LINK = C.IFLA_LINK
- IFLA_QDISC = C.IFLA_QDISC
- IFLA_STATS = C.IFLA_STATS
- IFLA_COST = C.IFLA_COST
- IFLA_PRIORITY = C.IFLA_PRIORITY
- IFLA_MASTER = C.IFLA_MASTER
- IFLA_WIRELESS = C.IFLA_WIRELESS
- IFLA_PROTINFO = C.IFLA_PROTINFO
- IFLA_TXQLEN = C.IFLA_TXQLEN
- IFLA_MAP = C.IFLA_MAP
- IFLA_WEIGHT = C.IFLA_WEIGHT
- IFLA_OPERSTATE = C.IFLA_OPERSTATE
- IFLA_LINKMODE = C.IFLA_LINKMODE
- IFLA_LINKINFO = C.IFLA_LINKINFO
- IFLA_NET_NS_PID = C.IFLA_NET_NS_PID
- IFLA_IFALIAS = C.IFLA_IFALIAS
- IFLA_MAX = C.IFLA_MAX
- RT_SCOPE_UNIVERSE = C.RT_SCOPE_UNIVERSE
- RT_SCOPE_SITE = C.RT_SCOPE_SITE
- RT_SCOPE_LINK = C.RT_SCOPE_LINK
- RT_SCOPE_HOST = C.RT_SCOPE_HOST
- RT_SCOPE_NOWHERE = C.RT_SCOPE_NOWHERE
- RT_TABLE_UNSPEC = C.RT_TABLE_UNSPEC
- RT_TABLE_COMPAT = C.RT_TABLE_COMPAT
- RT_TABLE_DEFAULT = C.RT_TABLE_DEFAULT
- RT_TABLE_MAIN = C.RT_TABLE_MAIN
- RT_TABLE_LOCAL = C.RT_TABLE_LOCAL
- RT_TABLE_MAX = C.RT_TABLE_MAX
- RTA_UNSPEC = C.RTA_UNSPEC
- RTA_DST = C.RTA_DST
- RTA_SRC = C.RTA_SRC
- RTA_IIF = C.RTA_IIF
- RTA_OIF = C.RTA_OIF
- RTA_GATEWAY = C.RTA_GATEWAY
- RTA_PRIORITY = C.RTA_PRIORITY
- RTA_PREFSRC = C.RTA_PREFSRC
- RTA_METRICS = C.RTA_METRICS
- RTA_MULTIPATH = C.RTA_MULTIPATH
- RTA_FLOW = C.RTA_FLOW
- RTA_CACHEINFO = C.RTA_CACHEINFO
- RTA_TABLE = C.RTA_TABLE
- RTN_UNSPEC = C.RTN_UNSPEC
- RTN_UNICAST = C.RTN_UNICAST
- RTN_LOCAL = C.RTN_LOCAL
- RTN_BROADCAST = C.RTN_BROADCAST
- RTN_ANYCAST = C.RTN_ANYCAST
- RTN_MULTICAST = C.RTN_MULTICAST
- RTN_BLACKHOLE = C.RTN_BLACKHOLE
- RTN_UNREACHABLE = C.RTN_UNREACHABLE
- RTN_PROHIBIT = C.RTN_PROHIBIT
- RTN_THROW = C.RTN_THROW
- RTN_NAT = C.RTN_NAT
- RTN_XRESOLVE = C.RTN_XRESOLVE
- RTNLGRP_NONE = C.RTNLGRP_NONE
- RTNLGRP_LINK = C.RTNLGRP_LINK
- RTNLGRP_NOTIFY = C.RTNLGRP_NOTIFY
- RTNLGRP_NEIGH = C.RTNLGRP_NEIGH
- RTNLGRP_TC = C.RTNLGRP_TC
- RTNLGRP_IPV4_IFADDR = C.RTNLGRP_IPV4_IFADDR
- RTNLGRP_IPV4_MROUTE = C.RTNLGRP_IPV4_MROUTE
- RTNLGRP_IPV4_ROUTE = C.RTNLGRP_IPV4_ROUTE
- RTNLGRP_IPV4_RULE = C.RTNLGRP_IPV4_RULE
- RTNLGRP_IPV6_IFADDR = C.RTNLGRP_IPV6_IFADDR
- RTNLGRP_IPV6_MROUTE = C.RTNLGRP_IPV6_MROUTE
- RTNLGRP_IPV6_ROUTE = C.RTNLGRP_IPV6_ROUTE
- RTNLGRP_IPV6_IFINFO = C.RTNLGRP_IPV6_IFINFO
- RTNLGRP_IPV6_PREFIX = C.RTNLGRP_IPV6_PREFIX
- RTNLGRP_IPV6_RULE = C.RTNLGRP_IPV6_RULE
- RTNLGRP_ND_USEROPT = C.RTNLGRP_ND_USEROPT
- SizeofNlMsghdr = C.sizeof_struct_nlmsghdr
- SizeofNlMsgerr = C.sizeof_struct_nlmsgerr
- SizeofRtGenmsg = C.sizeof_struct_rtgenmsg
- SizeofNlAttr = C.sizeof_struct_nlattr
- SizeofRtAttr = C.sizeof_struct_rtattr
- SizeofIfInfomsg = C.sizeof_struct_ifinfomsg
- SizeofIfAddrmsg = C.sizeof_struct_ifaddrmsg
- SizeofRtMsg = C.sizeof_struct_rtmsg
- SizeofRtNexthop = C.sizeof_struct_rtnexthop
-)
-
-type NlMsghdr C.struct_nlmsghdr
-
-type NlMsgerr C.struct_nlmsgerr
-
-type RtGenmsg C.struct_rtgenmsg
-
-type NlAttr C.struct_nlattr
-
-type RtAttr C.struct_rtattr
-
-type IfInfomsg C.struct_ifinfomsg
-
-type IfAddrmsg C.struct_ifaddrmsg
-
-type RtMsg C.struct_rtmsg
-
-type RtNexthop C.struct_rtnexthop
-
-// Linux socket filter
-
-const (
- SizeofSockFilter = C.sizeof_struct_sock_filter
- SizeofSockFprog = C.sizeof_struct_sock_fprog
-)
-
-type SockFilter C.struct_sock_filter
-
-type SockFprog C.struct_sock_fprog
-
-// Inotify
-
-type InotifyEvent C.struct_inotify_event
-
-const SizeofInotifyEvent = C.sizeof_struct_inotify_event
-
-// Ptrace
-
-// Register structures
-type PtraceRegs C.PtraceRegs
-
-// Structures contained in PtraceRegs on s390x (exported by mkpost.go)
-type PtracePsw C.ptracePsw
-
-type PtraceFpregs C.ptraceFpregs
-
-type PtracePer C.ptracePer
-
-// Misc
-
-type FdSet C.fd_set
-
-type Sysinfo_t C.struct_sysinfo
-
-type Utsname C.struct_utsname
-
-type Ustat_t C.struct_ustat
-
-type EpollEvent C.struct_my_epoll_event
-
-const (
- AT_FDCWD = C.AT_FDCWD
- AT_REMOVEDIR = C.AT_REMOVEDIR
- AT_SYMLINK_FOLLOW = C.AT_SYMLINK_FOLLOW
- AT_SYMLINK_NOFOLLOW = C.AT_SYMLINK_NOFOLLOW
-)
-
-type PollFd C.struct_pollfd
-
-const (
- POLLIN = C.POLLIN
- POLLPRI = C.POLLPRI
- POLLOUT = C.POLLOUT
- POLLRDHUP = C.POLLRDHUP
- POLLERR = C.POLLERR
- POLLHUP = C.POLLHUP
- POLLNVAL = C.POLLNVAL
-)
-
-type Sigset_t C.sigset_t
-
-// sysconf information
-
-const _SC_PAGESIZE = C._SC_PAGESIZE
-
-// Terminal handling
-
-type Termios C.termios_t
diff --git a/vendor/golang.org/x/sys/unix/mkall.sh b/vendor/golang.org/x/sys/unix/mkall.sh
deleted file mode 100755
index f0d6566..0000000
--- a/vendor/golang.org/x/sys/unix/mkall.sh
+++ /dev/null
@@ -1,179 +0,0 @@
-#!/usr/bin/env bash
-# Copyright 2009 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.
-
-# This script runs or (given -n) prints suggested commands to generate files for
-# the Architecture/OS specified by the GOARCH and GOOS environment variables.
-# See README.md for more information about how the build system works.
-
-GOOSARCH="${GOOS}_${GOARCH}"
-
-# defaults
-mksyscall="./mksyscall.pl"
-mkerrors="./mkerrors.sh"
-zerrors="zerrors_$GOOSARCH.go"
-mksysctl=""
-zsysctl="zsysctl_$GOOSARCH.go"
-mksysnum=
-mktypes=
-run="sh"
-cmd=""
-
-case "$1" in
--syscalls)
- for i in zsyscall*go
- do
- # Run the command line that appears in the first line
- # of the generated file to regenerate it.
- sed 1q $i | sed 's;^// ;;' | sh > _$i && gofmt < _$i > $i
- rm _$i
- done
- exit 0
- ;;
--n)
- run="cat"
- cmd="echo"
- shift
-esac
-
-case "$#" in
-0)
- ;;
-*)
- echo 'usage: mkall.sh [-n]' 1>&2
- exit 2
-esac
-
-if [[ "$GOOS" = "linux" ]] && [[ "$GOARCH" != "sparc64" ]]; then
- # Use then new build system
- # Files generated through docker (use $cmd so you can Ctl-C the build or run)
- $cmd docker build --tag generate:$GOOS $GOOS
- $cmd docker run --interactive --tty --volume $(dirname "$(readlink -f "$0")"):/build generate:$GOOS
- exit
-fi
-
-GOOSARCH_in=syscall_$GOOSARCH.go
-case "$GOOSARCH" in
-_* | *_ | _)
- echo 'undefined $GOOS_$GOARCH:' "$GOOSARCH" 1>&2
- exit 1
- ;;
-darwin_386)
- mkerrors="$mkerrors -m32"
- mksyscall="./mksyscall.pl -l32"
- mksysnum="./mksysnum_darwin.pl $(xcrun --show-sdk-path --sdk macosx)/usr/include/sys/syscall.h"
- mktypes="GOARCH=$GOARCH go tool cgo -godefs"
- ;;
-darwin_amd64)
- mkerrors="$mkerrors -m64"
- mksysnum="./mksysnum_darwin.pl $(xcrun --show-sdk-path --sdk macosx)/usr/include/sys/syscall.h"
- mktypes="GOARCH=$GOARCH go tool cgo -godefs"
- ;;
-darwin_arm)
- mkerrors="$mkerrors"
- mksysnum="./mksysnum_darwin.pl /usr/include/sys/syscall.h"
- mktypes="GOARCH=$GOARCH go tool cgo -godefs"
- ;;
-darwin_arm64)
- mkerrors="$mkerrors -m64"
- mksysnum="./mksysnum_darwin.pl $(xcrun --show-sdk-path --sdk iphoneos)/usr/include/sys/syscall.h"
- mktypes="GOARCH=$GOARCH go tool cgo -godefs"
- ;;
-dragonfly_386)
- mkerrors="$mkerrors -m32"
- mksyscall="./mksyscall.pl -l32 -dragonfly"
- mksysnum="curl -s 'http://gitweb.dragonflybsd.org/dragonfly.git/blob_plain/HEAD:/sys/kern/syscalls.master' | ./mksysnum_dragonfly.pl"
- mktypes="GOARCH=$GOARCH go tool cgo -godefs"
- ;;
-dragonfly_amd64)
- mkerrors="$mkerrors -m64"
- mksyscall="./mksyscall.pl -dragonfly"
- mksysnum="curl -s 'http://gitweb.dragonflybsd.org/dragonfly.git/blob_plain/HEAD:/sys/kern/syscalls.master' | ./mksysnum_dragonfly.pl"
- mktypes="GOARCH=$GOARCH go tool cgo -godefs"
- ;;
-freebsd_386)
- mkerrors="$mkerrors -m32"
- mksyscall="./mksyscall.pl -l32"
- mksysnum="curl -s 'http://svn.freebsd.org/base/stable/10/sys/kern/syscalls.master' | ./mksysnum_freebsd.pl"
- mktypes="GOARCH=$GOARCH go tool cgo -godefs"
- ;;
-freebsd_amd64)
- mkerrors="$mkerrors -m64"
- mksysnum="curl -s 'http://svn.freebsd.org/base/stable/10/sys/kern/syscalls.master' | ./mksysnum_freebsd.pl"
- mktypes="GOARCH=$GOARCH go tool cgo -godefs"
- ;;
-freebsd_arm)
- mkerrors="$mkerrors"
- mksyscall="./mksyscall.pl -l32 -arm"
- mksysnum="curl -s 'http://svn.freebsd.org/base/stable/10/sys/kern/syscalls.master' | ./mksysnum_freebsd.pl"
- # Let the type of C char be signed for making the bare syscall
- # API consistent across over platforms.
- mktypes="GOARCH=$GOARCH go tool cgo -godefs -- -fsigned-char"
- ;;
-linux_sparc64)
- GOOSARCH_in=syscall_linux_sparc64.go
- unistd_h=/usr/include/sparc64-linux-gnu/asm/unistd.h
- mkerrors="$mkerrors -m64"
- mksysnum="./mksysnum_linux.pl $unistd_h"
- mktypes="GOARCH=$GOARCH go tool cgo -godefs"
- ;;
-netbsd_386)
- mkerrors="$mkerrors -m32"
- mksyscall="./mksyscall.pl -l32 -netbsd"
- mksysnum="curl -s 'http://cvsweb.netbsd.org/bsdweb.cgi/~checkout~/src/sys/kern/syscalls.master' | ./mksysnum_netbsd.pl"
- mktypes="GOARCH=$GOARCH go tool cgo -godefs"
- ;;
-netbsd_amd64)
- mkerrors="$mkerrors -m64"
- mksyscall="./mksyscall.pl -netbsd"
- mksysnum="curl -s 'http://cvsweb.netbsd.org/bsdweb.cgi/~checkout~/src/sys/kern/syscalls.master' | ./mksysnum_netbsd.pl"
- mktypes="GOARCH=$GOARCH go tool cgo -godefs"
- ;;
-openbsd_386)
- mkerrors="$mkerrors -m32"
- mksyscall="./mksyscall.pl -l32 -openbsd"
- mksysctl="./mksysctl_openbsd.pl"
- zsysctl="zsysctl_openbsd.go"
- mksysnum="curl -s 'http://cvsweb.openbsd.org/cgi-bin/cvsweb/~checkout~/src/sys/kern/syscalls.master' | ./mksysnum_openbsd.pl"
- mktypes="GOARCH=$GOARCH go tool cgo -godefs"
- ;;
-openbsd_amd64)
- mkerrors="$mkerrors -m64"
- mksyscall="./mksyscall.pl -openbsd"
- mksysctl="./mksysctl_openbsd.pl"
- zsysctl="zsysctl_openbsd.go"
- mksysnum="curl -s 'http://cvsweb.openbsd.org/cgi-bin/cvsweb/~checkout~/src/sys/kern/syscalls.master' | ./mksysnum_openbsd.pl"
- mktypes="GOARCH=$GOARCH go tool cgo -godefs"
- ;;
-solaris_amd64)
- mksyscall="./mksyscall_solaris.pl"
- mkerrors="$mkerrors -m64"
- mksysnum=
- mktypes="GOARCH=$GOARCH go tool cgo -godefs"
- ;;
-*)
- echo 'unrecognized $GOOS_$GOARCH: ' "$GOOSARCH" 1>&2
- exit 1
- ;;
-esac
-
-(
- if [ -n "$mkerrors" ]; then echo "$mkerrors |gofmt >$zerrors"; fi
- case "$GOOS" in
- *)
- syscall_goos="syscall_$GOOS.go"
- case "$GOOS" in
- darwin | dragonfly | freebsd | netbsd | openbsd)
- syscall_goos="syscall_bsd.go $syscall_goos"
- ;;
- esac
- if [ -n "$mksyscall" ]; then echo "$mksyscall -tags $GOOS,$GOARCH $syscall_goos $GOOSARCH_in |gofmt >zsyscall_$GOOSARCH.go"; fi
- ;;
- esac
- if [ -n "$mksysctl" ]; then echo "$mksysctl |gofmt >$zsysctl"; fi
- if [ -n "$mksysnum" ]; then echo "$mksysnum |gofmt >zsysnum_$GOOSARCH.go"; fi
- if [ -n "$mktypes" ]; then
- echo "$mktypes types_$GOOS.go | go run mkpost.go > ztypes_$GOOSARCH.go";
- fi
-) | $run
diff --git a/vendor/golang.org/x/sys/unix/mkerrors.sh b/vendor/golang.org/x/sys/unix/mkerrors.sh
deleted file mode 100755
index 1bd1c9e..0000000
--- a/vendor/golang.org/x/sys/unix/mkerrors.sh
+++ /dev/null
@@ -1,545 +0,0 @@
-#!/usr/bin/env bash
-# Copyright 2009 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.
-
-# Generate Go code listing errors and other #defined constant
-# values (ENAMETOOLONG etc.), by asking the preprocessor
-# about the definitions.
-
-unset LANG
-export LC_ALL=C
-export LC_CTYPE=C
-
-if test -z "$GOARCH" -o -z "$GOOS"; then
- echo 1>&2 "GOARCH or GOOS not defined in environment"
- exit 1
-fi
-
-# Check that we are using the new build system if we should
-if [[ "$GOOS" -eq "linux" ]] && [[ "$GOARCH" != "sparc64" ]]; then
- if [[ "$GOLANG_SYS_BUILD" -ne "docker" ]]; then
- echo 1>&2 "In the new build system, mkerrors should not be called directly."
- echo 1>&2 "See README.md"
- exit 1
- fi
-fi
-
-CC=${CC:-cc}
-
-if [[ "$GOOS" -eq "solaris" ]]; then
- # Assumes GNU versions of utilities in PATH.
- export PATH=/usr/gnu/bin:$PATH
-fi
-
-uname=$(uname)
-
-includes_Darwin='
-#define _DARWIN_C_SOURCE
-#define KERNEL
-#define _DARWIN_USE_64_BIT_INODE
-#include
-#include
-#include
-#include
-#include
-#include
-#include
-#include
-#include
-#include
-#include
-#include
-#include
-#include
-#include
-'
-
-includes_DragonFly='
-#include
-#include
-#include
-#include
-#include
-#include
-#include
-#include
-#include
-#include
-#include
-#include
-#include
-#include
-#include
-#include
-'
-
-includes_FreeBSD='
-#include
-#include
-#include
-#include
-#include
-#include
-#include
-#include
-#include
-#include
-#include
-#include
-#include
-#include
-#include
-#include
-#include
-#include
-
-#if __FreeBSD__ >= 10
-#define IFT_CARP 0xf8 // IFT_CARP is deprecated in FreeBSD 10
-#undef SIOCAIFADDR
-#define SIOCAIFADDR _IOW(105, 26, struct oifaliasreq) // ifaliasreq contains if_data
-#undef SIOCSIFPHYADDR
-#define SIOCSIFPHYADDR _IOW(105, 70, struct oifaliasreq) // ifaliasreq contains if_data
-#endif
-'
-
-includes_Linux='
-#define _LARGEFILE_SOURCE
-#define _LARGEFILE64_SOURCE
-#ifndef __LP64__
-#define _FILE_OFFSET_BITS 64
-#endif
-#define _GNU_SOURCE
-
-// is broken on powerpc64, as it fails to include definitions of
-// these structures. We just include them copied from .
-#if defined(__powerpc__)
-struct sgttyb {
- char sg_ispeed;
- char sg_ospeed;
- char sg_erase;
- char sg_kill;
- short sg_flags;
-};
-
-struct tchars {
- char t_intrc;
- char t_quitc;
- char t_startc;
- char t_stopc;
- char t_eofc;
- char t_brkc;
-};
-
-struct ltchars {
- char t_suspc;
- char t_dsuspc;
- char t_rprntc;
- char t_flushc;
- char t_werasc;
- char t_lnextc;
-};
-#endif
-
-#include
-#include
-#include
-#include
-#include
-#include
-#include
-#include
-#include
-#include
-#include
-#include
-#include
-#include
-#include
-#include
-#include
-#include
-#include
-#include
-#include
-#include
-#include
-#include
-#include
-#include
-#include
-#include
-#include
-#include
-#include
-#include
-#include
-#include
-
-#ifndef MSG_FASTOPEN
-#define MSG_FASTOPEN 0x20000000
-#endif
-
-#ifndef PTRACE_GETREGS
-#define PTRACE_GETREGS 0xc
-#endif
-
-#ifndef PTRACE_SETREGS
-#define PTRACE_SETREGS 0xd
-#endif
-
-#ifndef SOL_NETLINK
-#define SOL_NETLINK 270
-#endif
-
-#ifdef SOL_BLUETOOTH
-// SPARC includes this in /usr/include/sparc64-linux-gnu/bits/socket.h
-// but it is already in bluetooth_linux.go
-#undef SOL_BLUETOOTH
-#endif
-
-// Certain constants are missing from the fs/crypto UAPI
-#define FS_KEY_DESC_PREFIX "fscrypt:"
-#define FS_KEY_DESC_PREFIX_SIZE 8
-#define FS_MAX_KEY_SIZE 64
-'
-
-includes_NetBSD='
-#include
-#include
-#include
-#include
-#include
-#include
-#include
-#include
-#include
-#include
-#include
-#include
-#include
-#include
-#include
-#include
-#include
-#include
-#include
-
-// Needed since refers to it...
-#define schedppq 1
-'
-
-includes_OpenBSD='
-#include
-#include
-#include
-#include
-#include
-#include
-#include
-#include
-#include
-#include
-#include
-#include
-#include
-#include
-#include
-#include
-#include
-#include
-#include
-#include
-#include
-
-// We keep some constants not supported in OpenBSD 5.5 and beyond for
-// the promise of compatibility.
-#define EMUL_ENABLED 0x1
-#define EMUL_NATIVE 0x2
-#define IPV6_FAITH 0x1d
-#define IPV6_OPTIONS 0x1
-#define IPV6_RTHDR_STRICT 0x1
-#define IPV6_SOCKOPT_RESERVED1 0x3
-#define SIOCGIFGENERIC 0xc020693a
-#define SIOCSIFGENERIC 0x80206939
-#define WALTSIG 0x4
-'
-
-includes_SunOS='
-#include
-#include
-#include
-#include
-#include
-#include
-#include
-#include
-#include
-#include
-#include
-#include
-#include
-#include
-#include
-#include
-'
-
-
-includes='
-#include
-#include
-#include
-#include
-#include
-#include
-#include
-#include
-#include
-#include
-#include
-#include
-#include
-#include
-'
-ccflags="$@"
-
-# Write go tool cgo -godefs input.
-(
- echo package unix
- echo
- echo '/*'
- indirect="includes_$(uname)"
- echo "${!indirect} $includes"
- echo '*/'
- echo 'import "C"'
- echo 'import "syscall"'
- echo
- echo 'const ('
-
- # The gcc command line prints all the #defines
- # it encounters while processing the input
- echo "${!indirect} $includes" | $CC -x c - -E -dM $ccflags |
- awk '
- $1 != "#define" || $2 ~ /\(/ || $3 == "" {next}
-
- $2 ~ /^E([ABCD]X|[BIS]P|[SD]I|S|FL)$/ {next} # 386 registers
- $2 ~ /^(SIGEV_|SIGSTKSZ|SIGRT(MIN|MAX))/ {next}
- $2 ~ /^(SCM_SRCRT)$/ {next}
- $2 ~ /^(MAP_FAILED)$/ {next}
- $2 ~ /^ELF_.*$/ {next}# contains ELF_ARCH, etc.
-
- $2 ~ /^EXTATTR_NAMESPACE_NAMES/ ||
- $2 ~ /^EXTATTR_NAMESPACE_[A-Z]+_STRING/ {next}
-
- $2 !~ /^ETH_/ &&
- $2 !~ /^EPROC_/ &&
- $2 !~ /^EQUIV_/ &&
- $2 !~ /^EXPR_/ &&
- $2 ~ /^E[A-Z0-9_]+$/ ||
- $2 ~ /^B[0-9_]+$/ ||
- $2 == "BOTHER" ||
- $2 ~ /^CI?BAUD(EX)?$/ ||
- $2 == "IBSHIFT" ||
- $2 ~ /^V[A-Z0-9]+$/ ||
- $2 ~ /^CS[A-Z0-9]/ ||
- $2 ~ /^I(SIG|CANON|CRNL|UCLC|EXTEN|MAXBEL|STRIP|UTF8)$/ ||
- $2 ~ /^IGN/ ||
- $2 ~ /^IX(ON|ANY|OFF)$/ ||
- $2 ~ /^IN(LCR|PCK)$/ ||
- $2 ~ /(^FLU?SH)|(FLU?SH$)/ ||
- $2 ~ /^C(LOCAL|READ|MSPAR|RTSCTS)$/ ||
- $2 == "BRKINT" ||
- $2 == "HUPCL" ||
- $2 == "PENDIN" ||
- $2 == "TOSTOP" ||
- $2 == "XCASE" ||
- $2 == "ALTWERASE" ||
- $2 == "NOKERNINFO" ||
- $2 ~ /^PAR/ ||
- $2 ~ /^SIG[^_]/ ||
- $2 ~ /^O[CNPFPL][A-Z]+[^_][A-Z]+$/ ||
- $2 ~ /^(NL|CR|TAB|BS|VT|FF)DLY$/ ||
- $2 ~ /^(NL|CR|TAB|BS|VT|FF)[0-9]$/ ||
- $2 ~ /^O?XTABS$/ ||
- $2 ~ /^TC[IO](ON|OFF)$/ ||
- $2 ~ /^IN_/ ||
- $2 ~ /^LOCK_(SH|EX|NB|UN)$/ ||
- $2 ~ /^(AF|SOCK|SO|SOL|IPPROTO|IP|IPV6|ICMP6|TCP|EVFILT|NOTE|EV|SHUT|PROT|MAP|PACKET|MSG|SCM|MCL|DT|MADV|PR)_/ ||
- $2 ~ /^FALLOC_/ ||
- $2 == "ICMPV6_FILTER" ||
- $2 == "SOMAXCONN" ||
- $2 == "NAME_MAX" ||
- $2 == "IFNAMSIZ" ||
- $2 ~ /^CTL_(MAXNAME|NET|QUERY)$/ ||
- $2 ~ /^SYSCTL_VERS/ ||
- $2 ~ /^(MS|MNT)_/ ||
- $2 ~ /^TUN(SET|GET|ATTACH|DETACH)/ ||
- $2 ~ /^(O|F|FD|NAME|S|PTRACE|PT)_/ ||
- $2 ~ /^LINUX_REBOOT_CMD_/ ||
- $2 ~ /^LINUX_REBOOT_MAGIC[12]$/ ||
- $2 !~ "NLA_TYPE_MASK" &&
- $2 ~ /^(NETLINK|NLM|NLMSG|NLA|IFA|IFAN|RT|RTCF|RTN|RTPROT|RTNH|ARPHRD|ETH_P)_/ ||
- $2 ~ /^SIOC/ ||
- $2 ~ /^TIOC/ ||
- $2 ~ /^TCGET/ ||
- $2 ~ /^TCSET/ ||
- $2 ~ /^TC(FLSH|SBRKP?|XONC)$/ ||
- $2 !~ "RTF_BITS" &&
- $2 ~ /^(IFF|IFT|NET_RT|RTM|RTF|RTV|RTA|RTAX)_/ ||
- $2 ~ /^BIOC/ ||
- $2 ~ /^RUSAGE_(SELF|CHILDREN|THREAD)/ ||
- $2 ~ /^RLIMIT_(AS|CORE|CPU|DATA|FSIZE|NOFILE|STACK)|RLIM_INFINITY/ ||
- $2 ~ /^PRIO_(PROCESS|PGRP|USER)/ ||
- $2 ~ /^CLONE_[A-Z_]+/ ||
- $2 !~ /^(BPF_TIMEVAL)$/ &&
- $2 ~ /^(BPF|DLT)_/ ||
- $2 ~ /^CLOCK_/ ||
- $2 ~ /^CAN_/ ||
- $2 ~ /^ALG_/ ||
- $2 ~ /^FS_(POLICY_FLAGS|KEY_DESC|ENCRYPTION_MODE|[A-Z0-9_]+_KEY_SIZE|IOC_(GET|SET)_ENCRYPTION)/ ||
- $2 ~ /^GRND_/ ||
- $2 ~ /^SPLICE_/ ||
- $2 ~ /^(VM|VMADDR)_/ ||
- $2 !~ "WMESGLEN" &&
- $2 ~ /^W[A-Z0-9]+$/ ||
- $2 ~ /^BLK[A-Z]*(GET$|SET$|BUF$|PART$|SIZE)/ {printf("\t%s = C.%s\n", $2, $2)}
- $2 ~ /^__WCOREFLAG$/ {next}
- $2 ~ /^__W[A-Z0-9]+$/ {printf("\t%s = C.%s\n", substr($2,3), $2)}
-
- {next}
- ' | sort
-
- echo ')'
-) >_const.go
-
-# Pull out the error names for later.
-errors=$(
- echo '#include ' | $CC -x c - -E -dM $ccflags |
- awk '$1=="#define" && $2 ~ /^E[A-Z0-9_]+$/ { print $2 }' |
- sort
-)
-
-# Pull out the signal names for later.
-signals=$(
- echo '#include ' | $CC -x c - -E -dM $ccflags |
- awk '$1=="#define" && $2 ~ /^SIG[A-Z0-9]+$/ { print $2 }' |
- egrep -v '(SIGSTKSIZE|SIGSTKSZ|SIGRT)' |
- sort
-)
-
-# Again, writing regexps to a file.
-echo '#include ' | $CC -x c - -E -dM $ccflags |
- awk '$1=="#define" && $2 ~ /^E[A-Z0-9_]+$/ { print "^\t" $2 "[ \t]*=" }' |
- sort >_error.grep
-echo '#include ' | $CC -x c - -E -dM $ccflags |
- awk '$1=="#define" && $2 ~ /^SIG[A-Z0-9]+$/ { print "^\t" $2 "[ \t]*=" }' |
- egrep -v '(SIGSTKSIZE|SIGSTKSZ|SIGRT)' |
- sort >_signal.grep
-
-echo '// mkerrors.sh' "$@"
-echo '// Code generated by the command above; see README.md. DO NOT EDIT.'
-echo
-echo "// +build ${GOARCH},${GOOS}"
-echo
-go tool cgo -godefs -- "$@" _const.go >_error.out
-cat _error.out | grep -vf _error.grep | grep -vf _signal.grep
-echo
-echo '// Errors'
-echo 'const ('
-cat _error.out | grep -f _error.grep | sed 's/=\(.*\)/= syscall.Errno(\1)/'
-echo ')'
-
-echo
-echo '// Signals'
-echo 'const ('
-cat _error.out | grep -f _signal.grep | sed 's/=\(.*\)/= syscall.Signal(\1)/'
-echo ')'
-
-# Run C program to print error and syscall strings.
-(
- echo -E "
-#include
-#include
-#include
-#include
-#include
-#include
-
-#define nelem(x) (sizeof(x)/sizeof((x)[0]))
-
-enum { A = 'A', Z = 'Z', a = 'a', z = 'z' }; // avoid need for single quotes below
-
-int errors[] = {
-"
- for i in $errors
- do
- echo -E ' '$i,
- done
-
- echo -E "
-};
-
-int signals[] = {
-"
- for i in $signals
- do
- echo -E ' '$i,
- done
-
- # Use -E because on some systems bash builtin interprets \n itself.
- echo -E '
-};
-
-static int
-intcmp(const void *a, const void *b)
-{
- return *(int*)a - *(int*)b;
-}
-
-int
-main(void)
-{
- int i, e;
- char buf[1024], *p;
-
- printf("\n\n// Error table\n");
- printf("var errors = [...]string {\n");
- qsort(errors, nelem(errors), sizeof errors[0], intcmp);
- for(i=0; i 0 && errors[i-1] == e)
- continue;
- strcpy(buf, strerror(e));
- // lowercase first letter: Bad -> bad, but STREAM -> STREAM.
- if(A <= buf[0] && buf[0] <= Z && a <= buf[1] && buf[1] <= z)
- buf[0] += a - A;
- printf("\t%d: \"%s\",\n", e, buf);
- }
- printf("}\n\n");
-
- printf("\n\n// Signal table\n");
- printf("var signals = [...]string {\n");
- qsort(signals, nelem(signals), sizeof signals[0], intcmp);
- for(i=0; i 0 && signals[i-1] == e)
- continue;
- strcpy(buf, strsignal(e));
- // lowercase first letter: Bad -> bad, but STREAM -> STREAM.
- if(A <= buf[0] && buf[0] <= Z && a <= buf[1] && buf[1] <= z)
- buf[0] += a - A;
- // cut trailing : number.
- p = strrchr(buf, ":"[0]);
- if(p)
- *p = '\0';
- printf("\t%d: \"%s\",\n", e, buf);
- }
- printf("}\n\n");
-
- return 0;
-}
-
-'
-) >_errors.c
-
-$CC $ccflags -o _errors _errors.c && $GORUN ./_errors && rm -f _errors.c _errors _const.go _error.grep _signal.grep _error.out
diff --git a/vendor/golang.org/x/sys/unix/mkpost.go b/vendor/golang.org/x/sys/unix/mkpost.go
deleted file mode 100644
index d3ff659..0000000
--- a/vendor/golang.org/x/sys/unix/mkpost.go
+++ /dev/null
@@ -1,88 +0,0 @@
-// Copyright 2016 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.
-
-// +build ignore
-
-// mkpost processes the output of cgo -godefs to
-// modify the generated types. It is used to clean up
-// the sys API in an architecture specific manner.
-//
-// mkpost is run after cgo -godefs; see README.md.
-package main
-
-import (
- "bytes"
- "fmt"
- "go/format"
- "io/ioutil"
- "log"
- "os"
- "regexp"
-)
-
-func main() {
- // Get the OS and architecture (using GOARCH_TARGET if it exists)
- goos := os.Getenv("GOOS")
- goarch := os.Getenv("GOARCH_TARGET")
- if goarch == "" {
- goarch = os.Getenv("GOARCH")
- }
- // Check that we are using the new build system if we should be.
- if goos == "linux" && goarch != "sparc64" {
- if os.Getenv("GOLANG_SYS_BUILD") != "docker" {
- os.Stderr.WriteString("In the new build system, mkpost should not be called directly.\n")
- os.Stderr.WriteString("See README.md\n")
- os.Exit(1)
- }
- }
-
- b, err := ioutil.ReadAll(os.Stdin)
- if err != nil {
- log.Fatal(err)
- }
-
- // If we have empty Ptrace structs, we should delete them. Only s390x emits
- // nonempty Ptrace structs.
- ptraceRexexp := regexp.MustCompile(`type Ptrace((Psw|Fpregs|Per) struct {\s*})`)
- b = ptraceRexexp.ReplaceAll(b, nil)
-
- // Replace the control_regs union with a blank identifier for now.
- controlRegsRegex := regexp.MustCompile(`(Control_regs)\s+\[0\]uint64`)
- b = controlRegsRegex.ReplaceAll(b, []byte("_ [0]uint64"))
-
- // Remove fields that are added by glibc
- // Note that this is unstable as the identifers are private.
- removeFieldsRegex := regexp.MustCompile(`X__glibc\S*`)
- b = removeFieldsRegex.ReplaceAll(b, []byte("_"))
-
- // We refuse to export private fields on s390x
- if goarch == "s390x" && goos == "linux" {
- // Remove cgo padding fields
- removeFieldsRegex := regexp.MustCompile(`Pad_cgo_\d+`)
- b = removeFieldsRegex.ReplaceAll(b, []byte("_"))
-
- // Remove padding, hidden, or unused fields
- removeFieldsRegex = regexp.MustCompile(`X_\S+`)
- b = removeFieldsRegex.ReplaceAll(b, []byte("_"))
- }
-
- // Remove the first line of warning from cgo
- b = b[bytes.IndexByte(b, '\n')+1:]
- // Modify the command in the header to include:
- // mkpost, our own warning, and a build tag.
- replacement := fmt.Sprintf(`$1 | go run mkpost.go
-// Code generated by the command above; see README.md. DO NOT EDIT.
-
-// +build %s,%s`, goarch, goos)
- cgoCommandRegex := regexp.MustCompile(`(cgo -godefs .*)`)
- b = cgoCommandRegex.ReplaceAll(b, []byte(replacement))
-
- // gofmt
- b, err = format.Source(b)
- if err != nil {
- log.Fatal(err)
- }
-
- os.Stdout.Write(b)
-}
diff --git a/vendor/golang.org/x/sys/unix/mksyscall.pl b/vendor/golang.org/x/sys/unix/mksyscall.pl
deleted file mode 100755
index fb929b4..0000000
--- a/vendor/golang.org/x/sys/unix/mksyscall.pl
+++ /dev/null
@@ -1,328 +0,0 @@
-#!/usr/bin/env perl
-# Copyright 2009 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.
-
-# This program reads a file containing function prototypes
-# (like syscall_darwin.go) and generates system call bodies.
-# The prototypes are marked by lines beginning with "//sys"
-# and read like func declarations if //sys is replaced by func, but:
-# * The parameter lists must give a name for each argument.
-# This includes return parameters.
-# * The parameter lists must give a type for each argument:
-# the (x, y, z int) shorthand is not allowed.
-# * If the return parameter is an error number, it must be named errno.
-
-# A line beginning with //sysnb is like //sys, except that the
-# goroutine will not be suspended during the execution of the system
-# call. This must only be used for system calls which can never
-# block, as otherwise the system call could cause all goroutines to
-# hang.
-
-use strict;
-
-my $cmdline = "mksyscall.pl " . join(' ', @ARGV);
-my $errors = 0;
-my $_32bit = "";
-my $plan9 = 0;
-my $openbsd = 0;
-my $netbsd = 0;
-my $dragonfly = 0;
-my $arm = 0; # 64-bit value should use (even, odd)-pair
-my $tags = ""; # build tags
-
-if($ARGV[0] eq "-b32") {
- $_32bit = "big-endian";
- shift;
-} elsif($ARGV[0] eq "-l32") {
- $_32bit = "little-endian";
- shift;
-}
-if($ARGV[0] eq "-plan9") {
- $plan9 = 1;
- shift;
-}
-if($ARGV[0] eq "-openbsd") {
- $openbsd = 1;
- shift;
-}
-if($ARGV[0] eq "-netbsd") {
- $netbsd = 1;
- shift;
-}
-if($ARGV[0] eq "-dragonfly") {
- $dragonfly = 1;
- shift;
-}
-if($ARGV[0] eq "-arm") {
- $arm = 1;
- shift;
-}
-if($ARGV[0] eq "-tags") {
- shift;
- $tags = $ARGV[0];
- shift;
-}
-
-if($ARGV[0] =~ /^-/) {
- print STDERR "usage: mksyscall.pl [-b32 | -l32] [-tags x,y] [file ...]\n";
- exit 1;
-}
-
-# Check that we are using the new build system if we should
-if($ENV{'GOOS'} eq "linux" && $ENV{'GOARCH'} ne "sparc64") {
- if($ENV{'GOLANG_SYS_BUILD'} ne "docker") {
- print STDERR "In the new build system, mksyscall should not be called directly.\n";
- print STDERR "See README.md\n";
- exit 1;
- }
-}
-
-
-sub parseparamlist($) {
- my ($list) = @_;
- $list =~ s/^\s*//;
- $list =~ s/\s*$//;
- if($list eq "") {
- return ();
- }
- return split(/\s*,\s*/, $list);
-}
-
-sub parseparam($) {
- my ($p) = @_;
- if($p !~ /^(\S*) (\S*)$/) {
- print STDERR "$ARGV:$.: malformed parameter: $p\n";
- $errors = 1;
- return ("xx", "int");
- }
- return ($1, $2);
-}
-
-my $text = "";
-while(<>) {
- chomp;
- s/\s+/ /g;
- s/^\s+//;
- s/\s+$//;
- my $nonblock = /^\/\/sysnb /;
- next if !/^\/\/sys / && !$nonblock;
-
- # Line must be of the form
- # func Open(path string, mode int, perm int) (fd int, errno error)
- # Split into name, in params, out params.
- if(!/^\/\/sys(nb)? (\w+)\(([^()]*)\)\s*(?:\(([^()]+)\))?\s*(?:=\s*((?i)SYS_[A-Z0-9_]+))?$/) {
- print STDERR "$ARGV:$.: malformed //sys declaration\n";
- $errors = 1;
- next;
- }
- my ($func, $in, $out, $sysname) = ($2, $3, $4, $5);
-
- # Split argument lists on comma.
- my @in = parseparamlist($in);
- my @out = parseparamlist($out);
-
- # Try in vain to keep people from editing this file.
- # The theory is that they jump into the middle of the file
- # without reading the header.
- $text .= "// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT\n\n";
-
- # Go function header.
- my $out_decl = @out ? sprintf(" (%s)", join(', ', @out)) : "";
- $text .= sprintf "func %s(%s)%s {\n", $func, join(', ', @in), $out_decl;
-
- # Check if err return available
- my $errvar = "";
- foreach my $p (@out) {
- my ($name, $type) = parseparam($p);
- if($type eq "error") {
- $errvar = $name;
- last;
- }
- }
-
- # Prepare arguments to Syscall.
- my @args = ();
- my $n = 0;
- foreach my $p (@in) {
- my ($name, $type) = parseparam($p);
- if($type =~ /^\*/) {
- push @args, "uintptr(unsafe.Pointer($name))";
- } elsif($type eq "string" && $errvar ne "") {
- $text .= "\tvar _p$n *byte\n";
- $text .= "\t_p$n, $errvar = BytePtrFromString($name)\n";
- $text .= "\tif $errvar != nil {\n\t\treturn\n\t}\n";
- push @args, "uintptr(unsafe.Pointer(_p$n))";
- $n++;
- } elsif($type eq "string") {
- print STDERR "$ARGV:$.: $func uses string arguments, but has no error return\n";
- $text .= "\tvar _p$n *byte\n";
- $text .= "\t_p$n, _ = BytePtrFromString($name)\n";
- push @args, "uintptr(unsafe.Pointer(_p$n))";
- $n++;
- } elsif($type =~ /^\[\](.*)/) {
- # Convert slice into pointer, length.
- # Have to be careful not to take address of &a[0] if len == 0:
- # pass dummy pointer in that case.
- # Used to pass nil, but some OSes or simulators reject write(fd, nil, 0).
- $text .= "\tvar _p$n unsafe.Pointer\n";
- $text .= "\tif len($name) > 0 {\n\t\t_p$n = unsafe.Pointer(\&${name}[0])\n\t}";
- $text .= " else {\n\t\t_p$n = unsafe.Pointer(&_zero)\n\t}";
- $text .= "\n";
- push @args, "uintptr(_p$n)", "uintptr(len($name))";
- $n++;
- } elsif($type eq "int64" && ($openbsd || $netbsd)) {
- push @args, "0";
- if($_32bit eq "big-endian") {
- push @args, "uintptr($name>>32)", "uintptr($name)";
- } elsif($_32bit eq "little-endian") {
- push @args, "uintptr($name)", "uintptr($name>>32)";
- } else {
- push @args, "uintptr($name)";
- }
- } elsif($type eq "int64" && $dragonfly) {
- if ($func !~ /^extp(read|write)/i) {
- push @args, "0";
- }
- if($_32bit eq "big-endian") {
- push @args, "uintptr($name>>32)", "uintptr($name)";
- } elsif($_32bit eq "little-endian") {
- push @args, "uintptr($name)", "uintptr($name>>32)";
- } else {
- push @args, "uintptr($name)";
- }
- } elsif($type eq "int64" && $_32bit ne "") {
- if(@args % 2 && $arm) {
- # arm abi specifies 64-bit argument uses
- # (even, odd) pair
- push @args, "0"
- }
- if($_32bit eq "big-endian") {
- push @args, "uintptr($name>>32)", "uintptr($name)";
- } else {
- push @args, "uintptr($name)", "uintptr($name>>32)";
- }
- } else {
- push @args, "uintptr($name)";
- }
- }
-
- # Determine which form to use; pad args with zeros.
- my $asm = "Syscall";
- if ($nonblock) {
- $asm = "RawSyscall";
- }
- if(@args <= 3) {
- while(@args < 3) {
- push @args, "0";
- }
- } elsif(@args <= 6) {
- $asm .= "6";
- while(@args < 6) {
- push @args, "0";
- }
- } elsif(@args <= 9) {
- $asm .= "9";
- while(@args < 9) {
- push @args, "0";
- }
- } else {
- print STDERR "$ARGV:$.: too many arguments to system call\n";
- }
-
- # System call number.
- if($sysname eq "") {
- $sysname = "SYS_$func";
- $sysname =~ s/([a-z])([A-Z])/${1}_$2/g; # turn FooBar into Foo_Bar
- $sysname =~ y/a-z/A-Z/;
- }
-
- # Actual call.
- my $args = join(', ', @args);
- my $call = "$asm($sysname, $args)";
-
- # Assign return values.
- my $body = "";
- my @ret = ("_", "_", "_");
- my $do_errno = 0;
- for(my $i=0; $i<@out; $i++) {
- my $p = $out[$i];
- my ($name, $type) = parseparam($p);
- my $reg = "";
- if($name eq "err" && !$plan9) {
- $reg = "e1";
- $ret[2] = $reg;
- $do_errno = 1;
- } elsif($name eq "err" && $plan9) {
- $ret[0] = "r0";
- $ret[2] = "e1";
- next;
- } else {
- $reg = sprintf("r%d", $i);
- $ret[$i] = $reg;
- }
- if($type eq "bool") {
- $reg = "$reg != 0";
- }
- if($type eq "int64" && $_32bit ne "") {
- # 64-bit number in r1:r0 or r0:r1.
- if($i+2 > @out) {
- print STDERR "$ARGV:$.: not enough registers for int64 return\n";
- }
- if($_32bit eq "big-endian") {
- $reg = sprintf("int64(r%d)<<32 | int64(r%d)", $i, $i+1);
- } else {
- $reg = sprintf("int64(r%d)<<32 | int64(r%d)", $i+1, $i);
- }
- $ret[$i] = sprintf("r%d", $i);
- $ret[$i+1] = sprintf("r%d", $i+1);
- }
- if($reg ne "e1" || $plan9) {
- $body .= "\t$name = $type($reg)\n";
- }
- }
- if ($ret[0] eq "_" && $ret[1] eq "_" && $ret[2] eq "_") {
- $text .= "\t$call\n";
- } else {
- $text .= "\t$ret[0], $ret[1], $ret[2] := $call\n";
- }
- $text .= $body;
-
- if ($plan9 && $ret[2] eq "e1") {
- $text .= "\tif int32(r0) == -1 {\n";
- $text .= "\t\terr = e1\n";
- $text .= "\t}\n";
- } elsif ($do_errno) {
- $text .= "\tif e1 != 0 {\n";
- $text .= "\t\terr = errnoErr(e1)\n";
- $text .= "\t}\n";
- }
- $text .= "\treturn\n";
- $text .= "}\n\n";
-}
-
-chomp $text;
-chomp $text;
-
-if($errors) {
- exit 1;
-}
-
-print <) {
- chomp;
- s/\s+/ /g;
- s/^\s+//;
- s/\s+$//;
- $package = $1 if !$package && /^package (\S+)$/;
- my $nonblock = /^\/\/sysnb /;
- next if !/^\/\/sys / && !$nonblock;
-
- # Line must be of the form
- # func Open(path string, mode int, perm int) (fd int, err error)
- # Split into name, in params, out params.
- if(!/^\/\/sys(nb)? (\w+)\(([^()]*)\)\s*(?:\(([^()]+)\))?\s*(?:=\s*(?:(\w*)\.)?(\w*))?$/) {
- print STDERR "$ARGV:$.: malformed //sys declaration\n";
- $errors = 1;
- next;
- }
- my ($nb, $func, $in, $out, $modname, $sysname) = ($1, $2, $3, $4, $5, $6);
-
- # Split argument lists on comma.
- my @in = parseparamlist($in);
- my @out = parseparamlist($out);
-
- # So file name.
- if($modname eq "") {
- $modname = "libc";
- }
-
- # System call name.
- if($sysname eq "") {
- $sysname = "$func";
- }
-
- # System call pointer variable name.
- my $sysvarname = "proc$sysname";
-
- my $strconvfunc = "BytePtrFromString";
- my $strconvtype = "*byte";
-
- $sysname =~ y/A-Z/a-z/; # All libc functions are lowercase.
-
- # Runtime import of function to allow cross-platform builds.
- $dynimports .= "//go:cgo_import_dynamic libc_${sysname} ${sysname} \"$modname.so\"\n";
- # Link symbol to proc address variable.
- $linknames .= "//go:linkname ${sysvarname} libc_${sysname}\n";
- # Library proc address variable.
- push @vars, $sysvarname;
-
- # Go function header.
- $out = join(', ', @out);
- if($out ne "") {
- $out = " ($out)";
- }
- if($text ne "") {
- $text .= "\n"
- }
- $text .= sprintf "func %s(%s)%s {\n", $func, join(', ', @in), $out;
-
- # Check if err return available
- my $errvar = "";
- foreach my $p (@out) {
- my ($name, $type) = parseparam($p);
- if($type eq "error") {
- $errvar = $name;
- last;
- }
- }
-
- # Prepare arguments to Syscall.
- my @args = ();
- my $n = 0;
- foreach my $p (@in) {
- my ($name, $type) = parseparam($p);
- if($type =~ /^\*/) {
- push @args, "uintptr(unsafe.Pointer($name))";
- } elsif($type eq "string" && $errvar ne "") {
- $text .= "\tvar _p$n $strconvtype\n";
- $text .= "\t_p$n, $errvar = $strconvfunc($name)\n";
- $text .= "\tif $errvar != nil {\n\t\treturn\n\t}\n";
- push @args, "uintptr(unsafe.Pointer(_p$n))";
- $n++;
- } elsif($type eq "string") {
- print STDERR "$ARGV:$.: $func uses string arguments, but has no error return\n";
- $text .= "\tvar _p$n $strconvtype\n";
- $text .= "\t_p$n, _ = $strconvfunc($name)\n";
- push @args, "uintptr(unsafe.Pointer(_p$n))";
- $n++;
- } elsif($type =~ /^\[\](.*)/) {
- # Convert slice into pointer, length.
- # Have to be careful not to take address of &a[0] if len == 0:
- # pass nil in that case.
- $text .= "\tvar _p$n *$1\n";
- $text .= "\tif len($name) > 0 {\n\t\t_p$n = \&$name\[0]\n\t}\n";
- push @args, "uintptr(unsafe.Pointer(_p$n))", "uintptr(len($name))";
- $n++;
- } elsif($type eq "int64" && $_32bit ne "") {
- if($_32bit eq "big-endian") {
- push @args, "uintptr($name >> 32)", "uintptr($name)";
- } else {
- push @args, "uintptr($name)", "uintptr($name >> 32)";
- }
- } elsif($type eq "bool") {
- $text .= "\tvar _p$n uint32\n";
- $text .= "\tif $name {\n\t\t_p$n = 1\n\t} else {\n\t\t_p$n = 0\n\t}\n";
- push @args, "uintptr(_p$n)";
- $n++;
- } else {
- push @args, "uintptr($name)";
- }
- }
- my $nargs = @args;
-
- # Determine which form to use; pad args with zeros.
- my $asm = "sysvicall6";
- if ($nonblock) {
- $asm = "rawSysvicall6";
- }
- if(@args <= 6) {
- while(@args < 6) {
- push @args, "0";
- }
- } else {
- print STDERR "$ARGV:$.: too many arguments to system call\n";
- }
-
- # Actual call.
- my $args = join(', ', @args);
- my $call = "$asm(uintptr(unsafe.Pointer(&$sysvarname)), $nargs, $args)";
-
- # Assign return values.
- my $body = "";
- my $failexpr = "";
- my @ret = ("_", "_", "_");
- my @pout= ();
- my $do_errno = 0;
- for(my $i=0; $i<@out; $i++) {
- my $p = $out[$i];
- my ($name, $type) = parseparam($p);
- my $reg = "";
- if($name eq "err") {
- $reg = "e1";
- $ret[2] = $reg;
- $do_errno = 1;
- } else {
- $reg = sprintf("r%d", $i);
- $ret[$i] = $reg;
- }
- if($type eq "bool") {
- $reg = "$reg != 0";
- }
- if($type eq "int64" && $_32bit ne "") {
- # 64-bit number in r1:r0 or r0:r1.
- if($i+2 > @out) {
- print STDERR "$ARGV:$.: not enough registers for int64 return\n";
- }
- if($_32bit eq "big-endian") {
- $reg = sprintf("int64(r%d)<<32 | int64(r%d)", $i, $i+1);
- } else {
- $reg = sprintf("int64(r%d)<<32 | int64(r%d)", $i+1, $i);
- }
- $ret[$i] = sprintf("r%d", $i);
- $ret[$i+1] = sprintf("r%d", $i+1);
- }
- if($reg ne "e1") {
- $body .= "\t$name = $type($reg)\n";
- }
- }
- if ($ret[0] eq "_" && $ret[1] eq "_" && $ret[2] eq "_") {
- $text .= "\t$call\n";
- } else {
- $text .= "\t$ret[0], $ret[1], $ret[2] := $call\n";
- }
- $text .= $body;
-
- if ($do_errno) {
- $text .= "\tif e1 != 0 {\n";
- $text .= "\t\terr = e1\n";
- $text .= "\t}\n";
- }
- $text .= "\treturn\n";
- $text .= "}\n";
-}
-
-if($errors) {
- exit 1;
-}
-
-print < "net.inet",
- "net.inet.ipproto" => "net.inet",
- "net.inet6.ipv6proto" => "net.inet6",
- "net.inet6.ipv6" => "net.inet6.ip6",
- "net.inet.icmpv6" => "net.inet6.icmp6",
- "net.inet6.divert6" => "net.inet6.divert",
- "net.inet6.tcp6" => "net.inet.tcp",
- "net.inet6.udp6" => "net.inet.udp",
- "mpls" => "net.mpls",
- "swpenc" => "vm.swapencrypt"
-);
-
-# Node mappings
-my %node_map = (
- "net.inet.ip.ifq" => "net.ifq",
- "net.inet.pfsync" => "net.pfsync",
- "net.mpls.ifq" => "net.ifq"
-);
-
-my $ctlname;
-my %mib = ();
-my %sysctl = ();
-my $node;
-
-sub debug() {
- print STDERR "$_[0]\n" if $debug;
-}
-
-# Walk the MIB and build a sysctl name to OID mapping.
-sub build_sysctl() {
- my ($node, $name, $oid) = @_;
- my %node = %{$node};
- my @oid = @{$oid};
-
- foreach my $key (sort keys %node) {
- my @node = @{$node{$key}};
- my $nodename = $name.($name ne '' ? '.' : '').$key;
- my @nodeoid = (@oid, $node[0]);
- if ($node[1] eq 'CTLTYPE_NODE') {
- if (exists $node_map{$nodename}) {
- $node = \%mib;
- $ctlname = $node_map{$nodename};
- foreach my $part (split /\./, $ctlname) {
- $node = \%{@{$$node{$part}}[2]};
- }
- } else {
- $node = $node[2];
- }
- &build_sysctl($node, $nodename, \@nodeoid);
- } elsif ($node[1] ne '') {
- $sysctl{$nodename} = \@nodeoid;
- }
- }
-}
-
-foreach my $ctl (@ctls) {
- $ctls{$ctl} = $ctl;
-}
-
-# Build MIB
-foreach my $header (@headers) {
- &debug("Processing $header...");
- open HEADER, "/usr/include/$header" ||
- print STDERR "Failed to open $header\n";
- while () {
- if ($_ =~ /^#define\s+(CTL_NAMES)\s+{/ ||
- $_ =~ /^#define\s+(CTL_(.*)_NAMES)\s+{/ ||
- $_ =~ /^#define\s+((.*)CTL_NAMES)\s+{/) {
- if ($1 eq 'CTL_NAMES') {
- # Top level.
- $node = \%mib;
- } else {
- # Node.
- my $nodename = lc($2);
- if ($header =~ /^netinet\//) {
- $ctlname = "net.inet.$nodename";
- } elsif ($header =~ /^netinet6\//) {
- $ctlname = "net.inet6.$nodename";
- } elsif ($header =~ /^net\//) {
- $ctlname = "net.$nodename";
- } else {
- $ctlname = "$nodename";
- $ctlname =~ s/^(fs|net|kern)_/$1\./;
- }
- if (exists $ctl_map{$ctlname}) {
- $ctlname = $ctl_map{$ctlname};
- }
- if (not exists $ctls{$ctlname}) {
- &debug("Ignoring $ctlname...");
- next;
- }
-
- # Walk down from the top of the MIB.
- $node = \%mib;
- foreach my $part (split /\./, $ctlname) {
- if (not exists $$node{$part}) {
- &debug("Missing node $part");
- $$node{$part} = [ 0, '', {} ];
- }
- $node = \%{@{$$node{$part}}[2]};
- }
- }
-
- # Populate current node with entries.
- my $i = -1;
- while (defined($_) && $_ !~ /^}/) {
- $_ =