深入理解D5/Tengo中的对象类型系统
tengo A fast script language for Go 项目地址: https://gitcode.com/gh_mirrors/te/tengo
引言
D5/Tengo作为一门动态类型脚本语言,其核心特性之一就是灵活的对象类型系统。本文将全面解析Tengo中的对象类型机制,包括内置运行时类型和用户自定义类型的实现方式,帮助开发者更好地理解和扩展Tengo语言。
对象接口基础
在Tengo中,所有对象类型都必须实现Object接口,这是类型系统的基石。Object接口定义了类型必须实现的一系列方法,这些方法决定了对象在运行时如何表现。
核心方法解析
-
TypeName() string
- 返回类型名称字符串
- 主要用于调试和错误报告
- 建议保持简短且具有区分度
-
String() string
- 返回对象的字符串表示形式
- 在需要字符串转换时自动调用
- 影响print等函数的输出
-
BinaryOp(op token.Token, rhs Object) (Object, error)
- 实现运算符重载的关键方法
- 支持
+
、-
、*
等二元运算符 - 注意:
<
和<=
通过交换操作数实现
-
IsFalsy() bool
- 定义对象的"假值"概念
- 影响if、while等条件判断
- 例如:空数组、空字符串、0、false等应返回true
-
Equals(Object) bool
- 定义对象相等性逻辑
- 建议保持跨类型比较的一致性
高级特性实现
可索引对象
通过实现以下方法,对象可以支持索引访问:
IndexGet(index Object) (value Object, err error)
IndexSet(index, value Object) error
典型应用场景:
- 数组类型:索引为整数
- 字典类型:索引为字符串
- 自定义数据结构:可定义任意索引类型
可调用对象
通过实现以下方法,对象可以像函数一样被调用:
CanCall() bool
Call(args ...Object) (ret Object, err error)
应用场景:
- 函数对象
- 可调用类实例
- 特殊操作符重载
可迭代对象
通过实现以下方法,对象可以用于for-in循环:
CanIterate() bool
Iterate() Iterator
迭代器接口需要实现:
- Next() bool - 是否还有下一个元素
- Key() Object - 当前元素的键
- Value() Object - 当前元素的值
运行时内置类型
Tengo提供了一系列内置类型,开发者可以直接使用:
-
基本类型:
- Int - 整数
- Float - 浮点数
- String - 字符串
- Bool - 布尔值
- Char - 字符
- Bytes - 字节数组
- Time - 时间
-
复合类型:
- Array - 可变数组
- ImmutableArray - 不可变数组
- Map - 可变字典
- ImmutableMap - 不可变字典
-
函数类型:
- CompiledFunction - 编译函数
- BuiltinFunction - 内置函数
- UserFunction - 用户函数
-
特殊类型:
- Error - 错误对象
- Undefined - 未定义值
- Break/Continue/ReturnValue - 流程控制
自定义类型实践
通过实现Object接口,开发者可以轻松扩展Tengo的类型系统。下面是一个完整的自定义字符串数组类型示例:
type StringArray struct {
tengo.ObjectImpl // 嵌入默认实现
Value []string // 实际数据存储
}
// 必须实现的方法
func (o *StringArray) TypeName() string { return "string-array" }
func (o *StringArray) String() string { return strings.Join(o.Value, ", ") }
// 可选实现的方法
func (o *StringArray) BinaryOp(op token.Token, rhs tengo.Object) (tengo.Object, error) {
// 实现+运算符连接数组
if rhs, ok := rhs.(*StringArray); ok && op == token.Add {
return &StringArray{Value: append(o.Value, rhs.Value...)}, nil
}
return nil, tengo.ErrInvalidOperator
}
func (o *StringArray) IsFalsy() bool { return len(o.Value) == 0 }
// 索引访问实现
func (o *StringArray) IndexGet(index tengo.Object) (tengo.Object, error) {
if idx, ok := index.(*tengo.Int); ok {
if idx.Value >= 0 && idx.Value < int64(len(o.Value)) {
return &tengo.String{Value: o.Value[idx.Value]}, nil
}
return nil, tengo.ErrIndexOutOfBounds
}
return nil, tengo.ErrInvalidIndexType
}
// 函数调用实现
func (o *StringArray) CanCall() bool { return true }
func (o *StringArray) Call(args ...tengo.Object) (tengo.Object, error) {
if len(args) != 1 {
return nil, tengo.ErrWrongNumArguments
}
// 实现查找字符串返回索引的功能
if s, ok := tengo.ToString(args[0]); ok {
for i, v := range o.Value {
if v == s {
return &tengo.Int{Value: int64(i)}, nil
}
}
return tengo.UndefinedValue, nil
}
return nil, tengo.ErrInvalidArgumentType{...}
}
// 迭代器实现
func (o *StringArray) CanIterate() bool { return true }
func (o *StringArray) Iterate() tengo.Iterator {
return &StringArrayIterator{strArr: o, idx: -1}
}
type StringArrayIterator struct {
tengo.ObjectImpl
strArr *StringArray
idx int
}
func (i *StringArrayIterator) Next() bool {
i.idx++
return i.idx < len(i.strArr.Value)
}
func (i *StringArrayIterator) Key() tengo.Object {
return &tengo.Int{Value: int64(i.idx)}
}
func (i *StringArrayIterator) Value() tengo.Object {
return &tengo.String{Value: i.strArr.Value[i.idx]}
}
最佳实践建议
-
类型设计原则:
- 保持类型行为的一致性
- 合理处理边界情况
- 提供有意义的错误信息
-
性能考虑:
- 避免频繁的对象创建
- 考虑使用指针接收器
- 缓存常用对象
-
交互性:
- 提供完整的字符串表示
- 支持常用运算符
- 考虑与内置类型的互操作性
总结
Tengo的对象类型系统设计精巧而灵活,既提供了丰富的基础类型,又允许开发者通过实现Object接口来扩展语言功能。理解这一机制对于深度使用Tengo或开发Tengo扩展至关重要。通过合理设计自定义类型,可以大大增强Tengo的表达能力和适用场景。
tengo A fast script language for Go 项目地址: https://gitcode.com/gh_mirrors/te/tengo
创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考