写一个中文编程语言:实现代码入口和注册寄存器
6 实现 注册代码: 虚拟寄存器,把指令注册到寄存器上下文
构造一个思想,如果时间太长就把注意力放在实践,它将改进理论思想,
执行实践,如果时间太长则把注意力放在思想,它将改善理论思想。
上下文是个啥东西,它帮助我们管理代码,它整理指令周围的信息,负责管理指令的执行范围,属于哪个环境,是否main入口。
最重要的是,它管理着我们的最终指令组,一如以前 我们计划在这里解析的语句,组织为 二元组的 代码块。
[][2]string{{'IPUSH', '3'}, {'IPUSH', '4'}, {'IPUSH', '5'}, {'IMUL',''} {'IADD',''} {'PRINTI'.''} }
因此,我们定义一个 TVMContext 上下文管理器,只要管理我们的二元代码块
type TVMContext struct {
Code [][2]string
Scope string
}
我们将代码范围默认指定为 global,并且提供把代码块注册到虚拟机VM的 机制。
/*
检测代码的顶级函数
*/
func GenerateTVMC(mode *tlink) *TVMContext {
tcontext := MakeWVMContext("")
for mode.size > 0 {
_, statements := mode.MvTheOne()
Datas := statements.GetNodeData()
if Datas != nil {
Generate(Datas, tcontext)
}
}
tcontext.Code = append(tcontext.Code, [2]string{"HALT"})
return tcontext
}
识别代码语句,将其存储在TVMContext,Generate执行这个工作,但是我们并不知道下一个Node节点中Data的实际类型。
这里使用 any,以便 接受 其不同节点值的类型。
/*
@param:node 结构体实例, 比如 PrintStatement,
*/
func Generate(nodeData *Node, context *TVMContext) string {
node = nodeData.Data
switch nt := node.(type) {
case *PrintStatement:
val := node.(*PrintStatement)
ty := Generate(val.Value, context)
if ty == "int" {
context.Code = append(context.Code, [2]string{"PRINTI"})
return ""
}
return ""
case *Integer:
val := node.(*Integer)
context.Code = append(context.Code, [2]string{"IPUSH", val.Value})
return "int"
case *BinOp:
var rettype string
var instr [2]string
nod := node.(*BinOp)
leftype := Generate(nod.Left, context)
Generate(nod.Right, context)
// fmt.Printf("right type:%#v \n", righttype)
rettype = leftype
if leftype == "int" { //整型计算
if nod.Op == "+" || nod.Op == "PLUS" {
instr = [2]string{"IADD", ""}
} else if nod.Op == "-" || nod.Op == "MINUS" {
instr = [2]string{"ISUB", ""}
} else if nod.Op == "*" || nod.Op == "TIMES" {
instr = [2]string{"IMUL", ""}
} else if nod.Op == "/" || nod.Op == "DIVIDE" {
instr = [2]string{"IDIV", ""}
} else if nod.Op == "&&" || nod.Op == "LAND" {
instr = [2]string{"AND", ""}
} else if nod.Op == "||" || nod.Op == "LOR" {
instr = [2]string{"OR", ""}
}
}
context.Code = append(context.Code, instr)
return rettype
default:
msg := fmt.Sprintf("Couldn't generate %#v with nt:%#v ", node, nt)
panic(msg)
}
return ""
}
依靠以上的实现,我们可以将代码语句转换为虚拟机可以匹配和执行的形式
[][2]string{{'IPUSH', '3'}, {'IPUSH', '4'}, {'IPUSH', '5'}, {'IMUL',''} {'IADD',''} {'PRINTI'.''} }
没有太多神奇的地方,对吗? 确实如此,这里重在理解其工作方式。
有趣的是如果拥有大量 tvm. 这样每个最终将拥有大量函数,将指向 VM的代码置入 context中管理,并在不同ENV中选择执行有大量工作。
这有一个讨巧的方法,但是也节省了一些时间。
相反,我们创建了一个全局调度器解析 对象 TVM,并显式地使用,以检索虚拟机的全部函数并进行匹配和执行。
6.1 调度和执行指令:调度虚拟机_VirtualMachine 把指令集执行后结果返回到 VM 的堆栈
现在我们已经有了一组指令,还有了一些硬件执行措施,现在最需要的是调度和执行,比如这里的执行者组织,简单易懂 就叫CallFunc。
同时由于这里的虚拟机是全局唯一的,因此,我们只需要顺序调度在上一节产生的指令,然后在虚拟机TVM执行即可。
这里没有堆栈跟踪,但是在发送错误时,可以查找到哪一行的报错。
我们认为这样的东西通常对刚开始更好。
当表达式不是按照用户直觉的顺序计算时——在不同的实现中有可能以不同的顺序! 可能会很痛苦。
现在我们已经解析完成表达式,只需要按顺序执行即可。 那就很简单了对吗
/*
@param T 待调用结构体
@return 返回它的函数
*/
func Methods[T any](tt T) []string {
var t T
val := reflect.ValueOf(&t)
typ := val.Type()
var NamesMethod []string
for i := 0; i < val.NumMethod(); i++ {
NamesMethod = append(NamesMethod, typ.Method(i).Name)
}
return NamesMethod
}
/*
@param tys 虚拟机实例
@param fcName 函数名称
@param refVals
@return 返回虚拟机实例, 调用任意结构体的某个函数,用于查看函数执行结果.
*/
func CallFuncs(tys TVM, fcName string, refVals []reflect.Value) TVM {
ms := Methods(tys)
for _, m := range ms {
if m == fcName {
reflect.ValueOf(&tys).MethodByName(m).Call(refVals)
break
}
}
return tys
}
把它嵌入到虚拟机上下文管理器中即可。
这是一个简单的实现,但它已经足够我们完成当前的任务。
7 组织计算的入口: 中文表达式计算器_Evaluating Expressions 基于堆的TVM 虚拟机
我们将把之前的分词器,解析器,虚拟机整合为一个入口,为了醒目,我们使用: 提醒使用者计算结果在哪里。
var filePath string
if len(os.Args) < 2 {
filePath = "./testxen.bl"
} else {
filePath = os.Args[1]
}
modelLinked := comp.ParseFile(filePath)
code := GenerateTVM(modelLinked)
tvm := MakeTVM()
fmt.Printf("\n:\n\n")
tvm.Run(code)
这就是完整的中文编程语言。
我们完成了挑战,在三个章节里完成一个可用的中文编程语言,也许可称之为计算器更多人容易接受。
我们使用了一种基于解释器的方法,去实现了中文编程语言,必须承认,这才刚刚开始,还有更多值得尝试的内容。
8 写在最后: 前景:穷途末路了吗
在现在的人工智能时代,有些人类学家,语言学家认为,其计算机用以处理现有的字符流,字节流,
有序依次或并行地处理输入,在工程上取得了优秀的成绩,
回顾发展历史,其根本逻辑的变化很小,也许深度学习的方式算得一些变化。
但是这并不能表示人们已经完全掌握了 人类的进化机制,学习机制,人们如何面对未知和学习未知的机制。
———— web summary 大会记
更新更快的量子计算机,在将来我们也许可以处理更多更复杂的 宇宙尺度的数据,其承载的物理介质有些变化。
究其演化本质,是否仍然在重复人们自己, 是否值得重复,是个值得思考的问题。
关于更多尝试和优化Optimizatio
在未来我们可做的事情:
字节码 更通用地接近硬件
内存管理
控制流,惰性计算
需要面向对象吗?
哈希表,闭包
按需扫描,跳跃 goto
类型系统
类型检查, 类型互感
超类,方法和实例化,泛型,全局,局部变量,可变参数和类型。
跨通用平台
纯函数式的
LSP编译器向导
- 点赞
- 收藏
- 关注作者
评论(0)