跳到主要内容
版本:master

Hello, World!

本章节包含如何在非IDE的命令行环境下运行你的第一个 GS 程序、GS driver 的常用启动参数、GS driver shell 的常用方法等内容。面向初次运行 GS 程序、不了解GS driver 相关概念或操作的同学。阅读完本章节后,应当能够使用命令行运行 GS 程序、使用 shell 调用函数、操作数据、查询函数等。

提示

注意:本教程假定你熟悉命令行操作。

1. driver 概念

driver 实际指代安装过程中我们安装的可执行文件,windows下为 gs.exe 其他系统下为 gs。它是 GS 语言的驱动程序,是用于编译运行 .gs 文件的解释器程序/在 driver 中我们可以动态的编译程序并执行,查看函数、宏、枚举的说明,查看当前内存占用信息,手动调用函数,调用分析工具等。

2. drvier 启动

查看安装过程中创建的/helloworld/src/helloworld.gs 项目目录下的 GS 源码,其代码如下所示:

public void main()
{
write("hello, welcome to g-bits!\n");
write("This is a language powered by g-bits!\n");
return;
}

main();

示例 2-1:gip init 初始化的默认 helloworld.gs 源码

我们用任意文本编辑器打开,并修改源码如下:

void main()
{
write("Hello,World!\n");
return;
}

main();

示例 2-2:一个打印 Hello,World!的程序

保存文件,并使用命令行打开至当前 helloworld 项目的的根目录下,即\helloworld\路径下,运行如下命令编译并执行程序:

.\bin\gs.exe /r ./ /e ./src/helloworld.gs

此时可以看到终端窗口中字符串 Hello, world!被打印出来,同时来到了 driver 的 shell中,如下图所示:

好的,🎊恭喜你运行了属于你的第一个GS 程序。现在让我们暂时离开控制台窗口,了解下driver 常用的启动参数。

3.driver 常用启动参数

下面将介绍一些常用的启动参数,更详细的启动参数见drvier 启动参数

  • /r 设置根目录
  • /e 启动脚本
  • /eq 启动脚本,在脚本执行完成后不进入shell 而是退出
  • /m 可以带多个预加载的脚本,如果有多个用逗号隔开(,)
  • --mount 挂载目录
  • /D 预定义宏
  • --require || --require all 导入内置插件(用于兼容旧代码,详细说明见 import 章节中的相关说明)
  • /c1 开启命令行交互模式
  • --enable-jit [0,1,2] 启用 jit。0 代表解释执行,1 代表 asmjit,2代表 llvmjit。

其中/c1 常用于linux系统中,windows下是默认开启的。

此时再看我们之前的启动命令:

.\bin\gs.exe /r ./ /e ./src/helloworld.gs

实际是将启动 driver 并将根目录设置在当前目录,并编译运行 helloworld.gs 启动脚本,接下来让我们简单尝试下,预加载脚本,挂载目录,预定义宏启动参数。

helloworld/src/目录下让我们额外创建一个preload.gsh,内容如下:

write("load preload.gsh\n"); 

示例 3-1:预加载过程简单输出

调整helloworld/src/目录下 helloworld.gs 源码如下:

public void main()
{
write("Hello,World!\n");
write(lucky_num);
return;
}
main();

示例 3-3:添加宏展开代码的 helloworld.gs 源码

完成调整后目录结构如下:

helloworld
├─ vscode                 Vscode 配置文件
├─ src
| ├─preload.gsh          预加载脚本
| ├─helloworld.gs GS 源码
├─ .gitignore             git 忽略配置文件
├─ package.json GIP 的包管理配置文件
├─ README.md 项目说明文档

同样在项目根目录helloworld/下以如下命令启动程序:

.\bin\gs.exe /r ./src/ /e ./helloworld.gs /m /preload.gsh --mount ./src::/source /D lucky_num=888

命令中我们以启动参数做了如下操作:

  • /r ./src/helloworld/src/设为 driver 的根目录

  • /e ./helloworld.gs 将 driver 启动脚本设为 helloworld.gs

  • /m /preload.gsh 预加载 preload.gsh 脚本

  • --mount ./src::/source 将 /src 挂载为 /source 目录

  • /D lucky_num=888 预定义宏,将 lucky_num 宏定义为数字 888

  • --require json 导入 json 插件

运行的结果如下图:

可以看到源码中的

  • preload.gsh 预加载脚本

  • lucky_num:被正确宏展开为了数字888

此时不要关闭终端,让我们在 shell 中输入如下命令

'get_system_info()

检查输出map 表中的如下字段:

  • root_path: 可以看到/r 启动参数设置的根路径

  • mount_info:可以看到我们将根路径挂载到了/source/路径下

接下来我们介绍下 driver shell的概念及常用的操作。

3. shell 介绍

3.1 shell 概念

Shell 是driver的一个命令行工具,它作为用户与 driver 之间的桥梁,负责接收用户输入的命令,解释并执行这些命令,同时将结果返回给用户。

Shell 本质是一个driver中持续运行的 GS 脚本,相应源码可查阅 shell.gs

当终端终端中带有如下形式输出时,说明shell以打开并可接收并执行命令。

Shell>

3.2 shell 常用方法

在 helloword 项目启动的 shell 中逐个尝试以下 shell 的常用方法并查看 shell 输出:

  • write("Hello,world!")printf("Hello,world!")'"Hello,world!" 输出 "Hello,world!" 字符串
    • shell中可以使用 ' 单引号进行快捷打印
  • man 用于查找函数,枚举或宏说明
    • 想查看array相关的函数,在Shell敲入man("array") 或者敲man array, 两种写法是等价的。可以查找并看到array相关的函数描述,参数列表等。
    • 想查看变量类型枚举信息,在Shell敲入man ValueType
  • cls 用于清空屏幕
  • #define 可以定义在shell 中使用的宏
    • 在shell分别敲入 #define new_num 666 'new_num 两条命令,查看 new_num 是否被正确的宏展开
    • 出于安全考虑,覆盖同名的宏(无论是之前在shell中定义的还是环境中定义的),都会导致之后执行的代码发出警告
  • find_object(handle|string name_or_id) 查找一个object
    • 在shell敲入 'find_object(shell) 可以查找到内置的 shell.gs 脚本的 object 实例
    • 在shell敲入 find_object("/helloworld.gs").main() 可以使用找到的 helloworld.gs 的 object 调用其公开的 main 函数
  • get_obj_var(object ob, string var_name) 查看某个object的私有变量
    • 在shell敲入 'get_obj_var(find_object(shell), "_shutdown_cmds") 可以查找到内置的 shell.gs 脚本中 shutdown driver 的命令列表,为中英文 dot 符号构成的数组
  • set_obj_var(object ob, string var_name, mixed val) 设置某个私有变量的值,避免添加一个set函数->patch->删除这个set函数->patch这样的工作
    • 'set_obj_var(find_object(shell), "_shutdown_cmds", make_readonly(["down"])) 设置内置的 shell.gs 脚本中 shutdown driver 的命令列表,现在键入 down 即可关闭driver
  • monkey.patch(program_name) 或 monkey.patch("代码文件路径")
    • 在不关闭shell的情况下修改 helloworld.gs 增加一个任意输出,敲入 monkey.patch("/helloworld.gs")再次敲入find_object("/helloworld.gs").main()可以看到修改被热更新到已有的 object 上
    • 可以尝试通过handle.get_all("program")查询当前有哪些program
    • 然后通过monkey.patch(program_name)去热更新
    • update()也可以用于热更新, 但是实现原理不太相同, update的逻辑更简单compile → destruct_object → load_static,但是对于有状态的对象,成员变量无法恢复,无法进行热更新。monkey.patch更为通用。
  • => 用于切换 shell 执行时的域
    • => find_object("/helloworld.gs") 即可进入 helloworld.gs 的 object 所在域,域的概念将在后续章节介绍
    • => ob.get_domain() 可以切换到 ob 所属的域,之后shell的操作将在该域中执行
    • => nil 可以切换到 shell 的默认域
    • 切换到目标域后的 shell 并不会持续占有域锁,而是仅当执行 shell 命令时占有域锁,执行完毕后会自动释放域锁。
    • 特别注意,如果被切换的目标域始终不让出,可能导致 shell 无法进入而阻塞
  • clear env 可以清除此前在shell中定义的变量、函数和宏
  • child_objects(string | object name) 某个对象的所有实例
  • || . 用于退出driver