Gox语言是以Go语言(Golang)为基础的解释型/脚本语言,它除了具有一般脚本语言所具有的编写快捷、语言简洁、易于理解等特点外,还支持其他语言所不具备的跨平台原生图形界面(GUI)开发,并且代码写起来非常舒畅。
Gox语言的主要特点包括:
下面用一个例子来介绍一下用Gox编写代码的方便程度,这是一个实现了简易计算器功能的代码:
expression = getInput("Please enter the expression: ")
result = eval(expression)
println("result:", result)
仅仅4行代码而已,实现的效果如下:
可以看到,4行语言就实现了一个命令行版本的简易表达式计算器。
如果用GUI图形界面来实现怎么样呢?同样的,只需要相对很简单的代码就能实现:
text1 = new(string)
onButton1Click = func() {
// evaluate the expression in the text input
rs = eval(*text1)
println(rs)
// set the result back into the text input
setValue(text1, rs)
}
// close the window, also terminate the application
onButton2Click = func() {
os.Exit(1)
}
// main window loop
loop = func() {
// set the layout of GUI
layoutT := []gui.Widget{
gui.Label("Enter an expression."),
gui.InputText("", 0, text1),
// widgets in line layout is aligned left to right
gui.Line(gui.Button("Calculate", onButton1Click),
gui.Button("Close", onButton2Click)),
}
gui.SingleWindow("Calculator", layoutT)
}
// text1 used to hold the string value of the text input
// notice: text1 is a pointer
// setup the title, size (width and height, 400*200), style and font-loading function of main window,
mainWindow := gui.NewMasterWindow("Calculator", 400, 200, gui.MasterWindowFlagsNotResizable, nil)
// show the window and start the message loop
gui.LoopWindow(mainWindow, loop)
除去注释,有效代码不超过20行,就能够实现一个界面相当不错的图形化计算器了,而且可以实现跨平台!实际效果如下图所示:
那么,更为令人叹为观止地,一个支持中文、跨平台、功能齐全而又简洁的代码编辑器也不需要多少Gox代码就可以实现,运行效果如下图所示,有效代码仅需不到200行(请参见本文末尾所附代码)。
由于仅是示例,篇幅所限,没有实现代码高亮和自动完成功能,但除此以外,一个代码编辑器所需的所有主要功能都已经实现了,甚至还包括代码加密、解密和运行功能。
下面看看Gox语言的主要使用方式。
首先,直接去Gox语言官网(gox.topget.org)或其Github/Gitee的release页面(topxeq/gox)下载Gox语言的单一可执行文件,将其放在一个目录下(如果是下载的zip压缩包,需要进行解压缩);该目录最好在操作系统可找到的目录下(即加入PATH环境变量中),以便执行;
使用类似 gox -edit basic.gox 的命令可以启动Gox的内置编辑器编辑名为basic.gox的Gox代码文件,而如果直接用 gox -edit 不带文件名,则将打开一个新文件进行编辑;
运行Gox代码,只需要执行类似 gox basic.gox 的命令即可,也可以直接执行JavaScript代码文件,例如 gox test.js。
使用类似 gox -encrypt=mycode basic.gox 的命令来对源代码进行加密(mycode是密码,将会生成一个名为basic.goxe的加密代码文件),然后可以使用类似 gox -decrypt=mycode basic.goxe 的命令来对代码进行解密,或者直接用 gox -decrun=mycode basic.goxe 命令来解密执行源代码,也可以直接 gox basic.goxe 执行源代码,此时将会要求先输入密码后才能执行;
除使用内置的代码编辑器外,也可以使用功能更为丰富的其他编辑器,例如Visual Studio Code就是一个很好的选择,将.gox类型文件的文件的代码高亮方案设置成与Go语言相同即可;
更多的说明和代码示例可以参考Github上Gox语言的代码库,github.com/topxeq/gox。
当然,Gox语言目前也存在不足,我们要心里有数,以便在使用Gox语言时清楚它更适合做什么和不太适合做什么。
目前,Gox语言最主要的不足就是:作为解释型/脚本语言,Gox也具有这类语言的通病,就是执行代码的速度相对比较慢,比一些具有多年优化积累的解释型语言如Python、Java等也要更慢。
另外,Gox语言虽然只有一个主程序,但相对来说比较大(有几十M之多)。但这也比较好理解,毕竟还需要支持图形化编程并自带代码编辑器。考虑到这一点,Gox语言也提供一个去除了图形化编程功能的纯命令行版本,文件大小会小一些,效率也会高一点。另外还有一个Gox Tiny版本,保留了图形界面编程的能力,但去掉了一些不常用的功能。
第三,Gox语言如果要支持更多的第三方类库,需要手动编译源码,但这点对于程序员来说应该不是大问题。
总的来说,Gox语言虽然具备开发各种各种应用场景的全栈开发语言的潜力,但至少目前仍不太适合开发高密度计算类型和需要极高速相应的程序和系统;但它非常适合作为“胶水语言”来作为大型系统之间的粘合剂,也非常适合快速开发一些无需太高速度要求的功能系统(例如可以取代shell脚本的开发、进行各种复杂的文本处理、实现各种网络编程、编写一些图形化操控界面)以及做一些快速原型演示等。
附——全功能简易代码编辑器源码:
argsG = os.Args
editFileNameG = ""
editFileCleanFlagG = ""
editSecureCodeG = new(string)
fcT = ""
aryT = tk.GetAllParameters(argsG)
lenT = len(aryT)
if lenT < 3 {
editFileNameG = ""
editFileCleanFlagG = "*"
} else {
editFileNameG = aryT[2]
fcT = tk.LoadStringFromFile(editFileNameG)
if tk.IsErrorString(fcT) {
gui.SimpleError("错误提示", tk.Spr("载入文件时发生错误:%v", tk.GetErrorString(fcT)))
return
}
editFileCleanFlagG = ""
}
// hold the text in main edit control
text1 = new(string)
setValue(text1, fcT)
onEditChange = func() {
editFileCleanFlagG = "*"
}
onButtonLoad = func() {
if editFileCleanFlagG != "" {
rs = gui.GetConfirm("请确认", "文件已被修改,确认要打开另一个文件吗?")
if rs == false {
return
}
}
fileNameNewT = gui.SelectFile("请选择要打开的文件", "所有文件", "*")
if tk.IsErrorString(fileNameNewT) {
if tk.EndsWith(fileNameNewT, "Cancelled") {
gui.MessageBox("信息", tk.Spr("操作被用户取消"))
return
}
gui.MessageBox("错误提示", tk.Spr("选择文件失败:%v", tk.GetErrorString(fileNameNewT)))
return
}
fcT = tk.LoadStringFromFile(fileNameNewT)
if tk.IsErrorString(fcT) {
gui.MessageBox("错误提示", tk.Spr("载入文件内容失败:%v", tk.GetErrorString(fileNameNewT)))
return
}
editFileNameG = fileNameNewT
setValue(text1, fcT)
editFileCleanFlagG = ""
}
onButtonRunClick = func() {
rs = runScript(*text1, "")
gui.MessageBox("运行结果", tk.Spr("%v", rs))
}
editorSaveAs = func() {
fileNameNewT = gui.SelectSaveFile("请选择要保存的文件", "所有文件", "*")
if tk.IsErrorString(fileNameNewT) {
if tk.EndsWith(fileNameNewT, "Cancelled") {
gui.MessageBox("信息", tk.Spr("操作被用户取消"))
return
}
gui.MessageBox("错误提示", tk.Spr("选择文件失败:%v", tk.GetErrorString(fileNameNewT)))
return
}
editFileNameG = fileNameNewT
rs1 = tk.SaveStringToFile(*text1, editFileNameG)
if rs1 != "" {
gui.MessageBox("错误提示", tk.Spr("保存文件失败:%v", rs))
return
}
gui.MessageBox("信息", tk.Spr("文件已被保存至:%v", editFileNameG))
editFileCleanFlagG = ""
}
editorSave = func() {
if editFileNameG == "" {
editorSaveAs()
return
}
rs = false
if tk.IfFileExists(editFileNameG) {
rs = gui.GetConfirm("请确认", "文件已存在,是否要覆盖?")
}
if rs == true {
rs1 = tk.SaveStringToFile(*text1, editFileNameG)
if rs1 != "" {
gui.MessageBox("错误提示", tk.Spr("保存文件失败:%v", rs))
return
}
gui.MessageBox("信息", tk.Spr("文件已被保存至:%v", editFileNameG))
editFileCleanFlagG = ""
}
}
editEncrypt = func() {
gui.CloseCurrentPopup()
sourceT = *text1
encStrT = tk.EncryptStringByTXDEF(sourceT, *editSecureCodeG)
if tk.IsErrorString(encStrT) {
gui.SimpleError("错误提示", tk.Spr("加密失败:%v", tk.GetErrorString(encStrT)))
return
}
setValue(text1, "//TXDEF#" + encStrT)
editFileCleanFlagG = "*"
setValue(editSecureCodeG, "")
}
editEncryptClick = func() {
gui.OpenPopup("请输入密码##EncryptInputSecureCode")
}
editDecrypt = func() {
gui.CloseCurrentPopup()
sourceT = tk.Trim(*text1)
encStrT = tk.DecryptStringByTXDEF(sourceT, *editSecureCodeG)
if tk.IsErrorString(encStrT) {
gui.SimpleError("错误提示", tk.Spr("解密失败:%v", tk.GetErrorString(encStrT)))
return
}
setValue(text1, encStrT)
editFileCleanFlagG = "*"
setValue(editSecureCodeG, "")
}
editDecryptClick = func() {
gui.OpenPopup("请输入密码##DecryptInputSecureCode")
}
onButtonCloseClick = func() {
exit()
}
loop = func() {
layoutT = make(gui.Layout)
layoutT = append(layoutT, gui.Label(editFileNameG + editFileCleanFlagG))
layoutT = append(layoutT, gui.InputTextMultiline("InputTextMultiline001", text1, -1, -30, 0, nil, onEditChange))
layoutT = append(layoutT, gui.Line(gui.Button("打开", onButtonLoad), gui.Button("保存", editorSave), gui.Button("另存为", editorSaveAs), gui.Button("加密", editEncryptClick), gui.Button("解密", editDecryptClick), gui.Button("运行", onButtonRunClick), gui.Button("关闭", onButtonCloseClick)))
layoutT = append(layoutT, gui.PopupModal("请输入密码##EncryptInputSecureCode", []gui.Widget{gui.Line(gui.Label("密码"), gui.InputTextV("", 40, editSecureCodeG, gui.InputTextFlagsPassword, nil, nil)),
gui.Line(gui.Button("确定", editEncrypt), gui.Button("取消", func() { gui.CloseCurrentPopup() })),
}))
layoutT = append(layoutT, gui.PopupModal("请输入密码##DecryptInputSecureCode", []gui.Widget{
gui.Line(gui.Label("密码"),
gui.InputTextV("", 40, editSecureCodeG, gui.InputTextFlagsPassword, nil, nil)),
gui.Line(gui.Button("确定", editDecrypt),
gui.Button("取消", func() { gui.CloseCurrentPopup() })),
}))
// add this to the layout if you would use gui.MessageBox function later
layoutT = append(layoutT, gui.PrepareMessageBox())
gui.SingleWindow("Gox编辑器", layoutT)
}
osNameT = tk.GetOSName()
if tk.Contains(osNameT, "darwin") {
setVar("Font", "/Library/Fonts/Microsoft/SimHei.ttf")
} else {
setVar("Font", "c:/Windows/Fonts/simsun.ttc")
}
setVar("FontRange", "COMMON")
setVar("FontSize", "15")
mainWindow = gui.NewMasterWindow("Gox编辑器", 800, 600, 0, gui.LoadFont)
gui.LoopWindow(mainWindow, loop)