package core import ( "errors" "fmt" "math/big" "reflect" "strconv" "strings" ) type ValueType int const ( NilValueType ValueType = iota BoolValueType FloatValueType IntegerValueType StringValueType ListValueType TupleValueType ObjectValueType FunctionValueType BuiltinFunctionValueType RecordValueType ) func (v ValueType) String() string { switch v { case NilValueType: return "nil" case BoolValueType: return "bool" case ObjectValueType: return "object" case FloatValueType: return "float" case IntegerValueType: return "int" case StringValueType: return "string" case ListValueType: return "list" case TupleValueType: return "tuple" case FunctionValueType: return "function" case BuiltinFunctionValueType: return "builtin function" case RecordValueType: return "record" } return "undefined" } // GoToValue convert go values to anglais VM-values. Works for some values (nil, bool, float64, int, string, slices, maps) func GoToValue(gov interface{}) Value { switch v := gov.(type) { case nil: return &NilValue{} case bool: return &BoolValue{ v, } case int: return &IntegerValue{ new(big.Int).SetInt64(int64(v)), } case *big.Int: return &IntegerValue{ v, } case float64: return &FloatValue{ v, } case string: return &StringValue{ v, } case map[string]interface{}: values := map[string]Value{} for key, value := range v { values[key] = GoToValue(value) } return &ObjectValue{ values, } case Value: return v default: if reflect.TypeOf(v).Kind() == reflect.Slice { return &ListValue{ v.([]Value), } } } panic(fmt.Sprintf("unsupported automatic type conversion: %v (%s)", gov, reflect.TypeOf(gov))) } type Value interface { // Type get the type of the value (a ValueType) Type() ValueType // String Convert this value to a string fit for human consumption String() string // DebugString get a debug string of this value. Used in lists. DebugString() string // Equals Check if two values are equal Equals(Value) bool // Get a member from the value. An error is returned if the member does not exist Get(string) (Value, error) // Clone create a clone of the value. The returned value is a pointer to a new value of the same type as the value. Clone() Value } type NilValue struct{} func (v *NilValue) Type() ValueType { return NilValueType } func (v *NilValue) String() string { return "nil" } func (v *NilValue) DebugString() string { return v.String() } func (v *NilValue) Equals(other Value) bool { return other.Type() == NilValueType } func (v *NilValue) Get(_ string) (Value, error) { return nil, errors.New("nil has no properties") } func (v *NilValue) Clone() Value { return &NilValue{} } type BoolValue struct { Boolean bool } func (v *BoolValue) Type() ValueType { return BoolValueType } func (v *BoolValue) String() string { if v.Boolean { return "true" } else { return "false" } } func (v *BoolValue) DebugString() string { return v.String() } func (v *BoolValue) Equals(other Value) bool { return other.Type() == BoolValueType && other.(*BoolValue).Boolean == v.Boolean } func (v *BoolValue) Get(_ string) (Value, error) { return nil, errors.New("booleans have no properties") } func (v *BoolValue) Clone() Value { return &BoolValue{ v.Boolean, } } // ObjectValue An object with any number of members (key-value pairs) type ObjectValue struct { Members map[string]Value } func (v *ObjectValue) Type() ValueType { return ObjectValueType } func (v *ObjectValue) String() string { out := "{" for key, value := range v.Members { if out != "{" { out += ", " } out += fmt.Sprintf("%q=%s", key, value.DebugString()) } out += "}" return out } func (v *ObjectValue) DebugString() string { return v.String() } func (v *ObjectValue) Equals(other Value) bool { object, ok := other.(*ObjectValue) if !ok { return false } for key, value := range v.Members { if !object.Members[key].Equals(value) { return false } } return true } var ObjectPrototype = map[string]*BuiltinFunctionValue{ "set": { "set", &FunctionSignature{ []TypeSignature{&StringSignature{}, &ListSignature{}}, &NilSignature{}, }, func(vm *VM, _this Value, params []Value) (Value, error) { this := _this.(*ObjectValue) p := params[1] v, ok := params[0].(*StringValue) if !ok { return nil, errors.New("property is not a string") } this.Members[v.Text] = p return &NilValue{}, nil }, nil, false, }, } func (v *ObjectValue) Get(key string) (Value, error) { if member, ok := v.Members[key]; ok { return member, nil } else if p, ok := ObjectPrototype[key]; ok { return p, nil } else { return nil, errors.New("no property found with name \"" + key + "\"") } } func (v *ObjectValue) Clone() Value { m := make(map[string]Value, len(v.Members)) for name, mem := range v.Members { m[name] = mem.Clone() } return &ObjectValue{ m, } } // FloatValue floating-point values type FloatValue struct { Number float64 } const FloatSize int = 64 func (v *FloatValue) Type() ValueType { return FloatValueType } func (v *FloatValue) String() string { s := strconv.FormatFloat(v.Number, 'g', -1, FloatSize) if strings.Index(s, ".") == -1 { s += ".0" } return s } func (v *FloatValue) DebugString() string { return v.String() } func (v *FloatValue) Equals(other Value) bool { return other.Type() == FloatValueType && other.(*FloatValue).Number == v.Number } func (v *FloatValue) Get(_ string) (Value, error) { // TODO maybe add standard functions for number values? return nil, errors.New("numbers have no properties") } func (v *FloatValue) Clone() Value { return &FloatValue{ v.Number, } } // IntegerValue whole number/integer values type IntegerValue struct { Number *big.Int } func (v *IntegerValue) Type() ValueType { return IntegerValueType } func (v *IntegerValue) String() string { return v.Number.String() } func (v *IntegerValue) DebugString() string { return v.String() } func (v *IntegerValue) Equals(other Value) bool { return other.Type() == IntegerValueType && other.(*IntegerValue).Number.Cmp(v.Number) == 0 } func (v *IntegerValue) Get(_ string) (Value, error) { return nil, errors.New("numbers have no properties") } func (v *IntegerValue) Clone() Value { return &IntegerValue{ new(big.Int).Set(v.Number), } } type StringValue struct { Text string } func (v *StringValue) Type() ValueType { return StringValueType } func (v *StringValue) String() string { return v.Text } func (v *StringValue) DebugString() string { return "\"" + v.String() + "\"" } func (v *StringValue) Equals(other Value) bool { return other.Type() == StringValueType && other.(*StringValue).Text == v.Text } var StringPrototype = map[string]*BuiltinFunctionValue{ "split": { "split", &FunctionSignature{ []TypeSignature{&StringSignature{}}, &ListSignature{ &StringSignature{}, }, }, func(vm *VM, this Value, v []Value) (Value, error) { str := this.(*StringValue).String() sep := v[0].(*StringValue).String() prev := 0 var out []Value for i := 1; i < len(str)-len(sep); i++ { if str[i:i+len(sep)] == sep { out = append(out, &StringValue{str[prev:i]}) prev = i + len(sep) } } if prev != len(str) { out = append(out, &StringValue{str[prev:len(str)]}) } return &ListValue{out}, nil }, nil, true, }, "length": { Name: "length", Signature: &FunctionSignature{ []TypeSignature{}, &IntegerSignature{}, }, F: func(vm *VM, this Value, _ []Value) (Value, error) { return GoToValue(len(this.(*StringValue).Text)), nil }, }, "at": { Name: "at", Signature: &FunctionSignature{ []TypeSignature{&IntegerSignature{}}, &StringSignature{}, }, F: func(vm *VM, this Value, args []Value) (Value, error) { i := int(args[0].(*IntegerValue).Number.Int64()) if i < 0 || i >= len(this.(*StringValue).Text) { return nil, errors.New("index is out of range") } return GoToValue(string(this.(*StringValue).Text[i])), nil }, }, } func (v *StringValue) Get(key string) (Value, error) { if prop, ok := StringPrototype[key]; ok { return prop, nil } return nil, errors.New(fmt.Sprintf("string has no property \"%s\"", key)) } func (v *StringValue) Clone() Value { return &StringValue{ v.Text, } } // ListValue a dynamic list of values type ListValue struct { Items []Value } func (v *ListValue) Type() ValueType { return ListValueType } func (v *ListValue) String() string { out := "[" for i, item := range v.Items { if i != 0 { out += ", " } out += item.DebugString() } out += "]" return out } func (v *ListValue) DebugString() string { return v.String() } func (v *ListValue) Equals(other Value) bool { if other.Type() != ListValueType { return false } l := other.(*ListValue) if len(v.Items) != len(l.Items) { return false } for i, item := range v.Items { if !item.Equals(l.Items[i]) { return false } } return true } var ListPrototype = map[string]*BuiltinFunctionValue{ "append": { "append", &FunctionSignature{ []TypeSignature{&AnySignature{}}, &NilSignature{}, }, func(_ *VM, this Value, v []Value) (Value, error) { this.(*ListValue).Items = append(this.(*ListValue).Items, v[0]) return &NilValue{}, nil }, nil, false, }, "at": { "at", &FunctionSignature{ []TypeSignature{ &IntegerSignature{}, }, &InnerSignature{}, }, func(_ *VM, this Value, p []Value) (Value, error) { items := this.(*ListValue).Items index := int(p[0].(*IntegerValue).Number.Int64()) if index >= len(items) { return nil, errors.New(fmt.Sprintf("list index %x out of range", index)) } return items[index], nil }, nil, false, }, "put": { "put", &FunctionSignature{ []TypeSignature{ &FloatSignature{}, &InnerSignature{}, }, &NilSignature{}, }, func(_ *VM, this Value, args []Value) (Value, error) { l := this.(*ListValue) i := int(args[0].(*FloatValue).Number) v := args[1] // bounds check if i < 0 || i >= len(l.Items) { return nil, errors.New(fmt.Sprintf("index %x out of range", i)) } l.Items[i] = v return &NilValue{}, nil }, nil, false, }, "length": { "length", &FunctionSignature{ []TypeSignature{}, &IntegerSignature{}, }, func(_ *VM, this Value, _ []Value) (Value, error) { return GoToValue(len(this.(*ListValue).Items)), nil }, nil, false, }, "reduce": { "reduce", &FunctionSignature{ []TypeSignature{ &FunctionSignature{ []TypeSignature{ &AnySignature{}, &AnySignature{}, }, &AnySignature{}, }, &AnySignature{}, }, &AnySignature{}, }, func(vm *VM, value Value, m []Value) (Value, error) { list := value.(*ListValue) f := m[0] sum := m[1] for _, v := range list.Items { result, err := vm.Call(f, []Value{sum, v}) if err != nil { return nil, err } sum = result } return sum, nil }, nil, false, }, } func (v *ListValue) Get(key string) (Value, error) { if prop, ok := ListPrototype[key]; ok { return prop, nil } return nil, errors.New(fmt.Sprintf("list has no property \"%s\"", key)) } func (v *ListValue) Clone() Value { n := make([]Value, len(v.Items)) for i, item := range v.Items { n[i] = item.Clone() } return &ListValue{ n, } } type TupleValue struct { Items []Value } func (v *TupleValue) Type() ValueType { return TupleValueType } func (v *TupleValue) String() string { out := "(" for i, item := range v.Items { if i != 0 { out += ", " } out += item.DebugString() } if len(v.Items) <= 1 { out += "," } out += ")" return out } func (v *TupleValue) DebugString() string { return v.String() } func (v *TupleValue) Clone() Value { n := make([]Value, len(v.Items)) for i, item := range v.Items { n[i] = item.Clone() } return &TupleValue{ n, } } func (v *TupleValue) Equals(other Value) bool { if other.Type() != TupleValueType { return false } t := other.(*TupleValue).Items for i, item := range v.Items { if !item.Equals(t[i]) { return false } } return true } var TuplePrototype = map[string]*BuiltinFunctionValue{ "at": &BuiltinFunctionValue{ "at", &FunctionSignature{ []TypeSignature{&IntegerSignature{}}, &InnerSignature{}, }, func(vm *VM, this Value, args []Value) (Value, error) { i := args[0].(*IntegerValue).Number.Int64() if i < 0 || i >= int64(len(this.(*TupleValue).Items)) { return nil, errors.New(fmt.Sprintf("index %x out of range", i)) } return this.(*TupleValue).Items[i], nil }, nil, true, }, } func (v *TupleValue) Get(key string) (Value, error) { if prop, ok := TuplePrototype[key]; ok { return prop, nil } return nil, errors.New(fmt.Sprintf("tuple has no property \"%s\"", key)) } type FunctionValue struct { Name string Params []FunctionParameter Yield TypeSignature Chunk *Chunk Parent Value Scope *Scope } func (v *FunctionValue) Type() ValueType { return FunctionValueType } func (v *FunctionValue) String() string { return fmt.Sprintf("", v.Name, v.Chunk) } func (v *FunctionValue) DebugString() string { return v.String() } func (v *FunctionValue) Equals(other Value) bool { return other.Type() == FunctionValueType && v.Chunk == other.(*FunctionValue).Chunk } func (v *FunctionValue) Get(_ string) (Value, error) { return nil, errors.New("functions have no properties") } func (v *FunctionValue) Clone() Value { return &FunctionValue{ v.Name, v.Params, v.Yield, v.Chunk, v.Parent, v.Scope, } } type BuiltinFunctionValue struct { Name string Signature *FunctionSignature F func(*VM, Value, []Value) (Value, error) Parent Value Constant bool } func (v *BuiltinFunctionValue) Type() ValueType { return BuiltinFunctionValueType } func (v *BuiltinFunctionValue) String() string { return fmt.Sprintf("", v.Name) } func (v *BuiltinFunctionValue) DebugString() string { return v.String() } func (v *BuiltinFunctionValue) Equals(other Value) bool { return other.Type() == BuiltinFunctionValueType && v.Name == other.(*BuiltinFunctionValue).Name } func (v *BuiltinFunctionValue) Get(_ string) (Value, error) { return nil, errors.New("functions have no properties") } func (v *BuiltinFunctionValue) Clone() Value { return &BuiltinFunctionValue{ v.Name, v.Signature, v.F, v.Parent, v.Constant, } } type RecordValue struct { Entries map[string]Value } func (v *RecordValue) Type() ValueType { return RecordValueType } func (v *RecordValue) String() string { return "" } func (v *RecordValue) DebugString() string { return v.String() } func (v *RecordValue) Clone() Value { return &RecordValue{ v.Entries, } } func (v *RecordValue) Equals(other Value) bool { if other.Type() != RecordValueType { return false } r := other.(*RecordValue) if len(r.Entries) != len(v.Entries) { return false } for key, val := range v.Entries { oth, ok := r.Entries[key] if !ok { return false } if !val.Equals(oth) { return false } } return true } func (v *RecordValue) Get(key string) (Value, error) { val, ok := v.Entries[key] if !ok { return nil, errors.New(fmt.Sprintf("record has no property \"%s\"", key)) } return val, nil }