Mirror of BoringSSL (grpc依赖)
https://boringssl.googlesource.com/boringssl
You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
713 lines
18 KiB
713 lines
18 KiB
// Copyright (c) 2019, Google Inc. |
|
// |
|
// Permission to use, copy, modify, and/or distribute this software for any |
|
// purpose with or without fee is hereby granted, provided that the above |
|
// copyright notice and this permission notice appear in all copies. |
|
// |
|
// THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES |
|
// WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF |
|
// MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY |
|
// SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES |
|
// WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION |
|
// OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN |
|
// CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. |
|
|
|
// +build interactive |
|
|
|
package main |
|
|
|
import ( |
|
"bytes" |
|
"encoding/json" |
|
"errors" |
|
"fmt" |
|
"io" |
|
"io/ioutil" |
|
neturl "net/url" |
|
"os" |
|
"os/exec" |
|
"os/signal" |
|
"path/filepath" |
|
"reflect" |
|
"strconv" |
|
"strings" |
|
"syscall" |
|
|
|
"boringssl.googlesource.com/boringssl/util/fipstools/acvp/acvptool/acvp" |
|
"golang.org/x/crypto/ssh/terminal" |
|
) |
|
|
|
const interactiveModeSupported = true |
|
|
|
func updateTerminalSize(term *terminal.Terminal) { |
|
width, height, err := terminal.GetSize(0) |
|
if err != nil { |
|
return |
|
} |
|
term.SetSize(width, height) |
|
} |
|
|
|
func skipWS(node *node32) *node32 { |
|
for ; node != nil && node.pegRule == ruleWS; node = node.next { |
|
} |
|
return node |
|
} |
|
|
|
func assertNodeType(node *node32, rule pegRule) { |
|
if node.pegRule != rule { |
|
panic(fmt.Sprintf("expected %q, found %q", rul3s[rule], rul3s[node.pegRule])) |
|
} |
|
} |
|
|
|
type Object interface { |
|
String() (string, error) |
|
Index(string) (Object, error) |
|
Search(acvp.Query) (Object, error) |
|
Action(action string, args []string) error |
|
} |
|
|
|
type ServerObjectSet struct { |
|
env *Env |
|
name string |
|
searchKeys map[string][]acvp.Relation |
|
resultType reflect.Type |
|
subObjects map[string]func(*Env, string) (Object, error) |
|
canEnumerate bool |
|
} |
|
|
|
func (set ServerObjectSet) String() (string, error) { |
|
if !set.canEnumerate { |
|
return "[object set " + set.name + "]", nil |
|
} |
|
|
|
data := reflect.New(reflect.SliceOf(set.resultType)).Interface() |
|
if err := set.env.server.GetPaged(data, "acvp/v1/"+set.name, nil); err != nil { |
|
return "", err |
|
} |
|
ret, err := json.MarshalIndent(data, "", " ") |
|
return string(ret), err |
|
} |
|
|
|
func (set ServerObjectSet) Index(indexStr string) (Object, error) { |
|
index, err := strconv.ParseUint(indexStr, 0, 64) |
|
if err != nil { |
|
return nil, fmt.Errorf("object set indexes must be unsigned integers, trying to parse %q failed: %s", indexStr, err) |
|
} |
|
return ServerObject{&set, index}, nil |
|
} |
|
|
|
func (set ServerObjectSet) Search(condition acvp.Query) (Object, error) { |
|
if set.searchKeys == nil { |
|
return nil, errors.New("this object set cannot be searched") |
|
} |
|
|
|
for _, conj := range condition { |
|
NextCondition: |
|
for _, cond := range conj { |
|
allowed, ok := set.searchKeys[cond.Param] |
|
if !ok { |
|
return nil, fmt.Errorf("search key %q not valid for this object set", cond.Param) |
|
} |
|
|
|
for _, rel := range allowed { |
|
if rel == cond.Relation { |
|
continue NextCondition |
|
} |
|
} |
|
|
|
return nil, fmt.Errorf("search key %q cannot be used with relation %q", cond.Param, cond.Relation.String()) |
|
} |
|
} |
|
|
|
return Search{ServerObjectSet: set, query: condition}, nil |
|
} |
|
|
|
func (set ServerObjectSet) Action(action string, args []string) error { |
|
switch action { |
|
default: |
|
return fmt.Errorf("unknown action %q", action) |
|
|
|
case "new": |
|
if len(args) != 0 { |
|
return fmt.Errorf("found %d arguments but %q takes none", len(args), action) |
|
} |
|
|
|
newContents, err := edit("") |
|
if err != nil { |
|
return err |
|
} |
|
|
|
if strings.TrimSpace(string(newContents)) == "" { |
|
io.WriteString(set.env.term, "Resulting file was empty. Ignoring.\n") |
|
return nil |
|
} |
|
|
|
var result map[string]interface{} |
|
if err := set.env.server.Post(&result, "acvp/v1/"+set.name, newContents); err != nil { |
|
return err |
|
} |
|
|
|
// In case it's a testSession that was just created, poke any access token |
|
// into the server's lookup table and the cache. |
|
if urlInterface, ok := result["url"]; ok { |
|
if url, ok := urlInterface.(string); ok { |
|
if tokenInterface, ok := result["accessToken"]; ok { |
|
if token, ok := tokenInterface.(string); ok { |
|
for strings.HasPrefix(url, "/") { |
|
url = url[1:] |
|
} |
|
set.env.server.PrefixTokens[url] = token |
|
if len(set.env.config.SessionTokensCache) > 0 { |
|
ioutil.WriteFile(filepath.Join(set.env.config.SessionTokensCache, neturl.PathEscape(url))+".token", []byte(token), 0600) |
|
} |
|
} |
|
} |
|
} |
|
} |
|
|
|
ret, err := json.MarshalIndent(result, "", " ") |
|
if err != nil { |
|
return err |
|
} |
|
set.env.term.Write(ret) |
|
return nil |
|
} |
|
} |
|
|
|
type ServerObject struct { |
|
set *ServerObjectSet |
|
index uint64 |
|
} |
|
|
|
func (obj ServerObject) String() (string, error) { |
|
data := reflect.New(obj.set.resultType).Interface() |
|
if err := obj.set.env.server.Get(data, "acvp/v1/"+obj.set.name+"/"+strconv.FormatUint(obj.index, 10)); err != nil { |
|
return "", err |
|
} |
|
ret, err := json.MarshalIndent(data, "", " ") |
|
return string(ret), err |
|
} |
|
|
|
func (obj ServerObject) Index(index string) (Object, error) { |
|
if obj.set.subObjects == nil { |
|
return nil, errors.New("cannot index " + obj.set.name + " objects") |
|
} |
|
constr, ok := obj.set.subObjects[index] |
|
if !ok { |
|
return nil, fmt.Errorf("no such subobject %q", index) |
|
} |
|
return constr(obj.set.env, fmt.Sprintf("%s/%d", obj.set.name, obj.index)) |
|
} |
|
|
|
func (ServerObject) Search(condition acvp.Query) (Object, error) { |
|
return nil, errors.New("cannot search individual object") |
|
} |
|
|
|
func edit(initialContents string) ([]byte, error) { |
|
tmp, err := ioutil.TempFile("", "acvp*.json") |
|
if err != nil { |
|
return nil, err |
|
} |
|
path := tmp.Name() |
|
defer os.Remove(path) |
|
|
|
_, err = io.WriteString(tmp, initialContents) |
|
tmp.Close() |
|
if err != nil { |
|
return nil, err |
|
} |
|
|
|
editor := os.Getenv("EDITOR") |
|
if len(editor) == 0 { |
|
editor = "vim" |
|
} |
|
|
|
cmd := exec.Command(editor, path) |
|
cmd.Stdout = os.Stdout |
|
cmd.Stdin = os.Stdin |
|
cmd.Stderr = os.Stderr |
|
if err := cmd.Run(); err != nil { |
|
return nil, err |
|
} |
|
|
|
return ioutil.ReadFile(path) |
|
} |
|
|
|
func (obj ServerObject) Action(action string, args []string) error { |
|
switch action { |
|
default: |
|
return fmt.Errorf("unknown action %q", action) |
|
|
|
case "edit": |
|
if len(args) != 0 { |
|
return fmt.Errorf("found %d arguments but %q takes none", len(args), action) |
|
} |
|
|
|
contents, err := obj.String() |
|
if err != nil { |
|
return err |
|
} |
|
|
|
newContents, err := edit(contents) |
|
if err != nil { |
|
return err |
|
} |
|
|
|
if trimmed := strings.TrimSpace(string(newContents)); len(trimmed) == 0 || trimmed == strings.TrimSpace(contents) { |
|
io.WriteString(obj.set.env.term, "Resulting file was equal or empty. Not updating.\n") |
|
return nil |
|
} |
|
|
|
var status acvp.RequestStatus |
|
if err := obj.set.env.server.Put(&status, "acvp/v1/"+obj.set.name+"/"+strconv.FormatUint(obj.index, 10), newContents); err != nil { |
|
return err |
|
} |
|
|
|
fmt.Fprintf(obj.set.env.term, "%#v\n", status) |
|
return nil |
|
|
|
case "delete": |
|
if len(args) != 0 { |
|
return fmt.Errorf("found %d arguments but %q takes none", len(args), action) |
|
} |
|
return obj.set.env.server.Delete("acvp/v1/" + obj.set.name + "/" + strconv.FormatUint(obj.index, 10)) |
|
} |
|
} |
|
|
|
type Search struct { |
|
ServerObjectSet |
|
query acvp.Query |
|
} |
|
|
|
func (search Search) String() (string, error) { |
|
data := reflect.New(reflect.SliceOf(search.resultType)).Interface() |
|
fmt.Printf("Searching for %#v\n", search.query) |
|
if err := search.env.server.GetPaged(data, "acvp/v1/"+search.name, search.query); err != nil { |
|
return "", err |
|
} |
|
ret, err := json.MarshalIndent(data, "", " ") |
|
return string(ret), err |
|
} |
|
|
|
func (search Search) Index(_ string) (Object, error) { |
|
return nil, errors.New("indexing of search results not supported") |
|
} |
|
|
|
func (search Search) Search(condition acvp.Query) (Object, error) { |
|
search.query = append(search.query, condition...) |
|
return search, nil |
|
} |
|
|
|
func (Search) Action(_ string, _ []string) error { |
|
return errors.New("no actions supported on search objects") |
|
} |
|
|
|
type Algorithms struct { |
|
ServerObjectSet |
|
} |
|
|
|
func (algos Algorithms) String() (string, error) { |
|
var result struct { |
|
Algorithms []map[string]interface{} `json:"algorithms"` |
|
} |
|
if err := algos.env.server.Get(&result, "acvp/v1/algorithms"); err != nil { |
|
return "", err |
|
} |
|
ret, err := json.MarshalIndent(result.Algorithms, "", " ") |
|
return string(ret), err |
|
} |
|
|
|
type Env struct { |
|
line string |
|
variables map[string]Object |
|
server *acvp.Server |
|
term *terminal.Terminal |
|
config Config |
|
} |
|
|
|
func (e *Env) bytes(node *node32) []byte { |
|
return []byte(e.line[node.begin:node.end]) |
|
} |
|
|
|
func (e *Env) contents(node *node32) string { |
|
return e.line[node.begin:node.end] |
|
} |
|
|
|
type stringLiteral struct { |
|
env *Env |
|
contents string |
|
} |
|
|
|
func (s stringLiteral) String() (string, error) { |
|
return s.contents, nil |
|
} |
|
|
|
func (stringLiteral) Index(_ string) (Object, error) { |
|
return nil, errors.New("cannot index strings") |
|
} |
|
|
|
func (stringLiteral) Search(_ acvp.Query) (Object, error) { |
|
return nil, errors.New("cannot search strings") |
|
} |
|
|
|
func (s stringLiteral) Action(action string, args []string) error { |
|
switch action { |
|
default: |
|
return fmt.Errorf("action %q not supported on string literals", action) |
|
|
|
case "GET": |
|
if len(args) != 0 { |
|
return fmt.Errorf("found %d arguments but %q takes none", len(args), action) |
|
} |
|
|
|
var results map[string]interface{} |
|
if err := s.env.server.Get(&results, s.contents); err != nil { |
|
return err |
|
} |
|
ret, err := json.MarshalIndent(results, "", " ") |
|
if err != nil { |
|
return err |
|
} |
|
s.env.term.Write(ret) |
|
return nil |
|
} |
|
} |
|
|
|
type results struct { |
|
env *Env |
|
prefix string |
|
} |
|
|
|
func (r results) String() (string, error) { |
|
var results map[string]interface{} |
|
if err := r.env.server.Get(&results, "acvp/v1/"+r.prefix+"/results"); err != nil { |
|
return "", err |
|
} |
|
ret, err := json.MarshalIndent(results, "", " ") |
|
return string(ret), err |
|
} |
|
|
|
func (results) Index(_ string) (Object, error) { |
|
return nil, errors.New("cannot index results objects") |
|
} |
|
|
|
func (results) Search(_ acvp.Query) (Object, error) { |
|
return nil, errors.New("cannot search results objects") |
|
} |
|
|
|
func (results) Action(_ string, _ []string) error { |
|
return errors.New("no actions supported on results objects") |
|
} |
|
|
|
func (e *Env) parseStringLiteral(node *node32) string { |
|
assertNodeType(node, ruleStringLiteral) |
|
in := e.bytes(node) |
|
var buf bytes.Buffer |
|
for i := 1; i < len(in)-1; i++ { |
|
if in[i] == '\\' { |
|
switch in[i+1] { |
|
case '\\': |
|
buf.WriteByte('\\') |
|
case 'n': |
|
buf.WriteByte('\n') |
|
case '"': |
|
buf.WriteByte('"') |
|
default: |
|
panic("unknown escape") |
|
} |
|
i++ |
|
continue |
|
} |
|
buf.WriteByte(in[i]) |
|
} |
|
|
|
return buf.String() |
|
} |
|
|
|
func (e *Env) evalExpression(node *node32) (obj Object, err error) { |
|
switch node.pegRule { |
|
case ruleStringLiteral: |
|
return stringLiteral{e, e.parseStringLiteral(node)}, nil |
|
|
|
case ruleVariable: |
|
varName := e.contents(node) |
|
obj, ok := e.variables[varName] |
|
if !ok { |
|
return nil, fmt.Errorf("unknown variable %q", varName) |
|
} |
|
return obj, nil |
|
|
|
case ruleIndexing: |
|
node = node.up |
|
assertNodeType(node, ruleVariable) |
|
varName := e.contents(node) |
|
obj, ok := e.variables[varName] |
|
if !ok { |
|
return nil, fmt.Errorf("unknown variable %q", varName) |
|
} |
|
|
|
node = node.next |
|
for node != nil { |
|
assertNodeType(node, ruleIndex) |
|
indexStr := e.contents(node) |
|
if obj, err = obj.Index(indexStr); err != nil { |
|
return nil, err |
|
} |
|
node = node.next |
|
} |
|
|
|
return obj, nil |
|
|
|
case ruleSearch: |
|
node = node.up |
|
assertNodeType(node, ruleVariable) |
|
varName := e.contents(node) |
|
obj, ok := e.variables[varName] |
|
if !ok { |
|
return nil, fmt.Errorf("unknown variable %q", varName) |
|
} |
|
|
|
node = skipWS(node.next) |
|
assertNodeType(node, ruleQuery) |
|
node = node.up |
|
|
|
var query acvp.Query |
|
for node != nil { |
|
assertNodeType(node, ruleConjunctions) |
|
query = append(query, e.parseConjunction(node.up)) |
|
node = skipWS(node.next) |
|
} |
|
|
|
if len(query) == 0 { |
|
return nil, errors.New("cannot have empty query") |
|
} |
|
|
|
return obj.Search(query) |
|
} |
|
|
|
panic("unhandled") |
|
} |
|
|
|
func (e *Env) evalAction(node *node32) error { |
|
assertNodeType(node, ruleExpression) |
|
obj, err := e.evalExpression(node.up) |
|
if err != nil { |
|
return err |
|
} |
|
|
|
node = node.next |
|
assertNodeType(node, ruleCommand) |
|
node = node.up |
|
assertNodeType(node, ruleFunction) |
|
function := e.contents(node) |
|
node = node.next |
|
|
|
var args []string |
|
for node != nil { |
|
assertNodeType(node, ruleArgs) |
|
node = node.up |
|
args = append(args, e.parseStringLiteral(node)) |
|
|
|
node = skipWS(node.next) |
|
} |
|
|
|
return obj.Action(function, args) |
|
} |
|
|
|
func (e *Env) parseConjunction(node *node32) (ret acvp.Conjunction) { |
|
for node != nil { |
|
assertNodeType(node, ruleConjunction) |
|
ret = append(ret, e.parseCondition(node.up)) |
|
|
|
node = skipWS(node.next) |
|
if node != nil { |
|
assertNodeType(node, ruleConjunctions) |
|
node = node.up |
|
} |
|
} |
|
return ret |
|
} |
|
|
|
func (e *Env) parseCondition(node *node32) (ret acvp.Condition) { |
|
assertNodeType(node, ruleField) |
|
ret.Param = e.contents(node) |
|
node = skipWS(node.next) |
|
|
|
assertNodeType(node, ruleRelation) |
|
switch e.contents(node) { |
|
case "==": |
|
ret.Relation = acvp.Equals |
|
case "!=": |
|
ret.Relation = acvp.NotEquals |
|
case "contains": |
|
ret.Relation = acvp.Contains |
|
case "startsWith": |
|
ret.Relation = acvp.StartsWith |
|
case "endsWith": |
|
ret.Relation = acvp.EndsWith |
|
default: |
|
panic("relation not handled: " + e.contents(node)) |
|
} |
|
node = skipWS(node.next) |
|
|
|
ret.Value = e.parseStringLiteral(node) |
|
|
|
return ret |
|
} |
|
|
|
func runInteractive(server *acvp.Server, config Config) { |
|
oldState, err := terminal.MakeRaw(0) |
|
if err != nil { |
|
panic(err) |
|
} |
|
defer terminal.Restore(0, oldState) |
|
term := terminal.NewTerminal(os.Stdin, "> ") |
|
|
|
resizeChan := make(chan os.Signal, 1) |
|
go func() { |
|
for _ = range resizeChan { |
|
updateTerminalSize(term) |
|
} |
|
}() |
|
signal.Notify(resizeChan, syscall.SIGWINCH) |
|
|
|
env := &Env{variables: make(map[string]Object), server: server, term: term, config: config} |
|
env.variables["requests"] = ServerObjectSet{ |
|
env: env, |
|
name: "requests", |
|
resultType: reflect.TypeOf(&acvp.RequestStatus{}), |
|
canEnumerate: true, |
|
} |
|
env.variables["vendors"] = ServerObjectSet{ |
|
env: env, |
|
name: "vendors", |
|
searchKeys: map[string][]acvp.Relation{ |
|
"name": []acvp.Relation{acvp.Equals, acvp.StartsWith, acvp.EndsWith, acvp.Contains}, |
|
"website": []acvp.Relation{acvp.Equals, acvp.StartsWith, acvp.EndsWith, acvp.Contains}, |
|
"email": []acvp.Relation{acvp.Equals, acvp.StartsWith, acvp.EndsWith, acvp.Contains}, |
|
"phoneNumber": []acvp.Relation{acvp.Equals, acvp.StartsWith, acvp.EndsWith, acvp.Contains}, |
|
}, |
|
subObjects: map[string]func(*Env, string) (Object, error){ |
|
"contacts": func(env *Env, prefix string) (Object, error) { |
|
return ServerObjectSet{ |
|
env: env, |
|
name: prefix + "/contacts", |
|
resultType: reflect.TypeOf(&acvp.Person{}), |
|
canEnumerate: true, |
|
}, nil |
|
}, |
|
"addresses": func(env *Env, prefix string) (Object, error) { |
|
return ServerObjectSet{ |
|
env: env, |
|
name: prefix + "/addresses", |
|
resultType: reflect.TypeOf(&acvp.Address{}), |
|
canEnumerate: true, |
|
}, nil |
|
}, |
|
}, |
|
resultType: reflect.TypeOf(&acvp.Vendor{}), |
|
} |
|
env.variables["persons"] = ServerObjectSet{ |
|
env: env, |
|
name: "persons", |
|
searchKeys: map[string][]acvp.Relation{ |
|
"fullName": []acvp.Relation{acvp.Equals, acvp.StartsWith, acvp.EndsWith, acvp.Contains}, |
|
"email": []acvp.Relation{acvp.Equals, acvp.StartsWith, acvp.EndsWith, acvp.Contains}, |
|
"phoneNumber": []acvp.Relation{acvp.Equals, acvp.StartsWith, acvp.EndsWith, acvp.Contains}, |
|
"vendorId": []acvp.Relation{acvp.Equals, acvp.NotEquals, acvp.LessThan, acvp.LessThanEqual, acvp.GreaterThan, acvp.GreaterThanEqual}, |
|
}, |
|
resultType: reflect.TypeOf(&acvp.Person{}), |
|
} |
|
env.variables["modules"] = ServerObjectSet{ |
|
env: env, |
|
name: "modules", |
|
searchKeys: map[string][]acvp.Relation{ |
|
"name": []acvp.Relation{acvp.Equals, acvp.StartsWith, acvp.EndsWith, acvp.Contains}, |
|
"version": []acvp.Relation{acvp.Equals, acvp.StartsWith, acvp.EndsWith, acvp.Contains}, |
|
"website": []acvp.Relation{acvp.Equals, acvp.StartsWith, acvp.EndsWith, acvp.Contains}, |
|
"description": []acvp.Relation{acvp.Equals, acvp.StartsWith, acvp.EndsWith, acvp.Contains}, |
|
"type": []acvp.Relation{acvp.Equals, acvp.NotEquals}, |
|
"vendorId": []acvp.Relation{acvp.Equals, acvp.NotEquals, acvp.LessThan, acvp.LessThanEqual, acvp.GreaterThan, acvp.GreaterThanEqual}, |
|
}, |
|
resultType: reflect.TypeOf(&acvp.Module{}), |
|
} |
|
env.variables["oes"] = ServerObjectSet{ |
|
env: env, |
|
name: "oes", |
|
searchKeys: map[string][]acvp.Relation{ |
|
"name": []acvp.Relation{acvp.Equals, acvp.StartsWith, acvp.EndsWith, acvp.Contains}, |
|
}, |
|
resultType: reflect.TypeOf(&acvp.OperationalEnvironment{}), |
|
} |
|
env.variables["deps"] = ServerObjectSet{ |
|
env: env, |
|
name: "dependencies", |
|
searchKeys: map[string][]acvp.Relation{ |
|
"name": []acvp.Relation{acvp.Equals, acvp.StartsWith, acvp.EndsWith, acvp.Contains}, |
|
"type": []acvp.Relation{acvp.Equals, acvp.StartsWith, acvp.EndsWith, acvp.Contains}, |
|
"description": []acvp.Relation{acvp.Equals, acvp.StartsWith, acvp.EndsWith, acvp.Contains}, |
|
}, |
|
resultType: reflect.TypeOf(&acvp.Dependency{}), |
|
} |
|
env.variables["algos"] = Algorithms{ |
|
ServerObjectSet{ |
|
env: env, |
|
name: "algorithms", |
|
resultType: reflect.TypeOf(&acvp.Algorithm{}), |
|
canEnumerate: true, |
|
}, |
|
} |
|
env.variables["sessions"] = ServerObjectSet{ |
|
env: env, |
|
name: "testSessions", |
|
resultType: reflect.TypeOf(&acvp.TestSession{}), |
|
canEnumerate: true, |
|
subObjects: map[string]func(env *Env, prefix string) (Object, error){ |
|
"results": func(env *Env, prefix string) (Object, error) { |
|
return results{env: env, prefix: prefix}, nil |
|
}, |
|
}, |
|
} |
|
|
|
for { |
|
if env.line, err = term.ReadLine(); err != nil { |
|
return |
|
} |
|
if len(env.line) == 0 { |
|
continue |
|
} |
|
|
|
stmt := Statement{Buffer: env.line, Pretty: true} |
|
stmt.Init() |
|
if err := stmt.Parse(); err != nil { |
|
io.WriteString(term, err.Error()) |
|
continue |
|
} |
|
|
|
node := skipWS(stmt.AST().up) |
|
switch node.pegRule { |
|
case ruleExpression: |
|
obj, err := env.evalExpression(node.up) |
|
var repr string |
|
if err == nil { |
|
repr, err = obj.String() |
|
} |
|
|
|
if err != nil { |
|
fmt.Fprintf(term, "error while evaluating expression: %s\n", err) |
|
} else { |
|
io.WriteString(term, repr) |
|
io.WriteString(term, "\n") |
|
} |
|
|
|
case ruleAction: |
|
if err := env.evalAction(node.up); err != nil { |
|
io.WriteString(term, err.Error()) |
|
io.WriteString(term, "\n") |
|
} |
|
|
|
default: |
|
fmt.Fprintf(term, "internal error parsing input.\n") |
|
} |
|
} |
|
}
|
|
|