mirror of
https://github.com/go-sylixos/elvish.git
synced 2024-12-04 10:57:50 +08:00
298 lines
6.3 KiB
Go
298 lines
6.3 KiB
Go
package eval
|
|
|
|
import (
|
|
"errors"
|
|
"fmt"
|
|
"math/big"
|
|
"reflect"
|
|
|
|
"github.com/elves/elvish/util"
|
|
)
|
|
|
|
// Definitions for Value interfaces, some simple Value types and some common
|
|
// Value helpers.
|
|
|
|
var (
|
|
NoPretty = util.MinInt
|
|
)
|
|
|
|
// Value is an elvish value.
|
|
type Value interface {
|
|
Kinder
|
|
Reprer
|
|
}
|
|
|
|
// Kinder is anything with kind string.
|
|
type Kinder interface {
|
|
Kind() string
|
|
}
|
|
|
|
// Reprer is anything with a Repr method.
|
|
type Reprer interface {
|
|
// Repr returns a string that represents a Value. The string either be a
|
|
// literal of that Value that is preferably deep-equal to it (like `[a b c]`
|
|
// for a list), or a string enclosed in "<>" containing the kind and
|
|
// identity of the Value(like `<fn 0xdeadcafe>`).
|
|
//
|
|
// If indent is at least 0, it should be pretty-printed with the current
|
|
// indentation level of indent; the indent of the first line has already
|
|
// been written and shall not be written in Repr. The returned string
|
|
// should never contain a trailing newline.
|
|
Repr(indent int) string
|
|
}
|
|
|
|
func IncIndent(indent, inc int) int {
|
|
if indent < 0 {
|
|
return indent
|
|
}
|
|
return indent + inc
|
|
}
|
|
|
|
// Booler is anything that can be converted to a bool.
|
|
type Booler interface {
|
|
Bool() bool
|
|
}
|
|
|
|
// Stringer is anything that can be converted to a string.
|
|
type Stringer interface {
|
|
String() string
|
|
}
|
|
|
|
// Lener is anything with a length.
|
|
type Lener interface {
|
|
Len() int
|
|
}
|
|
|
|
// Elemser is anything that can produce a series of Value elements.
|
|
type Elemser interface {
|
|
Elems() <-chan Value
|
|
}
|
|
|
|
func collectElems(elemser Elemser) []Value {
|
|
var vs []Value
|
|
for v := range elemser.Elems() {
|
|
vs = append(vs, v)
|
|
}
|
|
return vs
|
|
}
|
|
|
|
// Caller is anything may be called on an evalCtx with a list of Value's.
|
|
type Caller interface {
|
|
Call(ec *EvalCtx, args []Value)
|
|
}
|
|
|
|
type CallerValue interface {
|
|
Value
|
|
Caller
|
|
}
|
|
|
|
func mustCaller(v Value) Caller {
|
|
caller, ok := getCaller(v, true)
|
|
if !ok {
|
|
throw(fmt.Errorf("a %s is not callable", v.Kind()))
|
|
}
|
|
return caller
|
|
}
|
|
|
|
// getCaller adapts a Value to a Caller if there is an adapter. It adapts an
|
|
// Indexer if adaptIndexer is true.
|
|
func getCaller(v Value, adaptIndexer bool) (Caller, bool) {
|
|
if caller, ok := v.(Caller); ok {
|
|
return caller, true
|
|
}
|
|
if adaptIndexer {
|
|
if indexer, ok := getIndexer(v, nil); ok {
|
|
return IndexerCaller{indexer}, true
|
|
}
|
|
}
|
|
return nil, false
|
|
}
|
|
|
|
// IndexerCaller adapts an Indexer to a Caller.
|
|
type IndexerCaller struct {
|
|
Indexer
|
|
}
|
|
|
|
func (ic IndexerCaller) Call(ec *EvalCtx, args []Value) {
|
|
results := ic.Indexer.Index(args)
|
|
for _, v := range results {
|
|
ec.ports[1].Chan <- v
|
|
}
|
|
}
|
|
|
|
// Indexer is anything that can be indexed by Values and yields Values.
|
|
type Indexer interface {
|
|
Index(idx []Value) []Value
|
|
}
|
|
|
|
// IndexOneer is anything that can be indexed by one Value and yields one Value.
|
|
type IndexOneer interface {
|
|
IndexOne(idx Value) Value
|
|
}
|
|
|
|
// IndexSetter is a Value whose elements can be get as well as set.
|
|
type IndexSetter interface {
|
|
IndexOneer
|
|
IndexSet(idx Value, v Value)
|
|
}
|
|
|
|
func mustIndexer(v Value, ec *EvalCtx) Indexer {
|
|
indexer, ok := getIndexer(v, ec)
|
|
if !ok {
|
|
throw(fmt.Errorf("a %s is not indexable", v.Kind()))
|
|
}
|
|
return indexer
|
|
}
|
|
|
|
// getIndexer adapts a Value to an Indexer if there is an adapter. It adapts a
|
|
// Caller if ec is not nil.
|
|
func getIndexer(v Value, ec *EvalCtx) (Indexer, bool) {
|
|
if indexer, ok := v.(Indexer); ok {
|
|
return indexer, true
|
|
}
|
|
if indexOneer, ok := v.(IndexOneer); ok {
|
|
return IndexOneerIndexer{indexOneer}, true
|
|
}
|
|
if ec != nil {
|
|
if caller, ok := v.(Caller); ok {
|
|
return CallerIndexer{caller, ec}, true
|
|
}
|
|
}
|
|
return nil, false
|
|
}
|
|
|
|
// IndexOneerIndexer adapts an IndexOneer to an Indexer by calling all the
|
|
// indicies on the IndexOner and collect the results.
|
|
type IndexOneerIndexer struct {
|
|
IndexOneer
|
|
}
|
|
|
|
func (ioi IndexOneerIndexer) Index(vs []Value) []Value {
|
|
results := make([]Value, len(vs))
|
|
for i, v := range vs {
|
|
results[i] = ioi.IndexOneer.IndexOne(v)
|
|
}
|
|
return results
|
|
}
|
|
|
|
// CallerIndexer adapts a Caller to an Indexer.
|
|
type CallerIndexer struct {
|
|
Caller
|
|
ec *EvalCtx
|
|
}
|
|
|
|
func (ci CallerIndexer) Index(idx []Value) []Value {
|
|
// XXX We don't have location information.
|
|
return captureOutput(ci.ec, Op{func(ec *EvalCtx) {
|
|
ci.Caller.Call(ec, idx)
|
|
}, -1, -1})
|
|
}
|
|
|
|
// Error definitions.
|
|
var (
|
|
ErrOnlyStrOrRat = errors.New("only str or rat may be converted to rat")
|
|
)
|
|
|
|
// Bool represents truthness.
|
|
type Bool bool
|
|
|
|
func (Bool) Kind() string {
|
|
return "bool"
|
|
}
|
|
|
|
func (b Bool) Repr(int) string {
|
|
if b {
|
|
return "$true"
|
|
}
|
|
return "$false"
|
|
}
|
|
|
|
func (b Bool) Bool() bool {
|
|
return bool(b)
|
|
}
|
|
|
|
// ToBool converts a Value to bool. When the Value type implements Bool(), it
|
|
// is used. Otherwise it is considered true.
|
|
func ToBool(v Value) bool {
|
|
if b, ok := v.(Booler); ok {
|
|
return b.Bool()
|
|
}
|
|
return true
|
|
}
|
|
|
|
// Rat is a rational number.
|
|
type Rat struct {
|
|
b *big.Rat
|
|
}
|
|
|
|
func (Rat) Kind() string {
|
|
return "string"
|
|
}
|
|
|
|
func (r Rat) Repr(int) string {
|
|
return "(rat " + r.String() + ")"
|
|
}
|
|
|
|
func (r Rat) String() string {
|
|
if r.b.IsInt() {
|
|
return r.b.Num().String()
|
|
}
|
|
return r.b.String()
|
|
}
|
|
|
|
// ToRat converts a Value to rat. A str can be converted to a rat if it can be
|
|
// parsed. A rat is returned as-is. Other types of values cannot be converted.
|
|
func ToRat(v Value) (Rat, error) {
|
|
switch v := v.(type) {
|
|
case Rat:
|
|
return v, nil
|
|
case String:
|
|
r := big.Rat{}
|
|
_, err := fmt.Sscanln(string(v), &r)
|
|
if err != nil {
|
|
return Rat{}, fmt.Errorf("%s cannot be parsed as rat", v.Repr(NoPretty))
|
|
}
|
|
return Rat{&r}, nil
|
|
default:
|
|
return Rat{}, ErrOnlyStrOrRat
|
|
}
|
|
}
|
|
|
|
// FromJSONInterface converts a interface{} that results from json.Unmarshal to
|
|
// a Value.
|
|
func FromJSONInterface(v interface{}) Value {
|
|
if v == nil {
|
|
// TODO Use a more appropriate type
|
|
return String("")
|
|
}
|
|
switch v.(type) {
|
|
case bool:
|
|
return Bool(v.(bool))
|
|
case float64, string:
|
|
// TODO Use a numeric type for float64
|
|
return String(fmt.Sprint(v))
|
|
case []interface{}:
|
|
a := v.([]interface{})
|
|
vs := make([]Value, len(a))
|
|
for i, v := range a {
|
|
vs[i] = FromJSONInterface(v)
|
|
}
|
|
return List{&vs}
|
|
case map[string]interface{}:
|
|
m := v.(map[string]interface{})
|
|
m_ := make(map[Value]Value)
|
|
for k, v := range m {
|
|
m_[String(k)] = FromJSONInterface(v)
|
|
}
|
|
return Map{&m_}
|
|
default:
|
|
throw(fmt.Errorf("unexpected json type: %T", v))
|
|
return nil // not reached
|
|
}
|
|
}
|
|
|
|
// DeepEq compares two Value's deeply.
|
|
func DeepEq(a, b Value) bool {
|
|
return reflect.DeepEqual(a, b)
|
|
}
|